diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-02-05 10:39:46 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-05 01:39:46 +0000 |
| commit | fbc6d0de54031de840c39be3a2c7c63fe522c439 (patch) | |
| tree | 038f40d401f1fa2ca8309a40dd852e6111b0bcf4 /packages | |
| parent | Update CHANGELOG.md (diff) | |
| download | misskey-fbc6d0de54031de840c39be3a2c7c63fe522c439.tar.gz misskey-fbc6d0de54031de840c39be3a2c7c63fe522c439.tar.bz2 misskey-fbc6d0de54031de840c39be3a2c7c63fe522c439.zip | |
enhance: ページslugに使用可能な文字を限定 (#15395)
* wip
* paramの正規表現で弾くように
* apiWithDialogを使用するように
* Update CHANGELOG.md
---------
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
Diffstat (limited to 'packages')
4 files changed, 56 insertions, 66 deletions
diff --git a/packages/backend/src/models/Page.ts b/packages/backend/src/models/Page.ts index 1695bf570e..0b59e7a92c 100644 --- a/packages/backend/src/models/Page.ts +++ b/packages/backend/src/models/Page.ts @@ -118,3 +118,5 @@ export class MiPage { } } } + +export const pageNameSchema = { type: 'string', pattern: /^[^\s:\/?#\[\]@!$&'()*+,;=\\%\x00-\x20]{1,256}$/.source } as const; diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index fa03b0b457..6de5fe3d44 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -7,7 +7,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import type { DriveFilesRepository, PagesRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; -import { MiPage } from '@/models/Page.js'; +import { MiPage, pageNameSchema } from '@/models/Page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -51,7 +51,7 @@ export const paramDef = { type: 'object', properties: { title: { type: 'string' }, - name: { type: 'string', minLength: 1 }, + name: { ...pageNameSchema, minLength: 1 }, summary: { type: 'string', nullable: true }, content: { type: 'array', items: { type: 'object', additionalProperties: true, diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index e52d9c32df..a6aeb6002e 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -10,6 +10,7 @@ import type { PagesRepository, DriveFilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; +import { pageNameSchema } from '@/models/Page.js'; export const meta = { tags: ['pages'], @@ -31,13 +32,11 @@ export const meta = { code: 'NO_SUCH_PAGE', id: '21149b9e-3616-4778-9592-c4ce89f5a864', }, - accessDenied: { message: 'Access denied.', code: 'ACCESS_DENIED', id: '3c15cd52-3b4b-4274-967d-6456fc4f792b', }, - noSuchFile: { message: 'No such file.', code: 'NO_SUCH_FILE', @@ -56,7 +55,7 @@ export const paramDef = { properties: { pageId: { type: 'string', format: 'misskey:id' }, title: { type: 'string' }, - name: { type: 'string', minLength: 1 }, + name: { ...pageNameSchema, minLength: 1 }, summary: { type: 'string', nullable: true }, content: { type: 'array', items: { type: 'object', additionalProperties: true, diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue index ddb808390c..c08cfebab3 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.vue @@ -96,7 +96,7 @@ const summary = ref<string | null>(null); const name = ref(Date.now().toString()); const eyeCatchingImage = ref<Misskey.entities.DriveFile | null>(null); const eyeCatchingImageId = ref<string | null>(null); -const font = ref('sans-serif'); +const font = ref<'sans-serif' | 'serif'>('sans-serif'); const content = ref<Misskey.entities.Page['content']>([]); const alignCenter = ref(false); const hideTitleWhenPinned = ref(false); @@ -113,7 +113,7 @@ watch(eyeCatchingImageId, async () => { } }); -function getSaveOptions() { +function getSaveOptions(): Misskey.entities.PagesCreateRequest { return { title: title.value.trim(), name: name.value.trim(), @@ -128,80 +128,69 @@ function getSaveOptions() { }; } -function save() { +async function save() { const options = getSaveOptions(); - const onError = err => { - if (err.id === '3d81ceae-475f-4600-b2a8-2bc116157532') { - if (err.info.param === 'name') { - os.alert({ - type: 'error', - title: i18n.ts._pages.invalidNameTitle, - text: i18n.ts._pages.invalidNameText, - }); - } - } else if (err.code === 'NAME_ALREADY_EXISTS') { - os.alert({ - type: 'error', + if (pageId.value) { + const updateOptions: Misskey.entities.PagesUpdateRequest = { + pageId: pageId.value, + ...options, + }; + + await os.apiWithDialog('pages/update', updateOptions, undefined, { + '2298a392-d4a1-44c5-9ebb-ac1aeaa5a9ab': { + title: i18n.ts.somethingHappened, text: i18n.ts._pages.nameAlreadyExists, - }); - } - }; + }, + }); - if (pageId.value) { - options.pageId = pageId.value; - misskeyApi('pages/update', options) - .then(page => { - currentName.value = name.value.trim(); - os.alert({ - type: 'success', - text: i18n.ts._pages.updated, - }); - }).catch(onError); + currentName.value = name.value.trim(); } else { - misskeyApi('pages/create', options) - .then(created => { - pageId.value = created.id; - currentName.value = name.value.trim(); - os.alert({ - type: 'success', - text: i18n.ts._pages.created, - }); - mainRouter.push(`/pages/edit/${pageId.value}`); - }).catch(onError); + const created = await os.apiWithDialog('pages/create', options, undefined, { + '4650348e-301c-499a-83c9-6aa988c66bc1': { + title: i18n.ts.somethingHappened, + text: i18n.ts._pages.nameAlreadyExists, + }, + }); + + pageId.value = created.id; + currentName.value = name.value.trim(); + mainRouter.replace(`/pages/edit/${pageId.value}`); } } -function del() { - os.confirm({ +async function del() { + if (!pageId.value) return; + + const { canceled } = await os.confirm({ type: 'warning', text: i18n.tsx.removeAreYouSure({ x: title.value.trim() }), - }).then(({ canceled }) => { - if (canceled) return; - misskeyApi('pages/delete', { - pageId: pageId.value, - }).then(() => { - os.alert({ - type: 'success', - text: i18n.ts._pages.deleted, - }); - mainRouter.push('/pages'); - }); }); + + if (canceled) return; + + await os.apiWithDialog('pages/delete', { + pageId: pageId.value, + }); + + mainRouter.replace('/pages'); } -function duplicate() { +async function duplicate() { title.value = title.value + ' - copy'; name.value = name.value + '-copy'; - misskeyApi('pages/create', getSaveOptions()).then(created => { - pageId.value = created.id; - currentName.value = name.value.trim(); - os.alert({ - type: 'success', - text: i18n.ts._pages.created, - }); - mainRouter.push(`/pages/edit/${pageId.value}`); + + const created = await os.apiWithDialog('pages/create', getSaveOptions(), undefined, { + '4650348e-301c-499a-83c9-6aa988c66bc1': { + title: i18n.ts.somethingHappened, + text: i18n.ts._pages.nameAlreadyExists, + }, }); + + pageId.value = created.id; + currentName.value = name.value.trim(); + + mainRouter.push(`/pages/edit/${pageId.value}`); } async function add() { @@ -216,7 +205,7 @@ async function add() { content.value.push({ id, type }); } -function setEyeCatchingImage(img) { +function setEyeCatchingImage(img: Event) { selectFile(img.currentTarget ?? img.target, null).then(file => { eyeCatchingImageId.value = file.id; }); |