143 lines
4.0 KiB
TypeScript
143 lines
4.0 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
import ms from 'ms';
|
|
import { Inject, Injectable } from '@nestjs/common';
|
|
import { In } from 'typeorm';
|
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
|
import type { DriveFilesRepository, UsersRepository } from '@/models/_.js';
|
|
import { NoteEditService } from '@/core/NoteEditService.js';
|
|
import { GetterService } from '@/server/api/GetterService.js';
|
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
|
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
|
|
import { RoleService } from '@/core/RoleService.js';
|
|
import { DI } from '@/di-symbols.js';
|
|
import { ApiError } from '../../error.js';
|
|
|
|
export const meta = {
|
|
tags: ['notes'],
|
|
|
|
requireCredential: true,
|
|
requireRolePolicy: 'canEditNote',
|
|
|
|
kind: 'write:notes',
|
|
|
|
limit: {
|
|
duration: ms('1hour'),
|
|
max: 10,
|
|
minInterval: ms('2min'),
|
|
},
|
|
|
|
errors: {
|
|
noSuchNote: {
|
|
message: 'No such note.',
|
|
code: 'NO_SUCH_NOTE',
|
|
id: 'a6584e14-6e01-4ad3-b566-851e7bf0d474',
|
|
},
|
|
accessDenied: {
|
|
message: 'Access denied.',
|
|
code: 'ACCESS_DENIED',
|
|
id: 'fe8d7103-0ea8-4ec3-814d-f8b401dc69e9',
|
|
},
|
|
containsProhibitedWords: {
|
|
message: 'Cannot post because it contains prohibited words.',
|
|
code: 'CONTAINS_PROHIBITED_WORDS',
|
|
id: 'aa6e01d3-a85c-669d-758a-76aab43af334',
|
|
},
|
|
},
|
|
} as const;
|
|
|
|
export const paramDef = {
|
|
type: 'object',
|
|
properties: {
|
|
noteId: { type: 'string', format: 'misskey:id' },
|
|
text: {
|
|
type: 'string',
|
|
minLength: 1,
|
|
maxLength: MAX_NOTE_TEXT_LENGTH,
|
|
nullable: false,
|
|
},
|
|
cw: {
|
|
type: 'string',
|
|
nullable: true,
|
|
maxLength: 100,
|
|
},
|
|
fileIds: {
|
|
type: 'array',
|
|
uniqueItems: true,
|
|
minItems: 1,
|
|
maxItems: 16,
|
|
items: { type: 'string', format: 'misskey:id' },
|
|
},
|
|
poll: {
|
|
type: 'object',
|
|
nullable: true,
|
|
properties: {
|
|
choices: {
|
|
type: 'array',
|
|
uniqueItems: true,
|
|
minItems: 2,
|
|
maxItems: 10,
|
|
items: { type: 'string', minLength: 1, maxLength: 50 },
|
|
},
|
|
multiple: { type: 'boolean' },
|
|
expiresAt: { type: 'integer', nullable: true },
|
|
expiredAfter: { type: 'integer', nullable: true, minimum: 1 },
|
|
},
|
|
required: ['choices'],
|
|
},
|
|
},
|
|
required: ['noteId', 'text', 'cw'],
|
|
} as const;
|
|
|
|
@Injectable()
|
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|
constructor(
|
|
@Inject(DI.driveFilesRepository)
|
|
private driveFilesRepository: DriveFilesRepository,
|
|
|
|
@Inject(DI.usersRepository)
|
|
private usersRepository: UsersRepository,
|
|
|
|
private getterService: GetterService,
|
|
private globalEventService: GlobalEventService,
|
|
private noteEditService: NoteEditService,
|
|
private roleService: RoleService,
|
|
) {
|
|
super(meta, paramDef, async (ps, me) => {
|
|
const note = await this.getterService.getNote(ps.noteId).catch(err => {
|
|
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
|
throw err;
|
|
});
|
|
|
|
// この操作を行うのが投稿者とは限らない(例えばモデレーター)ため
|
|
if (!await this.roleService.isModerator(me) && (note.userId !== me.id) && (await this.roleService.getUserPolicies(me.id)).canEditNote !== true) {
|
|
throw new ApiError(meta.errors.accessDenied);
|
|
}
|
|
|
|
try {
|
|
const targetNote = await this.noteEditService.edit(await this.usersRepository.findOneByOrFail({ id: note.userId }), note.id, {
|
|
text: ps.text,
|
|
cw: ps.cw,
|
|
files: ps.fileIds ? await this.driveFilesRepository.findBy({ id: In(ps.fileIds) }) : undefined,
|
|
poll: ps.poll ? {
|
|
choices: ps.poll.choices,
|
|
multiple: ps.poll.multiple ?? false,
|
|
expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null,
|
|
} : undefined,
|
|
}, undefined, me);
|
|
this.globalEventService.publishNoteStream(note.id, 'updated', {
|
|
note: targetNote,
|
|
});
|
|
} catch (e) {
|
|
if (e instanceof NoteEditService.ContainsProhibitedWordsError) {
|
|
throw new ApiError(meta.errors.containsProhibitedWords);
|
|
}
|
|
throw e;
|
|
}
|
|
});
|
|
}
|
|
}
|