summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2025-02-05 10:39:46 +0900
committerGitHub <noreply@github.com>2025-02-05 01:39:46 +0000
commitfbc6d0de54031de840c39be3a2c7c63fe522c439 (patch)
tree038f40d401f1fa2ca8309a40dd852e6111b0bcf4 /packages
parentUpdate CHANGELOG.md (diff)
downloadmisskey-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')
-rw-r--r--packages/backend/src/models/Page.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/pages/create.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/pages/update.ts5
-rw-r--r--packages/frontend/src/pages/page-editor/page-editor.vue111
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;
});