Refactoring, Clean up and bug fixes

This commit is contained in:
syuilo 2018-11-02 03:32:24 +09:00
parent b4b9e76c8d
commit 931bdc6aac
No known key found for this signature in database
GPG Key ID: BDC4C49D06AB9D69
108 changed files with 1722 additions and 1539 deletions

10
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "10.36.0", "version": "10.37.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -2250,9 +2250,9 @@
} }
}, },
"cafy": { "cafy": {
"version": "11.3.0", "version": "12.0.0",
"resolved": "https://registry.npmjs.org/cafy/-/cafy-11.3.0.tgz", "resolved": "https://registry.npmjs.org/cafy/-/cafy-12.0.0.tgz",
"integrity": "sha512-7kqqF4I6seSNSAWihRfnM78wP/OwaZMrCNIUzu0+TC1pDGfF2uoVfMsAJ1oV1jZsZ2L2qlUSvo9zhSEIouS/xQ==" "integrity": "sha512-HGsunRfyqFyG1/oh+Szw8GtVpj4pwehyqmp8sTO1QwDF3htjDP+vVBWzg7iOU2Y3Cm+h+UiEpf6DJ0p57RNmAg=="
}, },
"caller-path": { "caller-path": {
"version": "0.1.0", "version": "0.1.0",
@ -15830,7 +15830,7 @@
}, },
"fast-deep-equal": { "fast-deep-equal": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
}, },
"ignore": { "ignore": {

View File

@ -91,7 +91,7 @@
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"bee-queue": "1.2.2", "bee-queue": "1.2.2",
"bootstrap-vue": "2.0.0-rc.11", "bootstrap-vue": "2.0.0-rc.11",
"cafy": "11.3.0", "cafy": "12.0.0",
"chai": "4.2.0", "chai": "4.2.0",
"chai-http": "4.2.0", "chai-http": "4.2.0",
"chalk": "2.4.1", "chalk": "2.4.1",

View File

@ -4,23 +4,31 @@ import isObjectId from './is-objectid';
export const isAnId = (x: any) => mongo.ObjectID.isValid(x); export const isAnId = (x: any) => mongo.ObjectID.isValid(x);
export const isNotAnId = (x: any) => !isAnId(x); export const isNotAnId = (x: any) => !isAnId(x);
export const transform = (x: string | mongo.ObjectID): mongo.ObjectID => {
if (x == null) return null;
if (isAnId(x) && !isObjectId(x)) {
return new mongo.ObjectID(x);
} else {
return x as mongo.ObjectID;
}
};
export const transformMany = (xs: (string | mongo.ObjectID)[]): mongo.ObjectID[] => {
if (xs == null) return null;
return xs.map(x => transform(x));
};
export type ObjectId = mongo.ObjectID;
/** /**
* ID * ID
*/ */
export default class ID extends Context<mongo.ObjectID> { export default class ID extends Context<string> {
constructor() { constructor() {
super(); super();
this.transform = v => { this.push((v: any) => {
if (isAnId(v) && !isObjectId(v)) {
return new mongo.ObjectID(v);
} else {
return v;
}
};
this.push(v => {
if (!isObjectId(v) && isNotAnId(v)) { if (!isObjectId(v) && isNotAnId(v)) {
return new Error('must-be-an-id'); return new Error('must-be-an-id');
} }

View File

@ -189,7 +189,7 @@ export async function getRelation(me: mongo.ObjectId, target: mongo.ObjectId) {
return { return {
isFollowing: following1 !== null, isFollowing: following1 !== null,
isStalking: following1 && following1.stalk, isStalking: following1 ? following1.stalk : false,
hasPendingFollowRequestFromYou: followReq1 !== null, hasPendingFollowRequestFromYou: followReq1 !== null,
hasPendingFollowRequestToYou: followReq2 !== null, hasPendingFollowRequestToYou: followReq2 !== null,
isFollowed: following2 !== null, isFollowed: following2 !== null,

View File

@ -1,7 +1,7 @@
import * as mongo from 'mongodb'; import * as mongo from 'mongodb';
import * as Router from 'koa-router'; import * as Router from 'koa-router';
import config from '../../config'; import config from '../../config';
import $ from 'cafy'; import ID from '../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../misc/cafy-id';
import User from '../../models/user'; import User from '../../models/user';
import Following from '../../models/following'; import Following from '../../models/following';
import pack from '../../remote/activitypub/renderer'; import pack from '../../remote/activitypub/renderer';
@ -49,7 +49,7 @@ export default async (ctx: Router.IRouterContext) => {
// カーソルが指定されている場合 // カーソルが指定されている場合
if (cursor) { if (cursor) {
query._id = { query._id = {
$lt: cursor $lt: transform(cursor)
}; };
} }

View File

@ -1,7 +1,7 @@
import * as mongo from 'mongodb'; import * as mongo from 'mongodb';
import * as Router from 'koa-router'; import * as Router from 'koa-router';
import config from '../../config'; import config from '../../config';
import $ from 'cafy'; import ID from '../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../misc/cafy-id';
import User from '../../models/user'; import User from '../../models/user';
import Following from '../../models/following'; import Following from '../../models/following';
import pack from '../../remote/activitypub/renderer'; import pack from '../../remote/activitypub/renderer';
@ -49,7 +49,7 @@ export default async (ctx: Router.IRouterContext) => {
// カーソルが指定されている場合 // カーソルが指定されている場合
if (cursor) { if (cursor) {
query._id = { query._id = {
$lt: cursor $lt: transform(cursor)
}; };
} }

View File

@ -1,7 +1,7 @@
import * as mongo from 'mongodb'; import * as mongo from 'mongodb';
import * as Router from 'koa-router'; import * as Router from 'koa-router';
import config from '../../config'; import config from '../../config';
import $ from 'cafy'; import ID from '../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../misc/cafy-id';
import User from '../../models/user'; import User from '../../models/user';
import pack from '../../remote/activitypub/renderer'; import pack from '../../remote/activitypub/renderer';
import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection'; import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection';
@ -61,11 +61,11 @@ export default async (ctx: Router.IRouterContext) => {
if (sinceId) { if (sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: transform(sinceId)
}; };
} else if (untilId) { } else if (untilId) {
query._id = { query._id = {
$lt: untilId $lt: transform(untilId)
}; };
} }
//#endregion //#endregion

View File

@ -1,12 +1,21 @@
import { Context } from 'cafy';
import * as path from 'path'; import * as path from 'path';
import * as glob from 'glob'; import * as glob from 'glob';
export interface IEndpointMeta { export interface IEndpointMeta {
stability?: 'deprecated' | 'experimental' | 'stable'; stability?: string; //'deprecated' | 'experimental' | 'stable';
desc?: any; desc?: { [key: string]: string };
params?: any; params?: {
[key: string]: {
validator: Context<any>;
transform?: any;
default?: any;
desc?: { [key: string]: string };
ref?: string;
};
};
res?: any; res?: any;

View File

@ -1,5 +1,5 @@
import $ from 'cafy'; import $ from 'cafy';
import ID from '../../../../misc/cafy-id'; import ID, { transform } from '../../../../misc/cafy-id';
import getParams from '../../get-params'; import getParams from '../../get-params';
import User from '../../../../models/user'; import User from '../../../../models/user';
@ -13,12 +13,14 @@ export const meta = {
requireAdmin: true, requireAdmin: true,
params: { params: {
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーID', 'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to suspend' 'en-US': 'The user ID which you want to suspend'
} }
}), },
} }
}; };

View File

@ -1,5 +1,5 @@
import $ from 'cafy'; import $ from 'cafy';
import ID from '../../../../misc/cafy-id'; import ID, { transform } from '../../../../misc/cafy-id';
import getParams from '../../get-params'; import getParams from '../../get-params';
import User from '../../../../models/user'; import User from '../../../../models/user';
@ -13,12 +13,14 @@ export const meta = {
requireAdmin: true, requireAdmin: true,
params: { params: {
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーID', 'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to unsuspend' 'en-US': 'The user ID which you want to unsuspend'
} }
}), },
} }
}; };

View File

@ -1,5 +1,5 @@
import $ from 'cafy'; import $ from 'cafy';
import ID from '../../../../misc/cafy-id'; import ID, { transform } from '../../../../misc/cafy-id';
import getParams from '../../get-params'; import getParams from '../../get-params';
import User from '../../../../models/user'; import User from '../../../../models/user';
@ -13,12 +13,14 @@ export const meta = {
requireAdmin: true, requireAdmin: true,
params: { params: {
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーID', 'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to unverify' 'en-US': 'The user ID which you want to unverify'
} }
}), },
} }
}; };

View File

@ -11,41 +11,47 @@ export const meta = {
requireAdmin: true, requireAdmin: true,
params: { params: {
broadcasts: $.arr($.obj()).optional.nullable.note({ broadcasts: {
validator: $.arr($.obj()).optional.nullable,
desc: { desc: {
'ja-JP': 'ブロードキャスト' 'ja-JP': 'ブロードキャスト'
} }
}), },
emojis: $.arr($.obj()).optional.note({ emojis: {
validator: $.arr($.obj()).optional,
desc: { desc: {
'ja-JP': 'カスタム絵文字定義' 'ja-JP': 'カスタム絵文字定義'
} }
}), },
disableRegistration: $.bool.optional.nullable.note({ disableRegistration: {
validator: $.bool.optional.nullable,
desc: { desc: {
'ja-JP': '招待制か否か' 'ja-JP': '招待制か否か'
} }
}), },
disableLocalTimeline: $.bool.optional.nullable.note({ disableLocalTimeline: {
validator: $.bool.optional.nullable,
desc: { desc: {
'ja-JP': 'ローカルタイムライン(とソーシャルタイムライン)を無効にするか否か' 'ja-JP': 'ローカルタイムライン(とソーシャルタイムライン)を無効にするか否か'
} }
}), },
hidedTags: $.arr($.str).optional.nullable.note({ hidedTags: {
validator: $.arr($.str).optional.nullable,
desc: { desc: {
'ja-JP': '統計などで無視するハッシュタグ' 'ja-JP': '統計などで無視するハッシュタグ'
} }
}), },
bannerUrl: $.str.optional.nullable.note({ bannerUrl: {
validator: $.str.optional.nullable,
desc: { desc: {
'ja-JP': 'インスタンスのバナー画像URL' 'ja-JP': 'インスタンスのバナー画像URL'
} }
}), },
} }
}; };

View File

@ -1,5 +1,5 @@
import $ from 'cafy'; import $ from 'cafy';
import ID from '../../../../misc/cafy-id'; import ID, { transform } from '../../../../misc/cafy-id';
import getParams from '../../get-params'; import getParams from '../../get-params';
import User from '../../../../models/user'; import User from '../../../../models/user';
@ -13,12 +13,14 @@ export const meta = {
requireAdmin: true, requireAdmin: true,
params: { params: {
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーID', 'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to verify' 'en-US': 'The user ID which you want to verify'
} }
}), },
} }
}; };

View File

@ -1,66 +0,0 @@
import Note from '../../../../models/note';
import Meta from '../../../../models/meta';
export default () => new Promise(async (res, rej) => {
const meta = await Meta.findOne({});
const hidedTags = meta ? (meta.hidedTags || []).map(t => t.toLowerCase()) : [];
const span = 1000 * 60 * 60 * 24 * 7; // 1週間
//#region 1. 指定期間の内に投稿されたハッシュタグ(とユーザーのペア)を集計
const data = await Note.aggregate([{
$match: {
createdAt: {
$gt: new Date(Date.now() - span)
},
tagsLower: {
$exists: true,
$ne: []
}
}
}, {
$unwind: '$tagsLower'
}, {
$group: {
_id: { tag: '$tagsLower', userId: '$userId' }
}
}]) as Array<{
_id: {
tag: string;
userId: any;
}
}>;
//#endregion
if (data.length == 0) {
return res([]);
}
let tags: Array<{
name: string;
count: number;
}> = [];
// カウント
data.map(x => x._id).forEach(x => {
// ブラックリストに登録されているタグなら弾く
if (hidedTags.includes(x.tag)) return;
const i = tags.findIndex(tag => tag.name == x.tag);
if (i != -1) {
tags[i].count++;
} else {
tags.push({
name: x.tag,
count: 1
});
}
});
// タグを人気順に並べ替え
tags = tags.sort((a, b) => b.count - a.count);
tags = tags.slice(0, 30);
res(tags);
});

View File

@ -1,110 +0,0 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id';
import User from '../../../../../models/user';
import Note from '../../../../../models/note';
// TODO: likeやfollowも集計
/**
* Aggregate activity of a user
*/
export default (params: any) => new Promise(async (res, rej) => {
// Get 'limit' parameter
const [limit = 365, limitErr] = $.num.optional.range(1, 365).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Get 'userId' parameter
const [userId, userIdErr] = $.type(ID).get(params.userId);
if (userIdErr) return rej('invalid userId param');
// Lookup user
const user = await User.findOne({
_id: userId
}, {
fields: {
_id: true
}
});
if (user === null) {
return rej('user not found');
}
const datas = await Note
.aggregate([
{ $match: { userId: user._id } },
{ $project: {
renoteId: '$renoteId',
replyId: '$replyId',
createdAt: { $add: ['$createdAt', 9 * 60 * 60 * 1000] } // Convert into JST
}},
{ $project: {
date: {
year: { $year: '$createdAt' },
month: { $month: '$createdAt' },
day: { $dayOfMonth: '$createdAt' }
},
type: {
$cond: {
if: { $ne: ['$renoteId', null] },
then: 'renote',
else: {
$cond: {
if: { $ne: ['$replyId', null] },
then: 'reply',
else: 'note'
}
}
}
}}
},
{ $group: { _id: {
date: '$date',
type: '$type'
}, count: { $sum: 1 } } },
{ $group: {
_id: '$_id.date',
data: { $addToSet: {
type: '$_id.type',
count: '$count'
}}
} }
]);
datas.forEach((data: any) => {
data.date = data._id;
delete data._id;
data.notes = (data.data.filter((x: any) => x.type == 'note')[0] || { count: 0 }).count;
data.renotes = (data.data.filter((x: any) => x.type == 'renote')[0] || { count: 0 }).count;
data.replies = (data.data.filter((x: any) => x.type == 'reply')[0] || { count: 0 }).count;
delete data.data;
});
const graph = [];
for (let i = 0; i < limit; i++) {
const day = new Date(new Date().setDate(new Date().getDate() - i));
const data = datas.filter((d: any) =>
d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate()
)[0];
if (data) {
graph.push(data);
} else {
graph.push({
date: {
year: day.getFullYear(),
month: day.getMonth() + 1, // In JavaScript, month is zero-based.
day: day.getDate()
},
notes: 0,
renotes: 0,
replies: 0
});
}
}
res(graph);
});

View File

