Use PostgreSQL instead of MongoDB (#4572)

* wip

* Update note.ts

* Update timeline.ts

* Update core.ts

* wip

* Update generate-visibility-query.ts

* wip

* wip

* wip

* wip

* wip

* Update global-timeline.ts

* wip

* wip

* wip

* Update vote.ts

* wip

* wip

* Update create.ts

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update files.ts

* wip

* wip

* Update CONTRIBUTING.md

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update read-notification.ts

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update cancel.ts

* wip

* wip

* wip

* Update show.ts

* wip

* wip

* Update gen-id.ts

* Update create.ts

* Update id.ts

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Docker: Update files about Docker (#4599)

* Docker: Use cache if files used by `yarn install` was not updated

This patch reduces the number of times to installing node_modules.
For example, `yarn install` step will be skipped when only ".config/default.yml" is updated.

* Docker: Migrate MongoDB to Postgresql

Misskey uses Postgresql as a database instead of Mongodb since version 11.

* Docker: Uncomment about data persistence

This patch will save a lot of databases.

* wip

* wip

* wip

* Update activitypub.ts

* wip

* wip

* wip

* Update logs.ts

* wip

* Update drive-file.ts

* Update register.ts

* wip

* wip

* Update mentions.ts

* wip

* wip

* wip

* Update recommendation.ts

* wip

* Update index.ts

* wip

* Update recommendation.ts

* Doc: Update docker.ja.md and docker.en.md (#1) (#4608)

Update how to set up misskey.

* wip

* ✌️

* wip

* Update note.ts

* Update postgre.ts

* wip

* wip

* wip

* wip

* Update add-file.ts

* wip

* wip

* wip

* Clean up

* Update logs.ts

* wip

* 🍕

* wip

* Ad notes

* wip

* Update api-visibility.ts

* Update note.ts

* Update add-file.ts

* tests

* tests

* Update postgre.ts

* Update utils.ts

* wip

* wip

* Refactor

* wip

* Refactor

* wip

* wip

* Update show-users.ts

* Update update-instance.ts

* wip

* Update feed.ts

* Update outbox.ts

* Update outbox.ts

* Update user.ts

* wip

* Update list.ts

* Update update-hashtag.ts

* wip

* Update update-hashtag.ts

* Refactor

* Update update.ts

* wip

* wip

* ✌️

* clean up

* docs

* Update push.ts

* wip

* Update api.ts

* wip

* ✌️

* Update make-pagination-query.ts

* ✌️

* Delete hashtags.ts

* Update instances.ts

* Update instances.ts

* Update create.ts

* Update search.ts

* Update reversi-game.ts

* Update signup.ts

* Update user.ts

* id

* Update example.yml

* 🎨

* objectid

* fix

* reversi

* reversi

* Fix bug of chart engine

* Add test of chart engine

* Improve test

* Better testing

* Improve chart engine

* Refactor

* Add test of chart engine

* Refactor

* Add chart test

* Fix bug

* コミットし忘れ

* Refactoring

* ✌️

* Add tests

* Add test

* Extarct note tests

* Refactor

* 存在しないユーザーにメンションできなくなっていた問題を修正

* Fix bug

* Update update-meta.ts

* Fix bug

* Update mention.vue

* Fix bug

* Update meta.ts

* Update CONTRIBUTING.md

* Fix bug

* Fix bug

* Fix bug

* Clean up

* Clean up

* Update notification.ts

* Clean up

* Add mute tests

* Add test

* Refactor

* Add test

* Fix test

* Refactor

* Refactor

* Add tests

* Update utils.ts

* Update utils.ts

* Fix test

* Update package.json

* Update update.ts

* Update manifest.ts

* Fix bug

* Fix bug

* Add test

* 🎨

* Update endpoint permissions

* Updaye permisison

* Update person.ts

#4299

* データベースと同期しないように

* Fix bug

* Fix bug

* Update reversi-game.ts

* Use a feature of Node v11.7.0 to extract a public key (#4644)

* wip

* wip

* ✌️

* Refactoring

#1540

* test

* test

* test

* test

* test

* test

* test

* Fix bug

* Fix test

* 🍣

* wip

* #4471

* Add test for #4335

* Refactor

* Fix test

* Add tests

* 🕓

* Fix bug

* Add test

* Add test

* rename

* Fix bug
This commit is contained in:
syuilo
2019-04-07 21:50:36 +09:00
committed by GitHub
parent 13caf37991
commit f0a29721c9
592 changed files with 13463 additions and 14147 deletions

View File

@ -1,7 +1,8 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import Report, { packMany } from '../../../../models/abuse-user-report';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { AbuseUserReports } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
export const meta = {
tags: ['admin'],
@ -17,37 +18,18 @@ export const meta = {
sinceId: {
validator: $.optional.type(ID),
transform: transform,
},
untilId: {
validator: $.optional.type(ID),
transform: transform,
},
}
};
export default define(meta, async (ps) => {
const sort = {
_id: -1
};
const query = {} as any;
if (ps.sinceId) {
sort._id = 1;
query._id = {
$gt: ps.sinceId
};
} else if (ps.untilId) {
query._id = {
$lt: ps.untilId
};
}
const query = makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId);
const reports = await Report
.find(query, {
limit: ps.limit,
sort: sort
});
const reports = await query.take(ps.limit).getMany();
return await packMany(reports);
return await AbuseUserReports.packMany(reports);
});

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import File, { packMany } from '../../../../../models/drive-file';
import define from '../../../define';
import { fallback } from '../../../../../prelude/symbol';
import { DriveFiles } from '../../../../../models';
export const meta = {
tags: ['admin'],
@ -41,27 +41,25 @@ export const meta = {
};
const sort: any = { // < https://github.com/Microsoft/TypeScript/issues/1863
'+createdAt': { uploadDate: -1 },
'-createdAt': { uploadDate: 1 },
'+size': { length: -1 },
'-size': { length: 1 },
[fallback]: { _id: -1 }
'+createdAt': { createdAt: -1 },
'-createdAt': { createdAt: 1 },
'+size': { size: -1 },
'-size': { size: 1 },
[fallback]: { id: -1 }
};
export default define(meta, async (ps, me) => {
const q = {
'metadata.deletedAt': { $exists: false },
} as any;
const q = {} as any;
if (ps.origin == 'local') q['metadata._user.host'] = null;
if (ps.origin == 'remote') q['metadata._user.host'] = { $ne: null };
if (ps.origin == 'local') q['userHost'] = null;
if (ps.origin == 'remote') q['userHost'] = { $ne: null };
const files = await File
.find(q, {
limit: ps.limit,
sort: sort[ps.sort] || sort[fallback],
skip: ps.offset
});
const files = await DriveFiles.find({
where: q,
take: ps.limit,
order: sort[ps.sort] || sort[fallback],
skip: ps.offset
});
return await packMany(files, { detail: true, withUser: true, self: true });
return await DriveFiles.packMany(files, { detail: true, withUser: true, self: true });
});

View File

@ -1,8 +1,8 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import DriveFile from '../../../../../models/drive-file';
import { ApiError } from '../../../error';
import { DriveFiles } from '../../../../../models';
export const meta = {
tags: ['admin'],
@ -13,7 +13,6 @@ export const meta = {
params: {
fileId: {
validator: $.type(ID),
transform: transform,
},
},
@ -27,9 +26,7 @@ export const meta = {
};
export default define(meta, async (ps, me) => {
const file = await DriveFile.findOne({
_id: ps.fileId
});
const file = await DriveFiles.findOne(ps.fileId);
if (file == null) {
throw new ApiError(meta.errors.noSuchFile);

View File

@ -1,7 +1,8 @@
import $ from 'cafy';
import Emoji from '../../../../../models/emoji';
import define from '../../../define';
import { detectUrlMine } from '../../../../../misc/detect-url-mine';
import { Emojis } from '../../../../../models';
import { genId } from '../../../../../misc/gen-id';
export const meta = {
desc: {
@ -32,7 +33,8 @@ export const meta = {
export default define(meta, async (ps) => {
const type = await detectUrlMine(ps.url);
const emoji = await Emoji.insert({
const emoji = await Emojis.save({
id: genId(),
updatedAt: new Date(),
name: ps.name,
host: null,
@ -42,6 +44,6 @@ export default define(meta, async (ps) => {
});
return {
id: emoji._id
id: emoji.id
};
});

View File

@ -1,6 +1,6 @@
import $ from 'cafy';
import Emoji from '../../../../../models/emoji';
import define from '../../../define';
import { Emojis } from '../../../../../models';
export const meta = {
desc: {
@ -21,12 +21,12 @@ export const meta = {
};
export default define(meta, async (ps) => {
const emojis = await Emoji.find({
const emojis = await Emojis.find({
host: ps.host
});
return emojis.map(e => ({
id: e._id,
id: e.id,
name: e.name,
aliases: e.aliases,
host: e.host,

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import Emoji from '../../../../../models/emoji';
import define from '../../../define';
import ID from '../../../../../misc/cafy-id';
import { ID } from '../../../../../misc/cafy-id';
import { Emojis } from '../../../../../models';
export const meta = {
desc: {
@ -21,13 +21,9 @@ export const meta = {
};
export default define(meta, async (ps) => {
const emoji = await Emoji.findOne({
_id: ps.id
});
const emoji = await Emojis.findOne(ps.id);
if (emoji == null) throw new Error('emoji not found');
await Emoji.remove({ _id: emoji._id });
return;
await Emojis.delete(emoji.id);
});

View File

@ -1,8 +1,8 @@
import $ from 'cafy';
import Emoji from '../../../../../models/emoji';
import define from '../../../define';
import ID from '../../../../../misc/cafy-id';
import { detectUrlMine } from '../../../../../misc/detect-url-mine';
import { ID } from '../../../../../misc/cafy-id';
import { Emojis } from '../../../../../models';
export const meta = {
desc: {
@ -34,23 +34,17 @@ export const meta = {
};
export default define(meta, async (ps) => {
const emoji = await Emoji.findOne({
_id: ps.id
});
const emoji = await Emojis.findOne(ps.id);
if (emoji == null) throw new Error('emoji not found');
const type = await detectUrlMine(ps.url);
await Emoji.update({ _id: emoji._id }, {
$set: {
updatedAt: new Date(),
name: ps.name,
aliases: ps.aliases,
url: ps.url,
type,
}
await Emojis.update(emoji.id, {
updatedAt: new Date(),
name: ps.name,
aliases: ps.aliases,
url: ps.url,
type,
});
return;
});

View File

@ -1,8 +1,7 @@
import $ from 'cafy';
import define from '../../../define';
import Following from '../../../../../models/following';
import User from '../../../../../models/user';
import deleteFollowing from '../../../../../services/following/delete';
import { Followings, Users } from '../../../../../models';
export const meta = {
tags: ['admin'],
@ -18,13 +17,13 @@ export const meta = {
};
export default define(meta, async (ps, me) => {
const followings = await Following.find({
'_follower.host': ps.host
const followings = await Followings.find({
followerHost: ps.host
});
const pairs = await Promise.all(followings.map(f => Promise.all([
User.findOne({ _id: f.followerId }),
User.findOne({ _id: f.followeeId })
Users.findOne(f.followerId),
Users.findOne(f.followeeId)
])));
for (const pair of pairs) {

View File

@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../../define';
import Instance from '../../../../../models/instance';
import { Instances } from '../../../../../models';
export const meta = {
tags: ['admin'],
@ -13,10 +13,6 @@ export const meta = {
validator: $.str
},
isBlocked: {
validator: $.bool
},
isClosed: {
validator: $.bool
},
@ -24,18 +20,13 @@ export const meta = {
};
export default define(meta, async (ps, me) => {
const instance = await Instance.findOne({ host: ps.host });
const instance = await Instances.findOne({ host: ps.host });
if (instance == null) {
throw new Error('instance not found');
}
Instance.update({ host: ps.host }, {
$set: {
isBlocked: ps.isBlocked,
isMarkedAsClosed: ps.isClosed
}
Instances.update({ host: ps.host }, {
isMarkedAsClosed: ps.isClosed
});
return;
});

View File

@ -1,6 +1,7 @@
import rndstr from 'rndstr';
import RegistrationTicket from '../../../../models/registration-tickets';
import define from '../../define';
import { RegistrationTickets } from '../../../../models';
import { genId } from '../../../../misc/gen-id';
export const meta = {
desc: {
@ -18,7 +19,8 @@ export const meta = {
export default define(meta, async (ps) => {
const code = rndstr({ length: 5, chars: '0-9' });
await RegistrationTicket.insert({
await RegistrationTickets.save({
id: genId(),
createdAt: new Date(),
code: code
});

View File

@ -1,6 +1,7 @@
import $ from 'cafy';
import define from '../../define';
import Log from '../../../../models/log';
import { Logs } from '../../../../models';
import { Brackets } from 'typeorm';
export const meta = {
tags: ['admin'],
@ -27,41 +28,44 @@ export const meta = {
};
export default define(meta, async (ps) => {
const sort = {
_id: -1
};
const query = {} as any;
const query = Logs.createQueryBuilder('log');
if (ps.level) query.andWhere('log.level = :level', { level: ps.level });
if (ps.level) query.level = ps.level;
if (ps.domain) {
for (const d of ps.domain.split(' ')) {
const qs: any[] = [];
let i = 0;
for (const sd of (d.startsWith('-') ? d.substr(1) : d).split('.')) {
qs.push({
[`domain.${i}`]: d.startsWith('-') ? { $ne: sd } : sd
});
i++;
}
if (d.startsWith('-')) {
if (query['$and'] == null) query['$and'] = [];
query['$and'].push({
$and: qs
});
} else {
if (query['$or'] == null) query['$or'] = [];
query['$or'].push({
$and: qs
});
}
const whiteDomains = ps.domain.split(' ').filter(x => !x.startsWith('-'));
const blackDomains = ps.domain.split(' ').filter(x => x.startsWith('-'));
if (whiteDomains.length > 0) {
query.andWhere(new Brackets(qb => {
for (const whiteDomain of whiteDomains) {
let i = 0;
for (const subDomain of whiteDomain.split('.')) {
const p = `whiteSubDomain_${subDomain}_${i}`;
// SQL is 1 based, so we need '+ 1'
qb.orWhere(`log.domain[${i + 1}] = :${p}`, { [p]: subDomain });
i++;
}
}
}));
}
if (blackDomains.length > 0) {
query.andWhere(new Brackets(qb => {
for (const blackDomain of blackDomains) {
let i = 0;
for (const subDomain of blackDomain.split('.')) {
const p = `blackSubDomain_${subDomain}_${i}`;
// SQL is 1 based, so we need '+ 1'
qb.andWhere(`log.domain[${i + 1}] != :${p}`, { [p]: subDomain });
i++;
}
}
}));
}
}
const logs = await Log
.find(query, {
limit: ps.limit,
sort: sort
});
const logs = await query.take(ps.limit).getMany();
return logs;
});

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import User from '../../../../../models/user';
import { Users } from '../../../../../models';
export const meta = {
desc: {
@ -17,7 +17,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID'
@ -27,21 +26,13 @@ export const meta = {
};
export default define(meta, async (ps) => {
const user = await User.findOne({
_id: ps.userId
});
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');
}
await User.update({
_id: user._id
}, {
$set: {
isModerator: true
}
await Users.update(user.id, {
isModerator: true
});
return;
});

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import User from '../../../../../models/user';
import { Users } from '../../../../../models';
export const meta = {
desc: {
@ -17,7 +17,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID'
@ -27,21 +26,13 @@ export const meta = {
};
export default define(meta, async (ps) => {
const user = await User.findOne({
_id: ps.userId
});
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');
}
await User.update({
_id: user._id
}, {
$set: {
isModerator: false
}
await Users.update(user.id, {
isModerator: false
});
return;
});

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import AbuseUserReport from '../../../../models/abuse-user-report';
import { AbuseUserReports } from '../../../../models';
export const meta = {
tags: ['admin'],
@ -12,23 +12,16 @@ export const meta = {
params: {
reportId: {
validator: $.type(ID),
transform: transform
},
}
};
export default define(meta, async (ps) => {
const report = await AbuseUserReport.findOne({
_id: ps.reportId
});
const report = await AbuseUserReports.findOne(ps.reportId);
if (report == null) {
throw new Error('report not found');
}
await AbuseUserReport.remove({
_id: report._id
});
return;
await AbuseUserReports.delete(report.id);
});

View File

@ -1,9 +1,9 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import User from '../../../../models/user';
import * as bcrypt from 'bcryptjs';
import rndstr from 'rndstr';
import { Users } from '../../../../models';
export const meta = {
desc: {
@ -18,7 +18,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to suspend'
@ -28,9 +27,7 @@ export const meta = {
};
export default define(meta, async (ps) => {
const user = await User.findOne({
_id: ps.userId
});
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');
@ -45,12 +42,8 @@ export default define(meta, async (ps) => {
// Generate hash of password
const hash = bcrypt.hashSync(passwd);
await User.findOneAndUpdate({
_id: user._id
}, {
$set: {
password: hash
}
await Users.update(user.id, {
password: hash
});
return {

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import User from '../../../../models/user';
import { Users } from '../../../../models';
export const meta = {
desc: {
@ -16,7 +16,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to suspend'
@ -26,9 +25,7 @@ export const meta = {
};
export default define(meta, async (ps, me) => {
const user = await User.findOne({
_id: ps.userId
});
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');

View File

@ -1,7 +1,6 @@
import $ from 'cafy';
import User, { pack } from '../../../../models/user';
import define from '../../define';
import { fallback } from '../../../../prelude/symbol';
import { Users } from '../../../../models';
export const meta = {
tags: ['admin'],
@ -55,51 +54,38 @@ export const meta = {
}
};
const sort: any = { // < https://github.com/Microsoft/TypeScript/issues/1863
'+follower': { followersCount: -1 },
'-follower': { followersCount: 1 },
'+createdAt': { createdAt: -1 },
'-createdAt': { createdAt: 1 },
'+updatedAt': { updatedAt: -1 },
'-updatedAt': { updatedAt: 1 },
[fallback]: { _id: -1 }
};
export default define(meta, async (ps, me) => {
const q = {
$and: []
} as any;
const query = Users.createQueryBuilder('user');
// state
q.$and.push(
ps.state == 'admin' ? { isAdmin: true } :
ps.state == 'moderator' ? { isModerator: true } :
ps.state == 'adminOrModerator' ? {
$or: [{
isAdmin: true
}, {
isModerator: true
}]
} :
ps.state == 'verified' ? { isVerified: true } :
ps.state == 'silenced' ? { isSilenced: true } :
ps.state == 'suspended' ? { isSuspended: true } :
{}
);
switch (ps.state) {
case 'admin': query.where('user.isAdmin = TRUE'); break;
case 'moderator': query.where('user.isModerator = TRUE'); break;
case 'adminOrModerator': query.where('user.isAdmin = TRUE OR isModerator = TRUE'); break;
case 'verified': query.where('user.isVerified = TRUE'); break;
case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break;
case 'silenced': query.where('user.isSilenced = TRUE'); break;
case 'suspended': query.where('user.isSuspended = TRUE'); break;
}
// origin
q.$and.push(
ps.origin == 'local' ? { host: null } :
ps.origin == 'remote' ? { host: { $ne: null } } :
{}
);
switch (ps.origin) {
case 'local': query.andWhere('user.host IS NULL'); break;
case 'remote': query.andWhere('user.host IS NOT NULL'); break;
}
const users = await User
.find(q, {
limit: ps.limit,
sort: sort[ps.sort] || sort[fallback],
skip: ps.offset
});
switch (ps.sort) {
case '+follower': query.orderBy('user.followersCount', 'DESC'); break;
case '-follower': query.orderBy('user.followersCount', 'ASC'); break;
case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break;
case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break;
case '+updatedAt': query.orderBy('user.updatedAt', 'DESC'); break;
case '-updatedAt': query.orderBy('user.updatedAt', 'ASC'); break;
default: query.orderBy('user.id', 'ASC'); break;
}
return await Promise.all(users.map(user => pack(user, me, { detail: true })));
query.take(ps.limit);
query.skip(ps.offset);
const users = await query.getMany();
return await Users.packMany(users, me, { detail: true });
});

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import User from '../../../../models/user';
import { Users } from '../../../../models';
export const meta = {
desc: {
@ -17,7 +17,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to make silence'
@ -27,9 +26,7 @@ export const meta = {
};
export default define(meta, async (ps) => {
const user = await User.findOne({
_id: ps.userId
});
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');
@ -39,13 +36,7 @@ export default define(meta, async (ps) => {
throw new Error('cannot silence admin');
}
await User.findOneAndUpdate({
_id: user._id
}, {
$set: {
isSilenced: true
}
await Users.update(user.id, {
isSilenced: true
});
return;
});

View File

@ -1,9 +1,9 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import User, { IUser } from '../../../../models/user';
import Following from '../../../../models/following';
import deleteFollowing from '../../../../services/following/delete';
import { Users, Followings } from '../../../../models';
import { User } from '../../../../models/entities/user';
export const meta = {
desc: {
@ -19,7 +19,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to suspend'
@ -29,9 +28,7 @@ export const meta = {
};
export default define(meta, async (ps) => {
const user = await User.findOne({
_id: ps.userId
});
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');
@ -45,27 +42,21 @@ export default define(meta, async (ps) => {
throw new Error('cannot suspend moderator');
}
await User.findOneAndUpdate({
_id: user._id
}, {
$set: {
isSuspended: true
}
await Users.update(user.id, {
isSuspended: true
});
unFollowAll(user);
return;
});
async function unFollowAll(follower: IUser) {
const followings = await Following.find({
followerId: follower._id
async function unFollowAll(follower: User) {
const followings = await Followings.find({
followerId: follower.id
});
for (const following of followings) {
const followee = await User.findOne({
_id: following.followeeId
const followee = await Users.findOne({
id: following.followeeId
});
if (followee == null) {

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import User from '../../../../models/user';
import { Users } from '../../../../models';
export const meta = {
desc: {
@ -17,7 +17,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to unsilence'
@ -27,21 +26,13 @@ export const meta = {
};
export default define(meta, async (ps) => {
const user = await User.findOne({
_id: ps.userId
});
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');
}
await User.findOneAndUpdate({
_id: user._id
}, {
$set: {
isSilenced: false
}
await Users.update(user.id, {
isSilenced: false
});
return;
});

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import User from '../../../../models/user';
import { Users } from '../../../../models';
export const meta = {
desc: {
@ -17,7 +17,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to unsuspend'
@ -27,21 +26,13 @@ export const meta = {
};
export default define(meta, async (ps) => {
const user = await User.findOne({
_id: ps.userId
});
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');
}
await User.findOneAndUpdate({
_id: user._id
}, {
$set: {
isSuspended: false
}
await Users.update(user.id, {
isSuspended: false
});
return;
});

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import User from '../../../../models/user';
import { Users } from '../../../../models';
export const meta = {
desc: {
@ -17,7 +17,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to unverify'
@ -27,21 +26,13 @@ export const meta = {
};
export default define(meta, async (ps) => {
const user = await User.findOne({
_id: ps.userId
});
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');
}
await User.findOneAndUpdate({
_id: user._id
}, {
$set: {
isVerified: false
}
await Users.update(user.id, {
isVerified: false
});
return;
});

View File

@ -1,6 +1,7 @@
import $ from 'cafy';
import Meta from '../../../../models/meta';
import define from '../../define';
import { Metas } from '../../../../models';
import { Meta } from '../../../../models/entities/meta';
export const meta = {
desc: {
@ -55,7 +56,7 @@ export const meta = {
}
},
hidedTags: {
hiddenTags: {
validator: $.optional.nullable.arr($.str),
desc: {
'ja-JP': '統計などで無視するハッシュタグ'
@ -253,27 +254,6 @@ export const meta = {
}
},
enableExternalUserRecommendation: {
validator: $.optional.bool,
desc: {
'ja-JP': '外部ユーザーレコメンデーションを有効にする'
}
},
externalUserRecommendationEngine: {
validator: $.optional.nullable.str,
desc: {
'ja-JP': '外部ユーザーレコメンデーションのサードパーティエンジン'
}
},
externalUserRecommendationTimeout: {
validator: $.optional.nullable.num.min(0),
desc: {
'ja-JP': '外部ユーザーレコメンデーションのタイムアウト (ミリ秒)'
}
},
enableEmail: {
validator: $.optional.bool,
desc: {
@ -347,7 +327,7 @@ export const meta = {
};
export default define(meta, async (ps) => {
const set = {} as any;
const set = {} as Partial<Meta>;
if (ps.announcements) {
set.announcements = ps.announcements;
@ -373,8 +353,8 @@ export default define(meta, async (ps) => {
set.useStarForReactionFallback = ps.useStarForReactionFallback;
}
if (Array.isArray(ps.hidedTags)) {
set.hidedTags = ps.hidedTags;
if (Array.isArray(ps.hiddenTags)) {
set.hiddenTags = ps.hiddenTags;
}
if (ps.mascotImageUrl !== undefined) {
@ -430,11 +410,11 @@ export default define(meta, async (ps) => {
}
if (ps.maintainerName !== undefined) {
set['maintainer.name'] = ps.maintainerName;
set.maintainerName = ps.maintainerName;
}
if (ps.maintainerEmail !== undefined) {
set['maintainer.email'] = ps.maintainerEmail;
set.maintainerEmail = ps.maintainerEmail;
}
if (ps.langs !== undefined) {
@ -481,18 +461,6 @@ export default define(meta, async (ps) => {
set.discordClientSecret = ps.discordClientSecret;
}
if (ps.enableExternalUserRecommendation !== undefined) {
set.enableExternalUserRecommendation = ps.enableExternalUserRecommendation;
}
if (ps.externalUserRecommendationEngine !== undefined) {
set.externalUserRecommendationEngine = ps.externalUserRecommendationEngine;
}
if (ps.externalUserRecommendationTimeout !== undefined) {
set.externalUserRecommendationTimeout = ps.externalUserRecommendationTimeout;
}
if (ps.enableEmail !== undefined) {
set.enableEmail = ps.enableEmail;
}
@ -537,9 +505,11 @@ export default define(meta, async (ps) => {
set.swPrivateKey = ps.swPrivateKey;
}
await Meta.update({}, {
$set: set
}, { upsert: true });
const meta = await Metas.findOne();
return;
if (meta) {
await Metas.update(meta.id, set);
} else {
await Metas.save(set);
}
});

View File

@ -1,6 +1,5 @@
import * as mongo from 'mongodb';
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { getRemoteUser } from '../../common/getters';
import { updatePerson } from '../../../../remote/activitypub/models/person';
@ -19,7 +18,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to update'
@ -29,11 +27,6 @@ export const meta = {
};
export default define(meta, async (ps) => {
await updatePersonById(ps.userId);
return;
});
async function updatePersonById(userId: mongo.ObjectID) {
const user = await getRemoteUser(userId);
const user = await getRemoteUser(ps.userId);
await updatePerson(user.uri);
}
});

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import User from '../../../../models/user';
import { Users } from '../../../../models';
export const meta = {
desc: {
@ -17,7 +17,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to verify'
@ -27,21 +26,13 @@ export const meta = {
};
export default define(meta, async (ps) => {
const user = await User.findOne({
_id: ps.userId
});
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');
}
await User.findOneAndUpdate({
_id: user._id
}, {
$set: {
isVerified: true
}
await Users.update(user.id, {
isVerified: true
});
return;
});

View File

@ -1,72 +0,0 @@
import Note from '../../../../models/note';
import define from '../../define';
import fetchMeta from '../../../../misc/fetch-meta';
export const meta = {
tags: ['hashtags'],
requireCredential: false,
};
export default define(meta, async (ps) => {
const instance = await fetchMeta();
const hidedTags = instance.hidedTags.map(t => t.toLowerCase());
// 重い
//const span = 1000 * 60 * 60 * 24 * 7; // 1週間
const span = 1000 * 60 * 60 * 24; // 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 {
_id: {
tag: string;
userId: any;
}
}[];
//#endregion
if (data.length == 0) {
return [];
}
let tags: {
name: string;
count: number;
}[] = [];
// カウント
for (const x of data.map(x => x._id).filter(x => !hidedTags.includes(x.tag))) {
const i = tags.findIndex(tag => tag.name == x.tag);
if (i != -1) {
tags[i].count++;
} else {
tags.push({
name: x.tag,
count: 1
});
}
}
// タグを人気順に並べ替え
tags.sort((a, b) => b.count - a.count);
tags = tags.slice(0, 30);
return tags;
});

View File

@ -1,15 +1,15 @@
import $ from 'cafy';
import define from '../../define';
import config from '../../../../config';
import * as mongo from 'mongodb';
import User, { pack as packUser, IUser } from '../../../../models/user';
import { createPerson } from '../../../../remote/activitypub/models/person';
import Note, { pack as packNote, INote } from '../../../../models/note';
import { createNote } from '../../../../remote/activitypub/models/note';
import Resolver from '../../../../remote/activitypub/resolver';
import { ApiError } from '../../error';
import Instance from '../../../../models/instance';
import { extractDbHost } from '../../../../misc/convert-host';
import { Users, Notes } from '../../../../models';
import { Note } from '../../../../models/entities/note';
import { User } from '../../../../models/entities/user';
import fetchMeta from '../../../../misc/fetch-meta';
export const meta = {
tags: ['federation'],
@ -53,25 +53,40 @@ export default define(meta, async (ps) => {
async function fetchAny(uri: string) {
// URIがこのサーバーを指しているなら、ローカルユーザーIDとしてDBからフェッチ
if (uri.startsWith(config.url + '/')) {
const id = new mongo.ObjectID(uri.split('/').pop());
const [user, note] = await Promise.all([
User.findOne({ _id: id }),
Note.findOne({ _id: id })
]);
const parts = uri.split('/');
const id = parts.pop();
const type = parts.pop();
const packed = await mergePack(user, note);
if (packed !== null) return packed;
if (type === 'notes') {
const note = await Notes.findOne(id);
if (note) {
return {
type: 'Note',
object: await Notes.pack(note, null, { detail: true })
};
}
} else if (type === 'users') {
const user = await Users.findOne(id);
if (user) {
return {
type: 'User',
object: await Users.pack(user, null, { detail: true })
};
}
}
}
// ブロックしてたら中断
const instance = await Instance.findOne({ host: extractDbHost(uri) });
if (instance && instance.isBlocked) return null;
const meta = await fetchMeta();
if (meta.blockedHosts.includes(extractDbHost(uri))) return null;
// URI(AP Object id)としてDB検索
{
const [user, note] = await Promise.all([
User.findOne({ uri: uri }),
Note.findOne({ uri: uri })
Users.findOne({ uri: uri }),
Notes.findOne({ uri: uri })
]);
const packed = await mergePack(user, note);
@ -86,8 +101,8 @@ async function fetchAny(uri: string) {
// これはDBに存在する可能性があるため再度DB検索
if (uri !== object.id) {
const [user, note] = await Promise.all([
User.findOne({ uri: object.id }),
Note.findOne({ uri: object.id })
Users.findOne({ uri: object.id }),
Notes.findOne({ uri: object.id })
]);
const packed = await mergePack(user, note);
@ -99,7 +114,7 @@ async function fetchAny(uri: string) {
const user = await createPerson(object.id);
return {
type: 'User',
object: await packUser(user, null, { detail: true })
object: await Users.pack(user, null, { detail: true })
};
}
@ -107,25 +122,25 @@ async function fetchAny(uri: string) {
const note = await createNote(object.id);
return {
type: 'Note',
object: await packNote(note, null, { detail: true })
object: await Notes.pack(note, null, { detail: true })
};
}
return null;
}
async function mergePack(user: IUser, note: INote) {
async function mergePack(user: User, note: Note) {
if (user !== null) {
return {
type: 'User',
object: await packUser(user, null, { detail: true })
object: await Users.pack(user, null, { detail: true })
};
}
if (note !== null) {
return {
type: 'Note',
object: await packNote(note, null, { detail: true })
object: await Notes.pack(note, null, { detail: true })
};
}

View File

@ -1,7 +1,8 @@
import rndstr from 'rndstr';
import $ from 'cafy';
import App, { pack } from '../../../../models/app';
import define from '../../define';
import { Apps } from '../../../../models';
import { genId } from '../../../../misc/gen-id';
export const meta = {
tags: ['app'],
@ -34,9 +35,10 @@ export default define(meta, async (ps, user) => {
const secret = rndstr('a-zA-Z0-9', 32);
// Create account
const app = await App.insert({
const app = await Apps.save({
id: genId(),
createdAt: new Date(),
userId: user && user._id,
userId: user && user.id,
name: ps.name,
description: ps.description,
permission: ps.permission,
@ -44,7 +46,7 @@ export default define(meta, async (ps, user) => {
secret: secret
});
return await pack(app, null, {
return await Apps.pack(app, null, {
detail: true,
includeSecret: true
});

View File

@ -1,8 +1,8 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import App, { pack } from '../../../../models/app';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { ApiError } from '../../error';
import { Apps } from '../../../../models';
export const meta = {
tags: ['app'],
@ -10,7 +10,6 @@ export const meta = {
params: {
appId: {
validator: $.type(ID),
transform: transform
},
},
@ -27,14 +26,14 @@ export default define(meta, async (ps, user, app) => {
const isSecure = user != null && app == null;
// Lookup app
const ap = await App.findOne({ _id: ps.appId });
const ap = await Apps.findOne(ps.appId);
if (ap === null) {
if (ap == null) {
throw new ApiError(meta.errors.noSuchApp);
}
return await pack(ap, user, {
return await Apps.pack(ap, user, {
detail: true,
includeSecret: isSecure && ap.userId.equals(user._id)
includeSecret: isSecure && (ap.userId === user.id)
});
});

View File

@ -1,11 +1,10 @@
import rndstr from 'rndstr';
import * as crypto from 'crypto';
import $ from 'cafy';
import App from '../../../../models/app';
import AuthSess from '../../../../models/auth-session';
import AccessToken from '../../../../models/access-token';
import define from '../../define';
import { ApiError } from '../../error';
import { AuthSessions, AccessTokens, Apps } from '../../../../models';
import { genId } from '../../../../misc/gen-id';
export const meta = {
tags: ['auth'],
@ -31,10 +30,10 @@ export const meta = {
export default define(meta, async (ps, user) => {
// Fetch token
const session = await AuthSess
const session = await AuthSessions
.findOne({ token: ps.token });
if (session === null) {
if (session == null) {
throw new ApiError(meta.errors.noSuchSession);
}
@ -42,16 +41,14 @@ export default define(meta, async (ps, user) => {
const accessToken = rndstr('a-zA-Z0-9', 32);
// Fetch exist access token
const exist = await AccessToken.findOne({
const exist = await AccessTokens.findOne({
appId: session.appId,
userId: user._id,
userId: user.id,
});
if (exist === null) {
if (exist == null) {
// Lookup app
const app = await App.findOne({
_id: session.appId
});
const app = await Apps.findOne(session.appId);
// Generate Hash
const sha256 = crypto.createHash('sha256');
@ -59,20 +56,19 @@ export default define(meta, async (ps, user) => {
const hash = sha256.digest('hex');
// Insert access token doc
await AccessToken.insert({
await AccessTokens.save({
id: genId(),
createdAt: new Date(),
appId: session.appId,
userId: user._id,
userId: user.id,
token: accessToken,
hash: hash
});
}
// Update session
await AuthSess.update(session._id, {
$set: {
userId: user._id
}
await AuthSessions.update(session.id, {
userId: user.id
});
return;

View File

@ -1,10 +1,10 @@
import * as uuid from 'uuid';
import $ from 'cafy';
import App from '../../../../../models/app';
import AuthSess from '../../../../../models/auth-session';
import config from '../../../../../config';
import define from '../../../define';
import { ApiError } from '../../../error';
import { Apps, AuthSessions } from '../../../../../models';
import { genId } from '../../../../../misc/gen-id';
export const meta = {
tags: ['auth'],
@ -46,7 +46,7 @@ export const meta = {
export default define(meta, async (ps) => {
// Lookup app
const app = await App.findOne({
const app = await Apps.findOne({
secret: ps.appSecret
});
@ -58,9 +58,10 @@ export default define(meta, async (ps) => {
const token = uuid.v4();
// Create session token document
const doc = await AuthSess.insert({
const doc = await AuthSessions.save({
id: genId(),
createdAt: new Date(),
appId: app._id,
appId: app.id,
token: token
});

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import AuthSess, { pack } from '../../../../../models/auth-session';
import define from '../../../define';
import { ApiError } from '../../../error';
import { AuthSessions } from '../../../../../models';
export const meta = {
tags: ['auth'],
@ -29,7 +29,7 @@ export const meta = {
export default define(meta, async (ps, user) => {
// Lookup session
const session = await AuthSess.findOne({
const session = await AuthSessions.findOne({
token: ps.token
});
@ -37,5 +37,5 @@ export default define(meta, async (ps, user) => {
throw new ApiError(meta.errors.noSuchSession);
}
return await pack(session, user);
return await AuthSessions.pack(session, user);
});

View File

@ -1,10 +1,7 @@
import $ from 'cafy';
import App from '../../../../../models/app';
import AuthSess from '../../../../../models/auth-session';
import AccessToken from '../../../../../models/access-token';
import { pack } from '../../../../../models/user';
import define from '../../../define';
import { ApiError } from '../../../error';
import { Apps, AuthSessions, AccessTokens, Users } from '../../../../../models';
export const meta = {
tags: ['auth'],
@ -67,7 +64,7 @@ export const meta = {
export default define(meta, async (ps) => {
// Lookup app
const app = await App.findOne({
const app = await Apps.findOne({
secret: ps.appSecret
});
@ -76,13 +73,12 @@ export default define(meta, async (ps) => {
}
// Fetch token
const session = await AuthSess
.findOne({
token: ps.token,
appId: app._id
});
const session = await AuthSessions.findOne({
token: ps.token,
appId: app.id
});
if (session === null) {
if (session == null) {
throw new ApiError(meta.errors.noSuchSession);
}
@ -91,25 +87,17 @@ export default define(meta, async (ps) => {
}
// Lookup access token
const accessToken = await AccessToken.findOne({
appId: app._id,
const accessToken = await AccessTokens.findOne({
appId: app.id,
userId: session.userId
});
// Delete session
/* https://github.com/Automattic/monk/issues/178
AuthSess.deleteOne({
_id: session._id
});
*/
AuthSess.remove({
_id: session._id
});
AuthSessions.delete(session.id);
return {
accessToken: accessToken.token,
user: await pack(session.userId, null, {
user: await Users.pack(session.userId, null, {
detail: true
})
};

View File

@ -1,12 +1,11 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import * as ms from 'ms';
import { pack } from '../../../../models/user';
import Blocking from '../../../../models/blocking';
import create from '../../../../services/blocking/create';
import define from '../../define';
import { ApiError } from '../../error';
import { getUser } from '../../common/getters';
import { Blockings, NoteWatchings } from '../../../../models';
export const meta = {
stability: 'stable',
@ -25,12 +24,11 @@ export const meta = {
requireCredential: true,
kind: 'following-write',
kind: 'write:blocks',
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'
@ -63,7 +61,7 @@ export default define(meta, async (ps, user) => {
const blocker = user;
// 自分自身
if (user._id.equals(ps.userId)) {
if (user.id === ps.userId) {
throw new ApiError(meta.errors.blockeeIsYourself);
}
@ -74,19 +72,22 @@ export default define(meta, async (ps, user) => {
});
// Check if already blocking
const exist = await Blocking.findOne({
blockerId: blocker._id,
blockeeId: blockee._id
const exist = await Blockings.findOne({
blockerId: blocker.id,
blockeeId: blockee.id
});
if (exist !== null) {
if (exist != null) {
throw new ApiError(meta.errors.alreadyBlocking);
}
// Create blocking
await create(blocker, blockee);
return await pack(blockee._id, user, {
detail: true
NoteWatchings.delete({
userId: blocker.id,
noteUserId: blockee.id
});
return await Blockings.pack(blockee.id, user);
});

View File

@ -1,12 +1,11 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import * as ms from 'ms';
import { pack } from '../../../../models/user';
import Blocking from '../../../../models/blocking';
import deleteBlocking from '../../../../services/blocking/delete';
import define from '../../define';
import { ApiError } from '../../error';
import { getUser } from '../../common/getters';
import { Blockings } from '../../../../models';
export const meta = {
stability: 'stable',
@ -25,12 +24,11 @@ export const meta = {
requireCredential: true,
kind: 'following-write',
kind: 'write:blocks',
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'
@ -63,7 +61,7 @@ export default define(meta, async (ps, user) => {
const blocker = user;
// Check if the blockee is yourself
if (user._id.equals(ps.userId)) {
if (user.id === ps.userId) {
throw new ApiError(meta.errors.blockeeIsYourself);
}
@ -74,19 +72,17 @@ export default define(meta, async (ps, user) => {
});
// Check not blocking
const exist = await Blocking.findOne({
blockerId: blocker._id,
blockeeId: blockee._id
const exist = await Blockings.findOne({
blockerId: blocker.id,
blockeeId: blockee.id
});
if (exist === null) {
if (exist == null) {
throw new ApiError(meta.errors.notBlocking);
}
// Delete blocking
await deleteBlocking(blocker, blockee);
return await pack(blockee._id, user, {
detail: true
});
return await Blockings.pack(blockee.id, user);
});

View File

@ -1,7 +1,8 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import Blocking, { packMany } from '../../../../models/blocking';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { Blockings } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
export const meta = {
desc: {
@ -13,7 +14,7 @@ export const meta = {
requireCredential: true,
kind: 'following-read',
kind: 'read:blocks',
params: {
limit: {
@ -23,12 +24,10 @@ export const meta = {
sinceId: {
validator: $.optional.type(ID),
transform: transform,
},
untilId: {
validator: $.optional.type(ID),
transform: transform,
},
},
@ -41,30 +40,12 @@ export const meta = {
};
export default define(meta, async (ps, me) => {
const query = {
blockerId: me._id
} as any;
const query = makePaginationQuery(Blockings.createQueryBuilder('blocking'), ps.sinceId, ps.untilId)
.andWhere(`blocking.blockerId = :meId`, { meId: me.id });
const sort = {
_id: -1
};
const blockings = await query
.take(ps.limit)
.getMany();
if (ps.sinceId) {
sort._id = 1;
query._id = {
$gt: ps.sinceId
};
} else if (ps.untilId) {
query._id = {
$lt: ps.untilId
};
}
const blockings = await Blocking
.find(query, {
limit: ps.limit,
sort: sort
});
return await packMany(blockings, me);
return await Blockings.packMany(blockings, me);
});

View File

@ -1,6 +1,7 @@
import $ from 'cafy';
import define from '../../define';
import activeUsersChart from '../../../../services/chart/active-users';
import { convertLog } from '../../../../services/chart/core';
import { activeUsersChart } from '../../../../services/chart';
export const meta = {
stability: 'stable',
@ -28,12 +29,7 @@ export const meta = {
},
},
res: {
type: 'array',
items: {
type: 'object',
},
},
res: convertLog(activeUsersChart.schema),
};
export default define(meta, async (ps) => {

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import define from '../../define';
import driveChart, { driveLogSchema } from '../../../../services/chart/drive';
import { convertLog } from '../../../../services/chart';
import { convertLog } from '../../../../services/chart/core';
import { driveChart } from '../../../../services/chart';
export const meta = {
stability: 'stable',
@ -29,7 +29,7 @@ export const meta = {
},
},
res: convertLog(driveLogSchema),
res: convertLog(driveChart.schema),
};
export default define(meta, async (ps) => {

View File

@ -1,6 +1,7 @@
import $ from 'cafy';
import define from '../../define';
import federationChart from '../../../../services/chart/federation';
import { convertLog } from '../../../../services/chart/core';
import { federationChart } from '../../../../services/chart';
export const meta = {
stability: 'stable',
@ -28,12 +29,7 @@ export const meta = {
},
},
res: {
type: 'array',
items: {
type: 'object',
},
},
res: convertLog(federationChart.schema),
};
export default define(meta, async (ps) => {

View File

@ -1,6 +1,7 @@
import $ from 'cafy';
import define from '../../define';
import hashtagChart from '../../../../services/chart/hashtag';
import { convertLog } from '../../../../services/chart/core';
import { hashtagChart } from '../../../../services/chart';
export const meta = {
stability: 'stable',
@ -35,12 +36,7 @@ export const meta = {
},
},
res: {
type: 'array',
items: {
type: 'object',
},
},
res: convertLog(hashtagChart.schema),
};
export default define(meta, async (ps) => {

View File

@ -1,6 +1,7 @@
import $ from 'cafy';
import define from '../../define';
import instanceChart from '../../../../services/chart/instance';
import { convertLog } from '../../../../services/chart/core';
import { instanceChart } from '../../../../services/chart';
export const meta = {
stability: 'stable',
@ -36,12 +37,7 @@ export const meta = {
}
},
res: {
type: 'array',
items: {
type: 'object',
},
},
res: convertLog(instanceChart.schema),
};
export default define(meta, async (ps) => {

View File

@ -1,6 +1,7 @@
import $ from 'cafy';
import define from '../../define';
import networkChart from '../../../../services/chart/network';
import { convertLog } from '../../../../services/chart/core';
import { networkChart } from '../../../../services/chart';
export const meta = {
stability: 'stable',
@ -28,12 +29,7 @@ export const meta = {
},
},
res: {
type: 'array',
items: {
type: 'object',
},
},
res: convertLog(networkChart.schema),
};
export default define(meta, async (ps) => {

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import define from '../../define';
import notesChart, { notesLogSchema } from '../../../../services/chart/notes';
import { convertLog } from '../../../../services/chart';
import { convertLog } from '../../../../services/chart/core';
import { notesChart } from '../../../../services/chart';
export const meta = {
stability: 'stable',
@ -29,7 +29,7 @@ export const meta = {
},
},
res: convertLog(notesLogSchema),
res: convertLog(notesChart.schema),
};
export default define(meta, async (ps) => {

View File

@ -1,8 +1,8 @@
import $ from 'cafy';
import define from '../../../define';
import perUserDriveChart, { perUserDriveLogSchema } from '../../../../../services/chart/per-user-drive';
import ID, { transform } from '../../../../../misc/cafy-id';
import { convertLog } from '../../../../../services/chart';
import { ID } from '../../../../../misc/cafy-id';
import { convertLog } from '../../../../../services/chart/core';
import { perUserDriveChart } from '../../../../../services/chart';
export const meta = {
stability: 'stable',
@ -31,7 +31,6 @@ export const meta = {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'
@ -39,7 +38,7 @@ export const meta = {
}
},
res: convertLog(perUserDriveLogSchema),
res: convertLog(perUserDriveChart.schema),
};
export default define(meta, async (ps) => {

View File

@ -1,8 +1,8 @@
import $ from 'cafy';
import define from '../../../define';
import perUserFollowingChart, { perUserFollowingLogSchema } from '../../../../../services/chart/per-user-following';
import ID, { transform } from '../../../../../misc/cafy-id';
import { convertLog } from '../../../../../services/chart';
import { ID } from '../../../../../misc/cafy-id';
import { convertLog } from '../../../../../services/chart/core';
import { perUserFollowingChart } from '../../../../../services/chart';
export const meta = {
stability: 'stable',
@ -31,7 +31,6 @@ export const meta = {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'
@ -39,9 +38,9 @@ export const meta = {
}
},
res: convertLog(perUserFollowingLogSchema),
res: convertLog(perUserFollowingChart.schema),
};
export default define(meta, async (ps) => {
return await perUserFollowingChart.getChart(ps.span as any, ps.limit, ps.userId);
return await perUserFollowingChart.getChart(ps.span as any, ps.limit, ps.userId);
});

View File

@ -1,8 +1,8 @@
import $ from 'cafy';
import define from '../../../define';
import perUserNotesChart, { perUserNotesLogSchema } from '../../../../../services/chart/per-user-notes';
import ID, { transform } from '../../../../../misc/cafy-id';
import { convertLog } from '../../../../../services/chart';
import { ID } from '../../../../../misc/cafy-id';
import { convertLog } from '../../../../../services/chart/core';
import { perUserNotesChart } from '../../../../../services/chart';
export const meta = {
stability: 'stable',
@ -31,7 +31,6 @@ export const meta = {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'
@ -39,7 +38,7 @@ export const meta = {
}
},
res: convertLog(perUserNotesLogSchema),
res: convertLog(perUserNotesChart.schema),
};
export default define(meta, async (ps) => {

View File

@ -1,7 +1,8 @@
import $ from 'cafy';
import define from '../../../define';
import perUserReactionsChart from '../../../../../services/chart/per-user-reactions';
import ID, { transform } from '../../../../../misc/cafy-id';
import { ID } from '../../../../../misc/cafy-id';
import { convertLog } from '../../../../../services/chart/core';
import { perUserReactionsChart } from '../../../../../services/chart';
export const meta = {
stability: 'stable',
@ -30,7 +31,6 @@ export const meta = {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'
@ -38,12 +38,7 @@ export const meta = {
}
},
res: {
type: 'array',
items: {
type: 'object',
},
},
res: convertLog(perUserReactionsChart.schema),
};
export default define(meta, async (ps) => {

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import define from '../../define';
import usersChart, { usersLogSchema } from '../../../../services/chart/users';
import { convertLog } from '../../../../services/chart';
import { convertLog } from '../../../../services/chart/core';
import { usersChart } from '../../../../services/chart';
export const meta = {
stability: 'stable',
@ -29,7 +29,7 @@ export const meta = {
},
},
res: convertLog(usersLogSchema),
res: convertLog(usersChart.schema),
};
export default define(meta, async (ps) => {

View File

@ -1,6 +1,6 @@
import DriveFile from '../../../models/drive-file';
import define from '../define';
import fetchMeta from '../../../misc/fetch-meta';
import { DriveFiles } from '../../../models';
export const meta = {
desc: {
@ -12,7 +12,7 @@ export const meta = {
requireCredential: true,
kind: 'drive-read',
kind: 'read:drive',
res: {
type: 'object',
@ -31,27 +31,7 @@ export default define(meta, async (ps, user) => {
const instance = await fetchMeta();
// Calculate drive usage
const usage = await DriveFile.aggregate([{
$match: {
'metadata.userId': user._id,
'metadata.deletedAt': { $exists: false }
}
}, {
$project: {
length: true
}
}, {
$group: {
_id: null,
usage: { $sum: '$length' }
}
}])
.then((aggregates: any[]) => {
if (aggregates.length > 0) {
return aggregates[0].usage;
}
return 0;
});
const usage = await DriveFiles.clacDriveUsageOf(user);
return {
capacity: 1024 * 1024 * instance.localDriveCapacityMb,

View File

@ -1,7 +1,8 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import DriveFile, { packMany } from '../../../../models/drive-file';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { DriveFiles } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
export const meta = {
desc: {
@ -13,7 +14,7 @@ export const meta = {
requireCredential: true,
kind: 'drive-read',
kind: 'read:drive',
params: {
limit: {
@ -23,18 +24,15 @@ export const meta = {
sinceId: {
validator: $.optional.type(ID),
transform: transform,
},
untilId: {
validator: $.optional.type(ID),
transform: transform,
},
folderId: {
validator: $.optional.nullable.type(ID),
default: null as any,
transform: transform,
},
type: {
@ -51,36 +49,24 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const sort = {
_id: -1
};
const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId)
.andWhere('file.userId = :userId', { userId: user.id });
const query = {
'metadata.userId': user._id,
'metadata.folderId': ps.folderId,
'metadata.deletedAt': { $exists: false }
} as any;
if (ps.sinceId) {
sort._id = 1;
query._id = {
$gt: ps.sinceId
};
} else if (ps.untilId) {
query._id = {
$lt: ps.untilId
};
if (ps.folderId) {
query.andWhere('file.folderId = :folderId', { folderId: ps.folderId });
} else {
query.andWhere('file.folderId IS NULL');
}
if (ps.type) {
query.contentType = new RegExp(`^${ps.type.replace(/\*/g, '.+?')}$`);
if (ps.type.endsWith('/*')) {
query.andWhere('file.type like :type', { type: ps.type.replace('/*', '/') + '%' });
} else {
query.andWhere('file.type = :type', { type: ps.type });
}
}
const files = await DriveFile
.find(query, {
limit: ps.limit,
sort: sort
});
const files = await query.take(ps.limit).getMany();
return await packMany(files, { detail: false, self: true });
return await DriveFiles.packMany(files, { detail: false, self: true });
});

View File

@ -1,9 +1,8 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFile from '../../../../../models/drive-file';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { packMany } from '../../../../../models/note';
import { ApiError } from '../../../error';
import { DriveFiles } from '../../../../../models';
export const meta = {
stability: 'stable',
@ -17,12 +16,11 @@ export const meta = {
requireCredential: true,
kind: 'drive-read',
kind: 'read:drive',
params: {
fileId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のファイルID',
'en-US': 'Target file ID'
@ -48,18 +46,17 @@ export const meta = {
export default define(meta, async (ps, user) => {
// Fetch file
const file = await DriveFile
.findOne({
_id: ps.fileId,
'metadata.userId': user._id,
'metadata.deletedAt': { $exists: false }
});
const file = await DriveFiles.findOne({
id: ps.fileId,
userId: user.id,
});
if (file === null) {
if (file == null) {
throw new ApiError(meta.errors.noSuchFile);
}
/* v11 TODO
return await packMany(file.metadata.attachedNoteIds || [], user, {
detail: true
});
});*/
});

View File

@ -1,6 +1,6 @@
import $ from 'cafy';
import DriveFile, { pack } from '../../../../../models/drive-file';
import define from '../../../define';
import { DriveFiles } from '../../../../../models';
export const meta = {
desc: {
@ -12,7 +12,7 @@ export const meta = {
requireCredential: true,
kind: 'drive-read',
kind: 'read:drive',
params: {
md5: {
@ -29,11 +29,12 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const file = await DriveFile.findOne({
const file = await DriveFiles.findOne({
md5: ps.md5,
'metadata.userId': user._id,
'metadata.deletedAt': { $exists: false }
userId: user.id,
});
return { file: file ? await pack(file, { self: true }) : null };
return {
file: file ? await DriveFiles.pack(file, { self: true }) : null
};
});

View File

@ -1,11 +1,11 @@
import * as ms from 'ms';
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import { validateFileName, pack } from '../../../../../models/drive-file';
import { ID } from '../../../../../misc/cafy-id';
import create from '../../../../../services/drive/add-file';
import define from '../../../define';
import { apiLogger } from '../../../logger';
import { ApiError } from '../../../error';
import { DriveFiles } from '../../../../../models';
export const meta = {
desc: {
@ -24,12 +24,11 @@ export const meta = {
requireFile: true,
kind: 'drive-write',
kind: 'write:drive',
params: {
folderId: {
validator: $.optional.nullable.type(ID),
transform: transform,
default: null as any,
desc: {
'ja-JP': 'フォルダID'
@ -78,7 +77,7 @@ export default define(meta, async (ps, user, app, file, cleanup) => {
name = null;
} else if (name === 'blob') {
name = null;
} else if (!validateFileName(name)) {
} else if (!DriveFiles.validateFileName(name)) {
throw new ApiError(meta.errors.invalidFileName);
}
} else {
@ -88,7 +87,7 @@ export default define(meta, async (ps, user, app, file, cleanup) => {
try {
// Create file
const driveFile = await create(user, file.path, name, null, ps.folderId, ps.force, false, null, null, ps.isSensitive);
return pack(driveFile, { self: true });
return DriveFiles.pack(driveFile, { self: true });
} catch (e) {
apiLogger.error(e);
throw new ApiError();

View File

@ -1,10 +1,10 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFile from '../../../../../models/drive-file';
import { ID } from '../../../../../misc/cafy-id';
import del from '../../../../../services/drive/delete-file';
import { publishDriveStream } from '../../../../../services/stream';
import define from '../../../define';
import { ApiError } from '../../../error';
import { DriveFiles } from '../../../../../models';
export const meta = {
stability: 'stable',
@ -18,12 +18,11 @@ export const meta = {
requireCredential: true,
kind: 'drive-write',
kind: 'write:drive',
params: {
fileId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のファイルID',
'en-US': 'Target file ID'
@ -47,17 +46,13 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
// Fetch file
const file = await DriveFile
.findOne({
_id: ps.fileId
});
const file = await DriveFiles.findOne(ps.fileId);
if (file === null) {
if (file == null) {
throw new ApiError(meta.errors.noSuchFile);
}
if (!user.isAdmin && !user.isModerator && !file.metadata.userId.equals(user._id)) {
if (!user.isAdmin && !user.isModerator && (file.userId !== user.id)) {
throw new ApiError(meta.errors.accessDenied);
}
@ -65,7 +60,5 @@ export default define(meta, async (ps, user) => {
await del(file);
// Publish fileDeleted event
publishDriveStream(user._id, 'fileDeleted', file._id);
return;
publishDriveStream(user.id, 'fileDeleted', file.id);
});

View File

@ -1,14 +1,14 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFile, { pack } from '../../../../../models/drive-file';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { DriveFiles } from '../../../../../models';
export const meta = {
requireCredential: true,
tags: ['drive'],
kind: 'drive-read',
kind: 'read:drive',
params: {
name: {
@ -17,7 +17,6 @@ export const meta = {
folderId: {
validator: $.optional.nullable.type(ID),
transform: transform,
default: null as any,
desc: {
'ja-JP': 'フォルダID'
@ -27,12 +26,11 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const files = await DriveFile
.find({
filename: ps.name,
'metadata.userId': user._id,
'metadata.folderId': ps.folderId
});
const files = await DriveFiles.find({
name: ps.name,
userId: user.id,
folderId: ps.folderId
});
return await Promise.all(files.map(file => pack(file, { self: true })));
return await Promise.all(files.map(file => DriveFiles.pack(file, { self: true })));
});

View File

@ -1,10 +1,9 @@
import $ from 'cafy';
import * as mongo from 'mongodb';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFile, { pack, IDriveFile } from '../../../../../models/drive-file';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import config from '../../../../../config';
import { ApiError } from '../../../error';
import { DriveFile } from '../../../../../models/entities/drive-file';
import { DriveFiles } from '../../../../../models';
export const meta = {
stability: 'stable',
@ -18,12 +17,11 @@ export const meta = {
requireCredential: true,
kind: 'drive-read',
kind: 'read:drive',
params: {
fileId: {
validator: $.optional.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のファイルID',
'en-US': 'Target file ID'
@ -65,49 +63,33 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
let file: IDriveFile;
let file: DriveFile;
if (ps.fileId) {
file = await DriveFile.findOne({
_id: ps.fileId,
'metadata.deletedAt': { $exists: false }
});
file = await DriveFiles.findOne(ps.fileId);
} else if (ps.url) {
const isInternalStorageUrl = ps.url.startsWith(config.driveUrl);
if (isInternalStorageUrl) {
// Extract file ID from url
// e.g.
// http://misskey.local/files/foo?original=bar --> foo
const fileId = new mongo.ObjectID(ps.url.replace(config.driveUrl, '').replace(/\?(.*)$/, '').replace(/\//g, ''));
file = await DriveFile.findOne({
_id: fileId,
'metadata.deletedAt': { $exists: false }
});
} else {
file = await DriveFile.findOne({
$or: [{
'metadata.url': ps.url
}, {
'metadata.webpublicUrl': ps.url
}, {
'metadata.thumbnailUrl': ps.url
}],
'metadata.deletedAt': { $exists: false }
});
}
file = await DriveFiles.findOne({
where: [{
url: ps.url
}, {
webpublicUrl: ps.url
}, {
thumbnailUrl: ps.url
}],
});
} else {
throw new ApiError(meta.errors.fileIdOrUrlRequired);
}
if (!user.isAdmin && !user.isModerator && !file.metadata.userId.equals(user._id)) {
if (!user.isAdmin && !user.isModerator && (file.userId !== user.id)) {
throw new ApiError(meta.errors.accessDenied);
}
if (file === null) {
if (file == null) {
throw new ApiError(meta.errors.noSuchFile);
}
return await pack(file, {
return await DriveFiles.pack(file, {
detail: true,
self: true
});

View File

@ -1,11 +1,9 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder from '../../../../../models/drive-folder';
import DriveFile, { validateFileName, pack } from '../../../../../models/drive-file';
import { ID } from '../../../../../misc/cafy-id';
import { publishDriveStream } from '../../../../../services/stream';
import define from '../../../define';
import Note from '../../../../../models/note';
import { ApiError } from '../../../error';
import { DriveFiles, DriveFolders } from '../../../../../models';
export const meta = {
desc: {
@ -17,12 +15,11 @@ export const meta = {
requireCredential: true,
kind: 'drive-write',
kind: 'write:drive',
params: {
fileId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のファイルID'
}
@ -30,7 +27,6 @@ export const meta = {
folderId: {
validator: $.optional.nullable.type(ID),
transform: transform,
default: undefined as any,
desc: {
'ja-JP': 'フォルダID'
@ -38,7 +34,7 @@ export const meta = {
},
name: {
validator: $.optional.str.pipe(validateFileName),
validator: $.optional.str.pipe(DriveFiles.validateFileName),
default: undefined as any,
desc: {
'ja-JP': 'ファイル名',
@ -78,69 +74,47 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
// Fetch file
const file = await DriveFile
.findOne({
_id: ps.fileId
});
const file = await DriveFiles.findOne(ps.fileId);
if (file === null) {
if (file == null) {
throw new ApiError(meta.errors.noSuchFile);
}
if (!user.isAdmin && !user.isModerator && !file.metadata.userId.equals(user._id)) {
if (!user.isAdmin && !user.isModerator && (file.userId !== user.id)) {
throw new ApiError(meta.errors.accessDenied);
}
if (ps.name) file.filename = ps.name;
if (ps.name) file.name = ps.name;
if (ps.isSensitive !== undefined) file.metadata.isSensitive = ps.isSensitive;
if (ps.isSensitive !== undefined) file.isSensitive = ps.isSensitive;
if (ps.folderId !== undefined) {
if (ps.folderId === null) {
file.metadata.folderId = null;
file.folderId = null;
} else {
// Fetch folder
const folder = await DriveFolder
.findOne({
_id: ps.folderId,
userId: user._id
});
const folder = await DriveFolders.findOne({
id: ps.folderId,
userId: user.id
});
if (folder === null) {
if (folder == null) {
throw new ApiError(meta.errors.noSuchFolder);
}
file.metadata.folderId = folder._id;
file.folderId = folder.id;
}
}
await DriveFile.update(file._id, {
$set: {
filename: file.filename,
'metadata.folderId': file.metadata.folderId,
'metadata.isSensitive': file.metadata.isSensitive
}
await DriveFiles.update(file.id, {
name: file.name,
folderId: file.folderId,
isSensitive: file.isSensitive
});
// ドライブのファイルが非正規化されているドキュメントも更新
Note.find({
'_files._id': file._id
}).then(notes => {
for (const note of notes) {
note._files[note._files.findIndex(f => f._id.equals(file._id))] = file;
Note.update({ _id: note._id }, {
$set: {
_files: note._files
}
});
}
});
const fileObj = await pack(file, { self: true });
const fileObj = await DriveFiles.pack(file, { self: true });
// Publish fileUpdated event
publishDriveStream(user._id, 'fileUpdated', fileObj);
publishDriveStream(user.id, 'fileUpdated', fileObj);
return fileObj;
});

View File

@ -1,9 +1,9 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import { ID } from '../../../../../misc/cafy-id';
import * as ms from 'ms';
import { pack } from '../../../../../models/drive-file';
import uploadFromUrl from '../../../../../services/drive/upload-from-url';
import define from '../../../define';
import { DriveFiles } from '../../../../../models';
export const meta = {
desc: {
@ -19,7 +19,7 @@ export const meta = {
requireCredential: true,
kind: 'drive-write',
kind: 'write:drive',
params: {
url: {
@ -30,7 +30,6 @@ export const meta = {
folderId: {
validator: $.optional.nullable.type(ID),
default: null as any,
transform: transform
},
isSensitive: {
@ -53,5 +52,5 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
return await pack(await uploadFromUrl(ps.url, user, ps.folderId, null, ps.isSensitive, ps.force), { self: true });
return await DriveFiles.pack(await uploadFromUrl(ps.url, user, ps.folderId, null, ps.isSensitive, ps.force), { self: true });
});

View File

@ -1,7 +1,8 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import DriveFolder, { pack } from '../../../../models/drive-folder';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { DriveFolders } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
export const meta = {
desc: {
@ -13,7 +14,7 @@ export const meta = {
requireCredential: true,
kind: 'drive-read',
kind: 'read:drive',
params: {
limit: {
@ -23,18 +24,15 @@ export const meta = {
sinceId: {
validator: $.optional.type(ID),
transform: transform,
},
untilId: {
validator: $.optional.type(ID),
transform: transform,
},
folderId: {
validator: $.optional.nullable.type(ID),
default: null as any,
transform: transform,
}
},
@ -47,29 +45,16 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const sort = {
_id: -1
};
const query = {
userId: user._id,
parentId: ps.folderId
} as any;
if (ps.sinceId) {
sort._id = 1;
query._id = {
$gt: ps.sinceId
};
} else if (ps.untilId) {
query._id = {
$lt: ps.untilId
};
const query = makePaginationQuery(DriveFolders.createQueryBuilder('folder'), ps.sinceId, ps.untilId)
.andWhere('folder.userId = :userId', { userId: user.id });
if (ps.folderId) {
query.andWhere('folder.parentId = :parentId', { parentId: ps.folderId });
} else {
query.andWhere('folder.parentId IS NULL');
}
const folders = await DriveFolder
.find(query, {
limit: ps.limit,
sort: sort
});
const folders = await query.take(ps.limit).getMany();
return await Promise.all(folders.map(folder => pack(folder)));
return await Promise.all(folders.map(folder => DriveFolders.pack(folder)));
});

View File

@ -1,9 +1,10 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder';
import { ID } from '../../../../../misc/cafy-id';
import { publishDriveStream } from '../../../../../services/stream';
import define from '../../../define';
import { ApiError } from '../../../error';
import { DriveFolders } from '../../../../../models';
import { genId } from '../../../../../misc/gen-id';
export const meta = {
stability: 'stable',
@ -17,11 +18,11 @@ export const meta = {
requireCredential: true,
kind: 'drive-write',
kind: 'write:drive',
params: {
name: {
validator: $.optional.str.pipe(isValidFolderName),
validator: $.optional.str.pipe(DriveFolders.validateFolderName),
default: 'Untitled',
desc: {
'ja-JP': 'フォルダ名',
@ -31,7 +32,6 @@ export const meta = {
parentId: {
validator: $.optional.nullable.type(ID),
transform: transform,
desc: {
'ja-JP': '親フォルダID',
'en-US': 'Parent folder ID'
@ -53,29 +53,29 @@ export default define(meta, async (ps, user) => {
let parent = null;
if (ps.parentId) {
// Fetch parent folder
parent = await DriveFolder
.findOne({
_id: ps.parentId,
userId: user._id
});
parent = await DriveFolders.findOne({
id: ps.parentId,
userId: user.id
});
if (parent === null) {
if (parent == null) {
throw new ApiError(meta.errors.noSuchFolder);
}
}
// Create folder
const folder = await DriveFolder.insert({
const folder = await DriveFolders.save({
id: genId(),
createdAt: new Date(),
name: ps.name,
parentId: parent !== null ? parent._id : null,
userId: user._id
parentId: parent !== null ? parent.id : null,
userId: user.id
});
const folderObj = await pack(folder);
const folderObj = await DriveFolders.pack(folder);
// Publish folderCreated event
publishDriveStream(user._id, 'folderCreated', folderObj);
publishDriveStream(user.id, 'folderCreated', folderObj);
return folderObj;
});

View File

@ -1,10 +1,9 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder from '../../../../../models/drive-folder';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { publishDriveStream } from '../../../../../services/stream';
import DriveFile from '../../../../../models/drive-file';
import { ApiError } from '../../../error';
import { DriveFolders, DriveFiles } from '../../../../../models';
export const meta = {
stability: 'stable',
@ -18,12 +17,11 @@ export const meta = {
requireCredential: true,
kind: 'drive-write',
kind: 'write:drive',
params: {
folderId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のフォルダID',
'en-US': 'Target folder ID'
@ -48,29 +46,26 @@ export const meta = {
export default define(meta, async (ps, user) => {
// Get folder
const folder = await DriveFolder
.findOne({
_id: ps.folderId,
userId: user._id
});
const folder = await DriveFolders.findOne({
id: ps.folderId,
userId: user.id
});
if (folder === null) {
if (folder == null) {
throw new ApiError(meta.errors.noSuchFolder);
}
const [childFoldersCount, childFilesCount] = await Promise.all([
DriveFolder.count({ parentId: folder._id }),
DriveFile.count({ 'metadata.folderId': folder._id })
DriveFolders.count({ parentId: folder.id }),
DriveFiles.count({ folderId: folder.id })
]);
if (childFoldersCount !== 0 || childFilesCount !== 0) {
throw new ApiError(meta.errors.hasChildFilesOrFolders);
}
await DriveFolder.remove({ _id: folder._id });
await DriveFolders.delete(folder.id);
// Publish folderCreated event
publishDriveStream(user._id, 'folderDeleted', folder._id);
return;
publishDriveStream(user.id, 'folderDeleted', folder.id);
});

View File

@ -1,14 +1,14 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder, { pack } from '../../../../../models/drive-folder';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { DriveFolders } from '../../../../../models';
export const meta = {
tags: ['drive'],
requireCredential: true,
kind: 'drive-read',
kind: 'read:drive',
params: {
name: {
@ -17,7 +17,6 @@ export const meta = {
parentId: {
validator: $.optional.nullable.type(ID),
transform: transform,
default: null as any,
desc: {
'ja-JP': 'フォルダID'
@ -34,12 +33,11 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const folders = await DriveFolder
.find({
name: ps.name,
userId: user._id,
parentId: ps.parentId
});
const folders = await DriveFolders.find({
name: ps.name,
userId: user.id,
parentId: ps.parentId
});
return await Promise.all(folders.map(folder => pack(folder)));
return await Promise.all(folders.map(folder => DriveFolders.pack(folder)));
});

View File

@ -1,8 +1,8 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder, { pack } from '../../../../../models/drive-folder';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { ApiError } from '../../../error';
import { DriveFolders } from '../../../../../models';
export const meta = {
stability: 'stable',
@ -16,12 +16,11 @@ export const meta = {
requireCredential: true,
kind: 'drive-read',
kind: 'read:drive',
params: {
folderId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のフォルダID',
'en-US': 'Target folder ID'
@ -44,17 +43,16 @@ export const meta = {
export default define(meta, async (ps, user) => {
// Get folder
const folder = await DriveFolder
.findOne({
_id: ps.folderId,
userId: user._id
});
const folder = await DriveFolders.findOne({
id: ps.folderId,
userId: user.id
});
if (folder === null) {
if (folder == null) {
throw new ApiError(meta.errors.noSuchFolder);
}
return await pack(folder, {
return await DriveFolders.pack(folder, {
detail: true
});
});

View File

@ -1,9 +1,9 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder';
import { ID } from '../../../../../misc/cafy-id';
import { publishDriveStream } from '../../../../../services/stream';
import define from '../../../define';
import { ApiError } from '../../../error';
import { DriveFolders } from '../../../../../models';
export const meta = {
stability: 'stable',
@ -17,12 +17,11 @@ export const meta = {
requireCredential: true,
kind: 'drive-write',
kind: 'write:drive',
params: {
folderId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のフォルダID',
'en-US': 'Target folder ID'
@ -30,7 +29,7 @@ export const meta = {
},
name: {
validator: $.optional.str.pipe(isValidFolderName),
validator: $.optional.str.pipe(DriveFolders.validateFolderName),
desc: {
'ja-JP': 'フォルダ名',
'en-US': 'Folder name'
@ -39,7 +38,6 @@ export const meta = {
parentId: {
validator: $.optional.nullable.type(ID),
transform: transform,
desc: {
'ja-JP': '親フォルダID',
'en-US': 'Parent folder ID'
@ -70,46 +68,41 @@ export const meta = {
export default define(meta, async (ps, user) => {
// Fetch folder
const folder = await DriveFolder
.findOne({
_id: ps.folderId,
userId: user._id
});
const folder = await DriveFolders.findOne({
id: ps.folderId,
userId: user.id
});
if (folder === null) {
if (folder == null) {
throw new ApiError(meta.errors.noSuchFolder);
}
if (ps.name) folder.name = ps.name;
if (ps.parentId !== undefined) {
if (ps.parentId.equals(folder._id)) {
if (ps.parentId === folder.id) {
throw new ApiError(meta.errors.recursiveNesting);
} else if (ps.parentId === null) {
folder.parentId = null;
} else {
// Get parent folder
const parent = await DriveFolder
.findOne({
_id: ps.parentId,
userId: user._id
});
const parent = await DriveFolders.findOne({
id: ps.parentId,
userId: user.id
});
if (parent === null) {
if (parent == null) {
throw new ApiError(meta.errors.noSuchParentFolder);
}
// Check if the circular reference will occur
async function checkCircle(folderId: any): Promise<boolean> {
// Fetch folder
const folder2 = await DriveFolder.findOne({
_id: folderId
}, {
_id: true,
parentId: true
const folder2 = await DriveFolders.findOne({
id: folderId
});
if (folder2._id.equals(folder._id)) {
if (folder2.id === folder.id) {
return true;
} else if (folder2.parentId) {
return await checkCircle(folder2.parentId);
@ -124,22 +117,20 @@ export default define(meta, async (ps, user) => {
}
}
folder.parentId = parent._id;
folder.parentId = parent.id;
}
}
// Update
DriveFolder.update(folder._id, {
$set: {
name: folder.name,
parentId: folder.parentId
}
DriveFolders.update(folder.id, {
name: folder.name,
parentId: folder.parentId
});
const folderObj = await pack(folder);
const folderObj = await DriveFolders.pack(folder);
// Publish folderUpdated event
publishDriveStream(user._id, 'folderUpdated', folderObj);
publishDriveStream(user.id, 'folderUpdated', folderObj);
return folderObj;
});

View File

@ -1,14 +1,15 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import DriveFile, { packMany } from '../../../../models/drive-file';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { DriveFiles } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
export const meta = {
tags: ['drive'],
requireCredential: true,
kind: 'drive-read',
kind: 'read:drive',
params: {
limit: {
@ -18,12 +19,10 @@ export const meta = {
sinceId: {
validator: $.optional.type(ID),
transform: transform,
},
untilId: {
validator: $.optional.type(ID),
transform: transform,
},
type: {
@ -40,35 +39,18 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const sort = {
_id: -1
};
const query = {
'metadata.userId': user._id,
'metadata.deletedAt': { $exists: false }
} as any;
if (ps.sinceId) {
sort._id = 1;
query._id = {
$gt: ps.sinceId
};
} else if (ps.untilId) {
query._id = {
$lt: ps.untilId
};
}
const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId)
.andWhere('file.userId = :userId', { userId: user.id });
if (ps.type) {
query.contentType = new RegExp(`^${ps.type.replace(/\*/g, '.+?')}$`);
if (ps.type.endsWith('/*')) {
query.andWhere('file.type like :type', { type: ps.type.replace('/*', '/') + '%' });
} else {
query.andWhere('file.type = :type', { type: ps.type });
}
}
const files = await DriveFile
.find(query, {
limit: ps.limit,
sort: sort
});
const files = await query.take(ps.limit).getMany();
return await packMany(files, { self: true });
return await DriveFiles.packMany(files, { detail: false, self: true });
});

View File

@ -1,6 +1,7 @@
import $ from 'cafy';
import define from '../../define';
import Instance from '../../../../models/instance';
import { Instances } from '../../../../models';
import fetchMeta from '../../../../misc/fetch-meta';
export const meta = {
tags: ['federation'],
@ -37,92 +38,55 @@ export const meta = {
};
export default define(meta, async (ps, me) => {
let sort;
const query = Instances.createQueryBuilder('instance');
if (ps.sort) {
if (ps.sort == '+notes') {
sort = {
notesCount: -1
};
} else if (ps.sort == '-notes') {
sort = {
notesCount: 1
};
} else if (ps.sort == '+users') {
sort = {
usersCount: -1
};
} else if (ps.sort == '-users') {
sort = {
usersCount: 1
};
} else if (ps.sort == '+following') {
sort = {
followingCount: -1
};
} else if (ps.sort == '-following') {
sort = {
followingCount: 1
};
} else if (ps.sort == '+followers') {
sort = {
followersCount: -1
};
} else if (ps.sort == '-followers') {
sort = {
followersCount: 1
};
} else if (ps.sort == '+caughtAt') {
sort = {
caughtAt: -1
};
} else if (ps.sort == '-caughtAt') {
sort = {
caughtAt: 1
};
} else if (ps.sort == '+lastCommunicatedAt') {
sort = {
lastCommunicatedAt: -1
};
} else if (ps.sort == '-lastCommunicatedAt') {
sort = {
lastCommunicatedAt: 1
};
} else if (ps.sort == '+driveUsage') {
sort = {
driveUsage: -1
};
} else if (ps.sort == '-driveUsage') {
sort = {
driveUsage: 1
};
} else if (ps.sort == '+driveFiles') {
sort = {
driveFiles: -1
};
} else if (ps.sort == '-driveFiles') {
sort = {
driveFiles: 1
};
}
} else {
sort = {
_id: -1
};
switch (ps.sort) {
case '+notes': query.orderBy('instance.notesCount', 'DESC'); break;
case '-notes': query.orderBy('instance.notesCount', 'ASC'); break;
case '+usersCount': query.orderBy('instance.usersCount', 'DESC'); break;
case '-usersCount': query.orderBy('instance.usersCount', 'ASC'); break;
case '+followingCount': query.orderBy('instance.followingCount', 'DESC'); break;
case '-followingCount': query.orderBy('instance.followingCount', 'ASC'); break;
case '+followersCount': query.orderBy('instance.followersCount', 'DESC'); break;
case '-followersCount': query.orderBy('instance.followersCount', 'ASC'); break;
case '+caughtAt': query.orderBy('instance.caughtAt', 'DESC'); break;
case '-caughtAt': query.orderBy('instance.caughtAt', 'ASC'); break;
case '+lastCommunicatedAt': query.orderBy('instance.lastCommunicatedAt', 'DESC'); break;
case '-lastCommunicatedAt': query.orderBy('instance.lastCommunicatedAt', 'ASC'); break;
case '+driveUsage': query.orderBy('instance.driveUsage', 'DESC'); break;
case '-driveUsage': query.orderBy('instance.driveUsage', 'ASC'); break;
case '+driveFiles': query.orderBy('instance.driveFiles', 'DESC'); break;
case '-driveFiles': query.orderBy('instance.driveFiles', 'ASC'); break;
default: query.orderBy('instance.id', 'DESC'); break;
}
const q = {} as any;
if (typeof ps.blocked === 'boolean') {
const meta = await fetchMeta();
if (ps.blocked) {
query.andWhere('instance.host IN (:...blocks)', { blocks: meta.blockedHosts });
} else {
query.andWhere('instance.host NOT IN (:...blocks)', { blocks: meta.blockedHosts });
}
}
if (typeof ps.blocked === 'boolean') q.isBlocked = ps.blocked;
if (typeof ps.notResponding === 'boolean') q.isNotResponding = ps.notResponding;
if (typeof ps.markedAsClosed === 'boolean') q.isMarkedAsClosed = ps.markedAsClosed;
if (typeof ps.notResponding === 'boolean') {
if (ps.notResponding) {
query.andWhere('instance.isNotResponding = TRUE');
} else {
query.andWhere('instance.isNotResponding = FALSE');
}
}
const instances = await Instance
.find(q, {
limit: ps.limit,
sort: sort,
skip: ps.offset
});
if (typeof ps.markedAsClosed === 'boolean') {
if (ps.markedAsClosed) {
query.andWhere('instance.isMarkedAsClosed = TRUE');
} else {
query.andWhere('instance.isMarkedAsClosed = FALSE');
}
}
const instances = await query.take(ps.limit).skip(ps.offset).getMany();
return instances;
});

View File

@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../define';
import Instance from '../../../../models/instance';
import { Instances } from '../../../../models';
export const meta = {
tags: ['federation'],
@ -15,7 +15,7 @@ export const meta = {
};
export default define(meta, async (ps, me) => {
const instance = await Instance
const instance = await Instances
.findOne({ host: ps.host });
return instance;

View File

@ -1,12 +1,11 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import * as ms from 'ms';
import { pack } from '../../../../models/user';
import Following from '../../../../models/following';
import create from '../../../../services/following/create';
import define from '../../define';
import { ApiError } from '../../error';
import { getUser } from '../../common/getters';
import { Followings, Users } from '../../../../models';
export const meta = {
stability: 'stable',
@ -25,12 +24,11 @@ export const meta = {
requireCredential: true,
kind: 'following-write',
kind: 'write:following',
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'
@ -75,7 +73,7 @@ export default define(meta, async (ps, user) => {
const follower = user;
// 自分自身
if (user._id.equals(ps.userId)) {
if (user.id === ps.userId) {
throw new ApiError(meta.errors.followeeIsYourself);
}
@ -86,12 +84,12 @@ export default define(meta, async (ps, user) => {
});
// Check if already following
const exist = await Following.findOne({
followerId: follower._id,
followeeId: followee._id
const exist = await Followings.findOne({
followerId: follower.id,
followeeId: followee.id
});
if (exist !== null) {
if (exist != null) {
throw new ApiError(meta.errors.alreadyFollowing);
}
@ -103,5 +101,5 @@ export default define(meta, async (ps, user) => {
throw e;
}
return await pack(followee._id, user);
return await Users.pack(followee.id, user);
});

View File

@ -1,12 +1,11 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import * as ms from 'ms';
import { pack } from '../../../../models/user';
import Following from '../../../../models/following';
import deleteFollowing from '../../../../services/following/delete';
import define from '../../define';
import { ApiError } from '../../error';
import { getUser } from '../../common/getters';
import { Followings, Users } from '../../../../models';
export const meta = {
stability: 'stable',
@ -25,12 +24,11 @@ export const meta = {
requireCredential: true,
kind: 'following-write',
kind: 'write:following',
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'
@ -63,7 +61,7 @@ export default define(meta, async (ps, user) => {
const follower = user;
// Check if the followee is yourself
if (user._id.equals(ps.userId)) {
if (user.id === ps.userId) {
throw new ApiError(meta.errors.followeeIsYourself);
}
@ -74,16 +72,16 @@ export default define(meta, async (ps, user) => {
});
// Check not following
const exist = await Following.findOne({
followerId: follower._id,
followeeId: followee._id
const exist = await Followings.findOne({
followerId: follower.id,
followeeId: followee.id
});
if (exist === null) {
if (exist == null) {
throw new ApiError(meta.errors.notFollowing);
}
await deleteFollowing(follower, followee);
return await pack(followee._id, user);
return await Users.pack(followee.id, user);
});

View File

@ -1,5 +1,5 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import { ID } from '../../../../../misc/cafy-id';
import acceptFollowRequest from '../../../../../services/following/requests/accept';
import define from '../../../define';
import { ApiError } from '../../../error';
@ -15,12 +15,11 @@ export const meta = {
requireCredential: true,
kind: 'following-write',
kind: 'write:following',
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'

View File

@ -1,10 +1,10 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import { ID } from '../../../../../misc/cafy-id';
import cancelFollowRequest from '../../../../../services/following/requests/cancel';
import { pack } from '../../../../../models/user';
import define from '../../../define';
import { ApiError } from '../../../error';
import { getUser } from '../../../common/getters';
import { Users } from '../../../../../models';
export const meta = {
desc: {
@ -16,12 +16,11 @@ export const meta = {
requireCredential: true,
kind: 'following-write',
kind: 'write:following',
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'
@ -58,5 +57,5 @@ export default define(meta, async (ps, user) => {
throw e;
}
return await pack(followee._id, user);
return await Users.pack(followee.id, user);
});

View File

@ -1,5 +1,5 @@
import FollowRequest, { pack } from '../../../../../models/follow-request';
import define from '../../../define';
import { FollowRequests } from '../../../../../models';
export const meta = {
desc: {
@ -11,13 +11,13 @@ export const meta = {
requireCredential: true,
kind: 'following-read'
kind: 'read:following'
};
export default define(meta, async (ps, user) => {
const reqs = await FollowRequest.find({
followeeId: user._id
const reqs = await FollowRequests.find({
followeeId: user.id
});
return await Promise.all(reqs.map(req => pack(req)));
return await Promise.all(reqs.map(req => FollowRequests.pack(req)));
});

View File

@ -1,5 +1,5 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import { ID } from '../../../../../misc/cafy-id';
import rejectFollowRequest from '../../../../../services/following/requests/reject';
import define from '../../../define';
import { ApiError } from '../../../error';
@ -15,12 +15,11 @@ export const meta = {
requireCredential: true,
kind: 'following-write',
kind: 'write:following',
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'

View File

@ -1,7 +1,9 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import ReversiGame, { pack } from '../../../../../models/games/reversi/game';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { ReversiGames } from '../../../../../models';
import { makePaginationQuery } from '../../../common/make-pagination-query';
import { Brackets } from 'typeorm';
export const meta = {
tags: ['games'],
@ -14,12 +16,10 @@ export const meta = {
sinceId: {
validator: $.optional.type(ID),
transform: transform,
},
untilId: {
validator: $.optional.type(ID),
transform: transform,
},
my: {
@ -30,39 +30,20 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const q: any = ps.my ? {
isStarted: true,
$or: [{
user1Id: user._id
}, {
user2Id: user._id
}]
} : {
isStarted: true
};
const query = makePaginationQuery(ReversiGames.createQueryBuilder('game'), ps.sinceId, ps.untilId)
.andWhere('game.isStarted = TRUE');
const sort = {
_id: -1
};
if (ps.sinceId) {
sort._id = 1;
q._id = {
$gt: ps.sinceId
};
} else if (ps.untilId) {
q._id = {
$lt: ps.untilId
};
if (ps.my) {
query.andWhere(new Brackets(qb => { qb
.where('game.user1Id = :userId', { userId: user.id })
.orWhere('game.user2Id = :userId', { userId: user.id });
}));
}
// Fetch games
const games = await ReversiGame.find(q, {
sort: sort,
limit: ps.limit
});
const games = await query.take(ps.limit).getMany();
return await Promise.all(games.map((g) => pack(g, user, {
return await Promise.all(games.map((g) => ReversiGames.pack(g, user, {
detail: false
})));
});

View File

@ -1,9 +1,9 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../../misc/cafy-id';
import ReversiGame, { pack } from '../../../../../../models/games/reversi/game';
import { ID } from '../../../../../../misc/cafy-id';
import Reversi from '../../../../../../games/reversi/core';
import define from '../../../../define';
import { ApiError } from '../../../../error';
import { ReversiGames } from '../../../../../../models';
export const meta = {
tags: ['games'],
@ -11,7 +11,6 @@ export const meta = {
params: {
gameId: {
validator: $.type(ID),
transform: transform,
},
},
@ -25,22 +24,23 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const game = await ReversiGame.findOne({ _id: ps.gameId });
const game = await ReversiGames.findOne(ps.gameId);
if (game == null) {
throw new ApiError(meta.errors.noSuchGame);
}
const o = new Reversi(game.settings.map, {
isLlotheo: game.settings.isLlotheo,
canPutEverywhere: game.settings.canPutEverywhere,
loopedBoard: game.settings.loopedBoard
const o = new Reversi(game.map, {
isLlotheo: game.isLlotheo,
canPutEverywhere: game.canPutEverywhere,
loopedBoard: game.loopedBoard
});
for (const log of game.logs)
for (const log of game.logs) {
o.put(log.color, log.pos);
}
const packed = await pack(game, user);
const packed = await ReversiGames.pack(game, user);
return Object.assign({
board: o.board,

View File

@ -1,9 +1,9 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../../misc/cafy-id';
import ReversiGame, { pack } from '../../../../../../models/games/reversi/game';
import { ID } from '../../../../../../misc/cafy-id';
import { publishReversiGameStream } from '../../../../../../services/stream';
import define from '../../../../define';
import { ApiError } from '../../../../error';
import { ReversiGames } from '../../../../../../models';
export const meta = {
tags: ['games'],
@ -17,7 +17,6 @@ export const meta = {
params: {
gameId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '投了したい対局'
}
@ -46,7 +45,7 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const game = await ReversiGame.findOne({ _id: ps.gameId });
const game = await ReversiGames.findOne(ps.gameId);
if (game == null) {
throw new ApiError(meta.errors.noSuchGame);
@ -56,26 +55,20 @@ export default define(meta, async (ps, user) => {
throw new ApiError(meta.errors.alreadyEnded);
}
if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) {
if ((game.user1Id !== user.id) && (game.user2Id !== user.id)) {
throw new ApiError(meta.errors.accessDenied);
}
const winnerId = game.user1Id.equals(user._id) ? game.user2Id : game.user1Id;
const winnerId = game.user1Id === user.id ? game.user2Id : game.user1Id;
await ReversiGame.update({
_id: game._id
}, {
$set: {
surrendered: user._id,
isEnded: true,
winnerId: winnerId
}
await ReversiGames.update(game.id, {
surrendered: user.id,
isEnded: true,
winnerId: winnerId
});
publishReversiGameStream(game._id, 'ended', {
publishReversiGameStream(game.id, 'ended', {
winnerId: winnerId,
game: await pack(game._id, user)
game: await ReversiGames.pack(game.id, user)
});
return;
});

View File

@ -1,5 +1,5 @@
import Matching, { pack as packMatching } from '../../../../../models/games/reversi/matching';
import define from '../../../define';
import { ReversiMatchings } from '../../../../../models';
export const meta = {
tags: ['games'],
@ -9,13 +9,9 @@ export const meta = {
export default define(meta, async (ps, user) => {
// Find session
const invitations = await Matching.find({
childId: user._id
}, {
sort: {
_id: -1
}
const invitations = await ReversiMatchings.find({
childId: user.id
});
return await Promise.all(invitations.map((i) => packMatching(i, user)));
return await Promise.all(invitations.map((i) => ReversiMatchings.pack(i, user)));
});

View File

@ -1,12 +1,14 @@
import $ from 'cafy';
import ID, { transform } from '../../../../../misc/cafy-id';
import Matching, { pack as packMatching } from '../../../../../models/games/reversi/matching';
import ReversiGame, { pack as packGame } from '../../../../../models/games/reversi/game';
import { ID } from '../../../../../misc/cafy-id';
import { publishMainStream, publishReversiStream } from '../../../../../services/stream';
import { eighteight } from '../../../../../games/reversi/maps';
import define from '../../../define';
import { ApiError } from '../../../error';
import { getUser } from '../../../common/getters';
import { genId } from '../../../../../misc/gen-id';
import { ReversiMatchings, ReversiGames } from '../../../../../models';
import { ReversiGame } from '../../../../../models/entities/games/reversi/game';
import { ReversiMatching } from '../../../../../models/entities/games/reversi/matching';
export const meta = {
tags: ['games'],
@ -16,7 +18,6 @@ export const meta = {
params: {
userId: {
validator: $.type(ID),
transform: transform,
desc: {
'ja-JP': '対象のユーザーのID',
'en-US': 'Target user ID'
@ -41,50 +42,47 @@ export const meta = {
export default define(meta, async (ps, user) => {
// Myself
if (ps.userId.equals(user._id)) {
if (ps.userId === user.id) {
throw new ApiError(meta.errors.isYourself);
}
// Find session
const exist = await Matching.findOne({
const exist = await ReversiMatchings.findOne({
parentId: ps.userId,
childId: user._id
childId: user.id
});
if (exist) {
// Destroy session
Matching.remove({
_id: exist._id
});
ReversiMatchings.delete(exist.id);
// Create game
const game = await ReversiGame.insert({
const game = await ReversiGames.save({
id: genId(),
createdAt: new Date(),
user1Id: exist.parentId,
user2Id: user._id,
user2Id: user.id,
user1Accepted: false,
user2Accepted: false,
isStarted: false,
isEnded: false,
logs: [],
settings: {
map: eighteight.data,
bw: 'random',
isLlotheo: false
}
});
map: eighteight.data,
bw: 'random',
isLlotheo: false
} as ReversiGame);
publishReversiStream(exist.parentId, 'matched', await packGame(game, exist.parentId));
publishReversiStream(exist.parentId, 'matched', await ReversiGames.pack(game, exist.parentId));
const other = await Matching.count({
childId: user._id
const other = await ReversiMatchings.count({
childId: user.id
});
if (other == 0) {
publishMainStream(user._id, 'reversiNoInvites');
publishMainStream(user.id, 'reversiNoInvites');
}
return await packGame(game, user);
return await ReversiGames.pack(game, user);
} else {
// Fetch child
const child = await getUser(ps.userId).catch(e => {
@ -93,21 +91,22 @@ export default define(meta, async (ps, user) => {
});
// 以前のセッションはすべて削除しておく
await Matching.remove({
parentId: user._id
await ReversiMatchings.delete({
parentId: user.id
});
// セッションを作成
const matching = await Matching.insert({
const matching = await ReversiMatchings.save({
id: genId(),
createdAt: new Date(),
parentId: user._id,
childId: child._id
});
parentId: user.id,
childId: child.id
} as ReversiMatching);
const packed = await packMatching(matching, child);
publishReversiStream(child._id, 'invited', packed);
publishMainStream(child._id, 'reversiInvited', packed);
const packed = await ReversiMatchings.pack(matching, child);
publishReversiStream(child.id, 'invited', packed);
publishMainStream(child.id, 'reversiInvited', packed);
return;
return null;
}
});

View File

@ -1,5 +1,5 @@
import Matching from '../../../../../../models/games/reversi/matching';
import define from '../../../../define';
import { ReversiMatchings } from '../../../../../../models';
export const meta = {
tags: ['games'],
@ -8,9 +8,7 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
await Matching.remove({
parentId: user._id
await ReversiMatchings.delete({
parentId: user.id
});
return;
});

View File

@ -1,6 +1,6 @@
import $ from 'cafy';
import define from '../../define';
import Hashtag from '../../../../models/hashtag';
import { Hashtags } from '../../../../models';
export const meta = {
tags: ['hashtags'],
@ -54,40 +54,39 @@ export const meta = {
},
};
const sort: any = {
'+mentionedUsers': { mentionedUsersCount: -1 },
'-mentionedUsers': { mentionedUsersCount: 1 },
'+mentionedLocalUsers': { mentionedLocalUsersCount: -1 },
'-mentionedLocalUsers': { mentionedLocalUsersCount: 1 },
'+mentionedRemoteUsers': { mentionedRemoteUsersCount: -1 },
'-mentionedRemoteUsers': { mentionedRemoteUsersCount: 1 },
'+attachedUsers': { attachedUsersCount: -1 },
'-attachedUsers': { attachedUsersCount: 1 },
'+attachedLocalUsers': { attachedLocalUsersCount: -1 },
'-attachedLocalUsers': { attachedLocalUsersCount: 1 },
'+attachedRemoteUsers': { attachedRemoteUsersCount: -1 },
'-attachedRemoteUsers': { attachedRemoteUsersCount: 1 },
};
export default define(meta, async (ps, me) => {
const q = {} as any;
if (ps.attachedToUserOnly) q.attachedUsersCount = { $ne: 0 };
if (ps.attachedToLocalUserOnly) q.attachedLocalUsersCount = { $ne: 0 };
if (ps.attachedToRemoteUserOnly) q.attachedRemoteUsersCount = { $ne: 0 };
const tags = await Hashtag
.find(q, {
limit: ps.limit,
sort: sort[ps.sort],
fields: {
tag: true,
mentionedUsersCount: true,
mentionedLocalUsersCount: true,
mentionedRemoteUsersCount: true,
attachedUsersCount: true,
attachedLocalUsersCount: true,
attachedRemoteUsersCount: true
}
});
const query = Hashtags.createQueryBuilder('tag');
if (ps.attachedToUserOnly) query.andWhere('tag.attachedUsersCount != 0');
if (ps.attachedToLocalUserOnly) query.andWhere('tag.attachedLocalUsersCount != 0');
if (ps.attachedToRemoteUserOnly) query.andWhere('tag.attachedRemoteUsersCount != 0');
switch (ps.sort) {
case '+mentionedUsers': query.orderBy('tag.mentionedUsersCount', 'DESC'); break;
case '-mentionedUsers': query.orderBy('tag.mentionedUsersCount', 'ASC'); break;
case '+mentionedLocalUsers': query.orderBy('tag.mentionedLocalUsersCount', 'DESC'); break;
case '-mentionedLocalUsers': query.orderBy('tag.mentionedLocalUsersCount', 'ASC'); break;
case '+mentionedRemoteUsers': query.orderBy('tag.mentionedRemoteUsersCount', 'DESC'); break;
case '-mentionedRemoteUsers': query.orderBy('tag.mentionedRemoteUsersCount', 'ASC'); break;
case '+attachedUsers': query.orderBy('tag.attachedUsersCount', 'DESC'); break;
case '-attachedUsers': query.orderBy('tag.attachedUsersCount', 'ASC'); break;
case '+attachedLocalUsers': query.orderBy('tag.attachedLocalUsersCount', 'DESC'); break;
case '-attachedLocalUsers': query.orderBy('tag.attachedLocalUsersCount', 'ASC'); break;
case '+attachedRemoteUsers': query.orderBy('tag.attachedRemoteUsersCount', 'DESC'); break;
case '-attachedRemoteUsers': query.orderBy('tag.attachedRemoteUsersCount', 'ASC'); break;
}
query.select([
'tag.name',
'tag.mentionedUsersCount',
'tag.mentionedLocalUsersCount',
'tag.mentionedRemoteUsersCount',
'tag.attachedUsersCount',
'tag.attachedLocalUsersCount',
'tag.attachedRemoteUsersCount',
]);
const tags = await query.take(ps.limit).getMany();
return tags;
});

View File

@ -1,7 +1,6 @@
import $ from 'cafy';
import Hashtag from '../../../../models/hashtag';
import define from '../../define';
import * as escapeRegexp from 'escape-regexp';
import { Hashtags } from '../../../../models';
export const meta = {
desc: {
@ -46,16 +45,12 @@ export const meta = {
};
export default define(meta, async (ps) => {
const hashtags = await Hashtag
.find({
tag: new RegExp('^' + escapeRegexp(ps.query.toLowerCase()))
}, {
sort: {
count: -1
},
limit: ps.limit,
skip: ps.offset
});
const hashtags = await Hashtags.createQueryBuilder('tag')
.where('tag.name like :q', { q: ps.query.toLowerCase() + '%' })
.orderBy('tag.count', 'DESC')
.take(ps.limit)
.skip(ps.offset)
.getMany();
return hashtags.map(tag => tag.tag);
return hashtags.map(tag => tag.name);
});

View File

@ -1,17 +1,19 @@
import Note from '../../../../models/note';
import { erase } from '../../../../prelude/array';
import define from '../../define';
import fetchMeta from '../../../../misc/fetch-meta';
import { Notes } from '../../../../models';
import { Note } from '../../../../models/entities/note';
/*
トレンドに載るためには「『直近a分間のユニーク投稿数が今からa分前今からb分前の間のユニーク投稿数のn倍以上』のハッシュタグの上位5位以内に入る」ことが必要
ユニーク投稿数とはそのハッシュタグと投稿ユーザーのペアのカウントで、例えば同じユーザーが複数回同じハッシュタグを投稿してもそのハッシュタグのユニーク投稿数は1とカウントされる
..が理想だけどPostgreSQLでどうするのか分からないので単に「直近Aの内に投稿されたユニーク投稿数が多いハッシュタグ」で妥協する
*/
const rangeA = 1000 * 60 * 30; // 30分
const rangeB = 1000 * 60 * 120; // 2時間
const coefficient = 1.25; // 「n倍」の部分
const requiredUsers = 3; // 最低何人がそのタグを投稿している必要があるか
//const rangeB = 1000 * 60 * 120; // 2時間
//const coefficient = 1.25; // 「n倍」の部分
//const requiredUsers = 3; // 最低何人がそのタグを投稿している必要があるか
const max = 5;
@ -23,92 +25,47 @@ export const meta = {
export default define(meta, async () => {
const instance = await fetchMeta();
const hidedTags = instance.hidedTags.map(t => t.toLowerCase());
const hiddenTags = instance.hiddenTags.map(t => t.toLowerCase());
//#region 1. 直近Aの内に投稿されたハッシュタグ(とユーザーのペア)を集計
const data = await Note.aggregate([{
$match: {
createdAt: {
$gt: new Date(Date.now() - rangeA)
},
tagsLower: {
$exists: true,
$ne: []
}
}
}, {
$unwind: '$tagsLower'
}, {
$group: {
_id: { tag: '$tagsLower', userId: '$userId' }
}
}]) as {
_id: {
tag: string;
userId: any;
}
}[];
//#endregion
const tagNotes = await Notes.createQueryBuilder('note')
.where(`note.createdAt > :date`, { date: new Date(Date.now() - rangeA) })
.andWhere(`note.tags != '{}'`)
.select(['note.tags', 'note.userId'])
.getMany();
if (data.length == 0) {
if (tagNotes.length === 0) {
return [];
}
const tags: {
name: string;
count: number;
users: Note['userId'][];
}[] = [];
// カウント
for (const x of data.map(x => x._id).filter(x => !hidedTags.includes(x.tag))) {
const i = tags.findIndex(tag => tag.name == x.tag);
if (i != -1) {
tags[i].count++;
} else {
tags.push({
name: x.tag,
count: 1
});
for (const note of tagNotes) {
for (const tag of note.tags) {
if (hiddenTags.includes(tag)) continue;
const x = tags.find(x => x.name === tag);
if (x) {
if (!x.users.includes(note.userId)) {
x.users.push(note.userId);
}
} else {
tags.push({
name: tag,
users: [note.userId]
});
}
}
}
// 最低要求投稿者数を下回るならカットする
const limitedTags = tags.filter(tag => tag.count >= requiredUsers);
//#region 2. 1で取得したそれぞれのタグについて、「直近a分間のユニーク投稿数が今からa分前今からb分前の間のユニーク投稿数のn倍以上」かどうかを判定する
const hotsPromises = limitedTags.map(async tag => {
const passedCount = (await Note.distinct('userId', {
tagsLower: tag.name,
createdAt: {
$lt: new Date(Date.now() - rangeA),
$gt: new Date(Date.now() - rangeB)
}
}) as any).length;
if (tag.count >= (passedCount * coefficient)) {
return tag;
} else {
return null;
}
});
//#endregion
// タグを人気順に並べ替え
let hots = erase(null, await Promise.all(hotsPromises))
.sort((a, b) => b.count - a.count)
const hots = tags
.sort((a, b) => b.users.length - a.users.length)
.map(tag => tag.name)
.slice(0, max);
//#region 3. もし上記の方法でのトレンド抽出の結果、求められているタグ数に達しなければ「ただ単に現在投稿数が多いハッシュタグ」に切り替える
if (hots.length < max) {
hots = hots.concat(tags
.filter(tag => hots.indexOf(tag.name) == -1)
.sort((a, b) => b.count - a.count)
.map(tag => tag.name)
.slice(0, max - hots.length));
}
//#endregion
//#region 2(または3)で話題と判定されたタグそれぞれについて過去の投稿数グラフを取得する
const countPromises: Promise<any[]>[] = [];
@ -118,23 +75,25 @@ export default define(meta, async () => {
const interval = 1000 * 60 * 10;
for (let i = 0; i < range; i++) {
countPromises.push(Promise.all(hots.map(tag => Note.distinct('userId', {
tagsLower: tag,
createdAt: {
$lt: new Date(Date.now() - (interval * i)),
$gt: new Date(Date.now() - (interval * (i + 1)))
}
}))));
countPromises.push(Promise.all(hots.map(tag => Notes.createQueryBuilder('note')
.select('count(distinct note.userId)')
.where(':tag = ANY(note.tags)', { tag: tag })
.andWhere('note.createdAt < :lt', { lt: new Date(Date.now() - (interval * i)) })
.andWhere('note.createdAt > :gt', { gt: new Date(Date.now() - (interval * (i + 1))) })
.getRawOne()
.then(x => parseInt(x.count, 10))
)));
}
const countsLog = await Promise.all(countPromises);
const totalCounts: any = await Promise.all(hots.map(tag => Note.distinct('userId', {
tagsLower: tag,
createdAt: {
$gt: new Date(Date.now() - (interval * range))
}
})));
const totalCounts: any = await Promise.all(hots.map(tag => Notes.createQueryBuilder('note')
.select('count(distinct note.userId)')
.where(':tag = ANY(note.tags)', { tag: tag })
.andWhere('note.createdAt > :gt', { gt: new Date(Date.now() - (interval * range)) })
.getRawOne()
.then(x => parseInt(x.count, 10))
));
//#endregion
const stats = hots.map((tag, i) => ({

View File

@ -1,6 +1,6 @@
import $ from 'cafy';
import User, { pack } from '../../../../models/user';
import define from '../../define';
import { Users } from '../../../../models';
export const meta = {
requireCredential: false,
@ -54,39 +54,32 @@ export const meta = {
},
};
const sort: any = {
'+follower': { followersCount: -1 },
'-follower': { followersCount: 1 },
'+createdAt': { createdAt: -1 },
'-createdAt': { createdAt: 1 },
'+updatedAt': { updatedAt: -1 },
'-updatedAt': { updatedAt: 1 },
};
export default define(meta, async (ps, me) => {
const q = {
tags: ps.tag,
$and: []
} as any;
const query = Users.createQueryBuilder('user')
.where(':tag = ANY(user.tags)', { tag: ps.tag });
// state
q.$and.push(
ps.state == 'alive' ? { updatedAt: { $gt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 5)) } } :
{}
);
const recent = new Date(Date.now() - (1000 * 60 * 60 * 24 * 5));
// origin
q.$and.push(
ps.origin == 'local' ? { host: null } :
ps.origin == 'remote' ? { host: { $ne: null } } :
{}
);
if (ps.state === 'alive') {
query.andWhere('user.updatedAt > :date', { date: recent });
}
const users = await User
.find(q, {
limit: ps.limit,
sort: sort[ps.sort],
});
if (ps.origin === 'local') {
query.andWhere('user.host IS NULL');
} else if (ps.origin === 'remote') {
query.andWhere('user.host IS NOT NULL');
}
return await Promise.all(users.map(user => pack(user, me, { detail: true })));
switch (ps.sort) {
case '+follower': query.orderBy('user.followersCount', 'DESC'); break;
case '-follower': query.orderBy('user.followersCount', 'ASC'); break;
case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break;
case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break;
case '+updatedAt': query.orderBy('user.updatedAt', 'DESC'); break;
case '-updatedAt': query.orderBy('user.updatedAt', 'ASC'); break;
}
const users = await query.take(ps.limit).getMany();
return await Users.packMany(users, me, { detail: true });
});

View File

@ -1,5 +1,5 @@
import { pack } from '../../../models/user';
import define from '../define';
import { Users } from '../../../models';
export const meta = {
stability: 'stable',
@ -22,7 +22,7 @@ export const meta = {
export default define(meta, async (ps, user, app) => {
const isSecure = user != null && app == null;
return await pack(user, user, {
return await Users.pack(user, user, {
detail: true,
includeHasUnreadNotes: true,
includeSecrets: isSecure

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import * as speakeasy from 'speakeasy';
import User from '../../../../../models/user';
import define from '../../../define';
import { Users } from '../../../../../models';
export const meta = {
requireCredential: true,
@ -32,12 +32,8 @@ export default define(meta, async (ps, user) => {
throw new Error('not verified');
}
await User.update(user._id, {
$set: {
'twoFactorSecret': user.twoFactorTempSecret,
'twoFactorEnabled': true
}
await Users.update(user.id, {
twoFactorSecret: user.twoFactorTempSecret,
twoFactorEnabled: true
});
return;
});

View File

@ -2,9 +2,9 @@ import $ from 'cafy';
import * as bcrypt from 'bcryptjs';
import * as speakeasy from 'speakeasy';
import * as QRCode from 'qrcode';
import User from '../../../../../models/user';
import config from '../../../../../config';
import define from '../../../define';
import { Users } from '../../../../../models';
export const meta = {
requireCredential: true,
@ -31,10 +31,8 @@ export default define(meta, async (ps, user) => {
length: 32
});
await User.update(user._id, {
$set: {
twoFactorTempSecret: secret.base32
}
await Users.update(user.id, {
twoFactorTempSecret: secret.base32
});
// Get the data URL of the authenticator URL

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import * as bcrypt from 'bcryptjs';
import User from '../../../../../models/user';
import define from '../../../define';
import { Users } from '../../../../../models';
export const meta = {
requireCredential: true,
@ -23,11 +23,9 @@ export default define(meta, async (ps, user) => {
throw new Error('incorrect password');
}
await User.update(user._id, {
$set: {
'twoFactorSecret': null,
'twoFactorEnabled': false
}
await Users.update(user.id, {
twoFactorSecret: null,
twoFactorEnabled: false
});
return;

View File

@ -1,7 +1,6 @@
import $ from 'cafy';
import AccessToken from '../../../../models/access-token';
import { pack } from '../../../../models/app';
import define from '../../define';
import { AccessTokens, Apps } from '../../../../models';
export const meta = {
requireCredential: true,
@ -28,18 +27,18 @@ export const meta = {
export default define(meta, async (ps, user) => {
// Get tokens
const tokens = await AccessToken
.find({
userId: user._id
}, {
limit: ps.limit,
skip: ps.offset,
sort: {
_id: ps.sort == 'asc' ? 1 : -1
}
});
const tokens = await AccessTokens.find({
where: {
userId: user.id
},
take: ps.limit,
skip: ps.offset,
order: {
id: ps.sort == 'asc' ? 1 : -1
}
});
return await Promise.all(tokens.map(token => pack(token.appId, user, {
return await Promise.all(tokens.map(token => Apps.pack(token.appId, user, {
detail: true
})));
});

View File

@ -1,7 +1,7 @@
import $ from 'cafy';
import * as bcrypt from 'bcryptjs';
import User from '../../../../models/user';
import define from '../../define';
import { Users } from '../../../../models';
export const meta = {
requireCredential: true,
@ -31,11 +31,7 @@ export default define(meta, async (ps, user) => {
const salt = await bcrypt.genSalt(8);
const hash = await bcrypt.hash(ps.newPassword, salt);
await User.update(user._id, {
$set: {
'password': hash
}
await Users.update(user.id, {
password: hash
});
return;
});

View File

@ -1,23 +0,0 @@
import User from '../../../../models/user';
import define from '../../define';
export const meta = {
tags: ['account', 'following'],
requireCredential: true,
kind: 'account-write',
params: {
}
};
export default define(meta, async (ps, user) => {
await User.update({ _id: user._id }, {
$set: {
pendingReceivedFollowRequestsCount: 0
}
});
return;
});

View File

@ -1,10 +1,7 @@
import $ from 'cafy';
import * as bcrypt from 'bcryptjs';
import User from '../../../../models/user';
import define from '../../define';
import { createDeleteNotesJob, createDeleteDriveFilesJob } from '../../../../queue';
import Message from '../../../../models/messaging-message';
import Signin from '../../../../models/signin';
import { Users } from '../../../../models';
export const meta = {
requireCredential: true,
@ -26,27 +23,5 @@ export default define(meta, async (ps, user) => {
throw new Error('incorrect password');
}
await User.update({ _id: user._id }, {
$set: {
isDeleted: true,
name: null,
description: null,
pinnedNoteIds: [],
password: null,
email: null,
twitter: null,
github: null,
discord: null,
profile: {},
fields: [],
clientSettings: {},
}
});
Message.remove({ userId: user._id });
Signin.remove({ userId: user._id });
createDeleteNotesJob(user);
createDeleteDriveFilesJob(user);
return;
await Users.delete(user.id);
});

View File

@ -13,6 +13,4 @@ export const meta = {
export default define(meta, async (ps, user) => {
createExportBlockingJob(user);
return;
});

View File

@ -13,6 +13,4 @@ export const meta = {
export default define(meta, async (ps, user) => {
createExportFollowingJob(user);
return;
});

View File

@ -13,6 +13,4 @@ export const meta = {
export default define(meta, async (ps, user) => {
createExportMuteJob(user);
return;
});

View File

@ -13,6 +13,4 @@ export const meta = {
export default define(meta, async (ps, user) => {
createExportNotesJob(user);
return;
});

View File

@ -13,6 +13,4 @@ export const meta = {
export default define(meta, async (ps, user) => {
createExportUserListsJob(user);
return;
});

View File

@ -1,7 +1,8 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import Favorite, { packMany } from '../../../../models/favorite';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { NoteFavorites } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
export const meta = {
desc: {
@ -23,42 +24,22 @@ export const meta = {
sinceId: {
validator: $.optional.type(ID),
transform: transform,
},
untilId: {
validator: $.optional.type(ID),
transform: transform,
}
},
}
};
export default define(meta, async (ps, user) => {
const query = {
userId: user._id
} as any;
const query = makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId)
.andWhere(`favorite.userId = :meId`, { meId: user.id })
.leftJoinAndSelect('favorite.note', 'note');
const sort = {
_id: -1
};
const favorites = await query
.take(ps.limit)
.getMany();
if (ps.sinceId) {
sort._id = 1;
query._id = {
$gt: ps.sinceId
};
} else if (ps.untilId) {
query._id = {
$lt: ps.untilId
};
}
// Get favorites
const favorites = await Favorite
.find(query, {
limit: ps.limit,
sort: sort
});
return await packMany(favorites, user);
return await NoteFavorites.packMany(favorites, user);
});

View File

@ -1,10 +1,10 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { createImportFollowingJob } from '../../../../queue';
import ms = require('ms');
import DriveFile from '../../../../models/drive-file';
import { ApiError } from '../../error';
import { DriveFiles } from '../../../../models';
export const meta = {
secure: true,
@ -17,7 +17,6 @@ export const meta = {
params: {
fileId: {
validator: $.type(ID),
transform: transform,
}
},
@ -49,16 +48,12 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const file = await DriveFile.findOne({
_id: ps.fileId
});
const file = await DriveFiles.findOne(ps.fileId);
if (file == null) throw new ApiError(meta.errors.noSuchFile);
//if (!file.contentType.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType);
if (file.length > 50000) throw new ApiError(meta.errors.tooBigFile);
if (file.length === 0) throw new ApiError(meta.errors.emptyFile);
//if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType);
if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile);
if (file.size === 0) throw new ApiError(meta.errors.emptyFile);
createImportFollowingJob(user, file._id);
return;
createImportFollowingJob(user, file.id);
});

View File

@ -1,10 +1,10 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { createImportUserListsJob } from '../../../../queue';
import ms = require('ms');
import DriveFile from '../../../../models/drive-file';
import { ApiError } from '../../error';
import { DriveFiles } from '../../../../models';
export const meta = {
secure: true,
@ -17,7 +17,6 @@ export const meta = {
params: {
fileId: {
validator: $.type(ID),
transform: transform,
}
},
@ -49,16 +48,12 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const file = await DriveFile.findOne({
_id: ps.fileId
});
const file = await DriveFiles.findOne(ps.fileId);
if (file == null) throw new ApiError(meta.errors.noSuchFile);
//if (!file.contentType.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType);
if (file.length > 30000) throw new ApiError(meta.errors.tooBigFile);
if (file.length === 0) throw new ApiError(meta.errors.emptyFile);
//if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType);
if (file.size > 30000) throw new ApiError(meta.errors.tooBigFile);
if (file.size === 0) throw new ApiError(meta.errors.emptyFile);
createImportUserListsJob(user, file._id);
return;
createImportUserListsJob(user, file.id);
});

View File

@ -1,11 +1,9 @@
import $ from 'cafy';
import ID, { transform } from '../../../../misc/cafy-id';
import Notification from '../../../../models/notification';
import { packMany } from '../../../../models/notification';
import { getFriendIds } from '../../common/get-friends';
import read from '../../common/read-notification';
import { ID } from '../../../../misc/cafy-id';
import { readNotification } from '../../common/read-notification';
import define from '../../define';
import { getHideUserIds } from '../../common/get-hide-users';
import { makePaginationQuery } from '../../common/make-pagination-query';
import { Notifications, Followings, Mutings } from '../../../../models';
export const meta = {
desc: {
@ -17,7 +15,7 @@ export const meta = {
requireCredential: true,
kind: 'account-read',
kind: 'read:notifications',
params: {
limit: {
@ -27,12 +25,10 @@ export const meta = {
sinceId: {
validator: $.optional.type(ID),
transform: transform,
},
untilId: {
validator: $.optional.type(ID),
transform: transform,
},
following: {
@ -46,12 +42,12 @@ export const meta = {
},
includeTypes: {
validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'poll_vote', 'receiveFollowRequest'])),
validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest'])),
default: [] as string[]
},
excludeTypes: {
validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'poll_vote', 'receiveFollowRequest'])),
validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest'])),
default: [] as string[]
}
},
@ -65,63 +61,38 @@ export const meta = {
};
export default define(meta, async (ps, user) => {
const hideUserIds = await getHideUserIds(user);
const followingQuery = Followings.createQueryBuilder('following')
.select('following.followeeId')
.where('following.followerId = :followerId', { followerId: user.id });
const query = {
notifieeId: user._id,
$and: [{
notifierId: {
$nin: hideUserIds
}
}]
} as any;
const mutingQuery = Mutings.createQueryBuilder('muting')
.select('muting.muteeId')
.where('muting.muterId = :muterId', { muterId: user.id });
const sort = {
_id: -1
};
const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId)
.andWhere(`notification.notifieeId = :meId`, { meId: user.id })
.leftJoinAndSelect('notification.notifier', 'notifier');
query.andWhere(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`);
query.setParameters(mutingQuery.getParameters());
if (ps.following) {
// ID list of the user itself and other users who the user follows
const followingIds = await getFriendIds(user._id);
query.$and.push({
notifierId: {
$in: followingIds
}
});
}
if (ps.sinceId) {
sort._id = 1;
query._id = {
$gt: ps.sinceId
};
} else if (ps.untilId) {
query._id = {
$lt: ps.untilId
};
query.andWhere(`((notification.notifierId IN (${ followingQuery.getQuery() })) OR (notification.notifierId = :meId))`, { meId: user.id });
query.setParameters(followingQuery.getParameters());
}
if (ps.includeTypes.length > 0) {
query.type = {
$in: ps.includeTypes
};
query.andWhere(`notification.type IN (:...includeTypes)`, { includeTypes: ps.includeTypes });
} else if (ps.excludeTypes.length > 0) {
query.type = {
$nin: ps.excludeTypes
};
query.andWhere(`notification.type NOT IN (:...excludeTypes)`, { excludeTypes: ps.excludeTypes });
}
const notifications = await Notification
.find(query, {
limit: ps.limit,
sort: sort
});
const notifications = await query.take(ps.limit).getMany();
// Mark all as read
if (notifications.length > 0 && ps.markAsRead) {
read(user._id, notifications);
readNotification(user.id, notifications.map(x => x.id));
}
return await packMany(notifications);
return await Notifications.packMany(notifications);
});

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