From 8b6381a6a83816421709399a9391293f686342a1 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 18 Jul 2022 17:41:08 +0200 Subject: [PATCH 001/132] add OAuth 2.0 Bearer Token authentication --- .../backend/src/server/api/api-handler.ts | 3 ++- .../backend/src/server/api/authenticate.ts | 21 +++++++++++++++++-- packages/backend/src/server/api/streaming.ts | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/server/api/api-handler.ts b/packages/backend/src/server/api/api-handler.ts index ec71ddd2c..3fecea3fd 100644 --- a/packages/backend/src/server/api/api-handler.ts +++ b/packages/backend/src/server/api/api-handler.ts @@ -43,7 +43,8 @@ export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res }; // Authentication - authenticate(body['i']).then(([user, app]) => { + // for GET requests, do not even pass on the body parameter as it is considered unsafe + authenticate(ctx.headers.authorization, ctx.method === 'GET' ? null : body['i']).then(([user, app]) => { // API invoking call(endpoint.name, user, app, body, ctx).then((res: any) => { if (ctx.method === 'GET' && endpoint.meta.cacheSec && !body['i'] && !user) { diff --git a/packages/backend/src/server/api/authenticate.ts b/packages/backend/src/server/api/authenticate.ts index 65ccfcf55..192f20ebc 100644 --- a/packages/backend/src/server/api/authenticate.ts +++ b/packages/backend/src/server/api/authenticate.ts @@ -15,8 +15,25 @@ export class AuthenticationError extends Error { } } -export default async (token: string | null): Promise<[CacheableLocalUser | null | undefined, AccessToken | null | undefined]> => { - if (token == null) { +export default async (authorization: string | null | undefined, bodyToken: string | null): Promise<[CacheableLocalUser | null | undefined, AccessToken | null | undefined]> => { + let token: string | null = null; + + // check if there is an authorization header set + if (authorization != null) { + if (bodyToken != null) { + throw new AuthenticationError('using multiple authorization schemes'); + } + + // check if OAuth 2.0 Bearer tokens are being used + // Authorization schemes are case insensitive + if (authorization.substring(0, 7).toLowerCase() === 'bearer ') { + token = authorization.substring(7); + } else { + throw new AuthenticationError('unsupported authentication scheme'); + } + } else if (bodyToken != null) { + token = bodyToken; + } else { return [null, null]; } diff --git a/packages/backend/src/server/api/streaming.ts b/packages/backend/src/server/api/streaming.ts index f8e42d27f..35d0c0fc0 100644 --- a/packages/backend/src/server/api/streaming.ts +++ b/packages/backend/src/server/api/streaming.ts @@ -20,7 +20,7 @@ export const initializeStreamingServer = (server: http.Server) => { // TODO: トークンが間違ってるなどしてauthenticateに失敗したら // コネクション切断するなりエラーメッセージ返すなりする // (現状はエラーがキャッチされておらずサーバーのログに流れて邪魔なので) - const [user, app] = await authenticate(q.i as string); + const [user, app] = await authenticate(request.httpRequest.headers.authorization, q.i); if (user?.isSuspended) { request.reject(400); From 69059b2b1fdf31c68ee878c9cab6a19a1b6811e4 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 18 Jul 2022 17:42:14 +0200 Subject: [PATCH 002/132] improve authentication errors --- packages/backend/src/server/api/api-handler.ts | 10 +++++++--- packages/backend/src/server/api/authenticate.ts | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/server/api/api-handler.ts b/packages/backend/src/server/api/api-handler.ts index 3fecea3fd..3cb94f10f 100644 --- a/packages/backend/src/server/api/api-handler.ts +++ b/packages/backend/src/server/api/api-handler.ts @@ -81,11 +81,15 @@ export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res } }).catch(e => { if (e instanceof AuthenticationError) { - reply(403, new ApiError({ - message: 'Authentication failed. Please ensure your token is correct.', + ctx.response.status = 403; + ctx.response.set('WWW-Authenticate', 'Bearer'); + ctx.response.body = { + message: 'Authentication failed: ' + e.message, code: 'AUTHENTICATION_FAILED', id: 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14', - })); + kind: 'client', + }; + res(); } else { reply(500, new ApiError()); } diff --git a/packages/backend/src/server/api/authenticate.ts b/packages/backend/src/server/api/authenticate.ts index 192f20ebc..39be06c29 100644 --- a/packages/backend/src/server/api/authenticate.ts +++ b/packages/backend/src/server/api/authenticate.ts @@ -42,7 +42,7 @@ export default async (authorization: string | null | undefined, bodyToken: strin () => Users.findOneBy({ token }) as Promise); if (user == null) { - throw new AuthenticationError('user not found'); + throw new AuthenticationError('unknown token'); } return [user, null]; @@ -56,7 +56,7 @@ export default async (authorization: string | null | undefined, bodyToken: strin }); if (accessToken == null) { - throw new AuthenticationError('invalid signature'); + throw new AuthenticationError('unknown token'); } AccessTokens.update(accessToken.id, { From ad2f017af8083b97343a6d5c2fe83b4aed2b8de3 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 18 Jul 2022 18:37:41 +0200 Subject: [PATCH 003/132] update openapi spec generator Properly document GET API endpoints. Added Bearer token authentication. --- .../src/server/api/openapi/gen-spec.ts | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts index 68fa81404..86f2f4228 100644 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ b/packages/backend/src/server/api/openapi/gen-spec.ts @@ -33,6 +33,11 @@ export function genOpenapiSpec() { in: 'body', name: 'i', }, + // TODO: change this to oauth2 when the remaining oauth stuff is set up + Bearer: { + type: 'http', + scheme: 'bearer', + } }, }, }; @@ -71,6 +76,19 @@ export function genOpenapiSpec() { schema.required.push('file'); } + const security = [ + { + ApiKeyAuth: [], + }, + { + Bearer: [], + }, + ]; + if (!endpoint.meta.requireCredential) { + // add this to make authentication optional + security.push({}); + } + const info = { operationId: endpoint.name, summary: endpoint.name, @@ -79,14 +97,8 @@ export function genOpenapiSpec() { description: 'Source code', url: `https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`, }, - ...(endpoint.meta.tags ? { - tags: [endpoint.meta.tags[0]], - } : {}), - ...(endpoint.meta.requireCredential ? { - security: [{ - ApiKeyAuth: [], - }], - } : {}), + tags: endpoint.meta.tags || undefined, + security, requestBody: { required: true, content: { @@ -181,9 +193,16 @@ export function genOpenapiSpec() { }, }; - spec.paths['/' + endpoint.name] = { + const path = { post: info, }; + if (endpoint.meta.allowGet) { + path.get = { ...info }; + // API Key authentication is not permitted for GET requests + path.get.security = path.get.security.filter(elem => !Object.prototype.hasOwnProperty.call(elem, 'ApiKeyAuth')); + } + + spec.paths['/' + endpoint.name] = path; } return spec; From 5217f18ca4cbb140213ad84d914c825d920b1977 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Mon, 18 Jul 2022 23:32:03 +0200 Subject: [PATCH 004/132] handle authentication errors in stream API --- packages/backend/src/server/api/streaming.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/server/api/streaming.ts b/packages/backend/src/server/api/streaming.ts index 35d0c0fc0..cfe209d09 100644 --- a/packages/backend/src/server/api/streaming.ts +++ b/packages/backend/src/server/api/streaming.ts @@ -17,10 +17,14 @@ export const initializeStreamingServer = (server: http.Server) => { ws.on('request', async (request) => { const q = request.resourceURL.query as ParsedUrlQuery; - // TODO: トークンが間違ってるなどしてauthenticateに失敗したら - // コネクション切断するなりエラーメッセージ返すなりする - // (現状はエラーがキャッチされておらずサーバーのログに流れて邪魔なので) - const [user, app] = await authenticate(request.httpRequest.headers.authorization, q.i); + const [user, app] = await authenticate(request.httpRequest.headers.authorization, q.i) + .catch(err => { + request.reject(403, err.message); + return []; + }); + if (typeof user === 'undefined') { + return; + } if (user?.isSuspended) { request.reject(400); From c36d96316ee751cad34df94d617f92ed866f2035 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Tue, 19 Jul 2022 00:49:40 +0200 Subject: [PATCH 005/132] client: use bearer token authorization --- packages/client/src/components/cropper-dialog.vue | 4 +++- packages/client/src/components/page/page.post.vue | 4 +++- packages/client/src/os.ts | 15 +++++++++------ packages/client/src/scripts/upload.ts | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/client/src/components/cropper-dialog.vue b/packages/client/src/components/cropper-dialog.vue index a8bde6ea0..47335af6a 100644 --- a/packages/client/src/components/cropper-dialog.vue +++ b/packages/client/src/components/cropper-dialog.vue @@ -62,7 +62,6 @@ const ok = async () => { croppedCanvas.toBlob(blob => { const formData = new FormData(); formData.append('file', blob); - formData.append('i', $i.token); if (defaultStore.state.uploadFolder) { formData.append('folderId', defaultStore.state.uploadFolder); } @@ -70,6 +69,9 @@ const ok = async () => { fetch(apiUrl + '/drive/files/create', { method: 'POST', body: formData, + headers: { + authorization: `Bearer ${$i.token}`, + }, }) .then(response => response.json()) .then(f => { diff --git a/packages/client/src/components/page/page.post.vue b/packages/client/src/components/page/page.post.vue index 3401f945b..1b11e6f48 100644 --- a/packages/client/src/components/page/page.post.vue +++ b/packages/client/src/components/page/page.post.vue @@ -54,7 +54,6 @@ export default defineComponent({ canvas.toBlob(blob => { const formData = new FormData(); formData.append('file', blob); - formData.append('i', this.$i.token); if (this.$store.state.uploadFolder) { formData.append('folderId', this.$store.state.uploadFolder); } @@ -62,6 +61,9 @@ export default defineComponent({ fetch(apiUrl + '/drive/files/create', { method: 'POST', body: formData, + headers: { + authorization: `Bearer ${this.$i.token}`, + }, }) .then(response => response.json()) .then(f => { diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts index 00dae867d..9defc55df 100644 --- a/packages/client/src/os.ts +++ b/packages/client/src/os.ts @@ -23,17 +23,16 @@ export const api = ((endpoint: string, data: Record = {}, token?: s pendingApiRequestsCount.value--; }; - const promise = new Promise((resolve, reject) => { - // Append a credential - if ($i) (data as any).i = $i.token; - if (token !== undefined) (data as any).i = token; + const authorizationToken = token ?? $i?.token ?? undefined; + const authorization = authorizationToken ? `Bearer ${authorizationToken}` : undefined; - // Send request + const promise = new Promise((resolve, reject) => { fetch(endpoint.indexOf('://') > -1 ? endpoint : `${apiUrl}/${endpoint}`, { method: 'POST', body: JSON.stringify(data), credentials: 'omit', cache: 'no-cache', + headers: { authorization }, }).then(async (res) => { const body = res.status === 204 ? null : await res.json(); @@ -52,7 +51,7 @@ export const api = ((endpoint: string, data: Record = {}, token?: s return promise; }) as typeof apiClient.request; -export const apiGet = ((endpoint: string, data: Record = {}) => { +export const apiGet = ((endpoint: string, data: Record = {}, token?: string | null | undefined) => { pendingApiRequestsCount.value++; const onFinally = () => { @@ -61,12 +60,16 @@ export const apiGet = ((endpoint: string, data: Record = {}) => { const query = new URLSearchParams(data); + const authorizationToken = token ?? $i?.token ?? undefined; + const authorization = authorizationToken ? `Bearer ${authorizationToken}` : undefined; + const promise = new Promise((resolve, reject) => { // Send request fetch(`${apiUrl}/${endpoint}?${query}`, { method: 'GET', credentials: 'omit', cache: 'default', + headers: { authorization }, }).then(async (res) => { const body = res.status === 204 ? null : await res.json(); diff --git a/packages/client/src/scripts/upload.ts b/packages/client/src/scripts/upload.ts index 51f1c1b86..6f50e9bd9 100644 --- a/packages/client/src/scripts/upload.ts +++ b/packages/client/src/scripts/upload.ts @@ -71,7 +71,6 @@ export function uploadFile( } const formData = new FormData(); - formData.append('i', $i.token); formData.append('force', 'true'); formData.append('file', resizedImage || file); formData.append('name', ctx.name); @@ -79,6 +78,7 @@ export function uploadFile( const xhr = new XMLHttpRequest(); xhr.open('POST', apiUrl + '/drive/files/create', true); + xhr.setRequestHeader('Authorization', `Bearer ${$i.token}`); xhr.onload = (ev) => { if (xhr.status !== 200 || ev.target == null || ev.target.response == null) { // TODO: 消すのではなくて(ネットワーク的なエラーなら)再送できるようにしたい From e4139b7ac1f5f1247385e2c30f6a87e7b41bb0c7 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Tue, 19 Jul 2022 21:53:08 -0700 Subject: [PATCH 006/132] fix search --- packages/client/src/components/google.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/google.vue b/packages/client/src/components/google.vue index bb4b439ee..92c3835c8 100644 --- a/packages/client/src/components/google.vue +++ b/packages/client/src/components/google.vue @@ -15,7 +15,7 @@ const props = defineProps<{ const query = ref(props.q); const search = () => { - window.open(`https://www.google.com/search?q=${query.value}`, '_blank'); + window.open(`https://search.annoyingorange.xyz/search?q=${query.value}`, '_blank'); }; From 711322cd9a33b3d15c97783a5bb8c34ef8ba04a0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 20 Jul 2022 19:59:27 +0900 Subject: [PATCH 007/132] =?UTF-8?q?enhance(client):=20=E3=83=8D=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=81=97=E3=81=9F=E3=83=AB=E3=83=BC=E3=83=86=E3=82=A3?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/global/router-view.vue | 38 +++- .../client/src/components/page-window.vue | 2 +- packages/client/src/nirax.ts | 190 +++++++++++------- packages/client/src/pages/_empty_.vue | 7 + packages/client/src/pages/admin/index.vue | 99 +++------ packages/client/src/pages/settings/index.vue | 144 ++++--------- ....statusbar.vue => statusbar.statusbar.vue} | 0 .../{statusbars.vue => statusbar.vue} | 2 +- packages/client/src/router.ts | 174 +++++++++++++++- 9 files changed, 391 insertions(+), 265 deletions(-) create mode 100644 packages/client/src/pages/_empty_.vue rename packages/client/src/pages/settings/{statusbars.statusbar.vue => statusbar.statusbar.vue} (100%) rename packages/client/src/pages/settings/{statusbars.vue => statusbar.vue} (96%) diff --git a/packages/client/src/components/global/router-view.vue b/packages/client/src/components/global/router-view.vue index cd1e78019..1d841e050 100644 --- a/packages/client/src/components/global/router-view.vue +++ b/packages/client/src/components/global/router-view.vue @@ -11,8 +11,8 @@ diff --git a/packages/client/src/components/page-window.vue b/packages/client/src/components/page-window.vue index 98140b95c..43d75b0cf 100644 --- a/packages/client/src/components/page-window.vue +++ b/packages/client/src/components/page-window.vue @@ -114,7 +114,7 @@ function menu(ev) { function back() { history.pop(); - router.change(history[history.length - 1].path, history[history.length - 1].key); + router.replace(history[history.length - 1].path, history[history.length - 1].key); } function close() { diff --git a/packages/client/src/nirax.ts b/packages/client/src/nirax.ts index 4ba1fe70f..0ee39bf47 100644 --- a/packages/client/src/nirax.ts +++ b/packages/client/src/nirax.ts @@ -13,6 +13,7 @@ type RouteDef = { name?: string; hash?: string; globalCacheKey?: string; + children?: RouteDef[]; }; type ParsedPath = (string | { @@ -22,6 +23,8 @@ type ParsedPath = (string | { optional?: boolean; })[]; +export type Resolved = { route: RouteDef; props: Map; child?: Resolved; }; + function parsePath(path: string): ParsedPath { const res = [] as ParsedPath; @@ -51,8 +54,11 @@ export class Router extends EventEmitter<{ change: (ctx: { beforePath: string; path: string; - route: RouteDef | null; - props: Map | null; + resolved: Resolved; + key: string; + }) => void; + replace: (ctx: { + path: string; key: string; }) => void; push: (ctx: { @@ -65,12 +71,12 @@ export class Router extends EventEmitter<{ same: () => void; }> { private routes: RouteDef[]; + public current: Resolved; + public currentRef: ShallowRef = shallowRef(); + public currentRoute: ShallowRef = shallowRef(); private currentPath: string; - private currentComponent: Component | null = null; - private currentProps: Map | null = null; private currentKey = Date.now().toString(); - public currentRoute: ShallowRef = shallowRef(null); public navHook: ((path: string, flag?: any) => boolean) | null = null; constructor(routes: Router['routes'], currentPath: Router['currentPath']) { @@ -78,10 +84,10 @@ export class Router extends EventEmitter<{ this.routes = routes; this.currentPath = currentPath; - this.navigate(currentPath, null, true); + this.navigate(currentPath, null, false); } - public resolve(path: string): { route: RouteDef; props: Map; } | null { + public resolve(path: string): Resolved | null { let queryString: string | null = null; let hash: string | null = null; if (path[0] === '/') path = path.substring(1); @@ -96,77 +102,108 @@ export class Router extends EventEmitter<{ if (_DEV_) console.log('Routing: ', path, queryString); - const _parts = path.split('/').filter(part => part.length !== 0); + function check(routes: RouteDef[], _parts: string[]): Resolved | null { + forEachRouteLoop: + for (const route of routes) { + let parts = [ ..._parts ]; + const props = new Map(); - forEachRouteLoop: - for (const route of this.routes) { - let parts = [ ..._parts ]; - const props = new Map(); - - pathMatchLoop: - for (const p of parsePath(route.path)) { - if (typeof p === 'string') { - if (p === parts[0]) { - parts.shift(); - } else { - continue forEachRouteLoop; - } - } else { - if (parts[0] == null && !p.optional) { - continue forEachRouteLoop; - } - if (p.wildcard) { - if (parts.length !== 0) { - props.set(p.name, safeURIDecode(parts.join('/'))); - parts = []; - } - break pathMatchLoop; - } else { - if (p.startsWith) { - if (parts[0] == null || !parts[0].startsWith(p.startsWith)) continue forEachRouteLoop; - - props.set(p.name, safeURIDecode(parts[0].substring(p.startsWith.length))); + pathMatchLoop: + for (const p of parsePath(route.path)) { + if (typeof p === 'string') { + if (p === parts[0]) { parts.shift(); } else { - if (parts[0]) { - props.set(p.name, safeURIDecode(parts[0])); + continue forEachRouteLoop; + } + } else { + if (parts[0] == null && !p.optional) { + continue forEachRouteLoop; + } + if (p.wildcard) { + if (parts.length !== 0) { + props.set(p.name, safeURIDecode(parts.join('/'))); + parts = []; + } + break pathMatchLoop; + } else { + if (p.startsWith) { + if (parts[0] == null || !parts[0].startsWith(p.startsWith)) continue forEachRouteLoop; + + props.set(p.name, safeURIDecode(parts[0].substring(p.startsWith.length))); + parts.shift(); + } else { + if (parts[0]) { + props.set(p.name, safeURIDecode(parts[0])); + } + parts.shift(); } - parts.shift(); } } } - } - if (parts.length !== 0) continue forEachRouteLoop; + if (parts.length === 0) { + if (route.children) { + const child = check(route.children, []); + if (child) { + return { + route, + props, + child, + }; + } else { + continue forEachRouteLoop; + } + } - if (route.hash != null && hash != null) { - props.set(route.hash, safeURIDecode(hash)); - } - - if (route.query != null && queryString != null) { - const queryObject = [...new URLSearchParams(queryString).entries()] - .reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {}); - - for (const q in route.query) { - const as = route.query[q]; - if (queryObject[q]) { - props.set(as, safeURIDecode(queryObject[q])); + if (route.hash != null && hash != null) { + props.set(route.hash, safeURIDecode(hash)); + } + + if (route.query != null && queryString != null) { + const queryObject = [...new URLSearchParams(queryString).entries()] + .reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {}); + + for (const q in route.query) { + const as = route.query[q]; + if (queryObject[q]) { + props.set(as, safeURIDecode(queryObject[q])); + } + } + } + + return { + route, + props, + }; + } else { + if (route.children) { + const child = check(route.children, parts); + if (child) { + return { + route, + props, + child, + }; + } else { + continue forEachRouteLoop; + } + } else { + continue forEachRouteLoop; } } } - return { - route, - props, - }; + return null; } - return null; + const _parts = path.split('/').filter(part => part.length !== 0); + + return check(this.routes, _parts); } - private navigate(path: string, key: string | null | undefined, initial = false) { + private navigate(path: string, key: string | null | undefined, emitChange = true) { const beforePath = this.currentPath; - const beforeRoute = this.currentRoute.value; this.currentPath = path; const res = this.resolve(this.currentPath); @@ -181,28 +218,21 @@ export class Router extends EventEmitter<{ const isSamePath = beforePath === path; if (isSamePath && key == null) key = this.currentKey; - this.currentComponent = res.route.component; - this.currentProps = res.props; + this.current = res; + this.currentRef.value = res; this.currentRoute.value = res.route; - this.currentKey = this.currentRoute.value.globalCacheKey ?? key ?? Date.now().toString(); + this.currentKey = res.route.globalCacheKey ?? key ?? path; - if (!initial) { + if (emitChange) { this.emit('change', { beforePath, path, - route: this.currentRoute.value, - props: this.currentProps, + resolved: res, key: this.currentKey, }); } - } - public getCurrentComponent() { - return this.currentComponent; - } - - public getCurrentProps() { - return this.currentProps; + return res; } public getCurrentPath() { @@ -223,17 +253,23 @@ export class Router extends EventEmitter<{ const cancel = this.navHook(path, flag); if (cancel) return; } - this.navigate(path, null); + const res = this.navigate(path, null); this.emit('push', { beforePath, path, - route: this.currentRoute.value, - props: this.currentProps, + route: res.route, + props: res.props, key: this.currentKey, }); } - public change(path: string, key?: string | null) { + public replace(path: string, key?: string | null, emitEvent = true) { this.navigate(path, key); + if (emitEvent) { + this.emit('replace', { + path, + key: this.currentKey, + }); + } } } diff --git a/packages/client/src/pages/_empty_.vue b/packages/client/src/pages/_empty_.vue new file mode 100644 index 000000000..000b6decc --- /dev/null +++ b/packages/client/src/pages/_empty_.vue @@ -0,0 +1,7 @@ + + + diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue index d82880c34..2ff55d351 100644 --- a/packages/client/src/pages/admin/index.vue +++ b/packages/client/src/pages/admin/index.vue @@ -1,6 +1,6 @@ @@ -44,15 +44,10 @@ const indexInfo = { hideHeader: true, }; -const props = defineProps<{ - initialPage?: string, -}>(); - provide('shouldOmitHeaderTitle', false); let INFO = $ref(indexInfo); let childInfo = $ref(null); -let page = $ref(props.initialPage); let narrow = $ref(false); let view = $ref(null); let el = $ref(null); @@ -61,6 +56,7 @@ let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instan let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha; let noEmailServer = !instance.enableEmail; let thereIsUnresolvedAbuseReport = $ref(false); +let currentPage = $computed(() => router.currentRef.value.child); os.api('admin/abuse-user-reports', { state: 'unresolved', @@ -94,47 +90,47 @@ const menuDef = $computed(() => [{ icon: 'fas fa-tachometer-alt', text: i18n.ts.dashboard, to: '/admin/overview', - active: props.initialPage === 'overview', + active: currentPage?.route.name === 'overview', }, { icon: 'fas fa-users', text: i18n.ts.users, to: '/admin/users', - active: props.initialPage === 'users', + active: currentPage?.route.name === 'users', }, { icon: 'fas fa-laugh', text: i18n.ts.customEmojis, to: '/admin/emojis', - active: props.initialPage === 'emojis', + active: currentPage?.route.name === 'emojis', }, { icon: 'fas fa-globe', text: i18n.ts.federation, to: '/about#federation', - active: props.initialPage === 'federation', + active: currentPage?.route.name === 'federation', }, { icon: 'fas fa-clipboard-list', text: i18n.ts.jobQueue, to: '/admin/queue', - active: props.initialPage === 'queue', + active: currentPage?.route.name === 'queue', }, { icon: 'fas fa-cloud', text: i18n.ts.files, to: '/admin/files', - active: props.initialPage === 'files', + active: currentPage?.route.name === 'files', }, { icon: 'fas fa-broadcast-tower', text: i18n.ts.announcements, to: '/admin/announcements', - active: props.initialPage === 'announcements', + active: currentPage?.route.name === 'announcements', }, { icon: 'fas fa-audio-description', text: i18n.ts.ads, to: '/admin/ads', - active: props.initialPage === 'ads', + active: currentPage?.route.name === 'ads', }, { icon: 'fas fa-exclamation-circle', text: i18n.ts.abuseReports, to: '/admin/abuses', - active: props.initialPage === 'abuses', + active: currentPage?.route.name === 'abuses', }], }, { title: i18n.ts.settings, @@ -142,47 +138,47 @@ const menuDef = $computed(() => [{ icon: 'fas fa-cog', text: i18n.ts.general, to: '/admin/settings', - active: props.initialPage === 'settings', + active: currentPage?.route.name === 'settings', }, { icon: 'fas fa-envelope', text: i18n.ts.emailServer, to: '/admin/email-settings', - active: props.initialPage === 'email-settings', + active: currentPage?.route.name === 'email-settings', }, { icon: 'fas fa-cloud', text: i18n.ts.objectStorage, to: '/admin/object-storage', - active: props.initialPage === 'object-storage', + active: currentPage?.route.name === 'object-storage', }, { icon: 'fas fa-lock', text: i18n.ts.security, to: '/admin/security', - active: props.initialPage === 'security', + active: currentPage?.route.name === 'security', }, { icon: 'fas fa-globe', text: i18n.ts.relays, to: '/admin/relays', - active: props.initialPage === 'relays', + active: currentPage?.route.name === 'relays', }, { icon: 'fas fa-share-alt', text: i18n.ts.integration, to: '/admin/integrations', - active: props.initialPage === 'integrations', + active: currentPage?.route.name === 'integrations', }, { icon: 'fas fa-ban', text: i18n.ts.instanceBlocking, to: '/admin/instance-block', - active: props.initialPage === 'instance-block', + active: currentPage?.route.name === 'instance-block', }, { icon: 'fas fa-ghost', text: i18n.ts.proxyAccount, to: '/admin/proxy-account', - active: props.initialPage === 'proxy-account', + active: currentPage?.route.name === 'proxy-account', }, { icon: 'fas fa-cogs', text: i18n.ts.other, to: '/admin/other-settings', - active: props.initialPage === 'other-settings', + active: currentPage?.route.name === 'other-settings', }], }, { title: i18n.ts.info, @@ -190,55 +186,12 @@ const menuDef = $computed(() => [{ icon: 'fas fa-database', text: i18n.ts.database, to: '/admin/database', - active: props.initialPage === 'database', + active: currentPage?.route.name === 'database', }], }]); -const component = $computed(() => { - if (props.initialPage == null) return null; - switch (props.initialPage) { - case 'overview': return defineAsyncComponent(() => import('./overview.vue')); - case 'users': return defineAsyncComponent(() => import('./users.vue')); - case 'emojis': return defineAsyncComponent(() => import('./emojis.vue')); - //case 'federation': return defineAsyncComponent(() => import('../federation.vue')); - case 'queue': return defineAsyncComponent(() => import('./queue.vue')); - case 'files': return defineAsyncComponent(() => import('./files.vue')); - case 'announcements': return defineAsyncComponent(() => import('./announcements.vue')); - case 'ads': return defineAsyncComponent(() => import('./ads.vue')); - case 'database': return defineAsyncComponent(() => import('./database.vue')); - case 'abuses': return defineAsyncComponent(() => import('./abuses.vue')); - case 'settings': return defineAsyncComponent(() => import('./settings.vue')); - case 'email-settings': return defineAsyncComponent(() => import('./email-settings.vue')); - case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue')); - case 'security': return defineAsyncComponent(() => import('./security.vue')); - case 'relays': return defineAsyncComponent(() => import('./relays.vue')); - case 'integrations': return defineAsyncComponent(() => import('./integrations.vue')); - case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue')); - case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue')); - case 'other-settings': return defineAsyncComponent(() => import('./other-settings.vue')); - } -}); - -watch(component, () => { - pageProps = {}; - - nextTick(() => { - scroll(el, { top: 0 }); - }); -}, { immediate: true }); - -watch(() => props.initialPage, () => { - if (props.initialPage == null && !narrow) { - router.push('/admin/overview'); - } else { - if (props.initialPage == null) { - INFO = indexInfo; - } - } -}); - watch(narrow, () => { - if (props.initialPage == null && !narrow) { + if (currentPage?.route.name == null && !narrow) { router.push('/admin/overview'); } }); @@ -247,7 +200,7 @@ onMounted(() => { ro.observe(el); narrow = el.offsetWidth < NARROW_THRESHOLD; - if (props.initialPage == null && !narrow) { + if (currentPage?.route.name == null && !narrow) { router.push('/admin/overview'); } }); diff --git a/packages/client/src/pages/settings/index.vue b/packages/client/src/pages/settings/index.vue index 8b1cc6c12..8964333b3 100644 --- a/packages/client/src/pages/settings/index.vue +++ b/packages/client/src/pages/settings/index.vue @@ -4,15 +4,15 @@
- @@ -190,7 +190,7 @@ function more(ev: MouseEvent) { position: sticky; top: 0; z-index: 1; - padding: 2.5rem 0; + padding: 2rem 0; background: var(--X14); -webkit-backdrop-filter: var(--blur, blur(8px)); backdrop-filter: var(--blur, blur(8px)); @@ -207,7 +207,7 @@ function more(ev: MouseEvent) { mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%); } - > .instance { + > .account { position: relative; display: block; text-align: center; @@ -273,22 +273,14 @@ function more(ev: MouseEvent) { > .account { position: relative; - display: flex; - align-items: center; - padding-left: 30px; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; + display: block; + text-align: center; width: 100%; - text-align: left; - box-sizing: border-box; - margin-top: 16px; - > .avatar { - position: relative; - width: 32px; + > .icon { + display: inline-block; + width: 38px; aspect-ratio: 1; - margin-right: 8px; } } } @@ -377,12 +369,12 @@ function more(ev: MouseEvent) { position: sticky; top: 0; z-index: 1; - padding: 2.5rem 0; + padding: 2rem 0; background: var(--X14); -webkit-backdrop-filter: var(--blur, blur(8px)); backdrop-filter: var(--blur, blur(8px)); - > .instance { + > .account { display: block; text-align: center; width: 100%; @@ -443,19 +435,16 @@ function more(ev: MouseEvent) { } > .account { + position: relative; display: block; text-align: center; width: 100%; - > .avatar { + > .icon { display: inline-block; width: 38px; aspect-ratio: 1; } - - > .text { - display: none; - } } } From 4ee8834993edb170edf7731b836b1c69b6cb42cc Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sat, 23 Jul 2022 20:01:24 -0700 Subject: [PATCH 045/132] fix style --- packages/client/src/ui/_common_/navbar.vue | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/client/src/ui/_common_/navbar.vue b/packages/client/src/ui/_common_/navbar.vue index bb4fbfaef..107cd9a3b 100644 --- a/packages/client/src/ui/_common_/navbar.vue +++ b/packages/client/src/ui/_common_/navbar.vue @@ -259,12 +259,18 @@ function more(ev: MouseEvent) { } } + > .instance { + position: relative; + display: block; + text-align: center; + width: 100%; + > .icon { - position: relative; - margin-left: 30px; - margin-right: 8px; - width: 32px; + display: inline-block; + width: 32px !important; + aspect-ratio: 1; } + } > .text { position: relative; @@ -381,7 +387,7 @@ function more(ev: MouseEvent) { > .icon { display: inline-block; - width: 30px; + width: 40px; aspect-ratio: 1; } } @@ -434,7 +440,7 @@ function more(ev: MouseEvent) { } } - > .account { + > .instance { position: relative; display: block; text-align: center; @@ -442,7 +448,7 @@ function more(ev: MouseEvent) { > .icon { display: inline-block; - width: 38px; + width: 32px !important; aspect-ratio: 1; } } From cc8accf5d551acb875d40d36c5564ecfe3c99164 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sat, 23 Jul 2022 20:04:13 -0700 Subject: [PATCH 046/132] showGap true --- packages/client/src/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts index ee9c33e29..5c340cac0 100644 --- a/packages/client/src/store.ts +++ b/packages/client/src/store.ts @@ -182,7 +182,7 @@ export const defaultStore = markRaw(new Storage('base', { }, showGapBetweenNotesInTimeline: { where: 'device', - default: false, + default: true, }, darkMode: { where: 'device', From 9ce7413abe6180ab3ee64aea2666ddebfbad704d Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sat, 23 Jul 2022 20:06:26 -0700 Subject: [PATCH 047/132] spacing in navbar --- packages/client/src/ui/_common_/navbar.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/client/src/ui/_common_/navbar.vue b/packages/client/src/ui/_common_/navbar.vue index 107cd9a3b..a9bc8da0a 100644 --- a/packages/client/src/ui/_common_/navbar.vue +++ b/packages/client/src/ui/_common_/navbar.vue @@ -304,6 +304,7 @@ function more(ev: MouseEvent) { display: block; padding-left: 30px; line-height: 2.85rem; + margin-bottom: 0.5rem; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; From 86f7bd3b72a4dd604977146189cab4e8e76fd887 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sat, 23 Jul 2022 20:10:17 -0700 Subject: [PATCH 048/132] fix --- packages/client/src/ui/_common_/navbar.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/src/ui/_common_/navbar.vue b/packages/client/src/ui/_common_/navbar.vue index a9bc8da0a..c2939a56b 100644 --- a/packages/client/src/ui/_common_/navbar.vue +++ b/packages/client/src/ui/_common_/navbar.vue @@ -215,7 +215,7 @@ function more(ev: MouseEvent) { > .icon { display: inline-block; - width: 38px; + width: 55px; aspect-ratio: 1; } } @@ -258,6 +258,7 @@ function more(ev: MouseEvent) { background: var(--accentLighten); } } + } > .instance { position: relative; From d34c615105cabe9ddf21ab4020f2cf7fd7b0fbfd Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sat, 23 Jul 2022 20:21:19 -0700 Subject: [PATCH 049/132] fixxxx --- packages/client/src/ui/_common_/navbar.vue | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/client/src/ui/_common_/navbar.vue b/packages/client/src/ui/_common_/navbar.vue index c2939a56b..5994f34e1 100644 --- a/packages/client/src/ui/_common_/navbar.vue +++ b/packages/client/src/ui/_common_/navbar.vue @@ -258,6 +258,10 @@ function more(ev: MouseEvent) { background: var(--accentLighten); } } + + > .text { + position: relative; + } } > .instance { @@ -273,11 +277,6 @@ function more(ev: MouseEvent) { } } - > .text { - position: relative; - } - } - > .account { position: relative; display: block; From d8afd6e234b7ed0bb3e52865aa02c4fe891036ff Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sat, 23 Jul 2022 21:10:34 -0700 Subject: [PATCH 050/132] fix note button --- package.json | 2 +- packages/client/src/ui/_common_/navbar.vue | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2cc816755..4e04dfd1d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "12.118.0.b3.1-calc", + "version": "12.118.0.b3.2-calc", "codename": "indigo", "repository": { "type": "git", diff --git a/packages/client/src/ui/_common_/navbar.vue b/packages/client/src/ui/_common_/navbar.vue index 5994f34e1..dfddfa268 100644 --- a/packages/client/src/ui/_common_/navbar.vue +++ b/packages/client/src/ui/_common_/navbar.vue @@ -259,9 +259,12 @@ function more(ev: MouseEvent) { } } - > .text { + > .icon, > .text { position: relative; + left: 3rem; + color: var(--fgOnAccent); } + } > .instance { @@ -274,6 +277,7 @@ function more(ev: MouseEvent) { display: inline-block; width: 32px !important; aspect-ratio: 1; + margin-top: 0.5rem; } } From 3b80d3f348ba48cca5b82f13625d1a262c556ed5 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sat, 23 Jul 2022 21:16:35 -0700 Subject: [PATCH 051/132] More RPine --- packages/client/src/ui/_common_/navbar.vue | 4 ++++ packages/client/src/widgets/activity.chart.vue | 6 +++--- packages/client/src/widgets/calendar.vue | 6 +++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/client/src/ui/_common_/navbar.vue b/packages/client/src/ui/_common_/navbar.vue index dfddfa268..3c79072a4 100644 --- a/packages/client/src/ui/_common_/navbar.vue +++ b/packages/client/src/ui/_common_/navbar.vue @@ -265,6 +265,10 @@ function more(ev: MouseEvent) { color: var(--fgOnAccent); } + > .text { + margin-left: 1rem; + } + } > .instance { diff --git a/packages/client/src/widgets/activity.chart.vue b/packages/client/src/widgets/activity.chart.vue index b61e419f9..140bd6b59 100644 --- a/packages/client/src/widgets/activity.chart.vue +++ b/packages/client/src/widgets/activity.chart.vue @@ -4,17 +4,17 @@ :points="pointsNote" fill="none" stroke-width="1" - stroke="#41ddde"/> + stroke="#c4a7e7"/> + stroke="#eb6f92"/> + stroke="#ebbcba"/> ({ &:nth-child(1) { > .meter > .val { - background: #f7796c; + background: #eb6f92; } } &:nth-child(2) { > .meter > .val { - background: #a1de41; + background: #ebbcba; } } &:nth-child(3) { > .meter > .val { - background: #41ddde; + background: #c4a7e7; } } } From 266d79c122b9db197155314e02c28efcf7a75d61 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sat, 23 Jul 2022 21:23:03 -0700 Subject: [PATCH 052/132] More rpine stuff --- packages/client/src/pages/messaging/messaging-room.message.vue | 2 +- packages/client/src/widgets/activity.chart.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/pages/messaging/messaging-room.message.vue b/packages/client/src/pages/messaging/messaging-room.message.vue index 393d2a17b..8f1d0bea6 100644 --- a/packages/client/src/pages/messaging/messaging-room.message.vue +++ b/packages/client/src/pages/messaging/messaging-room.message.vue @@ -187,7 +187,7 @@ function del(): void { > p { padding: 30px; text-align: center; - color: #555; + color: #6e6a86; background: #ddd; } } diff --git a/packages/client/src/widgets/activity.chart.vue b/packages/client/src/widgets/activity.chart.vue index 140bd6b59..f24125aa3 100644 --- a/packages/client/src/widgets/activity.chart.vue +++ b/packages/client/src/widgets/activity.chart.vue @@ -19,7 +19,7 @@ :points="pointsTotal" fill="none" stroke-width="1" - stroke="#555" + stroke="#6e6a86" stroke-dasharray="2 2"/> From 8532c7eea5453bd6292be462863beb46213936d4 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sat, 23 Jul 2022 21:23:04 -0700 Subject: [PATCH 053/132] more rpine stuff --- packages/backend/src/server/web/bios.css | 34 +++++++++---------- packages/backend/src/server/web/boot.js | 21 ++++++------ packages/backend/src/server/web/cli.css | 18 +++++----- .../backend/src/server/web/views/flush.pug | 6 ++-- 4 files changed, 39 insertions(+), 40 deletions(-) diff --git a/packages/backend/src/server/web/bios.css b/packages/backend/src/server/web/bios.css index 318fc3e28..0f15b66f6 100644 --- a/packages/backend/src/server/web/bios.css +++ b/packages/backend/src/server/web/bios.css @@ -1,11 +1,11 @@ main > .tabs { padding: 16px; - border-bottom: 4px solid #c3c3c3; + border-bottom: 4px solid #908caa; } #lsEditor > .adder { margin: 16px; padding: 16px; - border: 2px solid #c3c3c3; + border: 2px solid #908caa; } #lsEditor > .adder > textarea { display: block; @@ -15,7 +15,7 @@ main > .tabs { } #lsEditor > .record { padding: 16px; - border-bottom: 1px solid #c3c3c3; + border-bottom: 1px solid #908caa; } #lsEditor > .record > header { font-weight: 700; @@ -28,15 +28,15 @@ main > .tabs { } html { - background: #222; + background: #191724; } main { - background: #333; + background: #1f1d2e; border-radius: 10px; } #tl > div { padding: 16px; - border-bottom: 1px solid #c3c3c3; + border-bottom: 1px solid #908caa; } #tl > div > header { font-weight: 700; @@ -50,8 +50,8 @@ main { } body, html { - background-color: #222; - color: #dfddcc; + background-color: #191724; + color: #e0def4; justify-content: center; margin: auto; padding: 10px; @@ -63,9 +63,9 @@ button { border: none; cursor: pointer; margin-bottom: 12px; - background: linear-gradient(90deg, rgb(134, 179, 0), rgb(74, 179, 0)); + background: linear-gradient(90deg, rgb(156, 207, 216), rgb(74, 179, 0)); line-height: 50px; - color: #222; + color: #191724; font-weight: bold; font-size: 20px; padding: 12px; @@ -80,7 +80,7 @@ button { button { background: #444; line-height: 40px; - color: rgb(153, 204, 0); + color: rgb(156, 207, 216); font-size: 16px; padding: 0 20px; margin-right: 5px; @@ -91,18 +91,18 @@ button:hover { background: #555; } #ls { - background: linear-gradient(90deg, rgb(134, 179, 0), rgb(74, 179, 0)); + background: linear-gradient(90deg, rgb(156, 207, 216), rgb(74, 179, 0)); line-height: 30px; - color: #222; + color: #191724; font-weight: bold; font-size: 18px; padding: 12px; } #ls:hover { - background: rgb(153, 204, 0); + background: rgb(156, 207, 216); } a { - color: rgb(134, 179, 0); + color: rgb(156, 207, 216); text-decoration: none; } p, @@ -120,7 +120,7 @@ textarea { background-color: #444; border: solid #aaa; border-radius: 10px; - color: #dfddcc; + color: #e0def4; margin-top: 1rem; margin-bottom: 1rem; width: 20rem; @@ -135,7 +135,7 @@ input { background-color: #666; border: solid #aaa; border-radius: 10px; - color: #dfddcc; + color: #e0def4; margin-top: 1rem; margin-bottom: 1rem; width: 10rem; diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index b0d529ec3..aeb13793e 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -137,9 +137,8 @@ Refresh

Don't worry, it's (probably) not your fault.

+

Please make sure your browser is up-to-date and any AdBlockers are off.

If the problem persists after refreshing, please contact your instance's administrator.
You may also try the following options:

-

Update your os and browser.

-

Disable an adblocker.