@ -1,104 +0,0 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id';
import User from '../../../../../models/user';
import Note from '../../../../../models/note';
/**
* Aggregate note of a user
*/
export default (params: any) => new Promise(async (res, rej) => {
// Get 'userId' parameter
const [userId, userIdErr] = $.type(ID).get(params.userId);
if (userIdErr) return rej('invalid userId param');
// Lookup user
const user = await User.findOne({
_id: userId
}, {
fields: {
_id: true
}
});
if (user === null) {
return rej('user not found');
}
const datas = await Note
.aggregate([
{ $match: { userId: user._id } },
{ $project: {
renoteId: '$renoteId',
replyId: '$replyId',
createdAt: { $add: ['$createdAt', 9 * 60 * 60 * 1000] } // Convert into JST
}},
{ $project: {
date: {
year: { $year: '$createdAt' },
month: { $month: '$createdAt' },
day: { $dayOfMonth: '$createdAt' }
},
type: {
$cond: {
if: { $ne: ['$renoteId', null] },
then: 'renote',
else: {
$cond: {
if: { $ne: ['$replyId', null] },
then: 'reply',
else: 'note'
}
}
}
}}
},
{ $group: { _id: {
date: '$date',
type: '$type'
}, count: { $sum: 1 } } },
{ $group: {
_id: '$_id.date',
data: { $addToSet: {
type: '$_id.type',
count: '$count'
}}
} }
]);
datas.forEach((data: any) => {
data.date = data._id;
delete data._id;
data.notes = (data.data.filter((x: any) => x.type == 'note')[0] || { count: 0 }).count;
data.renotes = (data.data.filter((x: any) => x.type == 'renote')[0] || { count: 0 }).count;
data.replies = (data.data.filter((x: any) => x.type == 'reply')[0] || { count: 0 }).count;
delete data.data;
});
const graph = [];
for (let i = 0; i < 30; i++) {
const day = new Date(new Date().setDate(new Date().getDate() - i));
const data = datas.filter((d: any) =>
d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate()
)[0];
if (data) {
graph.push(data);
} else {
graph.push({
date: {
year: day.getFullYear(),
month: day.getMonth() + 1, // In JavaScript, month is zero-based.
day: day.getDate()
},
notes: 0,
renotes: 0,
replies: 0
});
}
}
res(graph);
});

View File

@ -1,74 +0,0 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id';
import User from '../../../../../models/user';
import Reaction from '../../../../../models/note-reaction';
/**
* Aggregate reaction of a user
*/
export default (params: any) => new Promise(async (res, rej) => {
// Get 'userId' parameter
const [userId, userIdErr] = $.type(ID).get(params.userId);
if (userIdErr) return rej('invalid userId param');
// Lookup user
const user = await User.findOne({
_id: userId
}, {
fields: {
_id: true
}
});
if (user === null) {
return rej('user not found');
}
const datas = await Reaction
.aggregate([
{ $match: { userId: user._id } },
{ $project: {
createdAt: { $add: ['$createdAt', 9 * 60 * 60 * 1000] } // Convert into JST
}},
{ $project: {
date: {
year: { $year: '$createdAt' },
month: { $month: '$createdAt' },
day: { $dayOfMonth: '$createdAt' }
}
}},
{ $group: {
_id: '$date',
count: { $sum: 1 }
}}
]);
datas.forEach((data: any) => {
data.date = data._id;
delete data._id;
});
const graph = [];
for (let i = 0; i < 30; i++) {
const day = new Date(new Date().setDate(new Date().getDate() - i));
const data = datas.filter((d: any) =>
d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate()
)[0];
if (data) {
graph.push(data);
} else {
graph.push({
date: {
year: day.getFullYear(),
month: day.getMonth() + 1, // In JavaScript, month is zero-based.
day: day.getDate()
},
count: 0
});
}
}
res(graph);
});

View File

@ -16,11 +16,12 @@ export const meta = {
requireCredential: false, requireCredential: false,
params: { params: {
uri: $.str.note({ uri: {
validator: $.str,
desc: { desc: {
'ja-JP': 'ActivityPubオブジェクトのURI' 'ja-JP': 'ActivityPubオブジェクトのURI'
} }
}), },
}, },
}; };

View File

@ -1,19 +1,25 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import App, { pack, IApp } from '../../../../models/app'; import App, { pack, IApp } from '../../../../models/app';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = {
params: {
appId: {
validator: $.type(ID),
transform: transform
},
}
};
/**
* Show an app
*/
export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => {
const [ps, psErr] = getParams(meta, params);
if (psErr) return rej(psErr);
const isSecure = user != null && app == null; const isSecure = user != null && app == null;
// Get 'appId' parameter
const [appId, appIdErr] = $.type(ID).get(params.appId);
if (appIdErr) return rej('invalid appId param');
// Lookup app // Lookup app
const ap = await App.findOne({ _id: appId }); const ap = await App.findOne({ _id: ps.appId });
if (ap === null) { if (ap === null) {
return rej('app not found'); return rej('app not found');

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
const ms = require('ms'); const ms = require('ms');
import User, { pack, ILocalUser } from '../../../../models/user'; import User, { pack, ILocalUser } from '../../../../models/user';
import Blocking from '../../../../models/blocking'; import Blocking from '../../../../models/blocking';
@ -23,12 +23,14 @@ export const meta = {
kind: 'following-write', kind: 'following-write',
params: { params: {
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーのID', 'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID' 'en-US': 'Target user ID'
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
const ms = require('ms'); const ms = require('ms');
import User, { pack, ILocalUser } from '../../../../models/user'; import User, { pack, ILocalUser } from '../../../../models/user';
import Blocking from '../../../../models/blocking'; import Blocking from '../../../../models/blocking';
@ -23,12 +23,14 @@ export const meta = {
kind: 'following-write', kind: 'following-write',
params: { params: {
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーのID', 'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID' 'en-US': 'Target user ID'
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Blocking, { packMany } from '../../../../models/blocking'; import Blocking, { packMany } from '../../../../models/blocking';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params'; import getParams from '../../get-params';
@ -14,15 +14,20 @@ export const meta = {
kind: 'following-read', kind: 'following-read',
params: { params: {
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30 default: 30
}), },
sinceId: $.type(ID).optional.note({ sinceId: {
}), validator: $.type(ID).optional,
transform: transform,
},
untilId: $.type(ID).optional.note({ untilId: {
}), validator: $.type(ID).optional,
transform: transform,
},
} }
}; };

View File

@ -8,18 +8,20 @@ export const meta = {
}, },
params: { params: {
span: $.str.or(['day', 'hour']).note({ span: {
validator: $.str.or(['day', 'hour']),
desc: { desc: {
'ja-JP': '集計のスパン (day または hour)' 'ja-JP': '集計のスパン (day または hour)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30, default: 30,
desc: { desc: {
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
} }
}), },
} }
}; };

View File

@ -8,18 +8,20 @@ export const meta = {
}, },
params: { params: {
span: $.str.or(['day', 'hour']).note({ span: {
validator: $.str.or(['day', 'hour']),
desc: { desc: {
'ja-JP': '集計のスパン (day または hour)' 'ja-JP': '集計のスパン (day または hour)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30, default: 30,
desc: { desc: {
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
} }
}), },
} }
}; };

View File

@ -8,24 +8,27 @@ export const meta = {
}, },
params: { params: {
span: $.str.or(['day', 'hour']).note({ span: {
validator: $.str.or(['day', 'hour']),
desc: { desc: {
'ja-JP': '集計のスパン (day または hour)' 'ja-JP': '集計のスパン (day または hour)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30, default: 30,
desc: { desc: {
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
} }
}), },
tag: $.str.note({ tag: {
validator: $.str,
desc: { desc: {
'ja-JP': '対象のハッシュタグ' 'ja-JP': '対象のハッシュタグ'
} }
}), },
} }
}; };

View File

@ -8,18 +8,20 @@ export const meta = {
}, },
params: { params: {
span: $.str.or(['day', 'hour']).note({ span: {
validator: $.str.or(['day', 'hour']),
desc: { desc: {
'ja-JP': '集計のスパン (day または hour)' 'ja-JP': '集計のスパン (day または hour)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30, default: 30,
desc: { desc: {
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
} }
}), },
} }
}; };

View File

@ -8,18 +8,20 @@ export const meta = {
}, },
params: { params: {
span: $.str.or(['day', 'hour']).note({ span: {
validator: $.str.or(['day', 'hour']),
desc: { desc: {
'ja-JP': '集計のスパン (day または hour)' 'ja-JP': '集計のスパン (day または hour)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30, default: 30,
desc: { desc: {
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
} }
}), },
} }
}; };

View File

@ -1,7 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import getParams from '../../../get-params'; import getParams from '../../../get-params';
import perUserDriveChart from '../../../../../chart/per-user-drive'; import perUserDriveChart from '../../../../../chart/per-user-drive';
import ID from '../../../../../misc/cafy-id'; import ID, { transform } from '../../../../../misc/cafy-id';
export const meta = { export const meta = {
desc: { desc: {
@ -9,25 +9,29 @@ export const meta = {
}, },
params: { params: {
span: $.str.or(['day', 'hour']).note({ span: {
validator: $.str.or(['day', 'hour']),
desc: { desc: {
'ja-JP': '集計のスパン (day または hour)' 'ja-JP': '集計のスパン (day または hour)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30, default: 30,
desc: { desc: {
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
} }
}), },
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーのID', 'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID' 'en-US': 'Target user ID'
} }
}) }
} }
}; };

View File

@ -1,7 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import getParams from '../../../get-params'; import getParams from '../../../get-params';
import perUserFollowingChart from '../../../../../chart/per-user-following'; import perUserFollowingChart from '../../../../../chart/per-user-following';
import ID from '../../../../../misc/cafy-id'; import ID, { transform } from '../../../../../misc/cafy-id';
export const meta = { export const meta = {
desc: { desc: {
@ -9,25 +9,29 @@ export const meta = {
}, },
params: { params: {
span: $.str.or(['day', 'hour']).note({ span: {
validator: $.str.or(['day', 'hour']),
desc: { desc: {
'ja-JP': '集計のスパン (day または hour)' 'ja-JP': '集計のスパン (day または hour)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30, default: 30,
desc: { desc: {
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
} }
}), },
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーのID', 'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID' 'en-US': 'Target user ID'
} }
}) }
} }
}; };

View File

@ -1,7 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import getParams from '../../../get-params'; import getParams from '../../../get-params';
import perUserNotesChart from '../../../../../chart/per-user-notes'; import perUserNotesChart from '../../../../../chart/per-user-notes';
import ID from '../../../../../misc/cafy-id'; import ID, { transform } from '../../../../../misc/cafy-id';
export const meta = { export const meta = {
desc: { desc: {
@ -9,25 +9,29 @@ export const meta = {
}, },
params: { params: {
span: $.str.or(['day', 'hour']).note({ span: {
validator: $.str.or(['day', 'hour']),
desc: { desc: {
'ja-JP': '集計のスパン (day または hour)' 'ja-JP': '集計のスパン (day または hour)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30, default: 30,
desc: { desc: {
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
} }
}), },
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーのID', 'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID' 'en-US': 'Target user ID'
} }
}) }
} }
}; };

View File

@ -1,7 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import getParams from '../../../get-params'; import getParams from '../../../get-params';
import perUserReactionsChart from '../../../../../chart/per-user-reactions'; import perUserReactionsChart from '../../../../../chart/per-user-reactions';
import ID from '../../../../../misc/cafy-id'; import ID, { transform } from '../../../../../misc/cafy-id';
export const meta = { export const meta = {
desc: { desc: {
@ -9,25 +9,29 @@ export const meta = {
}, },
params: { params: {
span: $.str.or(['day', 'hour']).note({ span: {
validator: $.str.or(['day', 'hour']),
desc: { desc: {
'ja-JP': '集計のスパン (day または hour)' 'ja-JP': '集計のスパン (day または hour)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30, default: 30,
desc: { desc: {
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
} }
}), },
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーのID', 'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID' 'en-US': 'Target user ID'
} }
}) }
} }
}; };

View File

@ -8,18 +8,20 @@ export const meta = {
}, },
params: { params: {
span: $.str.or(['day', 'hour']).note({ span: {
validator: $.str.or(['day', 'hour']),
desc: { desc: {
'ja-JP': '集計のスパン (day または hour)' 'ja-JP': '集計のスパン (day または hour)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30, default: 30,
desc: { desc: {
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
} }
}), },
} }
}; };

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import DriveFile, { packMany } from '../../../../models/drive-file'; import DriveFile, { packMany } from '../../../../models/drive-file';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -10,68 +11,75 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'drive-read' kind: 'drive-read',
params: {
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: {
validator: $.type(ID).optional,
transform: transform,
},
folderId: {
validator: $.type(ID).optional.nullable,
default: null as any,
transform: transform,
},
type: {
validator: $.str.optional.match(/^[a-zA-Z\/\-\*]+$/)
}
}
}; };
export default async (params: any, user: ILocalUser) => { export default async (params: any, user: ILocalUser) => {
// Get 'limit' parameter const [ps, psErr] = getParams(meta, params);
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); if (psErr) throw psErr;
if (limitErr) throw 'invalid limit param';
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) throw 'invalid sinceId param';
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) throw 'invalid untilId param';
// Check if both of sinceId and untilId is specified // Check if both of sinceId and untilId is specified
if (sinceId && untilId) { if (ps.sinceId && ps.untilId) {
throw 'cannot set sinceId and untilId'; throw 'cannot set sinceId and untilId';
} }
// Get 'folderId' parameter
const [folderId = null, folderIdErr] = $.type(ID).optional.nullable.get(params.folderId);
if (folderIdErr) throw 'invalid folderId param';
// Get 'type' parameter
const [type, typeErr] = $.str.optional.match(/^[a-zA-Z\/\-\*]+$/).get(params.type);
if (typeErr) throw 'invalid type param';
// Construct query
const sort = { const sort = {
_id: -1 _id: -1
}; };
const query = { const query = {
'metadata.userId': user._id, 'metadata.userId': user._id,
'metadata.folderId': folderId, 'metadata.folderId': ps.folderId,
'metadata.deletedAt': { $exists: false } 'metadata.deletedAt': { $exists: false }
} as any; } as any;
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
query._id = { query._id = {
$lt: untilId $lt: ps.untilId
}; };
} }
if (type) { if (ps.type) {
query.contentType = new RegExp(`^${type.replace(/\*/g, '.+?')}$`); query.contentType = new RegExp(`^${ps.type.replace(/\*/g, '.+?')}$`);
} }
// Issue query
const files = await DriveFile const files = await DriveFile
.find(query, { .find(query, {
limit: limit, limit: ps.limit,
sort: sort sort: sort
}); });
// Serialize
return await packMany(files); return await packMany(files);
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFile from '../../../../../models/drive-file'; import DriveFile from '../../../../../models/drive-file';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params'; import getParams from '../../../get-params';
@ -17,12 +17,14 @@ export const meta = {
kind: 'drive-read', kind: 'drive-read',
params: { params: {
fileId: $.type(ID).note({ fileId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のファイルID', 'ja-JP': '対象のファイルID',
'en-US': 'Target file ID' 'en-US': 'Target file ID'
} }
}) }
} }
}; };

