diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 83441af8f5..124e71f6bb 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -706,6 +706,7 @@ common/views/components/profile-editor.vue:
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
+ user-lists: "リスト"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
enter-password: "パスワードを入力してください"
danger-zone: "危険な設定"
diff --git a/src/client/app/common/views/components/settings/profile.vue b/src/client/app/common/views/components/settings/profile.vue
index 61d273cfaf..16e7a3b259 100644
--- a/src/client/app/common/views/components/settings/profile.vue
+++ b/src/client/app/common/views/components/settings/profile.vue
@@ -97,6 +97,7 @@
+
{{ $t('export') }}
@@ -284,6 +285,7 @@ export default Vue.extend({
this.exportTarget == 'following' ? 'i/export-following' :
this.exportTarget == 'mute' ? 'i/export-mute' :
this.exportTarget == 'blocking' ? 'i/export-blocking' :
+ this.exportTarget == 'user-lists' ? 'i/export-user-lists' :
null, {});
this.$root.dialog({
diff --git a/src/queue/index.ts b/src/queue/index.ts
index 83cebe247b..00a4a48f14 100644
--- a/src/queue/index.ts
+++ b/src/queue/index.ts
@@ -136,6 +136,15 @@ export function createExportBlockingJob(user: ILocalUser) {
});
}
+export function createExportUserListsJob(user: ILocalUser) {
+ return dbQueue.add('exportUserLists', {
+ user: user
+ }, {
+ removeOnComplete: true,
+ removeOnFail: true
+ });
+}
+
export default function() {
if (!program.onlyServer) {
deliverQueue.process(128, processDeliver);
diff --git a/src/queue/processors/db/export-user-lists.ts b/src/queue/processors/db/export-user-lists.ts
new file mode 100644
index 0000000000..6b5f72c15e
--- /dev/null
+++ b/src/queue/processors/db/export-user-lists.ts
@@ -0,0 +1,73 @@
+import * as Bull from 'bull';
+import * as tmp from 'tmp';
+import * as fs from 'fs';
+import * as mongo from 'mongodb';
+
+import { queueLogger } from '../../logger';
+import addFile from '../../../services/drive/add-file';
+import User from '../../../models/user';
+import dateFormat = require('dateformat');
+import config from '../../../config';
+import UserList from '../../../models/user-list';
+
+const logger = queueLogger.createSubLogger('export-user-lists');
+
+export async function exportUserLists(job: Bull.Job, done: any): Promise {
+ logger.info(`Exporting user lists of ${job.data.user._id} ...`);
+
+ const user = await User.findOne({
+ _id: new mongo.ObjectID(job.data.user._id.toString())
+ });
+
+ const lists = await UserList.find({
+ userId: user._id
+ });
+
+ // Create temp file
+ const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
+ tmp.file((e, path, fd, cleanup) => {
+ if (e) return rej(e);
+ res([path, cleanup]);
+ });
+ });
+
+ logger.info(`Temp file is ${path}`);
+
+ const stream = fs.createWriteStream(path, { flags: 'a' });
+
+ for (const list of lists) {
+ const users = await User.find({
+ _id: { $in: list.userIds }
+ }, {
+ fields: {
+ username: true,
+ host: true
+ }
+ });
+
+ for (const u of users) {
+ const acct = u.host ? `${u.username}@${u.host}` : `${u.username}@${config.host}`;
+ const content = `${list.title},${acct}`;
+ await new Promise((res, rej) => {
+ stream.write(content + '\n', err => {
+ if (err) {
+ logger.error(err);
+ rej(err);
+ } else {
+ res();
+ }
+ });
+ });
+ }
+ }
+
+ stream.end();
+ logger.succ(`Exported to: ${path}`);
+
+ const fileName = 'user-lists-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.csv';
+ const driveFile = await addFile(user, path, fileName);
+
+ logger.succ(`Exported to: ${driveFile._id}`);
+ cleanup();
+ done();
+}
diff --git a/src/queue/processors/db/index.ts b/src/queue/processors/db/index.ts
index 31a176cdae..8ac9c1a3d6 100644
--- a/src/queue/processors/db/index.ts
+++ b/src/queue/processors/db/index.ts
@@ -5,6 +5,7 @@ import { exportNotes } from './export-notes';
import { exportFollowing } from './export-following';
import { exportMute } from './export-mute';
import { exportBlocking } from './export-blocking';
+import { exportUserLists } from './export-user-lists';
const jobs = {
deleteNotes,
@@ -13,6 +14,7 @@ const jobs = {
exportFollowing,
exportMute,
exportBlocking,
+ exportUserLists
} as any;
export default function(dbQueue: Bull.Queue) {
diff --git a/src/server/api/endpoints/i/export-user-lists.ts b/src/server/api/endpoints/i/export-user-lists.ts
new file mode 100644
index 0000000000..9d7424ad89
--- /dev/null
+++ b/src/server/api/endpoints/i/export-user-lists.ts
@@ -0,0 +1,18 @@
+import define from '../../define';
+import { createExportUserListsJob } from '../../../../queue';
+import ms = require('ms');
+
+export const meta = {
+ secure: true,
+ requireCredential: true,
+ limit: {
+ duration: ms('1min'),
+ max: 1,
+ },
+};
+
+export default define(meta, async (ps, user) => {
+ createExportUserListsJob(user);
+
+ return;
+});