From cdac3988b546f7cf457767f30ef9e24a591ae9d7 Mon Sep 17 00:00:00 2001 From: woxtu Date: Sun, 28 Jan 2024 15:08:45 +0900 Subject: [PATCH 1/3] fix(backend): Fix typos in job configurations (#13086) * Fix typos * Update CHANGELOG --- .config/example.yml | 6 +++--- CHANGELOG.md | 1 + packages/backend/src/config.ts | 12 ++++++------ packages/backend/src/queue/QueueProcessorService.ts | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.config/example.yml b/.config/example.yml index df423c2c83..3c9c3bc0d7 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -160,14 +160,14 @@ id: 'aidx' # Job concurrency per worker #deliverJobConcurrency: 128 #inboxJobConcurrency: 16 -#relashionshipJobConcurrency: 16 -# What's relashionshipJob?: +#relationshipJobConcurrency: 16 +# What's relationshipJob?: # Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations. # Job rate limiter #deliverJobPerSec: 128 #inboxJobPerSec: 32 -#relashionshipJobPerSec: 64 +#relationshipJobPerSec: 64 # Job attempts #deliverJobMaxAttempts: 12 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7edc9cf7cb..98d99b7af0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ - Fix: `notes/create`で、`text`が空白文字のみで構成されていてかつリノート、ファイルまたは投票を含んでいるリクエストに対するレスポンスの`text`が`""`から`null`になるように変更 - Fix: ipv4とipv6の両方が利用可能な環境でallowedPrivateNetworksが設定されていた場合プライベートipの検証ができていなかった問題を修正 - Fix: properly handle cc followers +- Fix: ジョブに関する設定の名前を修正 ### Service Worker - Enhance: オフライン表示のデザインを改善・多言語対応 diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index b25554b229..d433ce0eec 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -74,10 +74,10 @@ type Source = { deliverJobConcurrency?: number; inboxJobConcurrency?: number; - relashionshipJobConcurrency?: number; + relationshipJobConcurrency?: number; deliverJobPerSec?: number; inboxJobPerSec?: number; - relashionshipJobPerSec?: number; + relationshipJobPerSec?: number; deliverJobMaxAttempts?: number; inboxJobMaxAttempts?: number; @@ -135,10 +135,10 @@ export type Config = { outgoingAddressFamily: 'ipv4' | 'ipv6' | 'dual' | undefined; deliverJobConcurrency: number | undefined; inboxJobConcurrency: number | undefined; - relashionshipJobConcurrency: number | undefined; + relationshipJobConcurrency: number | undefined; deliverJobPerSec: number | undefined; inboxJobPerSec: number | undefined; - relashionshipJobPerSec: number | undefined; + relationshipJobPerSec: number | undefined; deliverJobMaxAttempts: number | undefined; inboxJobMaxAttempts: number | undefined; proxyRemoteFiles: boolean | undefined; @@ -241,10 +241,10 @@ export function loadConfig(): Config { outgoingAddressFamily: config.outgoingAddressFamily, deliverJobConcurrency: config.deliverJobConcurrency, inboxJobConcurrency: config.inboxJobConcurrency, - relashionshipJobConcurrency: config.relashionshipJobConcurrency, + relationshipJobConcurrency: config.relationshipJobConcurrency, deliverJobPerSec: config.deliverJobPerSec, inboxJobPerSec: config.inboxJobPerSec, - relashionshipJobPerSec: config.relashionshipJobPerSec, + relationshipJobPerSec: config.relationshipJobPerSec, deliverJobMaxAttempts: config.deliverJobMaxAttempts, inboxJobMaxAttempts: config.inboxJobMaxAttempts, proxyRemoteFiles: config.proxyRemoteFiles, diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index bcc1a69f80..cc64c9f5a3 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -283,9 +283,9 @@ export class QueueProcessorService implements OnApplicationShutdown { }, { ...baseQueueOptions(this.config, QUEUE.RELATIONSHIP), autorun: false, - concurrency: this.config.relashionshipJobConcurrency ?? 16, + concurrency: this.config.relationshipJobConcurrency ?? 16, limiter: { - max: this.config.relashionshipJobPerSec ?? 64, + max: this.config.relationshipJobPerSec ?? 64, duration: 1000, }, }); From fe7036a1a875d507d5db8446617df1681e13d915 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 28 Jan 2024 15:09:32 +0900 Subject: [PATCH 2/3] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98d99b7af0..345bd92c38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,7 +63,7 @@ - Fix: `notes/create`で、`text`が空白文字のみで構成されていてかつリノート、ファイルまたは投票を含んでいるリクエストに対するレスポンスの`text`が`""`から`null`になるように変更 - Fix: ipv4とipv6の両方が利用可能な環境でallowedPrivateNetworksが設定されていた場合プライベートipの検証ができていなかった問題を修正 - Fix: properly handle cc followers -- Fix: ジョブに関する設定の名前を修正 +- Fix: ジョブに関する設定の名前を修正 relashionshipJobPerSec -> relationshipJobPerSec ### Service Worker - Enhance: オフライン表示のデザインを改善・多言語対応 From b62d9f3920d94f68a6e0339c7d73659bbf5a0150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sun, 28 Jan 2024 19:22:38 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat(frontend/nirax):=20=E3=83=AA=E3=83=80?= =?UTF-8?q?=E3=82=A4=E3=83=AC=E3=82=AF=E3=83=88=E3=82=92=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1303?= =?UTF-8?q?0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(frontend/nirax): リダイレクトを設定できるように * revert demonstrative changes * fix * revert unrelated changes * リダイレクトの際にパスが変わらない問題を修正 * リダイレクトが必要なrouteを設定 * fix lint * router向けe2eテストの追加 * fix --------- Co-authored-by: syuilo Co-authored-by: samunohito <46447427+samunohito@users.noreply.github.com> --- cypress/e2e/router.cy.js | 30 ++++++ .../frontend/src/components/MkPageWindow.vue | 7 ++ .../frontend/src/global/router/definition.ts | 19 +++- packages/frontend/src/nirax.ts | 91 ++++++++++++++++--- 4 files changed, 131 insertions(+), 16 deletions(-) create mode 100644 cypress/e2e/router.cy.js diff --git a/cypress/e2e/router.cy.js b/cypress/e2e/router.cy.js new file mode 100644 index 0000000000..81f497b5b8 --- /dev/null +++ b/cypress/e2e/router.cy.js @@ -0,0 +1,30 @@ +describe('Router transition', () => { + describe('Redirect', () => { + // サーバの初期化。ルートのテストに関しては各describeごとに1度だけ実行で十分だと思う(使いまわした方が早い) + before(() => { + cy.resetState(); + + // インスタンス初期セットアップ + cy.registerUser('admin', 'pass', true); + + // ユーザー作成 + cy.registerUser('alice', 'alice1234'); + + cy.login('alice', 'alice1234'); + + // アカウント初期設定ウィザード + // 表示に時間がかかるのでデフォルト秒数だとタイムアウトする + cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 12000 }).click(); + cy.wait(500); + cy.get('[data-cy-modal-dialog-ok]').click(); + }); + + it('redirect to user profile', () => { + // テストのためだけに用意されたリダイレクト用ルートに飛ぶ + cy.visit('/redirect-test'); + + // プロフィールページのURLであることを確認する + cy.url().should('include', '/@alice') + }); + }); +}); diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index 28058c338b..ccd9df83ed 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -93,6 +93,13 @@ windowRouter.addListener('push', ctx => { history.value.push({ path: ctx.path, key: ctx.key }); }); +windowRouter.addListener('replace', ctx => { + history.value.pop(); + history.value.push({ path: ctx.path, key: ctx.key }); +}); + +windowRouter.init(); + provide('router', windowRouter); provideMetadataReceiver((info) => { pageMetadata.value = info; diff --git a/packages/frontend/src/global/router/definition.ts b/packages/frontend/src/global/router/definition.ts index 0333770a64..241b4fbcc7 100644 --- a/packages/frontend/src/global/router/definition.ts +++ b/packages/frontend/src/global/router/definition.ts @@ -4,6 +4,7 @@ */ import { App, AsyncComponentLoader, defineAsyncComponent, provide } from 'vue'; +import type { RouteDef } from '@/nirax.js'; import { IRouter, Router } from '@/nirax.js'; import { $i, iAmModerator } from '@/account.js'; import MkLoading from '@/pages/_loading_.vue'; @@ -16,7 +17,7 @@ const page = (loader: AsyncComponentLoader) => defineAsyncComponent({ errorComponent: MkError, }); -const routes = [{ +const routes: RouteDef[] = [{ path: '/@:initUser/pages/:initPageName/view-source', component: page(() => import('@/pages/page-editor/page-editor.vue')), }, { @@ -333,8 +334,7 @@ const routes = [{ component: page(() => import('@/pages/registry.vue')), }, { path: '/install-extentions', - // Note: This path is kept for compatibility. It may be deleted. - component: page(() => import('@/pages/install-extensions.vue')), + redirect: '/install-extensions', loginRequired: true, }, { path: '/install-extensions', @@ -557,6 +557,11 @@ const routes = [{ path: '/', component: $i ? page(() => import('@/pages/timeline.vue')) : page(() => import('@/pages/welcome.vue')), globalCacheKey: 'index', +}, { + // テスト用リダイレクト設定。ログイン中ユーザのプロフィールにリダイレクトする + path: '/redirect-test', + redirect: $i ? `@${$i.username}` : '/', + loginRequired: true, }, { path: '/:(*)', component: page(() => import('@/pages/not-found.vue')), @@ -575,8 +580,6 @@ export function setupRouter(app: App) { const mainRouter = createRouterImpl(location.pathname + location.search + location.hash); - window.history.replaceState({ key: mainRouter.getCurrentKey() }, '', location.href); - window.addEventListener('popstate', (event) => { mainRouter.replace(location.pathname + location.search + location.hash, event.state?.key); }); @@ -585,5 +588,11 @@ export function setupRouter(app: App) { window.history.pushState({ key: ctx.key }, '', ctx.path); }); + mainRouter.addListener('replace', ctx => { + window.history.replaceState({ key: ctx.key }, '', ctx.path); + }); + + mainRouter.init(); + setMainRouter(mainRouter); } diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts index a56aa6419e..ddb2a085db 100644 --- a/packages/frontend/src/nirax.ts +++ b/packages/frontend/src/nirax.ts @@ -9,16 +9,25 @@ import { Component, onMounted, shallowRef, ShallowRef } from 'vue'; import { EventEmitter } from 'eventemitter3'; import { safeURIDecode } from '@/scripts/safe-uri-decode.js'; -export type RouteDef = { +interface RouteDefBase { path: string; - component: Component; query?: Record; loginRequired?: boolean; name?: string; hash?: string; globalCacheKey?: string; children?: RouteDef[]; -}; +} + +interface RouteDefWithComponent extends RouteDefBase { + component: Component, +} + +interface RouteDefWithRedirect extends RouteDefBase { + redirect: string | ((props: Map) => string); +} + +export type RouteDef = RouteDefWithComponent | RouteDefWithRedirect; type ParsedPath = (string | { name: string; @@ -48,7 +57,19 @@ export type RouterEvent = { same: () => void; } -export type Resolved = { route: RouteDef; props: Map; child?: Resolved; }; +export type Resolved = { + route: RouteDef; + props: Map; + child?: Resolved; + redirected?: boolean; + + /** @internal */ + _parsedRoute: { + fullPath: string; + queryString: string | null; + hash: string | null; + }; +}; function parsePath(path: string): ParsedPath { const res = [] as ParsedPath; @@ -81,6 +102,11 @@ export interface IRouter extends EventEmitter { currentRoute: ShallowRef; navHook: ((path: string, flag?: any) => boolean) | null; + /** + * ルートの初期化(eventListenerの定義後に必ず呼び出すこと) + */ + init(): void; + resolve(path: string): Resolved | null; getCurrentPath(): any; @@ -156,12 +182,13 @@ export interface IRouter extends EventEmitter { export class Router extends EventEmitter implements IRouter { private routes: RouteDef[]; public current: Resolved; - public currentRef: ShallowRef = shallowRef(); - public currentRoute: ShallowRef = shallowRef(); + public currentRef: ShallowRef; + public currentRoute: ShallowRef; private currentPath: string; private isLoggedIn: boolean; private notFoundPageComponent: Component; private currentKey = Date.now().toString(); + private redirectCount = 0; public navHook: ((path: string, flag?: any) => boolean) | null = null; @@ -169,13 +196,24 @@ export class Router extends EventEmitter implements IRouter { super(); this.routes = routes; + this.current = this.resolve(currentPath)!; + this.currentRef = shallowRef(this.current); + this.currentRoute = shallowRef(this.current.route); this.currentPath = currentPath; this.isLoggedIn = isLoggedIn; this.notFoundPageComponent = notFoundPageComponent; - this.navigate(currentPath, null, false); + } + + public init() { + const res = this.navigate(this.currentPath, null, false); + this.emit('replace', { + path: res._parsedRoute.fullPath, + key: this.currentKey, + }); } public resolve(path: string): Resolved | null { + const fullPath = path; let queryString: string | null = null; let hash: string | null = null; if (path[0] === '/') path = path.substring(1); @@ -188,6 +226,12 @@ export class Router extends EventEmitter implements IRouter { path = path.substring(0, path.indexOf('?')); } + const _parsedRoute = { + fullPath, + queryString, + hash, + }; + if (_DEV_) console.log('Routing: ', path, queryString); function check(routes: RouteDef[], _parts: string[]): Resolved | null { @@ -238,6 +282,7 @@ export class Router extends EventEmitter implements IRouter { route, props, child, + _parsedRoute, }; } else { continue forEachRouteLoop; @@ -263,6 +308,7 @@ export class Router extends EventEmitter implements IRouter { return { route, props, + _parsedRoute, }; } else { if (route.children) { @@ -272,6 +318,7 @@ export class Router extends EventEmitter implements IRouter { route, props, child, + _parsedRoute, }; } else { continue forEachRouteLoop; @@ -290,7 +337,7 @@ export class Router extends EventEmitter implements IRouter { return check(this.routes, _parts); } - private navigate(path: string, key: string | null | undefined, emitChange = true) { + private navigate(path: string, key: string | null | undefined, emitChange = true, _redirected = false): Resolved { const beforePath = this.currentPath; this.currentPath = path; @@ -300,6 +347,20 @@ export class Router extends EventEmitter implements IRouter { throw new Error('no route found for: ' + path); } + if ('redirect' in res.route) { + let redirectPath: string; + if (typeof res.route.redirect === 'function') { + redirectPath = res.route.redirect(res.props); + } else { + redirectPath = res.route.redirect + (res._parsedRoute.queryString ? '?' + res._parsedRoute.queryString : '') + (res._parsedRoute.hash ? '#' + res._parsedRoute.hash : ''); + } + if (_DEV_) console.log('Redirecting to: ', redirectPath); + if (_redirected && this.redirectCount++ > 10) { + throw new Error('redirect loop detected'); + } + return this.navigate(redirectPath, null, emitChange, true); + } + if (res.route.loginRequired && !this.isLoggedIn) { res.route.component = this.notFoundPageComponent; res.props.set('showLoginPopup', true); @@ -321,7 +382,11 @@ export class Router extends EventEmitter implements IRouter { }); } - return res; + this.redirectCount = 0; + return { + ...res, + redirected: _redirected, + }; } public getCurrentPath() { @@ -345,7 +410,7 @@ export class Router extends EventEmitter implements IRouter { const res = this.navigate(path, null); this.emit('push', { beforePath, - path, + path: res._parsedRoute.fullPath, route: res.route, props: res.props, key: this.currentKey, @@ -353,7 +418,11 @@ export class Router extends EventEmitter implements IRouter { } public replace(path: string, key?: string | null) { - this.navigate(path, key); + const res = this.navigate(path, key); + this.emit('replace', { + path: res._parsedRoute.fullPath, + key: this.currentKey, + }); } }