View File

@ -13,11 +13,12 @@ export const meta = {
kind: 'drive-read', kind: 'drive-read',
params: { params: {
md5: $.str.note({ md5: {
validator: $.str,
desc: { desc: {
'ja-JP': 'ファイルのMD5ハッシュ' 'ja-JP': 'ファイルのMD5ハッシュ'
} }
}) }
} }
}; };

View File

@ -1,6 +1,6 @@
import * as fs from 'fs'; import * as fs from 'fs';
const ms = require('ms'); const ms = require('ms');
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import { validateFileName, pack } from '../../../../../models/drive-file'; import { validateFileName, pack } from '../../../../../models/drive-file';
import create from '../../../../../services/drive/add-file'; import create from '../../../../../services/drive/add-file';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
@ -24,27 +24,31 @@ export const meta = {
kind: 'drive-write', kind: 'drive-write',
params: { params: {
folderId: $.type(ID).optional.nullable.note({ folderId: {
default: null, validator: $.type(ID).optional.nullable,
transform: transform,
default: null as any,
desc: { desc: {
'ja-JP': 'フォルダID' 'ja-JP': 'フォルダID'
} }
}), },
isSensitive: $.bool.optional.note({ isSensitive: {
validator: $.bool.optional,
default: false, default: false,
desc: { desc: {
'ja-JP': 'このメディアが「閲覧注意」(NSFW)かどうか', 'ja-JP': 'このメディアが「閲覧注意」(NSFW)かどうか',
'en-US': 'Whether this media is NSFW' 'en-US': 'Whether this media is NSFW'
} }
}), },
force: $.bool.optional.note({ force: {
validator: $.bool.optional,
default: false, default: false,
desc: { desc: {
'ja-JP': 'true にすると、同じハッシュを持つファイルが既にアップロードされていても強制的にファイルを作成します。', 'ja-JP': 'true にすると、同じハッシュを持つファイルが既にアップロードされていても強制的にファイルを作成します。',
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFile from '../../../../../models/drive-file'; import DriveFile from '../../../../../models/drive-file';
import del from '../../../../../services/drive/delete-file'; import del from '../../../../../services/drive/delete-file';
import { publishDriveStream } from '../../../../../stream'; import { publishDriveStream } from '../../../../../stream';
@ -18,12 +18,14 @@ export const meta = {
kind: 'drive-write', kind: 'drive-write',
params: { params: {
fileId: $.type(ID).note({ fileId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のファイルID', 'ja-JP': '対象のファイルID',
'en-US': 'Target file ID' 'en-US': 'Target file ID'
} }
}) }
} }
}; };

View File

@ -1,31 +1,39 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFile, { pack } from '../../../../../models/drive-file'; import DriveFile, { pack } from '../../../../../models/drive-file';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params';
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
kind: 'drive-read' kind: 'drive-read',
params: {
name: {
validator: $.str
},
folderId: {
validator: $.type(ID).optional.nullable,
transform: transform,
default: null as any,
desc: {
'ja-JP': 'フォルダID'
}
},
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'name' parameter const [ps, psErr] = getParams(meta, params);
const [name, nameErr] = $.str.get(params.name); if (psErr) return rej(psErr);
if (nameErr) return rej('invalid name param');
// Get 'folderId' parameter
const [folderId = null, folderIdErr] = $.type(ID).optional.nullable.get(params.folderId);
if (folderIdErr) return rej('invalid folderId param');
// Issue query
const files = await DriveFile const files = await DriveFile
.find({ .find({
filename: name, filename: name,
'metadata.userId': user._id, 'metadata.userId': user._id,
'metadata.folderId': folderId 'metadata.folderId': ps.folderId
}); });
// Serialize res(await Promise.all(files.map(file => pack(file))));
res(await Promise.all(files.map(async file =>
await pack(file))));
}); });

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFile, { pack } from '../../../../../models/drive-file'; import DriveFile, { pack } from '../../../../../models/drive-file';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params'; import getParams from '../../../get-params';
@ -16,12 +16,14 @@ export const meta = {
kind: 'drive-read', kind: 'drive-read',
params: { params: {
fileId: $.type(ID).note({ fileId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のファイルID', 'ja-JP': '対象のファイルID',
'en-US': 'Target file ID' 'en-US': 'Target file ID'
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder from '../../../../../models/drive-folder'; import DriveFolder from '../../../../../models/drive-folder';
import DriveFile, { validateFileName, pack } from '../../../../../models/drive-file'; import DriveFile, { validateFileName, pack } from '../../../../../models/drive-file';
import { publishDriveStream } from '../../../../../stream'; import { publishDriveStream } from '../../../../../stream';
@ -17,34 +17,40 @@ export const meta = {
kind: 'drive-write', kind: 'drive-write',
params: { params: {
fileId: $.type(ID).note({ fileId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のファイルID' 'ja-JP': '対象のファイルID'
} }
}), },
folderId: $.type(ID).optional.nullable.note({ folderId: {
default: undefined, validator: $.type(ID).optional.nullable,
transform: transform,
default: undefined as any,
desc: { desc: {
'ja-JP': 'フォルダID' 'ja-JP': 'フォルダID'
} }
}), },
name: $.str.optional.pipe(validateFileName).note({ name: {
default: undefined, validator: $.str.optional.pipe(validateFileName),
default: undefined as any,
desc: { desc: {
'ja-JP': 'ファイル名', 'ja-JP': 'ファイル名',
'en-US': 'Name of the file' 'en-US': 'Name of the file'
} }
}), },
isSensitive: $.bool.optional.note({ isSensitive: {
default: undefined, validator: $.bool.optional,
default: undefined as any,
desc: { desc: {
'ja-JP': 'このメディアが「閲覧注意」(NSFW)かどうか', 'ja-JP': 'このメディアが「閲覧注意」(NSFW)かどうか',
'en-US': 'Whether this media is NSFW' 'en-US': 'Whether this media is NSFW'
} }
}) }
} }
}; };

View File

@ -1,8 +1,9 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
const ms = require('ms'); const ms = require('ms');
import { pack } from '../../../../../models/drive-file'; import { pack } from '../../../../../models/drive-file';
import uploadFromUrl from '../../../../../services/drive/upload-from-url'; import uploadFromUrl from '../../../../../services/drive/upload-from-url';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -16,21 +17,25 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'drive-write' kind: 'drive-write',
params: {
url: {
// TODO: Validate this url
validator: $.str,
},
folderId: {
validator: $.type(ID).optional.nullable,
default: null as any as any,
transform: transform
},
}
}; };
/**
* Create a file from a URL
*/
export default async (params: any, user: ILocalUser): Promise<any> => { export default async (params: any, user: ILocalUser): Promise<any> => {
// Get 'url' parameter const [ps, psErr] = getParams(meta, params);
// TODO: Validate this url if (psErr) throw psErr;
const [url, urlErr] = $.str.get(params.url);
if (urlErr) throw 'invalid url param';
// Get 'folderId' parameter return pack(await uploadFromUrl(ps.url, user, ps.folderId));
const [folderId = null, folderIdErr] = $.type(ID).optional.nullable.get(params.folderId);
if (folderIdErr) throw 'invalid folderId param';
return pack(await uploadFromUrl(url, user, folderId));
}; };

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import DriveFolder, { pack } from '../../../../models/drive-folder'; import DriveFolder, { pack } from '../../../../models/drive-folder';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -10,58 +11,64 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'drive-read' kind: 'drive-read',
params: {
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: {
validator: $.type(ID).optional,
transform: transform,
},
folderId: {
validator: $.type(ID).optional.nullable,
default: null as any,
transform: transform,
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'limit' parameter const [ps, psErr] = getParams(meta, params);
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); if (psErr) return rej(psErr);
if (limitErr) return rej('invalid limit param');
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) return rej('invalid sinceId param');
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) return rej('invalid untilId param');
// Check if both of sinceId and untilId is specified // Check if both of sinceId and untilId is specified
if (sinceId && untilId) { if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId'); return rej('cannot set sinceId and untilId');
} }
// Get 'folderId' parameter
const [folderId = null, folderIdErr] = $.type(ID).optional.nullable.get(params.folderId);
if (folderIdErr) return rej('invalid folderId param');
// Construct query
const sort = { const sort = {
_id: -1 _id: -1
}; };
const query = { const query = {
userId: user._id, userId: user._id,
parentId: folderId parentId: ps.folderId
} as any; } as any;
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
query._id = { query._id = {
$lt: untilId $lt: ps.untilId
}; };
} }
// Issue query
const folders = await DriveFolder const folders = await DriveFolder
.find(query, { .find(query, {
limit: limit, limit: ps.limit,
sort: sort sort: sort
}); });
// Serialize res(await Promise.all(folders.map(folder => pack(folder))));
res(await Promise.all(folders.map(async folder =>
await pack(folder))));
}); });

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder'; import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder';
import { publishDriveStream } from '../../../../../stream'; import { publishDriveStream } from '../../../../../stream';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
@ -17,20 +17,23 @@ export const meta = {
kind: 'drive-write', kind: 'drive-write',
params: { params: {
name: $.str.optional.pipe(isValidFolderName).note({ name: {
validator: $.str.optional.pipe(isValidFolderName),
default: 'Untitled', default: 'Untitled',
desc: { desc: {
'ja-JP': 'フォルダ名', 'ja-JP': 'フォルダ名',
'en-US': 'Folder name' 'en-US': 'Folder name'
} }
}), },
parentId: $.type(ID).optional.nullable.note({ parentId: {
validator: $.type(ID).optional.nullable,
transform: transform,
desc: { desc: {
'ja-JP': '親フォルダID', 'ja-JP': '親フォルダID',
'en-US': 'Parent folder ID' 'en-US': 'Parent folder ID'
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder from '../../../../../models/drive-folder'; import DriveFolder from '../../../../../models/drive-folder';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params'; import getParams from '../../../get-params';
@ -18,12 +18,14 @@ export const meta = {
kind: 'drive-write', kind: 'drive-write',
params: { params: {
folderId: $.type(ID).note({ folderId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のフォルダID', 'ja-JP': '対象のフォルダID',
'en-US': 'Target folder ID' 'en-US': 'Target folder ID'
} }
}) }
} }
}; };

View File

@ -1,30 +1,39 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder, { pack } from '../../../../../models/drive-folder'; import DriveFolder, { pack } from '../../../../../models/drive-folder';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params';
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
kind: 'drive-read' kind: 'drive-read',
params: {
name: {
validator: $.str
},
parentId: {
validator: $.type(ID).optional.nullable,
transform: transform,
default: null as any,
desc: {
'ja-JP': 'フォルダID'
}
},
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'name' parameter const [ps, psErr] = getParams(meta, params);
const [name, nameErr] = $.str.get(params.name); if (psErr) return rej(psErr);
if (nameErr) return rej('invalid name param');
// Get 'parentId' parameter
const [parentId = null, parentIdErr] = $.type(ID).optional.nullable.get(params.parentId);
if (parentIdErr) return rej('invalid parentId param');
// Issue query
const folders = await DriveFolder const folders = await DriveFolder
.find({ .find({
name: name, name: name,
userId: user._id, userId: user._id,
parentId: parentId parentId: ps.parentId
}); });
// Serialize
res(await Promise.all(folders.map(folder => pack(folder)))); res(await Promise.all(folders.map(folder => pack(folder))));
}); });

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder, { pack } from '../../../../../models/drive-folder'; import DriveFolder, { pack } from '../../../../../models/drive-folder';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params'; import getParams from '../../../get-params';
@ -16,12 +16,14 @@ export const meta = {
kind: 'drive-read', kind: 'drive-read',
params: { params: {
folderId: $.type(ID).note({ folderId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のフォルダID', 'ja-JP': '対象のフォルダID',
'en-US': 'Target folder ID' 'en-US': 'Target folder ID'
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder'; import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder';
import { publishDriveStream } from '../../../../../stream'; import { publishDriveStream } from '../../../../../stream';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
@ -17,26 +17,31 @@ export const meta = {
kind: 'drive-write', kind: 'drive-write',
params: { params: {
folderId: $.type(ID).note({ folderId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のフォルダID', 'ja-JP': '対象のフォルダID',
'en-US': 'Target folder ID' 'en-US': 'Target folder ID'
} }
}), },
name: $.str.optional.pipe(isValidFolderName).note({ name: {
validator: $.str.optional.pipe(isValidFolderName),
desc: { desc: {
'ja-JP': 'フォルダ名', 'ja-JP': 'フォルダ名',
'en-US': 'Folder name' 'en-US': 'Folder name'
} }
}), },
parentId: $.type(ID).optional.nullable.note({ parentId: {
validator: $.type(ID).optional.nullable,
transform: transform,
desc: { desc: {
'ja-JP': '親フォルダID', 'ja-JP': '親フォルダID',
'en-US': 'Parent folder ID' 'en-US': 'Parent folder ID'
} }
}) }
} }
}; };

View File

@ -1,36 +1,44 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import DriveFile, { packMany } from '../../../../models/drive-file'; import DriveFile, { packMany } from '../../../../models/drive-file';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
kind: 'drive-read' kind: 'drive-read',
params: {
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: {
validator: $.type(ID).optional,
transform: transform,
},
type: {
validator: $.str.optional.match(/^[a-zA-Z\/\-\*]+$/)
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'limit' parameter const [ps, psErr] = getParams(meta, params);
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); if (psErr) return rej(psErr);
if (limitErr) return rej('invalid limit param');
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) return rej('invalid sinceId param');
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) return rej('invalid untilId param');
// Check if both of sinceId and untilId is specified // Check if both of sinceId and untilId is specified
if (sinceId && untilId) { if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId'); return rej('cannot set sinceId and untilId');
} }
// Get 'type' parameter
const [type, typeErr] = $.str.optional.match(/^[a-zA-Z\/\-\*]+$/).get(params.type);
if (typeErr) return rej('invalid type param');
// Construct query
const sort = { const sort = {
_id: -1 _id: -1
}; };
@ -40,28 +48,26 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
'metadata.deletedAt': { $exists: false } 'metadata.deletedAt': { $exists: false }
} as any; } as any;
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
query._id = { query._id = {
$lt: untilId $lt: ps.untilId
}; };
} }
if (type) { if (ps.type) {
query.contentType = new RegExp(`^${type.replace(/\*/g, '.+?')}$`); query.contentType = new RegExp(`^${ps.type.replace(/\*/g, '.+?')}$`);
} }
// Issue query
const files = await DriveFile const files = await DriveFile
.find(query, { .find(query, {
limit: limit, limit: ps.limit,
sort: sort sort: sort
}); });
// Serialize
res(await packMany(files)); res(await packMany(files));
}); });

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
const ms = require('ms'); const ms = require('ms');
import User, { pack, ILocalUser } from '../../../../models/user'; import User, { pack, ILocalUser } from '../../../../models/user';
import Following from '../../../../models/following'; import Following from '../../../../models/following';
@ -23,12 +23,14 @@ export const meta = {
kind: 'following-write', kind: 'following-write',
params: { params: {
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーのID', 'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID' 'en-US': 'Target user ID'
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
const ms = require('ms'); const ms = require('ms');
import User, { pack, ILocalUser } from '../../../../models/user'; import User, { pack, ILocalUser } from '../../../../models/user';
import Following from '../../../../models/following'; import Following from '../../../../models/following';
@ -23,12 +23,14 @@ export const meta = {
kind: 'following-write', kind: 'following-write',
params: { params: {
userId: $.type(ID).note({ userId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象のユーザーのID', 'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID' 'en-US': 'Target user ID'
} }
}) }
} }
}; };

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import acceptFollowRequest from '../../../../../services/following/requests/accept'; import acceptFollowRequest from '../../../../../services/following/requests/accept';
import User, { ILocalUser } from '../../../../../models/user'; import User, { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -10,17 +11,23 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'following-write' kind: 'following-write',
params: {
userId: {
validator: $.type(ID),
transform: transform,
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'userId' parameter const [ps, psErr] = getParams(meta, params);
const [followerId, followerIdErr] = $.type(ID).get(params.userId); if (psErr) return rej(psErr);
if (followerIdErr) return rej('invalid userId param');
// Fetch follower // Fetch follower
const follower = await User.findOne({ const follower = await User.findOne({
_id: followerId _id: ps.userId
}); });
if (follower === null) { if (follower === null) {
@ -29,6 +36,5 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
await acceptFollowRequest(user, follower); await acceptFollowRequest(user, follower);
// Send response
res(); res();
}); });

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import cancelFollowRequest from '../../../../../services/following/requests/cancel'; import cancelFollowRequest from '../../../../../services/following/requests/cancel';
import User, { pack, ILocalUser } from '../../../../../models/user'; import User, { pack, ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -10,17 +11,23 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'following-write' kind: 'following-write',
params: {
userId: {
validator: $.type(ID),
transform: transform,
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'userId' parameter const [ps, psErr] = getParams(meta, params);
const [followeeId, followeeIdErr] = $.type(ID).get(params.userId); if (psErr) return rej(psErr);
if (followeeIdErr) return rej('invalid userId param');
// Fetch followee // Fetch followee
const followee = await User.findOne({ const followee = await User.findOne({
_id: followeeId _id: ps.userId
}); });
if (followee === null) { if (followee === null) {
@ -33,6 +40,5 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
return rej(e); return rej(e);
} }
// Send response
res(await pack(followee._id, user)); res(await pack(followee._id, user));
}); });

