diff options
Diffstat (limited to 'src/server/api/endpoints/i')
46 files changed, 0 insertions, 2442 deletions
diff --git a/src/server/api/endpoints/i/2fa/done.ts b/src/server/api/endpoints/i/2fa/done.ts deleted file mode 100644 index 2bd2128cce..0000000000 --- a/src/server/api/endpoints/i/2fa/done.ts +++ /dev/null @@ -1,41 +0,0 @@ -import $ from 'cafy'; -import * as speakeasy from 'speakeasy'; -import define from '../../../define'; -import { UserProfiles } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - token: { - validator: $.str - } - } -}; - -export default define(meta, async (ps, user) => { - const token = ps.token.replace(/\s/g, ''); - - const profile = await UserProfiles.findOneOrFail(user.id); - - if (profile.twoFactorTempSecret == null) { - throw new Error('二段階認証の設定が開始されていません'); - } - - const verified = (speakeasy as any).totp.verify({ - secret: profile.twoFactorTempSecret, - encoding: 'base32', - token: token - }); - - if (!verified) { - throw new Error('not verified'); - } - - await UserProfiles.update(user.id, { - twoFactorSecret: profile.twoFactorTempSecret, - twoFactorEnabled: true - }); -}); diff --git a/src/server/api/endpoints/i/2fa/key-done.ts b/src/server/api/endpoints/i/2fa/key-done.ts deleted file mode 100644 index b4d3af235a..0000000000 --- a/src/server/api/endpoints/i/2fa/key-done.ts +++ /dev/null @@ -1,150 +0,0 @@ -import $ from 'cafy'; -import * as bcrypt from 'bcryptjs'; -import { promisify } from 'util'; -import * as cbor from 'cbor'; -import define from '../../../define'; -import { - UserProfiles, - UserSecurityKeys, - AttestationChallenges, - Users -} from '@/models/index'; -import config from '@/config/index'; -import { procedures, hash } from '../../../2fa'; -import { publishMainStream } from '@/services/stream'; - -const cborDecodeFirst = promisify(cbor.decodeFirst) as any; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - clientDataJSON: { - validator: $.str - }, - attestationObject: { - validator: $.str - }, - password: { - validator: $.str - }, - challengeId: { - validator: $.str - }, - name: { - validator: $.str - } - } -}; - -const rpIdHashReal = hash(Buffer.from(config.hostname, 'utf-8')); - -export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOneOrFail(user.id); - - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { - throw new Error('incorrect password'); - } - - if (!profile.twoFactorEnabled) { - throw new Error('2fa not enabled'); - } - - const clientData = JSON.parse(ps.clientDataJSON); - - if (clientData.type != 'webauthn.create') { - throw new Error('not a creation attestation'); - } - if (clientData.origin != config.scheme + '://' + config.host) { - throw new Error('origin mismatch'); - } - - const clientDataJSONHash = hash(Buffer.from(ps.clientDataJSON, 'utf-8')); - - const attestation = await cborDecodeFirst(ps.attestationObject); - - const rpIdHash = attestation.authData.slice(0, 32); - if (!rpIdHashReal.equals(rpIdHash)) { - throw new Error('rpIdHash mismatch'); - } - - const flags = attestation.authData[32]; - - // tslint:disable-next-line:no-bitwise - if (!(flags & 1)) { - throw new Error('user not present'); - } - - const authData = Buffer.from(attestation.authData); - const credentialIdLength = authData.readUInt16BE(53); - const credentialId = authData.slice(55, 55 + credentialIdLength); - const publicKeyData = authData.slice(55 + credentialIdLength); - const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData); - if (publicKey.get(3) != -7) { - throw new Error('alg mismatch'); - } - - if (!(procedures as any)[attestation.fmt]) { - throw new Error('unsupported fmt'); - } - - const verificationData = (procedures as any)[attestation.fmt].verify({ - attStmt: attestation.attStmt, - authenticatorData: authData, - clientDataHash: clientDataJSONHash, - credentialId, - publicKey, - rpIdHash - }); - if (!verificationData.valid) throw new Error('signature invalid'); - - const attestationChallenge = await AttestationChallenges.findOne({ - userId: user.id, - id: ps.challengeId, - registrationChallenge: true, - challenge: hash(clientData.challenge).toString('hex') - }); - - if (!attestationChallenge) { - throw new Error('non-existent challenge'); - } - - await AttestationChallenges.delete({ - userId: user.id, - id: ps.challengeId - }); - - // Expired challenge (> 5min old) - if ( - new Date().getTime() - attestationChallenge.createdAt.getTime() >= - 5 * 60 * 1000 - ) { - throw new Error('expired challenge'); - } - - const credentialIdString = credentialId.toString('hex'); - - await UserSecurityKeys.save({ - userId: user.id, - id: credentialIdString, - lastUsed: new Date(), - name: ps.name, - publicKey: verificationData.publicKey.toString('hex') - }); - - // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, { - detail: true, - includeSecrets: true - })); - - return { - id: credentialIdString, - name: ps.name - }; -}); diff --git a/src/server/api/endpoints/i/2fa/password-less.ts b/src/server/api/endpoints/i/2fa/password-less.ts deleted file mode 100644 index 064828b638..0000000000 --- a/src/server/api/endpoints/i/2fa/password-less.ts +++ /dev/null @@ -1,21 +0,0 @@ -import $ from 'cafy'; -import define from '../../../define'; -import { UserProfiles } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - value: { - validator: $.boolean - } - } -}; - -export default define(meta, async (ps, user) => { - await UserProfiles.update(user.id, { - usePasswordLessLogin: ps.value - }); -}); diff --git a/src/server/api/endpoints/i/2fa/register-key.ts b/src/server/api/endpoints/i/2fa/register-key.ts deleted file mode 100644 index 1b385a10ee..0000000000 --- a/src/server/api/endpoints/i/2fa/register-key.ts +++ /dev/null @@ -1,59 +0,0 @@ -import $ from 'cafy'; -import * as bcrypt from 'bcryptjs'; -import define from '../../../define'; -import { UserProfiles, AttestationChallenges } from '@/models/index'; -import { promisify } from 'util'; -import * as crypto from 'crypto'; -import { genId } from '@/misc/gen-id'; -import { hash } from '../../../2fa'; - -const randomBytes = promisify(crypto.randomBytes); - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - password: { - validator: $.str - } - } -}; - -export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOneOrFail(user.id); - - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { - throw new Error('incorrect password'); - } - - if (!profile.twoFactorEnabled) { - throw new Error('2fa not enabled'); - } - - // 32 byte challenge - const entropy = await randomBytes(32); - const challenge = entropy.toString('base64') - .replace(/=/g, '') - .replace(/\+/g, '-') - .replace(/\//g, '_'); - - const challengeId = genId(); - - await AttestationChallenges.save({ - userId: user.id, - id: challengeId, - challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), - createdAt: new Date(), - registrationChallenge: true - }); - - return { - challengeId, - challenge - }; -}); diff --git a/src/server/api/endpoints/i/2fa/register.ts b/src/server/api/endpoints/i/2fa/register.ts deleted file mode 100644 index b03b98188a..0000000000 --- a/src/server/api/endpoints/i/2fa/register.ts +++ /dev/null @@ -1,54 +0,0 @@ -import $ from 'cafy'; -import * as bcrypt from 'bcryptjs'; -import * as speakeasy from 'speakeasy'; -import * as QRCode from 'qrcode'; -import config from '@/config/index'; -import define from '../../../define'; -import { UserProfiles } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - password: { - validator: $.str - } - } -}; - -export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOneOrFail(user.id); - - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { - throw new Error('incorrect password'); - } - - // Generate user's secret key - const secret = speakeasy.generateSecret({ - length: 32 - }); - - await UserProfiles.update(user.id, { - twoFactorTempSecret: secret.base32 - }); - - // Get the data URL of the authenticator URL - const dataUrl = await QRCode.toDataURL(speakeasy.otpauthURL({ - secret: secret.base32, - encoding: 'base32', - label: user.username, - issuer: config.host - })); - - return { - qr: dataUrl, - secret: secret.base32, - label: user.username, - issuer: config.host - }; -}); diff --git a/src/server/api/endpoints/i/2fa/remove-key.ts b/src/server/api/endpoints/i/2fa/remove-key.ts deleted file mode 100644 index dea56301ab..0000000000 --- a/src/server/api/endpoints/i/2fa/remove-key.ts +++ /dev/null @@ -1,45 +0,0 @@ -import $ from 'cafy'; -import * as bcrypt from 'bcryptjs'; -import define from '../../../define'; -import { UserProfiles, UserSecurityKeys, Users } from '@/models/index'; -import { publishMainStream } from '@/services/stream'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - password: { - validator: $.str - }, - credentialId: { - validator: $.str - }, - } -}; - -export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOneOrFail(user.id); - - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { - throw new Error('incorrect password'); - } - - // Make sure we only delete the user's own creds - await UserSecurityKeys.delete({ - userId: user.id, - id: ps.credentialId - }); - - // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, { - detail: true, - includeSecrets: true - })); - - return {}; -}); diff --git a/src/server/api/endpoints/i/2fa/unregister.ts b/src/server/api/endpoints/i/2fa/unregister.ts deleted file mode 100644 index af53033daa..0000000000 --- a/src/server/api/endpoints/i/2fa/unregister.ts +++ /dev/null @@ -1,32 +0,0 @@ -import $ from 'cafy'; -import * as bcrypt from 'bcryptjs'; -import define from '../../../define'; -import { UserProfiles } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - password: { - validator: $.str - } - } -}; - -export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOneOrFail(user.id); - - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { - throw new Error('incorrect password'); - } - - await UserProfiles.update(user.id, { - twoFactorSecret: null, - twoFactorEnabled: false - }); -}); diff --git a/src/server/api/endpoints/i/apps.ts b/src/server/api/endpoints/i/apps.ts deleted file mode 100644 index 994528e5c9..0000000000 --- a/src/server/api/endpoints/i/apps.ts +++ /dev/null @@ -1,43 +0,0 @@ -import $ from 'cafy'; -import define from '../../define'; -import { AccessTokens } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - sort: { - validator: $.optional.str.or([ - '+createdAt', - '-createdAt', - '+lastUsedAt', - '-lastUsedAt', - ]), - }, - } -}; - -export default define(meta, async (ps, user) => { - const query = AccessTokens.createQueryBuilder('token') - .where('token.userId = :userId', { userId: user.id }); - - switch (ps.sort) { - case '+createdAt': query.orderBy('token.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('token.createdAt', 'ASC'); break; - case '+lastUsedAt': query.orderBy('token.lastUsedAt', 'DESC'); break; - case '-lastUsedAt': query.orderBy('token.lastUsedAt', 'ASC'); break; - default: query.orderBy('token.id', 'ASC'); break; - } - - const tokens = await query.getMany(); - - return await Promise.all(tokens.map(token => ({ - id: token.id, - name: token.name, - createdAt: token.createdAt, - lastUsedAt: token.lastUsedAt, - permission: token.permission, - }))); -}); diff --git a/src/server/api/endpoints/i/authorized-apps.ts b/src/server/api/endpoints/i/authorized-apps.ts deleted file mode 100644 index 042fcd14e8..0000000000 --- a/src/server/api/endpoints/i/authorized-apps.ts +++ /dev/null @@ -1,44 +0,0 @@ -import $ from 'cafy'; -import define from '../../define'; -import { AccessTokens, Apps } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - - sort: { - validator: $.optional.str.or('desc|asc'), - default: 'desc', - } - } -}; - -export default define(meta, async (ps, user) => { - // Get tokens - 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 => Apps.pack(token.appId, user, { - detail: true - }))); -}); diff --git a/src/server/api/endpoints/i/change-password.ts b/src/server/api/endpoints/i/change-password.ts deleted file mode 100644 index 7ea5f8c488..0000000000 --- a/src/server/api/endpoints/i/change-password.ts +++ /dev/null @@ -1,39 +0,0 @@ -import $ from 'cafy'; -import * as bcrypt from 'bcryptjs'; -import define from '../../define'; -import { UserProfiles } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - currentPassword: { - validator: $.str - }, - - newPassword: { - validator: $.str - } - } -}; - -export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOneOrFail(user.id); - - // Compare password - const same = await bcrypt.compare(ps.currentPassword, profile.password!); - - if (!same) { - throw new Error('incorrect password'); - } - - // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(ps.newPassword, salt); - - await UserProfiles.update(user.id, { - password: hash - }); -}); diff --git a/src/server/api/endpoints/i/delete-account.ts b/src/server/api/endpoints/i/delete-account.ts deleted file mode 100644 index 10e5adf64a..0000000000 --- a/src/server/api/endpoints/i/delete-account.ts +++ /dev/null @@ -1,48 +0,0 @@ -import $ from 'cafy'; -import * as bcrypt from 'bcryptjs'; -import define from '../../define'; -import { UserProfiles, Users } from '@/models/index'; -import { doPostSuspend } from '@/services/suspend-user'; -import { publishUserEvent } from '@/services/stream'; -import { createDeleteAccountJob } from '@/queue'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - password: { - validator: $.str - }, - } -}; - -export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOneOrFail(user.id); - const userDetailed = await Users.findOneOrFail(user.id); - if (userDetailed.isDeleted) { - return; - } - - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { - throw new Error('incorrect password'); - } - - // 物理削除する前にDelete activityを送信する - await doPostSuspend(user).catch(e => {}); - - createDeleteAccountJob(user, { - soft: false - }); - - await Users.update(user.id, { - isDeleted: true, - }); - - // Terminate streaming - publishUserEvent(user.id, 'terminate', {}); -}); diff --git a/src/server/api/endpoints/i/export-blocking.ts b/src/server/api/endpoints/i/export-blocking.ts deleted file mode 100644 index e4797da0c1..0000000000 --- a/src/server/api/endpoints/i/export-blocking.ts +++ /dev/null @@ -1,16 +0,0 @@ -import define from '../../define'; -import { createExportBlockingJob } from '@/queue/index'; -import * as ms from 'ms'; - -export const meta = { - secure: true, - requireCredential: true as const, - limit: { - duration: ms('1hour'), - max: 1, - }, -}; - -export default define(meta, async (ps, user) => { - createExportBlockingJob(user); -}); diff --git a/src/server/api/endpoints/i/export-following.ts b/src/server/api/endpoints/i/export-following.ts deleted file mode 100644 index b0f154cda8..0000000000 --- a/src/server/api/endpoints/i/export-following.ts +++ /dev/null @@ -1,16 +0,0 @@ -import define from '../../define'; -import { createExportFollowingJob } from '@/queue/index'; -import * as ms from 'ms'; - -export const meta = { - secure: true, - requireCredential: true as const, - limit: { - duration: ms('1hour'), - max: 1, - }, -}; - -export default define(meta, async (ps, user) => { - createExportFollowingJob(user); -}); diff --git a/src/server/api/endpoints/i/export-mute.ts b/src/server/api/endpoints/i/export-mute.ts deleted file mode 100644 index 46d547fa53..0000000000 --- a/src/server/api/endpoints/i/export-mute.ts +++ /dev/null @@ -1,16 +0,0 @@ -import define from '../../define'; -import { createExportMuteJob } from '@/queue/index'; -import * as ms from 'ms'; - -export const meta = { - secure: true, - requireCredential: true as const, - limit: { - duration: ms('1hour'), - max: 1, - }, -}; - -export default define(meta, async (ps, user) => { - createExportMuteJob(user); -}); diff --git a/src/server/api/endpoints/i/export-notes.ts b/src/server/api/endpoints/i/export-notes.ts deleted file mode 100644 index 441bf16896..0000000000 --- a/src/server/api/endpoints/i/export-notes.ts +++ /dev/null @@ -1,16 +0,0 @@ -import define from '../../define'; -import { createExportNotesJob } from '@/queue/index'; -import * as ms from 'ms'; - -export const meta = { - secure: true, - requireCredential: true as const, - limit: { - duration: ms('1day'), - max: 1, - }, -}; - -export default define(meta, async (ps, user) => { - createExportNotesJob(user); -}); diff --git a/src/server/api/endpoints/i/export-user-lists.ts b/src/server/api/endpoints/i/export-user-lists.ts deleted file mode 100644 index 24043a862a..0000000000 --- a/src/server/api/endpoints/i/export-user-lists.ts +++ /dev/null @@ -1,16 +0,0 @@ -import define from '../../define'; -import { createExportUserListsJob } from '@/queue/index'; -import * as ms from 'ms'; - -export const meta = { - secure: true, - requireCredential: true as const, - limit: { - duration: ms('1min'), - max: 1, - }, -}; - -export default define(meta, async (ps, user) => { - createExportUserListsJob(user); -}); diff --git a/src/server/api/endpoints/i/favorites.ts b/src/server/api/endpoints/i/favorites.ts deleted file mode 100644 index b79d68ae73..0000000000 --- a/src/server/api/endpoints/i/favorites.ts +++ /dev/null @@ -1,50 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../define'; -import { NoteFavorites } from '@/models/index'; -import { makePaginationQuery } from '../../common/make-pagination-query'; - -export const meta = { - tags: ['account', 'notes', 'favorites'], - - requireCredential: true as const, - - kind: 'read:favorites', - - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10 - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - - res: { - type: 'array' as const, - optional: false as const, nullable: false as const, - items: { - type: 'object' as const, - optional: false as const, nullable: false as const, - ref: 'NoteFavorite', - } - }, -}; - -export default define(meta, async (ps, user) => { - const query = makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) - .andWhere(`favorite.userId = :meId`, { meId: user.id }) - .leftJoinAndSelect('favorite.note', 'note'); - - const favorites = await query - .take(ps.limit!) - .getMany(); - - return await NoteFavorites.packMany(favorites, user); -}); diff --git a/src/server/api/endpoints/i/gallery/likes.ts b/src/server/api/endpoints/i/gallery/likes.ts deleted file mode 100644 index 7a2935a5ec..0000000000 --- a/src/server/api/endpoints/i/gallery/likes.ts +++ /dev/null @@ -1,57 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../../define'; -import { GalleryLikes } from '@/models/index'; -import { makePaginationQuery } from '../../../common/make-pagination-query'; - -export const meta = { - tags: ['account', 'gallery'], - - requireCredential: true as const, - - kind: 'read:gallery-likes', - - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10 - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - - res: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - id: { - type: 'string' as const, - optional: false as const, nullable: false as const, - format: 'id' - }, - page: { - type: 'object' as const, - optional: false as const, nullable: false as const, - ref: 'GalleryPost' - } - } - } -}; - -export default define(meta, async (ps, user) => { - const query = makePaginationQuery(GalleryLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) - .andWhere(`like.userId = :meId`, { meId: user.id }) - .leftJoinAndSelect('like.post', 'post'); - - const likes = await query - .take(ps.limit!) - .getMany(); - - return await GalleryLikes.packMany(likes, user); -}); diff --git a/src/server/api/endpoints/i/gallery/posts.ts b/src/server/api/endpoints/i/gallery/posts.ts deleted file mode 100644 index 21bb8759fc..0000000000 --- a/src/server/api/endpoints/i/gallery/posts.ts +++ /dev/null @@ -1,49 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../../define'; -import { GalleryPosts } from '@/models/index'; -import { makePaginationQuery } from '../../../common/make-pagination-query'; - -export const meta = { - tags: ['account', 'gallery'], - - requireCredential: true as const, - - kind: 'read:gallery', - - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10 - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - - res: { - type: 'array' as const, - optional: false as const, nullable: false as const, - items: { - type: 'object' as const, - optional: false as const, nullable: false as const, - ref: 'GalleryPost' - } - } -}; - -export default define(meta, async (ps, user) => { - const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) - .andWhere(`post.userId = :meId`, { meId: user.id }); - - const posts = await query - .take(ps.limit!) - .getMany(); - - return await GalleryPosts.packMany(posts, user); -}); diff --git a/src/server/api/endpoints/i/get-word-muted-notes-count.ts b/src/server/api/endpoints/i/get-word-muted-notes-count.ts deleted file mode 100644 index 6b9be98582..0000000000 --- a/src/server/api/endpoints/i/get-word-muted-notes-count.ts +++ /dev/null @@ -1,33 +0,0 @@ -import define from '../../define'; -import { MutedNotes } from '@/models/index'; - -export const meta = { - tags: ['account'], - - requireCredential: true as const, - - kind: 'read:account', - - params: { - }, - - res: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - count: { - type: 'number' as const, - optional: false as const, nullable: false as const - } - } - } -}; - -export default define(meta, async (ps, user) => { - return { - count: await MutedNotes.count({ - userId: user.id, - reason: 'word' - }) - }; -}); diff --git a/src/server/api/endpoints/i/import-blocking.ts b/src/server/api/endpoints/i/import-blocking.ts deleted file mode 100644 index d44d0b6077..0000000000 --- a/src/server/api/endpoints/i/import-blocking.ts +++ /dev/null @@ -1,60 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../define'; -import { createImportBlockingJob } from '@/queue/index'; -import * as ms from 'ms'; -import { ApiError } from '../../error'; -import { DriveFiles } from '@/models/index'; - -export const meta = { - secure: true, - requireCredential: true as const, - - limit: { - duration: ms('1hour'), - max: 1, - }, - - params: { - fileId: { - validator: $.type(ID), - } - }, - - errors: { - noSuchFile: { - message: 'No such file.', - code: 'NO_SUCH_FILE', - id: 'ebb53e5f-6574-9c0c-0b92-7ca6def56d7e' - }, - - unexpectedFileType: { - message: 'We need csv file.', - code: 'UNEXPECTED_FILE_TYPE', - id: 'b6fab7d6-d945-d67c-dfdb-32da1cd12cfe' - }, - - tooBigFile: { - message: 'That file is too big.', - code: 'TOO_BIG_FILE', - id: 'b7fbf0b1-aeef-3b21-29ef-fadd4cb72ccf' - }, - - emptyFile: { - message: 'That file is empty.', - code: 'EMPTY_FILE', - id: '6f3a4dcc-f060-a707-4950-806fbdbe60d6' - }, - } -}; - -export default define(meta, async (ps, user) => { - const file = await DriveFiles.findOne(ps.fileId); - - if (file == null) throw new ApiError(meta.errors.noSuchFile); - //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); - - createImportBlockingJob(user, file.id); -}); diff --git a/src/server/api/endpoints/i/import-following.ts b/src/server/api/endpoints/i/import-following.ts deleted file mode 100644 index b3de397661..0000000000 --- a/src/server/api/endpoints/i/import-following.ts +++ /dev/null @@ -1,59 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../define'; -import { createImportFollowingJob } from '@/queue/index'; -import * as ms from 'ms'; -import { ApiError } from '../../error'; -import { DriveFiles } from '@/models/index'; - -export const meta = { - secure: true, - requireCredential: true as const, - limit: { - duration: ms('1hour'), - max: 1, - }, - - params: { - fileId: { - validator: $.type(ID), - } - }, - - errors: { - noSuchFile: { - message: 'No such file.', - code: 'NO_SUCH_FILE', - id: 'b98644cf-a5ac-4277-a502-0b8054a709a3' - }, - - unexpectedFileType: { - message: 'We need csv file.', - code: 'UNEXPECTED_FILE_TYPE', - id: '660f3599-bce0-4f95-9dde-311fd841c183' - }, - - tooBigFile: { - message: 'That file is too big.', - code: 'TOO_BIG_FILE', - id: 'dee9d4ed-ad07-43ed-8b34-b2856398bc60' - }, - - emptyFile: { - message: 'That file is empty.', - code: 'EMPTY_FILE', - id: '31a1b42c-06f7-42ae-8a38-a661c5c9f691' - }, - } -}; - -export default define(meta, async (ps, user) => { - const file = await DriveFiles.findOne(ps.fileId); - - if (file == null) throw new ApiError(meta.errors.noSuchFile); - //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); -}); diff --git a/src/server/api/endpoints/i/import-muting.ts b/src/server/api/endpoints/i/import-muting.ts deleted file mode 100644 index c17434c587..0000000000 --- a/src/server/api/endpoints/i/import-muting.ts +++ /dev/null @@ -1,60 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../define'; -import { createImportMutingJob } from '@/queue/index'; -import * as ms from 'ms'; -import { ApiError } from '../../error'; -import { DriveFiles } from '@/models/index'; - -export const meta = { - secure: true, - requireCredential: true as const, - - limit: { - duration: ms('1hour'), - max: 1, - }, - - params: { - fileId: { - validator: $.type(ID), - } - }, - - errors: { - noSuchFile: { - message: 'No such file.', - code: 'NO_SUCH_FILE', - id: 'e674141e-bd2a-ba85-e616-aefb187c9c2a' - }, - - unexpectedFileType: { - message: 'We need csv file.', - code: 'UNEXPECTED_FILE_TYPE', - id: '568c6e42-c86c-ba09-c004-517f83f9f1a8' - }, - - tooBigFile: { - message: 'That file is too big.', - code: 'TOO_BIG_FILE', - id: '9b4ada6d-d7f7-0472-0713-4f558bd1ec9c' - }, - - emptyFile: { - message: 'That file is empty.', - code: 'EMPTY_FILE', - id: 'd2f12af1-e7b4-feac-86a3-519548f2728e' - }, - } -}; - -export default define(meta, async (ps, user) => { - const file = await DriveFiles.findOne(ps.fileId); - - if (file == null) throw new ApiError(meta.errors.noSuchFile); - //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); - - createImportMutingJob(user, file.id); -}); diff --git a/src/server/api/endpoints/i/import-user-lists.ts b/src/server/api/endpoints/i/import-user-lists.ts deleted file mode 100644 index 9069a019a9..0000000000 --- a/src/server/api/endpoints/i/import-user-lists.ts +++ /dev/null @@ -1,59 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../define'; -import { createImportUserListsJob } from '@/queue/index'; -import * as ms from 'ms'; -import { ApiError } from '../../error'; -import { DriveFiles } from '@/models/index'; - -export const meta = { - secure: true, - requireCredential: true as const, - limit: { - duration: ms('1hour'), - max: 1, - }, - - params: { - fileId: { - validator: $.type(ID), - } - }, - - errors: { - noSuchFile: { - message: 'No such file.', - code: 'NO_SUCH_FILE', - id: 'ea9cc34f-c415-4bc6-a6fe-28ac40357049' - }, - - unexpectedFileType: { - message: 'We need csv file.', - code: 'UNEXPECTED_FILE_TYPE', - id: 'a3c9edda-dd9b-4596-be6a-150ef813745c' - }, - - tooBigFile: { - message: 'That file is too big.', - code: 'TOO_BIG_FILE', - id: 'ae6e7a22-971b-4b52-b2be-fc0b9b121fe9' - }, - - emptyFile: { - message: 'That file is empty.', - code: 'EMPTY_FILE', - id: '99efe367-ce6e-4d44-93f8-5fae7b040356' - }, - } -}; - -export default define(meta, async (ps, user) => { - const file = await DriveFiles.findOne(ps.fileId); - - if (file == null) throw new ApiError(meta.errors.noSuchFile); - //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); -}); diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts deleted file mode 100644 index 56668d03b7..0000000000 --- a/src/server/api/endpoints/i/notifications.ts +++ /dev/null @@ -1,138 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import { readNotification } from '../../common/read-notification'; -import define from '../../define'; -import { makePaginationQuery } from '../../common/make-pagination-query'; -import { Notifications, Followings, Mutings, Users } from '@/models/index'; -import { notificationTypes } from '@/types'; -import read from '@/services/note/read'; -import { Brackets } from 'typeorm'; - -export const meta = { - tags: ['account', 'notifications'], - - requireCredential: true as const, - - kind: 'read:notifications', - - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10 - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - following: { - validator: $.optional.bool, - default: false - }, - - unreadOnly: { - validator: $.optional.bool, - default: false - }, - - markAsRead: { - validator: $.optional.bool, - default: true - }, - - includeTypes: { - validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])), - }, - - excludeTypes: { - validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])), - } - }, - - res: { - type: 'array' as const, - optional: false as const, nullable: false as const, - items: { - type: 'object' as const, - optional: false as const, nullable: false as const, - ref: 'Notification', - } - }, -}; - -export default define(meta, async (ps, user) => { - // includeTypes が空の場合はクエリしない - if (ps.includeTypes && ps.includeTypes.length === 0) { - return []; - } - // excludeTypes に全指定されている場合はクエリしない - if (notificationTypes.every(type => ps.excludeTypes?.includes(type))) { - return []; - } - const followingQuery = Followings.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: user.id }); - - const mutingQuery = Mutings.createQueryBuilder('muting') - .select('muting.muteeId') - .where('muting.muterId = :muterId', { muterId: user.id }); - - const suspendedQuery = Users.createQueryBuilder('users') - .select('users.id') - .where('users.isSuspended = TRUE'); - - const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId) - .andWhere(`notification.notifieeId = :meId`, { meId: user.id }) - .leftJoinAndSelect('notification.notifier', 'notifier') - .leftJoinAndSelect('notification.note', 'note') - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); - - query.andWhere(new Brackets(qb => { qb - .where(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`) - .orWhere('notification.notifierId IS NULL'); - })); - query.setParameters(mutingQuery.getParameters()); - - query.andWhere(new Brackets(qb => { qb - .where(`notification.notifierId NOT IN (${ suspendedQuery.getQuery() })`) - .orWhere('notification.notifierId IS NULL'); - })); - - if (ps.following) { - query.andWhere(`((notification.notifierId IN (${ followingQuery.getQuery() })) OR (notification.notifierId = :meId))`, { meId: user.id }); - query.setParameters(followingQuery.getParameters()); - } - - if (ps.includeTypes && ps.includeTypes.length > 0) { - query.andWhere(`notification.type IN (:...includeTypes)`, { includeTypes: ps.includeTypes }); - } else if (ps.excludeTypes && ps.excludeTypes.length > 0) { - query.andWhere(`notification.type NOT IN (:...excludeTypes)`, { excludeTypes: ps.excludeTypes }); - } - - if (ps.unreadOnly) { - query.andWhere(`notification.isRead = false`); - } - - const notifications = await query.take(ps.limit!).getMany(); - - // Mark all as read - if (notifications.length > 0 && ps.markAsRead) { - readNotification(user.id, notifications.map(x => x.id)); - } - - const notes = notifications.filter(notification => ['mention', 'reply', 'quote'].includes(notification.type)).map(notification => notification.note!); - - if (notes.length > 0) { - read(user.id, notes); - } - - return await Notifications.packMany(notifications, user.id); -}); diff --git a/src/server/api/endpoints/i/page-likes.ts b/src/server/api/endpoints/i/page-likes.ts deleted file mode 100644 index fa2bc31730..0000000000 --- a/src/server/api/endpoints/i/page-likes.ts +++ /dev/null @@ -1,57 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../define'; -import { PageLikes } from '@/models/index'; -import { makePaginationQuery } from '../../common/make-pagination-query'; - -export const meta = { - tags: ['account', 'pages'], - - requireCredential: true as const, - - kind: 'read:page-likes', - - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10 - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - - res: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - id: { - type: 'string' as const, - optional: false as const, nullable: false as const, - format: 'id' - }, - page: { - type: 'object' as const, - optional: false as const, nullable: false as const, - ref: 'Page' - } - } - } -}; - -export default define(meta, async (ps, user) => { - const query = makePaginationQuery(PageLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) - .andWhere(`like.userId = :meId`, { meId: user.id }) - .leftJoinAndSelect('like.page', 'page'); - - const likes = await query - .take(ps.limit!) - .getMany(); - - return await PageLikes.packMany(likes, user); -}); diff --git a/src/server/api/endpoints/i/pages.ts b/src/server/api/endpoints/i/pages.ts deleted file mode 100644 index ee87fffa2d..0000000000 --- a/src/server/api/endpoints/i/pages.ts +++ /dev/null @@ -1,49 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../define'; -import { Pages } from '@/models/index'; -import { makePaginationQuery } from '../../common/make-pagination-query'; - -export const meta = { - tags: ['account', 'pages'], - - requireCredential: true as const, - - kind: 'read:pages', - - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10 - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - - res: { - type: 'array' as const, - optional: false as const, nullable: false as const, - items: { - type: 'object' as const, - optional: false as const, nullable: false as const, - ref: 'Page' - } - } -}; - -export default define(meta, async (ps, user) => { - const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) - .andWhere(`page.userId = :meId`, { meId: user.id }); - - const pages = await query - .take(ps.limit!) - .getMany(); - - return await Pages.packMany(pages); -}); diff --git a/src/server/api/endpoints/i/pin.ts b/src/server/api/endpoints/i/pin.ts deleted file mode 100644 index de94220ba9..0000000000 --- a/src/server/api/endpoints/i/pin.ts +++ /dev/null @@ -1,59 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import { addPinned } from '@/services/i/pin'; -import define from '../../define'; -import { ApiError } from '../../error'; -import { Users } from '@/models/index'; - -export const meta = { - tags: ['account', 'notes'], - - requireCredential: true as const, - - kind: 'write:account', - - params: { - noteId: { - validator: $.type(ID), - } - }, - - errors: { - noSuchNote: { - message: 'No such note.', - code: 'NO_SUCH_NOTE', - id: '56734f8b-3928-431e-bf80-6ff87df40cb3' - }, - - pinLimitExceeded: { - message: 'You can not pin notes any more.', - code: 'PIN_LIMIT_EXCEEDED', - id: '72dab508-c64d-498f-8740-a8eec1ba385a' - }, - - alreadyPinned: { - message: 'That note has already been pinned.', - code: 'ALREADY_PINNED', - id: '8b18c2b7-68fe-4edb-9892-c0cbaeb6c913' - }, - }, - - res: { - type: 'object' as const, - optional: false as const, nullable: false as const, - ref: 'User' - } -}; - -export default define(meta, async (ps, user) => { - await addPinned(user, ps.noteId).catch(e => { - if (e.id === '70c4e51f-5bea-449c-a030-53bee3cce202') throw new ApiError(meta.errors.noSuchNote); - if (e.id === '15a018eb-58e5-4da1-93be-330fcc5e4e1a') throw new ApiError(meta.errors.pinLimitExceeded); - if (e.id === '23f0cf4e-59a3-4276-a91d-61a5891c1514') throw new ApiError(meta.errors.alreadyPinned); - throw e; - }); - - return await Users.pack(user.id, user, { - detail: true - }); -}); diff --git a/src/server/api/endpoints/i/read-all-messaging-messages.ts b/src/server/api/endpoints/i/read-all-messaging-messages.ts deleted file mode 100644 index 9aca7611c9..0000000000 --- a/src/server/api/endpoints/i/read-all-messaging-messages.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { publishMainStream } from '@/services/stream'; -import define from '../../define'; -import { MessagingMessages, UserGroupJoinings } from '@/models/index'; - -export const meta = { - tags: ['account', 'messaging'], - - requireCredential: true as const, - - kind: 'write:account', - - params: { - } -}; - -export default define(meta, async (ps, user) => { - // Update documents - await MessagingMessages.update({ - recipientId: user.id, - isRead: false - }, { - isRead: true - }); - - const joinings = await UserGroupJoinings.find({ userId: user.id }); - - await Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder().update() - .set({ - reads: (() => `array_append("reads", '${user.id}')`) as any - }) - .where(`groupId = :groupId`, { groupId: j.userGroupId }) - .andWhere('userId != :userId', { userId: user.id }) - .andWhere('NOT (:userId = ANY(reads))', { userId: user.id }) - .execute())); - - publishMainStream(user.id, 'readAllMessagingMessages'); -}); diff --git a/src/server/api/endpoints/i/read-all-unread-notes.ts b/src/server/api/endpoints/i/read-all-unread-notes.ts deleted file mode 100644 index 2a7102a590..0000000000 --- a/src/server/api/endpoints/i/read-all-unread-notes.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { publishMainStream } from '@/services/stream'; -import define from '../../define'; -import { NoteUnreads } from '@/models/index'; - -export const meta = { - tags: ['account'], - - requireCredential: true as const, - - kind: 'write:account', - - params: { - } -}; - -export default define(meta, async (ps, user) => { - // Remove documents - await NoteUnreads.delete({ - userId: user.id - }); - - // 全て既読になったイベントを発行 - publishMainStream(user.id, 'readAllUnreadMentions'); - publishMainStream(user.id, 'readAllUnreadSpecifiedNotes'); -}); diff --git a/src/server/api/endpoints/i/read-announcement.ts b/src/server/api/endpoints/i/read-announcement.ts deleted file mode 100644 index 2f5036f953..0000000000 --- a/src/server/api/endpoints/i/read-announcement.ts +++ /dev/null @@ -1,60 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../define'; -import { ApiError } from '../../error'; -import { genId } from '@/misc/gen-id'; -import { AnnouncementReads, Announcements, Users } from '@/models/index'; -import { publishMainStream } from '@/services/stream'; - -export const meta = { - tags: ['account'], - - requireCredential: true as const, - - kind: 'write:account', - - params: { - announcementId: { - validator: $.type(ID), - }, - }, - - errors: { - noSuchAnnouncement: { - message: 'No such announcement.', - code: 'NO_SUCH_ANNOUNCEMENT', - id: '184663db-df88-4bc2-8b52-fb85f0681939' - }, - } -}; - -export default define(meta, async (ps, user) => { - // Check if announcement exists - const announcement = await Announcements.findOne(ps.announcementId); - - if (announcement == null) { - throw new ApiError(meta.errors.noSuchAnnouncement); - } - - // Check if already read - const read = await AnnouncementReads.findOne({ - announcementId: ps.announcementId, - userId: user.id - }); - - if (read != null) { - return; - } - - // Create read - await AnnouncementReads.insert({ - id: genId(), - createdAt: new Date(), - announcementId: ps.announcementId, - userId: user.id, - }); - - if (!await Users.getHasUnreadAnnouncement(user.id)) { - publishMainStream(user.id, 'readAllAnnouncements'); - } -}); diff --git a/src/server/api/endpoints/i/regenerate-token.ts b/src/server/api/endpoints/i/regenerate-token.ts deleted file mode 100644 index 1cce2d37be..0000000000 --- a/src/server/api/endpoints/i/regenerate-token.ts +++ /dev/null @@ -1,44 +0,0 @@ -import $ from 'cafy'; -import * as bcrypt from 'bcryptjs'; -import { publishMainStream, publishUserEvent } from '@/services/stream'; -import generateUserToken from '../../common/generate-native-user-token'; -import define from '../../define'; -import { Users, UserProfiles } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - password: { - validator: $.str - } - } -}; - -export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOneOrFail(user.id); - - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { - throw new Error('incorrect password'); - } - - // Generate secret - const secret = generateUserToken(); - - await Users.update(user.id, { - token: secret - }); - - // Publish event - publishMainStream(user.id, 'myTokenRegenerated'); - - // Terminate streaming - setTimeout(() => { - publishUserEvent(user.id, 'terminate', {}); - }, 5000); -}); diff --git a/src/server/api/endpoints/i/registry/get-all.ts b/src/server/api/endpoints/i/registry/get-all.ts deleted file mode 100644 index c8eaf83a25..0000000000 --- a/src/server/api/endpoints/i/registry/get-all.ts +++ /dev/null @@ -1,33 +0,0 @@ -import $ from 'cafy'; -import define from '../../../define'; -import { RegistryItems } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, - } -}; - -export default define(meta, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.scope = :scope', { scope: ps.scope }); - - const items = await query.getMany(); - - const res = {} as Record<string, any>; - - for (const item of items) { - res[item.key] = item.value; - } - - return res; -}); diff --git a/src/server/api/endpoints/i/registry/get-detail.ts b/src/server/api/endpoints/i/registry/get-detail.ts deleted file mode 100644 index 992800c44c..0000000000 --- a/src/server/api/endpoints/i/registry/get-detail.ts +++ /dev/null @@ -1,48 +0,0 @@ -import $ from 'cafy'; -import define from '../../../define'; -import { RegistryItems } from '@/models/index'; -import { ApiError } from '../../../error'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - key: { - validator: $.str - }, - - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, - }, - - errors: { - noSuchKey: { - message: 'No such key.', - code: 'NO_SUCH_KEY', - id: '97a1e8e7-c0f7-47d2-957a-92e61256e01a' - }, - }, -}; - -export default define(meta, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.key = :key', { key: ps.key }) - .andWhere('item.scope = :scope', { scope: ps.scope }); - - const item = await query.getOne(); - - if (item == null) { - throw new ApiError(meta.errors.noSuchKey); - } - - return { - updatedAt: item.updatedAt, - value: item.value, - }; -}); diff --git a/src/server/api/endpoints/i/registry/get.ts b/src/server/api/endpoints/i/registry/get.ts deleted file mode 100644 index 569c3a9280..0000000000 --- a/src/server/api/endpoints/i/registry/get.ts +++ /dev/null @@ -1,45 +0,0 @@ -import $ from 'cafy'; -import define from '../../../define'; -import { RegistryItems } from '@/models/index'; -import { ApiError } from '../../../error'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - key: { - validator: $.str - }, - - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, - }, - - errors: { - noSuchKey: { - message: 'No such key.', - code: 'NO_SUCH_KEY', - id: 'ac3ed68a-62f0-422b-a7bc-d5e09e8f6a6a' - }, - }, -}; - -export default define(meta, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.key = :key', { key: ps.key }) - .andWhere('item.scope = :scope', { scope: ps.scope }); - - const item = await query.getOne(); - - if (item == null) { - throw new ApiError(meta.errors.noSuchKey); - } - - return item.value; -}); diff --git a/src/server/api/endpoints/i/registry/keys-with-type.ts b/src/server/api/endpoints/i/registry/keys-with-type.ts deleted file mode 100644 index 16a4fee374..0000000000 --- a/src/server/api/endpoints/i/registry/keys-with-type.ts +++ /dev/null @@ -1,41 +0,0 @@ -import $ from 'cafy'; -import define from '../../../define'; -import { RegistryItems } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, - } -}; - -export default define(meta, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.scope = :scope', { scope: ps.scope }); - - const items = await query.getMany(); - - const res = {} as Record<string, string>; - - for (const item of items) { - const type = typeof item.value; - res[item.key] = - item.value === null ? 'null' : - Array.isArray(item.value) ? 'array' : - type === 'number' ? 'number' : - type === 'string' ? 'string' : - type === 'boolean' ? 'boolean' : - type === 'object' ? 'object' : - null as never; - } - - return res; -}); diff --git a/src/server/api/endpoints/i/registry/keys.ts b/src/server/api/endpoints/i/registry/keys.ts deleted file mode 100644 index 3a8aeaa195..0000000000 --- a/src/server/api/endpoints/i/registry/keys.ts +++ /dev/null @@ -1,28 +0,0 @@ -import $ from 'cafy'; -import define from '../../../define'; -import { RegistryItems } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, - } -}; - -export default define(meta, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .select('item.key') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.scope = :scope', { scope: ps.scope }); - - const items = await query.getMany(); - - return items.map(x => x.key); -}); diff --git a/src/server/api/endpoints/i/registry/remove.ts b/src/server/api/endpoints/i/registry/remove.ts deleted file mode 100644 index 07bc23d4a6..0000000000 --- a/src/server/api/endpoints/i/registry/remove.ts +++ /dev/null @@ -1,45 +0,0 @@ -import $ from 'cafy'; -import define from '../../../define'; -import { RegistryItems } from '@/models/index'; -import { ApiError } from '../../../error'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - key: { - validator: $.str - }, - - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, - }, - - errors: { - noSuchKey: { - message: 'No such key.', - code: 'NO_SUCH_KEY', - id: '1fac4e8a-a6cd-4e39-a4a5-3a7e11f1b019' - }, - }, -}; - -export default define(meta, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.key = :key', { key: ps.key }) - .andWhere('item.scope = :scope', { scope: ps.scope }); - - const item = await query.getOne(); - - if (item == null) { - throw new ApiError(meta.errors.noSuchKey); - } - - await RegistryItems.remove(item); -}); diff --git a/src/server/api/endpoints/i/registry/scopes.ts b/src/server/api/endpoints/i/registry/scopes.ts deleted file mode 100644 index ecbdb05a8e..0000000000 --- a/src/server/api/endpoints/i/registry/scopes.ts +++ /dev/null @@ -1,29 +0,0 @@ -import define from '../../../define'; -import { RegistryItems } from '@/models/index'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - } -}; - -export default define(meta, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .select('item.scope') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }); - - const items = await query.getMany(); - - const res = [] as string[][]; - - for (const item of items) { - if (res.some(scope => scope.join('.') === item.scope.join('.'))) continue; - res.push(item.scope); - } - - return res; -}); diff --git a/src/server/api/endpoints/i/registry/set.ts b/src/server/api/endpoints/i/registry/set.ts deleted file mode 100644 index f129ee1b70..0000000000 --- a/src/server/api/endpoints/i/registry/set.ts +++ /dev/null @@ -1,61 +0,0 @@ -import $ from 'cafy'; -import { publishMainStream } from '@/services/stream'; -import define from '../../../define'; -import { RegistryItems } from '@/models/index'; -import { genId } from '@/misc/gen-id'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - key: { - validator: $.str.min(1) - }, - - value: { - validator: $.nullable.any - }, - - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, - } -}; - -export default define(meta, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.key = :key', { key: ps.key }) - .andWhere('item.scope = :scope', { scope: ps.scope }); - - const existingItem = await query.getOne(); - - if (existingItem) { - await RegistryItems.update(existingItem.id, { - updatedAt: new Date(), - value: ps.value - }); - } else { - await RegistryItems.insert({ - id: genId(), - createdAt: new Date(), - updatedAt: new Date(), - userId: user.id, - domain: null, - scope: ps.scope, - key: ps.key, - value: ps.value - }); - } - - // TODO: サードパーティアプリが傍受出来てしまうのでどうにかする - publishMainStream(user.id, 'registryUpdated', { - scope: ps.scope, - key: ps.key, - value: ps.value - }); -}); diff --git a/src/server/api/endpoints/i/revoke-token.ts b/src/server/api/endpoints/i/revoke-token.ts deleted file mode 100644 index bed868def4..0000000000 --- a/src/server/api/endpoints/i/revoke-token.ts +++ /dev/null @@ -1,31 +0,0 @@ -import $ from 'cafy'; -import define from '../../define'; -import { AccessTokens } from '@/models/index'; -import { ID } from '@/misc/cafy-id'; -import { publishUserEvent } from '@/services/stream'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - tokenId: { - validator: $.type(ID) - } - } -}; - -export default define(meta, async (ps, user) => { - const token = await AccessTokens.findOne(ps.tokenId); - - if (token) { - await AccessTokens.delete({ - id: ps.tokenId, - userId: user.id, - }); - - // Terminate streaming - publishUserEvent(user.id, 'terminate'); - } -}); diff --git a/src/server/api/endpoints/i/signin-history.ts b/src/server/api/endpoints/i/signin-history.ts deleted file mode 100644 index a2c10148c6..0000000000 --- a/src/server/api/endpoints/i/signin-history.ts +++ /dev/null @@ -1,35 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../define'; -import { Signins } from '@/models/index'; -import { makePaginationQuery } from '../../common/make-pagination-query'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10 - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - } - } -}; - -export default define(meta, async (ps, user) => { - const query = makePaginationQuery(Signins.createQueryBuilder('signin'), ps.sinceId, ps.untilId) - .andWhere(`signin.userId = :meId`, { meId: user.id }); - - const history = await query.take(ps.limit!).getMany(); - - return await Promise.all(history.map(record => Signins.pack(record))); -}); diff --git a/src/server/api/endpoints/i/unpin.ts b/src/server/api/endpoints/i/unpin.ts deleted file mode 100644 index dc79e255ab..0000000000 --- a/src/server/api/endpoints/i/unpin.ts +++ /dev/null @@ -1,45 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import { removePinned } from '@/services/i/pin'; -import define from '../../define'; -import { ApiError } from '../../error'; -import { Users } from '@/models/index'; - -export const meta = { - tags: ['account', 'notes'], - - requireCredential: true as const, - - kind: 'write:account', - - params: { - noteId: { - validator: $.type(ID), - } - }, - - errors: { - noSuchNote: { - message: 'No such note.', - code: 'NO_SUCH_NOTE', - id: '454170ce-9d63-4a43-9da1-ea10afe81e21' - }, - }, - - res: { - type: 'object' as const, - optional: false as const, nullable: false as const, - ref: 'User' - } -}; - -export default define(meta, async (ps, user) => { - await removePinned(user, ps.noteId).catch(e => { - if (e.id === 'b302d4cf-c050-400a-bbb3-be208681f40c') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - return await Users.pack(user.id, user, { - detail: true - }); -}); diff --git a/src/server/api/endpoints/i/update-email.ts b/src/server/api/endpoints/i/update-email.ts deleted file mode 100644 index 9b6fb9c410..0000000000 --- a/src/server/api/endpoints/i/update-email.ts +++ /dev/null @@ -1,94 +0,0 @@ -import $ from 'cafy'; -import { publishMainStream } from '@/services/stream'; -import define from '../../define'; -import rndstr from 'rndstr'; -import config from '@/config/index'; -import * as ms from 'ms'; -import * as bcrypt from 'bcryptjs'; -import { Users, UserProfiles } from '@/models/index'; -import { sendEmail } from '@/services/send-email'; -import { ApiError } from '../../error'; -import { validateEmailForAccount } from '@/services/validate-email-for-account'; - -export const meta = { - requireCredential: true as const, - - secure: true, - - limit: { - duration: ms('1hour'), - max: 3 - }, - - params: { - password: { - validator: $.str - }, - - email: { - validator: $.optional.nullable.str - }, - }, - - errors: { - incorrectPassword: { - message: 'Incorrect password.', - code: 'INCORRECT_PASSWORD', - id: 'e54c1d7e-e7d6-4103-86b6-0a95069b4ad3' - }, - - unavailable: { - message: 'Unavailable email address.', - code: 'UNAVAILABLE', - id: 'a2defefb-f220-8849-0af6-17f816099323' - }, - } -}; - -export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOneOrFail(user.id); - - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { - throw new ApiError(meta.errors.incorrectPassword); - } - - if (ps.email != null) { - const available = await validateEmailForAccount(ps.email); - if (!available) { - throw new ApiError(meta.errors.unavailable); - } - } - - await UserProfiles.update(user.id, { - email: ps.email, - emailVerified: false, - emailVerifyCode: null - }); - - const iObj = await Users.pack(user.id, user, { - detail: true, - includeSecrets: true - }); - - // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', iObj); - - if (ps.email != null) { - const code = rndstr('a-z0-9', 16); - - await UserProfiles.update(user.id, { - emailVerifyCode: code - }); - - const link = `${config.url}/verify-email/${code}`; - - sendEmail(ps.email, 'Email verification', - `To verify email, please click this link:<br><a href="${link}">${link}</a>`, - `To verify email, please click this link: ${link}`); - } - - return iObj; -}); diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts deleted file mode 100644 index d0f201ab60..0000000000 --- a/src/server/api/endpoints/i/update.ts +++ /dev/null @@ -1,294 +0,0 @@ -import $ from 'cafy'; -import * as mfm from 'mfm-js'; -import { ID } from '@/misc/cafy-id'; -import { publishMainStream, publishUserEvent } from '@/services/stream'; -import acceptAllFollowRequests from '@/services/following/requests/accept-all'; -import { publishToFollowers } from '@/services/i/update'; -import define from '../../define'; -import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm'; -import { extractHashtags } from '@/misc/extract-hashtags'; -import * as langmap from 'langmap'; -import { updateUsertags } from '@/services/update-hashtag'; -import { ApiError } from '../../error'; -import { Users, DriveFiles, UserProfiles, Pages } from '@/models/index'; -import { User } from '@/models/entities/user'; -import { UserProfile } from '@/models/entities/user-profile'; -import { notificationTypes } from '@/types'; -import { normalizeForSearch } from '@/misc/normalize-for-search'; - -export const meta = { - tags: ['account'], - - requireCredential: true as const, - - kind: 'write:account', - - params: { - name: { - validator: $.optional.nullable.use(Users.validateName), - }, - - description: { - validator: $.optional.nullable.use(Users.validateDescription), - }, - - lang: { - validator: $.optional.nullable.str.or(Object.keys(langmap)), - }, - - location: { - validator: $.optional.nullable.use(Users.validateLocation), - }, - - birthday: { - validator: $.optional.nullable.use(Users.validateBirthday), - }, - - avatarId: { - validator: $.optional.nullable.type(ID), - }, - - bannerId: { - validator: $.optional.nullable.type(ID), - }, - - fields: { - validator: $.optional.arr($.object()).range(1, 4), - }, - - isLocked: { - validator: $.optional.bool, - }, - - isExplorable: { - validator: $.optional.bool, - }, - - hideOnlineStatus: { - validator: $.optional.bool, - }, - - publicReactions: { - validator: $.optional.bool, - }, - - ffVisibility: { - validator: $.optional.str, - }, - - carefulBot: { - validator: $.optional.bool, - }, - - autoAcceptFollowed: { - validator: $.optional.bool, - }, - - noCrawle: { - validator: $.optional.bool, - }, - - isBot: { - validator: $.optional.bool, - }, - - isCat: { - validator: $.optional.bool, - }, - - injectFeaturedNote: { - validator: $.optional.bool, - }, - - receiveAnnouncementEmail: { - validator: $.optional.bool, - }, - - alwaysMarkNsfw: { - validator: $.optional.bool, - }, - - pinnedPageId: { - validator: $.optional.nullable.type(ID), - }, - - mutedWords: { - validator: $.optional.arr($.arr($.str)) - }, - - mutingNotificationTypes: { - validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])) - }, - - emailNotificationTypes: { - validator: $.optional.arr($.str) - }, - }, - - errors: { - noSuchAvatar: { - message: 'No such avatar file.', - code: 'NO_SUCH_AVATAR', - id: '539f3a45-f215-4f81-a9a8-31293640207f' - }, - - noSuchBanner: { - message: 'No such banner file.', - code: 'NO_SUCH_BANNER', - id: '0d8f5629-f210-41c2-9433-735831a58595' - }, - - avatarNotAnImage: { - message: 'The file specified as an avatar is not an image.', - code: 'AVATAR_NOT_AN_IMAGE', - id: 'f419f9f8-2f4d-46b1-9fb4-49d3a2fd7191' - }, - - bannerNotAnImage: { - message: 'The file specified as a banner is not an image.', - code: 'BANNER_NOT_AN_IMAGE', - id: '75aedb19-2afd-4e6d-87fc-67941256fa60' - }, - - noSuchPage: { - message: 'No such page.', - code: 'NO_SUCH_PAGE', - id: '8e01b590-7eb9-431b-a239-860e086c408e' - }, - }, - - res: { - type: 'object' as const, - optional: false as const, nullable: false as const, - ref: 'User' - } -}; - -export default define(meta, async (ps, _user, token) => { - const user = await Users.findOneOrFail(_user.id); - const isSecure = token == null; - - const updates = {} as Partial<User>; - const profileUpdates = {} as Partial<UserProfile>; - - const profile = await UserProfiles.findOneOrFail(user.id); - - if (ps.name !== undefined) updates.name = ps.name; - if (ps.description !== undefined) profileUpdates.description = ps.description; - if (ps.lang !== undefined) profileUpdates.lang = ps.lang; - if (ps.location !== undefined) profileUpdates.location = ps.location; - if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday; - if (ps.ffVisibility !== undefined) profileUpdates.ffVisibility = ps.ffVisibility; - if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; - if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; - if (ps.mutedWords !== undefined) { - profileUpdates.mutedWords = ps.mutedWords; - profileUpdates.enableWordMute = ps.mutedWords.length > 0; - } - if (ps.mutingNotificationTypes !== undefined) profileUpdates.mutingNotificationTypes = ps.mutingNotificationTypes as typeof notificationTypes[number][]; - if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked; - if (typeof ps.isExplorable === 'boolean') updates.isExplorable = ps.isExplorable; - if (typeof ps.hideOnlineStatus === 'boolean') updates.hideOnlineStatus = ps.hideOnlineStatus; - if (typeof ps.publicReactions === 'boolean') profileUpdates.publicReactions = ps.publicReactions; - if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; - if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot; - if (typeof ps.autoAcceptFollowed === 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; - if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle; - if (typeof ps.isCat === 'boolean') updates.isCat = ps.isCat; - if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; - if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail; - if (typeof ps.alwaysMarkNsfw === 'boolean') profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw; - if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes; - - if (ps.avatarId) { - const avatar = await DriveFiles.findOne(ps.avatarId); - - if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar); - if (!avatar.type.startsWith('image/')) throw new ApiError(meta.errors.avatarNotAnImage); - - updates.avatarUrl = DriveFiles.getPublicUrl(avatar, true); - - if (avatar.blurhash) { - updates.avatarBlurhash = avatar.blurhash; - } - } - - if (ps.bannerId) { - const banner = await DriveFiles.findOne(ps.bannerId); - - if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner); - if (!banner.type.startsWith('image/')) throw new ApiError(meta.errors.bannerNotAnImage); - - updates.bannerUrl = DriveFiles.getPublicUrl(banner, false); - - if (banner.blurhash) { - updates.bannerBlurhash = banner.blurhash; - } - } - - if (ps.pinnedPageId) { - const page = await Pages.findOne(ps.pinnedPageId); - - if (page == null || page.userId !== user.id) throw new ApiError(meta.errors.noSuchPage); - - profileUpdates.pinnedPageId = page.id; - } else if (ps.pinnedPageId === null) { - profileUpdates.pinnedPageId = null; - } - - if (ps.fields) { - profileUpdates.fields = ps.fields - .filter(x => typeof x.name === 'string' && x.name !== '' && typeof x.value === 'string' && x.value !== '') - .map(x => { - return { name: x.name, value: x.value }; - }); - } - - //#region emojis/tags - - let emojis = [] as string[]; - let tags = [] as string[]; - - const newName = updates.name === undefined ? user.name : updates.name; - const newDescription = profileUpdates.description === undefined ? profile.description : profileUpdates.description; - - if (newName != null) { - const tokens = mfm.parsePlain(newName); - emojis = emojis.concat(extractCustomEmojisFromMfm(tokens!)); - } - - if (newDescription != null) { - const tokens = mfm.parse(newDescription); - emojis = emojis.concat(extractCustomEmojisFromMfm(tokens!)); - tags = extractHashtags(tokens!).map(tag => normalizeForSearch(tag)).splice(0, 32); - } - - updates.emojis = emojis; - updates.tags = tags; - - // ハッシュタグ更新 - updateUsertags(user, tags); - //#endregion - - if (Object.keys(updates).length > 0) await Users.update(user.id, updates); - if (Object.keys(profileUpdates).length > 0) await UserProfiles.update(user.id, profileUpdates); - - const iObj = await Users.pack(user.id, user, { - detail: true, - includeSecrets: isSecure - }); - - // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', iObj); - publishUserEvent(user.id, 'updateUserProfile', await UserProfiles.findOne(user.id)); - - // 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認 - if (user.isLocked && ps.isLocked === false) { - acceptAllFollowRequests(user); - } - - // フォロワーにUpdateを配信 - publishToFollowers(user.id); - - return iObj; -}); diff --git a/src/server/api/endpoints/i/user-group-invites.ts b/src/server/api/endpoints/i/user-group-invites.ts deleted file mode 100644 index 1ebde243ca..0000000000 --- a/src/server/api/endpoints/i/user-group-invites.ts +++ /dev/null @@ -1,61 +0,0 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; -import define from '../../define'; -import { UserGroupInvitations } from '@/models/index'; -import { makePaginationQuery } from '../../common/make-pagination-query'; - -export const meta = { - tags: ['account', 'groups'], - - requireCredential: true as const, - - kind: 'read:user-groups', - - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10 - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - - res: { - type: 'array' as const, - optional: false as const, nullable: false as const, - items: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - id: { - type: 'string' as const, - optional: false as const, nullable: false as const, - format: 'id' - }, - group: { - type: 'object' as const, - optional: false as const, nullable: false as const, - ref: 'UserGroup' - } - } - } - } -}; - -export default define(meta, async (ps, user) => { - const query = makePaginationQuery(UserGroupInvitations.createQueryBuilder('invitation'), ps.sinceId, ps.untilId) - .andWhere(`invitation.userId = :meId`, { meId: user.id }) - .leftJoinAndSelect('invitation.userGroup', 'user_group'); - - const invitations = await query - .take(ps.limit!) - .getMany(); - - return await UserGroupInvitations.packMany(invitations); -}); |