View File

@ -1,4 +1,4 @@
//import $ from 'cafy'; import ID from '../../../../../cafy-id'; //import $ from 'cafy'; import ID, { transform } from '../../../../../cafy-id';
import FollowRequest, { pack } from '../../../../../models/follow-request'; import FollowRequest, { pack } from '../../../../../models/follow-request';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import rejectFollowRequest from '../../../../../services/following/requests/reject'; import rejectFollowRequest from '../../../../../services/following/requests/reject';
import User, { ILocalUser } from '../../../../../models/user'; import User, { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -10,17 +11,23 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'following-write' kind: 'following-write',
params: {
userId: {
validator: $.type(ID),
transform: transform,
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'userId' parameter const [ps, psErr] = getParams(meta, params);
const [followerId, followerIdErr] = $.type(ID).get(params.userId); if (psErr) return rej(psErr);
if (followerIdErr) return rej('invalid userId param');
// Fetch follower // Fetch follower
const follower = await User.findOne({ const follower = await User.findOne({
_id: followerId _id: ps.userId
}); });
if (follower === null) { if (follower === null) {
@ -29,6 +36,5 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
await rejectFollowRequest(user, follower); await rejectFollowRequest(user, follower);
// Send response
res(); res();
}); });

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Following from '../../../../models/following'; import Following from '../../../../models/following';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -10,20 +11,26 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'following-write' kind: 'following-write',
params: {
userId: {
validator: $.type(ID),
transform: transform,
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
const follower = user; const [ps, psErr] = getParams(meta, params);
if (psErr) return rej(psErr);
// Get 'userId' parameter const follower = user;
const [userId, userIdErr] = $.type(ID).get(params.userId);
if (userIdErr) return rej('invalid userId param');
// Fetch following // Fetch following
const following = await Following.findOne({ const following = await Following.findOne({
followerId: follower._id, followerId: follower._id,
followeeId: userId followeeId: ps.userId
}); });
if (following === null) { if (following === null) {
@ -37,7 +44,6 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
} }
}); });
// Send response
res(); res();
// TODO: イベント // TODO: イベント

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Following from '../../../../models/following'; import Following from '../../../../models/following';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -10,20 +11,26 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'following-write' kind: 'following-write',
params: {
userId: {
validator: $.type(ID),
transform: transform,
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
const follower = user; const [ps, psErr] = getParams(meta, params);
if (psErr) return rej(psErr);
// Get 'userId' parameter const follower = user;
const [userId, userIdErr] = $.type(ID).get(params.userId);
if (userIdErr) return rej('invalid userId param');
// Fetch following // Fetch following
const following = await Following.findOne({ const following = await Following.findOne({
followerId: follower._id, followerId: follower._id,
followeeId: userId followeeId: ps.userId
}); });
if (following === null) { if (following === null) {
@ -37,7 +44,6 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
} }
}); });
// Send response
res(); res();
// TODO: イベント // TODO: イベント

View File

@ -1,33 +1,42 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import ReversiGame, { pack } from '../../../../../models/games/reversi/game'; import ReversiGame, { pack } from '../../../../../models/games/reversi/game';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params';
export const meta = { export const meta = {
params: {
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: {
validator: $.type(ID).optional,
transform: transform,
},
my: {
validator: $.bool.optional,
default: false
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'my' parameter const [ps, psErr] = getParams(meta, params);
const [my = false, myErr] = $.bool.optional.get(params.my); if (psErr) return rej(psErr);
if (myErr) return rej('invalid my param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) return rej('invalid sinceId param');
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) return rej('invalid untilId param');
// Check if both of sinceId and untilId is specified // Check if both of sinceId and untilId is specified
if (sinceId && untilId) { if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId'); return rej('cannot set sinceId and untilId');
} }
const q: any = my ? { const q: any = ps.my ? {
isStarted: true, isStarted: true,
$or: [{ $or: [{
user1Id: user._id user1Id: user._id
@ -42,21 +51,21 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
_id: -1 _id: -1
}; };
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
q._id = { q._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
q._id = { q._id = {
$lt: untilId $lt: ps.untilId
}; };
} }
// Fetch games // Fetch games
const games = await ReversiGame.find(q, { const games = await ReversiGame.find(q, {
sort, sort: sort,
limit limit: ps.limit
}); });
// Reponse // Reponse

View File

@ -1,14 +1,23 @@
import $ from 'cafy'; import ID from '../../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../../misc/cafy-id';
import ReversiGame, { pack } from '../../../../../../models/games/reversi/game'; import ReversiGame, { pack } from '../../../../../../models/games/reversi/game';
import Reversi from '../../../../../../games/reversi/core'; import Reversi from '../../../../../../games/reversi/core';
import { ILocalUser } from '../../../../../../models/user'; import { ILocalUser } from '../../../../../../models/user';
import getParams from '../../../../get-params';
export const meta = {
params: {
gameId: {
validator: $.type(ID),
transform: transform,
},
}
};
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'gameId' parameter const [ps, psErr] = getParams(meta, params);
const [gameId, gameIdErr] = $.type(ID).get(params.gameId); if (psErr) return rej(psErr);
if (gameIdErr) return rej('invalid gameId param');
const game = await ReversiGame.findOne({ _id: gameId }); const game = await ReversiGame.findOne({ _id: ps.gameId });
if (game == null) { if (game == null) {
return rej('game not found'); return rej('game not found');

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../../misc/cafy-id';
import ReversiGame, { pack } from '../../../../../../models/games/reversi/game'; import ReversiGame, { pack } from '../../../../../../models/games/reversi/game';
import { ILocalUser } from '../../../../../../models/user'; import { ILocalUser } from '../../../../../../models/user';
import getParams from '../../../../get-params'; import getParams from '../../../../get-params';
@ -12,11 +12,13 @@ export const meta = {
requireCredential: true, requireCredential: true,
params: { params: {
gameId: $.type(ID).note({ gameId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '投了したい対局' 'ja-JP': '投了したい対局'
} }
}) }
} }
}; };

View File

@ -1,27 +1,34 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import Matching, { pack as packMatching } from '../../../../../models/games/reversi/matching'; import Matching, { pack as packMatching } from '../../../../../models/games/reversi/matching';
import ReversiGame, { pack as packGame } from '../../../../../models/games/reversi/game'; import ReversiGame, { pack as packGame } from '../../../../../models/games/reversi/game';
import User, { ILocalUser } from '../../../../../models/user'; import User, { ILocalUser } from '../../../../../models/user';
import { publishMainStream, publishReversiStream } from '../../../../../stream'; import { publishMainStream, publishReversiStream } from '../../../../../stream';
import { eighteight } from '../../../../../games/reversi/maps'; import { eighteight } from '../../../../../games/reversi/maps';
import getParams from '../../../get-params';
export const meta = { export const meta = {
requireCredential: true requireCredential: true,
params: {
userId: {
validator: $.type(ID),
transform: transform,
},
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'userId' parameter const [ps, psErr] = getParams(meta, params);
const [childId, childIdErr] = $.type(ID).get(params.userId); if (psErr) return rej(psErr);
if (childIdErr) return rej('invalid userId param');
// Myself // Myself
if (childId.equals(user._id)) { if (ps.userId.equals(user._id)) {
return rej('invalid userId param'); return rej('invalid userId param');
} }
// Find session // Find session
const exist = await Matching.findOne({ const exist = await Matching.findOne({
parentId: childId, parentId: ps.userId,
childId: user._id childId: user._id
}); });
@ -63,7 +70,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
} else { } else {
// Fetch child // Fetch child
const child = await User.findOne({ const child = await User.findOne({
_id: childId _id: ps.userId
}, { }, {
fields: { fields: {
_id: true _id: true

View File

@ -11,25 +11,28 @@ export const meta = {
requireCredential: false, requireCredential: false,
params: { params: {
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 10, default: 10,
desc: { desc: {
'ja-JP': '最大数' 'ja-JP': '最大数'
} }
}), },
query: $.str.note({ query: {
validator: $.str,
desc: { desc: {
'ja-JP': 'クエリ' 'ja-JP': 'クエリ'
} }
}), },
offset: $.num.optional.min(0).note({ offset: {
validator: $.num.optional.min(0),
default: 0, default: 0,
desc: { desc: {
'ja-JP': 'オフセット' 'ja-JP': 'オフセット'
} }
}) }
} }
}; };

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Favorite, { packMany } from '../../../../models/favorite'; import Favorite, { packMany } from '../../../../models/favorite';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -10,24 +11,32 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'favorites-read' kind: 'favorites-read',
params: {
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: {
validator: $.type(ID).optional,
transform: transform,
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'limit' parameter const [ps, psErr] = getParams(meta, params);
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); if (psErr) return rej(psErr);
if (limitErr) return rej('invalid limit param');
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) return rej('invalid sinceId param');
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) return rej('invalid untilId param');
// Check if both of sinceId and untilId is specified // Check if both of sinceId and untilId is specified
if (sinceId && untilId) { if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId'); return rej('cannot set sinceId and untilId');
} }
@ -39,21 +48,23 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
_id: -1 _id: -1
}; };
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
query._id = { query._id = {
$lt: untilId $lt: ps.untilId
}; };
} }
// Get favorites // Get favorites
const favorites = await Favorite const favorites = await Favorite
.find(query, { limit, sort }); .find(query, {
limit: ps.limit,
sort: sort
});
// Serialize
res(await packMany(favorites, user)); res(await packMany(favorites, user));
}); });

View File

@ -1,38 +1,56 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Notification from '../../../../models/notification'; import Notification from '../../../../models/notification';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import { packMany } from '../../../../models/notification'; import { packMany } from '../../../../models/notification';
import { getFriendIds } from '../../common/get-friends'; import { getFriendIds } from '../../common/get-friends';
import read from '../../common/read-notification'; import read from '../../common/read-notification';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = {
desc: {
'ja-JP': '通知一覧を取得します。',
'en-US': 'Get notifications.'
},
requireCredential: true,
kind: 'account-read',
params: {
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: {
validator: $.type(ID).optional,
transform: transform,
},
following: {
validator: $.bool.optional,
default: false
},
markAsRead: {
validator: $.bool.optional,
default: true
}
}
};
/**
* Get notifications
*/
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'following' parameter const [ps, psErr] = getParams(meta, params);
const [following = false, followingError] = if (psErr) return rej(psErr);
$.bool.optional.get(params.following);
if (followingError) return rej('invalid following param');
// Get 'markAsRead' parameter
const [markAsRead = true, markAsReadErr] = $.bool.optional.get(params.markAsRead);
if (markAsReadErr) return rej('invalid markAsRead param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) return rej('invalid sinceId param');
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) return rej('invalid untilId param');
// Check if both of sinceId and untilId is specified // Check if both of sinceId and untilId is specified
if (sinceId && untilId) { if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId'); return rej('cannot set sinceId and untilId');
} }
@ -53,7 +71,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
_id: -1 _id: -1
}; };
if (following) { if (ps.following) {
// ID list of the user itself and other users who the user follows // ID list of the user itself and other users who the user follows
const followingIds = await getFriendIds(user._id); const followingIds = await getFriendIds(user._id);
@ -64,29 +82,27 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
}); });
} }
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
query._id = { query._id = {
$lt: untilId $lt: ps.untilId
}; };
} }
// Issue query
const notifications = await Notification const notifications = await Notification
.find(query, { .find(query, {
limit: limit, limit: ps.limit,
sort: sort sort: sort
}); });
// Serialize
res(await packMany(notifications)); res(await packMany(notifications));
// Mark all as read // Mark all as read
if (notifications.length > 0 && markAsRead) { if (notifications.length > 0 && ps.markAsRead) {
read(user._id, notifications); read(user._id, notifications);
} }
}); });

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import { pack } from '../../../../models/user'; import { pack } from '../../../../models/user';
import { addPinned } from '../../../../services/i/pin'; import { addPinned } from '../../../../services/i/pin';
@ -16,12 +16,14 @@ export const meta = {
kind: 'account-write', kind: 'account-write',
params: { params: {
noteId: $.type(ID).note({ noteId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象の投稿のID', 'ja-JP': '対象の投稿のID',
'en-US': 'Target note ID' 'en-US': 'Target note ID'
} }
}) }
} }
}; };

View File

@ -1,27 +1,37 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Signin, { pack } from '../../../../models/signin'; import Signin, { pack } from '../../../../models/signin';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
secure: true
secure: true,
params: {
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: {
validator: $.type(ID).optional,
transform: transform,
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'limit' parameter const [ps, psErr] = getParams(meta, params);
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); if (psErr) return rej(psErr);
if (limitErr) return rej('invalid limit param');
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) return rej('invalid sinceId param');
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) return rej('invalid untilId param');
// Check if both of sinceId and untilId is specified // Check if both of sinceId and untilId is specified
if (sinceId && untilId) { if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId'); return rej('cannot set sinceId and untilId');
} }
@ -33,25 +43,23 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
_id: -1 _id: -1
}; };
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
query._id = { query._id = {
$lt: untilId $lt: ps.untilId
}; };
} }
// Issue query
const history = await Signin const history = await Signin
.find(query, { .find(query, {
limit: limit, limit: ps.limit,
sort: sort sort: sort
}); });
// Serialize // Serialize
res(await Promise.all(history.map(async record => res(await Promise.all(history.map(record => pack(record))));
await pack(record))));
}); });

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import { pack } from '../../../../models/user'; import { pack } from '../../../../models/user';
import { removePinned } from '../../../../services/i/pin'; import { removePinned } from '../../../../services/i/pin';
@ -16,12 +16,14 @@ export const meta = {
kind: 'account-write', kind: 'account-write',
params: { params: {
noteId: $.type(ID).note({ noteId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象の投稿のID', 'ja-JP': '対象の投稿のID',
'en-US': 'Target note ID' 'en-US': 'Target note ID'
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import User, { isValidName, isValidDescription, isValidLocation, isValidBirthday, pack, ILocalUser } from '../../../../models/user'; import User, { isValidName, isValidDescription, isValidLocation, isValidBirthday, pack, ILocalUser } from '../../../../models/user';
import { publishMainStream } from '../../../../stream'; import { publishMainStream } from '../../../../stream';
import DriveFile from '../../../../models/drive-file'; import DriveFile from '../../../../models/drive-file';
@ -19,83 +19,99 @@ export const meta = {
kind: 'account-write', kind: 'account-write',
params: { params: {
name: $.str.optional.nullable.pipe(isValidName).note({ name: {
validator: $.str.optional.nullable.pipe(isValidName),
desc: { desc: {
'ja-JP': '名前(ハンドルネームやニックネーム)' 'ja-JP': '名前(ハンドルネームやニックネーム)'
} }
}), },
description: $.str.optional.nullable.pipe(isValidDescription).note({ description: {
validator: $.str.optional.nullable.pipe(isValidDescription),
desc: { desc: {
'ja-JP': 'アカウントの説明や自己紹介' 'ja-JP': 'アカウントの説明や自己紹介'
} }
}), },
location: $.str.optional.nullable.pipe(isValidLocation).note({ location: {
validator: $.str.optional.nullable.pipe(isValidLocation),
desc: { desc: {
'ja-JP': '住んでいる地域、所在' 'ja-JP': '住んでいる地域、所在'
} }
}), },
birthday: $.str.optional.nullable.pipe(isValidBirthday).note({ birthday: {
validator: $.str.optional.nullable.pipe(isValidBirthday),
desc: { desc: {
'ja-JP': '誕生日 (YYYY-MM-DD形式)' 'ja-JP': '誕生日 (YYYY-MM-DD形式)'
} }
}), },
avatarId: $.type(ID).optional.nullable.note({ avatarId: {
validator: $.type(ID).optional.nullable,
transform: transform,
desc: { desc: {
'ja-JP': 'アイコンに設定する画像のドライブファイルID' 'ja-JP': 'アイコンに設定する画像のドライブファイルID'
} }
}), },
bannerId: $.type(ID).optional.nullable.note({ bannerId: {
validator: $.type(ID).optional.nullable,
transform: transform,
desc: { desc: {
'ja-JP': 'バナーに設定する画像のドライブファイルID' 'ja-JP': 'バナーに設定する画像のドライブファイルID'
} }
}), },
wallpaperId: $.type(ID).optional.nullable.note({ wallpaperId: {
validator: $.type(ID).optional.nullable,
transform: transform,
desc: { desc: {
'ja-JP': '壁紙に設定する画像のドライブファイルID' 'ja-JP': '壁紙に設定する画像のドライブファイルID'
} }
}), },
isLocked: $.bool.optional.note({ isLocked: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': '鍵アカウントか否か' 'ja-JP': '鍵アカウントか否か'
} }
}), },
carefulBot: $.bool.optional.note({ carefulBot: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'Botからのフォローを承認制にするか' 'ja-JP': 'Botからのフォローを承認制にするか'
} }
}), },
isBot: $.bool.optional.note({ isBot: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'Botか否か' 'ja-JP': 'Botか否か'
} }
}), },
isCat: $.bool.optional.note({ isCat: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': '猫か否か' 'ja-JP': '猫か否か'
} }
}), },
autoWatch: $.bool.optional.note({ autoWatch: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': '投稿の自動ウォッチをするか否か' 'ja-JP': '投稿の自動ウォッチをするか否か'
} }
}), },
alwaysMarkNsfw: $.bool.optional.note({ alwaysMarkNsfw: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'アップロードするメディアをデフォルトで「閲覧注意」として設定するか' 'ja-JP': 'アップロードするメディアをデフォルトで「閲覧注意」として設定するか'
} }
}), },
} }
}; };

View File

@ -1,8 +1,9 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Message from '../../../../models/messaging-message'; import Message from '../../../../models/messaging-message';
import User, { ILocalUser } from '../../../../models/user'; import User, { ILocalUser } from '../../../../models/user';
import { pack } from '../../../../models/messaging-message'; import { pack } from '../../../../models/messaging-message';
import read from '../../common/read-messaging-message'; import read from '../../common/read-messaging-message';
import getParams from '../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -12,17 +13,48 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'messaging-read' kind: 'messaging-read',
params: {
userId: {
validator: $.type(ID),
transform: transform,
},
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: {
validator: $.type(ID).optional,
transform: transform,
},
markAsRead: {
validator: $.bool.optional,
default: true
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'userId' parameter const [ps, psErr] = getParams(meta, params);
const [recipientId, recipientIdErr] = $.type(ID).get(params.userId); if (psErr) return rej(psErr);
if (recipientIdErr) return rej('invalid userId param');
// Check if both of sinceId and untilId is specified
if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId');
}
// Fetch recipient // Fetch recipient
const recipient = await User.findOne({ const recipient = await User.findOne({
_id: recipientId _id: ps.userId
}, { }, {
fields: { fields: {
_id: true _id: true
@ -33,27 +65,6 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
return rej('user not found'); return rej('user not found');
} }
// Get 'markAsRead' parameter
const [markAsRead = true, markAsReadErr] = $.bool.optional.get(params.markAsRead);
if (markAsReadErr) return rej('invalid markAsRead param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) return rej('invalid sinceId param');
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) return rej('invalid untilId param');
// Check if both of sinceId and untilId is specified
if (sinceId && untilId) {
return rej('cannot set sinceId and untilId');
}
const query = { const query = {
$or: [{ $or: [{
userId: user._id, userId: user._id,
@ -68,36 +79,33 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
_id: -1 _id: -1
}; };
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
query._id = { query._id = {
$lt: untilId $lt: ps.untilId
}; };
} }
// Issue query
const messages = await Message const messages = await Message
.find(query, { .find(query, {
limit: limit, limit: ps.limit,
sort: sort sort: sort
}); });
// Serialize res(await Promise.all(messages.map(message => pack(message, user, {
res(await Promise.all(messages.map(async message => populateRecipient: false
await pack(message, user, { }))));
populateRecipient: false
}))));
if (messages.length === 0) { if (messages.length === 0) {
return; return;
} }
// Mark all as read // Mark all as read
if (markAsRead) { if (ps.markAsRead) {
read(user._id, recipient._id, messages); read(user._id, recipient._id, messages);
} }
}); });

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import Message from '../../../../../models/messaging-message'; import Message from '../../../../../models/messaging-message';
import { isValidText } from '../../../../../models/messaging-message'; import { isValidText } from '../../../../../models/messaging-message';
import History from '../../../../../models/messaging-history'; import History from '../../../../../models/messaging-history';
@ -9,6 +9,7 @@ import { pack } from '../../../../../models/messaging-message';
import { publishMainStream } from '../../../../../stream'; import { publishMainStream } from '../../../../../stream';
import { publishMessagingStream, publishMessagingIndexStream } from '../../../../../stream'; import { publishMessagingStream, publishMessagingIndexStream } from '../../../../../stream';
import pushSw from '../../../../../push-sw'; import pushSw from '../../../../../push-sw';
import getParams from '../../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -18,22 +19,37 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'messaging-write' kind: 'messaging-write',
params: {
userId: {
validator: $.type(ID),
transform: transform,
},
text: {
validator: $.str.optional.pipe(isValidText)
},
fileId: {
validator: $.type(ID).optional,
transform: transform,
}
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'userId' parameter const [ps, psErr] = getParams(meta, params);
const [recipientId, recipientIdErr] = $.type(ID).get(params.userId); if (psErr) return rej(psErr);
if (recipientIdErr) return rej('invalid userId param');
// Myself // Myself
if (recipientId.equals(user._id)) { if (ps.userId.equals(user._id)) {
return rej('cannot send message to myself'); return rej('cannot send message to myself');
} }
// Fetch recipient // Fetch recipient
const recipient = await User.findOne({ const recipient = await User.findOne({
_id: recipientId _id: ps.userId
}, { }, {
fields: { fields: {
_id: true _id: true
@ -44,18 +60,10 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
return rej('user not found'); return rej('user not found');
} }
// Get 'text' parameter
const [text, textErr] = $.str.optional.pipe(isValidText).get(params.text);
if (textErr) return rej('invalid text');
// Get 'fileId' parameter
const [fileId, fileIdErr] = $.type(ID).optional.get(params.fileId);
if (fileIdErr) return rej('invalid fileId param');
let file = null; let file = null;
if (fileId !== undefined) { if (ps.fileId != null) {
file = await DriveFile.findOne({ file = await DriveFile.findOne({
_id: fileId, _id: ps.fileId,
'metadata.userId': user._id 'metadata.userId': user._id
}); });
@ -65,7 +73,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
} }
// テキストが無いかつ添付ファイルも無かったらエラー // テキストが無いかつ添付ファイルも無かったらエラー
if (text === undefined && file === null) { if (ps.text == null && file == null) {
return rej('text or file is required'); return rej('text or file is required');
} }
@ -74,7 +82,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
createdAt: new Date(), createdAt: new Date(),
fileId: file ? file._id : undefined, fileId: file ? file._id : undefined,
recipientId: recipient._id, recipientId: recipient._id,
text: text ? text.trim() : undefined, text: ps.text ? ps.text.trim() : undefined,
userId: user._id, userId: user._id,
isRead: false isRead: false
}); });

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import Message from '../../../../../models/messaging-message'; import Message from '../../../../../models/messaging-message';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import read from '../../../common/read-messaging-message'; import read from '../../../common/read-messaging-message';
@ -15,12 +15,14 @@ export const meta = {
kind: 'messaging-write', kind: 'messaging-write',
params: { params: {
messageId: $.type(ID).note({ messageId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '既読にするメッセージのID', 'ja-JP': '既読にするメッセージのID',
'en-US': 'The ID of a message that you want to mark as read' 'en-US': 'The ID of a message that you want to mark as read'
} }
}) }
} }
}; };

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import User, { ILocalUser } from '../../../../models/user'; import User, { ILocalUser } from '../../../../models/user';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import getParams from '../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -10,24 +11,30 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'account/write' kind: 'account/write',
params: {
userId: {
validator: $.type(ID),
transform: transform,
},
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
const [ps, psErr] = getParams(meta, params);
if (psErr) return rej(psErr);
const muter = user; const muter = user;
// Get 'userId' parameter
const [userId, userIdErr] = $.type(ID).get(params.userId);
if (userIdErr) return rej('invalid userId param');
// 自分自身 // 自分自身
if (user._id.equals(userId)) { if (user._id.equals(ps.userId)) {
return rej('mutee is yourself'); return rej('mutee is yourself');
} }
// Get mutee // Get mutee
const mutee = await User.findOne({ const mutee = await User.findOne({
_id: userId _id: ps.userId
}, { }, {
fields: { fields: {
data: false, data: false,
@ -56,6 +63,5 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
muteeId: mutee._id, muteeId: mutee._id,
}); });
// Send response
res(); res();
}); });

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import User, { ILocalUser } from '../../../../models/user'; import User, { ILocalUser } from '../../../../models/user';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import getParams from '../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -10,24 +11,30 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'account/write' kind: 'account/write',
params: {
userId: {
validator: $.type(ID),
transform: transform,
},
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
const [ps, psErr] = getParams(meta, params);
if (psErr) return rej(psErr);
const muter = user; const muter = user;
// Get 'userId' parameter
const [userId, userIdErr] = $.type(ID).get(params.userId);
if (userIdErr) return rej('invalid userId param');
// Check if the mutee is yourself // Check if the mutee is yourself
if (user._id.equals(userId)) { if (user._id.equals(ps.userId)) {
return rej('mutee is yourself'); return rej('mutee is yourself');
} }
// Get mutee // Get mutee
const mutee = await User.findOne({ const mutee = await User.findOne({
_id: userId _id: ps.userId
}, { }, {
fields: { fields: {
data: false, data: false,
@ -54,6 +61,5 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
_id: exist._id _id: exist._id
}); });
// Send response
res(); res();
}); });

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Mute, { packMany } from '../../../../models/mute'; import Mute, { packMany } from '../../../../models/mute';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params'; import getParams from '../../get-params';
@ -14,15 +14,20 @@ export const meta = {
kind: 'account/read', kind: 'account/read',
params: { params: {
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 30 default: 30
}), },
sinceId: $.type(ID).optional.note({ sinceId: {
}), validator: $.type(ID).optional,
transform: transform,
},
untilId: $.type(ID).optional.note({ untilId: {
}), validator: $.type(ID).optional,
transform: transform,
},
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../misc/cafy-id';
import Note, { packMany } from '../../../models/note'; import Note, { packMany } from '../../../models/note';
import getParams from '../get-params'; import getParams from '../get-params';
@ -8,49 +8,62 @@ export const meta = {
}, },
params: { params: {
local: $.bool.optional.note({ local: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'ローカルの投稿に限定するか否か' 'ja-JP': 'ローカルの投稿に限定するか否か'
} }
}), },
reply: $.bool.optional.note({ reply: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': '返信に限定するか否か' 'ja-JP': '返信に限定するか否か'
} }
}), },
renote: $.bool.optional.note({ renote: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'Renoteに限定するか否か' 'ja-JP': 'Renoteに限定するか否か'
} }
}), },
withFiles: $.bool.optional.note({ withFiles: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か' 'ja-JP': 'ファイルが添付された投稿に限定するか否か'
} }
}), },
media: $.bool.optional.note({ media: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' 'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
} }
}), },
poll: $.bool.optional.note({ poll: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'アンケートが添付された投稿に限定するか否か' 'ja-JP': 'アンケートが添付された投稿に限定するか否か'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 10 default: 10
}), },
sinceId: $.type(ID).optional.note({}), sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: $.type(ID).optional.note({}), untilId: {
validator: $.type(ID).optional,
transform: transform,
},
} }
}; };

View File

@ -1,26 +1,41 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note, { packMany, INote } from '../../../../models/note'; import Note, { packMany, INote } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = {
desc: {
'ja-JP': '指定した投稿の文脈を取得します。',
'en-US': 'Show conversation of a note.'
},
requireCredential: false,
params: {
noteId: {
validator: $.type(ID),
transform: transform,
},
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
offset: {
validator: $.num.optional.min(0),
default: 0
},
}
};
/**
* Show conversation of a note
*/
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'noteId' parameter const [ps, psErr] = getParams(meta, params);
const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (psErr) return rej(psErr);
if (noteIdErr) return rej('invalid noteId param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Get 'offset' parameter
const [offset = 0, offsetErr] = $.num.optional.min(0).get(params.offset);
if (offsetErr) return rej('invalid offset param');
// Lookup note // Lookup note
const note = await Note.findOne({ const note = await Note.findOne({
_id: noteId _id: ps.noteId
}); });
if (note === null) { if (note === null) {
@ -34,11 +49,11 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
i++; i++;
const p = await Note.findOne({ _id: id }); const p = await Note.findOne({ _id: id });
if (i > offset) { if (i > ps.offset) {
conversation.push(p); conversation.push(p);
} }
if (conversation.length == limit) { if (conversation.length == ps.limit) {
return; return;
} }
@ -51,6 +66,5 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
await get(note.replyId); await get(note.replyId);
} }
// Serialize
res(await packMany(conversation, user)); res(await packMany(conversation, user));
}); });

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform, transformMany } from '../../../../misc/cafy-id';
const ms = require('ms'); const ms = require('ms');
import Note, { INote, isValidText, isValidCw, pack } from '../../../../models/note'; import Note, { INote, isValidText, isValidCw, pack } from '../../../../models/note';
import User, { ILocalUser, IUser } from '../../../../models/user'; import User, { ILocalUser, IUser } from '../../../../models/user';
@ -24,84 +24,106 @@ export const meta = {
kind: 'note-write', kind: 'note-write',
params: { params: {
visibility: $.str.optional.or(['public', 'home', 'followers', 'specified', 'private']).note({ visibility: {
validator: $.str.optional.or(['public', 'home', 'followers', 'specified', 'private']),
default: 'public', default: 'public',
desc: { desc: {
'ja-JP': '投稿の公開範囲' 'ja-JP': '投稿の公開範囲'
} }
}), },
visibleUserIds: $.arr($.type(ID)).optional.unique().min(1).note({ visibleUserIds: {
validator: $.arr($.type(ID)).optional.unique().min(1),
transform: transformMany,
desc: { desc: {
'ja-JP': '(投稿の公開範囲が specified の場合)投稿を閲覧できるユーザー' 'ja-JP': '(投稿の公開範囲が specified の場合)投稿を閲覧できるユーザー'
} }
}), },
text: $.str.optional.nullable.pipe(isValidText).note({ text: {
default: null, validator: $.str.optional.nullable.pipe(isValidText),
default: null as any,
desc: { desc: {
'ja-JP': '投稿内容' 'ja-JP': '投稿内容'
} }
}), },
cw: $.str.optional.nullable.pipe(isValidCw).note({ cw: {
validator: $.str.optional.nullable.pipe(isValidCw),
desc: { desc: {
'ja-JP': 'コンテンツの警告。このパラメータを指定すると設定したテキストで投稿のコンテンツを隠す事が出来ます。' 'ja-JP': 'コンテンツの警告。このパラメータを指定すると設定したテキストで投稿のコンテンツを隠す事が出来ます。'
} }
}), },
viaMobile: $.bool.optional.note({ viaMobile: {
validator: $.bool.optional,
default: false, default: false,
desc: { desc: {
'ja-JP': 'モバイルデバイスからの投稿か否か。' 'ja-JP': 'モバイルデバイスからの投稿か否か。'
} }
}), },
geo: $.obj({ geo: {
coordinates: $.arr().length(2) validator: $.obj({
.item(0, $.num.range(-180, 180)) coordinates: $.arr().length(2)
.item(1, $.num.range(-90, 90)), .item(0, $.num.range(-180, 180))
altitude: $.num.nullable, .item(1, $.num.range(-90, 90)),
accuracy: $.num.nullable, altitude: $.num.nullable,
altitudeAccuracy: $.num.nullable, accuracy: $.num.nullable,
heading: $.num.nullable.range(0, 360), altitudeAccuracy: $.num.nullable,
speed: $.num.nullable heading: $.num.nullable.range(0, 360),
}).optional.nullable.strict().note({ speed: $.num.nullable
}).optional.nullable.strict(),
desc: { desc: {
'ja-JP': '位置情報' 'ja-JP': '位置情報'
}, },
ref: 'geo' ref: 'geo'
}), },
fileIds: $.arr($.type(ID)).optional.unique().range(1, 4).note({ fileIds: {
validator: $.arr($.type(ID)).optional.unique().range(1, 4),
transform: transformMany,
desc: { desc: {
'ja-JP': '添付するファイル' 'ja-JP': '添付するファイル'
} }
}), },
mediaIds: $.arr($.type(ID)).optional.unique().range(1, 4).note({ mediaIds: {
validator: $.arr($.type(ID)).optional.unique().range(1, 4),
transform: transformMany,
desc: { desc: {
'ja-JP': '添付するファイル (このパラメータは廃止予定です。代わりに fileIds を使ってください。)' 'ja-JP': '添付するファイル (このパラメータは廃止予定です。代わりに fileIds を使ってください。)'
} }
}), },
renoteId: $.type(ID).optional.note({ replyId: {
validator: $.type(ID).optional,
transform: transform,
desc: {
'ja-JP': '返信対象'
}
},
renoteId: {
validator: $.type(ID).optional,
transform: transform,
desc: { desc: {
'ja-JP': 'Renote対象' 'ja-JP': 'Renote対象'
} }
}), },
poll: $.obj({ poll: {
choices: $.arr($.str) validator: $.obj({
.unique() choices: $.arr($.str)
.range(2, 10) .unique()
.each(c => c.length > 0 && c.length < 50) .range(2, 10)
}).optional.strict().note({ .each(c => c.length > 0 && c.length < 50)
}).optional.strict(),
desc: { desc: {
'ja-JP': 'アンケート' 'ja-JP': 'アンケート'
}, },
ref: 'poll' ref: 'poll'
}) }
}, },
res: { res: {
@ -117,15 +139,12 @@ export const meta = {
} }
}; };
/**
* Create a note
*/
export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (res, rej) => {
const [ps, psErr] = getParams(meta, params); const [ps, psErr] = getParams(meta, params);
if (psErr) return rej(psErr); if (psErr) return rej(psErr);
let visibleUsers: IUser[] = []; let visibleUsers: IUser[] = [];
if (ps.visibleUserIds !== undefined) { if (ps.visibleUserIds) {
visibleUsers = await Promise.all(ps.visibleUserIds.map(id => User.findOne({ visibleUsers = await Promise.all(ps.visibleUserIds.map(id => User.findOne({
_id: id _id: id
}))); })));
@ -145,7 +164,7 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (
} }
let renote: INote = null; let renote: INote = null;
if (ps.renoteId !== undefined) { if (ps.renoteId != null) {
// Fetch renote to note // Fetch renote to note
renote = await Note.findOne({ renote = await Note.findOne({
_id: ps.renoteId _id: ps.renoteId
@ -158,15 +177,11 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (
} }
} }
// Get 'replyId' parameter
const [replyId, replyIdErr] = $.type(ID).optional.get(params.replyId);
if (replyIdErr) return rej('invalid replyId');
let reply: INote = null; let reply: INote = null;
if (replyId !== undefined) { if (ps.replyId != null) {
// Fetch reply // Fetch reply
reply = await Note.findOne({ reply = await Note.findOne({
_id: replyId _id: ps.replyId
}); });
if (reply === null) { if (reply === null) {
@ -188,7 +203,7 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (
} }
// テキストが無いかつ添付ファイルが無いかつRenoteも無いかつ投票も無かったらエラー // テキストが無いかつ添付ファイルが無いかつRenoteも無いかつ投票も無かったらエラー
if ((ps.text === undefined || ps.text === null) && files === null && renote === null && ps.poll === undefined) { if ((ps.text == null) && files === null && renote === null && ps.poll == null) {
return rej('text, fileIds, renoteId or poll is required'); return rej('text, fileIds, renoteId or poll is required');
} }

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note from '../../../../models/note'; import Note from '../../../../models/note';
import deleteNote from '../../../../services/note/delete'; import deleteNote from '../../../../services/note/delete';
import User, { ILocalUser } from '../../../../models/user'; import User, { ILocalUser } from '../../../../models/user';
@ -17,12 +17,14 @@ export const meta = {
kind: 'note-write', kind: 'note-write',
params: { params: {
noteId: $.type(ID).note({ noteId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象の投稿のID', 'ja-JP': '対象の投稿のID',
'en-US': 'Target note ID.' 'en-US': 'Target note ID.'
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import Favorite from '../../../../../models/favorite'; import Favorite from '../../../../../models/favorite';
import Note from '../../../../../models/note'; import Note from '../../../../../models/note';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
@ -17,12 +17,14 @@ export const meta = {
kind: 'favorite-write', kind: 'favorite-write',
params: { params: {
noteId: $.type(ID).note({ noteId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象の投稿のID', 'ja-JP': '対象の投稿のID',
'en-US': 'Target note ID.' 'en-US': 'Target note ID.'
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import Favorite from '../../../../../models/favorite'; import Favorite from '../../../../../models/favorite';
import Note from '../../../../../models/note'; import Note from '../../../../../models/note';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
@ -17,12 +17,14 @@ export const meta = {
kind: 'favorite-write', kind: 'favorite-write',
params: { params: {
noteId: $.type(ID).note({ noteId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象の投稿のID', 'ja-JP': '対象の投稿のID',
'en-US': 'Target note ID.' 'en-US': 'Target note ID.'
} }
}) }
} }
}; };

View File

@ -13,12 +13,13 @@ export const meta = {
requireCredential: false, requireCredential: false,
params: { params: {
limit: $.num.optional.range(1, 30).note({ limit: {
validator: $.num.optional.range(1, 30),
default: 10, default: 10,
desc: { desc: {
'ja-JP': '最大数' 'ja-JP': '最大数'
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note from '../../../../models/note'; import Note from '../../../../models/note';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import { packMany } from '../../../../models/note'; import { packMany } from '../../../../models/note';
@ -12,29 +12,42 @@ export const meta = {
}, },
params: { params: {
withFiles: $.bool.optional.note({ withFiles: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か' 'ja-JP': 'ファイルが添付された投稿に限定するか否か'
} }
}), },
mediaOnly: $.bool.optional.note({ mediaOnly: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' 'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 10 default: 10
}), },
sinceId: $.type(ID).optional.note({}), sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: $.type(ID).optional.note({}), untilId: {
validator: $.type(ID).optional,
transform: transform,
},
sinceDate: $.num.optional.note({}), sinceDate: {
validator: $.num.optional
},
untilDate: $.num.optional.note({}), untilDate: {
validator: $.num.optional
},
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note from '../../../../models/note'; import Note from '../../../../models/note';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import { getFriends } from '../../common/get-friends'; import { getFriends } from '../../common/get-friends';
@ -13,69 +13,81 @@ export const meta = {
}, },
params: { params: {
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 10, default: 10,
desc: { desc: {
'ja-JP': '最大数' 'ja-JP': '最大数'
} }
}), },
sinceId: $.type(ID).optional.note({ sinceId: {
validator: $.type(ID).optional,
transform: transform,
desc: { desc: {
'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します' 'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します'
} }
}), },
untilId: $.type(ID).optional.note({ untilId: {
validator: $.type(ID).optional,
transform: transform,
desc: { desc: {
'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します' 'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
} }
}), },
sinceDate: $.num.optional.note({ sinceDate: {
validator: $.num.optional,
desc: { desc: {
'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。' 'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
} }
}), },
untilDate: $.num.optional.note({ untilDate: {
validator: $.num.optional,
desc: { desc: {
'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。' 'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
} }
}), },
includeMyRenotes: $.bool.optional.note({ includeMyRenotes: {
validator: $.bool.optional,
default: true, default: true,
desc: { desc: {
'ja-JP': '自分の行ったRenoteを含めるかどうか' 'ja-JP': '自分の行ったRenoteを含めるかどうか'
} }
}), },
includeRenotedMyNotes: $.bool.optional.note({ includeRenotedMyNotes: {
validator: $.bool.optional,
default: true, default: true,
desc: { desc: {
'ja-JP': 'Renoteされた自分の投稿を含めるかどうか' 'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
} }
}), },
includeLocalRenotes: $.bool.optional.note({ includeLocalRenotes: {
validator: $.bool.optional,
default: true, default: true,
desc: { desc: {
'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか' 'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
} }
}), },
withFiles: $.bool.optional.note({ withFiles: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します' 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
} }
}), },
mediaOnly: $.bool.optional.note({ mediaOnly: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
} }
}), },
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note from '../../../../models/note'; import Note from '../../../../models/note';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import { packMany } from '../../../../models/note'; import { packMany } from '../../../../models/note';
@ -12,42 +12,57 @@ export const meta = {
}, },
params: { params: {
withFiles: $.bool.optional.note({ withFiles: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か' 'ja-JP': 'ファイルが添付された投稿に限定するか否か'
} }
}), },
mediaOnly: $.bool.optional.note({ mediaOnly: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' 'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
} }
}), },
fileType: $.arr($.str).optional.note({ fileType: {
validator: $.arr($.str).optional,
desc: { desc: {
'ja-JP': '指定された種類のファイルが添付された投稿のみを取得します' 'ja-JP': '指定された種類のファイルが添付された投稿のみを取得します'
} }
}), },
excludeNsfw: $.bool.optional.note({ excludeNsfw: {
validator: $.bool.optional,
default: false, default: false,
desc: { desc: {
'ja-JP': 'true にすると、NSFW指定されたファイルを除外します(fileTypeが指定されている場合のみ有効)' 'ja-JP': 'true にすると、NSFW指定されたファイルを除外します(fileTypeが指定されている場合のみ有効)'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 10 default: 10
}), },
sinceId: $.type(ID).optional.note({}), sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: $.type(ID).optional.note({}), untilId: {
validator: $.type(ID).optional,
transform: transform,
},
sinceDate: $.num.optional.note({}), sinceDate: {
validator: $.num.optional,
},
untilDate: $.num.optional.note({}), untilDate: {
validator: $.num.optional,
},
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note from '../../../../models/note'; import Note from '../../../../models/note';
import { getFriendIds } from '../../common/get-friends'; import { getFriendIds } from '../../common/get-friends';
import { packMany } from '../../../../models/note'; import { packMany } from '../../../../models/note';
@ -15,22 +15,29 @@ export const meta = {
requireCredential: true, requireCredential: true,
params: { params: {
following: $.bool.optional.note({ following: {
validator: $.bool.optional,
default: false default: false
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 10 default: 10
}), },
sinceId: $.type(ID).optional.note({ sinceId: {
}), validator: $.type(ID).optional,
transform: transform,
},
untilId: $.type(ID).optional.note({ untilId: {
}), validator: $.type(ID).optional,
transform: transform,
},
visibility: $.str.optional.note({ visibility: {
}), validator: $.str.optional,
},
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import Vote from '../../../../../models/poll-vote'; import Vote from '../../../../../models/poll-vote';
import Note from '../../../../../models/note'; import Note from '../../../../../models/note';
import Watching from '../../../../../models/note-watching'; import Watching from '../../../../../models/note-watching';
@ -6,6 +6,7 @@ import watch from '../../../../../services/note/watch';
import { publishNoteStream } from '../../../../../stream'; import { publishNoteStream } from '../../../../../stream';
import notify from '../../../../../notify'; import notify from '../../../../../notify';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -15,17 +16,27 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'vote-write' kind: 'vote-write',
params: {
noteId: {
validator: $.type(ID),
transform: transform,
},
choice: {
validator: $.num
},
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'noteId' parameter const [ps, psErr] = getParams(meta, params);
const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (psErr) return rej(psErr);
if (noteIdErr) return rej('invalid noteId param');
// Get votee // Get votee
const note = await Note.findOne({ const note = await Note.findOne({
_id: noteId _id: ps.noteId
}); });
if (note === null) { if (note === null) {
@ -36,12 +47,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
return rej('poll not found'); return rej('poll not found');
} }
// Get 'choice' parameter if (!note.poll.choices.some(x => x.id == ps.choice)) return rej('invalid choice param');
const [choice, choiceError] =
$.num
.pipe(c => note.poll.choices.some(x => x.id == c))
.get(params.choice);
if (choiceError) return rej('invalid choice param');
// if already voted // if already voted
const exist = await Vote.findOne({ const exist = await Vote.findOne({
@ -58,14 +64,14 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
createdAt: new Date(), createdAt: new Date(),
noteId: note._id, noteId: note._id,
userId: user._id, userId: user._id,
choice: choice choice: ps.choice
}); });
// Send response // Send response
res(); res();
const inc: any = {}; const inc: any = {};
inc[`poll.choices.${note.poll.choices.findIndex(c => c.id == choice)}.votes`] = 1; inc[`poll.choices.${note.poll.choices.findIndex(c => c.id == ps.choice)}.votes`] = 1;
// Increment votes count // Increment votes count
await Note.update({ _id: note._id }, { await Note.update({ _id: note._id }, {
@ -73,14 +79,14 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
}); });
publishNoteStream(note._id, 'pollVoted', { publishNoteStream(note._id, 'pollVoted', {
choice: choice, choice: ps.choice,
userId: user._id.toHexString() userId: user._id.toHexString()
}); });
// Notify // Notify
notify(note.userId, user._id, 'poll_vote', { notify(note.userId, user._id, 'poll_vote', {
noteId: note._id, noteId: note._id,
choice: choice choice: ps.choice
}); });
// Fetch watchers // Fetch watchers
@ -99,7 +105,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
watchers.forEach(watcher => { watchers.forEach(watcher => {
notify(watcher.userId, user._id, 'poll_vote', { notify(watcher.userId, user._id, 'poll_vote', {
noteId: note._id, noteId: note._id,
choice: choice choice: ps.choice
}); });
}); });
}); });

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note from '../../../../models/note'; import Note from '../../../../models/note';
import Reaction, { pack } from '../../../../models/note-reaction'; import Reaction, { pack } from '../../../../models/note-reaction';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
@ -13,26 +13,34 @@ export const meta = {
requireCredential: false, requireCredential: false,
params: { params: {
noteId: $.type(ID).note({ noteId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象の投稿のID', 'ja-JP': '対象の投稿のID',
'en-US': 'The ID of the target note' 'en-US': 'The ID of the target note'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 10 default: 10
}), },
offset: $.num.optional.note({ offset: {
validator: $.num.optional,
default: 0 default: 0
}), },
sinceId: $.type(ID).optional.note({ sinceId: {
}), validator: $.type(ID).optional,
transform: transform,
},
untilId: $.type(ID).optional.note({ untilId: {
}), validator: $.type(ID).optional,
transform: transform,
},
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import Note from '../../../../../models/note'; import Note from '../../../../../models/note';
import create from '../../../../../services/note/reaction/create'; import create from '../../../../../services/note/reaction/create';
import { validateReaction } from '../../../../../models/note-reaction'; import { validateReaction } from '../../../../../models/note-reaction';
@ -18,17 +18,20 @@ export const meta = {
kind: 'reaction-write', kind: 'reaction-write',
params: { params: {
noteId: $.type(ID).note({ noteId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象の投稿' 'ja-JP': '対象の投稿'
} }
}), },
reaction: $.str.pipe(validateReaction.ok).note({ reaction: {
validator: $.str.pipe(validateReaction.ok),
desc: { desc: {
'ja-JP': 'リアクションの種類' 'ja-JP': 'リアクションの種類'
} }
}) }
} }
}; };

View File

@ -1,7 +1,8 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import Reaction from '../../../../../models/note-reaction'; import Reaction from '../../../../../models/note-reaction';
import Note from '../../../../../models/note'; import Note from '../../../../../models/note';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -11,17 +12,23 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'reaction-write' kind: 'reaction-write',
params: {
noteId: {
validator: $.type(ID),
transform: transform,
},
}
}; };
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'noteId' parameter const [ps, psErr] = getParams(meta, params);
const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (psErr) return rej(psErr);
if (noteIdErr) return rej('invalid noteId param');
// Fetch unreactee // Fetch unreactee
const note = await Note.findOne({ const note = await Note.findOne({
_id: noteId _id: ps.noteId
}); });
if (note === null) { if (note === null) {
@ -48,7 +55,6 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
} }
}); });
// Send response
res(); res();
const dec: any = {}; const dec: any = {};

View File

@ -0,0 +1,81 @@
import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note, { packMany } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = {
desc: {
'ja-JP': '指定した投稿のRenote一覧を取得します。',
'en-US': 'Show a renotes of a note.'
},
requireCredential: false,
params: {
noteId: {
validator: $.type(ID),
transform: transform,
},
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
sinceId: {
validator: $.type(ID).optional,
transform: transform,
},
untilId: {
validator: $.type(ID).optional,
transform: transform,
}
}
};
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
const [ps, psErr] = getParams(meta, params);
if (psErr) return rej(psErr);
// Check if both of sinceId and untilId is specified
if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId');
}
// Lookup note
const note = await Note.findOne({
_id: ps.noteId
});
if (note === null) {
return rej('note not found');
}
const sort = {
_id: -1
};
const query = {
renoteId: note._id
} as any;
if (ps.sinceId) {
sort._id = 1;
query._id = {
$gt: ps.sinceId
};
} else if (ps.untilId) {
query._id = {
$lt: ps.untilId
};
}
const renotes = await Note
.find(query, {
limit: ps.limit,
sort: sort
});
res(await packMany(renotes, user));
});

View File

@ -1,34 +1,48 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note, { packMany } from '../../../../models/note'; import Note, { packMany } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = {
desc: {
'ja-JP': '指定した投稿への返信を取得します。',
'en-US': 'Get replies of a note.'
},
requireCredential: false,
params: {
noteId: {
validator: $.type(ID),
transform: transform,
},
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
offset: {
validator: $.num.optional.min(0),
default: 0
},
}
};
/**
* Get replies of a note
*/
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'noteId' parameter const [ps, psErr] = getParams(meta, params);
const [noteId, noteIdErr] = $.type(ID).get(params.noteId); if (psErr) return rej(psErr);
if (noteIdErr) return rej('invalid noteId param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Get 'offset' parameter
const [offset = 0, offsetErr] = $.num.optional.min(0).get(params.offset);
if (offsetErr) return rej('invalid offset param');
// Lookup note // Lookup note
const note = await Note.findOne({ const note = await Note.findOne({
_id: noteId _id: ps.noteId
}); });
if (note === null) { if (note === null) {
return rej('note not found'); return rej('note not found');
} }
const ids = (note._replyIds || []).slice(offset, offset + limit); const ids = (note._replyIds || []).slice(ps.offset, ps.offset + ps.limit);
// Serialize
res(await packMany(ids, user)); res(await packMany(ids, user));
}); });

View File

@ -1,66 +0,0 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
import Note, { packMany } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user';
/**
* Show a renotes of a note
*/
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'noteId' parameter
const [noteId, noteIdErr] = $.type(ID).get(params.noteId);
if (noteIdErr) return rej('invalid noteId param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) return rej('invalid sinceId param');
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) return rej('invalid untilId param');
// Check if both of sinceId and untilId is specified
if (sinceId && untilId) {
return rej('cannot set sinceId and untilId');
}
// Lookup note
const note = await Note.findOne({
_id: noteId
});
if (note === null) {
return rej('note not found');
}
// Construct query
const sort = {
_id: -1
};
const query = {
renoteId: note._id
} as any;
if (sinceId) {
sort._id = 1;
query._id = {
$gt: sinceId
};
} else if (untilId) {
query._id = {
$lt: untilId
};
}
// Issue query
const renotes = await Note
.find(query, {
limit: limit,
sort: sort
});
// Serialize
res(await packMany(renotes, user));
});

View File

@ -1,11 +1,10 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note from '../../../../models/note'; import Note from '../../../../models/note';
import User, { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import { getFriendIds } from '../../common/get-friends'; import { getFriendIds } from '../../common/get-friends';
import { packMany } from '../../../../models/note'; import { packMany } from '../../../../models/note';
import getParams from '../../get-params'; import getParams from '../../get-params';
import { erase } from '../../../../prelude/array';
export const meta = { export const meta = {
desc: { desc: {
@ -13,99 +12,94 @@ export const meta = {
}, },
params: { params: {
tag: $.str.optional.note({ tag: {
validator: $.str.optional,
desc: { desc: {
'ja-JP': 'タグ' 'ja-JP': 'タグ'
} }
}), },
query: $.arr($.arr($.str)).optional.note({ query: {
validator: $.arr($.arr($.str)).optional,
desc: { desc: {
'ja-JP': 'クエリ' 'ja-JP': 'クエリ'
} }
}), },
includeUserIds: $.arr($.type(ID)).optional.note({ following: {
default: [] validator: $.bool.optional.nullable,
}), default: null as any
},
excludeUserIds: $.arr($.type(ID)).optional.note({ mute: {
default: [] validator: $.str.optional,
}),
includeUserUsernames: $.arr($.str).optional.note({
default: []
}),
excludeUserUsernames: $.arr($.str).optional.note({
default: []
}),
following: $.bool.optional.nullable.note({
default: null
}),
mute: $.str.optional.note({
default: 'mute_all' default: 'mute_all'
}), },
reply: $.bool.optional.nullable.note({
default: null,
reply: {
validator: $.bool.optional.nullable,
default: null as any,
desc: { desc: {
'ja-JP': '返信に限定するか否か' 'ja-JP': '返信に限定するか否か'
} }
}), },
renote: $.bool.optional.nullable.note({
default: null,
renote: {
validator: $.bool.optional.nullable,
default: null as any,
desc: { desc: {
'ja-JP': 'Renoteに限定するか否か' 'ja-JP': 'Renoteに限定するか否か'
} }
}), },
withFiles: $.bool.optional.note({ withFiles: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します' 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
} }
}), },
media: $.bool.optional.nullable.note({
default: null,
media: {
validator: $.bool.optional.nullable,
default: null as any,
desc: { desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' 'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
} }
}), },
poll: $.bool.optional.nullable.note({
default: null,
poll: {
validator: $.bool.optional.nullable,
default: null as any,
desc: { desc: {
'ja-JP': 'アンケートが添付された投稿に限定するか否か' 'ja-JP': 'アンケートが添付された投稿に限定するか否か'
} }
}), },
untilId: $.type(ID).optional.note({ untilId: {
validator: $.type(ID).optional,
transform: transform,
desc: { desc: {
'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します' 'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
} }
}), },
sinceDate: $.num.optional.note({ sinceDate: {
}), validator: $.num.optional,
},
untilDate: $.num.optional.note({ untilDate: {
}), validator: $.num.optional,
},
offset: $.num.optional.min(0).note({ offset: {
validator: $.num.optional.min(0),
default: 0 default: 0
}), },
limit: $.num.optional.range(1, 30).note({ limit: {
validator: $.num.optional.range(1, 30),
default: 10 default: 10
}), },
} }
}; };
@ -113,28 +107,6 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
const [ps, psErr] = getParams(meta, params); const [ps, psErr] = getParams(meta, params);
if (psErr) return rej(psErr); if (psErr) return rej(psErr);
if (ps.includeUserUsernames != null) {
const ids = erase(null, await Promise.all(ps.includeUserUsernames.map(async (username) => {
const _user = await User.findOne({
usernameLower: username.toLowerCase()
});
return _user ? _user._id : null;
})));
ids.forEach(id => ps.includeUserIds.push(id));
}
if (ps.excludeUserUsernames != null) {
const ids = erase(null, await Promise.all(ps.excludeUserUsernames.map(async (username) => {
const _user = await User.findOne({
usernameLower: username.toLowerCase()
});
return _user ? _user._id : null;
})));
ids.forEach(id => ps.excludeUserIds.push(id));
}
const q: any = { const q: any = {
$and: [ps.tag ? { $and: [ps.tag ? {
tagsLower: ps.tag.toLowerCase() tagsLower: ps.tag.toLowerCase()
@ -150,20 +122,6 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
const push = (x: any) => q.$and.push(x); const push = (x: any) => q.$and.push(x);
if (ps.includeUserIds && ps.includeUserIds.length != 0) {
push({
userId: {
$in: ps.includeUserIds
}
});
} else if (ps.excludeUserIds && ps.excludeUserIds.length != 0) {
push({
userId: {
$nin: ps.excludeUserIds
}
});
}
if (ps.following != null && me != null) { if (ps.following != null && me != null) {
const ids = await getFriendIds(me._id, false); const ids = await getFriendIds(me._id, false);
push({ push({

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note, { pack } from '../../../../models/note'; import Note, { pack } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params'; import getParams from '../../get-params';
@ -14,12 +14,14 @@ export const meta = {
requireCredential: false, requireCredential: false,
params: { params: {
noteId: $.type(ID).note({ noteId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象の投稿のID', 'ja-JP': '対象の投稿のID',
'en-US': 'Target note ID.' 'en-US': 'Target note ID.'
} }
}) }
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note from '../../../../models/note'; import Note from '../../../../models/note';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import { getFriends } from '../../common/get-friends'; import { getFriends } from '../../common/get-friends';
@ -16,69 +16,81 @@ export const meta = {
requireCredential: true, requireCredential: true,
params: { params: {
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 10, default: 10,
desc: { desc: {
'ja-JP': '最大数' 'ja-JP': '最大数'
} }
}), },
sinceId: $.type(ID).optional.note({ sinceId: {
validator: $.type(ID).optional,
transform: transform,
desc: { desc: {
'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します' 'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します'
} }
}), },
untilId: $.type(ID).optional.note({ untilId: {
validator: $.type(ID).optional,
transform: transform,
desc: { desc: {
'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します' 'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
} }
}), },
sinceDate: $.num.optional.note({ sinceDate: {
validator: $.num.optional,
desc: { desc: {
'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。' 'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
} }
}), },
untilDate: $.num.optional.note({ untilDate: {
validator: $.num.optional,
desc: { desc: {
'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。' 'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
} }
}), },
includeMyRenotes: $.bool.optional.note({ includeMyRenotes: {
validator: $.bool.optional,
default: true, default: true,
desc: { desc: {
'ja-JP': '自分の行ったRenoteを含めるかどうか' 'ja-JP': '自分の行ったRenoteを含めるかどうか'
} }
}), },
includeRenotedMyNotes: $.bool.optional.note({ includeRenotedMyNotes: {
validator: $.bool.optional,
default: true, default: true,
desc: { desc: {
'ja-JP': 'Renoteされた自分の投稿を含めるかどうか' 'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
} }
}), },
includeLocalRenotes: $.bool.optional.note({ includeLocalRenotes: {
validator: $.bool.optional,
default: true, default: true,
desc: { desc: {
'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか' 'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
} }
}), },
withFiles: $.bool.optional.note({ withFiles: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します' 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
} }
}), },
mediaOnly: $.bool.optional.note({ mediaOnly: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
} }
}), },
} }
}; };

View File

@ -1,4 +1,4 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note from '../../../../models/note'; import Note from '../../../../models/note';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import { packMany } from '../../../../models/note'; import { packMany } from '../../../../models/note';
@ -15,75 +15,89 @@ export const meta = {
requireCredential: true, requireCredential: true,
params: { params: {
listId: $.type(ID).note({ listId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': 'リストのID' 'ja-JP': 'リストのID'
} }
}), },
limit: $.num.optional.range(1, 100).note({ limit: {
validator: $.num.optional.range(1, 100),
default: 10, default: 10,
desc: { desc: {
'ja-JP': '最大数' 'ja-JP': '最大数'
} }
}), },
sinceId: $.type(ID).optional.note({ sinceId: {
validator: $.type(ID).optional,
transform: transform,
desc: { desc: {
'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します' 'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します'
} }
}), },
untilId: $.type(ID).optional.note({ untilId: {
validator: $.type(ID).optional,
transform: transform,
desc: { desc: {
'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します' 'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
} }
}), },
sinceDate: $.num.optional.note({ sinceDate: {
validator: $.num.optional,
desc: { desc: {
'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。' 'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
} }
}), },
untilDate: $.num.optional.note({ untilDate: {
validator: $.num.optional,
desc: { desc: {
'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。' 'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
} }
}), },
includeMyRenotes: $.bool.optional.note({ includeMyRenotes: {
validator: $.bool.optional,
default: true, default: true,
desc: { desc: {
'ja-JP': '自分の行ったRenoteを含めるかどうか' 'ja-JP': '自分の行ったRenoteを含めるかどうか'
} }
}), },
includeRenotedMyNotes: $.bool.optional.note({ includeRenotedMyNotes: {
validator: $.bool.optional,
default: true, default: true,
desc: { desc: {
'ja-JP': 'Renoteされた自分の投稿を含めるかどうか' 'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
} }
}), },
includeLocalRenotes: $.bool.optional.note({ includeLocalRenotes: {
validator: $.bool.optional,
default: true, default: true,
desc: { desc: {
'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか' 'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
} }
}), },
withFiles: $.bool.optional.note({ withFiles: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します' 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
} }
}), },
mediaOnly: $.bool.optional.note({ mediaOnly: {
validator: $.bool.optional,
desc: { desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
} }
}), },
} }
}; };

View File

@ -1,32 +1,49 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import User, { ILocalUser } from '../../../../models/user'; import User, { ILocalUser } from '../../../../models/user';
import Following from '../../../../models/following'; import Following from '../../../../models/following';
import { pack } from '../../../../models/user'; import { pack } from '../../../../models/user';
import { getFriendIds } from '../../common/get-friends'; import { getFriendIds } from '../../common/get-friends';
import getParams from '../../get-params';
export const meta = {
desc: {
'ja-JP': '指定したユーザーのフォロワー一覧を取得します。',
'en-US': 'Get followers of a user.'
},
requireCredential: false,
params: {
userId: {
validator: $.type(ID),
transform: transform,
},
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
cursor: {
validator: $.type(ID).optional,
default: null as any,
transform: transform,
},
iknow: {
validator: $.bool.optional,
default: false,
}
}
};
/**
* Get followers of a user
*/
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
// Get 'userId' parameter const [ps, psErr] = getParams(meta, params);
const [userId, userIdErr] = $.type(ID).get(params.userId); if (psErr) return rej(psErr);
if (userIdErr) return rej('invalid userId param');
// Get 'iknow' parameter
const [iknow = false, iknowErr] = $.bool.optional.get(params.iknow);
if (iknowErr) return rej('invalid iknow param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Get 'cursor' parameter
const [cursor = null, cursorErr] = $.type(ID).optional.get(params.cursor);
if (cursorErr) return rej('invalid cursor param');
// Lookup user // Lookup user
const user = await User.findOne({ const user = await User.findOne({
_id: userId _id: ps.userId
}, { }, {
fields: { fields: {
_id: true _id: true
@ -43,7 +60,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
} as any; } as any;
// ログインしていてかつ iknow フラグがあるとき // ログインしていてかつ iknow フラグがあるとき
if (me && iknow) { if (me && ps.iknow) {
// Get my friends // Get my friends
const myFriends = await getFriendIds(me._id); const myFriends = await getFriendIds(me._id);
@ -53,29 +70,27 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
} }
// カーソルが指定されている場合 // カーソルが指定されている場合
if (cursor) { if (ps.cursor) {
query._id = { query._id = {
$lt: cursor $lt: ps.cursor
}; };
} }
// Get followers // Get followers
const following = await Following const following = await Following
.find(query, { .find(query, {
limit: limit + 1, limit: ps.limit + 1,
sort: { _id: -1 } sort: { _id: -1 }
}); });
// 「次のページ」があるかどうか // 「次のページ」があるかどうか
const inStock = following.length === limit + 1; const inStock = following.length === ps.limit + 1;
if (inStock) { if (inStock) {
following.pop(); following.pop();
} }
// Serialize
const users = await Promise.all(following.map(f => pack(f.followerId, me, { detail: true }))); const users = await Promise.all(following.map(f => pack(f.followerId, me, { detail: true })));
// Response
res({ res({
users: users, users: users,
next: inStock ? following[following.length - 1]._id : null, next: inStock ? following[following.length - 1]._id : null,

View File

@ -1,32 +1,49 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import User, { ILocalUser } from '../../../../models/user'; import User, { ILocalUser } from '../../../../models/user';
import Following from '../../../../models/following'; import Following from '../../../../models/following';
import { pack } from '../../../../models/user'; import { pack } from '../../../../models/user';
import { getFriendIds } from '../../common/get-friends'; import { getFriendIds } from '../../common/get-friends';
import getParams from '../../get-params';
export const meta = {
desc: {
'ja-JP': '指定したユーザーのフォロー一覧を取得します。',
'en-US': 'Get following users of a user.'
},
requireCredential: false,
params: {
userId: {
validator: $.type(ID),
transform: transform,
},
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
cursor: {
validator: $.type(ID).optional,
default: null as any,
transform: transform,
},
iknow: {
validator: $.bool.optional,
default: false,
}
}
};
/**
* Get following users of a user
*/
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
// Get 'userId' parameter const [ps, psErr] = getParams(meta, params);
const [userId, userIdErr] = $.type(ID).get(params.userId); if (psErr) return rej(psErr);
if (userIdErr) return rej('invalid userId param');
// Get 'iknow' parameter
const [iknow = false, iknowErr] = $.bool.optional.get(params.iknow);
if (iknowErr) return rej('invalid iknow param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Get 'cursor' parameter
const [cursor = null, cursorErr] = $.type(ID).optional.get(params.cursor);
if (cursorErr) return rej('invalid cursor param');
// Lookup user // Lookup user
const user = await User.findOne({ const user = await User.findOne({
_id: userId _id: ps.userId
}, { }, {
fields: { fields: {
_id: true _id: true
@ -43,7 +60,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
} as any; } as any;
// ログインしていてかつ iknow フラグがあるとき // ログインしていてかつ iknow フラグがあるとき
if (me && iknow) { if (me && ps.iknow) {
// Get my friends // Get my friends
const myFriends = await getFriendIds(me._id); const myFriends = await getFriendIds(me._id);
@ -53,29 +70,27 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
} }
// カーソルが指定されている場合 // カーソルが指定されている場合
if (cursor) { if (ps.cursor) {
query._id = { query._id = {
$lt: cursor $lt: ps.cursor
}; };
} }
// Get followers // Get followers
const following = await Following const following = await Following
.find(query, { .find(query, {
limit: limit + 1, limit: ps.limit + 1,
sort: { _id: -1 } sort: { _id: -1 }
}); });
// 「次のページ」があるかどうか // 「次のページ」があるかどうか
const inStock = following.length === limit + 1; const inStock = following.length === ps.limit + 1;
if (inStock) { if (inStock) {
following.pop(); following.pop();
} }
// Serialize
const users = await Promise.all(following.map(f => pack(f.followeeId, me, { detail: true }))); const users = await Promise.all(following.map(f => pack(f.followeeId, me, { detail: true })));
// Response
res({ res({
users: users, users: users,
next: inStock ? following[following.length - 1]._id : null, next: inStock ? following[following.length - 1]._id : null,

View File

@ -1,19 +1,31 @@
import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id';
import Note from '../../../../models/note'; import Note from '../../../../models/note';
import User, { pack, ILocalUser } from '../../../../models/user'; import User, { pack, ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = {
requireCredential: false,
params: {
userId: {
validator: $.type(ID),
transform: transform,
},
limit: {
validator: $.num.optional.range(1, 100),
default: 10
},
}
};
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
// Get 'userId' parameter const [ps, psErr] = getParams(meta, params);
const [userId, userIdErr] = $.type(ID).get(params.userId); if (psErr) return rej(psErr);
if (userIdErr) return rej('invalid userId param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Lookup user // Lookup user
const user = await User.findOne({ const user = await User.findOne({
_id: userId _id: ps.userId
}, { }, {
fields: { fields: {
_id: true _id: true
@ -83,7 +95,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]); const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]);
// Extract top replied users // Extract top replied users
const topRepliedUsers = repliedUsersSorted.slice(0, limit); const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit);
// Make replies object (includes weights) // Make replies object (includes weights)
const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({ const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({
@ -91,6 +103,5 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
weight: repliedUsers[user] / peak weight: repliedUsers[user] / peak
}))); })));
// Response
res(repliesObj); res(repliesObj);
}); });

View File

@ -1,5 +1,5 @@
import $ from 'cafy'; import $ from 'cafy';
import ID from '../../../../../misc/cafy-id'; import ID, { transform } from '../../../../../misc/cafy-id';
import UserList from '../../../../../models/user-list'; import UserList from '../../../../../models/user-list';
import { ILocalUser } from '../../../../../models/user'; import { ILocalUser } from '../../../../../models/user';
import getParams from '../../../get-params'; import getParams from '../../../get-params';
@ -15,12 +15,14 @@ export const meta = {
kind: 'account-write', kind: 'account-write',
params: { params: {
listId: $.type(ID).note({ listId: {
validator: $.type(ID),
transform: transform,
desc: { desc: {
'ja-JP': '対象となるユーザーリストのID', 'ja-JP': '対象となるユーザーリストのID',
'en-US': 'ID of target user list' 'en-US': 'ID of target user list'
} }
}) }
} }
}; };

View File

@ -1,10 +1,11 @@
import $ from 'cafy'; import ID from '../../../../../misc/cafy-id'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
import UserList from '../../../../../models/user-list'; import UserList from '../../../../../models/user-list';
import User, { pack as packUser, isRemoteUser, getGhost, ILocalUser } from '../../../../../models/user'; import User, { pack as packUser, isRemoteUser, getGhost, ILocalUser } from '../../../../../models/user';
import { publishUserListStream } from '../../../../../stream'; import { publishUserListStream } from '../../../../../stream';
import ap from '../../../../../remote/activitypub/renderer'; import ap from '../../../../../remote/activitypub/renderer';
import renderFollow from '../../../../../remote/activitypub/renderer/follow'; import renderFollow from '../../../../../remote/activitypub/renderer/follow';
import { deliver } from '../../../../../queue'; import { deliver } from '../../../../../queue';
import getParams from '../../../get-params';
export const meta = { export const meta = {
desc: { desc: {
@ -14,20 +15,28 @@ export const meta = {
requireCredential: true, requireCredential: true,
kind: 'account-write' kind: 'account-write',
params: {
listId: {
validator: $.type(ID),
transform: transform,
},
userId: {
validator: $.type(ID),
transform: transform,
},
}
}; };
/**
* Add a user to a user list
*/
export default async (params: any, me: ILocalUser) => new Promise(async (res, rej) => { export default async (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
// Get 'listId' parameter const [ps, psErr] = getParams(meta, params);
const [listId, listIdErr] = $.type(ID).get(params.listId); if (psErr) return rej(psErr);
if (listIdErr) return rej('invalid listId param');
// Fetch the list // Fetch the list
const userList = await UserList.findOne({ const userList = await UserList.findOne({
_id: listId, _id: ps.listId,
userId: me._id, userId: me._id,
}); });
@ -35,13 +44,9 @@ export default async (params: any, me: ILocalUser) => new Promise(async (res, re
return rej('list not found'); return rej('list not found');
} }
// Get 'userId' parameter
const [userId, userIdErr] = $.type(ID).get(params.userId);
if (userIdErr) return rej('invalid userId param');
// Fetch the user // Fetch the user
const user = await User.findOne({ const user = await User.findOne({
_id: userId _id: ps.userId
}); });
if (user == null) { if (user == null) {

Some files were not shown because too many files have changed in this diff Show More