From 8c9f8de6d2f0ed193911c17510e45d23b18ef60c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Mar 2021 20:15:24 +0000 Subject: [PATCH 01/76] Bump @types/koa from 2.13.0 to 2.13.1 Bumps [@types/koa](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/koa) from 2.13.0 to 2.13.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/koa) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> --- package.json | 2 +- yarn.lock | 29 +++++------------------------ 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 2c0525b605..8fc4af571c 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@types/jsdom": "16.2.6", "@types/jsonld": "1.5.4", "@types/katex": "0.11.0", - "@types/koa": "2.13.0", + "@types/koa": "2.13.1", "@types/koa-bodyparser": "4.3.0", "@types/koa-cors": "0.0.0", "@types/koa-favicon": "2.0.19", diff --git a/yarn.lock b/yarn.lock index c03fa4182e..d5fece07ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -778,24 +778,10 @@ dependencies: "@types/koa" "*" -"@types/koa@*": - version "2.11.6" - resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.11.6.tgz#b7030caa6b44af801c2aea13ba77d74aff7484d5" - integrity sha512-BhyrMj06eQkk04C97fovEDQMpLpd2IxCB4ecitaXwOKGq78Wi2tooaDOWOFGajPk8IkQOAtMppApgSVkYe1F/A== - dependencies: - "@types/accepts" "*" - "@types/content-disposition" "*" - "@types/cookies" "*" - "@types/http-assert" "*" - "@types/http-errors" "*" - "@types/keygrip" "*" - "@types/koa-compose" "*" - "@types/node" "*" - -"@types/koa@2.13.0": - version "2.13.0" - resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.0.tgz#6356c48521c0941103b6fcfb97bb01426a99d56d" - integrity sha512-hNs1Z2lX+R5sZroIy/WIGbPlH/719s/Nd5uIjSIAdHn9q+g7z6mxOnhwMjK1urE4/NUP0SOoYUOD4MnvD9FRNw== +"@types/koa@*", "@types/koa@2.13.1": + version "2.13.1" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.1.tgz#e29877a6b5ad3744ab1024f6ec75b8cbf6ec45db" + integrity sha512-Qbno7FWom9nNqu0yHZ6A0+RWt4mrYBhw3wpBAQ3+IuzGcLlfeYkzZrnMq5wsxulN2np8M4KKeUpTodsOsSad5Q== dependencies: "@types/accepts" "*" "@types/content-disposition" "*" @@ -874,12 +860,7 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*": - version "14.14.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.13.tgz#9e425079799322113ae8477297ae6ef51b8e0cdf" - integrity sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ== - -"@types/node@14.14.31": +"@types/node@*", "@types/node@14.14.31": version "14.14.31" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055" integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g== From 936fcfb1c7cee448b8e5036205aaf8843f8d8171 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Fri, 12 Mar 2021 23:10:15 +0900 Subject: [PATCH 02/76] =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9?= =?UTF-8?q?=E3=81=8C=E5=8F=96=E5=BE=97=E3=81=A7=E3=81=8D=E3=82=8B=E3=81=8B?= =?UTF-8?q?=E3=81=A9=E3=81=86=E3=81=8B=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=81=AA=E3=81=A9=20(#7316)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Tests for Fetch resource * Remove unnecessary copy * Update koa-views, pug --- gulpfile.ts | 1 - package.json | 4 +- test/fetch-resource.ts | 170 +++++++++++++++++++ test/utils.ts | 61 ++++--- yarn.lock | 373 ++++++++++++++++------------------------- 5 files changed, 355 insertions(+), 254 deletions(-) create mode 100644 test/fetch-resource.ts diff --git a/gulpfile.ts b/gulpfile.ts index 95b2394887..018134ca26 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -64,7 +64,6 @@ gulp.task('build:client:style', () => { gulp.task('build:copy', gulp.parallel('build:copy:locales', 'build:copy:views', 'build:client:script', 'build:client:style', 'build:copy:fonts', () => gulp.src([ './src/emojilist.json', - './src/server/web/views/**/*', './src/**/assets/**/*', '!./src/client/assets/**/*' ]).pipe(gulp.dest('./built/')) diff --git a/package.json b/package.json index 8fc4af571c..18a35ff585 100644 --- a/package.json +++ b/package.json @@ -174,7 +174,7 @@ "koa-mount": "4.0.0", "koa-send": "5.0.1", "koa-slow": "2.1.0", - "koa-views": "6.3.1", + "koa-views": "7.0.0", "langmap": "0.0.16", "lookup-dns-cache": "2.1.0", "markdown-it": "12.0.4", @@ -200,7 +200,7 @@ "probe-image-size": "6.0.0", "promise-limit": "2.7.0", "promise-sequential": "1.1.1", - "pug": "2.0.4", + "pug": "3.0.2", "punycode": "2.1.1", "pureimage": "0.2.5", "qrcode": "1.4.4", diff --git a/test/fetch-resource.ts b/test/fetch-resource.ts new file mode 100644 index 0000000000..72ef133ef8 --- /dev/null +++ b/test/fetch-resource.ts @@ -0,0 +1,170 @@ +/* + * Tests for Fetch resource + * + * How to run the tests: + * > TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true npx mocha test/fetch-resource.ts --require ts-node/register + * + * To specify test: + * > TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true npx mocha test/fetch-resource.ts --require ts-node/register -g 'test name' + */ + +process.env.NODE_ENV = 'test'; + +import * as assert from 'assert'; +import * as childProcess from 'child_process'; +import { async, launchServer, signup, post, request, simpleGet } from './utils'; + +// Request Accept +const ONLY_AP = 'application/activity+json'; +const PREFER_AP = 'application/activity+json, */*'; +const PREFER_HTML = 'text/html, */*'; +const UNSPECIFIED = '*/*'; + +// Response Contet-Type +const AP = 'application/activity+json; charset=utf-8'; +const HTML = 'text/html; charset=utf-8'; + +describe('Fetch resource', () => { + let p: childProcess.ChildProcess; + + let alice: any; + let alicesPost: any; + + before(launchServer(g => p = g, async () => { + alice = await signup({ username: 'alice' }); + alicesPost = await post(alice, { + text: 'test' + }); + })); + + after(() => { + p.kill(); + }); + + describe('Common', () => { + it('meta', async(async () => { + const res = await request('/meta', { + }); + + assert.strictEqual(res.status, 200); + })); + + it('GET root', async(async () => { + const res = await simpleGet('/', 'text/html'); + assert.strictEqual(res.status, 200); + })); + + it('GET docs', async(async () => { + const res = await simpleGet('/docs/ja-JP/about', 'text/html'); + assert.strictEqual(res.status, 200); + })); + + it('GET api-doc', async(async () => { + const res = await simpleGet('/api-doc', 'text/html'); + assert.strictEqual(res.status, 200); + })); + + it('GET api.json', async(async () => { + const res = await simpleGet('/api.json', 'application/json'); + assert.strictEqual(res.status, 200); + })); + }); + + describe('/@:username', () => { + it('Only AP => AP', async(async () => { + const res = await simpleGet(`/@${alice.username}`, ONLY_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer AP => AP', async(async () => { + const res = await simpleGet(`/@${alice.username}`, PREFER_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer HTML => HTML', async(async () => { + const res = await simpleGet(`/@${alice.username}`, PREFER_HTML); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); + })); + + it('Unspecified => HTML', async(async () => { + const res = await simpleGet(`/@${alice.username}`, UNSPECIFIED); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); + })); + }); + + describe('/users/:id', () => { + it('Only AP => AP', async(async () => { + const res = await simpleGet(`/users/${alice.id}`, ONLY_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer AP => AP', async(async () => { + const res = await simpleGet(`/users/${alice.id}`, PREFER_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer HTML => Redirect to /@:username', async(async () => { + const res = await simpleGet(`/users/${alice.id}`, PREFER_HTML); + assert.strictEqual(res.status, 302); + assert.strictEqual(res.location, `/@${alice.username}`); + })); + + it('Undecided => HTML', async(async () => { + const res = await simpleGet(`/users/${alice.id}`, UNSPECIFIED); + assert.strictEqual(res.status, 302); + assert.strictEqual(res.location, `/@${alice.username}`); + })); + }); + + describe('/notes/:id', () => { + it('Only AP => AP', async(async () => { + const res = await simpleGet(`/notes/${alicesPost.id}`, ONLY_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer AP => AP', async(async () => { + const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_AP); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, AP); + })); + + it('Prefer HTML => HTML', async(async () => { + const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_HTML); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); + })); + + it('Unspecified => HTML', async(async () => { + const res = await simpleGet(`/notes/${alicesPost.id}`, UNSPECIFIED); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); + })); + }); + + describe('Feeds', () => { + it('RSS', async(async () => { + const res = await simpleGet(`/@${alice.username}.rss`, UNSPECIFIED); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, 'application/rss+xml; charset=utf-8'); + })); + + it('ATOM', async(async () => { + const res = await simpleGet(`/@${alice.username}.atom`, UNSPECIFIED); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, 'application/atom+xml; charset=utf-8'); + })); + + it('JSON', async(async () => { + const res = await simpleGet(`/@${alice.username}.json`, UNSPECIFIED); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, 'application/json; charset=utf-8'); + })); + }); +}); diff --git a/test/utils.ts b/test/utils.ts index 066bd33a56..55e877a8a2 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -3,6 +3,10 @@ import * as WebSocket from 'ws'; import fetch from 'node-fetch'; const FormData = require('form-data'); import * as childProcess from 'child_process'; +import * as http from 'http'; +import loadConfig from '../src/config/load'; + +const port = loadConfig().port; export const async = (fn: Function) => (done: Function) => { fn().then(() => { @@ -17,26 +21,20 @@ export const request = async (endpoint: string, params: any, me?: any): Promise< i: me.token } : {}; - try { - const res = await fetch('http://localhost:8080/api' + endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(Object.assign(auth, params)) - }); + const res = await fetch(`http://localhost:${port}/api${endpoint}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(Object.assign(auth, params)) + }); - const status = res.status; - const body = res.status !== 204 ? await res.json().catch() : null; + const status = res.status; + const body = res.status !== 204 ? await res.json().catch() : null; - return { - body, status - }; - } catch (e) { - return { - body: null, status: 500 - }; - } + return { + body, status + }; }; export const signup = async (params?: any): Promise<any> => { @@ -72,7 +70,7 @@ export const uploadFile = (user: any, path?: string): Promise<any> => { formData.append('i', user.token); formData.append('file', fs.createReadStream(path || __dirname + '/resources/Lenna.png')); - return fetch('http://localhost:8080/api/drive/files/create', { + return fetch(`http://localhost:${port}/api/drive/files/create`, { method: 'post', body: formData, timeout: 30 * 1000, @@ -87,7 +85,7 @@ export const uploadFile = (user: any, path?: string): Promise<any> => { export function connectStream(user: any, channel: string, listener: (message: Record<string, any>) => any, params?: any): Promise<WebSocket> { return new Promise((res, rej) => { - const ws = new WebSocket(`ws://localhost:8080/streaming?i=${user.token}`); + const ws = new WebSocket(`ws://localhost:${port}/streaming?i=${user.token}`); ws.on('open', () => { ws.on('message', data => { @@ -112,6 +110,29 @@ export function connectStream(user: any, channel: string, listener: (message: Re }); } +export const simpleGet = async (path: string, accept: string): Promise<{ status?: number, type?: string, location?: string }> => { + // node-fetchだと3xxを取れない + return await new Promise((resolve, reject) => { + const req = http.request(`http://localhost:${port}${path}`, { + headers: { + Accept: accept + } + }, res => { + if (res.statusCode! >= 400) { + reject(res); + } else { + resolve({ + status: res.statusCode, + type: res.headers['content-type'], + location: res.headers.location, + }); + } + }); + + req.end(); + }); +}; + export function launchServer(callbackSpawnedProcess: (p: childProcess.ChildProcess) => void, moreProcess: () => Promise<void> = async () => {}) { return (done: (err?: Error) => any) => { const p = childProcess.spawn('node', [__dirname + '/../index.js'], { diff --git a/yarn.lock b/yarn.lock index d5fece07ca..2e72ff3be0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -123,6 +123,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.4.tgz#340211b0da94a351a6f10e63671fa727333d13ab" integrity sha512-uvoOulWHhI+0+1f9L4BoozY7U5cIkZ9PgJqvb041d6vypgUmtVPG4vmGm4pSggjl8BELzvHyUeJSUyEMY6b+qA== +"@babel/parser@^7.9.6": + version "7.13.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.9.tgz#ca34cb95e1c2dd126863a84465ae8ef66114be99" + integrity sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw== + "@babel/plugin-transform-runtime@7.13.9": version "7.13.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.9.tgz#744d3103338a0d6c90dee0497558150b490cee07" @@ -184,7 +189,7 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@babel/types@^7.13.0": +"@babel/types@^7.13.0", "@babel/types@^7.9.6": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== @@ -1453,13 +1458,6 @@ accepts@^1.3.5: mime-types "~2.1.24" negotiator "0.6.2" -acorn-globals@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" - integrity sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8= - dependencies: - acorn "^4.0.4" - acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -1483,16 +1481,6 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== -acorn@^3.1.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= - -acorn@^4.0.4, acorn@~4.0.2: - version "4.0.13" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" - integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c= - acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" @@ -1525,15 +1513,6 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc= - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - alphanum-sort@^1.0.0, alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -1788,6 +1767,11 @@ asn1@~0.2.3: dependencies: safer-buffer "~2.1.0" +assert-never@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/assert-never/-/assert-never-1.2.1.tgz#11f0e363bf146205fb08193b5c7b90f4d1cf44fe" + integrity sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw== + assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" @@ -1932,6 +1916,13 @@ babel-plugin-polyfill-regenerator@^0.1.2: dependencies: "@babel/helper-define-polyfill-provider" "^0.1.2" +babel-walk@3.0.0-canary-5: + version "3.0.0-canary-5" + resolved "https://registry.yarnpkg.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz#f66ecd7298357aee44955f235a6ef54219104b11" + integrity sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw== + dependencies: + "@babel/types" "^7.9.6" + bach@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" @@ -2021,7 +2012,7 @@ bl@^4.0.1, bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" -bluebird@^3.1.1, bluebird@^3.7.2: +bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -2288,11 +2279,6 @@ camel-case@^3.0.0: no-case "^2.2.0" upper-case "^1.1.1" -camelcase@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= - camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" @@ -2361,14 +2347,6 @@ cbor@7.0.3: "@cto.af/textdecoder" "^0.0.0" nofilter "^2.0.3" -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60= - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - chai@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.0.tgz#5523a5faf7f819c8a92480d70a8cccbadacfc25f" @@ -2422,7 +2400,7 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -character-parser@^2.1.1: +character-parser@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-2.2.0.tgz#c7ce28f36d4bcd9744e5ffc2c5fcde1c73261fc0" integrity sha1-x84o821LzZdE5f/CxfzeHHMmH8A= @@ -2557,7 +2535,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -clean-css@^4.1.11, clean-css@^4.2.1: +clean-css@^4.2.1: version "4.2.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== @@ -2585,15 +2563,6 @@ clipboard@^2.0.0: select "^1.1.2" tiny-emitter "^2.0.0" -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE= - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - cliui@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" @@ -2904,13 +2873,6 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -consolidate@0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" - integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw== - dependencies: - bluebird "^3.1.1" - consolidate@^0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.16.0.tgz#a11864768930f2f19431660a65906668f5fbdc16" @@ -2918,7 +2880,7 @@ consolidate@^0.16.0: dependencies: bluebird "^3.7.2" -constantinople@^3.0.1, constantinople@^3.1.2, constantinople@^4.0.1: +constantinople@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-4.0.1.tgz#0def113fa0e4dc8de83331a5cf79c8b325213151" integrity sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw== @@ -3358,7 +3320,7 @@ debuglog@^1.0.0: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: +decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -5408,13 +5370,13 @@ is-directory@^0.3.1: resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= -is-expression@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-3.0.0.tgz#39acaa6be7fd1f3471dc42c7416e61c24317ac9f" - integrity sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8= +is-expression@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-4.0.0.tgz#c33155962abf21d0afd2552514d67d2ec16fd2ab" + integrity sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A== dependencies: - acorn "~4.0.2" - object-assign "^4.0.1" + acorn "^7.1.1" + object-assign "^4.1.1" is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" @@ -5717,7 +5679,7 @@ js-sha3@0.8.0: resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== -js-stringify@^1.0.1: +js-stringify@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" integrity sha1-Fzb939lyTyijaCrcYjCufk6Weds= @@ -6066,17 +6028,18 @@ koa-slow@2.1.0: lodash.isregexp "3.0.5" q "1.4.1" -koa-views@6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/koa-views/-/koa-views-6.3.1.tgz#8d23fa2118c71e9119fb47a75a58053345e37356" - integrity sha512-weIaPs2cCHWT2qK8qHRmwlZ29xRCvUVy1v/z12AGavVV5j4QIU0W/Y7OVBBu1sTkcO9dDJ25ajGYHGZ/aY43IQ== +koa-views@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/koa-views/-/koa-views-7.0.0.tgz#039a90620991f6668566010b3c1df31481f78c98" + integrity sha512-I9H2Dood5MhwzmWk7hkWuxEBObd3uv/4eEPJP2r43Ro+rK8ICKjrIKEzKrfT89JNX69vWOEU8JreJwRjb5HOFw== dependencies: - consolidate "0.15.1" + consolidate "^0.16.0" debug "^4.1.0" get-paths "0.0.7" koa-send "^5.0.0" mz "^2.4.0" pretty "^2.0.0" + resolve-path "^1.4.0" koa@2.13.1: version "2.13.1" @@ -6150,11 +6113,6 @@ last-run@^1.1.0: default-resolution "^2.0.0" es6-weak-map "^2.0.1" -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= - lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" @@ -6393,11 +6351,6 @@ log-symbols@4.0.0: dependencies: chalk "^4.0.0" -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= - lookup-dns-cache@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/lookup-dns-cache/-/lookup-dns-cache-2.1.0.tgz#6362340e269071e20b6f0bcf51da98873411e051" @@ -8481,110 +8434,108 @@ psl@^1.1.24, psl@^1.1.28: resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== -pug-attrs@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-2.0.4.tgz#b2f44c439e4eb4ad5d4ef25cac20d18ad28cc336" - integrity sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ== +pug-attrs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-3.0.0.tgz#b10451e0348165e31fad1cc23ebddd9dc7347c41" + integrity sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA== dependencies: - constantinople "^3.0.1" - js-stringify "^1.0.1" - pug-runtime "^2.0.5" + constantinople "^4.0.1" + js-stringify "^1.0.2" + pug-runtime "^3.0.0" -pug-code-gen@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-2.0.3.tgz#122eb9ada9b5bf601705fe15aaa0a7d26bc134ab" - integrity sha512-r9sezXdDuZJfW9J91TN/2LFbiqDhmltTFmGpHTsGdrNGp3p4SxAjjXEfnuK2e4ywYsRIVP0NeLbSAMHUcaX1EA== +pug-code-gen@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-3.0.2.tgz#ad190f4943133bf186b60b80de483100e132e2ce" + integrity sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg== dependencies: - constantinople "^3.1.2" + constantinople "^4.0.1" doctypes "^1.1.0" - js-stringify "^1.0.1" - pug-attrs "^2.0.4" - pug-error "^1.3.3" - pug-runtime "^2.0.5" - void-elements "^2.0.1" - with "^5.0.0" + js-stringify "^1.0.2" + pug-attrs "^3.0.0" + pug-error "^2.0.0" + pug-runtime "^3.0.0" + void-elements "^3.1.0" + with "^7.0.0" -pug-error@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-1.3.3.tgz#f342fb008752d58034c185de03602dd9ffe15fa6" - integrity sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ== +pug-error@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-2.0.0.tgz#5c62173cb09c34de2a2ce04f17b8adfec74d8ca5" + integrity sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ== -pug-filters@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-3.1.1.tgz#ab2cc82db9eeccf578bda89130e252a0db026aa7" - integrity sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg== +pug-filters@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-4.0.0.tgz#d3e49af5ba8472e9b7a66d980e707ce9d2cc9b5e" + integrity sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A== dependencies: - clean-css "^4.1.11" - constantinople "^3.0.1" + constantinople "^4.0.1" jstransformer "1.0.0" - pug-error "^1.3.3" - pug-walk "^1.1.8" - resolve "^1.1.6" - uglify-js "^2.6.1" + pug-error "^2.0.0" + pug-walk "^2.0.0" + resolve "^1.15.1" -pug-lexer@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-4.1.0.tgz#531cde48c7c0b1fcbbc2b85485c8665e31489cfd" - integrity sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA== - dependencies: - character-parser "^2.1.1" - is-expression "^3.0.0" - pug-error "^1.3.3" - -pug-linker@^3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-3.0.6.tgz#f5bf218b0efd65ce6670f7afc51658d0f82989fb" - integrity sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg== - dependencies: - pug-error "^1.3.3" - pug-walk "^1.1.8" - -pug-load@^2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-2.0.12.tgz#d38c85eb85f6e2f704dea14dcca94144d35d3e7b" - integrity sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg== - dependencies: - object-assign "^4.1.0" - pug-walk "^1.1.8" - -pug-parser@^5.0.1: +pug-lexer@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-5.0.1.tgz#03e7ada48b6840bd3822f867d7d90f842d0ffdc9" - integrity sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA== + resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-5.0.1.tgz#ae44628c5bef9b190b665683b288ca9024b8b0d5" + integrity sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w== dependencies: - pug-error "^1.3.3" - token-stream "0.0.1" + character-parser "^2.2.0" + is-expression "^4.0.0" + pug-error "^2.0.0" -pug-runtime@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-2.0.5.tgz#6da7976c36bf22f68e733c359240d8ae7a32953a" - integrity sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw== - -pug-strip-comments@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz#cc1b6de1f6e8f5931cf02ec66cdffd3f50eaf8a8" - integrity sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw== +pug-linker@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-4.0.0.tgz#12cbc0594fc5a3e06b9fc59e6f93c146962a7708" + integrity sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw== dependencies: - pug-error "^1.3.3" + pug-error "^2.0.0" + pug-walk "^2.0.0" -pug-walk@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-1.1.8.tgz#b408f67f27912f8c21da2f45b7230c4bd2a5ea7a" - integrity sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA== - -pug@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pug/-/pug-2.0.4.tgz#ee7682ec0a60494b38d48a88f05f3b0ac931377d" - integrity sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw== +pug-load@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-3.0.0.tgz#9fd9cda52202b08adb11d25681fb9f34bd41b662" + integrity sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ== dependencies: - pug-code-gen "^2.0.2" - pug-filters "^3.1.1" - pug-lexer "^4.1.0" - pug-linker "^3.0.6" - pug-load "^2.0.12" - pug-parser "^5.0.1" - pug-runtime "^2.0.5" - pug-strip-comments "^1.0.4" + object-assign "^4.1.1" + pug-walk "^2.0.0" + +pug-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-6.0.0.tgz#a8fdc035863a95b2c1dc5ebf4ecf80b4e76a1260" + integrity sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw== + dependencies: + pug-error "^2.0.0" + token-stream "1.0.0" + +pug-runtime@^3.0.0, pug-runtime@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-3.0.1.tgz#f636976204723f35a8c5f6fad6acda2a191b83d7" + integrity sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg== + +pug-strip-comments@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz#f94b07fd6b495523330f490a7f554b4ff876303e" + integrity sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ== + dependencies: + pug-error "^2.0.0" + +pug-walk@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-2.0.0.tgz#417aabc29232bb4499b5b5069a2b2d2a24d5f5fe" + integrity sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ== + +pug@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pug/-/pug-3.0.2.tgz#f35c7107343454e43bc27ae0ff76c731b78ea535" + integrity sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw== + dependencies: + pug-code-gen "^3.0.2" + pug-filters "^4.0.0" + pug-lexer "^5.0.1" + pug-linker "^4.0.0" + pug-load "^3.0.0" + pug-parser "^6.0.0" + pug-runtime "^3.0.1" + pug-strip-comments "^2.0.0" pump@^2.0.0: version "2.0.1" @@ -8945,7 +8896,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== -repeat-string@^1.5.2, repeat-string@^1.6.1: +repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -9153,7 +9104,7 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.4.0, is-core-module "^2.0.0" path-parse "^1.0.6" -resolve@^1.14.2: +resolve@^1.14.2, resolve@^1.15.1: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -9188,13 +9139,6 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8= - dependencies: - align-text "^0.1.1" - rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -9578,7 +9522,7 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: +source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -10351,10 +10295,10 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -token-stream@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-0.0.1.tgz#ceeefc717a76c4316f126d0b9dbaa55d7e7df01a" - integrity sha1-zu78cXp2xDFvEm0LnbqlXX598Bo= +token-stream@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-1.0.0.tgz#cc200eab2613f4166d27ff9afc7ca56d49df6eb4" + integrity sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ= token-types@^2.0.0: version "2.0.0" @@ -10587,16 +10531,6 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== -uglify-js@^2.6.1: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" - integrity sha1-KcVzMUgFe7Th913zW3qcty5qWd0= - dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" - uglify-js@^3.5.1: version "3.9.1" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.9.1.tgz#a56a71c8caa2d36b5556cc1fd57df01ae3491539" @@ -10604,11 +10538,6 @@ uglify-js@^3.5.1: dependencies: commander "~2.20.3" -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc= - ulid@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/ulid/-/ulid-2.3.0.tgz#93063522771a9774121a84d126ecd3eb9804071f" @@ -10893,10 +10822,10 @@ vinyl@^2.0.0, vinyl@^2.2.0: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -void-elements@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" - integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= +void-elements@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk= vue-color@2.8.1: version "2.8.1" @@ -11164,29 +11093,21 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0= - -with@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/with/-/with-5.1.1.tgz#fa4daa92daf32c4ea94ed453c81f04686b575dfe" - integrity sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4= +with@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/with/-/with-7.0.2.tgz#ccee3ad542d25538a7a7a80aad212b9828495bac" + integrity sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w== dependencies: - acorn "^3.1.0" - acorn-globals "^3.0.0" + "@babel/parser" "^7.9.6" + "@babel/types" "^7.9.6" + assert-never "^1.2.1" + babel-walk "3.0.0-canary-5" word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= - workerpool@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" @@ -11435,16 +11356,6 @@ yargs@^7.1.0: y18n "^3.2.1" yargs-parser "^5.0.0" -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - integrity sha1-9+572FfdfB0tOMDnTvvWgdFDH9E= - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" - ylru@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f" From b8d9dd15ace536536f3122cc1d43bfd50ea98352 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 12 Mar 2021 23:31:01 +0900 Subject: [PATCH 03/76] Update dependencies :rocket: --- package.json | 57 ++++--- yarn.lock | 413 +++++++++++++++++++++++++++------------------------ 2 files changed, 249 insertions(+), 221 deletions(-) diff --git a/package.json b/package.json index 18a35ff585..a583e21aee 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,8 @@ "@types/gulp-replace": "0.0.31", "@types/is-url": "1.2.28", "@types/js-yaml": "4.0.0", - "@types/jsdom": "16.2.6", - "@types/jsonld": "1.5.4", + "@types/jsdom": "16.2.7", + "@types/jsonld": "1.5.5", "@types/katex": "0.11.0", "@types/koa": "2.13.1", "@types/koa-bodyparser": "4.3.0", @@ -78,7 +78,7 @@ "@types/markdown-it": "12.0.1", "@types/matter-js": "0.14.10", "@types/mocha": "8.2.1", - "@types/node": "14.14.31", + "@types/node": "14.14.34", "@types/node-fetch": "2.5.8", "@types/nodemailer": "6.4.0", "@types/nprogress": "0.2.0", @@ -105,19 +105,19 @@ "@types/web-push": "3.3.0", "@types/webpack": "4.41.26", "@types/webpack-stream": "3.2.11", - "@types/websocket": "1.0.1", + "@types/websocket": "1.0.2", "@types/ws": "7.4.0", - "@typescript-eslint/parser": "4.16.1", + "@typescript-eslint/parser": "4.17.0", "@vue/compiler-sfc": "3.0.5", "abort-controller": "3.0.0", "apexcharts": "3.25.0", "autobind-decorator": "2.4.0", "autosize": "4.0.2", "autwh": "0.1.0", - "aws-sdk": "2.848.0", + "aws-sdk": "2.862.0", "bcryptjs": "2.4.3", "blurhash": "1.1.3", - "broadcast-channel": "3.4.1", + "broadcast-channel": "3.5.3", "bull": "3.20.1", "cafy": "15.2.1", "cbor": "7.0.3", @@ -126,23 +126,23 @@ "cli-highlight": "2.1.10", "commander": "4.1.1", "content-disposition": "0.5.3", - "core-js": "3.9.0", + "core-js": "3.9.1", "crc-32": "1.2.0", - "css-loader": "5.0.2", + "css-loader": "5.1.2", "cssnano": "4.1.10", "dateformat": "4.5.1", "diskusage": "1.1.3", "double-ended-queue": "2.1.0-0", "escape-regexp": "0.0.1", "eslint": "7.21.0", - "eslint-plugin-vue": "7.6.0", + "eslint-plugin-vue": "7.7.0", "eventemitter3": "4.0.7", "feed": "4.2.2", "fibers": "5.0.0", - "file-type": "16.2.0", + "file-type": "16.3.0", "fluent-ffmpeg": "2.1.2", "glob": "7.1.6", - "got": "11.8.1", + "got": "11.8.2", "gulp": "4.0.2", "gulp-cssnano": "2.1.3", "gulp-rename": "2.0.0", @@ -155,17 +155,17 @@ "http-proxy-agent": "4.0.1", "http-signature": "1.3.5", "https-proxy-agent": "5.0.0", - "idb-keyval": "5.0.2", + "idb-keyval": "5.0.4", "insert-text-at-cursor": "0.3.0", "is-root": "2.1.0", - "is-svg": "4.2.1", + "is-svg": "4.2.2", "js-yaml": "4.0.0", - "jsdom": "16.4.0", + "jsdom": "16.5.0", "json5": "2.2.0", "json5-loader": "4.0.1", "jsonld": "4.0.1", "jsrsasign": "8.0.20", - "katex": "0.12.0", + "katex": "0.13.0", "koa": "2.13.1", "koa-bodyparser": "4.3.0", "koa-favicon": "2.1.0", @@ -178,9 +178,9 @@ "langmap": "0.0.16", "lookup-dns-cache": "2.1.0", "markdown-it": "12.0.4", - "markdown-it-anchor": "7.0.2", + "markdown-it-anchor": "7.1.0", "matter-js": "0.16.1", - "mocha": "8.3.0", + "mocha": "8.3.2", "moji": "0.5.1", "ms": "2.1.3", "multer": "1.4.2", @@ -189,20 +189,19 @@ "nodemailer": "6.5.0", "object-assign-deep": "0.4.0", "os-utils": "0.0.14", - "p-cancelable": "2.0.0", "parse5": "6.0.1", "parsimmon": "1.16.0", "pg": "8.5.1", "portscanner": "2.2.0", - "postcss": "8.2.7", - "postcss-loader": "5.0.0", + "postcss": "8.2.8", + "postcss-loader": "5.2.0", "prismjs": "1.23.0", - "probe-image-size": "6.0.0", + "probe-image-size": "7.0.0", "promise-limit": "2.7.0", "promise-sequential": "1.1.1", "pug": "3.0.2", "punycode": "2.1.1", - "pureimage": "0.2.5", + "pureimage": "0.2.7", "qrcode": "1.4.4", "random-seed": "0.3.0", "ratelimiter": "3.4.1", @@ -221,25 +220,25 @@ "sass": "1.32.8", "sass-loader": "11.0.1", "seedrandom": "3.0.5", - "sharp": "0.27.1", + "sharp": "0.27.2", "speakeasy": "2.0.0", "stringz": "2.1.0", "style-loader": "2.0.0", "summaly": "2.4.0", "syslog-pro": "1.0.0", - "systeminformation": "5.6.1", + "systeminformation": "5.6.2", "syuilo-password-strength": "0.0.1", "textarea-caret": "3.1.0", "three": "0.117.1", "throttle-debounce": "3.0.1", "tinycolor2": "1.4.2", "tmp": "0.2.1", - "ts-loader": "8.0.17", + "ts-loader": "8.0.18", "ts-node": "9.1.1", "tslint": "6.1.3", "tslint-sonarts": "1.9.0", "typeorm": "0.2.31", - "typescript": "4.1.5", + "typescript": "4.2.3", "ulid": "2.3.0", "url-loader": "4.1.1", "uuid": "8.3.2", @@ -254,10 +253,10 @@ "vue-style-loader": "4.1.3", "vuedraggable": "4.0.1", "web-push": "3.4.4", - "webpack": "5.24.2", + "webpack": "5.25.0", "webpack-cli": "4.5.0", "websocket": "1.0.33", - "ws": "7.4.3", + "ws": "7.4.4", "xev": "2.0.1" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 2e72ff3be0..2daa9acc76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -686,10 +686,10 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.0.tgz#d1a11688112091f2c711674df3a65ea2f47b5dfb" integrity sha512-4vlpCM5KPCL5CfGmTbpjwVKbISRYhduEJvvUWsH5EB7QInhEj94XPZ3ts/9FPiLZFqYO0xoW4ZL8z2AabTGgJA== -"@types/jsdom@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.6.tgz#9ddf0521e49be5365797e690c3ba63148e562c29" - integrity sha512-yQA+HxknGtW9AkRTNyiSH3OKW5V+WzO8OPTdne99XwJkYC+KYxfNIcoJjeiSqP3V00PUUpFP6Myoo9wdIu78DQ== +"@types/jsdom@16.2.7": + version "16.2.7" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.7.tgz#27d2f77d655a3db15f7c3f104f1a6d15e3112938" + integrity sha512-jJ0QDvwZxAO+SninBaQdW6najEs1dCZ1uMsXFBTitwfAtz+0wfDZWd3GFEqkL4flD3IefB+VGBcrN9HbRdAdog== dependencies: "@types/node" "*" "@types/parse5" "*" @@ -705,10 +705,10 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== -"@types/jsonld@1.5.4": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@types/jsonld/-/jsonld-1.5.4.tgz#37e7869fae65bd9b35aa3467cbfc60c52f881a36" - integrity sha512-e51UoELQzKJWGsVNqoKI5nnXazupqoOYTBMAGe3Iram0sWxeVTzgk38BKYgmOZmuIXaMFfuAz1ZimK2oyQNnRA== +"@types/jsonld@1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@types/jsonld/-/jsonld-1.5.5.tgz#c2238462a83e90f003929cef6a36eded2ceed069" + integrity sha512-/4PvZJJh3Lqz9bkwvSwfyLWnR1xFQ7h4KLq8i0yaGjBBq09qLMJlCqvfXfE5qFEnClmWM+GK2Edjbb6QDS2SXQ== "@types/katex@0.11.0": version "0.11.0" @@ -865,11 +865,16 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@14.14.31": +"@types/node@*": version "14.14.31" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055" integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g== +"@types/node@14.14.34": + version "14.14.34" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.34.tgz#07935194fc049069a1c56c0c274265abeddf88da" + integrity sha512-dBPaxocOK6UVyvhbnpFIj2W+S+1cBTkHQbFQfeeJhoKFbzYcVUGHvddeWPSucKATb3F0+pgDq0i6ghEaZjsugA== + "@types/nodemailer@6.4.0": version "6.4.0" resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.0.tgz#d8c039be3ed685c4719a026455555be82c124b74" @@ -1144,10 +1149,10 @@ "@types/webpack-sources" "*" source-map "^0.6.0" -"@types/websocket@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.1.tgz#039272c196c2c0e4868a0d8a1a27bbb86e9e9138" - integrity sha512-f5WLMpezwVxCLm1xQe/kdPpQIOmL0TXYx2O15VYfYzc7hTIdxiOoOvez+McSIw3b7z/1zGovew9YSL7+h4h7/Q== +"@types/websocket@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.2.tgz#d2855c6a312b7da73ed16ba6781815bf30c6187a" + integrity sha512-B5m9aq7cbbD/5/jThEr33nUY8WEfVi6A2YKCTOvw5Ldy7mtsOkqRvGjnzy6g7iMMDsgu7xREuCzqATLDLQVKcQ== dependencies: "@types/node" "*" @@ -1158,48 +1163,48 @@ dependencies: "@types/node" "*" -"@typescript-eslint/parser@4.16.1": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.16.1.tgz#3bbd3234dd3c5b882b2bcd9899bc30e1e1586d2a" - integrity sha512-/c0LEZcDL5y8RyI1zLcmZMvJrsR6SM1uetskFkoh3dvqDKVXPsXI+wFB/CbVw7WkEyyTKobC1mUNp/5y6gRvXg== +"@typescript-eslint/parser@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.17.0.tgz#141b647ffc72ebebcbf9b0fe6087f65b706d3215" + integrity sha512-KYdksiZQ0N1t+6qpnl6JeK9ycCFprS9xBAiIrw4gSphqONt8wydBw4BXJi3C11ywZmyHulvMaLjWsxDjUSDwAw== dependencies: - "@typescript-eslint/scope-manager" "4.16.1" - "@typescript-eslint/types" "4.16.1" - "@typescript-eslint/typescript-estree" "4.16.1" + "@typescript-eslint/scope-manager" "4.17.0" + "@typescript-eslint/types" "4.17.0" + "@typescript-eslint/typescript-estree" "4.17.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.16.1": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.16.1.tgz#244e2006bc60cfe46987e9987f4ff49c9e3f00d5" - integrity sha512-6IlZv9JaurqV0jkEg923cV49aAn8V6+1H1DRfhRcvZUrptQ+UtSKHb5kwTayzOYTJJ/RsYZdcvhOEKiBLyc0Cw== +"@typescript-eslint/scope-manager@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.17.0.tgz#f4edf94eff3b52a863180f7f89581bf963e3d37d" + integrity sha512-OJ+CeTliuW+UZ9qgULrnGpPQ1bhrZNFpfT/Bc0pzNeyZwMik7/ykJ0JHnQ7krHanFN9wcnPK89pwn84cRUmYjw== dependencies: - "@typescript-eslint/types" "4.16.1" - "@typescript-eslint/visitor-keys" "4.16.1" + "@typescript-eslint/types" "4.17.0" + "@typescript-eslint/visitor-keys" "4.17.0" -"@typescript-eslint/types@4.16.1": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.16.1.tgz#5ba2d3e38b1a67420d2487519e193163054d9c15" - integrity sha512-nnKqBwMgRlhzmJQF8tnFDZWfunXmJyuXj55xc8Kbfup4PbkzdoDXZvzN8//EiKR27J6vUSU8j4t37yUuYPiLqA== +"@typescript-eslint/types@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.17.0.tgz#f57d8fc7f31b348db946498a43050083d25f40ad" + integrity sha512-RN5z8qYpJ+kXwnLlyzZkiJwfW2AY458Bf8WqllkondQIcN2ZxQowAToGSd9BlAUZDB5Ea8I6mqL2quGYCLT+2g== -"@typescript-eslint/typescript-estree@4.16.1": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.16.1.tgz#c2fc46b05a48fbf8bbe8b66a63f0a9ba04b356f1" - integrity sha512-m8I/DKHa8YbeHt31T+UGd/l8Kwr0XCTCZL3H4HMvvLCT7HU9V7yYdinTOv1gf/zfqNeDcCgaFH2BMsS8x6NvJg== +"@typescript-eslint/typescript-estree@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.17.0.tgz#b835d152804f0972b80dbda92477f9070a72ded1" + integrity sha512-lRhSFIZKUEPPWpWfwuZBH9trYIEJSI0vYsrxbvVvNyIUDoKWaklOAelsSkeh3E2VBSZiNe9BZ4E5tYBZbUczVQ== dependencies: - "@typescript-eslint/types" "4.16.1" - "@typescript-eslint/visitor-keys" "4.16.1" + "@typescript-eslint/types" "4.17.0" + "@typescript-eslint/visitor-keys" "4.17.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.16.1": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.16.1.tgz#d7571fb580749fae621520deeb134370bbfc7293" - integrity sha512-s/aIP1XcMkEqCNcPQtl60ogUYjSM8FU2mq1O7y5cFf3Xcob1z1iXWNB6cC43Op+NGRTFgGolri6s8z/efA9i1w== +"@typescript-eslint/visitor-keys@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.17.0.tgz#9c304cfd20287c14a31d573195a709111849b14d" + integrity sha512-WfuMN8mm5SSqXuAr9NM+fItJ0SVVphobWYkWOwQ1odsfC014Vdxk/92t4JwS1Q6fCA/ABfCKpa3AVtpUKTNKGQ== dependencies: - "@typescript-eslint/types" "4.16.1" + "@typescript-eslint/types" "4.17.0" eslint-visitor-keys "^2.0.0" "@ungap/promise-all-settled@1.1.2": @@ -1438,6 +1443,11 @@ abab@^2.0.3: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== +abab@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -1491,6 +1501,11 @@ acorn@^8.0.4: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.0.4.tgz#7a3ae4191466a6984eee0fe3407a4f3aa9db8354" integrity sha512-XNP0PqF1XD19ZlLKvB7cMmnZswW4C/03pRHgirB30uSJTaS3A3V1/P4sS3HPvFmjoriPCJQs+JDSbm4bL1TxGQ== +acorn@^8.0.5: + version "8.1.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe" + integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA== + agent-base@6: version "6.0.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.0.tgz#5d0101f19bbfaed39980b22ae866de153b93f09a" @@ -1867,10 +1882,10 @@ autwh@0.1.0: dependencies: oauth "0.9.15" -aws-sdk@2.848.0: - version "2.848.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.848.0.tgz#5e7706ddd30a55a2d5a5b64c29682a757607ee64" - integrity sha512-c/e5kaEFl+9aYkrYDkmu5mSZlL+EfP6DnBOMD06fH12gIsaFSMBGtbsDTHABhvSu++LxeI1dJAD148O17MuZvg== +aws-sdk@2.862.0: + version "2.862.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.862.0.tgz#3f9afc830de24723dfb7801f4138c5db101f4205" + integrity sha512-kAvEQZ2tKJRZKRHZQJdvrUZz76cAEnyN1UoLw39kCqGIpxw9rcPK+bIy8fDBmcvWVZvymO7mp4Ut2jM/ZiDCMQ== dependencies: buffer "4.9.2" events "1.1.1" @@ -2063,10 +2078,10 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -broadcast-channel@3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.4.1.tgz#65b63068d0a5216026a19905c9b2d5e9adf0928a" - integrity sha512-VXYivSkuBeQY+pL5hNQQNvBdKKQINBAROm4G8lAbWQfOZ7Yn4TMcgLNlJyEqlkxy5G8JJBsI3VJ1u8FUTOROcg== +broadcast-channel@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.5.3.tgz#c75c39d923ae8af6284a893bfdc8bd3996d2dd2d" + integrity sha512-OLOXfwReZa2AAAh9yOUyiALB3YxBe0QpThwwuyRHLgpl8bSznSDmV6Mz7LeBJg1VZsMcDcNMy7B53w12qHrIhQ== dependencies: "@babel/runtime" "^7.7.2" detect-node "^2.0.4" @@ -2821,6 +2836,11 @@ commander@^2.12.1, commander@^2.19.0, commander@^2.20.0, commander@~2.20.3: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + commander@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.0.0.tgz#3e2bbfd8bb6724760980988fb5b22b7ee6b71ab2" @@ -2949,10 +2969,10 @@ core-js-compat@^3.8.1: browserslist "^4.16.3" semver "7.0.0" -core-js@3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.0.tgz#790b1bb11553a2272b36e2625c7179db345492f8" - integrity sha512-PyFBJaLq93FlyYdsndE5VaueA9K5cNB7CGzeCj191YYLhkQM0gdZR2SKihM70oF0wdqKSKClv/tEBOpoRmdOVQ== +core-js@3.9.1: + version "3.9.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.1.tgz#cec8de593db8eb2a85ffb0dbdeb312cb6e5460ae" + integrity sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -3041,16 +3061,16 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" -css-loader@5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.0.2.tgz#24f758dae349bad0a440c50d7e2067742e0899cb" - integrity sha512-gbkBigdcHbmNvZ1Cg6aV6qh6k9N6XOr8YWzISLQGrwk2mgOH8LLrizhkxbDhQtaLtktyKHD4970S0xwz5btfTA== +css-loader@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.1.2.tgz#b93dba498ec948b543b49d4fab5017205d4f5c3e" + integrity sha512-T7vTXHSx0KrVEg/xjcl7G01RcVXpcw4OELwDPvkr7izQNny85A84dK3dqrczuEfBcu7Yg7mdTjJLSTibRUoRZg== dependencies: camelcase "^6.2.0" cssesc "^3.0.0" icss-utils "^5.1.0" loader-utils "^2.0.0" - postcss "^8.2.4" + postcss "^8.2.8" postcss-modules-extract-imports "^3.0.0" postcss-modules-local-by-default "^4.0.0" postcss-modules-scope "^3.0.0" @@ -3246,10 +3266,10 @@ cssom@~0.3.6: resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssstyle@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.2.0.tgz#e4c44debccd6b7911ed617a4395e5754bba59992" - integrity sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA== +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: cssom "~0.3.6" @@ -3330,10 +3350,10 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -decimal.js@^10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231" - integrity sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw== +decimal.js@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" + integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== decode-uri-component@^0.2.0: version "0.2.0" @@ -3865,27 +3885,27 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.3, escape-string-regexp@^ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@^1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" - integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== dependencies: esprima "^4.0.1" - estraverse "^4.2.0" + estraverse "^5.2.0" esutils "^2.0.2" optionator "^0.8.1" optionalDependencies: source-map "~0.6.1" -eslint-plugin-vue@7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.6.0.tgz#ea616e6dfd45d545adb16cba628c5a992cc31f0b" - integrity sha512-qYpKwAvpcQXyUXVcG8Zd+fxHDx9iSgTQuO7dql7Ug/2BCvNNDr6s3I9p8MoUo23JJdO7ZAjW3vSwY/EBf4uBcw== +eslint-plugin-vue@7.7.0: + version "7.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.7.0.tgz#a90df4595e670821bf243bd2750ededdb74948b8" + integrity sha512-mYz4bpLGv5jx6YG/GvKkqbGSfV7uma2u1P3mLA41Q5vQl8W1MeuTneB8tfsLq6xxxesFubcrOC0BZBJ5R+eaCQ== dependencies: eslint-utils "^2.1.0" natural-compare "^1.4.0" semver "^7.3.2" - vue-eslint-parser "^7.5.0" + vue-eslint-parser "^7.6.0" eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" @@ -3997,7 +4017,7 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -4226,10 +4246,10 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -file-type@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.2.0.tgz#d4f1da71ddda758db7f15f93adfaed09ce9e2715" - integrity sha512-1Wwww3mmZCMmLjBfslCluwt2mxH80GsAXYrvPnfQ42G1EGWag336kB1iyCgyn7UXiKY3cJrNykXPrCwA7xb5Ag== +file-type@16.3.0: + version "16.3.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.3.0.tgz#f03af91db30f92cc9a0b335c0644c46101522f6d" + integrity sha512-ZA0hV64611vJT42ltw0T9IDwHApQuxRdrmQZWTeDmeAUtZBBVSQW3nSQqhhW1cAgpXgqcJvm410BYHXJQ9AymA== dependencies: readable-web-to-node-stream "^3.0.0" strtok3 "^6.0.3" @@ -4662,10 +4682,10 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" -got@11.8.1: - version "11.8.1" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.1.tgz#df04adfaf2e782babb3daabc79139feec2f7e85d" - integrity sha512-9aYdZL+6nHmvJwHALLwKSUZ0hMwGaJGYv3hoPLPgnT8BoBXm1SjnZeky+91tfwJaDzun2s4RsBRy48IEYv2q2Q== +got@11.8.2: + version "11.8.2" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" + integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== dependencies: "@sindresorhus/is" "^4.0.0" "@szmarczak/http-timer" "^4.0.5" @@ -5110,10 +5130,10 @@ icss-utils@^5.0.0, icss-utils@^5.1.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -idb-keyval@5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-5.0.2.tgz#243cf2b7db1bee2a8a41b78c14a18a85db0e1646" - integrity sha512-1DYjY/nX2U9pkTkwFoAmKcK1ZWmkNgO32Oon9tp/9+HURizxUQ4fZRxMJZs093SldP7q6dotVj03kIkiqOILyA== +idb-keyval@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-5.0.4.tgz#182881b1eafbb47d11a269422ae6d5243f0db0c7" + integrity sha512-qS0kplHuadZujoE90ze0NUkhW0/Fbfib7d+mYNMXNEn45NSh2NWY3fBewoX4GZUsKkGHBgc8JiAwMx0zrfL3LQ== ieee754@1.1.13, ieee754@^1.1.13, ieee754@^1.1.4: version "1.1.13" @@ -5533,10 +5553,10 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-svg@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.2.1.tgz#095b496e345fec9211c2a7d5d021003e040d6f81" - integrity sha512-PHx3ANecKsKNl5y5+Jvt53Y4J7MfMpbNZkv384QNiswMKAWIbvcqbPz+sYbFKJI8Xv3be01GSFniPmoaP+Ai5A== +is-svg@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.2.2.tgz#a4ea0f3f78dada7085db88f1e85b6f845626cfae" + integrity sha512-JlA7Mc7mfWjdxxTkJ094oUK9amGD7gQaj5xA/NCY0vlVvZ1stmj4VX+bRuwOMN93IHRZ2ctpPH/0FO6DqvQ5Rw== dependencies: html-comment-regex "^1.1.2" @@ -5722,36 +5742,36 @@ jschardet@^2.1.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== -jsdom@16.4.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz#36005bde2d136f73eee1a830c6d45e55408edddb" - integrity sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w== +jsdom@16.5.0: + version "16.5.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.0.tgz#9e453505600cc5a70b385750d35256f380730cc4" + integrity sha512-QxZH0nmDTnTTVI0YDm4RUlaUPl5dcyn62G5TMDNfMmTW+J1u1v9gCR8WR+WZ6UghAa7nKJjDOFaI00eMMWvJFQ== dependencies: - abab "^2.0.3" - acorn "^7.1.1" + abab "^2.0.5" + acorn "^8.0.5" acorn-globals "^6.0.0" cssom "^0.4.4" - cssstyle "^2.2.0" + cssstyle "^2.3.0" data-urls "^2.0.0" - decimal.js "^10.2.0" + decimal.js "^10.2.1" domexception "^2.0.1" - escodegen "^1.14.1" + escodegen "^2.0.0" html-encoding-sniffer "^2.0.1" is-potential-custom-element-name "^1.0.0" nwsapi "^2.2.0" - parse5 "5.1.1" + parse5 "6.0.1" request "^2.88.2" - request-promise-native "^1.0.8" - saxes "^5.0.0" + request-promise-native "^1.0.9" + saxes "^5.0.1" symbol-tree "^3.2.4" - tough-cookie "^3.0.1" + tough-cookie "^4.0.0" w3c-hr-time "^1.0.2" w3c-xmlserializer "^2.0.0" webidl-conversions "^6.1.0" whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" - ws "^7.2.3" + ws "^7.4.4" xml-name-validator "^3.0.0" jsesc@^2.5.1: @@ -5881,12 +5901,12 @@ jws@^4.0.0: jwa "^2.0.0" safe-buffer "^5.0.1" -katex@0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/katex/-/katex-0.12.0.tgz#2fb1c665dbd2b043edcf8a1f5c555f46beaa0cb9" - integrity sha512-y+8btoc/CK70XqcHqjxiGWBOeIL8upbS0peTPXTvgrh21n1RiWWcIpSWM+4uXq+IAgNh9YYQWdc7LVDPDAEEAg== +katex@0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.13.0.tgz#62900e56c1ad8fdf7da23399e50d7a7b690b39ab" + integrity sha512-6cHbzbegYgS9vvVGuH8UA+o97X+ZshtboSqJJCdq7trBYzuD75JNwr7Ef606xkUjecPPhFnyB+afx1dVafielg== dependencies: - commander "^2.19.0" + commander "^6.0.0" keygrip@~1.0.3: version "1.0.3" @@ -6450,10 +6470,10 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-it-anchor@7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-7.0.2.tgz#1ac28261e0ea377b8298a1a6760e422921346a03" - integrity sha512-UtYFAkce16mJlixXUMXUf14ZmOWK2YHLGKUpkZUn98w3qP8nVhb7k5sCBZmVHGMq3SpYtrxQ5XOdHQHRuLR40Q== +markdown-it-anchor@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-7.1.0.tgz#30fb21497bf59e83ff4d1ddc052d821962e2489e" + integrity sha512-loQggrwsIkkP7TOrESvmYkV2ikbQNNKhHcWyqC7/C2CmfHl1tkUizJJU8C5aGgg7J6oXVQJx17gk7i47tNn/lQ== markdown-it@12.0.4: version "12.0.4" @@ -6676,10 +6696,10 @@ mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mocha@8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.3.0.tgz#a83a7432d382ae1ca29686062d7fdc2c36f63fe5" - integrity sha512-TQqyC89V1J/Vxx0DhJIXlq9gbbL9XFNdeLQ1+JsnZsVaSOV1z3tWfw0qZmQJGQRIfkvZcs7snQnZnOCKoldq1Q== +mocha@8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.3.2.tgz#53406f195fa86fbdebe71f8b1c6fb23221d69fcc" + integrity sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg== dependencies: "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" @@ -7267,7 +7287,7 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-cancelable@2.0.0, p-cancelable@^2.0.0: +p-cancelable@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== @@ -7421,16 +7441,16 @@ parse5-htmlparser2-tree-adapter@^6.0.0: dependencies: parse5 "^6.0.1" -parse5@5.1.1, parse5@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== - parse5@6.0.1, parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parse5@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + parseurl@^1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -7789,10 +7809,10 @@ postcss-filter-plugins@^2.0.0: dependencies: postcss "^5.0.4" -postcss-loader@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-5.0.0.tgz#bea95363dcb550d72ceb612ce44663356b7782d7" - integrity sha512-bOvyWP5VHCJbThbv7wrBwCBc3DsVpyCfd+k/wHOL3wTAMMHmSSfNts90EADf8bHa6I810ird1JBEKmBRgJu3cg== +postcss-loader@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-5.2.0.tgz#ccd6668a778902d653602289c765a8bc481986dc" + integrity sha512-uSuCkENFeUaOYsKrXm0eNNgVIxc71z8RcckLMbVw473rGojFnrUeqEz6zBgXsH2q1EIzXnO/4pEz9RhALjlITA== dependencies: cosmiconfig "^7.0.0" klona "^2.0.4" @@ -8249,10 +8269,10 @@ postcss-zindex@^2.0.1: postcss "^5.0.4" uniqs "^2.0.0" -postcss@8.2.7, postcss@^8.2.4: - version "8.2.7" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.7.tgz#48ed8d88b4de10afa0dfd1c3f840aa57b55c4d47" - integrity sha512-DsVLH3xJzut+VT+rYr0mtvOtpTjSyqDwPf5EZWXcb0uAKfitGpTY9Ec+afi2+TgdN8rWS9Cs88UDYehKo/RvOw== +postcss@8.2.8, postcss@^8.2.8: + version "8.2.8" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.8.tgz#0b90f9382efda424c4f0f69a2ead6f6830d08ece" + integrity sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw== dependencies: colorette "^1.2.2" nanoid "^3.1.20" @@ -8299,10 +8319,10 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" -prebuild-install@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.0.tgz#669022bcde57c710a869e39c5ca6bf9cd207f316" - integrity sha512-h2ZJ1PXHKWZpp1caLw0oX9sagVpL2YTk+ZwInQbQ3QqNd4J03O6MpFNmMTJlkfgPENWqe5kP0WjQLqz5OjLfsw== +prebuild-install@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.1.tgz#5902172f7a40eb67305b96c2a695db32636ee26d" + integrity sha512-7GOJrLuow8yeiyv75rmvZyeMGzl8mdEX5gY69d6a6bHWmiPevwqFw+tQavhK0EYMaSg3/KD24cWqeQv1EWsqDQ== dependencies: detect-libc "^1.0.3" expand-template "^2.0.3" @@ -8369,10 +8389,10 @@ prismjs@1.23.0: optionalDependencies: clipboard "^2.0.0" -probe-image-size@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-6.0.0.tgz#4a85b19d5af4e29a8de7d53a9aa036f6fd02f5f4" - integrity sha512-99PZ5+RU4gqiTfK5ZDMDkZtn6eL4WlKfFyVJV7lFQvH3iGmQ85DqMTOdxorERO26LHkevR2qsxnHp0x/2UDJPA== +probe-image-size@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.0.0.tgz#8a287207c04058f33edd31e7a66f6c5eaac02301" + integrity sha512-pddLh5UPAR2tj0GXin5qCFoYa9oo1HOvk+9HYb+V1z7sUjuhCsVbCAKDVvMXwJp5JSsYOoCZ6iQaDpjPvS04nQ== dependencies: deepmerge "^4.0.0" needle "^2.5.2" @@ -8429,7 +8449,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.24, psl@^1.1.28: +psl@^1.1.24, psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== @@ -8577,10 +8597,10 @@ punycode@^1.4.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -pureimage@0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/pureimage/-/pureimage-0.2.5.tgz#10c9d314bcdfba712229ec70c3849164f537c248" - integrity sha512-D/oP8uaS8HLIOPqaxeVU0ZcJHUwvvFjeclKwn0RBeZJn3TBtZgKn7FVBN5auuCHQTC4K/wDAHfkY/JOuGCiohQ== +pureimage@0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/pureimage/-/pureimage-0.2.7.tgz#e6da5868e42d03a94aab7ad1f9da67c865914ab8" + integrity sha512-F3z3QAoIpgqI0Uu122Y18oTRo0Y/aloQSqaAfRSiO4LRIQIHOjP+qUFl8sJ04mhOJ1sHsRfgZ8CUjQqVSXfV1g== dependencies: jpeg-js "^0.4.1" opentype.js "^0.4.3" @@ -8931,12 +8951,12 @@ request-promise-core@1.1.2: dependencies: lodash "^4.17.11" -request-promise-core@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" - integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== dependencies: - lodash "^4.17.15" + lodash "^4.17.19" request-promise-native@1.0.7: version "1.0.7" @@ -8947,12 +8967,12 @@ request-promise-native@1.0.7: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request-promise-native@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" - integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== +request-promise-native@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== dependencies: - request-promise-core "1.1.3" + request-promise-core "1.1.4" stealthy-require "^1.1.1" tough-cookie "^2.3.3" @@ -9233,7 +9253,7 @@ sax@>=0.6.0, sax@^1.2.4, sax@~1.2.1, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^5.0.0: +saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== @@ -9350,17 +9370,17 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -sharp@0.27.1: - version "0.27.1" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.27.1.tgz#cd04926406a697b58dfc5fb62e3c7a3a2d68389a" - integrity sha512-IQNXWdspb4nZcJemXa6cfgz+JvKONsuqP8Mwi1Oti23Uo7+J+UF2jihJDf6I1BQbrmhcZ0lagH/1WYG+ReAzyQ== +sharp@0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.27.2.tgz#a939775e630e88600c0b5e68f20593aea722252f" + integrity sha512-w3FVoONPG/x5MXCc3wsjOS+b9h3CI60qkus6EPQU4dkT0BDm0PyGhDCK6KhtfT3/vbeOMOXAKFNSw+I3QGWkMA== dependencies: array-flatten "^3.0.0" color "^3.1.3" detect-libc "^1.0.3" node-addon-api "^3.1.0" npmlog "^4.1.2" - prebuild-install "^6.0.0" + prebuild-install "^6.0.1" semver "^7.3.4" simple-get "^4.0.0" tar-fs "^2.1.1" @@ -10023,10 +10043,10 @@ syslog-pro@1.0.0: dependencies: moment "^2.22.2" -systeminformation@5.6.1: - version "5.6.1" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.6.1.tgz#3726ee88b0ba50ec2268f64242fbd9a93df6546c" - integrity sha512-5wJlHB4fzcrNENaqDVzy51+NlL0QRrLQ6pSRQOKl4k8v4jvYTlJUxChZYwpphk3McUw9iWPRcjdpKbTEj2Ucuw== +systeminformation@5.6.2: + version "5.6.2" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.6.2.tgz#41980e6e523eaff09febc3f7e114c4fa0fa239dd" + integrity sha512-B+xKaftatny2HGt9RS9Pk7Rc2Ja5zvb9rRZEiG7mcnQF4KcP68RZz4Ff+V0iVwkqfSVJ/3CIvkG+an/PWwXvbg== syuilo-password-strength@0.0.1: version "0.0.1" @@ -10325,6 +10345,15 @@ tough-cookie@^3.0.1: psl "^1.1.28" punycode "^2.1.1" +tough-cookie@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.1.2" + tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" @@ -10345,10 +10374,10 @@ trace-redirect@1.0.6: resolved "https://registry.yarnpkg.com/trace-redirect/-/trace-redirect-1.0.6.tgz#ac629b5bf8247d30dde5a35fe9811b811075b504" integrity sha512-UUfa1DjjU5flcjMdaFIiIEGDTyu2y/IiMjOX4uGXa7meKBS4vD4f2Uy/tken9Qkd4Jsm4sRsfZcIIPqrRVF3Mg== -ts-loader@8.0.17: - version "8.0.17" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.17.tgz#98f2ccff9130074f4079fd89b946b4c637b1f2fc" - integrity sha512-OeVfSshx6ot/TCxRwpBHQ/4lRzfgyTkvi7ghDVrLXOHzTbSK413ROgu/xNqM72i3AFeAIJgQy78FwSMKmOW68w== +ts-loader@8.0.18: + version "8.0.18" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.18.tgz#b2385cbe81c34ad9f997915129cdde3ad92a61ea" + integrity sha512-hRZzkydPX30XkLaQwJTDcWDoxZHK6IrEMDQpNd7tgcakFruFkeUp/aY+9hBb7BUGb+ZWKI0jiOGMo0MckwzdDQ== dependencies: chalk "^4.1.0" enhanced-resolve "^4.0.0" @@ -10521,10 +10550,10 @@ typeorm@0.2.31: yargonaut "^1.1.2" yargs "^16.0.3" -typescript@4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72" - integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== +typescript@4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3" + integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" @@ -10596,6 +10625,11 @@ unique-stream@^2.0.2: json-stable-stringify-without-jsonify "^1.0.1" through2-filter "^3.0.0" +universalify@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + unload@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7" @@ -10837,10 +10871,10 @@ vue-color@2.8.1: material-colors "^1.0.0" tinycolor2 "^1.1.2" -vue-eslint-parser@^7.5.0: - version "7.5.0" - resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.5.0.tgz#b68221c55fee061899afcfb4441ec74c1495285e" - integrity sha512-6EHzl00hIpy4yWZo3qSbtvtVw1A1cTKOv1w95QSuAqGgk4113XtRjvNIiEGo49r0YWOPYsrmI4Dl64axL5Agrw== +vue-eslint-parser@^7.6.0: + version "7.6.0" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.6.0.tgz#01ea1a2932f581ff244336565d712801f8f72561" + integrity sha512-QXxqH8ZevBrtiZMZK0LpwaMfevQi9UL7lY6Kcp+ogWHC88AuwUPwwCIzkOUc1LR4XsYAt/F9yHXAB/QoD17QXA== dependencies: debug "^4.1.1" eslint-scope "^5.0.0" @@ -10985,10 +11019,10 @@ webpack-sources@^2.1.1: source-list-map "^2.0.1" source-map "^0.6.1" -webpack@5.24.2: - version "5.24.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.24.2.tgz#33790dad631e8b639f4246d762e257720875fe54" - integrity sha512-uxxKYEY4kMNjP+D2Y+8aw5Vd7ar4pMuKCNemxV26ysr1nk0YDiQTylg9U3VZIdkmI0YHa0uC8ABxL+uGxGWWJg== +webpack@5.25.0: + version "5.25.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.25.0.tgz#f9409977f0f3b6d4b9c4f73adc7d7cb9603a09e9" + integrity sha512-jqQZopNCzt9c4K6Qa7j6kIhzHfR9wgF84go58VoNp4JbZrBr2D2l5lcv72CW80yc6NJl8CR6OY8xctnIs0r2uw== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.46" @@ -11165,15 +11199,10 @@ write-json-file@^2.3.0: sort-keys "^2.0.0" write-file-atomic "^2.0.0" -ws@7.4.3: - version "7.4.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" - integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== - -ws@^7.2.3: - version "7.4.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb" - integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ== +ws@7.4.4, ws@^7.4.4: + version "7.4.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" + integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== xev@2.0.1: version "2.0.1" From 3e2d2e5f6be371556164b89b820cb3ae5c5cdcea Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 12 Mar 2021 23:37:06 +0900 Subject: [PATCH 04/76] =?UTF-8?q?npm=20run=20watch=E3=81=A7=E3=82=B5?= =?UTF-8?q?=E3=83=BC=E3=83=90=E3=83=BC=E3=82=B5=E3=82=A4=E3=83=89=E3=82=82?= =?UTF-8?q?watch=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gulpfile.ts | 5 +++++ package.json | 5 ++++- yarn.lock | 63 +++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/gulpfile.ts b/gulpfile.ts index 018134ca26..b899d62f99 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -91,3 +91,8 @@ gulp.task('build', gulp.parallel( )); gulp.task('default', gulp.task('build')); + +gulp.watch([ + './src/**/*', + '!./src/client/**/*' +], gulp.task('build')); diff --git a/package.json b/package.json index a583e21aee..ac1a888adc 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ "build": "webpack && gulp build", "build-product": "cross-env NODE_ENV=production webpack && gulp build", "webpack": "webpack", - "watch": "webpack --watch", + "watch": "concurrently \"npm:watch-*\"", + "watch-webpack": "webpack --watch", + "watch-gulp": "gulp --watch", "gulp": "gulp build", "clean": "gulp clean", "cleanall": "gulp cleanall", @@ -125,6 +127,7 @@ "chart.js": "2.9.4", "cli-highlight": "2.1.10", "commander": "4.1.1", + "concurrently": "6.0.0", "content-disposition": "0.5.3", "core-js": "3.9.1", "crc-32": "1.2.0", diff --git a/yarn.lock b/yarn.lock index 2daa9acc76..d651fbfef2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -882,6 +882,11 @@ dependencies: "@types/node" "*" +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + "@types/nprogress@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@types/nprogress/-/nprogress-0.2.0.tgz#86c593682d4199212a0509cc3c4d562bbbd6e45f" @@ -2871,6 +2876,21 @@ concat-stream@^1.5.2, concat-stream@^1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" +concurrently@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.0.0.tgz#c1a876dd99390979c71f8c6fe6796882f3a13199" + integrity sha512-Ik9Igqnef2ONLjN2o/OVx1Ow5tymVvvEwQeYCQdD/oV+CN9oWhxLk7ibcBdOtv0UzBqHCEKRwbKceYoTK8t3fQ== + dependencies: + chalk "^4.1.0" + date-fns "^2.16.1" + lodash "^4.17.20" + read-pkg "^5.2.0" + rxjs "^6.6.3" + spawn-command "^0.0.2-1" + supports-color "^8.1.0" + tree-kill "^1.2.2" + yargs "^16.2.0" + condense-newlines@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/condense-newlines/-/condense-newlines-0.2.1.tgz#3de985553139475d32502c83b02f60684d24c55f" @@ -3302,6 +3322,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +date-fns@^2.16.1: + version "2.19.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.19.0.tgz#65193348635a28d5d916c43ec7ce6fbd145059e1" + integrity sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg== + dateformat@4.5.1: version "4.5.1" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.5.1.tgz#c20e7a9ca77d147906b6dc2261a8be0a5bd2173c" @@ -6964,7 +6989,7 @@ nopt@^5.0.0: dependencies: abbrev "1" -normalize-package-data@^2.3.2: +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -8735,6 +8760,16 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + readable-stream@1.1.x: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -9196,6 +9231,13 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== +rxjs@^6.6.3: + version "6.6.6" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" + integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== + dependencies: + tslib "^1.9.0" + s-age@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/s-age/-/s-age-1.1.2.tgz#c0cf15233ccc93f41de92ea42c36d957977d1ea2" @@ -9567,6 +9609,11 @@ sparkles@^1.0.0: resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== +spawn-command@^0.0.2-1: + version "0.0.2-1" + resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" + integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= + spawn-sync@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-2.0.0.tgz#3af5ba4b73cc5dc8a41d3747eede71e98d949555" @@ -9896,7 +9943,7 @@ summaly@2.4.0: require-all "2.2.0" trace-redirect "1.0.6" -supports-color@8.1.1: +supports-color@8.1.1, supports-color@^8.1.0: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -10374,6 +10421,11 @@ trace-redirect@1.0.6: resolved "https://registry.yarnpkg.com/trace-redirect/-/trace-redirect-1.0.6.tgz#ac629b5bf8247d30dde5a35fe9811b811075b504" integrity sha512-UUfa1DjjU5flcjMdaFIiIEGDTyu2y/IiMjOX4uGXa7meKBS4vD4f2Uy/tken9Qkd4Jsm4sRsfZcIIPqrRVF3Mg== +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + ts-loader@8.0.18: version "8.0.18" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.18.tgz#b2385cbe81c34ad9f997915129cdde3ad92a61ea" @@ -10488,6 +10540,11 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -11337,7 +11394,7 @@ yargs-unparser@2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" -yargs@16.2.0, yargs@^16.0.0, yargs@^16.0.3: +yargs@16.2.0, yargs@^16.0.0, yargs@^16.0.3, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== From 320f5fedfb0fe0c6761ba0ba5a4d37b730975f41 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 13 Mar 2021 00:18:41 +0900 Subject: [PATCH 05/76] =?UTF-8?q?eslintrc=E3=81=A8tsconfig=E3=82=92?= =?UTF-8?q?=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc | 20 +---------------- gulpfile.ts | 2 +- src/.eslintrc | 6 +++++ src/client/.eslintrc | 20 +++++++++++++++++ tsconfig.json => src/tsconfig.json | 8 +++---- test/.eslintrc | 7 ++++++ test/tsconfig.json | 36 ++++++++++++++++++++++++++++++ 7 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 src/.eslintrc rename tsconfig.json => src/tsconfig.json (89%) create mode 100644 test/.eslintrc create mode 100644 test/tsconfig.json diff --git a/.eslintrc b/.eslintrc index 3a220319e5..3e5b59cb04 100644 --- a/.eslintrc +++ b/.eslintrc @@ -3,29 +3,11 @@ "parser": "@typescript-eslint/parser" }, "extends": [ - "eslint:recommended", - "plugin:vue/recommended" + "eslint:recommended" ], "rules": { - "vue/require-v-for-key": 0, - "vue/max-attributes-per-line": 0, - "vue/html-indent": 0, - "vue/html-self-closing": 0, - "vue/no-unused-vars": 0, - "vue/attributes-order": 0, - "vue/require-prop-types": 0, - "vue/require-default-prop": 0, - "vue/html-closing-bracket-spacing": 0, - "vue/singleline-html-element-content-newline": 0, - "vue/no-v-html": 0, "no-console": 0, "no-unused-vars": 0, "no-empty": 0 - }, - "globals": { - "ENV": true, - "VERSION": true, - "API": true, - "LANGS": true } } diff --git a/gulpfile.ts b/gulpfile.ts index b899d62f99..0717d82086 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -14,7 +14,7 @@ const locales: { [x: string]: any } = require('./locales'); const meta = require('./package.json'); gulp.task('build:ts', () => { - const tsProject = ts.createProject('./tsconfig.json'); + const tsProject = ts.createProject('./src/tsconfig.json'); return tsProject .src() diff --git a/src/.eslintrc b/src/.eslintrc new file mode 100644 index 0000000000..d54e20f6b6 --- /dev/null +++ b/src/.eslintrc @@ -0,0 +1,6 @@ +{ + "env": { + "node": true, + "commonjs": true + } +} diff --git a/src/client/.eslintrc b/src/client/.eslintrc index 8829472b49..fffa28d9e4 100644 --- a/src/client/.eslintrc +++ b/src/client/.eslintrc @@ -1,4 +1,24 @@ { + "env": { + "node": false, + }, + "extends": [ + "eslint:recommended", + "plugin:vue/recommended" + ], + "rules": { + "vue/require-v-for-key": 0, + "vue/max-attributes-per-line": 0, + "vue/html-indent": 0, + "vue/html-self-closing": 0, + "vue/no-unused-vars": 0, + "vue/attributes-order": 0, + "vue/require-prop-types": 0, + "vue/require-default-prop": 0, + "vue/html-closing-bracket-spacing": 0, + "vue/singleline-html-element-content-newline": 0, + "vue/no-v-html": 0 + }, "globals": { "_DEV_": false, "_LANGS_": false, diff --git a/tsconfig.json b/src/tsconfig.json similarity index 89% rename from tsconfig.json rename to src/tsconfig.json index 075450bf64..95cb35fc5f 100644 --- a/tsconfig.json +++ b/src/tsconfig.json @@ -22,8 +22,8 @@ "resolveJsonModule": true, "isolatedModules": true, "typeRoots": [ - "node_modules/@types", - "src/@types" + "../node_modules/@types", + "./@types" ], "lib": [ "esnext" @@ -31,9 +31,9 @@ }, "compileOnSave": false, "include": [ - "./src/**/*.ts" + "./**/*.ts" ], "exclude": [ - "./src/client/**/*.ts" + "./client/**/*.ts" ] } diff --git a/test/.eslintrc b/test/.eslintrc new file mode 100644 index 0000000000..cea1b11388 --- /dev/null +++ b/test/.eslintrc @@ -0,0 +1,7 @@ +{ + "env": { + "node": true, + "mocha": true, + "commonjs": true + } +} diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 0000000000..987067ba81 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "allowJs": true, + "noEmitOnError": false, + "noImplicitAny": true, + "noImplicitReturns": true, + "noUnusedParameters": false, + "noUnusedLocals": true, + "noFallthroughCasesInSwitch": true, + "declaration": false, + "sourceMap": true, + "target": "es2017", + "module": "commonjs", + "moduleResolution": "node", + "removeComments": false, + "noLib": false, + "strict": true, + "strictNullChecks": true, + "strictPropertyInitialization": false, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "resolveJsonModule": true, + "isolatedModules": true, + "typeRoots": [ + "../node_modules/@types", + "../src/@types" + ], + "lib": [ + "esnext" + ] + }, + "compileOnSave": false, + "include": [ + "./**/*.ts" + ] +} From 0359db23d9f7a5c9d1e41570adb6af0f171cbda3 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 13 Mar 2021 00:18:46 +0900 Subject: [PATCH 06/76] add test --- test/fetch-resource.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/fetch-resource.ts b/test/fetch-resource.ts index 72ef133ef8..ba1c530e8a 100644 --- a/test/fetch-resource.ts +++ b/test/fetch-resource.ts @@ -68,6 +68,11 @@ describe('Fetch resource', () => { const res = await simpleGet('/api.json', 'application/json'); assert.strictEqual(res.status, 200); })); + + it('GET favicon.ico', async(async () => { + const res = await simpleGet('/favicon.ico', 'image/png'); + assert.strictEqual(res.status, 200); + })); }); describe('/@:username', () => { From 9b010074e11dcd4b63ae9efddd6239e82dbe6ded Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 13 Mar 2021 00:24:22 +0900 Subject: [PATCH 07/76] refactor: use TS_NODE_PROJECT instead of TS_NODE_COMPILER_OPTIONS --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac1a888adc..d805a5fee8 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "clean": "gulp clean", "cleanall": "gulp cleanall", "lint": "tslint 'src/**/*.ts'", - "test": "cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_COMPILER_OPTIONS=\"{\\\"target\\\":\\\"es2017\\\",\\\"module\\\":\\\"commonjs\\\",\\\"typeRoots\\\":[\\\"node_modules/@types\\\",\\\"src/@types\\\"]}\" mocha", + "test": "cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha", "format": "gulp format" }, "resolutions": { From aaa40e34c3cce3c0d6ff8e6020b8830b4e5f320f Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 13 Mar 2021 00:24:32 +0900 Subject: [PATCH 08/76] fix comment --- test/fetch-resource.ts | 4 ++-- test/get-file-info.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/fetch-resource.ts b/test/fetch-resource.ts index ba1c530e8a..6f976faa76 100644 --- a/test/fetch-resource.ts +++ b/test/fetch-resource.ts @@ -2,10 +2,10 @@ * Tests for Fetch resource * * How to run the tests: - * > TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true npx mocha test/fetch-resource.ts --require ts-node/register + * > npx cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true npx mocha test/fetch-resource.ts --require ts-node/register * * To specify test: - * > TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true npx mocha test/fetch-resource.ts --require ts-node/register -g 'test name' + * > npx cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true npx mocha test/fetch-resource.ts --require ts-node/register -g 'test name' */ process.env.NODE_ENV = 'test'; diff --git a/test/get-file-info.ts b/test/get-file-info.ts index 312b225aa2..8757d5a901 100644 --- a/test/get-file-info.ts +++ b/test/get-file-info.ts @@ -2,10 +2,10 @@ * Tests for detection of file information * * How to run the tests: - * > TS_NODE_FILES=true npx mocha test/get-file-info.ts --require ts-node/register + * > npx cross-env TS_NODE_FILES=true npx mocha test/get-file-info.ts --require ts-node/register * * To specify test: - * > TS_NODE_FILES=true npx mocha test/get-file-info.ts --require ts-node/register -g 'test name' + * > npx cross-env TS_NODE_FILES=true npx mocha test/get-file-info.ts --require ts-node/register -g 'test name' */ import * as assert from 'assert'; From 7ea207df5d48f17c6b706152eded0a41957eaf92 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 13 Mar 2021 00:26:29 +0900 Subject: [PATCH 09/76] Create CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..1fd048e8ea --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +see [releases](https://github.com/syuilo/misskey/releases) From e9ba81149d95a02e699f4dc2222bb785efde97ff Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 13 Mar 2021 00:40:35 +0900 Subject: [PATCH 10/76] fix watch --- gulpfile.ts | 10 ++++++---- package.json | 10 ++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/gulpfile.ts b/gulpfile.ts index 0717d82086..0cdd016fd7 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -92,7 +92,9 @@ gulp.task('build', gulp.parallel( gulp.task('default', gulp.task('build')); -gulp.watch([ - './src/**/*', - '!./src/client/**/*' -], gulp.task('build')); +gulp.task('watch', () => { + gulp.watch([ + './src/**/*', + '!./src/client/**/*' + ], { ignoreInitial: false }, gulp.task('build')); +}); diff --git a/package.json b/package.json index d805a5fee8..634182f552 100644 --- a/package.json +++ b/package.json @@ -11,18 +11,16 @@ "private": true, "scripts": { "start": "node ./index.js", - "start-product": "cross-env NODE_ENV=production node ./index.js", "init": "npm run migrate", "ormconfig": "node ./built/ormconfig.js", "migrate": "ts-node ./node_modules/typeorm/cli.js migration:run", "migrateandstart": "npm run migrate && npm run start", - "build": "webpack && gulp build", - "build-product": "cross-env NODE_ENV=production webpack && gulp build", - "webpack": "webpack", + "build": "npm run build-webpack && npm run build-gulp", + "build-webpack": "webpack", + "build-gulp": "gulp build", "watch": "concurrently \"npm:watch-*\"", "watch-webpack": "webpack --watch", - "watch-gulp": "gulp --watch", - "gulp": "gulp build", + "watch-gulp": "gulp watch", "clean": "gulp clean", "cleanall": "gulp cleanall", "lint": "tslint 'src/**/*.ts'", From a87930542a2975af05799adc5250771a08301811 Mon Sep 17 00:00:00 2001 From: Zero King <l2dy@icloud.com> Date: Fri, 12 Mar 2021 17:07:18 +0000 Subject: [PATCH 11/76] Fix typo (#7334) --- src/services/following/create.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/following/create.ts b/src/services/following/create.ts index c0583cdb86..6bc98aee87 100644 --- a/src/services/following/create.ts +++ b/src/services/following/create.ts @@ -93,7 +93,7 @@ export async function insertFollowingDoc(followee: User, follower: User) { // Publish followed event if (Users.isLocalUser(followee)) { - Users.pack(follower, followee).then(packed => publishMainStream(followee.id, 'followed', packed)), + Users.pack(follower, followee).then(packed => publishMainStream(followee.id, 'followed', packed)); // 通知を作成 createNotification(followee.id, 'follow', { From 16b5e121cd1e271036d8d081fe747f9133a07b10 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 12 Mar 2021 20:14:42 +0000 Subject: [PATCH 12/76] Bump @babel/plugin-transform-runtime from 7.13.9 to 7.13.10 Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.13.9 to 7.13.10. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.13.10/packages/babel-plugin-transform-runtime) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 634182f552..ee8f655bf8 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "lodash": "^4.17.20" }, "dependencies": { - "@babel/plugin-transform-runtime": "7.13.9", + "@babel/plugin-transform-runtime": "7.13.10", "@elastic/elasticsearch": "7.11.0", "@fortawesome/fontawesome-svg-core": "1.2.34", "@fortawesome/free-brands-svg-icons": "5.15.2", diff --git a/yarn.lock b/yarn.lock index d651fbfef2..e7576183ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -128,10 +128,10 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.9.tgz#ca34cb95e1c2dd126863a84465ae8ef66114be99" integrity sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw== -"@babel/plugin-transform-runtime@7.13.9": - version "7.13.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.9.tgz#744d3103338a0d6c90dee0497558150b490cee07" - integrity sha512-XCxkY/wBI6M6Jj2mlWxkmqbKPweRanszWbF3Tyut+hKh+PHcuIH/rSr/7lmmE7C3WW+HSIm2GT+d5jwmheuB0g== +"@babel/plugin-transform-runtime@7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.10.tgz#a1e40d22e2bf570c591c9c7e5ab42d6bf1e419e1" + integrity sha512-Y5k8ipgfvz5d/76tx7JYbKQTcgFSU6VgJ3kKQv4zGTKr+a9T/KBvfRvGtSFgKDQGt/DBykQixV0vNWKIdzWErA== dependencies: "@babel/helper-module-imports" "^7.12.13" "@babel/helper-plugin-utils" "^7.13.0" From 72120279370c43d5790df985568b3cb20ef57ed9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 12 Mar 2021 20:15:47 +0000 Subject: [PATCH 13/76] Bump vue-router from 4.0.4 to 4.0.5 Bumps [vue-router](https://github.com/vuejs/vue-router) from 4.0.4 to 4.0.5. - [Release notes](https://github.com/vuejs/vue-router/releases) - [Changelog](https://github.com/vuejs/vue-router/blob/dev/CHANGELOG.md) - [Commits](https://github.com/vuejs/vue-router/commits) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 634182f552..8d4b137512 100644 --- a/package.json +++ b/package.json @@ -250,7 +250,7 @@ "vue-json-pretty": "1.7.1", "vue-loader": "16.1.2", "vue-prism-editor": "2.0.0-alpha.2", - "vue-router": "4.0.4", + "vue-router": "4.0.5", "vue-style-loader": "4.1.3", "vuedraggable": "4.0.1", "web-push": "3.4.4", diff --git a/yarn.lock b/yarn.lock index d651fbfef2..0b9c41d754 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10959,10 +10959,10 @@ vue-prism-editor@2.0.0-alpha.2: resolved "https://registry.yarnpkg.com/vue-prism-editor/-/vue-prism-editor-2.0.0-alpha.2.tgz#aa53a88efaaed628027cbb282c2b1d37fc7c5c69" integrity sha512-Gu42ba9nosrE+gJpnAEuEkDMqG9zSUysIR8SdXUw8MQKDjBnnNR9lHC18uOr/ICz7yrA/5c7jHJr9lpElODC7w== -vue-router@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.0.4.tgz#ad9b4b7bbdad622407b4ff189b1646f48c1e9053" - integrity sha512-uN6PDEaYdU9aRO7mU+Dkr1uaY49hV3fucEDG/Vre/Qj8ct3RoJS16vcPrvKVzn69zDDjBV5b9Xw7fZA9r6b/Iw== +vue-router@4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.0.5.tgz#dd0a4134bc950c37aef64b973e9ee1008428d8fa" + integrity sha512-AQq+pllb6FCc7rS6vh4PPcce3XA1jgK3hKNkQ4hXHwoVN7jOeAOMKCnX7XAX3etV9rmN7iNW8iIwgPk95ckBjw== vue-style-loader@4.1.3: version "4.1.3" From c562d094790865ca782af0e061bce92c981a7461 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 12 Mar 2021 21:30:02 +0000 Subject: [PATCH 14/76] Bump vue and @vue/compiler-sfc Bumps [vue](https://github.com/vuejs/vue) and [@vue/compiler-sfc](https://github.com/vuejs/vue-next/tree/HEAD/packages/compiler-sfc). These dependencies needed to be updated together. Updates `vue` from 3.0.5 to 3.0.7 - [Release notes](https://github.com/vuejs/vue/releases) - [Commits](https://github.com/vuejs/vue/commits) Updates `@vue/compiler-sfc` from 3.0.5 to 3.0.7 - [Release notes](https://github.com/vuejs/vue-next/releases) - [Changelog](https://github.com/vuejs/vue-next/blob/master/CHANGELOG.md) - [Commits](https://github.com/vuejs/vue-next/commits/v3.0.7/packages/compiler-sfc) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> --- package.json | 4 +- yarn.lock | 210 +++++++++++++++++---------------------------------- 2 files changed, 70 insertions(+), 144 deletions(-) diff --git a/package.json b/package.json index 6c5334c4a5..67d7dfca89 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "@types/websocket": "1.0.2", "@types/ws": "7.4.0", "@typescript-eslint/parser": "4.17.0", - "@vue/compiler-sfc": "3.0.5", + "@vue/compiler-sfc": "3.0.7", "abort-controller": "3.0.0", "apexcharts": "3.25.0", "autobind-decorator": "2.4.0", @@ -245,7 +245,7 @@ "uuid": "8.3.2", "v-debounce": "0.1.2", "vanilla-tilt": "1.7.0", - "vue": "3.0.5", + "vue": "3.0.7", "vue-color": "2.8.1", "vue-json-pretty": "1.7.1", "vue-loader": "16.1.2", diff --git a/yarn.lock b/yarn.lock index d3fb4ff257..274f07631c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -89,11 +89,6 @@ dependencies: "@babel/types" "^7.12.13" -"@babel/helper-validator-identifier@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" - integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== - "@babel/helper-validator-identifier@^7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" @@ -113,17 +108,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.12.0", "@babel/parser@^7.6.0": - version "7.12.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.0.tgz#2ad388f3960045b22f9b7d4bf85e80b15a1c9e3a" - integrity sha512-dYmySMYnlus2jwl7JnnajAj11obRStZoW9cG04wh4ZuhozDn11tDUrhHcUZ9iuNHqALAhh60XqNaYXpvuuE/Gg== - -"@babel/parser@^7.12.13", "@babel/parser@^7.13.0": - version "7.13.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.4.tgz#340211b0da94a351a6f10e63671fa727333d13ab" - integrity sha512-uvoOulWHhI+0+1f9L4BoozY7U5cIkZ9PgJqvb041d6vypgUmtVPG4vmGm4pSggjl8BELzvHyUeJSUyEMY6b+qA== - -"@babel/parser@^7.9.6": +"@babel/parser@^7.12.0", "@babel/parser@^7.12.13", "@babel/parser@^7.13.0", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6": version "7.13.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.9.tgz#ca34cb95e1c2dd126863a84465ae8ef66114be99" integrity sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw== @@ -171,25 +156,7 @@ globals "^11.1.0" lodash "^4.17.19" -"@babel/types@^7.12.0", "@babel/types@^7.6.1": - version "7.12.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.7.tgz#6039ff1e242640a29452c9ae572162ec9a8f5d13" - integrity sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ== - dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - -"@babel/types@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.13.tgz#8be1aa8f2c876da11a9cf650c0ecf656913ad611" - integrity sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ== - dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - -"@babel/types@^7.13.0", "@babel/types@^7.9.6": +"@babel/types@^7.12.0", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.6.1", "@babel/types@^7.9.6": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== @@ -1217,83 +1184,83 @@ resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== -"@vue/compiler-core@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.5.tgz#a6e54cabe9536e74c6513acd2649f311af1d43ac" - integrity sha512-iFXwk2gmU/GGwN4hpBwDWWMLvpkIejf/AybcFtlQ5V1ur+5jwfBaV0Y1RXoR6ePfBPJixtKZ3PmN+M+HgMAtfQ== +"@vue/compiler-core@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.7.tgz#421782a4c67cc3f2b7c30457ef446d74f8524f74" + integrity sha512-JFohgBXoyUc3mdeI2WxlhjQZ5fakfemJkZHX8Gu/nFbEg3+lKVUZmNKWmmnp9aOzJQZKoj77LjmFxiP+P+7lMQ== dependencies: "@babel/parser" "^7.12.0" "@babel/types" "^7.12.0" - "@vue/shared" "3.0.5" + "@vue/shared" "3.0.7" estree-walker "^2.0.1" source-map "^0.6.1" -"@vue/compiler-dom@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.5.tgz#7885a13e6d18f64dde8ebceec052ed2c102696c2" - integrity sha512-HSOSe2XSPuCkp20h4+HXSiPH9qkhz6YbW9z9ZtL5vef2T2PMugH7/osIFVSrRZP/Ul5twFZ7MIRlp8tPX6e4/g== +"@vue/compiler-dom@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.7.tgz#54d2e12fb9a7aff53abd19dac2c2679533f0c919" + integrity sha512-VnIH9EbWQm/Tkcp+8dCaNVsVvhm/vxCrIKWRkXY9215hTqOqQOvejT8IMjd2kc++nIsYMsdQk6H9qqBvoLe/Cw== dependencies: - "@vue/compiler-core" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/compiler-core" "3.0.7" + "@vue/shared" "3.0.7" -"@vue/compiler-sfc@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.5.tgz#3ae08e60244a72faf9598361874fb7bdb5b1d37c" - integrity sha512-uOAC4X0Gx3SQ9YvDC7YMpbDvoCmPvP0afVhJoxRotDdJ+r8VO3q4hFf/2f7U62k4Vkdftp6DVni8QixrfYzs+w== +"@vue/compiler-sfc@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.7.tgz#900414750cc726553b870490f48073451fd14f07" + integrity sha512-37/QILpGE+J3V+bP9Slg9e6xGqfk+MmS2Yj8ChR4fS0/qWUU/YoYHE0GPIzjmBdH0JVOOmJqunxowIXmqNiHng== dependencies: "@babel/parser" "^7.12.0" "@babel/types" "^7.12.0" - "@vue/compiler-core" "3.0.5" - "@vue/compiler-dom" "3.0.5" - "@vue/compiler-ssr" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/compiler-core" "3.0.7" + "@vue/compiler-dom" "3.0.7" + "@vue/compiler-ssr" "3.0.7" + "@vue/shared" "3.0.7" consolidate "^0.16.0" estree-walker "^2.0.1" hash-sum "^2.0.0" lru-cache "^5.1.1" magic-string "^0.25.7" merge-source-map "^1.1.0" - postcss "^7.0.32" - postcss-modules "^3.2.2" + postcss "^8.1.10" + postcss-modules "^4.0.0" postcss-selector-parser "^6.0.4" source-map "^0.6.1" -"@vue/compiler-ssr@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.5.tgz#7661ad891a0be948726c7f7ad1e425253c587b83" - integrity sha512-Wm//Kuxa1DpgjE4P9W0coZr8wklOfJ35Jtq61CbU+t601CpPTK4+FL2QDBItaG7aoUUDCWL5nnxMkuaOgzTBKg== +"@vue/compiler-ssr@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.7.tgz#28b85d497381d75fe44234057b140b0065ca9dbf" + integrity sha512-nHRbHeSpfXwjypettjrA16TjgfDcPEwq3m/zHnGyLC1QqdLtklXmpSM43/CPwwTCRa/qdt0pldJf22MiCEuTSQ== dependencies: - "@vue/compiler-dom" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/compiler-dom" "3.0.7" + "@vue/shared" "3.0.7" -"@vue/reactivity@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.0.5.tgz#e3789e4d523d845f9ae0b4d770e2b45594742fd2" - integrity sha512-3xodUE3sEIJgS7ntwUbopIpzzvi7vDAOjVamfb2l+v1FUg0jpd3gf62N2wggJw3fxBMr+QvyxpD+dBoxLsmAjw== +"@vue/reactivity@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.0.7.tgz#e6ccc7bef7fc10b0972e4d974bad71679d3b26ad" + integrity sha512-FotWcNNaKhqpFZrdgsUOZ1enlJ5lhTt01CNTtLSyK7jYFgZBTuw8vKsEutZKDYZ1XKotOfoeO8N3pZQqmM6Etw== dependencies: - "@vue/shared" "3.0.5" + "@vue/shared" "3.0.7" -"@vue/runtime-core@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.0.5.tgz#da6331d5f300d5794e9e0ebdc8a8bd72a9e19962" - integrity sha512-Cnyi2NqREwOLcTEsIi1DQX1hHtkVj4eGm4hBG7HhokS05DqpK4/80jG6PCCnCH9rIJDB2FqtaODX397210plXg== +"@vue/runtime-core@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.0.7.tgz#d44c0b0a57d7e392912a87362a4430ccf446ecea" + integrity sha512-DBAZAwVvdmMXuyd6/9qqj/kYr/GaLTmn1L2/QLxLwP+UfhIboiTSBc/tUUb8MRk7Bb98GzNeAWkkT6AfooS3dQ== dependencies: - "@vue/reactivity" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/reactivity" "3.0.7" + "@vue/shared" "3.0.7" -"@vue/runtime-dom@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.0.5.tgz#1ce2c9c449e26ab06963da0064096e882a7a8935" - integrity sha512-iilX1KySeIzHHtErT6Y44db1rhWK5tAI0CiJIPr+SJoZ2jbjoOSE6ff/jfIQakchbm1d6jq6VtRVnp5xYdOXKA== +"@vue/runtime-dom@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.0.7.tgz#b70668d729020bc4ad608c20367223f259576ba6" + integrity sha512-Oij4ruOtnpQpCj+/Q3JPzgpTJ1Q7+N67pA53A8KVITEtxfvKL46NN6dhAZ5NGqwX6RWZpYqWQNewITeF0pHr8g== dependencies: - "@vue/runtime-core" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/runtime-core" "3.0.7" + "@vue/shared" "3.0.7" csstype "^2.6.8" -"@vue/shared@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.5.tgz#c131d88bd6713cc4d93b3bb1372edb1983225ff0" - integrity sha512-gYsNoGkWejBxNO6SNRjOh/xKeZ0H0V+TFzaPzODfBjkAIb0aQgBuixC1brandC/CDJy1wYPwSoYrXpvul7m6yw== +"@vue/shared@3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.7.tgz#96d52988efc07444c108c7c6803ba7cc93e40045" + integrity sha512-dn5FyfSc4ky424jH4FntiHno7Ss5yLkqKNmM/NXwANRnlkmqu74pnGetexDFVG5phMk9/FhwovUZCWGxsotVKg== "@webassemblyjs/ast@1.11.0": version "1.11.0" @@ -5143,13 +5110,6 @@ icss-replace-symbols@^1.1.0: resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= -icss-utils@^4.0.0, icss-utils@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" - integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== - dependencies: - postcss "^7.0.14" - icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" @@ -7974,28 +7934,11 @@ postcss-minify-selectors@^4.0.2: postcss "^7.0.0" postcss-selector-parser "^3.0.0" -postcss-modules-extract-imports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" - integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== - dependencies: - postcss "^7.0.5" - postcss-modules-extract-imports@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== -postcss-modules-local-by-default@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" - integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== - dependencies: - icss-utils "^4.1.1" - postcss "^7.0.32" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.1.0" - postcss-modules-local-by-default@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" @@ -8005,14 +7948,6 @@ postcss-modules-local-by-default@^4.0.0: postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" -postcss-modules-scope@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" - integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - postcss-modules-scope@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" @@ -8020,14 +7955,6 @@ postcss-modules-scope@^3.0.0: dependencies: postcss-selector-parser "^6.0.4" -postcss-modules-values@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" - integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== - dependencies: - icss-utils "^4.0.0" - postcss "^7.0.6" - postcss-modules-values@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" @@ -8035,19 +7962,18 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-modules@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-3.2.2.tgz#ee390de0f9f18e761e1778dfb9be26685c02c51f" - integrity sha512-JQ8IAqHELxC0N6tyCg2UF40pACY5oiL6UpiqqcIFRWqgDYO8B0jnxzoQ0EOpPrWXvcpu6BSbQU/3vSiq7w8Nhw== +postcss-modules@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-4.0.0.tgz#2bc7f276ab88f3f1b0fadf6cbd7772d43b5f3b9b" + integrity sha512-ghS/ovDzDqARm4Zj6L2ntadjyQMoyJmi0JkLlYtH2QFLrvNlxH5OAVRPWPeKilB0pY7SbuhO173KOWkPAxRJcw== dependencies: generic-names "^2.0.1" icss-replace-symbols "^1.1.0" lodash.camelcase "^4.3.0" - postcss "^7.0.32" - postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.2" - postcss-modules-scope "^2.2.0" - postcss-modules-values "^3.0.0" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" string-hash "^1.1.1" postcss-normalize-charset@^1.1.0: @@ -8227,7 +8153,7 @@ postcss-selector-parser@^3.0.0: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== @@ -8294,7 +8220,7 @@ postcss-zindex@^2.0.1: postcss "^5.0.4" uniqs "^2.0.0" -postcss@8.2.8, postcss@^8.2.8: +postcss@8.2.8, postcss@^8.1.10, postcss@^8.2.8: version "8.2.8" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.8.tgz#0b90f9382efda424c4f0f69a2ead6f6830d08ece" integrity sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw== @@ -8313,7 +8239,7 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 source-map "^0.5.6" supports-color "^3.2.3" -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27: version "7.0.32" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== @@ -10972,14 +10898,14 @@ vue-style-loader@4.1.3: hash-sum "^1.0.2" loader-utils "^1.0.2" -vue@3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.5.tgz#de1b82eba24abfe71e0970fc9b8d4b2babdc3fe1" - integrity sha512-TfaprOmtsAfhQau7WsomXZ8d9op/dkQLNIq8qPV3A0Vxs6GR5E+c1rfJS1SDkXRQj+dFyfnec7+U0Be1huiScg== +vue@3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.7.tgz#8bcff51f8be570f9e4ce8cc5f52e2ab0fe3c74a1" + integrity sha512-8h4TikD+JabbMK9aRlBO4laG0AtNHRPHynxYgWZ9sq1YUPfzynd9Jeeb27XNyZytC7aCQRX9xe1+TQJuc181Tw== dependencies: - "@vue/compiler-dom" "3.0.5" - "@vue/runtime-dom" "3.0.5" - "@vue/shared" "3.0.5" + "@vue/compiler-dom" "3.0.7" + "@vue/runtime-dom" "3.0.7" + "@vue/shared" "3.0.7" vuedraggable@4.0.1: version "4.0.1" From cbcf3b7599e0a03eb2fc8a33b6f4aad78c80fbeb Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 13 Mar 2021 10:43:37 +0900 Subject: [PATCH 15/76] Delete .autogen directory --- .autogen/check_pr.jq | 3 -- .autogen/next_url.jq | 2 - .autogen/patreon.jq | 39 -------------- .autogen/update_readme_patreon.sh | 87 ------------------------------- 4 files changed, 131 deletions(-) delete mode 100644 .autogen/check_pr.jq delete mode 100644 .autogen/next_url.jq delete mode 100644 .autogen/patreon.jq delete mode 100755 .autogen/update_readme_patreon.sh diff --git a/.autogen/check_pr.jq b/.autogen/check_pr.jq deleted file mode 100644 index 0adb0b503d..0000000000 --- a/.autogen/check_pr.jq +++ /dev/null @@ -1,3 +0,0 @@ -.[] -.head -.label diff --git a/.autogen/next_url.jq b/.autogen/next_url.jq deleted file mode 100644 index b4c3b819a5..0000000000 --- a/.autogen/next_url.jq +++ /dev/null @@ -1,2 +0,0 @@ -.links -.next diff --git a/.autogen/patreon.jq b/.autogen/patreon.jq deleted file mode 100644 index c761d587b8..0000000000 --- a/.autogen/patreon.jq +++ /dev/null @@ -1,39 +0,0 @@ -( - .data | - map( - select( - .relationships - .currently_entitled_tiers - .data[] - ) - ) | - map( - .relationships - .user - .data - .id - ) -) as $data | -.included | -map( - select( - .id as $id | - $data | - contains( - [ - $id - ] - ) - ) -) | -map( - .attributes | - [ - .full_name, - .thumb_url, - .url - ] | - @tsv -) | -.[] | -@text diff --git a/.autogen/update_readme_patreon.sh b/.autogen/update_readme_patreon.sh deleted file mode 100755 index 7a108c2b67..0000000000 --- a/.autogen/update_readme_patreon.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env bash -# __MISSKEY_BEARER_TOKEN= -# __MISSKEY_CAMPAIGN_ID= -# __MISSKEY_GITHUB_TOKEN= -# __MISSKEY_HEAD=syuilo:patch-autogen -# __MISSKEY_REPO=syuilo/misskey -# __MISSKEY_BRANCH=develop -test "$(curl -LSs -w '\n' -- "https://api.github.com/repos/$REPO/pulls?access_token=$__MISSKEY_GITHUB_TOKEN" | jq -r -f check_pr.jq | grep $__MISSKEY_HEAD)" && exit 1 -cd "$(dirname $0)/.." && \ -touch null.cache && \ -rm *.cache && \ -git checkout $__MISSKEY_BRANCH && \ -git pull origin $__MISSKEY_BRANCH && \ -git pull upstream $__MISSKEY_BRANCH && \ -git stash && \ -git rebase -f upstream/$__MISSKEY_BRANCH && \ -git branch patch-autogen && \ -git checkout patch-autogen && \ -git reset --hard HEAD || \ -exit 1 -touch patreon.md.cache && \ -rm patreon.md.cache && \ -echo '<!-- PATREON_START -->' > patreon.md.cache && \ -url="https://www.patreon.com/api/oauth2/v2/campaigns/$__MISSKEY_CAMPAIGN_ID/members?include=currently_entitled_tiers,user&fields%5Btier%5D=title&fields%5Buser%5D=full_name,thumb_url,url,hide_pledges" -while : - do - touch patreon.raw.cache && \ - rm patreon.raw.cache && \ - curl -LSs -w '\n' -H "Authorization: Bearer $__MISSKEY_BEARER_TOKEN" -- $url > patreon.raw.cache && \ - touch patreon.cache && \ - rm patreon.cache && \ - cat patreon.raw.cache | \ - jq -r -f patreon.jq >> patreon.cache && \ - echo '<table><tr>' >> patreon.md.cache && \ - cat patreon.cache | \ - awk -F'\t' '{print $2,$1}' | \ - sed -e 's/ /\\" alt=\\"/' | \ - xargs -I% echo '<td><img src="%" width="100"></td>' >> patreon.md.cache && \ - echo '</tr><tr>' >> patreon.md.cache && \ - cat patreon.cache | \ - awk -F'\t' '{print $3,$1}' | \ - sed -e 's/ /\\">/' | \ - xargs -I% echo '<td><a href="%</a></td>' >> patreon.md.cache && \ - echo '</tr></table>' >> patreon.md.cache || \ - exit 1 - new_url="$(cat patreon.raw.cache | jq -r -f next_url.jq)" - test "$new_url" = 'null' && \ - break || \ - URL="$url" -done -ignore= && \ -echo -e "\n**Last updated:** $(date -uR | sed 's/\+0000/UTC/')\n<!-- PATREON_END -->" >> patreon.md.cache && \ -touch README.md && \ -touch .autogen/README.md && \ -rm .autogen/README.md && \ -mv README.md .autogen/README.md && \ -cat .autogen/README.md | while IFS= read line; - do - if [[ -z "$ignore" ]] - then - if [[ "$line" = '<!-- PATREON_START -->' ]] - then - ignore='PATREON_INSIDE' - else - echo "$line" >> README.md - fi - else - if [[ "$LINE" = '<!-- PATREON_END -->' ]] - then - ignore= - cat patreon.md.cache >> README.md - fi - fi -done -cat patreon.md.cache -touch null.cache && \ -rm *.cache && \ -diff .autogen/README.md README.md > diff.cache -cat diff.cache && \ -test 4 -lt $(cat diff.cache | wc -l) && \ -git add README.md && \ -git commit -m 'Update README.md [AUTOGEN]' && \ -git push -f origin patch-autogen && \ -curl -LSs -w '\n' -X POST -d '{"title":"[AUTOMATED] Update README.md","body":"*This pull request was created by a tool.*","head":"'$__MISSKEY_HEAD'","base":"'$__MISSKEY_BRANCH'"}' -- "https://api.github.com/repos/$__MISSKEY_REPO/pulls?access_token=$__MISSKEY_GITHUB_TOKEN" -git stash -git checkout $__MISSKEY_BRANCH -git branch -D patch-autogen From 1a841b101e32179a858496b82602ccea92e04522 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sat, 13 Mar 2021 10:54:02 +0900 Subject: [PATCH 16/76] Use integrity for scripts hosted on the CDN (#7335) * Use integrity for scripts hosted on the CDN * 2.0.0-rc.50 --- assets/redoc.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/redoc.html b/assets/redoc.html index 4d2360fb20..9ee5a95c05 100644 --- a/assets/redoc.html +++ b/assets/redoc.html @@ -19,6 +19,6 @@ </head> <body> <redoc spec-url="/api.json" expand-responses="200" expand-single-schema-field="true"></redoc> - <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script> + <script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.50/bundles/redoc.standalone.js" integrity="sha256-WJbngBWN9vp6vkEuzeoSj5tE5saW9Hfj6/SinkzhL2s=" crossorigin="anonymous"></script> </body> </html> From bc4315da1edc8a729310d74eeec1f7b1d4a5e220 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Sat, 13 Mar 2021 10:55:29 +0900 Subject: [PATCH 17/76] Improve flush (#7328) --- src/server/web/views/flush.pug | 59 ++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/src/server/web/views/flush.pug b/src/server/web/views/flush.pug index 59fed1f15d..ec585a34db 100644 --- a/src/server/web/views/flush.pug +++ b/src/server/web/views/flush.pug @@ -4,35 +4,44 @@ html #msg script. const msg = document.getElementById('msg'); + const successText = `\nSuccess Flush! <a href="/">Back to Misskey</a>\n成功しました。<a href="/">Misskeyを開き直してください。</a>`; - try { - localStorage.clear(); - message('localStorage cleared'); + message('Start flushing.'); - const delidb = indexedDB.deleteDatabase('MisskeyClient'); - delidb.onsuccess = () => message('indexedDB cleared'); + (async function() { + try { + localStorage.clear(); + message('localStorage cleared.'); - if (navigator.serviceWorker.controller) { - navigator.serviceWorker.controller.postMessage('clear'); - navigator.serviceWorker.getRegistrations() - .then(registrations => { - return Promise.all(registrations.map(registration => registration.unregister())); - }) - .then(() => { - message('Success Flush! Please reopen Misskey.\n成功しました。Misskeyを開き直してください。'); - }) - .catch(e => { throw Error(e) }); - } else { - message('Success Flush! Please reopen Misskey.\n成功しました。Misskeyを開き直してください。'); + const idbPromises = ['MisskeyClient', 'keyval-store'].map((name, i, arr) => new Promise((res, rej) => { + const delidb = indexedDB.deleteDatabase(name); + delidb.onsuccess = () => res(message(`indexedDB "${name}" cleared. (${i + 1}/${arr.length})`)); + delidb.onerror = e => rej(e) + })); + + await Promise.all(idbPromises); + + if (navigator.serviceWorker.controller) { + navigator.serviceWorker.controller.postMessage('clear'); + await navigator.serviceWorker.getRegistrations() + .then(registrations => { + return Promise.all(registrations.map(registration => registration.unregister())); + }) + .catch(e => { throw Error(e) }); + } + + message(successText); + } catch (e) { + message(`\n${e}\n\nFlush Failed. <a href="/flush">Please retry.</a>\n失敗しました。<a href="/flush">もう一度試してみてください。</a>`); + message(`\nIf you retry more than 3 times, clear the browser cache or contact to instance admin.\n3回以上試しても失敗する場合、ブラウザのキャッシュを消去し、それでもだめならインスタンス管理者に連絡してみてください。\n`) + + console.error(e); + setTimeout(() => { + location = '/'; + }, 10000) } - } catch (e) { - console.error(e); - message(`${e}¥n¥nFlush Failed. Please reopen Misskey.\n失敗しました。Misskeyを開き直してください。`); - setTimeout(() => { - location = '/'; - }, 10000) - } + })(); function message(text) { - msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/¥n/g,'<br>')}</p>`) + msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/\n/g,'<br>')}</p>`) } From 522dbb1e138f9fa2180a5ccc0b9075da306d72aa Mon Sep 17 00:00:00 2001 From: marihachi <marihachi0620@gmail.com> Date: Sat, 13 Mar 2021 10:56:27 +0900 Subject: [PATCH 18/76] update page editor (#7317) * fix buttons visibility * fix title of page editor --- src/client/pages/page-editor/page-editor.vue | 22 +++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue index 45997dfd65..08856ebfe4 100644 --- a/src/client/pages/page-editor/page-editor.vue +++ b/src/client/pages/page-editor/page-editor.vue @@ -4,9 +4,9 @@ <MkA class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><Fa :icon="faExternalLinkSquareAlt"/> {{ $ts._pages.viewPage }}</MkA> <div class="buttons" style="margin: 16px 0;"> - <MkButton inline @click="save" primary class="save"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> + <MkButton inline @click="save" primary class="save" v-if="!readonly"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> <MkButton inline @click="duplicate" class="duplicate" v-if="pageId"><Fa :icon="faCopy"/> {{ $ts.duplicate }}</MkButton> - <MkButton inline @click="del" class="delete" v-if="pageId"><Fa :icon="faTrashAlt"/> {{ $ts.delete }}</MkButton> + <MkButton inline @click="del" class="delete" v-if="pageId && !readonly"><Fa :icon="faTrashAlt"/> {{ $ts.delete }}</MkButton> </div> <MkContainer :body-togglable="true" :expanded="true" class="_vMargin"> @@ -134,12 +134,18 @@ export default defineComponent({ data() { return { - INFO: computed(() => this.initPageId ? { - title: this.$ts._pages.editPage, - icon: faPencilAlt, - } : { - title: this.$ts._pages.newPage, - icon: faPencilAlt, + INFO: computed(() => { + let title = this.$ts._pages.newPage; + if (this.initPageId) { + title = this.$ts._pages.editPage; + } + else if (this.initPageName && this.initUser) { + title = this.$ts._pages.readPage; + } + return { + title: title, + icon: faPencilAlt, + }; }), author: this.$i, readonly: false, From f41c85110825443bfe26001161b72d828af56d7b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 13 Mar 2021 11:43:07 +0900 Subject: [PATCH 19/76] gulp refactor --- gulpfile.ts | 8 -------- src/client/pages/doc.vue | 2 +- src/server/web/index.ts | 8 ++++++++ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gulpfile.ts b/gulpfile.ts index 0cdd016fd7..771a5c0e32 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -77,17 +77,9 @@ gulp.task('cleanall', gulp.parallel('clean', cb => rimraf('./node_modules', cb) )); -gulp.task('copy:docs', () => - gulp.src([ - './src/docs/**/*', - ]) - .pipe(gulp.dest('./built/assets/docs/')) -); - gulp.task('build', gulp.parallel( 'build:ts', 'build:copy', - 'copy:docs', )); gulp.task('default', gulp.task('build')); diff --git a/src/client/pages/doc.vue b/src/client/pages/doc.vue index 3379a5fe68..ed4eae4d02 100644 --- a/src/client/pages/doc.vue +++ b/src/client/pages/doc.vue @@ -60,7 +60,7 @@ export default defineComponent({ methods: { fetchDoc() { - fetch(`${url}/assets/docs/${lang}/${this.doc}.md`).then(res => res.text()).then(md => { + fetch(`${url}/doc-assets/${lang}/${this.doc}.md`).then(res => res.text()).then(md => { this.parse(md); }); }, diff --git a/src/server/web/index.ts b/src/server/web/index.ts index 7b0b82eedf..c1f879f913 100644 --- a/src/server/web/index.ts +++ b/src/server/web/index.ts @@ -29,6 +29,7 @@ const markdown = MarkdownIt({ }); const staticAssets = `${__dirname}/../../../assets/`; +const docAssets = `${__dirname}/../../../src/docs/`; const assets = `${__dirname}/../../assets/`; // Init app @@ -65,6 +66,13 @@ router.get('/static-assets/(.*)', async ctx => { }); }); +router.get('/doc-assets/(.*)', async ctx => { + await send(ctx as any, ctx.path.replace('/doc-assets/', ''), { + root: docAssets, + maxage: ms('7 days'), + }); +}); + router.get('/assets/(.*)', async ctx => { await send(ctx as any, ctx.path.replace('/assets/', ''), { root: assets, From dfe10ac61f989d0b96915f870793b3066b7e1661 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 13 Mar 2021 13:05:20 +0900 Subject: [PATCH 20/76] Improve usability --- src/client/components/ui/modal.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/components/ui/modal.vue b/src/client/components/ui/modal.vue index ff5b98d39f..db6564bacc 100644 --- a/src/client/components/ui/modal.vue +++ b/src/client/components/ui/modal.vue @@ -1,7 +1,7 @@ <template> <transition :name="$store.state.animation ? popup ? 'modal-popup' : 'modal' : ''" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered"> <div v-show="manualShowing != null ? manualShowing : showing" class="mk-modal" v-hotkey.global="keymap" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }"> - <div class="bg _modalBg" @click="onBgClick"></div> + <div class="bg _modalBg" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div> <div class="content" :class="{ popup, fixed, top: position === 'top' }" @click.self="onBgClick" ref="content"> <slot></slot> </div> From b0d38446d7ae67d9002809b1d1ffffef98ed8ecf Mon Sep 17 00:00:00 2001 From: okpierre <1679025+okpierre@users.noreply.github.com> Date: Fri, 12 Mar 2021 23:19:49 -0500 Subject: [PATCH 21/76] Update index.ts (#7343) --- src/server/web/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/web/index.ts b/src/server/web/index.ts index c1f879f913..7b087bbf5b 100644 --- a/src/server/web/index.ts +++ b/src/server/web/index.ts @@ -83,7 +83,7 @@ router.get('/assets/(.*)', async ctx => { // Apple touch icon router.get('/apple-touch-icon.png', async ctx => { await send(ctx as any, '/apple-touch-icon.png', { - root: assets + root: staticAssets }); }); From 9cf811b048c03d093871b21c0a357813746378af Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 13 Mar 2021 13:21:33 +0900 Subject: [PATCH 22/76] test: Add apple-touch-icon.png test --- test/fetch-resource.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/fetch-resource.ts b/test/fetch-resource.ts index 6f976faa76..bd3a325dc2 100644 --- a/test/fetch-resource.ts +++ b/test/fetch-resource.ts @@ -73,6 +73,11 @@ describe('Fetch resource', () => { const res = await simpleGet('/favicon.ico', 'image/png'); assert.strictEqual(res.status, 200); })); + + it('GET apple-touch-icon.png', async(async () => { + const res = await simpleGet('/apple-touch-icon.png', 'image/png'); + assert.strictEqual(res.status, 200); + })); }); describe('/@:username', () => { From 9e634360fa1d95f7d5c1aa8654c8f64e459ae07d Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sat, 13 Mar 2021 22:15:20 +0900 Subject: [PATCH 23/76] Fix Schema (#7347) --- src/models/entities/drive-file.ts | 2 +- src/models/repositories/antenna.ts | 18 +++++++++++++----- src/models/repositories/drive-file.ts | 4 ++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/models/entities/drive-file.ts b/src/models/entities/drive-file.ts index 680a40bc06..698dfac222 100644 --- a/src/models/entities/drive-file.ts +++ b/src/models/entities/drive-file.ts @@ -77,7 +77,7 @@ export class DriveFile { default: {}, comment: 'The any properties of the DriveFile. For example, it includes image width/height.' }) - public properties: Record<string, any>; + public properties: { width?: number; height?: number; avgColor?: string }; @Index() @Column('boolean') diff --git a/src/models/repositories/antenna.ts b/src/models/repositories/antenna.ts index 0f0a5c0171..84e082bd25 100644 --- a/src/models/repositories/antenna.ts +++ b/src/models/repositories/antenna.ts @@ -56,16 +56,24 @@ export const packedAntennaSchema = { type: 'array' as const, optional: false as const, nullable: false as const, items: { - type: 'string' as const, - optional: false as const, nullable: false as const + type: 'array' as const, + optional: false as const, nullable: false as const, + items: { + type: 'string' as const, + optional: false as const, nullable: false as const + } } }, - execludeKeywords: { + excludeKeywords: { type: 'array' as const, optional: false as const, nullable: false as const, items: { - type: 'string' as const, - optional: false as const, nullable: false as const + type: 'array' as const, + optional: false as const, nullable: false as const, + items: { + type: 'string' as const, + optional: false as const, nullable: false as const + } } }, src: { diff --git a/src/models/repositories/drive-file.ts b/src/models/repositories/drive-file.ts index 5085c76e92..bc1e37489c 100644 --- a/src/models/repositories/drive-file.ts +++ b/src/models/repositories/drive-file.ts @@ -197,12 +197,12 @@ export const packedDriveFileSchema = { properties: { width: { type: 'number' as const, - optional: false as const, nullable: false as const, + optional: true as const, nullable: false as const, example: 1280 }, height: { type: 'number' as const, - optional: false as const, nullable: false as const, + optional: true as const, nullable: false as const, example: 720 }, avgColor: { From 7d02b36092bbb4e1887abde57500e3aba13497f4 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sat, 13 Mar 2021 23:22:54 +0900 Subject: [PATCH 24/76] Fix assets test and favicon type (#7344) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix * koa-faviconはimage/x-iconがデフォルトらしい * シンプルに * faviconなど --- assets/favicon.ico | Bin 0 -> 90022 bytes locales/ja-JP.yml | 2 +- src/server/web/index.ts | 2 +- test/fetch-resource.ts | 19 +++++++++++++------ test/utils.ts | 2 +- 5 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 assets/favicon.ico diff --git a/assets/favicon.ico b/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9be1ff62956cd5d1e21540bee043a3c6c144d453 GIT binary patch literal 90022 zcmeHw2YeLA+5YbJrj8_flLVso-a9ysdu+!xHnAPsu>sSIB#?xJQ1A2(q8J0FOCVIJ z>H-%W*Vu6q$A9OGyNw+T^MBsGn`3pHq&t-p`M%qq9?kCF?##UJ^Ugc7yYK7@f+(m2 zZ*Ku<XW=E4AWRhmp>t>R_I~U?jD6$Bo3}$J3c{oQf)EvD+181|%EtsD6zAYj<Y3;9 zL_oH77qOk6Ahz+f`y1^PTl)y2zt`>GeLV!BV}Ky2Rf6J+gMz@A1jT3j1(E$Kh<=`e zV&VX4pLj0K^7|)(csfzgzP3y-v3#L3&e4CkRZ!$5^j2KRhyhkAPR4aoe1B3ZN2LE> z91_G0^LvOpm#r3eER7Je=XOv>J*iXWEO}nFZ)uu(=fVk!@^$5k$_-3H+1g#I4f8ar z>TE%AF+~tlU-A%g!g7VZ5lq6aWt)t9mONlQyy8dW8!H&nPgPeo-2#||>Wy!y4z6sg zIvqPuaXPlIxNdHUus7^8VP80tuzOjB&+Fmg{wJathvuy<%!;d<KGt7I&el|9{7YGp z@wu2An<4C3{)O!P%C>K;4DWP$HRI4-+r~JIS2x`Bs>r-$s>)(IKvj}<Q#`ohmas33 z3HyPZr4?O|M~C+)Okf;__jWKnV7j)I1yp9S0G#Iwcmjs<45m9B%fx-lt>^d5i;Wm` zF@<p$KitJkAM9XluB>C7a85fQ5b&$WWL`z_%<D`n(?M4P<&m?rvVVTu>@j5-Hvu-f zEd6_rzwBmhu5D(aI47h!n{@%&W8LRm5;Nt;G1J*K%ycG>sSZMiyBAk?IS|%;Qd!2P zN#z;U6HC)y?fmW**7wR<)(_|P0=idc-{@4Do@F?*X1l&1;TFIMy3=t?`Fi*jzfH5d z_B|OM-!FGXVecc$o*Gr1eRDX@9}M&ddR1g@2|5vNP#=!;)fOZk))XZ(4s}6ddEdO~ z7e-x3xH0-tA{%jj&A-Q0WnUVL^F~r0RoTaS7AFSuuF4YB=Tik$Zd?~tQOX5naVjGy z3X-!%pG&xJN@eDc)2g%Br1JDn#+0Ooj;+d$7*&}SHsn%j`=Qr13ZrhG5)ABfK~=d< z5YHrqh()O@fyF{zLYv>7j?qr9%=*KO>g=ScWf^0}UtK5s_U}2u^uO;GhQV&9es@?f z3=9zza~>2F?6QD%At>M8A}FtH5`^M3;hqag!f!v?COq=(Az{Wl8>F`4`M@Z_>)|od zdX0ag)QSY*zEOfe?Gekg(hQ-)!=r_eKiw;sy0#X+LDMBr>()p|)cuc+(F1hO57*6p zrZcw31LUuyg&dE2xhpjg2)}Ex;CJM=om~86=S!!ml-3*+Yi3F7dK{wyG?x6uA>E|? zI8v!x|7r)-p~$hS*H?~ICchX63$+-jHJdwD6o2~d?en9a?xf0mWt=Mg<q^sSkN6`` z>3m&jy7|07Z;hDzd=GK`>?vZ#OM`?*MjPn|#czh`6=##57fZ6=63f<p3w$kJ&c39` zjlWlcdYMnI{lDVA5qfdUqF2P6uxsL;@Na>y#NA=%6zk?q#rgO{_xkKqlX~~!<*EZ= zpQv6B`w{7v>b*-hsN-gJP!^^<uP9&tjflQV4)kB2DNn|Y)<RdcDMAMs#jT6}fIjR; zYhT9uw4Csd6q&P!5U={2B~R-QMO@b(Ucqi7g6UsdzFAqm?rlZYMy9OV0GLzrHr7kB zH>m<WRhsJccPK#p?}LIe;z?gIXZb~IKgRn*O815{ar@$QP4{+sk3$i!dmLHGJY|r! z-&pyzs(Ryp;KLYFb6;y&m-bbxFH*-p@9`{TAUQk%C13;;?0rE@e>u2DKWtOJuwzM{ z_NCvn@;$P$!1s6*vj^4H%}jL_<==tLOkK6%f;wiFm!f-Ht@=>-Fx8pWFDlQ({z0*C zX&*JX#W<oH%Kv+9@}vAS`q}pd1Rjqn2t2up*@OBmlv4{dRU6K07CziYSGXoZQ=a~% zsxtGIq9XIASeE_?#vwDsh$lmZJ$09FMEUJct}1As7t8EH^ESpQwc*O9_YG&`GYn<v zKk6zonHEq33IKKZyJFw+RAKkBuj-QDH0S<+E_ty9UC`&-gZ7=R%q}o)(O=!jJj>FV z2g);I?4SpD9h6j$q6CaRmciCA1|jbE(|C-=M3!{tk~5xj9|-7nCa$1cK|Heu-MbiP z*@6C@EzG+-llfx2<Bf7@+(hFq13<da0N{fua${)xV=31%9-}cC)nn)4Gv2R05YV$A zzMxleBC`kmpSLr6@UC3T0x?eWM|pmL58wrO0CHVupdZEImF7B7xg3`AedawF(62bL zp#S+~W)Ft<cQ9Lcy}6OKMcHjpR%?L9j;#P1KgxAsx}3s13ow?;i($&W;9(D7Q@-DV zhx`YhPcFb1h}nbjgPlwc&v&=7_Ru+vIXht7N8`>m0F6Cs=%OT*d7p`4-g&E-_vsjB zJi3yJ5)YO!twnync@O%HxRiQ&WNA7ZS(;{xUrirjoNIvx>e8-iEeis#T~T&tpd-*8 zkn6$^I`_(7&Ac#%_Bw-9LM+oB#PvkZ66k>H0PI?vFWfW4H0E+@`Z(B<85u|knQUxX z`ZbS_cQcOG(0g~t-VNmi15^hZH+KTKE&?kun0H}3^FWy%Xj5{SP#@K6D0eUHaX+}& zwJ2X0(@QtDFrn|Hip;krS7kGfNtIb&jK7rnq~|9&%meU$ZyW1@vU@?#Jy0IT+tRo_ z2r1QpbkQEy5k9bE>-e3<@HWt&ieVy^PeA$NbJGmh;IAhZCiKI&Hgj4<X8zRj%-xeN zBu{NKd4SRj*9pGRe<-dq`lEh*QBH55ClCtAbn#2;iz#myPRA7)3gT}8%svvB=4ce! z-XbP!o1d?Eewu+gufXsJ1B3U>eYX3dMX4eC=gn)M^i&Xbi#^`jz<Ry8p2qlW5XSlg zfPO$9lug$i7G2!xifa$=#OF-fU5nf5^W#tG3lo`y0!vEUmUSJjOVr1rnfgfN8R60K z`rx&H?9}^Y_=&z}R{q*AKa%x36UqAIMpT66%o{TrWsC&qT4g9e*DC`7x@O_J=w6<2 zpxyrEexbO2Fyb0nduU~MT*H)U3zJx_D8g@N6Y_;=LyQA*S4Ivgj-~4wHu6F|(s(ws zcr_c8A6-1AI{T+FC~Fij0vHAi0k|&uRAv=-J-Mp$5L|Plf=5~m?3$oC9y3x?nDi09 zhO(Ec&n6!Cnf+U@5yf#Q$6QWg<4aT7gfal>*vrYNOTy1%DznPRqdZ;*y58ct7+974 zPG~`V|8eXc+)B7TCROLHk;ba3+%*s2I_ygouEA8rDS$l1dVXR|+xFg?ahH-_$2A$l z^%<L5l_{l@D$-G(l&?o#PJ3d)m9_t-I*gOm1J_)C?RP$T@^e(~;b=*2`9IZPa%fXj z<|n?SEKd7gaW0L?f$PF|lt*KFOk`gP6D}sroLZTAeL8rafik88)2gy=O)5=Y@As#B zRijJOpB!JkwgPqd_t@&S{~lSDRgCNK$s}{gI~(Q2TG#15iSqy9KFEUKsWE<?B^IYu zi|5k+4Sa$1JlsDSP2=p}vtnV$)_MA=<rzy*_9ypTUH1dhf1(|xk3A6`IGLRj4&u1M zxvTw$Tu2!>=zPkc-p8VSo?(9%g5EM;M_LQDS)u$DUmX>6NQEOYfx_v;K|*dqFN{S! zl&lJ4w7&=i`0a6)Cv<*fjAF|9<dA7)8RMs3O6@aj!(7c%tWe6)_|#-!%sZQe(eG~+ zM!dI07*dPOHpN>v#f85Ugi6%43c4a(%&akHCOuBbjTP?yc!%)NKlTU@e7Z}RfoplW zO<6N@ueekL;D8mdf<m^EMHUpC-XeT};-3b76{3U}kgjy9I^(TJx5ip;fb#lWh!Woy zqb}rKn7|^~*p+N$A(cxD-47iPL;=?O6tu&=xHaI`fLjA@4Y)N>w+86;oE&u@-Sof8 z%ck{)EY9Y^kEi>abdOXH+Q)xyNL?H`hw`U#8e2ZkM_zwguS*6IcU|cil?AvJD9B;n zFN!=5{M=-VyuS9z=lfKsV_oG#21&--$KRIW_CB+mR3<GXH{~h)uA1E63-|2%Xb!G? z1lNMIHOHb~(CiDFs$BD27bu4w1*t81;^$KyjalU7t5q`w`zlvG(_g!G_G6k&ug=k| zpZlC9<;980IS+J1`C1gD#vNMX$+mn*uHbTL#ku!)5VK#tN8B=RwzzHn0&&yaXT&vs z7$^+s<|*Ytwk_g;nZhhPPSmOPg^yOAOWT2G2A_$gYk$BqgB!pvSpP<GIqNOu*`x^7 z>SsIMKL3t7;3ub++vTeidR5xXQxrRwzJ_Pm|G@KxAAuXd&teWedstqj+`Mps>QDE! zMG0gFavfM@7iSFdR&JR$OSym9CFOyzZxsi^ZU8s2eqH(6^3Rkxi`OaRX3+?mY@6~> zDRe5mGI!0>iqh;)#ESK-R+O*5rMQ%ttKPn-zwCTl9`KWci^cBL1G}0On-{zSSwFwy zc?W;)VNP#|yOtePu6T+bFd-Phq614l$_GdK;Caj{_3Po^tMUA~R=l?SqB`w`!N@}m z-qos;aStoX)_z&5+%`IaEu2-YofB-XgJg3K3S8qb(wh?HYZV*kKP&7B|E3<<aqrC> z*MRtl!>V}?wWsqfZI^h|Xm>7pNp~pXN9~~qhO|~lzaLnBN)z$>PU=nbyDLkxE37dU zJhq~qRCzu<T{S3#p0&yC5StDzX3mt##6R`}?;mj497^|u-%@N@v{I!K@u*DlTS{I0 zAA$`BBd!|oEX+atqTjQ0sp?$n3dsJe0?)}D)&V_JD_{3F&1(^ZP(JybhO_|%o~^4D z+ZRXEGc{o!acfCwy*7{y?7=g*UCZ8AMb0$018`v4z4!^!8!NAy@GQ<@9E!NCD$Ood z;u)ROsM>G~x|?s2pLF2NLi$pu7CaOv<}5pB_c@dNnV;?YUOW%kz3gY@`nhx$fb37{ z`C3?%*U?Dk6pr9IW##(+aawkZzEqd9Qs6|X-?WvV<dbByq<kBj(jsk(v9p5lobPW9 zl%I*47Q`Sol3(p}Fg(TQSQK*-HxUO#xc!XQiFI{J_6A*7e`<rg4RD#MPPfM!mNsg8 zoFd`*9+3a@hRQE&UJ!55zt-<iL~5(!(ag_DNY96zklCs$JXcniW^d9C=uBfG^21za z>Q^<o9_>7}v+r%ApE;fsM9(T{Kk0;yBYi0#e^1E&&xXn`ZeEZ;J_LDb{SU854ZySL zfRoWqLH#D4Q#TNrvh2;;J{?Iml1mNpb=q|=_fhAsUZOgeuv2v*saSPB@sy%CVV&xD z<V@AF-?gLTEKn-q8!taHljLuGWMyg_Jfm_J4V9hFBl*?c+L$1ZM*GkRUv2KH*_zU{ zkJJ?zx1{ITcrGDwRAgKiFQuGS?^`kf$5WfpC}J@VYb5_%{<cRWQ`;dHpk1zWu*NdV z;~E`USJN@2SzA<IdQWvoTW>>NOpLB9{U>^kPtVXPo`9aC%k>~&TTz<&p*m;LM98ld z!=LDjc*#cDKbOD#v8dFJh_iGSwZ=%)7CRx^uPw>SQ4I_VG#m<l(NL21tsc)=c|1jJ zI*{z)Vseo(^2vURWseUNa+ZH-?)RzhcM#_Ok5T*Q@^?BBo!S|3m(D`_HsUlI01cgU zb<^MUMM-;27m~j;;n}wl{8Ai+9%+4gP~^o{DK^bNA?#ZAAG86>*vEPK;drF>-{s`0 z)F3>w4$5~9c4Jg(i(RqVuX}S7GhI$+o`^y40QVF}LNOeY4v^NR0|mu*97LS$j>R;V zf&9=x1Ni6i2c3#Z4Z*W>XTf8U9Z$hK#V|t`rZU9Pl%z3l#4dOthJeS?kPfVRsLdAe zyjF1`nTZrzvJ*O>REqJD#k%mA6Wf%ILwu09Wqtyezguo>YInp*I}3X;LbdKAxh*k8 z#;SG9=W;6Zh28r=Mv9-II3Cgg#qv063(B)`7|W9lAihM3cXC92#DY*v4CKdPLbCtR zyw#~aixQc$(7%f~B`2W6u?EDm_(Jwpuwg&QPVqw&$5fjR$QCHZ%dTI5?I;n$C$$5T z$q^lxW7BKM-|I|VYH!4#_bzr0_F}Z^+=uuzpVAB#fS98Iw0VkMq8KWQ<*H8)wfaWn zr8ofJN4`L62NY*ihaK3Fzfb;}RKztfXHloD@))-F%<*hq71^vcxNi;K0>M4SkU66R zTU(%hQUhP0I-dkzK(R$=2le>^JM#BG8=pGhLJD&hwlQ*bZF|7(TVGBy$IzkP6u(As zbQHtptR5s=K<tX~0^+WU)-dDw1g1T;%Hj*^X$La-aUmhK|AB?^se>=2u)&z?;SB1M zSN@yn+7=dwey%;*e0#`F@p}|I*H9gpkdG%~?R-w*nl+aYjkvp2O#8Z}9mr$;Y}-Oj z`RUq2Go&aXbtvr8S;%7)YhA~D8~=(d))De|L_gO7F=Z4xNIKwgj85qReFnuDdY_7B zK8SPlf!rLP*r%d)VC@s__<}p-R}U*rOc{Z`*h$<neuy|td!YDD+K<%d$_Ca6d3S-o z>kOGGZjs_ADRz<T!C_lK9$tlOm^b+LhTKxDC*~Y^%V0Vl$rR)V2o!&6J1&sP5AunB z<%n}h(WAhRllay|^P%d17vhCGmZY;FaMl&F^O#9{I;htc&~Cho6PQ=-D%g6AG}nse zTiFBMzZG^M*3n*{c&Ge|kwpp5V2;)=V^Gg}fw^2`>9~^gN**I?FZDnSaXZAbhM=v6 z!0st_mEuNu{3^wc+S5aAw%~*Do)=<@&9WoDmSSxQ6lZI*?unRJHO0K{f)1!X?1LT9 zHi2SrY2Vg)H2#zBrwl!`qWieAj6aWueK~~k%o}4brbO`=-P<Yb0kONJkHG46tQ-2~ z?%=H(xTpDU&gy{s1wZ&^lF5SrS#QVp%GY_^?`rtQIA%nDpxlEtup8q7DfZVQJH-Ig z{@d~^RVJMpalZ>ER%ZQz7+{8!Q7VUg-^B8abHfj>2%`CXR#42cW!vxV%`60VPqD)^ z*N?~SQrxbydI*5;@T9nAk{#Emw`H$?U5|U3BH2I=bU-#B#bDo-ADth+pC%4mKgS1i z4YQ|IW&a0YwPA8)*4yI>6UM@Ch@OARVYfl@=P)14j||4OO)s>OUXYz)mMLDCbU^Xb z&gh^G+L{-|Qsdr4UGden@z&ODT+eFA7f9^@oRba+7XBf>`Cg(%CEZgVx@Dg4l+yI2 z)2gyRm{z^^2F0XuVEfytm0530D2N{h+37ry;>6{kxa}@i*0VmSH)4bt=0-~K()M&v zpB^aQ-y7FtM#yf297gEDcs5>^(t2GmA8&xoC`o3n18NIo4_oHZ{SQfg$Ra4rI#3up z`fJA=TG40n`Q$%MElEqBT9TeM<x*<o#ItMe?VtEuD~s$3QKh^?{&w$dW_`hXKgdpV zR4G=QV#X<Uo#Mvr>A_xmpm`2{#fe`VPsjaa;Bw>oT#}bkC!wSGd-4OM1D<Z0E0@2< zb9Bh4H$F7DO~B%NL))x)sIT|4<2nfeM*i&E`V8Ioqn*+`0h%ii@cwqz2jiT9;B5e8 zrC4=}nRi47a$7(@`E%O~$(s%Pmrv9miJWQ3UsGm4?0jP(#sBYEz=Z9<w)wdK^=drG zH}iixhC$mg<>`LG2j;I1IkNatw_{5`13vF|bjdp*hZgSYy6xrrJSOy_>8~nLsZfT# zxq$_Ku${HVJc-`bh|`DcgCIM_$y2;O%^Pq=55ZO0w>n);-RKeZln=_FMby0(Hbj3m z{tBPl+~5>#MU6Q@3fO=Weo%90MLhB+|3{#4kS-)=?!!G!EPuOKZUpO{x5A9m=CoH{ z1nYS!{HxHzi{snOo`DJdf@%cpd<blnFbwNMAv?_zp!o`Rb%6bKwTGax^n?DJU!^Er zQPk2S0qHrIs5=@p4f7{H(_ubEqky>>+WdG%b1`(etGWCnyQ=%Hxs&_kuK0xJVGKAM z%?1{%k}$}U(!T!rQLOLjmH+8=WLccg^Z}ljPtFJ<fZ^bu=1I_e4MLrI!11<v=vJOl z)aJFYAk;-r=(Qv}uFi-WNe4>p$=IiK1xa7g+zwp>V0)f6H_j@5$3+hW^vhdul4KrS z6f4aG8Fns?4Yz>ShZe`NA^2@jVGJ9PAN8-^2Npd<^D{;RG*5))XwX~{ny10_;HVBl zD>AROKe3AX6`F-k@hBv>dC}4(D)tWu)}YP{A;(Wzn#V%(Tqt!G{5-S#()hpcfyL7Y z7sh-|a!T`IE+(*17ZVxH`{x*ieKb#I__;M~2+fl@6LlE#bZ*qA18xiQ_8|8Qy(+W* z(FJ`A$tZ!MI&2pMg)RxAD(v?jnllM$m{)Vt-rO3e_T{ZvgFL17KR7RP$#BdAA{obA zN|NT`j4Mf@xiAuFeOyU0qxm}0yAYs<Va2QeKD<2hLz*8$??k9i5B6*UedJf&&n3;Y z)I$_CL`N-)xlM?wtl0sWL$qIAl)}tNwxoR9ku>?1`cxc`KTr-Yh>a(?#zIDtc|utl zn^>Mk?`)7T5$Qziqd7{X3(^tZ3-R-avWz$Q93e+^z~^$2e?%X7y~jno=b>K^9>c=E zb&{ReBD`g{P9xcfYS+@P>cXS~HRd-p2t_I1s*XfYL9SB&H>xNulK7>0SS0gg+@G6L zNiffgqLlX0{3y}`=?J#)_0S6`uR#9mm@CDc(Zk^CwKsc}q-A+V&7?5}$!_*{bxV)h zN^QfegF}&nG0*Ew{=N-oQdME{CFS~gZ7lMOqtC8>0<vE>&j-tp=7UW|pN9EZR?s%r z1KGy7%gL95wk~XoxoSldudZVg07rE&pe$p5`%SL~lDG<k#n;y-JOAC>MkF05kFTBq zx&MZ^LuQsADXrTB=D}HxyROWOe-_zM9;8rl*n#C;!RwnOpCmW>Cz_YW$Nthfwn-lr z9sE50eBwencVJ<{)N$3>pH71Ok{;@@g&`H0r-Jr}Q5>Y8G~xwB4O#FmAuM#RQjr(` z6z14{DW7Lo$NDY2mt~V8@dbM82*poIdZc_NUPyis^YQSm7PH(mC(jn72hzdhvh+8G z?pj3lBk6F+x#S1%ZkYG2I<R94=p%m}T9Nri2(FvyJcOH4`B-h>q9?b3@(o&*A}{gx zisH1tV4fc%C~RQacU@7KvIDVilHCcGF^0m@zxGXd)&tjmQIPRFk{fdZYk>Aia}+Bw zPmepbswb7r&&9f;Z|>^eBg-;VCRDHe2>#%I&@cRqd5b{yS0gL4DhFLoo7ZLc5*jyB zrvqQ_fWM=I>spKy3SqVb@mTC2yf<e(Wc?WUMmV2-ol@YRnEO~Ho=JRC%$RL{+YHGr zQ@z?-dhLhUH})Ch)XQlzXW%_QkpElY25=Mj3G#nDtvoYf-0|qnmNKOr6go2xm^LN| z50BFfIu_lr&)N8){Vt@=>~kUY>0Sj1qr1HpMq^sZc`0dJYaZ8OR}(CH^Vopi2_pRI zew}bAItcSECkbbgpFlcAIJ_!U*t*!yS`x`_$L=)V6pA8gMI3u*MQhB*oI1Tc^Ce); zw9@p)CgraQ?efF~Nmx2gWunlf+Y8#Vv_X0}21EeMfJ}fd7}N9BrcKF~c-~Z=Jy3~e zu4*lt%S1Ai1Fr))z#%~57iX$HrK#h!=HdRqtpT?N+!}Cez^wtd2L5+z0Am7&mQQXl z_Q{RwH|KdUHp}BZZLu=)R<W`(#-K$v@uEg3iTy_qqL4mw3;(c;`@x*{``Mh1v!sDv zn73OGH>dws{)<s&NfpKBR6E|Bdh9T#?XH?ruAhFtnzx_%$($D5AU&sxkvG%DSwhKW zcB|y`<KO0WVC+bTk2GIjXYA{*uY)D$mQZ9KMFrqW!1|sDX$Va_-HTfTZVk9K;MRa! z18xntHQ?5OTLW$lxHaI`fLjA@4Y)Pn)__|BZVk9K;MRa!18xntHQ?5OTLaR=BZ@0? z1pgb=<%rz;nC6zBm*Ys<*t*!3L9v2$!S?vZmfw=++saax-26A&bDFaL^1OL@b*1v- zovjBym*+3d$3P~OQjoJf@MHKnJP%6$n-+P!xh!0^+T@r2&d-tO(-`@AK0IGumOT#m zK7I^8hv!jWJ@|3_JT5N<$Py^dmO#P19DE-?hUy?MpXZ6)cm2Za+7$BhJb0eY3eV5u z`80+Lo(Ip<US0TabgaBA&Nl^8Q*bqbf{o>%ee^ro1RYPd!0o`1vT=BeE^^uHlV2`3 zt#h3?BEO?T%Ii}X7q)fb^{J~aJa1kXzHLu#&N<~vlMyHwA0N)P3;K<Yp<{XZwsyeF z!XbCf!s}!!KiM*upUYmG{Is9TFK-JCk>9pHln1pbUKi3YFORsOxoRA=&41&zAa9do zOC%>RgQs%Yd3z^0NN%YNL-bR<j0Yp;8&0fRug{A+p*x#Ur9TswZ#cPXoAJoXDAT%^ z$0!2ni|^9$ay#Juz_x50+Tu%X@{_K)&3lMOotH8C$)U#0bHk0>7i`w=T69LYdvT?H z=c1FwZ3{M-HqBjP41cUY9f$KsUnD=b1Cn3v3wfE?eaFIo<9rbJ#2@8l=DlNpkC^hu zY2uCr$zsmpWB7jIWpUrqKO-%Xws$N@q2KXsdz!0Hwjj3y%FmVuUKZ7n+nH3BX6nEI zectK}u_Ws|p?n=CpVWi0wZCeLl1}SaKhqm|$@M@o*ee^mTWqOI*OJ}qUYH()Zzvwb zyF7oP_mkHLaZlKF-Ht^&w2zIUcLH<WlP%e61Ga+8>qA_~xi^WE2Lvj%FIb^?ZTY|O z?E}19uO85G+ZTk3<NDCtHg1o$JlJwCwRuGwUoU(cGfFI5_lsDLZznVWm<NAc`}8FG zu0w5Qle{gtQ2AVbxvq^G=uEeB@lIh6y_dcr2*0V{u_!|o+JV|2m%lz6uvZ_c9V*wo z@}Tm-@_#B{4`WW_!1BK<F<lPzAbTW!<u<^_7F0Khd+noRTB|RlzuOSbZO>7ZWPfj5 z`^wN-$|l{nlnuyrO>K?)Sd%{Nv0jQjVgGjMn+-L;<AL{C!B%8Y?hX5^Zsx=e$X{*) z_WA;OedM-iQteo{LiKw1O@K85zo@p%pKZef)q}u&zR{5MY;Sxo<y%L;8RF1!<?C+Y z+ZFUK1#W*-9@hcM?P?p~Ws?1I`6b&otbO@Gytn#SIfsp17x#w$WLPzmzRN;3!21F! zm)n1B^)YF7EsE3Nn=eg<?epi@)PviaLHok=w&4DIhd8zQc8nwE7;+X*vngAy1N`K& zh4d}=`^1S+zh>5OaDS_@eA_#pzIUXbdtX<SL;jq~Bfax+7}e#D`e?Sydm7)gxkcZ# zX+r!*v+k7{D39tvK2P%digtdUs!N$4I>KXvk11dGi$3=0ZnCnu4!Hf}r)FPxS-jsP z9T>Dvj&BQ&zPHC+lkCG7LXorl3)Rr>G~b=~!Q2OO?ybH^v;47O`sPtx-#cpXZwJG_ zQ2lNUjR~n9X4^lTu-4)41l98mrMmJ_m1cb|dKu`tfXXJ@<86R=am8Y{?W^`IuWk~a zWqp5*{G>4?{P@l#xe$`~17!c)21pN9U##E1e7hdsd}=oASsI7($Oq_k^B-uZsK7VS z8o{eG=NgVjzhEhw+McWX0M0*^E46)n`im25a8(<JjoEMEU)UYS-~;LVTHFV48{lnU zr;U0t)bPfNUjf!^IK1Kq^~_1+0}Q(2<ky_xud&A}%hr9b?9qYlSIPT9?w7H<Sr;mY zw>8Q4DffhZ>`MMsJD0qLI@tOEvIFvmdSlL#h4|(fYi<~~FML8ZIK)d)x&Ef~ojCed zoQq&Q9Pt3jYLVGheE@I!oPWJO>WQ9Cf2YRL@0rMtqi^8Rw@`BMuH#+Hn122PG#2FY z6Zcl%?{O&NtOve#*X(#bEXTNS`6Oi(zLV!-=+37chRoFekj=ZQ58(WBU#r(`Ul7p} z{OdN&rFZ<v$Ka#~@<#^GH&%Y(c_flGI}We7sxMC3*wF6;HtM{p4Zn!KCh~!<90SPt zm->F?o@Lisf`8SHMb#GmiF4v!;$P9Ol?T4{_^VUjdvx~uko@=~D?is>&bZVl9;+Mh zjZMor+83pF^%4){`{){o_^E55-|Itt=TkY>u^+z6^PiUBU)-_eC)9(F0j&HRmpszZ z=U61Zcj*$`P?u%@wWatsq`%k=I<OrB+VW?QZ{pA%Y>%~<mz95INLx>-PnNz<i~d>O zUpMu-P5&?KSi%%u2D%?g@>3fi=sY5y3i8Fb0bB)D^~PVU-~PPPu<xZd^nBx<r6Vo! zQ~Ree8RrkH)^*y)xu#<{s10-QeU$QJd0v##!ndvb<JnYTOY*Ou($5$5lJl?kjCv}_ z*>Alz=J<wk*A(X&Uk{&Xk)QKV+;E;KrEShFaZSe&xWAOA{5QEx(D}A`Qhs!-R{P3t z+O;JA#@~(fM?GrtAN^ENs}s@8RcHzx8=8l~KL4CE&O7JZ>LZ~ux^A)s>5=P_?1IiC zTafz%Zci5e?`>KBA00*flN?+&y%+fR2mh{u^qpvY^SLEb!@(NupL4}|r)}a<>KBpH zwGxe&sLv$OJ!B5PkA9c#X(FZbxE=63c}o22w6ENK{ClsO85D@`UAqeEtN2cK3&D6G zY!dNc;fecgN_m^7--u6%Z~bovxAsVyHPW<m-m`|I5t+KvG5Ln$D>i%VUHGcU+83u8 zUz!$#W637yc)1;LU%<<d@lW?<ntAL;V@W&XKck)Y?*sl@<NH{yLSw!Y-q>?3{M+6G z<87YyQ+xLCi2nTm{h64v^v!MgcenXB$m#pr9O`rNA9-w@^RUw2lj0D$9dH{X-mSJ} zZvS@||Gul94s!Ne?~OUWu^cw$e1p0D^Zt+XZf*0X`S*wD&Lw=P2EX*pb^hITZUYMJ zvu6Wj3u1ZtFCLp`-%s`fkT0Nod7nV3Ui;!b?OSyGXQ%!9#XKF<HaCX33XS2fvF950 zFPjV=sEv~i5I>X>S00{Q=RB$|OS>t3M;CnBvjJ`g+y*G+et@UwAJ!^NI(pW0M>(43 zrgdmZ{tb5cZx#D=Q2V@C<|?S)LjT`FFzj7A1-y`tCT=)9JdQ?e(BWGzT71_<3r^)W zV9yT757gxgG{x~(EpgfMvP_y;lRMsR{QHCd4&dKa(7d&UwG@oI7EJ~3)HeAz-qSN@ z;Q~FrO{&AU3UwC#<u*XJV9y5X^96?ED>vAdrJgyl6XLqf&*hqX3}|QkXRrPH$37F( z5#N<`6<UgWD%Y@W!E|s!<20Hx;cW{4-CzU0scHcCdJF$L@J%2aklO*-fIT~?%NH0k z{z(4DTAuo`v0bF+vGiP4;-?v}103W(VD&RWo$yUrS3&bO+J6hduy5Jp;Dhdg`f8q^ z7Hlkz|IUbST@(KXe6LaujtRBdKyALj)&}^vKrBuBS-a$sVb(I|+&4ht-xWT<F8`zN zxc}37^{k*S_%5ug&{Et}xw?{!Qq9woLiAhbKZI||er+mAWk!6H+X%mJ0Phm;O)I$# zG}ad=isG3j>*WO1#D1OCo96AKSPYlgfIa>nv%`Pfte~#=Mz*V<eS0fwX_$(Wm<itz z_kiy=q3xT{_Ko1*2<{CQ=xywvq5T2*0yW|)g{}DB@OFH6cqhKOyjyMq&2&G&4*z;P z{I^;2hoB&QSJ_q2z61VS5c-l#=5ZmJd7|xmpzV90?VG?6f%uo(0NFxQ`2rPvYkC`O z0MB8CT{0VJuCaiF{I>=F!QkIjXi4sMS2r-v;zZ_oIhA>#-}eOfo@o0X;NAmVn=G)k zgQoBWipwcXv6a5J4LjgAK(TqvH5PD!{}6mb-Br+DL;G(bcorovFZg|L_<b+<eJ^nD z3GO|?JMmwa4LIuy==zX@j}KL+W6=*R1pn{@J7EXA;Rp7hFW3t^*hj#1!Tu%!<wbc? z{!|Xiv*-WYqy2XW|E_}W+BVh_FkMJx-WOAt54iUM_ugpx-r&fK0Pe{KxE*jmKxtF^ z0yXB-3fn9;unT=bj>QHz|4n5B4)Wh2{@I|=!UX0jv;_Bt%C*b~eZCL+d|&kWKH%O5 zZJ+q}2FJv|tqnB97x4Rr{5p}}FW`Pax@Uy4l{>)yc0lq0umNhL1a1RO;REdOuX}9F z9oK&y5}pm}0l(-f=-x&9cOkUzY+>FQ@B5<9_d}oWhqmtv?tQ?$4|pfYZNSzJn!^_; z&nGc)+kDtSGxh@x^4~GxxuBj!iOf}SCHGX87i4RNKHm?1zZJOm1NVO5-j`tEpWA@k z4&?noUB19}Y{>5y)aMJ-C!=fj12x71)OR;>9N-}Toxp!@jEP+Z{kz+lt6(b0VE*Xy zTfy)9!|%6(?`;L%iGM$EPyEYmfZGA@51QK-sP@9=%lm<*91A$ef9J&KgZi9HVy=Sz z&*0w`cylB3ho23A-}i^#_Xqd>X#4))z7=>UkPX<g1M&l|@CAyp45pwyKyCv~bv<B@ z|Htp_|GR+yzTn?ga0T}|*q2Xn0t<xS4*>Up@cRMaJ^<YNgIhv<HsC5>pe~MQ!j@(j z3pmJs*QDoz`khZ^u7WH0_r$ecYxw;@^!cs9eQUJ+KyXh8u<-Aw9k{|5s4+jP$*&0= z<Uc6+`Je%~HgXm8@4^2!BMcSUtPSkB4f^~x==0lv`_^dtt%-kdPaqqR+d;GX0+cJi zhg|D(gr*(~ILLo+@*jf+UPxiCg5kX#thu1aeZ>HbW!s|9Zv(&I7Hz*RxNidvT7z>A zxgBsn!21L~CTR9?0mU1Yq%bv7>3#v-AC~S5G|@Go9sad;`u`B{KM20kRWQ5{{+kKD zX!q^FeLML5cHq7pxNi&Y32neX!JZ8?r!U~o6Q%pd@JF5nam+h6n)#ks$$U>nGT&2C z%qu^Zna(FN4W7f+=m%<C6FPZ+*e?HL@3{XHlJdu(!5AmG3eCm6C$9C{UrJ@|5z9r~ zw+H9#ENBbfIpj7#wqUP6XbxXsM1SOUGK%?ta|z(xC$}a%3sy4?Ju7aC`@;_M-#z7p zprO#4tI!<$8{gc(+M(@t0QVhmP1OOsx3A1%?ZGdhodt3mklTU0KWGYHVEZhIo;Q1= zz1QG9FPiz-f!CQBrljWrjlCwUoqryeF3of5p87)2F!)SY!T7;W)|4<{EE#w%nRSE= z9l?D^`23FGz5_V#P@T=%TTq`3G?y>%L?7>cg4#W`b>e(gz3@C0%|v=m*qHmn4)Pxg z{)eN#bQPMCd&<iP*Lt17eJ70fI)VF6;JzccmjLeNHqe~D!1g+k?j3rR;W;z7_XhXg z;M5!3TX8yudDj60-p6CNACR6EJ8*y44*#0RrT(9{f13ZI>yh?C=MmuFMYz?J{F^G) zvCbH~c81^Yj6T0J{C;QXy%Pc4ceH?PKyC-F@CBZzk5?}EMn6yEJ@3<4=YWs*b^z-A z81u>d0cY+H*Udk1FU@~yHF<#7sM7RX0Bd%Xr2WhIA^d+6!0_%C)&c!$7uaqW`28;6 zz6;uZ7jWMhd=vk28z5Vd+dy;s0<SZ%<~C0@AaCci-$8ig#o(F{aU#}!!1Aovk$4fi z{EwsYKec}=|5(?LE=~LQXk42#H%67Dy<_@t7i$UxpwAD2-|q^$?Fzr&72J0P_r!k} za7{K~&kkJS3%m-}NMn6!|6cI<Uf^E>cqhAX1}5|$4Z1&EKmXQwsiRBM%Eq7#H8)0I zN<G>X+<RZnU_rQM2?F;);64a#KM1@N|6Rd5@y}t;2Ab0sc%MsbGVZyZ=<q&nyJG<! zFG5_~#*8?|KiPnG^!cQivFNXw8zT!7UN(J%_TL!1wTX2>pC61qKNxim0r$aZ`@!Ho z2)q->2IO|&DqleHWnOrfh8O13lkfIIyq^TjukmUmRD0n2cOZUr7w!+{piiLfy@(y# zk7vj<PI%1*w2ywHV{&jT&eJ?@_5Z}X^}Pqf-iRDFt~`@9Gj0ypI=^#cxHsayVMjdE z?S?)-1pX=nZ9fFuhk*NFa89Vr26&&~=(vF2E1=Yodxw6oM^EtX37_u?fA0xDFCl+5 z^K1fmz@9`J3(`Ge$p$R^?<4;0*nl1W)pzpGZ9qG|H2v%Z=(D*n?sD30@-?Q9(f%6< z|BI=t8~Xfi@cZ3R*Y4oH8~7pqL%=;jZUe6J1+8F<UYJAQT-<Z}G2k72!glz8ov;C# zPeARO+P7o_)ZS%2pnm@OyJ^S<G$T($51m+<bqiq4fa^ncFQ#{;s69U3%^CtP#Bg>; zpC5{L)*XI76n?)ucqeoN?*zFG$nC(kKd8+Y*p3bPH9}p!Ab_s(Fn`(u{CiLeKQ96I z@;#aX8j2t0{Q%j3Z9gFKZm|U$|8Hskbl;EsKk?7sPbJyFgv+T3lfZvdWBi5Wc_@pn zIaCed-h}6RLAciI0ly!Lw$=mOhoYXL#6Nf^*xG=re1R8nY|A~_Kr?VJ<%zr%#5=bE z8Vgu`fTbU>+Ca_zzZU+v4XE{P{dAMcGmlNK%4SW4iKXeAIQK}E9)CgmcLwkm9dK{I z2mF3d;vRm#2e|J6?t6guPz!3agZjn=j*boO`2wH9c;<mVUuyRl>os@V=Qd?Lg|Wak z^aFK`1#0*Iw(Vd3-a~cosHc3VRAio-3cWTKQ_3^;w7qvIy=TMfZ=B*DcH&>2&3eM` z_d=iF6WsSi+wTeP3B<o6HqactpcTfUCbWAK-ko4Vn>3-lOTb(@lMA4w`@{0F0PhE^ z<3Y52T9=Lg)xRH4_kW0g^7qv4?Y;X_?K?O`H>E5iZCdqO)<E2vQkuRB`IFDLa!=)X z{w0Sw3P1S$-ngdfjXu8@`q^IKzBk%_FL2)r-1oHb@2DL#l`jZDolIzhCiM3$!9BMx z74`4(en4&m#J!b&^bxpbsE7X=et@`}aCXhaX;oRJ)32;$jsnMDo^U2^j6Lop{yzc# z4!|4t2zr#Hv)<_Q`@rw_M%(WL?t6p#-r$}1=Qbd>gZlb|#`*$^DKq89G9$P*qTL(O z?j@kj8(j<o-rFG3wV<^hkc|ha-4n<rXpDg4RJFH%P-FE4*71OaL*2yuHTO&_&)6`% zdhK6lTwTX(0o(Wtzi*`Da1Ot}T%+Av@)sQ8o{s5SwU+e(_kF=V@!ppJ?)zByCmXP} z1MUZSpTNfi_Qr<|@dfR1kHjMneCL68=-!wQ&IxDZTMW9BF_=?2&%O;v+=G7_FXEc9 z7XCqvwD7**4*u~gy(5kKaZ`r}NBR$4H@o-f17Tx^ZkXGr(_`c4n?C$KSNy$d(pVq+ z`85X5+adnjUfaz2UQT2Ez<ocod*Zzx_@=Zk(mod0vw`OF1r$@_dD_A~rFQ}M-0n2^ zUY|4;koy5@^915v@(~Ma<DYXQ_y5-WLg0|UBab+y_h!-iv*`Uv9Q=J+^gG!J9nZ&@ zl#kp7>gC_>gB=#`(^-FT-w&Mk$Mp#D-w*vzUjo^JjSX;LK>W*Xps9R;H|9tiFxDfV zi+9J|2JOEKpdepQ<3Vl%#5o7;!+gV9+CS%?w|iUesol$Y_co309@y%|$=$TUfvxZx zf4>#kf!q(sZGiHY%JD(JURxjFg|T4|_}Km!>kq(qZvcG$0C3+Qoc0Ir1hN6i4rDe! z{-C*h0o^OYb>7Fi*CU?OZ(lf4e{jV!uz?B#e7>O|;jV!mK1sSJq<N(@CM3=!V4e1% zkH9w=i7!6>r*>(Nd-C1Xhf`Z7K0Q0EeRf)xeRI=-4ljH&_~?@Ff{!g>7TgLxy7+&B z4lTIQWzU?IZQ~xNZ_bjfkj>co0m%kBzO%Vj{<|QKZy>lI2<``h`+?woAoM?gVBx=p z4PYPl19BT^N?+iYzh<-O=SjNYaWj<a^~a)Lx|{f?x@eDMPAQEAh;KQtPqAw;Q$09} zz6HwLCHKqnb}#4Nqw~f;-WPmiab>sT%UJglgyk&s<ei}X^gE8b6?|yn;r8)Q3`9A+ zKOlY4SWvp=>{pr1ym9~E)(5mlpFarP4+i&xB<{1=An;E74+PHyxee5?0~}+|4jOum zz{iGx#R*4IZ>m4PjwLw=lAd(OqF>fS?{^ba2Qe1d0vnJ6{A=)yms<EIJ0NbX+<Q&! z>)-A0qP@g*k5gf+=c#bkGdG;|k`RH^7Uti3;@BQ1!^lQ%1n-}>9C?y2ptfVRx#7ew z`t`Q&ZDRrG>jU23&bp!955ZV(2!0z3?gyjK9}G?jgTOmMZUb^Vu=WQy#-0r{)EBh7 zl=7zN??pb0^uot6q${p3vIDI?cXjIB#6Q(Vb9@!zh88gq{Q$Ll?WtJFM(yw~mxb)y z&@TL;kkI4Hs?B^yu-<tqSfA5`m8>t}3?Whm^M3lBj-%trKIr_=BTM$1h6ejkzN9Na zJp%j=2mfRT!z>tz?V;E|1iTN0&mRKbC4kex7XIZn(A>VDQ)${4s_BEtcIDjjF$|>y zX`Cds_R;C`<8yU|iR^BN((M1`{#cR$vJf|VpL+(j>Ukop%FMZ$>we(6e}0sN0cWFG zZQ#ey@nj?17J3|6x(j(zUm&%mQQ&t3cpfgpFljxT4a0B4!2M7HcpqW`*?>JeAV1L5 zz5ww=*L9Ik1){#3d(Jx_EAiB71IqDzO}c`FKmV8V&;7o!*Rkb$sEu>ZiR*y{gjH-% zVGJ8w3+Oij9Y@F0d1McgEg-)h2Ny4cK50y-8wG9&BMD>+ND0F&ApVDeSK?o817r*K zY@jK9LAT1xTc$1Z`^dN_|H%2qs&(P}xD9C3%O3aF6(oJ5D@=kPxI3YIa5Rnoc>CvL z8EX56(4BMd?U#?Xel|+ta1gj8zK0aWN*G$ansE%nItSlJDIGhQK=wekaN7=k?wa^? zPn4q{4IW1kz%#dl5!mJ!j&<Un!`2R((-(NZ5&2uxk+*x|p0|Ch-m!2S;C-k@vvFRq zwlL`?!0txK_$L{y<5;X4`{k{yptd`x5WE+ec_&VXor{w&{QMe57-0weH-4<u7G!qN z?_|V&lw%kJZpj9$c7XMf7RYTtZU?qD;H)pOy=Nr9R^-<R{-@)@P(SN973+0zkDsN5 zY=DltZ371)CftqOQ=MwzU*B`rtCQsIo%3$yJD!caKuBPtF4hkEosJuEK3=jDvIV&v zpl`Sp96vL7EVy>W2FMoVHejz$XewXOwm5MgICPeK$pG+AZUfRaf%atVpR`5r0e2ac z4_W;`wSR)-`v;s_kwm_m^G<DC;@rab=u3%=Fy>Mc8*|wPv=89Nkd1I#Ap4QqLI0y+ zbBKR#1CH1M*?`;*9Q6gZV?%zwz}6RF{!qE)I*+)M`+VZn(d7;s(43CX(iA1Ly9vri z$MNrf*5H3|!K!NFNow!Z)+OF8oP+PN5|Y`t5{?wCb0o8|*gy6%!E7633!^S3O8JaH ze%uZQ=SLqOUY_-Xvo_FNz96JD?H|f<ed*eSxTk9&?(?1Dp4&(b8xULj>NIB)&)iM? zD<<@#XExO4Yw(Y;+}FI#Q~Q<LdJWD~*!WUHs)PwhIru*M-E0$PTOdDSwgcWTtYJfo zR$oIrN69#}X@awMP@gYwv_BwU(6b`z2kp{FDISJ&(OB-0h4ngtY)HDUR@^hpLtU8k zff|0G#Zj329qJ{&ColPb{~3e4sn4c1Uz7LSoKGkN-<Zofk)s^z9F~3bI~_;%Fix@s ziyctkK)wLugAwQA{wJs)A$`1s|AyE=Q}_ZrN4{xXJGUG3!MP{DPwlrM?H;FD7oG>V z0gW=^sQ`6R%69;4paqeicpSCl_cO`gNn@36tDbCMqs^0#x3qQQQ{o(4Pr`d@Bvc@^ zh2=NNCggU2K7#rN?h9aJzqCCOGn4q|XbE57b2xGe<d<{L{l1mRMyzlfuwE0WHq8%J z7lZqj#Hl#CAJ4D-t@r(m!EKCa_rDTH)W4G-x8;2@-l4@Yr80xrg8wGj1#F?F9Z<hu zv4M*TpZn%488`tkL=&381{&fE0?(|O4?Q$=tna8<kPVPc-L`>4kyBORzs2xI)IF#V z**qWXkPXnbe$1uB|5t;1OMB<Mn)%KI@0n~WN0oV<_T7frPONrd^#x`d7=1C}iWt(~ zFsd^9`-x-&0QUp+^$F5FBiIb3&fYKJ<AM&wN!uZBb90X(q(yE6lCM?eu9>SUPK6D) z95)nCPNK0N=bqaoZR^HfOf0ardl~nfcZqY{qn~O4VmuiK-zTM3Td>%H#TS^z1u1Ow z`S_hEPuuQP>@4D++dy;r0{#pk_)_XcG0=zmKa!vOeB!COmOE@fnV+~(3I1J*lW}t( zAlW^&c`iG*0qvN=xFwR$x5vGl^J?&p`?hz&Y!BQP%>Lj`8yI;e=24Wd7Q3}KjH%52 zf!hGL16yCfuMceR7dYz+FmL3qioxCJ`2)9qS8$Kwq@|_};JQ#*kX+<K{(lsIGmM^P zlI)y&lArfanxT6Zhhm&}lg4-C^SM7KKBabU;e0yM+F-Q_i@n@o1Ep!-bzAZXJ%5n; zopyO^o}C0AK;U-JRO15qb>e`EtnW0h+)wvC_;}Bi+@o0Qbp@3xjl0B0#(IMPkCo@r zShM3~+%vVv&iSW0X(wDt*=^?D%)gv_E9dBYX&%4~8LaaHEVd!pfgKwdcRp!_r5<F1 zYI<%ypd`K2t_|237c|5dAfDh?WA>cRmU2l>S96adrG<O|_XCpsi!px)RGdru9$?Le zqLf0RS7*BBlec|d7hWHUkD)n>L%{z})Nb$KUdFead)sx%7I^<4vw_Lw>3<ELa~~gn zaJy40dUnzeEX{apvc(THr7u8C!A<Y|;e#MMZ}%i8mmRAvUwGNv4`>vd76gI&ADe~$ zqSUM6v?09D<9?s&z?VDfGvQp~(^E07=RRL*_YQK;ZQ%|ZsLcFf%*m+XD3tfTRIb$q zlv4*Ahn8o4SgQ>*z!xC4<c4R?GTZ0)yzO)OTOzg9xmuAux1FLm{VT<}bohZLLUG#1 z;<Ga-9@}2Km&+~ZLh?(KOVZX%!#I!oe9kMS_kas~kRL<qbgrcz_;o^2!ow(#w?WQ< zyj(Tn(o6$OGv6i~sMQXTFCQP)b<b!>W!AT*9ZP~OvXe~qY+tSeSFhh;17h6s0b+65 z2O{`yG>X&S6(eTSIIBMHx!iItxD6}^hd1SYy*=J-_i;Ord_Yz9HxmmJ?nNP-d+vX! zT)7Ttzf`XO@tCp0E3*Dss}0oS3p$@q$xw{%&-**dQ*Lim?%lH3%C8pM`)kF#_*vrl z^cw(k8t2l#6i=+4O8G!YTf3+7>RRr!fpbX@&Zy4*C+F0bYkS-L*y&Z-SH~WYBLB;M zzP&bRs{^XDT4>iw+vaq_qqs-(Rjqa)^93WTvVQJ*A$5~s<NS`blvkhZb?K!!f3q)t z<TE}(e)6lrx%7Vu=QEfC_*OWbI7=81%<pYD%RQIT)&|slW1jJzR+=7l&y{sw+2i|; zeOK0gH1$&IEJH`h1|9J~TnBfQO<XD;pJ?>lximQNbo>KB=TnpWlw@A)aXI}&m-DG< z?F*A$_I`auKe87(7Ldz*x3+t(Yo2oXslEj63rQZ0ux&{P;Z*#Cl06ipW(h@UhlR5# zD})nkrVBfl1q(0V&#&uk<>&cQ>deB=t5KJZkBl))E>4;<y&@}lMpgFF8P#h`@42$} z8qy2cJ~+KRbLGU-t7+~J`TE-GQdd3X<?^z*F3f(xoG;}q2mSWn$h)mBoRy!;&1L8Q zm2}7RvMs~WZGJxG#kt_+$#rkLZd(_g52cPS@^g6kJS7?B;QM(hKiARn?zZFPGH|(g z>T22T)kR(h`8uy7rSkI{qhEPGj;uGPth?=8N92_s)138}m*ePqbJoY5cgt$Po(;5Y zjohWXHQ?5OTLW$lxHaI`fLjA@4Y)Pn)__|BZVk9K;MRa!18xntHQ?5OTLW$lxHaI` zfLjA@4Y)Pn)__|BZVk9x1MGj>Qmi#gMCs20V*-HX7NwsG<9-!{akTw2rBQ;{H-gZK zwkgFi%1<%(infdKK7AZ3MqwXqQ;KtF5ow!JoFgsR#HoV#sd<wMq-}w3M>2-2ZZG4c z;%ndTBvt43@AYka*&L7UPj8<MLF(K7)T`$4w?&b%K_J<9d4BG7w+8+jH2^!NSQ+br z7fsdJ#t*jJ)?=G>MHy8ydu?&e?V8OKo{D=O%VC`?o{67DUQWJ5+H9NMll2HVH0wI8 zz~M#7cfcVQ+iZU--?sHXw*Jc2-$h#dA<vZ6^p`dKYm|-u#W~zRYso)<D!Dhw!FiGY pmi#(0lu{gveJIZ2|BK=4izRms36ae==HKXd^CD%<H~vqg{~vZ7#`ypM literal 0 HcmV?d00001 diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e5700fe059..5729da8da8 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -322,7 +322,7 @@ proxyRemoteFilesDescription: "この設定を有効にすると、未保存ま driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量" driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量" inMb: "メガバイト単位" -iconUrl: "アイコン画像のURL" +iconUrl: "アイコン画像のURL (faviconなど)" bannerUrl: "バナー画像のURL" basicInfo: "基本情報" pinnedUsers: "ピン留めユーザー" diff --git a/src/server/web/index.ts b/src/server/web/index.ts index 7b087bbf5b..ea356206ff 100644 --- a/src/server/web/index.ts +++ b/src/server/web/index.ts @@ -45,7 +45,7 @@ app.use(views(__dirname + '/views', { })); // Serve favicon -app.use(favicon(`${__dirname}/../../../assets/favicon.png`)); +app.use(favicon(`${__dirname}/../../../assets/favicon.ico`)); // Common request handler app.use(async (ctx, next) => { diff --git a/test/fetch-resource.ts b/test/fetch-resource.ts index bd3a325dc2..12a37b6cb0 100644 --- a/test/fetch-resource.ts +++ b/test/fetch-resource.ts @@ -22,6 +22,7 @@ const UNSPECIFIED = '*/*'; // Response Contet-Type const AP = 'application/activity+json; charset=utf-8'; +const JSON = 'application/json; charset=utf-8'; const HTML = 'text/html; charset=utf-8'; describe('Fetch resource', () => { @@ -50,33 +51,39 @@ describe('Fetch resource', () => { })); it('GET root', async(async () => { - const res = await simpleGet('/', 'text/html'); + const res = await simpleGet('/'); assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); })); it('GET docs', async(async () => { - const res = await simpleGet('/docs/ja-JP/about', 'text/html'); + const res = await simpleGet('/docs/ja-JP/about'); assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); })); it('GET api-doc', async(async () => { - const res = await simpleGet('/api-doc', 'text/html'); + const res = await simpleGet('/api-doc'); assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, HTML); })); it('GET api.json', async(async () => { - const res = await simpleGet('/api.json', 'application/json'); + const res = await simpleGet('/api.json'); assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, JSON); })); it('GET favicon.ico', async(async () => { - const res = await simpleGet('/favicon.ico', 'image/png'); + const res = await simpleGet('/favicon.ico'); assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, 'image/x-icon'); })); it('GET apple-touch-icon.png', async(async () => { - const res = await simpleGet('/apple-touch-icon.png', 'image/png'); + const res = await simpleGet('/apple-touch-icon.png'); assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, 'image/png'); })); }); diff --git a/test/utils.ts b/test/utils.ts index 55e877a8a2..b0393ee192 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -110,7 +110,7 @@ export function connectStream(user: any, channel: string, listener: (message: Re }); } -export const simpleGet = async (path: string, accept: string): Promise<{ status?: number, type?: string, location?: string }> => { +export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status?: number, type?: string, location?: string }> => { // node-fetchだと3xxを取れない return await new Promise((resolve, reject) => { const req = http.request(`http://localhost:${port}${path}`, { From ca4f026533adcab9ad0fcae0dd5d4549362e9458 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Tue, 16 Mar 2021 12:50:07 +0900 Subject: [PATCH 25/76] =?UTF-8?q?DB=E4=B8=8A=E3=81=A7=E5=A3=8A=E3=82=8C?= =?UTF-8?q?=E3=81=9F=E3=83=89=E3=83=A9=E3=82=A4=E3=83=96=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=82=92=E7=84=A1=E8=A6=96=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=20(#7345)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/repositories/drive-file.ts | 30 ++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/models/repositories/drive-file.ts b/src/models/repositories/drive-file.ts index bc1e37489c..62416f69ea 100644 --- a/src/models/repositories/drive-file.ts +++ b/src/models/repositories/drive-file.ts @@ -12,6 +12,12 @@ import { fetchMeta } from '../../misc/fetch-meta'; export type PackedDriveFile = SchemaType<typeof packedDriveFileSchema>; +type PackOptions = { + detail?: boolean, + self?: boolean, + withUser?: boolean, +}; + @EntityRepository(DriveFile) export class DriveFileRepository extends Repository<DriveFile> { public validateFileName(name: string): boolean { @@ -89,20 +95,19 @@ export class DriveFileRepository extends Repository<DriveFile> { return parseInt(sum, 10) || 0; } + public async pack(src: DriveFile['id'], options?: PackOptions): Promise<PackedDriveFile | null>; + public async pack(src: DriveFile, options?: PackOptions): Promise<PackedDriveFile>; public async pack( src: DriveFile['id'] | DriveFile, - options?: { - detail?: boolean, - self?: boolean, - withUser?: boolean, - } - ): Promise<PackedDriveFile> { + options?: PackOptions + ): Promise<PackedDriveFile | null> { const opts = Object.assign({ detail: false, self: false }, options); - const file = typeof src === 'object' ? src : await this.findOneOrFail(src); + const file = typeof src === 'object' ? src : await this.findOne(src); + if (file == null) return null; const meta = await fetchMeta(); @@ -128,15 +133,12 @@ export class DriveFileRepository extends Repository<DriveFile> { }); } - public packMany( + public async packMany( files: any[], - options?: { - detail?: boolean - self?: boolean, - withUser?: boolean, - } + options?: PackOptions ) { - return Promise.all(files.map(f => this.pack(f, options))); + const items = await Promise.all(files.map(f => this.pack(f, options))); + return items.filter(x => x != null); } } From d3c8368b0c3f037a87a2a8c9acfc5a54cf1d06e1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 18 Mar 2021 10:19:30 +0900 Subject: [PATCH 26/76] Improve API performance --- src/server/api/authenticate.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/server/api/authenticate.ts b/src/server/api/authenticate.ts index 0374ca35ea..7404c477fd 100644 --- a/src/server/api/authenticate.ts +++ b/src/server/api/authenticate.ts @@ -3,12 +3,18 @@ import { User } from '../../models/entities/user'; import { Users, AccessTokens, Apps } from '../../models'; import { AccessToken } from '../../models/entities/access-token'; +const cache = {} as Record<string, User>; + export default async (token: string): Promise<[User | null | undefined, AccessToken | null | undefined]> => { if (token == null) { return [null, null]; } if (isNativeToken(token)) { + if (cache[token]) { // TODO: キャッシュされてから一定時間経過していたら破棄する + return [cache[token], null]; + } + // Fetch user const user = await Users .findOne({ token }); @@ -17,8 +23,11 @@ export default async (token: string): Promise<[User | null | undefined, AccessTo throw new Error('user not found'); } + cache[token] = user; + return [user, null]; } else { + // TODO: cache const accessToken = await AccessTokens.findOne({ where: [{ hash: token.toLowerCase() // app From 8aa089178a54559cbc4e4fe84a618fc7535f178c Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 18 Mar 2021 10:49:14 +0900 Subject: [PATCH 27/76] Improve server performance --- src/misc/cache.ts | 25 +++++++++++++++++++ src/queue/processors/deliver.ts | 19 +++++++++----- src/server/api/authenticate.ts | 12 ++++++--- .../register-or-fetch-instance-doc.ts | 8 ++++++ 4 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 src/misc/cache.ts diff --git a/src/misc/cache.ts b/src/misc/cache.ts new file mode 100644 index 0000000000..356a3de7b9 --- /dev/null +++ b/src/misc/cache.ts @@ -0,0 +1,25 @@ +export class Cache<T> { + private cache: Map<string | null, { date: number; value: T; }>; + private lifetime: number; + + constructor(lifetime: Cache<never>['lifetime']) { + this.lifetime = lifetime; + } + + public set(key: string | null, value: T):void { + this.cache.set(key, { + date: Date.now(), + value + }); + } + + public get(key: string | null): T | null { + const cached = this.cache.get(key); + if (cached == null) return null; + if ((Date.now() - cached.date) > this.lifetime) { + this.cache.delete(key); + return null; + } + return cached.value; + } +} diff --git a/src/queue/processors/deliver.ts b/src/queue/processors/deliver.ts index cb7587ef81..a8b4ed4fe3 100644 --- a/src/queue/processors/deliver.ts +++ b/src/queue/processors/deliver.ts @@ -7,11 +7,15 @@ import { instanceChart } from '../../services/chart'; import { fetchInstanceMetadata } from '../../services/fetch-instance-metadata'; import { fetchMeta } from '../../misc/fetch-meta'; import { toPuny } from '../../misc/convert-host'; +import { Cache } from '../../misc/cache'; +import { Instance } from '../../models/entities/instance'; const logger = new Logger('deliver'); let latest: string | null = null; +const suspendedHostsCache = new Cache<Instance[]>(1000 * 60 * 60); + export default async (job: Bull.Job) => { const { host } = new URL(job.data.to); @@ -22,12 +26,15 @@ export default async (job: Bull.Job) => { } // isSuspendedなら中断 - const suspendedHosts = await Instances.find({ - where: { - isSuspended: true - }, - cache: 60 * 1000 - }); + let suspendedHosts = suspendedHostsCache.get(null); + if (suspendedHosts == null) { + suspendedHosts = await Instances.find({ + where: { + isSuspended: true + }, + }); + suspendedHostsCache.set(null, suspendedHosts); + } if (suspendedHosts.map(x => x.host).includes(toPuny(host))) { return 'skip (suspended)'; } diff --git a/src/server/api/authenticate.ts b/src/server/api/authenticate.ts index 7404c477fd..9c9ef74352 100644 --- a/src/server/api/authenticate.ts +++ b/src/server/api/authenticate.ts @@ -2,8 +2,11 @@ import isNativeToken from './common/is-native-token'; import { User } from '../../models/entities/user'; import { Users, AccessTokens, Apps } from '../../models'; import { AccessToken } from '../../models/entities/access-token'; +import { Cache } from '../../misc/cache'; -const cache = {} as Record<string, User>; +// TODO: TypeORMのカスタムキャッシュプロバイダを使っても良いかも +// ref. https://github.com/typeorm/typeorm/blob/master/docs/caching.md +const cache = new Cache<User>(1000 * 60 * 60); export default async (token: string): Promise<[User | null | undefined, AccessToken | null | undefined]> => { if (token == null) { @@ -11,8 +14,9 @@ export default async (token: string): Promise<[User | null | undefined, AccessTo } if (isNativeToken(token)) { - if (cache[token]) { // TODO: キャッシュされてから一定時間経過していたら破棄する - return [cache[token], null]; + const cached = cache.get(token); + if (cached) { + return [cached, null]; } // Fetch user @@ -23,7 +27,7 @@ export default async (token: string): Promise<[User | null | undefined, AccessTo throw new Error('user not found'); } - cache[token] = user; + cache.set(token, user); return [user, null]; } else { diff --git a/src/services/register-or-fetch-instance-doc.ts b/src/services/register-or-fetch-instance-doc.ts index 3501e20de1..2c39502288 100644 --- a/src/services/register-or-fetch-instance-doc.ts +++ b/src/services/register-or-fetch-instance-doc.ts @@ -3,10 +3,16 @@ import { Instances } from '../models'; import { federationChart } from './chart'; import { genId } from '../misc/gen-id'; import { toPuny } from '../misc/convert-host'; +import { Cache } from '../misc/cache'; + +const cache = new Cache<Instance>(1000 * 60 * 60); export async function registerOrFetchInstanceDoc(host: string): Promise<Instance> { host = toPuny(host); + const cached = cache.get(host); + if (cached) return cached; + const index = await Instances.findOne({ host }); if (index == null) { @@ -19,8 +25,10 @@ export async function registerOrFetchInstanceDoc(host: string): Promise<Instance federationChart.update(true); + cache.set(host, i); return i; } else { + cache.set(host, index); return index; } } From 0389e2c891177db3c2e6dfbc0b9656dfddf90892 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 18 Mar 2021 10:54:39 +0900 Subject: [PATCH 28/76] fix bug --- src/misc/cache.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/misc/cache.ts b/src/misc/cache.ts index 356a3de7b9..ccc5f01ff7 100644 --- a/src/misc/cache.ts +++ b/src/misc/cache.ts @@ -3,6 +3,7 @@ export class Cache<T> { private lifetime: number; constructor(lifetime: Cache<never>['lifetime']) { + this.cache = new Map(); this.lifetime = lifetime; } From e53a40658d40a91bfecc005ef1be712bbddbf336 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 18 Mar 2021 10:55:51 +0900 Subject: [PATCH 29/76] format --- src/misc/cache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc/cache.ts b/src/misc/cache.ts index ccc5f01ff7..5b7017a3b9 100644 --- a/src/misc/cache.ts +++ b/src/misc/cache.ts @@ -7,7 +7,7 @@ export class Cache<T> { this.lifetime = lifetime; } - public set(key: string | null, value: T):void { + public set(key: string | null, value: T): void { this.cache.set(key, { date: Date.now(), value From 0d19c2d42ee98e36d45d8040f9e9df7e4a9de0b7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 18 Mar 2021 11:03:38 +0900 Subject: [PATCH 30/76] add note --- src/queue/processors/inbox.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/queue/processors/inbox.ts b/src/queue/processors/inbox.ts index b4e8b85a46..a5822ff25f 100644 --- a/src/queue/processors/inbox.ts +++ b/src/queue/processors/inbox.ts @@ -40,6 +40,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => { return `Old keyId is no longer supported. ${keyIdLower}`; } + // TDOO: キャッシュ const dbResolver = new DbResolver(); // HTTP-Signature keyIdを元にDBから取得 From 4f249159d310d4296753fcfe8e2bbd390fd9002b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 18 Mar 2021 11:17:05 +0900 Subject: [PATCH 31/76] Improve chart performance (#7360) * wip * wip * wip * wip * wip * Update chart.ts * wip * Improve server performance * wip * wip --- migration/1615965918224-chart-v2.ts | 218 ++++++++++++++ migration/1615966519402-chart-v2-2.ts | 22 ++ package.json | 2 +- src/daemons/queue-stats.ts | 2 +- src/db/postgre.ts | 4 + src/global.d.ts | 1 + src/misc/before-shutdown.ts | 88 ++++++ src/queue/index.ts | 23 +- src/queue/initialize.ts | 18 ++ src/queue/queues.ts | 7 + .../chart/charts/classes/active-users.ts | 18 +- src/services/chart/charts/classes/drive.ts | 22 ++ .../chart/charts/classes/federation.ts | 11 + src/services/chart/charts/classes/hashtag.ts | 18 +- src/services/chart/charts/classes/instance.ts | 44 +++ src/services/chart/charts/classes/network.ts | 11 + src/services/chart/charts/classes/notes.ts | 26 ++ .../chart/charts/classes/per-user-drive.ts | 12 + .../charts/classes/per-user-following.ts | 30 ++ .../chart/charts/classes/per-user-notes.ts | 14 + .../charts/classes/per-user-reactions.ts | 12 + .../chart/charts/classes/test-grouped.ts | 11 + .../chart/charts/classes/test-unique.ts | 13 +- src/services/chart/charts/classes/test.ts | 11 + src/services/chart/charts/classes/users.ts | 16 + .../chart/charts/schemas/active-users.ts | 12 +- src/services/chart/charts/schemas/hashtag.ts | 12 +- .../chart/charts/schemas/test-unique.ts | 7 +- src/services/chart/core.ts | 278 +++++++++++------- src/services/chart/index.ts | 25 ++ test/chart.ts | 54 +++- yarn.lock | 8 +- 32 files changed, 889 insertions(+), 161 deletions(-) create mode 100644 migration/1615965918224-chart-v2.ts create mode 100644 migration/1615966519402-chart-v2-2.ts create mode 100644 src/global.d.ts create mode 100644 src/misc/before-shutdown.ts create mode 100644 src/queue/initialize.ts create mode 100644 src/queue/queues.ts diff --git a/migration/1615965918224-chart-v2.ts b/migration/1615965918224-chart-v2.ts new file mode 100644 index 0000000000..cacbd1945b --- /dev/null +++ b/migration/1615965918224-chart-v2.ts @@ -0,0 +1,218 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class chartV21615965918224 implements MigrationInterface { + name = 'chartV21615965918224' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`DELETE FROM "__chart__active_users" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__drive" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__federation" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__hashtag" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__instance" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__network" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__notes" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__per_user_drive" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__per_user_following" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__per_user_notes" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__per_user_reaction" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__test" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__test_grouped" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__test_unique" WHERE "span" = 'day'`); + await queryRunner.query(`DELETE FROM "__chart__users" WHERE "span" = 'day'`); + + await queryRunner.query(`DROP INDEX "IDX_15e91a03aeeac9dbccdf43fc06"`); + await queryRunner.query(`DROP INDEX "IDX_20f57cc8f142c131340ee16742"`); + await queryRunner.query(`DROP INDEX "IDX_c26e2c1cbb6e911e0554b27416"`); + await queryRunner.query(`DROP INDEX "IDX_3fa0d0f17ca72e3dc80999a032"`); + await queryRunner.query(`DROP INDEX "IDX_6e1df243476e20cbf86572ecc0"`); + await queryRunner.query(`DROP INDEX "IDX_06690fc959f1c9fdaf21928222"`); + await queryRunner.query(`DROP INDEX "IDX_e447064455928cf627590ef527"`); + await queryRunner.query(`DROP INDEX "IDX_2d416e6af791a82e338c79d480"`); + await queryRunner.query(`DROP INDEX "IDX_e9cd07672b37d8966cf3709283"`); + await queryRunner.query(`DROP INDEX "IDX_fcc181fb8283009c61cc4083ef"`); + await queryRunner.query(`DROP INDEX "IDX_49975586f50ed7b800fdd88fbd"`); + await queryRunner.query(`DROP INDEX "IDX_6d6f156ceefc6bc5f273a0e370"`); + await queryRunner.query(`DROP INDEX "IDX_c12f0af4a66cdd30c2287ce8aa"`); + await queryRunner.query(`DROP INDEX "IDX_d0a4f79af5a97b08f37b547197"`); + await queryRunner.query(`DROP INDEX "IDX_f5448d9633cff74208d850aabe"`); + await queryRunner.query(`DROP INDEX "IDX_f8dd01baeded2ffa833e0a610a"`); + await queryRunner.query(`DROP INDEX "IDX_08fac0eb3b11f04c200c0b40dd"`); + await queryRunner.query(`DROP INDEX "IDX_9ff6944f01acb756fdc92d7563"`); + await queryRunner.query(`DROP INDEX "IDX_e69096589f11e3baa98ddd64d0"`); + await queryRunner.query(`DROP INDEX "IDX_0c9a159c5082cbeef3ca6706b5"`); + await queryRunner.query(`DROP INDEX "IDX_924fc196c80ca24bae01dd37e4"`); + await queryRunner.query(`DROP INDEX "IDX_328f259961e60c4fa0bfcf55ca"`); + await queryRunner.query(`DROP INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53"`); + await queryRunner.query(`DROP INDEX "IDX_f2aeafde2ae6fbad38e857631b"`); + await queryRunner.query(`DROP INDEX "IDX_f92dd6d03f8d994f29987f6214"`); + await queryRunner.query(`DROP INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f"`); + await queryRunner.query(`DROP INDEX "IDX_4db3b84c7be0d3464714f3e0b1"`); + await queryRunner.query(`DROP INDEX "IDX_8d2cbbc8114d90d19b44d626b6"`); + await queryRunner.query(`DROP INDEX "IDX_046feeb12e9ef5f783f409866a"`); + await queryRunner.query(`DROP INDEX "IDX_f68a5ab958f9f5fa17a32ac23b"`); + await queryRunner.query(`DROP INDEX "IDX_65633a106bce43fc7c5c30a5c7"`); + await queryRunner.query(`DROP INDEX "IDX_edeb73c09c3143a81bcb34d569"`); + await queryRunner.query(`DROP INDEX "IDX_e316f01a6d24eb31db27f88262"`); + await queryRunner.query(`DROP INDEX "IDX_2be7ec6cebddc14dc11e206686"`); + await queryRunner.query(`DROP INDEX "IDX_a5133470f4825902e170328ca5"`); + await queryRunner.query(`DROP INDEX "IDX_84e661abb7bd1e51b690d4b017"`); + await queryRunner.query(`DROP INDEX "IDX_5c73bf61da4f6e6f15bae88ed1"`); + await queryRunner.query(`DROP INDEX "IDX_d70c86baedc68326be11f9c0ce"`); + await queryRunner.query(`DROP INDEX "IDX_66e1e1ecd2f29e57778af35b59"`); + await queryRunner.query(`DROP INDEX "IDX_92255988735563f0fe4aba1f05"`); + await queryRunner.query(`DROP INDEX "IDX_c5870993e25c3d5771f91f5003"`); + await queryRunner.query(`DROP INDEX "IDX_f170de677ea75ad4533de2723e"`); + await queryRunner.query(`DROP INDEX "IDX_7c184198ecf66a8d3ecb253ab3"`); + await queryRunner.query(`DROP INDEX "IDX_f091abb24193d50c653c6b77fc"`); + await queryRunner.query(`DROP INDEX "IDX_a770a57c70e668cc61590c9161"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__active_users_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___local_count"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_count"`); + await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__drive_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__federation_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__hashtag_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_count"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_count"`); + await queryRunner.query(`ALTER TABLE "__chart__instance" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__instance_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__instance" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__network" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__network_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__network" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__notes_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__per_user_drive_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_following" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__per_user_following_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_following" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__per_user_notes_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__per_user_reaction_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__test_grouped" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__test_grouped_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__test_grouped" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__test_unique_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "___foo"`); + await queryRunner.query(`ALTER TABLE "__chart__test" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__test_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__test" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__users" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__users_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__users" DROP COLUMN "unique"`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "__chart__users" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__users_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__users" ADD "span" "__chart__users_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__test" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__test_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__test" ADD "span" "__chart__test_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "___foo" bigint NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__test_unique_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "span" "__chart__test_unique_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__test_grouped" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__test_grouped_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__test_grouped" ADD "span" "__chart__test_grouped_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_reaction_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ADD "span" "__chart__per_user_reaction_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_notes_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ADD "span" "__chart__per_user_notes_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_following_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ADD "span" "__chart__per_user_following_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_drive_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ADD "span" "__chart__per_user_drive_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__notes_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "span" "__chart__notes_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__network" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__network_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__network" ADD "span" "__chart__network_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__instance" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__instance_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__instance" ADD "span" "__chart__instance_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___remote_count" bigint NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___local_count" bigint NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__hashtag_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "span" "__chart__hashtag_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__federation_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "span" "__chart__federation_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__drive_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "span" "__chart__drive_span_enum" NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___remote_count" bigint NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___local_count" bigint NOT NULL`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`CREATE TYPE "public"."__chart__active_users_span_enum" AS ENUM('hour', 'day')`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "span" "__chart__active_users_span_enum" NOT NULL`); + await queryRunner.query(`CREATE INDEX "IDX_a770a57c70e668cc61590c9161" ON "__chart__users" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_f091abb24193d50c653c6b77fc" ON "__chart__users" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_7c184198ecf66a8d3ecb253ab3" ON "__chart__users" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_f170de677ea75ad4533de2723e" ON "__chart__test" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_c5870993e25c3d5771f91f5003" ON "__chart__test" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_92255988735563f0fe4aba1f05" ON "__chart__test" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_66e1e1ecd2f29e57778af35b59" ON "__chart__test_unique" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_d70c86baedc68326be11f9c0ce" ON "__chart__test_unique" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_5c73bf61da4f6e6f15bae88ed1" ON "__chart__test_unique" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_84e661abb7bd1e51b690d4b017" ON "__chart__test_grouped" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_a5133470f4825902e170328ca5" ON "__chart__test_grouped" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_2be7ec6cebddc14dc11e206686" ON "__chart__test_grouped" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_e316f01a6d24eb31db27f88262" ON "__chart__per_user_reaction" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_edeb73c09c3143a81bcb34d569" ON "__chart__per_user_reaction" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_65633a106bce43fc7c5c30a5c7" ON "__chart__per_user_reaction" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_f68a5ab958f9f5fa17a32ac23b" ON "__chart__per_user_notes" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_046feeb12e9ef5f783f409866a" ON "__chart__per_user_notes" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_8d2cbbc8114d90d19b44d626b6" ON "__chart__per_user_notes" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_4db3b84c7be0d3464714f3e0b1" ON "__chart__per_user_following" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f" ON "__chart__per_user_following" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_f92dd6d03f8d994f29987f6214" ON "__chart__per_user_following" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_f2aeafde2ae6fbad38e857631b" ON "__chart__per_user_drive" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53" ON "__chart__per_user_drive" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_328f259961e60c4fa0bfcf55ca" ON "__chart__per_user_drive" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_924fc196c80ca24bae01dd37e4" ON "__chart__notes" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_0c9a159c5082cbeef3ca6706b5" ON "__chart__notes" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_e69096589f11e3baa98ddd64d0" ON "__chart__notes" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_9ff6944f01acb756fdc92d7563" ON "__chart__network" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_08fac0eb3b11f04c200c0b40dd" ON "__chart__network" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_f8dd01baeded2ffa833e0a610a" ON "__chart__network" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_f5448d9633cff74208d850aabe" ON "__chart__instance" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_d0a4f79af5a97b08f37b547197" ON "__chart__instance" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_c12f0af4a66cdd30c2287ce8aa" ON "__chart__instance" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_6d6f156ceefc6bc5f273a0e370" ON "__chart__hashtag" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_49975586f50ed7b800fdd88fbd" ON "__chart__hashtag" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_fcc181fb8283009c61cc4083ef" ON "__chart__hashtag" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_e9cd07672b37d8966cf3709283" ON "__chart__federation" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_2d416e6af791a82e338c79d480" ON "__chart__federation" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_e447064455928cf627590ef527" ON "__chart__federation" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_06690fc959f1c9fdaf21928222" ON "__chart__drive" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_6e1df243476e20cbf86572ecc0" ON "__chart__drive" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_3fa0d0f17ca72e3dc80999a032" ON "__chart__drive" ("span") `); + await queryRunner.query(`CREATE INDEX "IDX_c26e2c1cbb6e911e0554b27416" ON "__chart__active_users" ("date", "group", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_20f57cc8f142c131340ee16742" ON "__chart__active_users" ("date", "span") `); + await queryRunner.query(`CREATE INDEX "IDX_15e91a03aeeac9dbccdf43fc06" ON "__chart__active_users" ("span") `); + } + +} diff --git a/migration/1615966519402-chart-v2-2.ts b/migration/1615966519402-chart-v2-2.ts new file mode 100644 index 0000000000..a694f9542a --- /dev/null +++ b/migration/1615966519402-chart-v2-2.ts @@ -0,0 +1,22 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class chartV221615966519402 implements MigrationInterface { + name = 'chartV221615966519402' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___local_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___remote_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___local_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___remote_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); + await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "___foo" character varying array NOT NULL DEFAULT '{}'::varchar[]`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "___foo"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_users"`); + await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`); + } + +} diff --git a/package.json b/package.json index 67d7dfca89..4d7f3c8353 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@koa/router": "9.0.1", "@sentry/browser": "5.29.2", "@sentry/tracing": "5.29.2", - "@sinonjs/fake-timers": "6.0.1", + "@sinonjs/fake-timers": "7.0.2", "@syuilo/aiscript": "0.11.1", "@types/bcryptjs": "2.4.2", "@types/bull": "3.15.0", diff --git a/src/daemons/queue-stats.ts b/src/daemons/queue-stats.ts index 288e855ae9..77f09b18d6 100644 --- a/src/daemons/queue-stats.ts +++ b/src/daemons/queue-stats.ts @@ -1,5 +1,5 @@ import Xev from 'xev'; -import { deliverQueue, inboxQueue } from '../queue'; +import { deliverQueue, inboxQueue } from '../queue/queues'; const ev = new Xev(); diff --git a/src/db/postgre.ts b/src/db/postgre.ts index 2f3c910163..831e5e0592 100644 --- a/src/db/postgre.ts +++ b/src/db/postgre.ts @@ -1,3 +1,7 @@ +// https://github.com/typeorm/typeorm/issues/2400 +const types = require('pg').types; +types.setTypeParser(20, Number); + import { createConnection, Logger, getConnection } from 'typeorm'; import config from '../config'; import { entities as charts } from '../services/chart/entities'; diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000000..7343aa1994 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1 @@ +type FIXME = any; diff --git a/src/misc/before-shutdown.ts b/src/misc/before-shutdown.ts new file mode 100644 index 0000000000..58d0ea5108 --- /dev/null +++ b/src/misc/before-shutdown.ts @@ -0,0 +1,88 @@ +// https://gist.github.com/nfantone/1eaa803772025df69d07f4dbf5df7e58 + +'use strict'; + +/** + * @callback BeforeShutdownListener + * @param {string} [signalOrEvent] The exit signal or event name received on the process. + */ + +/** + * System signals the app will listen to initiate shutdown. + * @const {string[]} + */ +const SHUTDOWN_SIGNALS = ['SIGINT', 'SIGTERM']; + +/** + * Time in milliseconds to wait before forcing shutdown. + * @const {number} + */ +const SHUTDOWN_TIMEOUT = 15000; + +/** + * A queue of listener callbacks to execute before shutting + * down the process. + * @type {BeforeShutdownListener[]} + */ +const shutdownListeners = []; + +/** + * Listen for signals and execute given `fn` function once. + * @param {string[]} signals System signals to listen to. + * @param {function(string)} fn Function to execute on shutdown. + */ +const processOnce = (signals, fn) => { + return signals.forEach(sig => process.once(sig, fn)); +}; + +/** + * Sets a forced shutdown mechanism that will exit the process after `timeout` milliseconds. + * @param {number} timeout Time to wait before forcing shutdown (milliseconds) + */ +const forceExitAfter = timeout => () => { + setTimeout(() => { + // Force shutdown after timeout + console.warn(`Could not close resources gracefully after ${timeout}ms: forcing shutdown`); + return process.exit(1); + }, timeout).unref(); +}; + +/** + * Main process shutdown handler. Will invoke every previously registered async shutdown listener + * in the queue and exit with a code of `0`. Any `Promise` rejections from any listener will + * be logged out as a warning, but won't prevent other callbacks from executing. + * @param {string} signalOrEvent The exit signal or event name received on the process. + */ +async function shutdownHandler(signalOrEvent) { + console.warn(`Shutting down: received [${signalOrEvent}] signal`); + + for (const listener of shutdownListeners) { + try { + await listener(signalOrEvent); + } catch (err) { + console.warn(`A shutdown handler failed before completing with: ${err.message || err}`); + } + } + + return process.exit(0); +} + +/** + * Registers a new shutdown listener to be invoked before exiting + * the main process. Listener handlers are guaranteed to be called in the order + * they were registered. + * @param {BeforeShutdownListener} listener The shutdown listener to register. + * @returns {BeforeShutdownListener} Echoes back the supplied `listener`. + */ +export function beforeShutdown(listener) { + shutdownListeners.push(listener); + return listener; +} + +// Register shutdown callback that kills the process after `SHUTDOWN_TIMEOUT` milliseconds +// This prevents custom shutdown handlers from hanging the process indefinitely +processOnce(SHUTDOWN_SIGNALS, forceExitAfter(SHUTDOWN_TIMEOUT)); + +// Register process shutdown callback +// Will listen to incoming signal events and execute all registered handlers in the stack +processOnce(SHUTDOWN_SIGNALS, shutdownHandler); diff --git a/src/queue/index.ts b/src/queue/index.ts index 163c57d691..9fb4595a35 100644 --- a/src/queue/index.ts +++ b/src/queue/index.ts @@ -1,4 +1,3 @@ -import * as Queue from 'bull'; import * as httpSignature from 'http-signature'; import config from '../config'; @@ -13,22 +12,7 @@ import { queueLogger } from './logger'; import { DriveFile } from '../models/entities/drive-file'; import { getJobInfo } from './get-job-info'; import { IActivity } from '../remote/activitypub/type'; - -function initializeQueue(name: string, limitPerSec = -1) { - return new Queue(name, { - redis: { - port: config.redis.port, - host: config.redis.host, - password: config.redis.pass, - db: config.redis.db || 0, - }, - prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue', - limiter: limitPerSec > 0 ? { - max: limitPerSec * 5, - duration: 5000 - } : undefined - }); -} +import { dbQueue, deliverQueue, inboxQueue, objectStorageQueue } from './queues'; export type InboxJobData = { activity: IActivity, @@ -44,11 +28,6 @@ function renderError(e: Error): any { }; } -export const deliverQueue = initializeQueue('deliver', config.deliverJobPerSec || 128); -export const inboxQueue = initializeQueue('inbox', config.inboxJobPerSec || 16); -export const dbQueue = initializeQueue('db'); -export const objectStorageQueue = initializeQueue('objectStorage'); - const deliverLogger = queueLogger.createSubLogger('deliver'); const inboxLogger = queueLogger.createSubLogger('inbox'); const dbLogger = queueLogger.createSubLogger('db'); diff --git a/src/queue/initialize.ts b/src/queue/initialize.ts new file mode 100644 index 0000000000..92579531e4 --- /dev/null +++ b/src/queue/initialize.ts @@ -0,0 +1,18 @@ +import * as Queue from 'bull'; +import config from '../config'; + +export function initialize(name: string, limitPerSec = -1) { + return new Queue(name, { + redis: { + port: config.redis.port, + host: config.redis.host, + password: config.redis.pass, + db: config.redis.db || 0, + }, + prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue', + limiter: limitPerSec > 0 ? { + max: limitPerSec * 5, + duration: 5000 + } : undefined + }); +} diff --git a/src/queue/queues.ts b/src/queue/queues.ts new file mode 100644 index 0000000000..d589d9f7da --- /dev/null +++ b/src/queue/queues.ts @@ -0,0 +1,7 @@ +import config from '../config'; +import { initialize as initializeQueue } from './initialize'; + +export const deliverQueue = initializeQueue('deliver', config.deliverJobPerSec || 128); +export const inboxQueue = initializeQueue('inbox', config.inboxJobPerSec || 16); +export const dbQueue = initializeQueue('db'); +export const objectStorageQueue = initializeQueue('objectStorage'); diff --git a/src/services/chart/charts/classes/active-users.ts b/src/services/chart/charts/classes/active-users.ts index 5128150de6..4820f8281b 100644 --- a/src/services/chart/charts/classes/active-users.ts +++ b/src/services/chart/charts/classes/active-users.ts @@ -17,6 +17,18 @@ export default class ActiveUsersChart extends Chart<ActiveUsersLog> { return {}; } + @autobind + protected aggregate(logs: ActiveUsersLog[]): ActiveUsersLog { + return { + local: { + users: logs.reduce((a, b) => a.concat(b.local.users), [] as ActiveUsersLog['local']['users']), + }, + remote: { + users: logs.reduce((a, b) => a.concat(b.remote.users), [] as ActiveUsersLog['remote']['users']), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<ActiveUsersLog>> { return {}; @@ -25,11 +37,11 @@ export default class ActiveUsersChart extends Chart<ActiveUsersLog> { @autobind public async update(user: User) { const update: Obj = { - count: 1 + users: [user.id] }; - await this.incIfUnique({ + await this.inc({ [Users.isLocalUser(user) ? 'local' : 'remote']: update - }, 'users', user.id); + }); } } diff --git a/src/services/chart/charts/classes/drive.ts b/src/services/chart/charts/classes/drive.ts index 57bb120beb..46399a34d8 100644 --- a/src/services/chart/charts/classes/drive.ts +++ b/src/services/chart/charts/classes/drive.ts @@ -27,6 +27,28 @@ export default class DriveChart extends Chart<DriveLog> { }; } + @autobind + protected aggregate(logs: DriveLog[]): DriveLog { + return { + local: { + totalCount: logs[0].local.totalCount, + totalSize: logs[0].local.totalSize, + incCount: logs.reduce((a, b) => a + b.local.incCount, 0), + incSize: logs.reduce((a, b) => a + b.local.incSize, 0), + decCount: logs.reduce((a, b) => a + b.local.decCount, 0), + decSize: logs.reduce((a, b) => a + b.local.decSize, 0), + }, + remote: { + totalCount: logs[0].remote.totalCount, + totalSize: logs[0].remote.totalSize, + incCount: logs.reduce((a, b) => a + b.remote.incCount, 0), + incSize: logs.reduce((a, b) => a + b.remote.incSize, 0), + decCount: logs.reduce((a, b) => a + b.remote.decCount, 0), + decSize: logs.reduce((a, b) => a + b.remote.decSize, 0), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<DriveLog>> { const [localCount, remoteCount, localSize, remoteSize] = await Promise.all([ diff --git a/src/services/chart/charts/classes/federation.ts b/src/services/chart/charts/classes/federation.ts index bd2c497e7b..ab6ec2d4dd 100644 --- a/src/services/chart/charts/classes/federation.ts +++ b/src/services/chart/charts/classes/federation.ts @@ -20,6 +20,17 @@ export default class FederationChart extends Chart<FederationLog> { }; } + @autobind + protected aggregate(logs: FederationLog[]): FederationLog { + return { + instance: { + total: logs[0].instance.total, + inc: logs.reduce((a, b) => a + b.instance.inc, 0), + dec: logs.reduce((a, b) => a + b.instance.dec, 0), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<FederationLog>> { const [total] = await Promise.all([ diff --git a/src/services/chart/charts/classes/hashtag.ts b/src/services/chart/charts/classes/hashtag.ts index 38c3a94f0c..43db5b0a83 100644 --- a/src/services/chart/charts/classes/hashtag.ts +++ b/src/services/chart/charts/classes/hashtag.ts @@ -17,6 +17,18 @@ export default class HashtagChart extends Chart<HashtagLog> { return {}; } + @autobind + protected aggregate(logs: HashtagLog[]): HashtagLog { + return { + local: { + users: logs.reduce((a, b) => a.concat(b.local.users), [] as HashtagLog['local']['users']), + }, + remote: { + users: logs.reduce((a, b) => a.concat(b.remote.users), [] as HashtagLog['remote']['users']), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<HashtagLog>> { return {}; @@ -25,11 +37,11 @@ export default class HashtagChart extends Chart<HashtagLog> { @autobind public async update(hashtag: string, user: User) { const update: Obj = { - count: 1 + users: [user.id] }; - await this.incIfUnique({ + await this.inc({ [Users.isLocalUser(user) ? 'local' : 'remote']: update - }, 'users', user.id, hashtag); + }, hashtag); } } diff --git a/src/services/chart/charts/classes/instance.ts b/src/services/chart/charts/classes/instance.ts index 7575abfb6f..c32b864d87 100644 --- a/src/services/chart/charts/classes/instance.ts +++ b/src/services/chart/charts/classes/instance.ts @@ -36,6 +36,50 @@ export default class InstanceChart extends Chart<InstanceLog> { }; } + @autobind + protected aggregate(logs: InstanceLog[]): InstanceLog { + return { + requests: { + failed: logs.reduce((a, b) => a + b.requests.failed, 0), + succeeded: logs.reduce((a, b) => a + b.requests.succeeded, 0), + received: logs.reduce((a, b) => a + b.requests.received, 0), + }, + notes: { + total: logs[0].notes.total, + inc: logs.reduce((a, b) => a + b.notes.inc, 0), + dec: logs.reduce((a, b) => a + b.notes.dec, 0), + diffs: { + reply: logs.reduce((a, b) => a + b.notes.diffs.reply, 0), + renote: logs.reduce((a, b) => a + b.notes.diffs.renote, 0), + normal: logs.reduce((a, b) => a + b.notes.diffs.normal, 0), + }, + }, + users: { + total: logs[0].users.total, + inc: logs.reduce((a, b) => a + b.users.inc, 0), + dec: logs.reduce((a, b) => a + b.users.dec, 0), + }, + following: { + total: logs[0].following.total, + inc: logs.reduce((a, b) => a + b.following.inc, 0), + dec: logs.reduce((a, b) => a + b.following.dec, 0), + }, + followers: { + total: logs[0].followers.total, + inc: logs.reduce((a, b) => a + b.followers.inc, 0), + dec: logs.reduce((a, b) => a + b.followers.dec, 0), + }, + drive: { + totalFiles: logs[0].drive.totalFiles, + totalUsage: logs[0].drive.totalUsage, + incFiles: logs.reduce((a, b) => a + b.drive.incFiles, 0), + incUsage: logs.reduce((a, b) => a + b.drive.incUsage, 0), + decFiles: logs.reduce((a, b) => a + b.drive.decFiles, 0), + decUsage: logs.reduce((a, b) => a + b.drive.decUsage, 0), + }, + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<InstanceLog>> { const [ diff --git a/src/services/chart/charts/classes/network.ts b/src/services/chart/charts/classes/network.ts index 8b26e5c4c2..693af48f73 100644 --- a/src/services/chart/charts/classes/network.ts +++ b/src/services/chart/charts/classes/network.ts @@ -15,6 +15,17 @@ export default class NetworkChart extends Chart<NetworkLog> { return {}; } + @autobind + protected aggregate(logs: NetworkLog[]): NetworkLog { + return { + incomingRequests: logs.reduce((a, b) => a + b.incomingRequests, 0), + outgoingRequests: logs.reduce((a, b) => a + b.outgoingRequests, 0), + totalTime: logs.reduce((a, b) => a + b.totalTime, 0), + incomingBytes: logs.reduce((a, b) => a + b.incomingBytes, 0), + outgoingBytes: logs.reduce((a, b) => a + b.outgoingBytes, 0), + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<NetworkLog>> { return {}; diff --git a/src/services/chart/charts/classes/notes.ts b/src/services/chart/charts/classes/notes.ts index 815061c445..965087bc08 100644 --- a/src/services/chart/charts/classes/notes.ts +++ b/src/services/chart/charts/classes/notes.ts @@ -25,6 +25,32 @@ export default class NotesChart extends Chart<NotesLog> { }; } + @autobind + protected aggregate(logs: NotesLog[]): NotesLog { + return { + local: { + total: logs[0].local.total, + inc: logs.reduce((a, b) => a + b.local.inc, 0), + dec: logs.reduce((a, b) => a + b.local.dec, 0), + diffs: { + reply: logs.reduce((a, b) => a + b.local.diffs.reply, 0), + renote: logs.reduce((a, b) => a + b.local.diffs.renote, 0), + normal: logs.reduce((a, b) => a + b.local.diffs.normal, 0), + }, + }, + remote: { + total: logs[0].remote.total, + inc: logs.reduce((a, b) => a + b.remote.inc, 0), + dec: logs.reduce((a, b) => a + b.remote.dec, 0), + diffs: { + reply: logs.reduce((a, b) => a + b.remote.diffs.reply, 0), + renote: logs.reduce((a, b) => a + b.remote.diffs.renote, 0), + normal: logs.reduce((a, b) => a + b.remote.diffs.normal, 0), + }, + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<NotesLog>> { const [localCount, remoteCount] = await Promise.all([ diff --git a/src/services/chart/charts/classes/per-user-drive.ts b/src/services/chart/charts/classes/per-user-drive.ts index aed9f6fce7..e778f7bf61 100644 --- a/src/services/chart/charts/classes/per-user-drive.ts +++ b/src/services/chart/charts/classes/per-user-drive.ts @@ -20,6 +20,18 @@ export default class PerUserDriveChart extends Chart<PerUserDriveLog> { }; } + @autobind + protected aggregate(logs: PerUserDriveLog[]): PerUserDriveLog { + return { + totalCount: logs[0].totalCount, + totalSize: logs[0].totalSize, + incCount: logs.reduce((a, b) => a + b.incCount, 0), + incSize: logs.reduce((a, b) => a + b.incSize, 0), + decCount: logs.reduce((a, b) => a + b.decCount, 0), + decSize: logs.reduce((a, b) => a + b.decSize, 0), + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<PerUserDriveLog>> { const [count, size] = await Promise.all([ diff --git a/src/services/chart/charts/classes/per-user-following.ts b/src/services/chart/charts/classes/per-user-following.ts index 8295c0cb0d..8b536009c8 100644 --- a/src/services/chart/charts/classes/per-user-following.ts +++ b/src/services/chart/charts/classes/per-user-following.ts @@ -35,6 +35,36 @@ export default class PerUserFollowingChart extends Chart<PerUserFollowingLog> { }; } + @autobind + protected aggregate(logs: PerUserFollowingLog[]): PerUserFollowingLog { + return { + local: { + followings: { + total: logs[0].local.followings.total, + inc: logs.reduce((a, b) => a + b.local.followings.inc, 0), + dec: logs.reduce((a, b) => a + b.local.followings.dec, 0), + }, + followers: { + total: logs[0].local.followers.total, + inc: logs.reduce((a, b) => a + b.local.followers.inc, 0), + dec: logs.reduce((a, b) => a + b.local.followers.dec, 0), + }, + }, + remote: { + followings: { + total: logs[0].remote.followings.total, + inc: logs.reduce((a, b) => a + b.remote.followings.inc, 0), + dec: logs.reduce((a, b) => a + b.remote.followings.dec, 0), + }, + followers: { + total: logs[0].remote.followers.total, + inc: logs.reduce((a, b) => a + b.remote.followers.inc, 0), + dec: logs.reduce((a, b) => a + b.remote.followers.dec, 0), + }, + }, + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<PerUserFollowingLog>> { const [ diff --git a/src/services/chart/charts/classes/per-user-notes.ts b/src/services/chart/charts/classes/per-user-notes.ts index cccd495604..8d1fb8d2b0 100644 --- a/src/services/chart/charts/classes/per-user-notes.ts +++ b/src/services/chart/charts/classes/per-user-notes.ts @@ -20,6 +20,20 @@ export default class PerUserNotesChart extends Chart<PerUserNotesLog> { }; } + @autobind + protected aggregate(logs: PerUserNotesLog[]): PerUserNotesLog { + return { + total: logs[0].total, + inc: logs.reduce((a, b) => a + b.inc, 0), + dec: logs.reduce((a, b) => a + b.dec, 0), + diffs: { + reply: logs.reduce((a, b) => a + b.diffs.reply, 0), + renote: logs.reduce((a, b) => a + b.diffs.renote, 0), + normal: logs.reduce((a, b) => a + b.diffs.normal, 0), + }, + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<PerUserNotesLog>> { const [count] = await Promise.all([ diff --git a/src/services/chart/charts/classes/per-user-reactions.ts b/src/services/chart/charts/classes/per-user-reactions.ts index 124fb4153c..b4cdced40c 100644 --- a/src/services/chart/charts/classes/per-user-reactions.ts +++ b/src/services/chart/charts/classes/per-user-reactions.ts @@ -18,6 +18,18 @@ export default class PerUserReactionsChart extends Chart<PerUserReactionsLog> { return {}; } + @autobind + protected aggregate(logs: PerUserReactionsLog[]): PerUserReactionsLog { + return { + local: { + count: logs.reduce((a, b) => a + b.local.count, 0), + }, + remote: { + count: logs.reduce((a, b) => a + b.remote.count, 0), + }, + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<PerUserReactionsLog>> { return {}; diff --git a/src/services/chart/charts/classes/test-grouped.ts b/src/services/chart/charts/classes/test-grouped.ts index e32cbcf416..92c8df636e 100644 --- a/src/services/chart/charts/classes/test-grouped.ts +++ b/src/services/chart/charts/classes/test-grouped.ts @@ -21,6 +21,17 @@ export default class TestGroupedChart extends Chart<TestGroupedLog> { }; } + @autobind + protected aggregate(logs: TestGroupedLog[]): TestGroupedLog { + return { + foo: { + total: logs[0].foo.total, + inc: logs.reduce((a, b) => a + b.foo.inc, 0), + dec: logs.reduce((a, b) => a + b.foo.dec, 0), + }, + }; + } + @autobind protected async fetchActual(group: string): Promise<DeepPartial<TestGroupedLog>> { return { diff --git a/src/services/chart/charts/classes/test-unique.ts b/src/services/chart/charts/classes/test-unique.ts index 1eb396c293..5680d713ec 100644 --- a/src/services/chart/charts/classes/test-unique.ts +++ b/src/services/chart/charts/classes/test-unique.ts @@ -15,6 +15,13 @@ export default class TestUniqueChart extends Chart<TestUniqueLog> { return {}; } + @autobind + protected aggregate(logs: TestUniqueLog[]): TestUniqueLog { + return { + foo: logs.reduce((a, b) => a.concat(b.foo), [] as TestUniqueLog['foo']), + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<TestUniqueLog>> { return {}; @@ -22,8 +29,8 @@ export default class TestUniqueChart extends Chart<TestUniqueLog> { @autobind public async uniqueIncrement(key: string) { - await this.incIfUnique({ - foo: 1 - }, 'foos', key); + await this.inc({ + foo: [key] + }); } } diff --git a/src/services/chart/charts/classes/test.ts b/src/services/chart/charts/classes/test.ts index ea64040f3e..d37d298de7 100644 --- a/src/services/chart/charts/classes/test.ts +++ b/src/services/chart/charts/classes/test.ts @@ -21,6 +21,17 @@ export default class TestChart extends Chart<TestLog> { }; } + @autobind + protected aggregate(logs: TestLog[]): TestLog { + return { + foo: { + total: logs[0].foo.total, + inc: logs.reduce((a, b) => a + b.foo.inc, 0), + dec: logs.reduce((a, b) => a + b.foo.dec, 0), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<TestLog>> { return { diff --git a/src/services/chart/charts/classes/users.ts b/src/services/chart/charts/classes/users.ts index 47e4caa1b7..87b19d88f9 100644 --- a/src/services/chart/charts/classes/users.ts +++ b/src/services/chart/charts/classes/users.ts @@ -25,6 +25,22 @@ export default class UsersChart extends Chart<UsersLog> { }; } + @autobind + protected aggregate(logs: UsersLog[]): UsersLog { + return { + local: { + total: logs[0].local.total, + inc: logs.reduce((a, b) => a + b.local.inc, 0), + dec: logs.reduce((a, b) => a + b.local.dec, 0), + }, + remote: { + total: logs[0].remote.total, + inc: logs.reduce((a, b) => a + b.remote.inc, 0), + dec: logs.reduce((a, b) => a + b.remote.dec, 0), + }, + }; + } + @autobind protected async fetchActual(): Promise<DeepPartial<UsersLog>> { const [localCount, remoteCount] = await Promise.all([ diff --git a/src/services/chart/charts/schemas/active-users.ts b/src/services/chart/charts/schemas/active-users.ts index 6e26bb4698..cdf0579efb 100644 --- a/src/services/chart/charts/schemas/active-users.ts +++ b/src/services/chart/charts/schemas/active-users.ts @@ -1,11 +1,15 @@ export const logSchema = { /** - * アクティブユーザー数 + * アクティブユーザー */ - count: { - type: 'number' as const, + users: { + type: 'array' as const, optional: false as const, nullable: false as const, - description: 'アクティブユーザー数', + description: 'アクティブユーザー', + items: { + type: 'string' as const, + optional: false as const, nullable: false as const, + } }, }; diff --git a/src/services/chart/charts/schemas/hashtag.ts b/src/services/chart/charts/schemas/hashtag.ts index 4dfd61c97f..791d0d1721 100644 --- a/src/services/chart/charts/schemas/hashtag.ts +++ b/src/services/chart/charts/schemas/hashtag.ts @@ -1,11 +1,15 @@ export const logSchema = { /** - * 投稿された数 + * 投稿したユーザー */ - count: { - type: 'number' as const, + users: { + type: 'array' as const, optional: false as const, nullable: false as const, - description: '投稿された数', + description: '投稿したユーザー', + items: { + type: 'string' as const, + optional: false as const, nullable: false as const, + } }, }; diff --git a/src/services/chart/charts/schemas/test-unique.ts b/src/services/chart/charts/schemas/test-unique.ts index 075a8092d9..51280400ac 100644 --- a/src/services/chart/charts/schemas/test-unique.ts +++ b/src/services/chart/charts/schemas/test-unique.ts @@ -3,9 +3,12 @@ export const schema = { optional: false as const, nullable: false as const, properties: { foo: { - type: 'number' as const, + type: 'array' as const, optional: false as const, nullable: false as const, - description: '' + items: { + type: 'string' as const, + optional: false as const, nullable: false as const, + } }, } }; diff --git a/src/services/chart/core.ts b/src/services/chart/core.ts index dc09923ae4..10621be073 100644 --- a/src/services/chart/core.ts +++ b/src/services/chart/core.ts @@ -24,8 +24,6 @@ type ArrayValue<T> = { [P in keyof T]: T[P] extends number ? T[P][] : ArrayValue<T[P]>; }; -type Span = 'day' | 'hour'; - type Log = { id: number; @@ -38,22 +36,14 @@ type Log = { * 集計日時のUnixタイムスタンプ(秒) */ date: number; - - /** - * 集計期間 - */ - span: Span; - - /** - * ユニークインクリメント用 - */ - unique?: Record<string, any>; }; const camelToSnake = (str: string) => { return str.replace(/([A-Z])/g, s => '_' + s.charAt(0).toLowerCase()); }; +const removeDuplicates = (array: any[]) => Array.from(new Set(array)); + /** * 様々なチャートの管理を司るクラス */ @@ -62,10 +52,21 @@ export default abstract class Chart<T extends Record<string, any>> { private static readonly columnDot = '_'; private name: string; + private queue: { + diff: DeepPartial<T>; + group: string | null; + }[] = []; public schema: Schema; protected repository: Repository<Log>; + protected abstract genNewLog(latest: T): DeepPartial<T>; - protected abstract async fetchActual(group: string | null): Promise<DeepPartial<T>>; + + /** + * @param logs 日時が新しい方が先頭 + */ + protected abstract aggregate(logs: T[]): T; + + protected abstract fetchActual(group: string | null): Promise<DeepPartial<T>>; @autobind private static convertSchemaToFlatColumnDefinitions(schema: Schema) { @@ -75,10 +76,15 @@ export default abstract class Chart<T extends Record<string, any>> { const p = path ? `${path}${this.columnDot}${k}` : k; if (v.type === 'object') { flatColumns(v.properties, p); - } else { + } else if (v.type === 'number') { columns[this.columnPrefix + p] = { type: 'bigint', }; + } else if (v.type === 'array' && v.items.type === 'string') { + columns[this.columnPrefix + p] = { + type: 'varchar', + array: true, + }; } } }; @@ -99,11 +105,11 @@ export default abstract class Chart<T extends Record<string, any>> { @autobind private static convertObjectToFlattenColumns(x: Record<string, any>) { - const columns = {} as Record<string, number>; + const columns = {} as Record<string, number | unknown[]>; const flatten = (x: Obj, path?: string) => { for (const [k, v] of Object.entries(x)) { const p = path ? `${path}${this.columnDot}${k}` : k; - if (typeof v === 'object') { + if (typeof v === 'object' && !Array.isArray(v)) { flatten(v, p); } else { columns[this.columnPrefix + p] = v; @@ -115,14 +121,37 @@ export default abstract class Chart<T extends Record<string, any>> { } @autobind - private static convertQuery(x: Record<string, any>) { + private static countUniqueFields(x: Record<string, any>) { + const exec = (x: Obj) => { + const res = {} as Record<string, any>; + for (const [k, v] of Object.entries(x)) { + if (typeof v === 'object' && !Array.isArray(v)) { + res[k] = exec(v); + } else if (Array.isArray(v)) { + res[k] = Array.from(new Set(v)).length; + } else { + res[k] = v; + } + } + return res; + }; + return exec(x); + } + + @autobind + private static convertQuery(diff: Record<string, number | unknown[]>) { const query: Record<string, Function> = {}; - const columns = Chart.convertObjectToFlattenColumns(x); - - for (const [k, v] of Object.entries(columns)) { - if (v > 0) query[k] = () => `"${k}" + ${v}`; - if (v < 0) query[k] = () => `"${k}" - ${Math.abs(v)}`; + for (const [k, v] of Object.entries(diff)) { + if (typeof v === 'number') { + if (v > 0) query[k] = () => `"${k}" + ${v}`; + if (v < 0) query[k] = () => `"${k}" - ${Math.abs(v)}`; + } else if (Array.isArray(v)) { + // TODO: item が文字列以外の場合も対応 + // TODO: item をSQLエスケープ + const items = v.map(item => `"${item}"`).join(','); + query[k] = () => `array_cat("${k}", '{${items}}'::varchar[])`; + } } return query; @@ -169,28 +198,14 @@ export default abstract class Chart<T extends Record<string, any>> { length: 128, nullable: true }, - span: { - type: 'enum', - enum: ['hour', 'day'] - }, - unique: { - type: 'jsonb', - default: {} - }, ...Chart.convertSchemaToFlatColumnDefinitions(schema) }, indices: [{ columns: ['date'] - }, { - columns: ['span'] }, { columns: ['group'] - }, { - columns: ['span', 'date'] }, { columns: ['date', 'group'] - }, { - columns: ['span', 'date', 'group'] }] }); } @@ -200,7 +215,7 @@ export default abstract class Chart<T extends Record<string, any>> { this.schema = schema; const entity = Chart.schemaToEntity(name, schema); - const keys = ['span', 'date']; + const keys = ['date']; if (grouped) keys.push('group'); entity.options.uniques = [{ @@ -220,7 +235,8 @@ export default abstract class Chart<T extends Record<string, any>> { flatColumns(v.properties, p); } else { if (nestedProperty.get(log, p) == null) { - nestedProperty.set(log, p, 0); + const emptyValue = v.type === 'number' ? 0 : []; + nestedProperty.set(log, p, emptyValue); } } } @@ -230,10 +246,9 @@ export default abstract class Chart<T extends Record<string, any>> { } @autobind - private getLatestLog(span: Span, group: string | null = null): Promise<Log | null> { + private getLatestLog(group: string | null = null): Promise<Log | null> { return this.repository.findOne({ group: group, - span: span }, { order: { date: -1 @@ -242,17 +257,13 @@ export default abstract class Chart<T extends Record<string, any>> { } @autobind - private async getCurrentLog(span: Span, group: string | null = null): Promise<Log> { + private async getCurrentLog(group: string | null = null): Promise<Log> { const [y, m, d, h] = Chart.getCurrentDate(); - const current = - span == 'day' ? dateUTC([y, m, d, 0]) : - span == 'hour' ? dateUTC([y, m, d, h]) : - null as never; + const current = dateUTC([y, m, d, h]); - // 現在(今日または今のHour)のログ + // 現在(=今のHour)のログ const currentLog = await this.repository.findOne({ - span: span, date: Chart.dateToTimestamp(current), ...(group ? { group: group } : {}) }); @@ -271,7 +282,7 @@ export default abstract class Chart<T extends Record<string, any>> { // * 昨日何もチャートを更新するような出来事がなかった場合は、 // * ログがそもそも作られずドキュメントが存在しないということがあり得るため、 // * 「昨日の」と決め打ちせずに「もっとも最近の」とします - const latest = await this.getLatestLog(span, group); + const latest = await this.getLatestLog(group); if (latest != null) { const obj = Chart.convertFlattenColumnsToObject( @@ -286,17 +297,16 @@ export default abstract class Chart<T extends Record<string, any>> { // 初期ログデータを作成 data = this.getNewLog(null); - logger.info(`${this.name + (group ? `:${group}` : '')} (${span}): Initial commit created`); + logger.info(`${this.name + (group ? `:${group}` : '')}: Initial commit created`); } const date = Chart.dateToTimestamp(current); - const lockKey = `${this.name}:${date}:${group}:${span}`; + const lockKey = `${this.name}:${date}:${group}`; const unlock = await getChartInsertLock(lockKey); try { // ロック内でもう1回チェックする const currentLog = await this.repository.findOne({ - span: span, date: date, ...(group ? { group: group } : {}) }); @@ -307,12 +317,11 @@ export default abstract class Chart<T extends Record<string, any>> { // 新規ログ挿入 log = await this.repository.save({ group: group, - span: span, date: date, ...Chart.convertObjectToFlattenColumns(data) }); - logger.info(`${this.name + (group ? `:${group}` : '')} (${span}): New commit created`); + logger.info(`${this.name + (group ? `:${group}` : '')}: New commit created`); return log; } finally { @@ -321,38 +330,62 @@ export default abstract class Chart<T extends Record<string, any>> { } @autobind - protected commit(query: Record<string, Function>, group: string | null = null, uniqueKey?: string, uniqueValue?: string): Promise<any> { - const update = async (log: Log) => { - // ユニークインクリメントの場合、指定のキーに指定の値が既に存在していたら弾く - if ( - uniqueKey && log.unique && - log.unique[uniqueKey] && - log.unique[uniqueKey].includes(uniqueValue) - ) return; + protected commit(diff: DeepPartial<T>, group: string | null = null): void { + this.queue.push({ + diff, group, + }); + } - // ユニークインクリメントの指定のキーに値を追加 - if (uniqueKey && log.unique) { - if (log.unique[uniqueKey]) { - const sql = `jsonb_set("unique", '{${uniqueKey}}', ("unique"->>'${uniqueKey}')::jsonb || '["${uniqueValue}"]'::jsonb)`; - query['unique'] = () => sql; - } else { - const sql = `jsonb_set("unique", '{${uniqueKey}}', '["${uniqueValue}"]')`; - query['unique'] = () => sql; + @autobind + public async save() { + if (this.queue.length === 0) { + logger.info(`${this.name}: Write skipped`); + return; + } + + // TODO: 前の時間のログがqueueにあった場合のハンドリング + // 例えば、save が20分ごとに行われるとして、前回行われたのは 01:50 だったとする。 + // 次に save が行われるのは 02:10 ということになるが、もし 01:55 に新規ログが queue に追加されたとすると、 + // そのログは本来は 01:00~ のログとしてDBに保存されて欲しいのに、02:00~ のログ扱いになってしまう。 + // これを回避するための実装は複雑になりそうなため、一旦保留。 + + const update = async (log: Log) => { + const finalDiffs = {} as Record<string, number | unknown[]>; + + for (const diff of this.queue.filter(q => q.group === log.group).map(q => q.diff)) { + const columns = Chart.convertObjectToFlattenColumns(diff); + + for (const [k, v] of Object.entries(columns)) { + if (finalDiffs[k] == null) { + finalDiffs[k] = v; + } else { + if (typeof finalDiffs[k] === 'number') { + (finalDiffs[k] as number) += v as number; + } else { + (finalDiffs[k] as unknown[]) = (finalDiffs[k] as unknown[]).concat(v); + } + } } } + const query = Chart.convertQuery(finalDiffs); + // ログ更新 await this.repository.createQueryBuilder() .update() .set(query) .where('id = :id', { id: log.id }) .execute(); + + logger.info(`${this.name + (log.group ? `:${log.group}` : '')}: Updated`); + + // TODO: この一連の処理が始まった後に新たにqueueに入ったものは消さないようにする + this.queue = this.queue.filter(q => q.group !== log.group); }; - return Promise.all([ - this.getCurrentLog('day', group).then(log => update(log)), - this.getCurrentLog('hour', group).then(log => update(log)), - ]); + const groups = removeDuplicates(this.queue.map(log => log.group)); + + await Promise.all(groups.map(group => this.getCurrentLog(group).then(log => update(log)))); } @autobind @@ -367,39 +400,30 @@ export default abstract class Chart<T extends Record<string, any>> { .execute(); }; - return Promise.all([ - this.getCurrentLog('day', group).then(log => update(log)), - this.getCurrentLog('hour', group).then(log => update(log)), - ]); + return this.getCurrentLog(group).then(log => update(log)); } @autobind protected async inc(inc: DeepPartial<T>, group: string | null = null): Promise<void> { - await this.commit(Chart.convertQuery(inc as any), group); + await this.commit(inc, group); } @autobind - protected async incIfUnique(inc: DeepPartial<T>, key: string, value: string, group: string | null = null): Promise<void> { - await this.commit(Chart.convertQuery(inc as any), group, key, value); - } - - @autobind - public async getChart(span: Span, amount: number, begin: Date | null, group: string | null = null): Promise<ArrayValue<T>> { - const [y, m, d, h, _m, _s, _ms] = begin ? Chart.parseDate(subtractTime(addTime(begin, 1, span), 1)) : Chart.getCurrentDate(); - const [y2, m2, d2, h2] = begin ? Chart.parseDate(addTime(begin, 1, span)) : [] as never; + public async getChart(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise<ArrayValue<T>> { + const [y, m, d, h, _m, _s, _ms] = cursor ? Chart.parseDate(subtractTime(addTime(cursor, 1, span), 1)) : Chart.getCurrentDate(); + const [y2, m2, d2, h2] = cursor ? Chart.parseDate(addTime(cursor, 1, span)) : [] as never; const lt = dateUTC([y, m, d, h, _m, _s, _ms]); const gt = - span === 'day' ? subtractTime(begin ? dateUTC([y2, m2, d2, 0]) : dateUTC([y, m, d, 0]), amount - 1, 'day') : - span === 'hour' ? subtractTime(begin ? dateUTC([y2, m2, d2, h2]) : dateUTC([y, m, d, h]), amount - 1, 'hour') : + span === 'day' ? subtractTime(cursor ? dateUTC([y2, m2, d2, 0]) : dateUTC([y, m, d, 0]), amount - 1, 'day') : + span === 'hour' ? subtractTime(cursor ? dateUTC([y2, m2, d2, h2]) : dateUTC([y, m, d, h]), amount - 1, 'hour') : null as never; // ログ取得 let logs = await this.repository.find({ where: { group: group, - span: span, date: Between(Chart.dateToTimestamp(gt), Chart.dateToTimestamp(lt)) }, order: { @@ -413,7 +437,6 @@ export default abstract class Chart<T extends Record<string, any>> { // (すくなくともひとつログが無いと隙間埋めできないため) const recentLog = await this.repository.findOne({ group: group, - span: span }, { order: { date: -1 @@ -430,7 +453,6 @@ export default abstract class Chart<T extends Record<string, any>> { // (隙間埋めできないため) const outdatedLog = await this.repository.findOne({ group: group, - span: span, date: LessThan(Chart.dateToTimestamp(gt)) }, { order: { @@ -445,23 +467,56 @@ export default abstract class Chart<T extends Record<string, any>> { const chart: T[] = []; - // 整形 - for (let i = (amount - 1); i >= 0; i--) { - const current = - span === 'day' ? subtractTime(dateUTC([y, m, d, 0]), i, 'day') : - span === 'hour' ? subtractTime(dateUTC([y, m, d, h]), i, 'hour') : - null as never; + if (span === 'hour') { + for (let i = (amount - 1); i >= 0; i--) { + const current = subtractTime(dateUTC([y, m, d, h]), i, 'hour'); - const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current)); + const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current)); - if (log) { - const data = Chart.convertFlattenColumnsToObject(log as Record<string, any>); - chart.unshift(data); - } else { - // 隙間埋め - const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); - const data = latest ? Chart.convertFlattenColumnsToObject(latest as Record<string, any>) : null; - chart.unshift(this.getNewLog(data)); + if (log) { + const data = Chart.convertFlattenColumnsToObject(log as Record<string, any>); + chart.unshift(Chart.countUniqueFields(data)); + } else { + // 隙間埋め + const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); + const data = latest ? Chart.convertFlattenColumnsToObject(latest as Record<string, any>) : null; + chart.unshift(Chart.countUniqueFields(this.getNewLog(data))); + } + } + } else if (span === 'day') { + const logsForEachDays: T[][] = []; + let currentDay = -1; + let currentDayIndex = -1; + for (let i = ((amount - 1) * 24) + h; i >= 0; i--) { + const current = subtractTime(dateUTC([y, m, d, h]), i, 'hour'); + const _currentDay = Chart.parseDate(current)[2]; + if (currentDay != _currentDay) currentDayIndex++; + currentDay = _currentDay; + + const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current)); + + if (log) { + if (logsForEachDays[currentDayIndex]) { + logsForEachDays[currentDayIndex].unshift(Chart.convertFlattenColumnsToObject(log)); + } else { + logsForEachDays[currentDayIndex] = [Chart.convertFlattenColumnsToObject(log)]; + } + } else { + // 隙間埋め + const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); + const data = latest ? Chart.convertFlattenColumnsToObject(latest as Record<string, any>) : null; + const newLog = this.getNewLog(data); + if (logsForEachDays[currentDayIndex]) { + logsForEachDays[currentDayIndex].unshift(newLog); + } else { + logsForEachDays[currentDayIndex] = [newLog]; + } + } + } + + for (const logs of logsForEachDays) { + const log = this.aggregate(logs); + chart.unshift(Chart.countUniqueFields(log)); } } @@ -473,20 +528,19 @@ export default abstract class Chart<T extends Record<string, any>> { * { foo: [1, 2, 3], bar: [5, 6, 7] } * にする */ - const dive = (x: Obj, path?: string) => { + const compact = (x: Obj, path?: string) => { for (const [k, v] of Object.entries(x)) { const p = path ? `${path}.${k}` : k; - if (typeof v == 'object') { - dive(v, p); + if (typeof v === 'object' && !Array.isArray(v)) { + compact(v, p); } else { - const values = chart.map(s => nestedProperty.get(s, p)) - .map(v => parseInt(v, 10)); // TypeORMのバグ(?)で何故か数値カラムの値が文字列型になっているので数値に戻す + const values = chart.map(s => nestedProperty.get(s, p)); nestedProperty.set(res, p, values); } } }; - dive(chart[0]); + compact(chart[0]); return res; } diff --git a/src/services/chart/index.ts b/src/services/chart/index.ts index 9626e3d6b3..dde02bd64d 100644 --- a/src/services/chart/index.ts +++ b/src/services/chart/index.ts @@ -10,6 +10,7 @@ import PerUserReactionsChart from './charts/classes/per-user-reactions'; import HashtagChart from './charts/classes/hashtag'; import PerUserFollowingChart from './charts/classes/per-user-following'; import PerUserDriveChart from './charts/classes/per-user-drive'; +import { beforeShutdown } from '../../misc/before-shutdown'; export const federationChart = new FederationChart(); export const notesChart = new NotesChart(); @@ -23,3 +24,27 @@ export const perUserReactionsChart = new PerUserReactionsChart(); export const hashtagChart = new HashtagChart(); export const perUserFollowingChart = new PerUserFollowingChart(); export const perUserDriveChart = new PerUserDriveChart(); + +const charts = [ + federationChart, + notesChart, + usersChart, + networkChart, + activeUsersChart, + instanceChart, + perUserNotesChart, + driveChart, + perUserReactionsChart, + hashtagChart, + perUserFollowingChart, + perUserDriveChart, +]; + +// 20分おきにメモリ情報をDBに書き込み +setInterval(() => { + for (const chart of charts) { + chart.save(); + } +}, 1000 * 60 * 20); + +beforeShutdown(() => Promise.all(charts.map(chart => chart.save()))); diff --git a/test/chart.ts b/test/chart.ts index 25b083db17..55f6bd696c 100644 --- a/test/chart.ts +++ b/test/chart.ts @@ -72,7 +72,7 @@ describe('Chart', () => { testUniqueChart = new TestUniqueChart(); clock = lolex.install({ - now: new Date('2000-01-01 00:00:00') + now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)) }); done(); }); @@ -85,6 +85,7 @@ describe('Chart', () => { it('Can updates', async(async () => { await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -105,9 +106,10 @@ describe('Chart', () => { }, }); })); - + it('Can updates (dec)', async(async () => { await testChart.decrement(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -154,6 +156,7 @@ describe('Chart', () => { await testChart.increment(); await testChart.increment(); await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -177,10 +180,12 @@ describe('Chart', () => { it('Can updates at different times', async(async () => { await testChart.increment(); + await testChart.save(); clock.tick('01:00:00'); await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -202,12 +207,45 @@ describe('Chart', () => { }); })); + // 仕様上はこうなってほしいけど、実装は難しそうなのでskip + /* + it('Can updates at different times without save', async(async () => { + await testChart.increment(); + + clock.tick('01:00:00'); + + await testChart.increment(); + await testChart.save(); + + const chartHours = await testChart.getChart('hour', 3, null); + const chartDays = await testChart.getChart('day', 3, null); + + assert.deepStrictEqual(chartHours, { + foo: { + dec: [0, 0, 0], + inc: [1, 1, 0], + total: [2, 1, 0] + }, + }); + + assert.deepStrictEqual(chartDays, { + foo: { + dec: [0, 0, 0], + inc: [2, 0, 0], + total: [2, 0, 0] + }, + }); + })); + */ + it('Can padding', async(async () => { await testChart.increment(); + await testChart.save(); clock.tick('02:00:00'); await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -232,6 +270,7 @@ describe('Chart', () => { // 要求された範囲にログがひとつもない場合でもパディングできる it('Can padding from past range', async(async () => { await testChart.increment(); + await testChart.save(); clock.tick('05:00:00'); @@ -259,8 +298,12 @@ describe('Chart', () => { // Issue #3190 it('Can padding from past range 2', async(async () => { await testChart.increment(); + await testChart.save(); + clock.tick('05:00:00'); + await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -284,10 +327,12 @@ describe('Chart', () => { it('Can specify offset', async(async () => { await testChart.increment(); + await testChart.save(); clock.tick('01:00:00'); await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, new Date(Date.UTC(2000, 0, 1, 0, 0, 0))); const chartDays = await testChart.getChart('day', 3, new Date(Date.UTC(2000, 0, 1, 0, 0, 0))); @@ -313,10 +358,12 @@ describe('Chart', () => { clock.tick('00:30:00'); await testChart.increment(); + await testChart.save(); clock.tick('01:30:00'); await testChart.increment(); + await testChart.save(); const chartHours = await testChart.getChart('hour', 3, new Date(Date.UTC(2000, 0, 1, 0, 0, 0))); const chartDays = await testChart.getChart('day', 3, new Date(Date.UTC(2000, 0, 1, 0, 0, 0))); @@ -341,6 +388,7 @@ describe('Chart', () => { describe('Grouped', () => { it('Can updates', async(async () => { await testGroupedChart.increment('alice'); + await testGroupedChart.save(); const aliceChartHours = await testGroupedChart.getChart('hour', 3, null, 'alice'); const aliceChartDays = await testGroupedChart.getChart('day', 3, null, 'alice'); @@ -386,6 +434,7 @@ describe('Chart', () => { await testUniqueChart.uniqueIncrement('alice'); await testUniqueChart.uniqueIncrement('alice'); await testUniqueChart.uniqueIncrement('bob'); + await testUniqueChart.save(); const chartHours = await testUniqueChart.getChart('hour', 3, null); const chartDays = await testUniqueChart.getChart('day', 3, null); @@ -428,6 +477,7 @@ describe('Chart', () => { it('Can resync (2)', async(async () => { await testChart.increment(); + await testChart.save(); clock.tick('01:00:00'); diff --git a/yarn.lock b/yarn.lock index 274f07631c..c838362820 100644 --- a/yarn.lock +++ b/yarn.lock @@ -358,10 +358,10 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== +"@sinonjs/fake-timers@7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.0.2.tgz#a53e71d4154ee704ea9b36a6d0b0780e246fadd1" + integrity sha512-dF84L5YC90gIOegPDCYymPIsDmwMWWSh7BwfDXQYePi8lVIEp7IZ1UVGkME8FjXOsDPxan12x4aaK+Lo6wVh9A== dependencies: "@sinonjs/commons" "^1.7.0" From 48d767918f5f2dee52491e82788f3502738ed48a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Mar 2021 13:20:52 +0900 Subject: [PATCH 32/76] Bump jsdom from 16.5.0 to 16.5.1 (#7357) Bumps [jsdom](https://github.com/jsdom/jsdom) from 16.5.0 to 16.5.1. - [Release notes](https://github.com/jsdom/jsdom/releases) - [Changelog](https://github.com/jsdom/jsdom/blob/master/Changelog.md) - [Commits](https://github.com/jsdom/jsdom/compare/16.5.0...16.5.1) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 22 ++++++---------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 4d7f3c8353..44fc4e2aeb 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,7 @@ "is-root": "2.1.0", "is-svg": "4.2.2", "js-yaml": "4.0.0", - "jsdom": "16.5.0", + "jsdom": "16.5.1", "json5": "2.2.0", "json5-loader": "4.0.1", "jsonld": "4.0.1", diff --git a/yarn.lock b/yarn.lock index c838362820..09910149ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1410,12 +1410,7 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -abab@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" - integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== - -abab@^2.0.5: +abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== @@ -1468,12 +1463,7 @@ acorn@^7.1.1, acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.4: - version "8.0.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.0.4.tgz#7a3ae4191466a6984eee0fe3407a4f3aa9db8354" - integrity sha512-XNP0PqF1XD19ZlLKvB7cMmnZswW4C/03pRHgirB30uSJTaS3A3V1/P4sS3HPvFmjoriPCJQs+JDSbm4bL1TxGQ== - -acorn@^8.0.5: +acorn@^8.0.4, acorn@^8.0.5: version "8.1.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe" integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA== @@ -5727,10 +5717,10 @@ jschardet@^2.1.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== -jsdom@16.5.0: - version "16.5.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.0.tgz#9e453505600cc5a70b385750d35256f380730cc4" - integrity sha512-QxZH0nmDTnTTVI0YDm4RUlaUPl5dcyn62G5TMDNfMmTW+J1u1v9gCR8WR+WZ6UghAa7nKJjDOFaI00eMMWvJFQ== +jsdom@16.5.1: + version "16.5.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.1.tgz#4ced6bbd7b77d67fb980e64d9e3e6fb900f97dd6" + integrity sha512-pF73EOsJgwZekbDHEY5VO/yKXUkab/DuvrQB/ANVizbr6UAHJsDdHXuotZYwkJSGQl1JM+ivXaqY+XBDDL4TiA== dependencies: abab "^2.0.5" acorn "^8.0.5" From a3fff800d24230e31207b3ac39b6f10766f4cc33 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Mar 2021 13:21:04 +0900 Subject: [PATCH 33/76] Bump css-loader from 5.1.2 to 5.1.3 (#7358) Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.1.2 to 5.1.3. - [Release notes](https://github.com/webpack-contrib/css-loader/releases) - [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.1.2...v5.1.3) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 44fc4e2aeb..32dd175712 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "content-disposition": "0.5.3", "core-js": "3.9.1", "crc-32": "1.2.0", - "css-loader": "5.1.2", + "css-loader": "5.1.3", "cssnano": "4.1.10", "dateformat": "4.5.1", "diskusage": "1.1.3", diff --git a/yarn.lock b/yarn.lock index 09910149ff..cbb95b6970 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3038,10 +3038,10 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" -css-loader@5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.1.2.tgz#b93dba498ec948b543b49d4fab5017205d4f5c3e" - integrity sha512-T7vTXHSx0KrVEg/xjcl7G01RcVXpcw4OELwDPvkr7izQNny85A84dK3dqrczuEfBcu7Yg7mdTjJLSTibRUoRZg== +css-loader@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.1.3.tgz#87f6fc96816b20debe3cf682f85c7e56a963d0d1" + integrity sha512-CoPZvyh8sLiGARK3gqczpfdedbM74klGWurF2CsNZ2lhNaXdLIUks+3Mfax3WBeRuHoglU+m7KG/+7gY6G4aag== dependencies: camelcase "^6.2.0" cssesc "^3.0.0" From 9cc8b3656aa016a6443980488b3cb5d1e431fb34 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 18 Mar 2021 13:33:14 +0900 Subject: [PATCH 34/76] lint --- src/misc/before-shutdown.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/misc/before-shutdown.ts b/src/misc/before-shutdown.ts index 58d0ea5108..8639d42b04 100644 --- a/src/misc/before-shutdown.ts +++ b/src/misc/before-shutdown.ts @@ -32,7 +32,9 @@ const shutdownListeners = []; * @param {function(string)} fn Function to execute on shutdown. */ const processOnce = (signals, fn) => { - return signals.forEach(sig => process.once(sig, fn)); + for (const sig of signals) { + process.once(sig, fn); + } }; /** From 449ea4b669252cff3dee0de0192ce4a353106444 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 18 Mar 2021 17:35:47 +0900 Subject: [PATCH 35/76] perf: reduce query --- src/services/note/reaction/create.ts | 51 ++++++++++++++++------------ 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts index adc96ddc1f..8de9f53a17 100644 --- a/src/services/note/reaction/create.ts +++ b/src/services/note/reaction/create.ts @@ -11,33 +11,40 @@ import { perUserReactionsChart } from '../../chart'; import { genId } from '../../../misc/gen-id'; import { createNotification } from '../../create-notification'; import deleteReaction from './delete'; +import { isDuplicateKeyValueError } from '../../../misc/is-duplicate-key-value-error'; export default async (user: User, note: Note, reaction?: string) => { reaction = await toDbReaction(reaction, user.host); - const exist = await NoteReactions.findOne({ - noteId: note.id, - userId: user.id, - }); - - if (exist) { - if (exist.reaction !== reaction) { - // 別のリアクションがすでにされていたら置き換える - await deleteReaction(user, note); - } else { - // 同じリアクションがすでにされていたら何もしない - return; - } - } + let record; // Create reaction - const inserted = await NoteReactions.save({ - id: genId(), - createdAt: new Date(), - noteId: note.id, - userId: user.id, - reaction - }); + try { + record = await NoteReactions.save({ + id: genId(), + createdAt: new Date(), + noteId: note.id, + userId: user.id, + reaction + }); + } catch (e) { + if (isDuplicateKeyValueError(e)) { + record = await NoteReactions.findOneOrFail({ + noteId: note.id, + userId: user.id, + }); + + if (record.reaction !== reaction) { + // 別のリアクションがすでにされていたら置き換える + await deleteReaction(user, note); + } else { + // 同じリアクションがすでにされていたら何もしない + return; + } + } else { + throw e; + } + } // Increment reactions count const sql = `jsonb_set("reactions", '{${reaction}}', (COALESCE("reactions"->>'${reaction}', '0')::int + 1)::text::jsonb)`; @@ -101,7 +108,7 @@ export default async (user: User, note: Note, reaction?: string) => { //#region 配信 if (Users.isLocalUser(user) && !note.localOnly) { - const content = renderActivity(await renderLike(inserted, note)); + const content = renderActivity(await renderLike(record, note)); const dm = new DeliverManager(user, content); if (note.userHost !== null) { const reactee = await Users.findOne(note.userId); From bffdfea58af44c2faa543ed98cfb878b6d283935 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 18 Mar 2021 17:38:42 +0900 Subject: [PATCH 36/76] refactor --- src/services/note/reaction/create.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts index 8de9f53a17..30a17ad3e9 100644 --- a/src/services/note/reaction/create.ts +++ b/src/services/note/reaction/create.ts @@ -12,11 +12,12 @@ import { genId } from '../../../misc/gen-id'; import { createNotification } from '../../create-notification'; import deleteReaction from './delete'; import { isDuplicateKeyValueError } from '../../../misc/is-duplicate-key-value-error'; +import { NoteReaction } from '../../../models/entities/note-reaction'; export default async (user: User, note: Note, reaction?: string) => { reaction = await toDbReaction(reaction, user.host); - let record; + let record: NoteReaction; // Create reaction try { From 8a04fc33361e78ac0a7f91db430451649f6a3ddc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Mar 2021 20:17:53 +0000 Subject: [PATCH 37/76] Bump bull from 3.20.1 to 3.21.0 Bumps [bull](https://github.com/OptimalBits/bull) from 3.20.1 to 3.21.0. - [Release notes](https://github.com/OptimalBits/bull/releases) - [Changelog](https://github.com/OptimalBits/bull/blob/develop/CHANGELOG.md) - [Commits](https://github.com/OptimalBits/bull/compare/v3.20.1...v3.21.0) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 32dd175712..6ad10aa63c 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "bcryptjs": "2.4.3", "blurhash": "1.1.3", "broadcast-channel": "3.5.3", - "bull": "3.20.1", + "bull": "3.21.0", "cafy": "15.2.1", "cbor": "7.0.3", "chalk": "4.1.0", diff --git a/yarn.lock b/yarn.lock index cbb95b6970..16ba3dff3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2149,10 +2149,10 @@ builtin-modules@^1.1.1: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= -bull@3.20.1: - version "3.20.1" - resolved "https://registry.yarnpkg.com/bull/-/bull-3.20.1.tgz#97c4156f48001565baf3c04b81267a5604f40754" - integrity sha512-wDwpVu47WKaGhiguEPa/US5UMrtGLPKNPiGFPo4OgVs3EEGzJEWwv3LRPfjJVIf1COdMAN/yowGhZwYmoOonjQ== +bull@3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/bull/-/bull-3.21.0.tgz#60ab64a58469a489a9d4b62d543ac2046ffd3c44" + integrity sha512-8eZq28N80dntCvUZum6x7OEHrQUQYjwfUf8JwWR//VKVW92VjVTfsvXgdYoOePGeFY04fdXRolFWPxvKTny3HA== dependencies: cron-parser "^2.13.0" debuglog "^1.0.0" From 23d743bcae53e3522bc1518b46dcdc9ca115e661 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 18 Mar 2021 20:26:33 +0000 Subject: [PATCH 38/76] Bump koa-views from 7.0.0 to 7.0.1 Bumps [koa-views](https://github.com/queckezz/koa-views) from 7.0.0 to 7.0.1. - [Release notes](https://github.com/queckezz/koa-views/releases) - [Changelog](https://github.com/queckezz/koa-views/blob/master/history.md) - [Commits](https://github.com/queckezz/koa-views/compare/v7.0.0...v7.0.1) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> --- package.json | 2 +- yarn.lock | 25 ++++++++----------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 32dd175712..ddb4c2c714 100644 --- a/package.json +++ b/package.json @@ -175,7 +175,7 @@ "koa-mount": "4.0.0", "koa-send": "5.0.1", "koa-slow": "2.1.0", - "koa-views": "7.0.0", + "koa-views": "7.0.1", "langmap": "0.0.16", "lookup-dns-cache": "2.1.0", "markdown-it": "12.0.4", diff --git a/yarn.lock b/yarn.lock index cbb95b6970..8103c31bb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -750,7 +750,7 @@ dependencies: "@types/koa" "*" -"@types/koa@*", "@types/koa@2.13.1": +"@types/koa@*", "@types/koa@2.13.1", "@types/koa@^2.13.1": version "2.13.1" resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.1.tgz#e29877a6b5ad3744ab1024f6ec75b8cbf6ec45db" integrity sha512-Qbno7FWom9nNqu0yHZ6A0+RWt4mrYBhw3wpBAQ3+IuzGcLlfeYkzZrnMq5wsxulN2np8M4KKeUpTodsOsSad5Q== @@ -3310,7 +3310,7 @@ debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "2.1.2" -debug@^3.1.0, debug@^3.2.6: +debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -5996,7 +5996,7 @@ koa-mount@4.0.0: debug "^4.0.1" koa-compose "^4.1.0" -koa-send@5.0.1: +koa-send@5.0.1, koa-send@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.1.tgz#39dceebfafb395d0d60beaffba3a70b4f543fe79" integrity sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ== @@ -6005,16 +6005,6 @@ koa-send@5.0.1: http-errors "^1.7.3" resolve-path "^1.4.0" -koa-send@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.0.tgz#5e8441e07ef55737734d7ced25b842e50646e7eb" - integrity sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ== - dependencies: - debug "^3.1.0" - http-errors "^1.6.3" - mz "^2.7.0" - resolve-path "^1.4.0" - koa-slow@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/koa-slow/-/koa-slow-2.1.0.tgz#39007ca628c620f2b307b90dbf423d7a0c9be971" @@ -6023,11 +6013,12 @@ koa-slow@2.1.0: lodash.isregexp "3.0.5" q "1.4.1" -koa-views@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/koa-views/-/koa-views-7.0.0.tgz#039a90620991f6668566010b3c1df31481f78c98" - integrity sha512-I9H2Dood5MhwzmWk7hkWuxEBObd3uv/4eEPJP2r43Ro+rK8ICKjrIKEzKrfT89JNX69vWOEU8JreJwRjb5HOFw== +koa-views@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/koa-views/-/koa-views-7.0.1.tgz#0c8f8e65d5cd2e08249430cb83dc361e49a17a5a" + integrity sha512-yS8751DXHXXDbdl/oUZd0PsgnxR0MLiguu77Eqrgu6yawE9Hi99wNKiVENb0Kfgsmvq/8px7YCI+USgxaTB1LA== dependencies: + "@types/koa" "^2.13.1" consolidate "^0.16.0" debug "^4.1.0" get-paths "0.0.7" From 65e7204ec94cccb53a66f681efa9c057bb9580a9 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 19 Mar 2021 10:53:09 +0900 Subject: [PATCH 39/76] =?UTF-8?q?perf:=20myReaction=20=E3=81=AE=E5=8F=96?= =?UTF-8?q?=E5=BE=97=E3=82=92=E3=81=BE=E3=81=A8=E3=82=81=E3=81=A6=E8=A1=8C?= =?UTF-8?q?=E3=81=86=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related #6813 --- src/models/repositories/note.ts | 47 +++++++++++++++++++++++++--- src/services/note/reaction/create.ts | 1 + 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts index 32552db2fe..43caaf94b2 100644 --- a/src/models/repositories/note.ts +++ b/src/models/repositories/note.ts @@ -9,6 +9,7 @@ import { toString } from '../../mfm/to-string'; import { parse } from '../../mfm/parse'; import { Emoji } from '../entities/emoji'; import { concat } from '../../prelude/array'; +import { NoteReaction } from '../entities/note-reaction'; export type PackedNote = SchemaType<typeof packedNoteSchema>; @@ -83,6 +84,9 @@ export class NoteRepository extends Repository<Note> { options?: { detail?: boolean; skipHide?: boolean; + _hint_?: { + myReactions: Map<Note['id'], NoteReaction | null>; + }; } ): Promise<PackedNote> { const opts = Object.assign({ @@ -188,6 +192,16 @@ export class NoteRepository extends Repository<Note> { } async function populateMyReaction() { + if (options?._hint_?.myReactions) { + const reaction = options._hint_.myReactions.get(note.id); + if (reaction) { + return convertLegacyReaction(reaction.reaction); + } else if (reaction === null) { + return undefined; + } + // 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない + } + const reaction = await NoteReactions.findOne({ userId: meId!, noteId: note.id, @@ -245,11 +259,13 @@ export class NoteRepository extends Repository<Note> { ...(opts.detail ? { reply: note.replyId ? this.pack(note.replyId, meId, { - detail: false + detail: false, + _hint_: options?._hint_ }) : undefined, renote: note.renoteId ? this.pack(note.renoteId, meId, { - detail: true + detail: true, + _hint_: options?._hint_ }) : undefined, poll: note.hasPoll ? populatePoll() : undefined, @@ -272,7 +288,7 @@ export class NoteRepository extends Repository<Note> { return packed; } - public packMany( + public async packMany( notes: (Note['id'] | Note)[], me?: User['id'] | User | null | undefined, options?: { @@ -280,7 +296,30 @@ export class NoteRepository extends Repository<Note> { skipHide?: boolean; } ) { - return Promise.all(notes.map(n => this.pack(n, me, options))); + if (notes.length === 0) return []; + + const meId = me ? typeof me === 'string' ? me : me.id : null; + const noteIds = notes.map(n => typeof n === 'object' ? n.id : n); + const myReactionsMap = new Map<Note['id'], NoteReaction | null>(); + if (meId) { + const renoteIds = notes.filter(n => (typeof n === 'object') && (n.renoteId != null)).map(n => (n as Note).renoteId!); + const targets = [...noteIds, ...renoteIds]; + const myReactions = await NoteReactions.find({ + userId: meId, + noteId: In(targets), + }); + + for (const target of targets) { + myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); + } + } + + return await Promise.all(notes.map(n => this.pack(n, me, { + ...options, + _hint_: { + myReactions: myReactionsMap + } + }))); } } diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts index 30a17ad3e9..6c0a852f34 100644 --- a/src/services/note/reaction/create.ts +++ b/src/services/note/reaction/create.ts @@ -15,6 +15,7 @@ import { isDuplicateKeyValueError } from '../../../misc/is-duplicate-key-value-e import { NoteReaction } from '../../../models/entities/note-reaction'; export default async (user: User, note: Note, reaction?: string) => { + // TODO: cache reaction = await toDbReaction(reaction, user.host); let record: NoteReaction; From d381d31e5eb66205759bba492e4eef9d6c09fde3 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 19 Mar 2021 11:15:05 +0900 Subject: [PATCH 40/76] add note --- src/server/api/stream/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts index c56a0a157b..bb37cfa622 100644 --- a/src/server/api/stream/index.ts +++ b/src/server/api/stream/index.ts @@ -159,6 +159,8 @@ export default class Connection { } if (this.user && read) { + // TODO: クライアントでタイムライン読み込みなどすると、一度に大量のreadNoteが発生しクエリ数がすごいことになるので、ある程度まとめてreadNoteするようにする + // 具体的には、この箇所ではキュー的な配列にread予定ノートを溜めておくに留めて、別の箇所で定期的にキューにあるノートを配列でreadNoteに渡すような実装にする readNote(this.user.id, payload.id); } } From fbc9d5ecba46bb7da4bffcaf793e209658e5bf50 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 19 Mar 2021 18:22:14 +0900 Subject: [PATCH 41/76] refactor --- src/models/repositories/drive-file.ts | 2 +- src/models/repositories/notification.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/repositories/drive-file.ts b/src/models/repositories/drive-file.ts index 62416f69ea..61d24bd24e 100644 --- a/src/models/repositories/drive-file.ts +++ b/src/models/repositories/drive-file.ts @@ -134,7 +134,7 @@ export class DriveFileRepository extends Repository<DriveFile> { } public async packMany( - files: any[], + files: (DriveFile['id'] | DriveFile)[], options?: PackOptions ) { const items = await Promise.all(files.map(f => this.pack(f, options))); diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index 16de6c8c25..d593dc22a9 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -53,7 +53,7 @@ export class NotificationRepository extends Repository<Notification> { } public packMany( - notifications: any[], + notifications: Notification[], ) { return Promise.all(notifications.map(x => this.pack(x))); } From 5e61c60f85e262421c1655eb6f8a317b88ccb88f Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 19 Mar 2021 18:22:34 +0900 Subject: [PATCH 42/76] perf(server): Improver performance --- src/misc/fetch-meta.ts | 2 +- src/server/api/endpoints/i/notifications.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/misc/fetch-meta.ts b/src/misc/fetch-meta.ts index 680cf37a72..e7a945dc9e 100644 --- a/src/misc/fetch-meta.ts +++ b/src/misc/fetch-meta.ts @@ -32,4 +32,4 @@ setInterval(() => { fetchMeta(true).then(meta => { cache = meta; }); -}, 5000); +}, 1000 * 10); diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 0e09bc73fd..7a423edb8d 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -85,7 +85,9 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId) .andWhere(`notification.notifieeId = :meId`, { meId: user.id }) - .leftJoinAndSelect('notification.notifier', 'notifier'); + .leftJoinAndSelect('notification.notifier', 'notifier') + .leftJoinAndSelect('notification.note', 'note') + .leftJoinAndSelect('note.user', 'user'); query.andWhere(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`); query.setParameters(mutingQuery.getParameters()); From 87c8f9ff953499340496e9c5db09c93eaff08851 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 19 Mar 2021 20:43:24 +0900 Subject: [PATCH 43/76] perf: Reduce database query --- src/client/components/note-detailed.vue | 10 +- src/client/components/note.vue | 10 +- src/client/ui/chat/note.vue | 10 +- src/server/api/endpoints/notes/mentions.ts | 6 +- src/server/api/stream/index.ts | 38 +++++--- src/services/note/read-mention.ts | 29 ++++++ src/services/note/read-specified-note.ts | 29 ++++++ src/services/note/read.ts | 105 --------------------- 8 files changed, 114 insertions(+), 123 deletions(-) create mode 100644 src/services/note/read-mention.ts create mode 100644 src/services/note/read-specified-note.ts delete mode 100644 src/services/note/read.ts diff --git a/src/client/components/note-detailed.vue b/src/client/components/note-detailed.vue index 1ef3f43389..4ad3d2d898 100644 --- a/src/client/components/note-detailed.vue +++ b/src/client/components/note-detailed.vue @@ -350,7 +350,15 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); + this.connection.send('sn', { id: this.appearNote.id }); + if (this.appearNote.userId !== this.$i.id) { + if (this.appearNote.mentions && this.appearNote.mentions.includes(this.$i.id)) { + this.connection.send('readMention', { id: this.appearNote.id }); + } + if (this.appearNote.visibleUserIds && this.appearNote.visibleUserIds.includes(this.$i.id)) { + this.connection.send('readSpecifiedNote', { id: this.appearNote.id }); + } + } if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 65e09b7802..3b59afd71d 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -325,7 +325,15 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); + this.connection.send('sn', { id: this.appearNote.id }); + if (this.appearNote.userId !== this.$i.id) { + if (this.appearNote.mentions && this.appearNote.mentions.includes(this.$i.id)) { + this.connection.send('readMention', { id: this.appearNote.id }); + } + if (this.appearNote.visibleUserIds && this.appearNote.visibleUserIds.includes(this.$i.id)) { + this.connection.send('readSpecifiedNote', { id: this.appearNote.id }); + } + } if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/client/ui/chat/note.vue b/src/client/ui/chat/note.vue index 5a4a13d889..29bc61d9c5 100644 --- a/src/client/ui/chat/note.vue +++ b/src/client/ui/chat/note.vue @@ -325,7 +325,15 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); + this.connection.send('sn', { id: this.appearNote.id }); + if (this.appearNote.userId !== this.$i.id) { + if (this.appearNote.mentions && this.appearNote.mentions.includes(this.$i.id)) { + this.connection.send('readMention', { id: this.appearNote.id }); + } + if (this.appearNote.visibleUserIds && this.appearNote.visibleUserIds.includes(this.$i.id)) { + this.connection.send('readSpecifiedNote', { id: this.appearNote.id }); + } + } if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 8a9d295d38..1e3014bd46 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -1,12 +1,12 @@ import $ from 'cafy'; import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; -import read from '../../../../services/note/read'; import { Notes, Followings } from '../../../../models'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Brackets } from 'typeorm'; +import { readMention } from '../../../../services/note/read-mention'; export const meta = { desc: { @@ -79,9 +79,7 @@ export default define(meta, async (ps, user) => { const mentions = await query.take(ps.limit!).getMany(); - for (const note of mentions) { - read(user.id, note.id); - } + readMention(user.id, mentions.map(n => n.id)); return await Notes.packMany(mentions, user); }); diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts index bb37cfa622..4a87f61e7f 100644 --- a/src/server/api/stream/index.ts +++ b/src/server/api/stream/index.ts @@ -2,7 +2,6 @@ import autobind from 'autobind-decorator'; import * as websocket from 'websocket'; import { readNotification } from '../common/read-notification'; import call from '../call'; -import readNote from '../../../services/note/read'; import Channel from './channel'; import channels from './channels'; import { EventEmitter } from 'events'; @@ -14,6 +13,8 @@ import { AccessToken } from '../../../models/entities/access-token'; import { UserProfile } from '../../../models/entities/user-profile'; import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '../../../services/stream'; import { UserGroup } from '../../../models/entities/user-group'; +import { readMention } from '../../../services/note/read-mention'; +import { readSpecifiedNote } from '../../../services/note/read-specified-note'; /** * Main stream connection @@ -86,9 +87,10 @@ export default class Connection { switch (type) { case 'api': this.onApiRequest(body); break; case 'readNotification': this.onReadNotification(body); break; - case 'subNote': this.onSubscribeNote(body, true); break; - case 'sn': this.onSubscribeNote(body, true); break; // alias - case 's': this.onSubscribeNote(body, false); break; + case 'readMention': this.onReadMention(body); break; + case 'readSpecifiedNote': this.onReadSpecifiedNote(body); break; + case 'subNote': this.onSubscribeNote(body); break; + case 'sn': this.onSubscribeNote(body); break; // alias case 'unsubNote': this.onUnsubscribeNote(body); break; case 'un': this.onUnsubscribeNote(body); break; // alias case 'connect': this.onChannelConnectRequested(body); break; @@ -141,11 +143,31 @@ export default class Connection { readNotification(this.user!.id, [payload.id]); } + @autobind + private onReadMention(payload: any) { + if (!payload.id) return; + if (this.user) { + // TODO: ある程度まとめてreadMentionするようにする + // 具体的には、この箇所ではキュー的な配列にread予定ノートを溜めておくに留めて、別の箇所で定期的にキューにあるノートを配列でreadMentionに渡すような実装にする + readMention(this.user.id, [payload.id]); + } + } + + @autobind + private onReadSpecifiedNote(payload: any) { + if (!payload.id) return; + if (this.user) { + // TODO: ある程度まとめてreadSpecifiedNoteするようにする + // 具体的には、この箇所ではキュー的な配列にread予定ノートを溜めておくに留めて、別の箇所で定期的にキューにあるノートを配列でreadSpecifiedNoteに渡すような実装にする + readSpecifiedNote(this.user.id, [payload.id]); + } + } + /** * 投稿購読要求時 */ @autobind - private onSubscribeNote(payload: any, read: boolean) { + private onSubscribeNote(payload: any) { if (!payload.id) return; if (this.subscribingNotes[payload.id] == null) { @@ -157,12 +179,6 @@ export default class Connection { if (this.subscribingNotes[payload.id] === 1) { this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage); } - - if (this.user && read) { - // TODO: クライアントでタイムライン読み込みなどすると、一度に大量のreadNoteが発生しクエリ数がすごいことになるので、ある程度まとめてreadNoteするようにする - // 具体的には、この箇所ではキュー的な配列にread予定ノートを溜めておくに留めて、別の箇所で定期的にキューにあるノートを配列でreadNoteに渡すような実装にする - readNote(this.user.id, payload.id); - } } /** diff --git a/src/services/note/read-mention.ts b/src/services/note/read-mention.ts new file mode 100644 index 0000000000..2a668ecd6c --- /dev/null +++ b/src/services/note/read-mention.ts @@ -0,0 +1,29 @@ +import { publishMainStream } from '../stream'; +import { Note } from '../../models/entities/note'; +import { User } from '../../models/entities/user'; +import { NoteUnreads } from '../../models'; +import { In } from 'typeorm'; + +/** + * Mark a mention note as read + */ +export async function readMention( + userId: User['id'], + noteIds: Note['id'][] +) { + // Remove the records + await NoteUnreads.delete({ + userId: userId, + noteId: In(noteIds), + }); + + const mentionsCount = await NoteUnreads.count({ + userId: userId, + isMentioned: true + }); + + if (mentionsCount === 0) { + // 全て既読になったイベントを発行 + publishMainStream(userId, 'readAllUnreadMentions'); + } +} diff --git a/src/services/note/read-specified-note.ts b/src/services/note/read-specified-note.ts new file mode 100644 index 0000000000..0fcb66bf98 --- /dev/null +++ b/src/services/note/read-specified-note.ts @@ -0,0 +1,29 @@ +import { publishMainStream } from '../stream'; +import { Note } from '../../models/entities/note'; +import { User } from '../../models/entities/user'; +import { NoteUnreads } from '../../models'; +import { In } from 'typeorm'; + +/** + * Mark a specified note as read + */ +export async function readSpecifiedNote( + userId: User['id'], + noteIds: Note['id'][] +) { + // Remove the records + await NoteUnreads.delete({ + userId: userId, + noteId: In(noteIds), + }); + + const specifiedCount = await NoteUnreads.count({ + userId: userId, + isSpecified: true + }); + + if (specifiedCount === 0) { + // 全て既読になったイベントを発行 + publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); + } +} diff --git a/src/services/note/read.ts b/src/services/note/read.ts deleted file mode 100644 index 5a39ab30b7..0000000000 --- a/src/services/note/read.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { publishMainStream } from '../stream'; -import { Note } from '../../models/entities/note'; -import { User } from '../../models/entities/user'; -import { NoteUnreads, Antennas, AntennaNotes, Users } from '../../models'; -import { Not, IsNull } from 'typeorm'; - -/** - * Mark a note as read - */ -export default async function( - userId: User['id'], - noteId: Note['id'] -) { - async function careNoteUnreads() { - const exist = await NoteUnreads.findOne({ - userId: userId, - noteId: noteId, - }); - - if (!exist) return; - - // Remove the record - await NoteUnreads.delete({ - userId: userId, - noteId: noteId, - }); - - if (exist.isMentioned) { - NoteUnreads.count({ - userId: userId, - isMentioned: true - }).then(mentionsCount => { - if (mentionsCount === 0) { - // 全て既読になったイベントを発行 - publishMainStream(userId, 'readAllUnreadMentions'); - } - }); - } - - if (exist.isSpecified) { - NoteUnreads.count({ - userId: userId, - isSpecified: true - }).then(specifiedCount => { - if (specifiedCount === 0) { - // 全て既読になったイベントを発行 - publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); - } - }); - } - - if (exist.noteChannelId) { - NoteUnreads.count({ - userId: userId, - noteChannelId: Not(IsNull()) - }).then(channelNoteCount => { - if (channelNoteCount === 0) { - // 全て既読になったイベントを発行 - publishMainStream(userId, 'readAllChannels'); - } - }); - } - } - - async function careAntenna() { - const beforeUnread = await Users.getHasUnreadAntenna(userId); - if (!beforeUnread) return; - - const antennas = await Antennas.find({ userId }); - - await Promise.all(antennas.map(async antenna => { - const countBefore = await AntennaNotes.count({ - antennaId: antenna.id, - read: false - }); - - if (countBefore === 0) return; - - await AntennaNotes.update({ - antennaId: antenna.id, - noteId: noteId - }, { - read: true - }); - - const countAfter = await AntennaNotes.count({ - antennaId: antenna.id, - read: false - }); - - if (countAfter === 0) { - publishMainStream(userId, 'readAntenna', antenna); - } - })); - - Users.getHasUnreadAntenna(userId).then(unread => { - if (!unread) { - publishMainStream(userId, 'readAllAntennas'); - } - }); - } - - careNoteUnreads(); - careAntenna(); -} From 4b13431a19492b400c67da27e79cf800072a48d1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 19 Mar 2021 23:25:10 +0900 Subject: [PATCH 44/76] Update deps :rocket: --- package.json | 38 ++++---- yarn.lock | 257 ++++++++++++++++++++++++++------------------------- 2 files changed, 150 insertions(+), 145 deletions(-) diff --git a/package.json b/package.json index ae0567ce1f..622edcc5da 100644 --- a/package.json +++ b/package.json @@ -37,14 +37,14 @@ "dependencies": { "@babel/plugin-transform-runtime": "7.13.10", "@elastic/elasticsearch": "7.11.0", - "@fortawesome/fontawesome-svg-core": "1.2.34", - "@fortawesome/free-brands-svg-icons": "5.15.2", - "@fortawesome/free-regular-svg-icons": "5.15.2", - "@fortawesome/free-solid-svg-icons": "5.15.2", + "@fortawesome/fontawesome-svg-core": "1.2.35", + "@fortawesome/free-brands-svg-icons": "5.15.3", + "@fortawesome/free-regular-svg-icons": "5.15.3", + "@fortawesome/free-solid-svg-icons": "5.15.3", "@fortawesome/vue-fontawesome": "3.0.0-3", "@koa/cors": "3.1.0", "@koa/multer": "3.0.0", - "@koa/router": "9.0.1", + "@koa/router": "10.0.0", "@sentry/browser": "5.29.2", "@sentry/tracing": "5.29.2", "@sinonjs/fake-timers": "7.0.2", @@ -53,7 +53,6 @@ "@types/bull": "3.15.0", "@types/cbor": "5.0.1", "@types/dateformat": "3.0.1", - "@types/double-ended-queue": "2.1.1", "@types/escape-regexp": "0.0.0", "@types/glob": "7.1.3", "@types/gulp": "4.0.8", @@ -78,9 +77,9 @@ "@types/markdown-it": "12.0.1", "@types/matter-js": "0.14.10", "@types/mocha": "8.2.1", - "@types/node": "14.14.34", + "@types/node": "14.14.35", "@types/node-fetch": "2.5.8", - "@types/nodemailer": "6.4.0", + "@types/nodemailer": "6.4.1", "@types/nprogress": "0.2.0", "@types/oauth": "0.9.1", "@types/parse5": "6.0.0", @@ -107,20 +106,20 @@ "@types/webpack-stream": "3.2.11", "@types/websocket": "1.0.2", "@types/ws": "7.4.0", - "@typescript-eslint/parser": "4.17.0", + "@typescript-eslint/parser": "4.18.0", "@vue/compiler-sfc": "3.0.7", "abort-controller": "3.0.0", - "apexcharts": "3.25.0", + "apexcharts": "3.26.0", "autobind-decorator": "2.4.0", "autosize": "4.0.2", "autwh": "0.1.0", - "aws-sdk": "2.862.0", + "aws-sdk": "2.867.0", "bcryptjs": "2.4.3", "blurhash": "1.1.3", "broadcast-channel": "3.5.3", - "bull": "3.21.0", + "bull": "3.21.1", "cafy": "15.2.1", - "cbor": "7.0.3", + "cbor": "7.0.4", "chalk": "4.1.0", "chart.js": "2.9.4", "cli-highlight": "2.1.10", @@ -133,9 +132,8 @@ "cssnano": "4.1.10", "dateformat": "4.5.1", "diskusage": "1.1.3", - "double-ended-queue": "2.1.0-0", "escape-regexp": "0.0.1", - "eslint": "7.21.0", + "eslint": "7.22.0", "eslint-plugin-vue": "7.7.0", "eventemitter3": "4.0.7", "feed": "4.2.2", @@ -159,7 +157,7 @@ "idb-keyval": "5.0.4", "insert-text-at-cursor": "0.3.0", "is-root": "2.1.0", - "is-svg": "4.2.2", + "is-svg": "4.3.1", "js-yaml": "4.0.0", "jsdom": "16.5.1", "json5": "2.2.0", @@ -197,7 +195,7 @@ "postcss": "8.2.8", "postcss-loader": "5.2.0", "prismjs": "1.23.0", - "probe-image-size": "7.0.0", + "probe-image-size": "7.0.1", "promise-limit": "2.7.0", "promise-sequential": "1.1.1", "pug": "3.0.2", @@ -227,7 +225,7 @@ "style-loader": "2.0.0", "summaly": "2.4.0", "syslog-pro": "1.0.0", - "systeminformation": "5.6.2", + "systeminformation": "5.6.7", "syuilo-password-strength": "0.0.1", "textarea-caret": "3.1.0", "three": "0.117.1", @@ -254,7 +252,7 @@ "vue-style-loader": "4.1.3", "vuedraggable": "4.0.1", "web-push": "3.4.4", - "webpack": "5.25.0", + "webpack": "5.26.3", "webpack-cli": "4.5.0", "websocket": "1.0.33", "ws": "7.4.4", @@ -263,7 +261,7 @@ "devDependencies": { "@types/chai": "4.2.15", "@types/fluent-ffmpeg": "2.1.16", - "chai": "4.3.0", + "chai": "4.3.4", "cross-env": "7.0.3" } } diff --git a/yarn.lock b/yarn.lock index 2367fe95a9..6318a9b52c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -201,38 +201,38 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@fortawesome/fontawesome-common-types@^0.2.34": - version "0.2.34" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.34.tgz#0a8c348bb23b7b760030f5b1d912e582be4ec915" - integrity sha512-XcIn3iYbTEzGIxD0/dY5+4f019jIcEIWBiHc3KrmK/ROahwxmZ/s+tdj97p/5K0klz4zZUiMfUlYP0ajhSJjmA== +"@fortawesome/fontawesome-common-types@^0.2.35": + version "0.2.35" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz#01dd3d054da07a00b764d78748df20daf2b317e9" + integrity sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw== -"@fortawesome/fontawesome-svg-core@1.2.34": - version "1.2.34" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.34.tgz#1d1a7c92537cbc2b8a83eef6b6d824b4b5b46b26" - integrity sha512-0KNN0nc5eIzaJxlv43QcDmTkDY1CqeN6J7OCGSs+fwGPdtv0yOQqRjieopBCmw+yd7uD3N2HeNL3Zm5isDleLg== +"@fortawesome/fontawesome-svg-core@1.2.35": + version "1.2.35" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz#85aea8c25645fcec88d35f2eb1045c38d3e65cff" + integrity sha512-uLEXifXIL7hnh2sNZQrIJWNol7cTVIzwI+4qcBIq9QWaZqUblm0IDrtSqbNg+3SQf8SMGHkiSigD++rHmCHjBg== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.34" + "@fortawesome/fontawesome-common-types" "^0.2.35" -"@fortawesome/free-brands-svg-icons@5.15.2": - version "5.15.2" - resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.2.tgz#d74e2540b5552b915d6bef719f17e361b70a8d65" - integrity sha512-YPlVjE1cEO+OJ9I9ay3TQ3I88+XkxMTYwnnddqAboxLhPNGncsHV0DjWOVLCyuAY66yPfyndWwVn4v7vuqsO1g== +"@fortawesome/free-brands-svg-icons@5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.3.tgz#bec2821d23b9c667be1d192a6c5bfb2667e588eb" + integrity sha512-1hirPcbjj72ZJtFvdnXGPbAbpn3Ox6mH3g5STbANFp3vGSiE5u5ingAKV06mK6ZVqNYxUPlh4DlTnaIvLtF2kw== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.34" + "@fortawesome/fontawesome-common-types" "^0.2.35" -"@fortawesome/free-regular-svg-icons@5.15.2": - version "5.15.2" - resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.2.tgz#61eeb8c206e792c530eaa58279cc32c55332fe8f" - integrity sha512-Uv5NQCYjyisNVTu/1Xjs+z8vwQjbfT6hiqYvQNfF0n8qdgfWLM581bAfVMQ3BCs1SPy+eEUKNcGkK4n0FihFHg== +"@fortawesome/free-regular-svg-icons@5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.3.tgz#1ec4f2410ff638db549c5c5484fc60b66407dbe6" + integrity sha512-q4/p8Xehy9qiVTdDWHL4Z+o5PCLRChePGZRTXkl+/Z7erDVL8VcZUuqzJjs6gUz6czss4VIPBRdCz6wP37/zMQ== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.34" + "@fortawesome/fontawesome-common-types" "^0.2.35" -"@fortawesome/free-solid-svg-icons@5.15.2": - version "5.15.2" - resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.2.tgz#25bb035de57cf85aee8072965732368ccc8e8943" - integrity sha512-ZfCU+QjaFsdNZmOGmfqEWhzI3JOe37x5dF4kz9GeXvKn/sTxhqMtZ7mh3lBf76SvcYY5/GKFuyG7p1r4iWMQqw== +"@fortawesome/free-solid-svg-icons@5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz#52eebe354f60dc77e0bde934ffc5c75ffd04f9d8" + integrity sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.34" + "@fortawesome/fontawesome-common-types" "^0.2.35" "@fortawesome/vue-fontawesome@3.0.0-3": version "3.0.0-3" @@ -251,10 +251,10 @@ resolved "https://registry.yarnpkg.com/@koa/multer/-/multer-3.0.0.tgz#439777949f28097d7b329c0b4ce3048074c862f8" integrity sha512-y+OQBmex5D1jIl723gAEUYcAWPEicIXppaAKw/zCMfpllQ08ZNweDPwoCLxEoatqd5pCu2XG6V8dl67JRq3RJw== -"@koa/router@9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@koa/router/-/router-9.0.1.tgz#4090a14223ea7e78aa13b632761209cba69acd95" - integrity sha512-OI+OU49CJV4px0WkIMmayBeqVXB/JS1ZMq7UoGlTZt6Y7ijK7kdeQ18+SEHHJPytmtI1y6Hf8XLrpxva3mhv5Q== +"@koa/router@10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@koa/router/-/router-10.0.0.tgz#699115561afbd2740e2848ba299fc76b9e058ad6" + integrity sha512-z9ytrKWn/j/qUApMSJzZbUwkbLcN2ZXGq6UsqWkZb50Us+/Qpu0RwgZ6ytawVOhfFBZ1ai5iVWeD2Dcu0qcnJw== dependencies: debug "^4.1.1" http-errors "^1.7.3" @@ -496,11 +496,6 @@ resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== -"@types/double-ended-queue@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/double-ended-queue/-/double-ended-queue-2.1.1.tgz#f077386134f0f736d927812c85c43a04f21ddc27" - integrity sha512-O2+umEIlHBVyi+ePmucPjpINqTvSnsz+hAok0D4IpvrOsIsDr6c34B0AbNXW2UDVYuxbv51z5dxnrRt23ohgWg== - "@types/escape-regexp@0.0.0": version "0.0.0" resolved "https://registry.yarnpkg.com/@types/escape-regexp/-/escape-regexp-0.0.0.tgz#bff0225f9ef30d0dbdbe0e2a24283ee5342990c3" @@ -837,15 +832,15 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055" integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g== -"@types/node@14.14.34": - version "14.14.34" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.34.tgz#07935194fc049069a1c56c0c274265abeddf88da" - integrity sha512-dBPaxocOK6UVyvhbnpFIj2W+S+1cBTkHQbFQfeeJhoKFbzYcVUGHvddeWPSucKATb3F0+pgDq0i6ghEaZjsugA== +"@types/node@14.14.35": + version "14.14.35" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313" + integrity sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag== -"@types/nodemailer@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.0.tgz#d8c039be3ed685c4719a026455555be82c124b74" - integrity sha512-KY7bFWB0MahRZvVW4CuW83qcCDny59pJJ0MQ5ifvfcjNwPlIT0vW4uARO4u1gtkYnWdhSvURegecY/tzcukJcA== +"@types/nodemailer@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.1.tgz#31f96f4410632f781d3613bd1f4293649e423f75" + integrity sha512-8081UY/0XTTDpuGqCnDc8IY+Q3DSg604wB3dBH0CaZlj4nZWHWuxtZ3NRZ9c9WUrz1Vfm6wioAUnqL3bsh49uQ== dependencies: "@types/node" "*" @@ -1135,48 +1130,48 @@ dependencies: "@types/node" "*" -"@typescript-eslint/parser@4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.17.0.tgz#141b647ffc72ebebcbf9b0fe6087f65b706d3215" - integrity sha512-KYdksiZQ0N1t+6qpnl6JeK9ycCFprS9xBAiIrw4gSphqONt8wydBw4BXJi3C11ywZmyHulvMaLjWsxDjUSDwAw== +"@typescript-eslint/parser@4.18.0": + version "4.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.18.0.tgz#a211edb14a69fc5177054bec04c95b185b4dde21" + integrity sha512-W3z5S0ZbecwX3PhJEAnq4mnjK5JJXvXUDBYIYGoweCyWyuvAKfGHvzmpUzgB5L4cRBb+cTu9U/ro66dx7dIimA== dependencies: - "@typescript-eslint/scope-manager" "4.17.0" - "@typescript-eslint/types" "4.17.0" - "@typescript-eslint/typescript-estree" "4.17.0" + "@typescript-eslint/scope-manager" "4.18.0" + "@typescript-eslint/types" "4.18.0" + "@typescript-eslint/typescript-estree" "4.18.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.17.0.tgz#f4edf94eff3b52a863180f7f89581bf963e3d37d" - integrity sha512-OJ+CeTliuW+UZ9qgULrnGpPQ1bhrZNFpfT/Bc0pzNeyZwMik7/ykJ0JHnQ7krHanFN9wcnPK89pwn84cRUmYjw== +"@typescript-eslint/scope-manager@4.18.0": + version "4.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.18.0.tgz#d75b55234c35d2ff6ac945758d6d9e53be84a427" + integrity sha512-olX4yN6rvHR2eyFOcb6E4vmhDPsfdMyfQ3qR+oQNkAv8emKKlfxTWUXU5Mqxs2Fwe3Pf1BoPvrwZtwngxDzYzQ== dependencies: - "@typescript-eslint/types" "4.17.0" - "@typescript-eslint/visitor-keys" "4.17.0" + "@typescript-eslint/types" "4.18.0" + "@typescript-eslint/visitor-keys" "4.18.0" -"@typescript-eslint/types@4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.17.0.tgz#f57d8fc7f31b348db946498a43050083d25f40ad" - integrity sha512-RN5z8qYpJ+kXwnLlyzZkiJwfW2AY458Bf8WqllkondQIcN2ZxQowAToGSd9BlAUZDB5Ea8I6mqL2quGYCLT+2g== +"@typescript-eslint/types@4.18.0": + version "4.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.18.0.tgz#bebe323f81f2a7e2e320fac9415e60856267584a" + integrity sha512-/BRociARpj5E+9yQ7cwCF/SNOWwXJ3qhjurMuK2hIFUbr9vTuDeu476Zpu+ptxY2kSxUHDGLLKy+qGq2sOg37A== -"@typescript-eslint/typescript-estree@4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.17.0.tgz#b835d152804f0972b80dbda92477f9070a72ded1" - integrity sha512-lRhSFIZKUEPPWpWfwuZBH9trYIEJSI0vYsrxbvVvNyIUDoKWaklOAelsSkeh3E2VBSZiNe9BZ4E5tYBZbUczVQ== +"@typescript-eslint/typescript-estree@4.18.0": + version "4.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.18.0.tgz#756d3e61da8c16ab99185532c44872f4cd5538cb" + integrity sha512-wt4xvF6vvJI7epz+rEqxmoNQ4ZADArGQO9gDU+cM0U5fdVv7N+IAuVoVAoZSOZxzGHBfvE3XQMLdy+scsqFfeg== dependencies: - "@typescript-eslint/types" "4.17.0" - "@typescript-eslint/visitor-keys" "4.17.0" + "@typescript-eslint/types" "4.18.0" + "@typescript-eslint/visitor-keys" "4.18.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.17.0": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.17.0.tgz#9c304cfd20287c14a31d573195a709111849b14d" - integrity sha512-WfuMN8mm5SSqXuAr9NM+fItJ0SVVphobWYkWOwQ1odsfC014Vdxk/92t4JwS1Q6fCA/ABfCKpa3AVtpUKTNKGQ== +"@typescript-eslint/visitor-keys@4.18.0": + version "4.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz#4e6fe2a175ee33418318a029610845a81e2ff7b6" + integrity sha512-Q9t90JCvfYaN0OfFUgaLqByOfz8yPeTAdotn/XYNm5q9eHax90gzdb+RJ6E9T5s97Kv/UHWKERTmqA0jTKAEHw== dependencies: - "@typescript-eslint/types" "4.17.0" + "@typescript-eslint/types" "4.18.0" eslint-visitor-keys "^2.0.0" "@ungap/promise-all-settled@1.1.2": @@ -1580,10 +1575,10 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -apexcharts@3.25.0: - version "3.25.0" - resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.25.0.tgz#f3f0f9f344f997230f5c7f2918408aa072627496" - integrity sha512-uM7OF+jLL4ba79noYcrMwMgJW8DI+Ff28CCQoGq23g25z8nGSQEoU+u12YWlECA9gBA5tbmdaQhMxjlK+M6B9Q== +apexcharts@3.26.0: + version "3.26.0" + resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.26.0.tgz#a78abc108b2e1b3086a738f0ec7c98e292f4a14b" + integrity sha512-zdYHs3k3tgmCn1BpYLj7rhGEndBYF33Pq1+g0ora37xAr+3act5CJrpdXM2jx2boVUyXgavoSp6sa8WpK7RkSA== dependencies: svg.draggable.js "^2.2.2" svg.easing.js "^2.0.0" @@ -1844,10 +1839,10 @@ autwh@0.1.0: dependencies: oauth "0.9.15" -aws-sdk@2.862.0: - version "2.862.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.862.0.tgz#3f9afc830de24723dfb7801f4138c5db101f4205" - integrity sha512-kAvEQZ2tKJRZKRHZQJdvrUZz76cAEnyN1UoLw39kCqGIpxw9rcPK+bIy8fDBmcvWVZvymO7mp4Ut2jM/ZiDCMQ== +aws-sdk@2.867.0: + version "2.867.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.867.0.tgz#3fae9a583bfd89f6098dc9bca6553b5b85e2a2fc" + integrity sha512-6iXRTW6dlCU4UwMivDGHoRyMCvX9Ch1G2gIZZl5yKwzVNaBNpJiAyByWFuxrcBqQsSIFKBEbxeRjePE6QiBbkw== dependencies: buffer "4.9.2" events "1.1.1" @@ -2149,16 +2144,16 @@ builtin-modules@^1.1.1: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= -bull@3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/bull/-/bull-3.21.0.tgz#60ab64a58469a489a9d4b62d543ac2046ffd3c44" - integrity sha512-8eZq28N80dntCvUZum6x7OEHrQUQYjwfUf8JwWR//VKVW92VjVTfsvXgdYoOePGeFY04fdXRolFWPxvKTny3HA== +bull@3.21.1: + version "3.21.1" + resolved "https://registry.yarnpkg.com/bull/-/bull-3.21.1.tgz#f0ff54b73c6a5ec54c5e12bb9d3ceb54229d055d" + integrity sha512-7IjQBz+mm2GYwlNOf5Pbq8F+EwqkBv0RjwWpG+5Kfk/QDWylQ6eL3FzJbvFQQ7uHjTVPsuzsQocArjhm/SUsqA== dependencies: cron-parser "^2.13.0" debuglog "^1.0.0" get-port "^5.1.1" ioredis "^4.22.0" - lodash "^4.17.19" + lodash "^4.17.21" p-timeout "^3.2.0" promise.prototype.finally "^3.1.2" semver "^7.3.2" @@ -2316,24 +2311,24 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -cbor@7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.3.tgz#216d292f2aedd1bb61414a8f949b63e4de11b33b" - integrity sha512-Io+lJytjYBJKgJqZQUz2bFaMPj+HtlsnT9kHSUiIJFqzWa05lm5/ycQ+NiZWpks3DR2Fz7j7axiTGeT57w/syg== +cbor@7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.4.tgz#8a3ef39f07ac3fdb69dda461b87db7113233481b" + integrity sha512-9hBTn31l7+9qteBso7+HPp2R5ytqFRBd98fHK4ZTpvrba8V7CuoOsEL0S6vf7+11gubMTd3RW97lOgMTl5SNfg== dependencies: "@cto.af/textdecoder" "^0.0.0" nofilter "^2.0.3" -chai@4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.0.tgz#5523a5faf7f819c8a92480d70a8cccbadacfc25f" - integrity sha512-/BFd2J30EcOwmdOgXvVsmM48l0Br0nmZPlO0uOW4XKh6kpsUumRXBgPV+IlaqFaqr9cYbeoZAM1Npx0i4A+aiA== +chai@4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== dependencies: assertion-error "^1.1.0" check-error "^1.0.2" deep-eql "^3.0.1" get-func-name "^2.0.0" - pathval "^1.1.0" + pathval "^1.1.1" type-detect "^4.0.5" chalk@4.0.0: @@ -3607,11 +3602,6 @@ dotenv@^8.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== -double-ended-queue@2.1.0-0: - version "2.1.0-0" - resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" - integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw= - duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" @@ -3914,10 +3904,10 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@7.21.0: - version "7.21.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.21.0.tgz#4ecd5b8c5b44f5dedc9b8a110b01bbfeb15d1c83" - integrity sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg== +eslint@7.22.0: + version "7.22.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.22.0.tgz#07ecc61052fec63661a2cab6bd507127c07adc6f" + integrity sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.0" @@ -3936,7 +3926,7 @@ eslint@7.21.0: file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" - globals "^12.1.0" + globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" @@ -3944,7 +3934,7 @@ eslint@7.21.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.20" + lodash "^4.17.21" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -4190,6 +4180,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-xml-parser@^3.19.0: + version "3.19.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" + integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg== + fastest-levenshtein@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" @@ -4638,6 +4633,13 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globals@^13.6.0: + version "13.7.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.7.0.tgz#aed3bcefd80ad3ec0f0be2cf0c895110c0591795" + integrity sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA== + dependencies: + type-fest "^0.20.2" + globby@^11.0.1: version "11.0.1" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" @@ -4945,7 +4947,7 @@ hsla-regex@^1.0.0: resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= -html-comment-regex@^1.1.0, html-comment-regex@^1.1.2: +html-comment-regex@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== @@ -5528,12 +5530,12 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-svg@4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.2.2.tgz#a4ea0f3f78dada7085db88f1e85b6f845626cfae" - integrity sha512-JlA7Mc7mfWjdxxTkJ094oUK9amGD7gQaj5xA/NCY0vlVvZ1stmj4VX+bRuwOMN93IHRZ2ctpPH/0FO6DqvQ5Rw== +is-svg@4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.3.1.tgz#8c63ec8c67c8c7f0a8de0a71c8c7d58eccf4406b" + integrity sha512-h2CGs+yPUyvkgTJQS9cJzo9lYK06WgRiXUqBBHtglSzVKAuH4/oWsqk7LGfbSa1hGk9QcZ0SyQtVggvBA8LZXA== dependencies: - html-comment-regex "^1.1.2" + fast-xml-parser "^3.19.0" is-svg@^2.0.0: version "2.1.0" @@ -6325,7 +6327,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20: +lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -7510,10 +7512,10 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pathval@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" - integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== peek-readable@^3.1.0: version "3.1.0" @@ -8321,10 +8323,10 @@ prismjs@1.23.0: optionalDependencies: clipboard "^2.0.0" -probe-image-size@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.0.0.tgz#8a287207c04058f33edd31e7a66f6c5eaac02301" - integrity sha512-pddLh5UPAR2tj0GXin5qCFoYa9oo1HOvk+9HYb+V1z7sUjuhCsVbCAKDVvMXwJp5JSsYOoCZ6iQaDpjPvS04nQ== +probe-image-size@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.0.1.tgz#ac815d78bdf8c5643af6e541a572c0077c75f2c1" + integrity sha512-4ua2TGdO761JWaTgbLD2Uq9yFvB34QBAxnqz9GOs3w4+SC4dfbQcnnNY8loHjZV5NRW8PSQRz+WZ6NfUvbuRDA== dependencies: deepmerge "^4.0.0" needle "^2.5.2" @@ -9997,10 +9999,10 @@ syslog-pro@1.0.0: dependencies: moment "^2.22.2" -systeminformation@5.6.2: - version "5.6.2" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.6.2.tgz#41980e6e523eaff09febc3f7e114c4fa0fa239dd" - integrity sha512-B+xKaftatny2HGt9RS9Pk7Rc2Ja5zvb9rRZEiG7mcnQF4KcP68RZz4Ff+V0iVwkqfSVJ/3CIvkG+an/PWwXvbg== +systeminformation@5.6.7: + version "5.6.7" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.6.7.tgz#f56bbc89cac2de4bcb085b96a93b9d627bd6aba8" + integrity sha512-NTgaL6AsRoXKbfZs6t+BkCUwLZjqIiT4IwqGUV2f7PgvDz8359HzOF0xYgDlTHCBR2GeWhFQAfo2wmoYTpz/og== syuilo-password-strength@0.0.1: version "0.0.1" @@ -10447,6 +10449,11 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" @@ -10983,10 +10990,10 @@ webpack-sources@^2.1.1: source-list-map "^2.0.1" source-map "^0.6.1" -webpack@5.25.0: - version "5.25.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.25.0.tgz#f9409977f0f3b6d4b9c4f73adc7d7cb9603a09e9" - integrity sha512-jqQZopNCzt9c4K6Qa7j6kIhzHfR9wgF84go58VoNp4JbZrBr2D2l5lcv72CW80yc6NJl8CR6OY8xctnIs0r2uw== +webpack@5.26.3: + version "5.26.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.26.3.tgz#bafd439abac08fbb82657ec855d038743b725ab8" + integrity sha512-z/F2lt2N1fZqaud1B4SzjL3OW03eULThbBXQ2OX4LSrZX4N9k1A5d0Rje3zS2g887DTWyAV0KGqEf64ois2dhg== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.46" From 606e5c08789d040a2f1d858eed3016cc5fa2c311 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 19 Mar 2021 23:26:44 +0900 Subject: [PATCH 45/76] fix: suppress disk stats error --- src/daemons/server-stats.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/daemons/server-stats.ts b/src/daemons/server-stats.ts index bc1da5eef6..8dfa946250 100644 --- a/src/daemons/server-stats.ts +++ b/src/daemons/server-stats.ts @@ -75,5 +75,5 @@ async function net() { // FS STAT async function fs() { const data = await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 })); - return data; + return data || { rIO_sec: 0, wIO_sec: 0 }; } From e523e54881834a50b7b5e1f0efebcc98cfc34dd7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 20 Mar 2021 11:05:33 +0900 Subject: [PATCH 46/76] perf(server): Reduce database query --- src/models/repositories/notification.ts | 67 ++++++++++++++++++--- src/server/api/endpoints/i/notifications.ts | 2 +- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index d593dc22a9..a9fe5e7b4e 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -1,8 +1,11 @@ -import { EntityRepository, Repository } from 'typeorm'; -import { Users, Notes, UserGroupInvitations, AccessTokens } from '..'; +import { EntityRepository, In, Repository } from 'typeorm'; +import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '..'; import { Notification } from '../entities/notification'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; +import { Note } from '../entities/note'; +import { NoteReaction } from '../entities/note-reaction'; +import { User } from '../entities/user'; export type PackedNotification = SchemaType<typeof packedNotificationSchema>; @@ -10,6 +13,11 @@ export type PackedNotification = SchemaType<typeof packedNotificationSchema>; export class NotificationRepository extends Repository<Notification> { public async pack( src: Notification['id'] | Notification, + options: { + _hintForEachNotes_: { + myReactions: Map<Note['id'], NoteReaction | null>; + }; + } ): Promise<PackedNotification> { const notification = typeof src === 'object' ? src : await this.findOneOrFail(src); const token = notification.appAccessTokenId ? await AccessTokens.findOneOrFail(notification.appAccessTokenId) : null; @@ -22,23 +30,41 @@ export class NotificationRepository extends Repository<Notification> { userId: notification.notifierId, user: notification.notifierId ? Users.pack(notification.notifier || notification.notifierId) : null, ...(notification.type === 'mention' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'reply' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'renote' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'quote' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), } : {}), ...(notification.type === 'reaction' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), reaction: notification.reaction } : {}), ...(notification.type === 'pollVote' ? { - note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId), + note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId, { + detail: true, + _hint_: options._hintForEachNotes_ + }), choice: notification.choice } : {}), ...(notification.type === 'groupInvited' ? { @@ -52,10 +78,31 @@ export class NotificationRepository extends Repository<Notification> { }); } - public packMany( + public async packMany( notifications: Notification[], + meId: User['id'] ) { - return Promise.all(notifications.map(x => this.pack(x))); + if (notifications.length === 0) return []; + + const notes = notifications.filter(x => x.note != null).map(x => x.note!); + const noteIds = notes.map(n => n.id); + const myReactionsMap = new Map<Note['id'], NoteReaction | null>(); + const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); + const targets = [...noteIds, ...renoteIds]; + const myReactions = await NoteReactions.find({ + userId: meId, + noteId: In(targets), + }); + + for (const target of targets) { + myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); + } + + return await Promise.all(notifications.map(x => this.pack(x, { + _hintForEachNotes_: { + myReactions: myReactionsMap + } + }))); } } diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 7a423edb8d..af49549d37 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -112,5 +112,5 @@ export default define(meta, async (ps, user) => { readNotification(user.id, notifications.map(x => x.id)); } - return await Notifications.packMany(notifications); + return await Notifications.packMany(notifications, user.id); }); From f27e4033a6599203ba37efe01edbd988699b4068 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 20 Mar 2021 13:54:59 +0900 Subject: [PATCH 47/76] perf(server): Reduce database query Related: #6813 --- src/models/repositories/note.ts | 86 +++++++++++++++++-- src/models/repositories/notification.ts | 45 +++++++++- src/models/repositories/user.ts | 40 +++++++-- src/server/api/endpoints/antennas/notes.ts | 4 + src/server/api/endpoints/channels/timeline.ts | 4 + src/server/api/endpoints/clips/notes.ts | 4 + src/server/api/endpoints/i/notifications.ts | 6 +- src/server/api/endpoints/notes.ts | 6 +- src/server/api/endpoints/notes/children.ts | 6 +- src/server/api/endpoints/notes/featured.ts | 6 +- .../api/endpoints/notes/global-timeline.ts | 6 +- .../api/endpoints/notes/hybrid-timeline.ts | 4 + .../api/endpoints/notes/local-timeline.ts | 6 +- src/server/api/endpoints/notes/mentions.ts | 6 +- src/server/api/endpoints/notes/renotes.ts | 6 +- src/server/api/endpoints/notes/replies.ts | 6 +- .../api/endpoints/notes/search-by-tag.ts | 6 +- src/server/api/endpoints/notes/search.ts | 6 +- src/server/api/endpoints/notes/timeline.ts | 4 + .../api/endpoints/notes/user-list-timeline.ts | 4 + src/server/api/endpoints/users/notes.ts | 6 +- 21 files changed, 240 insertions(+), 27 deletions(-) diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts index 43caaf94b2..1fcedbd56f 100644 --- a/src/models/repositories/note.ts +++ b/src/models/repositories/note.ts @@ -85,6 +85,7 @@ export class NoteRepository extends Repository<Note> { detail?: boolean; skipHide?: boolean; _hint_?: { + emojis: Emoji[] | null; myReactions: Map<Note['id'], NoteReaction | null>; }; } @@ -141,11 +142,43 @@ export class NoteRepository extends Repository<Note> { * @param reactionNames Note等にリアクションされたカスタム絵文字名 (:は含めない) */ async function populateEmojis(emojiNames: string[], noteUserHost: string | null, reactionNames: string[]) { + const customReactions = reactionNames?.map(x => decodeReaction(x)).filter(x => x.name); + let all = [] as { name: string, url: string }[]; + // 与えられたhintだけで十分(=新たにクエリする必要がない)かどうかを表すフラグ + let enough = true; + if (options?._hint_?.emojis) { + for (const name of emojiNames) { + const matched = options._hint_.emojis.find(x => x.name === name && x.host === noteUserHost); + if (matched) { + all.push({ + name: matched.name, + url: matched.url, + }); + } else { + enough = false; + } + } + for (const customReaction of customReactions) { + const matched = options._hint_.emojis.find(x => x.name === customReaction.name && x.host === customReaction.host); + if (matched) { + all.push({ + name: `${matched.name}@${matched.host || '.'}`, // @host付きでローカルは. + url: matched.url, + }); + } else { + enough = false; + } + } + } else { + enough = false; + } + if (enough) return all; + // カスタム絵文字 if (emojiNames?.length > 0) { const tmp = await Emojis.find({ @@ -164,8 +197,6 @@ export class NoteRepository extends Repository<Note> { all = concat([all, tmp]); } - const customReactions = reactionNames?.map(x => decodeReaction(x)).filter(x => x.name); - if (customReactions?.length > 0) { const where = [] as {}[]; @@ -230,7 +261,12 @@ export class NoteRepository extends Repository<Note> { id: note.id, createdAt: note.createdAt.toISOString(), userId: note.userId, - user: Users.pack(note.user || note.userId, meId), + user: Users.pack(note.user || note.userId, meId, { + detail: false, + _hint_: { + emojis: options?._hint_?.emojis || null + } + }), text: text, cw: note.cw, visibility: note.visibility, @@ -258,12 +294,12 @@ export class NoteRepository extends Repository<Note> { _prId_: (note as any)._prId_ || undefined, ...(opts.detail ? { - reply: note.replyId ? this.pack(note.replyId, meId, { + reply: note.replyId ? this.pack(note.reply || note.replyId, meId, { detail: false, _hint_: options?._hint_ }) : undefined, - renote: note.renoteId ? this.pack(note.renoteId, meId, { + renote: note.renoteId ? this.pack(note.renote || note.renoteId, meId, { detail: true, _hint_: options?._hint_ }) : undefined, @@ -314,10 +350,48 @@ export class NoteRepository extends Repository<Note> { } } + // TODO: ここら辺の処理をaggregateEmojisみたいな関数に切り出したい + let emojisWhere: any[] = []; + for (const note of notes) { + if (typeof note !== 'object') continue; + emojisWhere.push({ + name: In(note.emojis), + host: note.userHost + }); + if (note.renote) { + emojisWhere.push({ + name: In(note.renote.emojis), + host: note.renote.userHost + }); + if (note.renote.user) { + emojisWhere.push({ + name: In(note.renote.user.emojis), + host: note.renote.userHost + }); + } + } + const customReactions = Object.keys(note.reactions).map(x => decodeReaction(x)).filter(x => x.name); + emojisWhere = emojisWhere.concat(customReactions.map(x => ({ + name: x.name, + host: x.host + }))); + if (note.user) { + emojisWhere.push({ + name: In(note.user.emojis), + host: note.userHost + }); + } + } + const emojis = emojisWhere.length > 0 ? await Emojis.find({ + where: emojisWhere, + select: ['name', 'host', 'url'] + }) : null; + return await Promise.all(notes.map(n => this.pack(n, me, { ...options, _hint_: { - myReactions: myReactionsMap + myReactions: myReactionsMap, + emojis: emojis } }))); } diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index a9fe5e7b4e..1027155873 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -1,11 +1,13 @@ import { EntityRepository, In, Repository } from 'typeorm'; -import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '..'; +import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions, Emojis } from '..'; import { Notification } from '../entities/notification'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; import { Note } from '../entities/note'; import { NoteReaction } from '../entities/note-reaction'; import { User } from '../entities/user'; +import { decodeReaction } from '../../misc/reaction-lib'; +import { Emoji } from '../entities/emoji'; export type PackedNotification = SchemaType<typeof packedNotificationSchema>; @@ -15,6 +17,7 @@ export class NotificationRepository extends Repository<Notification> { src: Notification['id'] | Notification, options: { _hintForEachNotes_: { + emojis: Emoji[] | null; myReactions: Map<Note['id'], NoteReaction | null>; }; } @@ -98,9 +101,47 @@ export class NotificationRepository extends Repository<Notification> { myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); } + // TODO: ここら辺の処理をaggregateEmojisみたいな関数に切り出したい + let emojisWhere: any[] = []; + for (const note of notes) { + if (typeof note !== 'object') continue; + emojisWhere.push({ + name: In(note.emojis), + host: note.userHost + }); + if (note.renote) { + emojisWhere.push({ + name: In(note.renote.emojis), + host: note.renote.userHost + }); + if (note.renote.user) { + emojisWhere.push({ + name: In(note.renote.user.emojis), + host: note.renote.userHost + }); + } + } + const customReactions = Object.keys(note.reactions).map(x => decodeReaction(x)).filter(x => x.name); + emojisWhere = emojisWhere.concat(customReactions.map(x => ({ + name: x.name, + host: x.host + }))); + if (note.user) { + emojisWhere.push({ + name: In(note.user.emojis), + host: note.userHost + }); + } + } + const emojis = emojisWhere.length > 0 ? await Emojis.find({ + where: emojisWhere, + select: ['name', 'host', 'url'] + }) : null; + return await Promise.all(notifications.map(x => this.pack(x, { _hintForEachNotes_: { - myReactions: myReactionsMap + myReactions: myReactionsMap, + emojis: emojis, } }))); } diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index 3a6ab48c5f..19b0e54239 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -5,6 +5,7 @@ import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMes import config from '../../config'; import { SchemaType } from '../../misc/schema'; import { awaitAll } from '../../prelude/await-all'; +import { Emoji } from '../entities/emoji'; export type PackedUser = SchemaType<typeof packedUserSchema>; @@ -149,6 +150,9 @@ export class UserRepository extends Repository<User> { options?: { detail?: boolean, includeSecrets?: boolean, + _hint_?: { + emojis: Emoji[] | null; + }; } ): Promise<PackedUser> { const opts = Object.assign({ @@ -166,6 +170,34 @@ export class UserRepository extends Repository<User> { }) : []; const profile = opts.detail ? await UserProfiles.findOneOrFail(user.id) : null; + let emojis: Emoji[] = []; + if (user.emojis.length > 0) { + // 与えられたhintだけで十分(=新たにクエリする必要がない)かどうかを表すフラグ + let enough = true; + if (options?._hint_?.emojis) { + for (const name of user.emojis) { + const matched = options._hint_.emojis.find(x => x.name === name && x.host === user.host); + if (matched) { + emojis.push(matched); + } else { + enough = false; + } + } + } else { + enough = false; + } + + if (!enough) { + emojis = await Emojis.find({ + where: { + name: In(user.emojis), + host: user.host + }, + select: ['name', 'host', 'url', 'aliases'] + }); + } + } + const falsy = opts.detail ? false : undefined; const packed = { @@ -190,13 +222,7 @@ export class UserRepository extends Repository<User> { } : undefined) : undefined, // カスタム絵文字添付 - emojis: user.emojis.length > 0 ? Emojis.find({ - where: { - name: In(user.emojis), - host: user.host - }, - select: ['name', 'host', 'url', 'aliases'] - }) : [], + emojis: emojis, ...(opts.detail ? { url: profile!.url, diff --git a/src/server/api/endpoints/antennas/notes.ts b/src/server/api/endpoints/antennas/notes.ts index 750fc080cf..2ea3e43745 100644 --- a/src/server/api/endpoints/antennas/notes.ts +++ b/src/server/api/endpoints/antennas/notes.ts @@ -74,6 +74,10 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ antennaQuery.getQuery() })`) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(antennaQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/src/server/api/endpoints/channels/timeline.ts b/src/server/api/endpoints/channels/timeline.ts index acb34f124d..292f21b63d 100644 --- a/src/server/api/endpoints/channels/timeline.ts +++ b/src/server/api/endpoints/channels/timeline.ts @@ -88,6 +88,10 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.channelId = :channelId', { channelId: channel.id }) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); //#endregion diff --git a/src/server/api/endpoints/clips/notes.ts b/src/server/api/endpoints/clips/notes.ts index 6a507e2036..86d1e507f4 100644 --- a/src/server/api/endpoints/clips/notes.ts +++ b/src/server/api/endpoints/clips/notes.ts @@ -72,6 +72,10 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ clipQuery.getQuery() })`) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(clipQuery.getParameters()); if (user) { diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index af49549d37..812a4bd1dd 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -87,7 +87,11 @@ export default define(meta, async (ps, user) => { .andWhere(`notification.notifieeId = :meId`, { meId: user.id }) .leftJoinAndSelect('notification.notifier', 'notifier') .leftJoinAndSelect('notification.note', 'note') - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); query.andWhere(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`); query.setParameters(mutingQuery.getParameters()); diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index fab8455d78..708ce38c0e 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -76,7 +76,11 @@ export default define(meta, async (ps) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.visibility = 'public'`) .andWhere(`note.localOnly = FALSE`) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); if (ps.local) { query.andWhere('note.userHost IS NULL'); diff --git a/src/server/api/endpoints/notes/children.ts b/src/server/api/endpoints/notes/children.ts index 0875e0f240..a8b239e445 100644 --- a/src/server/api/endpoints/notes/children.ts +++ b/src/server/api/endpoints/notes/children.ts @@ -64,7 +64,11 @@ export default define(meta, async (ps, user) => { })); })); })) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts index 4dda7d0edb..6c416b1c04 100644 --- a/src/server/api/endpoints/notes/featured.ts +++ b/src/server/api/endpoints/notes/featured.ts @@ -49,7 +49,11 @@ export default define(meta, async (ps, user) => { .andWhere(`note.score > 0`) .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) .andWhere(`note.visibility = 'public'`) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 6d99f1fdbc..64c2f5851c 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -81,7 +81,11 @@ export default define(meta, async (ps, user) => { ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateRepliesQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index 2b91b8c67b..c01e170aa9 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -130,6 +130,10 @@ export default define(meta, async (ps, user) => { .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); })) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index 51e35e6241..31abb26882 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -98,7 +98,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateChannelQuery(query, user); generateRepliesQuery(query, user); diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 1e3014bd46..83e890d9ad 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -63,7 +63,11 @@ export default define(meta, async (ps, user) => { .where(`:meId = ANY(note.mentions)`, { meId: user.id }) .orWhere(`:meId = ANY(note.visibleUserIds)`, { meId: user.id }); })) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts index 31c24f294a..f528197220 100644 --- a/src/server/api/endpoints/notes/renotes.ts +++ b/src/server/api/endpoints/notes/renotes.ts @@ -68,7 +68,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.renoteId = :renoteId`, { renoteId: note.id }) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 9fad74c78e..5ac663d77a 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -59,7 +59,11 @@ export const meta = { export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts index e0f7f4d62c..d878f379c3 100644 --- a/src/server/api/endpoints/notes/search-by-tag.ts +++ b/src/server/api/endpoints/notes/search-by-tag.ts @@ -95,7 +95,11 @@ export const meta = { export default define(meta, async (ps, me) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index 1aca056299..ae0e9242a6 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -79,7 +79,11 @@ export default define(meta, async (ps, me) => { query .andWhere('note.text ILIKE :q', { q: `%${ps.query}%` }) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index f09f3d1733..c91bea0c1f 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -123,6 +123,10 @@ export default define(meta, async (ps, user) => { if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); })) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index b0ff499d95..040f017fd5 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -131,6 +131,10 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.userId IN (${ listQuery.getQuery() })`) .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(listQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index 33e3ecb03f..91d1d7f4dd 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -131,7 +131,11 @@ export default define(meta, async (ps, me) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.userId = :userId', { userId: user.id }) - .leftJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me, user); From 73df95c42d98c38d362e02a79c5d7620a06f6633 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 20 Mar 2021 14:09:17 +0900 Subject: [PATCH 48/76] =?UTF-8?q?=E3=82=AF=E3=83=A9=E3=82=A4=E3=82=A2?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=82=B5=E3=82=A4=E3=83=89=E3=81=A7=E5=AE=9F?= =?UTF-8?q?=E8=A3=85=E3=81=97=E3=81=9F=E3=81=84=E3=81=9F=E3=82=81=E4=B8=80?= =?UTF-8?q?=E6=97=A6=20injectPromo=20=E3=81=8A=E3=82=88=E3=81=B3=20injectF?= =?UTF-8?q?eatured=20=E3=82=92=E7=84=A1=E5=8A=B9=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/api/endpoints/notes/global-timeline.ts | 5 ----- src/server/api/endpoints/notes/hybrid-timeline.ts | 5 ----- src/server/api/endpoints/notes/local-timeline.ts | 5 ----- src/server/api/endpoints/notes/timeline.ts | 5 ----- 4 files changed, 20 deletions(-) diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 64c2f5851c..985760bd9d 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -8,8 +8,6 @@ import { Notes } from '../../../../models'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { activeUsersChart } from '../../../../services/chart'; import { generateRepliesQuery } from '../../common/generate-replies-query'; -import { injectPromo } from '../../common/inject-promo'; -import { injectFeatured } from '../../common/inject-featured'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; export const meta = { @@ -98,9 +96,6 @@ export default define(meta, async (ps, user) => { const timeline = await query.take(ps.limit!).getMany(); - await injectPromo(timeline, user); - await injectFeatured(timeline, user); - process.nextTick(() => { if (user) { activeUsersChart.update(user); diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index c01e170aa9..69f2a7c107 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -10,8 +10,6 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query' import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { activeUsersChart } from '../../../../services/chart'; import { generateRepliesQuery } from '../../common/generate-replies-query'; -import { injectPromo } from '../../common/inject-promo'; -import { injectFeatured } from '../../common/inject-featured'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; import { generateChannelQuery } from '../../common/generate-channel-query'; @@ -179,9 +177,6 @@ export default define(meta, async (ps, user) => { const timeline = await query.take(ps.limit!).getMany(); - await injectPromo(timeline, user); - await injectFeatured(timeline, user); - process.nextTick(() => { if (user) { activeUsersChart.update(user); diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index 31abb26882..9e1c081966 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -10,8 +10,6 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query' import { activeUsersChart } from '../../../../services/chart'; import { Brackets } from 'typeorm'; import { generateRepliesQuery } from '../../common/generate-replies-query'; -import { injectPromo } from '../../common/inject-promo'; -import { injectFeatured } from '../../common/inject-featured'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; import { generateChannelQuery } from '../../common/generate-channel-query'; @@ -132,9 +130,6 @@ export default define(meta, async (ps, user) => { const timeline = await query.take(ps.limit!).getMany(); - await injectPromo(timeline, user); - await injectFeatured(timeline, user); - process.nextTick(() => { if (user) { activeUsersChart.update(user); diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index c91bea0c1f..a158bcd3f0 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -8,8 +8,6 @@ import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { activeUsersChart } from '../../../../services/chart'; import { Brackets } from 'typeorm'; import { generateRepliesQuery } from '../../common/generate-replies-query'; -import { injectPromo } from '../../common/inject-promo'; -import { injectFeatured } from '../../common/inject-featured'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query'; import { generateChannelQuery } from '../../common/generate-channel-query'; @@ -172,9 +170,6 @@ export default define(meta, async (ps, user) => { const timeline = await query.take(ps.limit!).getMany(); - await injectPromo(timeline, user); - await injectFeatured(timeline, user); - process.nextTick(() => { if (user) { activeUsersChart.update(user); From 9bb6dc52b855dcebd0c0493240d6282ba21810c2 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 20 Mar 2021 15:42:02 +0900 Subject: [PATCH 49/76] =?UTF-8?q?clean=20up=20=E3=81=AA=E3=81=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/init.ts | 34 ---------------------------------- src/client/store.ts | 1 - src/client/theme-store.ts | 27 --------------------------- src/server/api/define.ts | 1 + src/server/api/endpoints/i.ts | 27 +++------------------------ 5 files changed, 4 insertions(+), 86 deletions(-) diff --git a/src/client/init.ts b/src/client/init.ts index 1c44e7f23e..2a2b6a2f86 100644 --- a/src/client/init.ts +++ b/src/client/init.ts @@ -4,40 +4,6 @@ import '@/style.scss'; -// TODO: そのうち消す -if (localStorage.getItem('vuex') != null) { - const vuex = JSON.parse(localStorage.getItem('vuex')); - - localStorage.setItem('account', JSON.stringify({ - ...vuex.i, - token: localStorage.getItem('i') - })); - localStorage.setItem('accounts', JSON.stringify(vuex.device.accounts)); - localStorage.setItem('miux:themes', JSON.stringify(vuex.device.themes)); - - if (vuex.device.userData) { - for (const [k, v] of Object.entries(vuex.device.userData)) { - localStorage.setItem('pizzax::base::' + k, JSON.stringify({ - widgets: v.widgets - })); - - if (v.deck) { - localStorage.setItem('pizzax::deck::' + k, JSON.stringify({ - columns: v.deck.columns, - layout: v.deck.layout, - })); - } - } - } - - localStorage.setItem('vuex-old', JSON.stringify(vuex)); - localStorage.removeItem('vuex'); - localStorage.removeItem('i'); - localStorage.removeItem('locale'); - - location.reload(); -} - import * as Sentry from '@sentry/browser'; import { Integrations } from '@sentry/tracing'; import { createApp, watch } from 'vue'; diff --git a/src/client/store.ts b/src/client/store.ts index 14924dadd0..e6fdd12f1d 100644 --- a/src/client/store.ts +++ b/src/client/store.ts @@ -212,7 +212,6 @@ type Plugin = { */ export class ColdDeviceStorage { public static default = { - themes: [] as Theme[], // TODO: そのうち消す // TODO: テーマをアカウントに保存するようになったのにもかかわらず、以下のどのテーマを使うかという情報だけがブラウザ保存になっていて、アカウント切り替えたりログアウトしたときに不具合が発生するのでなんとかする // テーマIDを保存するのではなく、テーマ自体を保存するようにすれば解決するかも darkTheme: '8050783a-7f63-445a-b270-36d0f6ba1677', diff --git a/src/client/theme-store.ts b/src/client/theme-store.ts index 5e440efbf9..8e21af70fc 100644 --- a/src/client/theme-store.ts +++ b/src/client/theme-store.ts @@ -33,30 +33,3 @@ export async function removeTheme(theme: Theme): Promise<void> { await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); localStorage.setItem(lsCacheKey, JSON.stringify(themes)); } - -// TODO: そのうち消す -if (ColdDeviceStorage.get('themes').length > 0) { - const lsThemes = ColdDeviceStorage.get('themes'); - let registryThemes; - try { - registryThemes = await api('i/registry/get', { scope: ['client'], key: 'themes' }); - } catch (e) { - if (e.code === 'NO_SUCH_KEY') { - registryThemes = []; - } else { - throw e; - } - } - const themes = [] as Theme[]; - for (const theme of lsThemes) { - if (themes.some(x => x.id === theme.id)) continue; - themes.push(theme); - } - for (const theme of registryThemes) { - if (themes.some(x => x.id === theme.id)) continue; - themes.push(theme); - } - await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); - localStorage.setItem(lsCacheKey, JSON.stringify(themes)); - ColdDeviceStorage.set('themes', []); -} diff --git a/src/server/api/define.ts b/src/server/api/define.ts index 1c7ee26479..4e59357c13 100644 --- a/src/server/api/define.ts +++ b/src/server/api/define.ts @@ -18,6 +18,7 @@ type executor<T extends IEndpointMeta> = (params: Params<T>, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any, cleanup?: Function) => Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>; +// TODO: API関数に user まるごと渡すのではなくユーザーIDなどの最小限のプロパティだけ渡すようにしたい(キャッシュとか考えないでよくなるため) export default function <T extends IEndpointMeta>(meta: T, cb: executor<T>) : (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any) => Promise<any> { return (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any) => { diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts index e5b65e0930..87f6ae778d 100644 --- a/src/server/api/endpoints/i.ts +++ b/src/server/api/endpoints/i.ts @@ -1,6 +1,5 @@ import define from '../define'; -import { RegistryItems, UserProfiles, Users } from '../../../models'; -import { genId } from '../../../misc/gen-id'; +import { Users } from '../../../models'; export const meta = { desc: { @@ -23,28 +22,8 @@ export const meta = { export default define(meta, async (ps, user, token) => { const isSecure = token == null; - // TODO: そのうち消す - const profile = await UserProfiles.findOneOrFail(user.id); - for (const [k, v] of Object.entries(profile.clientData)) { - await RegistryItems.insert({ - id: genId(), - createdAt: new Date(), - updatedAt: new Date(), - userId: user.id, - domain: null, - scope: ['client', 'base'], - key: k, - value: v - }); - } - await UserProfiles.createQueryBuilder().update() - .set({ - clientData: {}, - }) - .where('userId = :id', { id: user.id }) - .execute(); - - return await Users.pack(user, user, { + // ここで渡ってきている user はキャッシュされていて古い可能性もあるので id だけ渡す + return await Users.pack(user.id, user, { detail: true, includeSecrets: isSecure }); From d7e7848c9206b68ab86d2d566ec4247db87827c0 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 10:39:32 +0900 Subject: [PATCH 50/76] fix(server): Use inner join https://github.com/syuilo/misskey/issues/6813#issuecomment-803400023 --- src/server/api/common/inject-featured.ts | 2 +- src/server/api/endpoints/antennas/notes.ts | 10 +++++----- src/server/api/endpoints/channels/timeline.ts | 10 +++++----- src/server/api/endpoints/clips/notes.ts | 10 +++++----- src/server/api/endpoints/i/notifications.ts | 10 +++++----- src/server/api/endpoints/notes.ts | 10 +++++----- src/server/api/endpoints/notes/children.ts | 10 +++++----- src/server/api/endpoints/notes/featured.ts | 10 +++++----- src/server/api/endpoints/notes/global-timeline.ts | 10 +++++----- src/server/api/endpoints/notes/hybrid-timeline.ts | 10 +++++----- src/server/api/endpoints/notes/local-timeline.ts | 10 +++++----- src/server/api/endpoints/notes/mentions.ts | 10 +++++----- src/server/api/endpoints/notes/renotes.ts | 10 +++++----- src/server/api/endpoints/notes/replies.ts | 10 +++++----- src/server/api/endpoints/notes/search-by-tag.ts | 10 +++++----- src/server/api/endpoints/notes/search.ts | 10 +++++----- src/server/api/endpoints/notes/timeline.ts | 10 +++++----- src/server/api/endpoints/notes/user-list-timeline.ts | 10 +++++----- src/server/api/endpoints/users/notes.ts | 10 +++++----- 19 files changed, 91 insertions(+), 91 deletions(-) diff --git a/src/server/api/common/inject-featured.ts b/src/server/api/common/inject-featured.ts index 3f47c13385..bbed7f69cb 100644 --- a/src/server/api/common/inject-featured.ts +++ b/src/server/api/common/inject-featured.ts @@ -23,7 +23,7 @@ export async function injectFeatured(timeline: Note[], user?: User | null) { .andWhere(`note.score > 0`) .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) .andWhere(`note.visibility = 'public'`) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user'); if (user) { query.andWhere('note.userId != :userId', { userId: user.id }); diff --git a/src/server/api/endpoints/antennas/notes.ts b/src/server/api/endpoints/antennas/notes.ts index 2ea3e43745..8025ecc66b 100644 --- a/src/server/api/endpoints/antennas/notes.ts +++ b/src/server/api/endpoints/antennas/notes.ts @@ -73,11 +73,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ antennaQuery.getQuery() })`) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser') .setParameters(antennaQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/src/server/api/endpoints/channels/timeline.ts b/src/server/api/endpoints/channels/timeline.ts index 292f21b63d..33dcb24144 100644 --- a/src/server/api/endpoints/channels/timeline.ts +++ b/src/server/api/endpoints/channels/timeline.ts @@ -87,11 +87,11 @@ export default define(meta, async (ps, user) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.channelId = :channelId', { channelId: channel.id }) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); //#endregion diff --git a/src/server/api/endpoints/clips/notes.ts b/src/server/api/endpoints/clips/notes.ts index 86d1e507f4..12baa2e344 100644 --- a/src/server/api/endpoints/clips/notes.ts +++ b/src/server/api/endpoints/clips/notes.ts @@ -71,11 +71,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ clipQuery.getQuery() })`) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser') .setParameters(clipQuery.getParameters()); if (user) { diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 812a4bd1dd..e3091ebb0a 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -87,11 +87,11 @@ export default define(meta, async (ps, user) => { .andWhere(`notification.notifieeId = :meId`, { meId: user.id }) .leftJoinAndSelect('notification.notifier', 'notifier') .leftJoinAndSelect('notification.note', 'note') - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); query.andWhere(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`); query.setParameters(mutingQuery.getParameters()); diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index 708ce38c0e..9fd644dcab 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -76,11 +76,11 @@ export default define(meta, async (ps) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.visibility = 'public'`) .andWhere(`note.localOnly = FALSE`) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); if (ps.local) { query.andWhere('note.userHost IS NULL'); diff --git a/src/server/api/endpoints/notes/children.ts b/src/server/api/endpoints/notes/children.ts index a8b239e445..4a03ae2d39 100644 --- a/src/server/api/endpoints/notes/children.ts +++ b/src/server/api/endpoints/notes/children.ts @@ -64,11 +64,11 @@ export default define(meta, async (ps, user) => { })); })); })) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts index 6c416b1c04..74a28f25a4 100644 --- a/src/server/api/endpoints/notes/featured.ts +++ b/src/server/api/endpoints/notes/featured.ts @@ -49,11 +49,11 @@ export default define(meta, async (ps, user) => { .andWhere(`note.score > 0`) .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) .andWhere(`note.visibility = 'public'`) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 985760bd9d..e6ad9eeba8 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -79,11 +79,11 @@ export default define(meta, async (ps, user) => { ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); generateRepliesQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index 69f2a7c107..aa09be23d2 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -127,11 +127,11 @@ export default define(meta, async (ps, user) => { qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id }) .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); })) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser') .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index 9e1c081966..cb6f854b05 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -96,11 +96,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); generateChannelQuery(query, user); generateRepliesQuery(query, user); diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 83e890d9ad..447a689664 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -63,11 +63,11 @@ export default define(meta, async (ps, user) => { .where(`:meId = ANY(note.mentions)`, { meId: user.id }) .orWhere(`:meId = ANY(note.visibleUserIds)`, { meId: user.id }); })) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts index f528197220..5be8f42a16 100644 --- a/src/server/api/endpoints/notes/renotes.ts +++ b/src/server/api/endpoints/notes/renotes.ts @@ -68,11 +68,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.renoteId = :renoteId`, { renoteId: note.id }) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 5ac663d77a..0979d2e7e9 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -59,11 +59,11 @@ export const meta = { export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts index d878f379c3..af84db1745 100644 --- a/src/server/api/endpoints/notes/search-by-tag.ts +++ b/src/server/api/endpoints/notes/search-by-tag.ts @@ -95,11 +95,11 @@ export const meta = { export default define(meta, async (ps, me) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index ae0e9242a6..58aabcc22c 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -79,11 +79,11 @@ export default define(meta, async (ps, me) => { query .andWhere('note.text ILIKE :q', { q: `%${ps.query}%` }) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index a158bcd3f0..e8cda654c1 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -120,11 +120,11 @@ export default define(meta, async (ps, user) => { .where('note.userId = :meId', { meId: user.id }); if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); })) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser') .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index 040f017fd5..1e9af55f1d 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -130,11 +130,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.userId IN (${ listQuery.getQuery() })`) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser') .setParameters(listQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index 91d1d7f4dd..98c9f10308 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -131,11 +131,11 @@ export default define(meta, async (ps, me) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.userId = :userId', { userId: user.id }) - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .innerJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.reply', 'reply') + .innerJoinAndSelect('note.renote', 'renote') + .innerJoinAndSelect('reply.user', 'replyUser') + .innerJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me, user); From e987a50e9fc7bee1b86840cee15a6c86d2d1525d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 10:41:21 +0900 Subject: [PATCH 51/76] perf(server): Improve following/followers API performance Related #6813 --- src/server/api/endpoints/users/followers.ts | 3 ++- src/server/api/endpoints/users/following.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts index bd4a2739c6..fb83d7beb8 100644 --- a/src/server/api/endpoints/users/followers.ts +++ b/src/server/api/endpoints/users/followers.ts @@ -76,7 +76,8 @@ export default define(meta, async (ps, me) => { } const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere(`following.followeeId = :userId`, { userId: user.id }); + .andWhere(`following.followeeId = :userId`, { userId: user.id }) + .innerJoinAndSelect('following.follower', 'follower'); const followings = await query .take(ps.limit!) diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts index 9efb8bfc93..d5e8dc1f92 100644 --- a/src/server/api/endpoints/users/following.ts +++ b/src/server/api/endpoints/users/following.ts @@ -76,7 +76,8 @@ export default define(meta, async (ps, me) => { } const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere(`following.followerId = :userId`, { userId: user.id }); + .andWhere(`following.followerId = :userId`, { userId: user.id }) + .innerJoinAndSelect('following.followee', 'followee'); const followings = await query .take(ps.limit!) From c52b50414094e2d44aaea3ab78ef17d04566db80 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 12:31:56 +0900 Subject: [PATCH 52/76] Revert "fix(server): Use inner join" This reverts commit d7e7848c9206b68ab86d2d566ec4247db87827c0. --- src/server/api/common/inject-featured.ts | 2 +- src/server/api/endpoints/antennas/notes.ts | 10 +++++----- src/server/api/endpoints/channels/timeline.ts | 10 +++++----- src/server/api/endpoints/clips/notes.ts | 10 +++++----- src/server/api/endpoints/i/notifications.ts | 10 +++++----- src/server/api/endpoints/notes.ts | 10 +++++----- src/server/api/endpoints/notes/children.ts | 10 +++++----- src/server/api/endpoints/notes/featured.ts | 10 +++++----- src/server/api/endpoints/notes/global-timeline.ts | 10 +++++----- src/server/api/endpoints/notes/hybrid-timeline.ts | 10 +++++----- src/server/api/endpoints/notes/local-timeline.ts | 10 +++++----- src/server/api/endpoints/notes/mentions.ts | 10 +++++----- src/server/api/endpoints/notes/renotes.ts | 10 +++++----- src/server/api/endpoints/notes/replies.ts | 10 +++++----- src/server/api/endpoints/notes/search-by-tag.ts | 10 +++++----- src/server/api/endpoints/notes/search.ts | 10 +++++----- src/server/api/endpoints/notes/timeline.ts | 10 +++++----- src/server/api/endpoints/notes/user-list-timeline.ts | 10 +++++----- src/server/api/endpoints/users/notes.ts | 10 +++++----- 19 files changed, 91 insertions(+), 91 deletions(-) diff --git a/src/server/api/common/inject-featured.ts b/src/server/api/common/inject-featured.ts index bbed7f69cb..3f47c13385 100644 --- a/src/server/api/common/inject-featured.ts +++ b/src/server/api/common/inject-featured.ts @@ -23,7 +23,7 @@ export async function injectFeatured(timeline: Note[], user?: User | null) { .andWhere(`note.score > 0`) .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) .andWhere(`note.visibility = 'public'`) - .innerJoinAndSelect('note.user', 'user'); + .leftJoinAndSelect('note.user', 'user'); if (user) { query.andWhere('note.userId != :userId', { userId: user.id }); diff --git a/src/server/api/endpoints/antennas/notes.ts b/src/server/api/endpoints/antennas/notes.ts index 8025ecc66b..2ea3e43745 100644 --- a/src/server/api/endpoints/antennas/notes.ts +++ b/src/server/api/endpoints/antennas/notes.ts @@ -73,11 +73,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ antennaQuery.getQuery() })`) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(antennaQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/src/server/api/endpoints/channels/timeline.ts b/src/server/api/endpoints/channels/timeline.ts index 33dcb24144..292f21b63d 100644 --- a/src/server/api/endpoints/channels/timeline.ts +++ b/src/server/api/endpoints/channels/timeline.ts @@ -87,11 +87,11 @@ export default define(meta, async (ps, user) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.channelId = :channelId', { channelId: channel.id }) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); //#endregion diff --git a/src/server/api/endpoints/clips/notes.ts b/src/server/api/endpoints/clips/notes.ts index 12baa2e344..86d1e507f4 100644 --- a/src/server/api/endpoints/clips/notes.ts +++ b/src/server/api/endpoints/clips/notes.ts @@ -71,11 +71,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ clipQuery.getQuery() })`) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(clipQuery.getParameters()); if (user) { diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index e3091ebb0a..812a4bd1dd 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -87,11 +87,11 @@ export default define(meta, async (ps, user) => { .andWhere(`notification.notifieeId = :meId`, { meId: user.id }) .leftJoinAndSelect('notification.notifier', 'notifier') .leftJoinAndSelect('notification.note', 'note') - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); query.andWhere(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`); query.setParameters(mutingQuery.getParameters()); diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index 9fd644dcab..708ce38c0e 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -76,11 +76,11 @@ export default define(meta, async (ps) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.visibility = 'public'`) .andWhere(`note.localOnly = FALSE`) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); if (ps.local) { query.andWhere('note.userHost IS NULL'); diff --git a/src/server/api/endpoints/notes/children.ts b/src/server/api/endpoints/notes/children.ts index 4a03ae2d39..a8b239e445 100644 --- a/src/server/api/endpoints/notes/children.ts +++ b/src/server/api/endpoints/notes/children.ts @@ -64,11 +64,11 @@ export default define(meta, async (ps, user) => { })); })); })) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts index 74a28f25a4..6c416b1c04 100644 --- a/src/server/api/endpoints/notes/featured.ts +++ b/src/server/api/endpoints/notes/featured.ts @@ -49,11 +49,11 @@ export default define(meta, async (ps, user) => { .andWhere(`note.score > 0`) .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) .andWhere(`note.visibility = 'public'`) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index e6ad9eeba8..985760bd9d 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -79,11 +79,11 @@ export default define(meta, async (ps, user) => { ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateRepliesQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index aa09be23d2..69f2a7c107 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -127,11 +127,11 @@ export default define(meta, async (ps, user) => { qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id }) .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); })) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index cb6f854b05..9e1c081966 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -96,11 +96,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateChannelQuery(query, user); generateRepliesQuery(query, user); diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 447a689664..83e890d9ad 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -63,11 +63,11 @@ export default define(meta, async (ps, user) => { .where(`:meId = ANY(note.mentions)`, { meId: user.id }) .orWhere(`:meId = ANY(note.visibleUserIds)`, { meId: user.id }); })) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts index 5be8f42a16..f528197220 100644 --- a/src/server/api/endpoints/notes/renotes.ts +++ b/src/server/api/endpoints/notes/renotes.ts @@ -68,11 +68,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.renoteId = :renoteId`, { renoteId: note.id }) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 0979d2e7e9..5ac663d77a 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -59,11 +59,11 @@ export const meta = { export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts index af84db1745..d878f379c3 100644 --- a/src/server/api/endpoints/notes/search-by-tag.ts +++ b/src/server/api/endpoints/notes/search-by-tag.ts @@ -95,11 +95,11 @@ export const meta = { export default define(meta, async (ps, me) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index 58aabcc22c..ae0e9242a6 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -79,11 +79,11 @@ export default define(meta, async (ps, me) => { query .andWhere('note.text ILIKE :q', { q: `%${ps.query}%` }) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index e8cda654c1..a158bcd3f0 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -120,11 +120,11 @@ export default define(meta, async (ps, user) => { .where('note.userId = :meId', { meId: user.id }); if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); })) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index 1e9af55f1d..040f017fd5 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -130,11 +130,11 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.userId IN (${ listQuery.getQuery() })`) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') .setParameters(listQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index 98c9f10308..91d1d7f4dd 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -131,11 +131,11 @@ export default define(meta, async (ps, me) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.userId = :userId', { userId: user.id }) - .innerJoinAndSelect('note.user', 'user') - .innerJoinAndSelect('note.reply', 'reply') - .innerJoinAndSelect('note.renote', 'renote') - .innerJoinAndSelect('reply.user', 'replyUser') - .innerJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me, user); From 70b129cc421c10260a298519521ca646cbc59ab8 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 12:33:37 +0900 Subject: [PATCH 53/76] fix(server): Use inner join --- src/server/api/common/inject-featured.ts | 2 +- src/server/api/endpoints/antennas/notes.ts | 2 +- src/server/api/endpoints/channels/timeline.ts | 2 +- src/server/api/endpoints/clips/notes.ts | 2 +- src/server/api/endpoints/notes.ts | 2 +- src/server/api/endpoints/notes/children.ts | 2 +- src/server/api/endpoints/notes/featured.ts | 2 +- src/server/api/endpoints/notes/global-timeline.ts | 2 +- src/server/api/endpoints/notes/hybrid-timeline.ts | 2 +- src/server/api/endpoints/notes/local-timeline.ts | 2 +- src/server/api/endpoints/notes/mentions.ts | 2 +- src/server/api/endpoints/notes/renotes.ts | 2 +- src/server/api/endpoints/notes/replies.ts | 2 +- src/server/api/endpoints/notes/search-by-tag.ts | 2 +- src/server/api/endpoints/notes/search.ts | 2 +- src/server/api/endpoints/notes/timeline.ts | 2 +- src/server/api/endpoints/notes/user-list-timeline.ts | 2 +- src/server/api/endpoints/users/notes.ts | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/server/api/common/inject-featured.ts b/src/server/api/common/inject-featured.ts index 3f47c13385..bbed7f69cb 100644 --- a/src/server/api/common/inject-featured.ts +++ b/src/server/api/common/inject-featured.ts @@ -23,7 +23,7 @@ export async function injectFeatured(timeline: Note[], user?: User | null) { .andWhere(`note.score > 0`) .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) .andWhere(`note.visibility = 'public'`) - .leftJoinAndSelect('note.user', 'user'); + .innerJoinAndSelect('note.user', 'user'); if (user) { query.andWhere('note.userId != :userId', { userId: user.id }); diff --git a/src/server/api/endpoints/antennas/notes.ts b/src/server/api/endpoints/antennas/notes.ts index 2ea3e43745..6fd3cf2df5 100644 --- a/src/server/api/endpoints/antennas/notes.ts +++ b/src/server/api/endpoints/antennas/notes.ts @@ -73,7 +73,7 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ antennaQuery.getQuery() })`) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/channels/timeline.ts b/src/server/api/endpoints/channels/timeline.ts index 292f21b63d..00a7cd86d5 100644 --- a/src/server/api/endpoints/channels/timeline.ts +++ b/src/server/api/endpoints/channels/timeline.ts @@ -87,7 +87,7 @@ export default define(meta, async (ps, user) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.channelId = :channelId', { channelId: channel.id }) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/clips/notes.ts b/src/server/api/endpoints/clips/notes.ts index 86d1e507f4..676629c328 100644 --- a/src/server/api/endpoints/clips/notes.ts +++ b/src/server/api/endpoints/clips/notes.ts @@ -71,7 +71,7 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ clipQuery.getQuery() })`) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index 708ce38c0e..30e6e92fec 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -76,7 +76,7 @@ export default define(meta, async (ps) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.visibility = 'public'`) .andWhere(`note.localOnly = FALSE`) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/children.ts b/src/server/api/endpoints/notes/children.ts index a8b239e445..072a25e024 100644 --- a/src/server/api/endpoints/notes/children.ts +++ b/src/server/api/endpoints/notes/children.ts @@ -64,7 +64,7 @@ export default define(meta, async (ps, user) => { })); })); })) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts index 6c416b1c04..b3dffa4272 100644 --- a/src/server/api/endpoints/notes/featured.ts +++ b/src/server/api/endpoints/notes/featured.ts @@ -49,7 +49,7 @@ export default define(meta, async (ps, user) => { .andWhere(`note.score > 0`) .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) .andWhere(`note.visibility = 'public'`) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 985760bd9d..64fc3cbf6c 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -79,7 +79,7 @@ export default define(meta, async (ps, user) => { ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index 69f2a7c107..19c4593f5b 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -127,7 +127,7 @@ export default define(meta, async (ps, user) => { qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id }) .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); })) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index 9e1c081966..546d3619f7 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -96,7 +96,7 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 83e890d9ad..56640ec1ab 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -63,7 +63,7 @@ export default define(meta, async (ps, user) => { .where(`:meId = ANY(note.mentions)`, { meId: user.id }) .orWhere(`:meId = ANY(note.visibleUserIds)`, { meId: user.id }); })) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts index f528197220..dcda213918 100644 --- a/src/server/api/endpoints/notes/renotes.ts +++ b/src/server/api/endpoints/notes/renotes.ts @@ -68,7 +68,7 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.renoteId = :renoteId`, { renoteId: note.id }) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 5ac663d77a..6f33e2f233 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -59,7 +59,7 @@ export const meta = { export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts index d878f379c3..47b41d9294 100644 --- a/src/server/api/endpoints/notes/search-by-tag.ts +++ b/src/server/api/endpoints/notes/search-by-tag.ts @@ -95,7 +95,7 @@ export const meta = { export default define(meta, async (ps, me) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index ae0e9242a6..230d2b0294 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -79,7 +79,7 @@ export default define(meta, async (ps, me) => { query .andWhere('note.text ILIKE :q', { q: `%${ps.query}%` }) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index a158bcd3f0..d025944cc2 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -120,7 +120,7 @@ export default define(meta, async (ps, user) => { .where('note.userId = :meId', { meId: user.id }); if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); })) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index 040f017fd5..9ffb38bddc 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -130,7 +130,7 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.userId IN (${ listQuery.getQuery() })`) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index 91d1d7f4dd..fc5998c378 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -131,7 +131,7 @@ export default define(meta, async (ps, me) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.userId = :userId', { userId: user.id }) - .leftJoinAndSelect('note.user', 'user') + .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') From b6d0d4eb99d7b93d3bfa47c37d74de26ac87567c Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 14:33:18 +0900 Subject: [PATCH 54/76] add note --- src/server/api/stream/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts index 4a87f61e7f..a94923484d 100644 --- a/src/server/api/stream/index.ts +++ b/src/server/api/stream/index.ts @@ -52,6 +52,7 @@ export default class Connection { this.onBroadcastMessage(type, body); }); + // TODO: reidsでイベントをもらうようにし、ポーリングはしないようにする if (this.user) { this.updateFollowing(); this.followingClock = setInterval(this.updateFollowing, 5000); From 8050352ad88798be222f735a3217367acaee277f Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 15:14:03 +0900 Subject: [PATCH 55/76] =?UTF-8?q?perf:=20=E5=90=84=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=83=AA=E3=83=BC=E3=83=9F=E3=83=B3=E3=82=B0=E6=8E=A5=E7=B6=9A?= =?UTF-8?q?=E3=81=94=E3=81=A8=E3=81=AB=E3=83=9D=E3=83=BC=E3=83=AA=E3=83=B3?= =?UTF-8?q?=E3=82=B0=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/api/endpoints/channels/follow.ts | 3 + src/server/api/endpoints/channels/unfollow.ts | 3 + src/server/api/endpoints/i/update.ts | 3 +- src/server/api/endpoints/mute/create.ts | 3 + src/server/api/endpoints/mute/delete.ts | 3 + src/server/api/stream/index.ts | 57 +++++++++++++------ src/services/blocking/create.ts | 12 +++- src/services/following/create.ts | 7 ++- src/services/following/delete.ts | 7 ++- src/services/following/requests/reject.ts | 7 ++- src/services/stream.ts | 5 ++ 11 files changed, 83 insertions(+), 27 deletions(-) diff --git a/src/server/api/endpoints/channels/follow.ts b/src/server/api/endpoints/channels/follow.ts index bf2f2bbb57..11c6e37ff7 100644 --- a/src/server/api/endpoints/channels/follow.ts +++ b/src/server/api/endpoints/channels/follow.ts @@ -4,6 +4,7 @@ import define from '../../define'; import { ApiError } from '../../error'; import { Channels, ChannelFollowings } from '../../../../models'; import { genId } from '../../../../misc/gen-id'; +import { publishUserEvent } from '../../../../services/stream'; export const meta = { tags: ['channels'], @@ -42,4 +43,6 @@ export default define(meta, async (ps, user) => { followerId: user.id, followeeId: channel.id, }); + + publishUserEvent(user.id, 'followChannel', channel); }); diff --git a/src/server/api/endpoints/channels/unfollow.ts b/src/server/api/endpoints/channels/unfollow.ts index 8cab5c36a6..3eb0f1519b 100644 --- a/src/server/api/endpoints/channels/unfollow.ts +++ b/src/server/api/endpoints/channels/unfollow.ts @@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Channels, ChannelFollowings } from '../../../../models'; +import { publishUserEvent } from '../../../../services/stream'; export const meta = { tags: ['channels'], @@ -39,4 +40,6 @@ export default define(meta, async (ps, user) => { followerId: user.id, followeeId: channel.id, }); + + publishUserEvent(user.id, 'unfollowChannel', channel); }); diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index a1faf8f1c2..92be2e9e6d 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -1,6 +1,6 @@ import $ from 'cafy'; import { ID } from '../../../../misc/cafy-id'; -import { publishMainStream } from '../../../../services/stream'; +import { publishMainStream, publishUserEvent } from '../../../../services/stream'; import acceptAllFollowRequests from '../../../../services/following/requests/accept-all'; import { publishToFollowers } from '../../../../services/i/update'; import define from '../../define'; @@ -317,6 +317,7 @@ export default define(meta, async (ps, user, token) => { // Publish meUpdated event publishMainStream(user.id, 'meUpdated', iObj); + publishUserEvent(user.id, 'updateUserProfile', await UserProfiles.findOne(user.id)); // 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認 if (user.isLocked && ps.isLocked === false) { diff --git a/src/server/api/endpoints/mute/create.ts b/src/server/api/endpoints/mute/create.ts index 437ad96107..ebfc6028ed 100644 --- a/src/server/api/endpoints/mute/create.ts +++ b/src/server/api/endpoints/mute/create.ts @@ -6,6 +6,7 @@ import { getUser } from '../../common/getters'; import { genId } from '../../../../misc/gen-id'; import { Mutings, NoteWatchings } from '../../../../models'; import { Muting } from '../../../../models/entities/muting'; +import { publishUserEvent } from '../../../../services/stream'; export const meta = { desc: { @@ -82,6 +83,8 @@ export default define(meta, async (ps, user) => { muteeId: mutee.id, } as Muting); + publishUserEvent(user.id, 'mute', mutee); + NoteWatchings.delete({ userId: muter.id, noteUserId: mutee.id diff --git a/src/server/api/endpoints/mute/delete.ts b/src/server/api/endpoints/mute/delete.ts index 217352acb4..67a59e3ae4 100644 --- a/src/server/api/endpoints/mute/delete.ts +++ b/src/server/api/endpoints/mute/delete.ts @@ -4,6 +4,7 @@ import define from '../../define'; import { ApiError } from '../../error'; import { getUser } from '../../common/getters'; import { Mutings } from '../../../../models'; +import { publishUserEvent } from '../../../../services/stream'; export const meta = { desc: { @@ -76,4 +77,6 @@ export default define(meta, async (ps, user) => { await Mutings.delete({ id: exist.id }); + + publishUserEvent(user.id, 'unmute', mutee); }); diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts index a94923484d..748e894f83 100644 --- a/src/server/api/stream/index.ts +++ b/src/server/api/stream/index.ts @@ -30,10 +30,6 @@ export default class Connection { public subscriber: EventEmitter; private channels: Channel[] = []; private subscribingNotes: any = {}; - private followingClock: ReturnType<typeof setInterval>; - private mutingClock: ReturnType<typeof setInterval>; - private followingChannelsClock: ReturnType<typeof setInterval>; - private userProfileClock: ReturnType<typeof setInterval>; constructor( wsConnection: websocket.connection, @@ -52,19 +48,51 @@ export default class Connection { this.onBroadcastMessage(type, body); }); - // TODO: reidsでイベントをもらうようにし、ポーリングはしないようにする if (this.user) { this.updateFollowing(); - this.followingClock = setInterval(this.updateFollowing, 5000); - this.updateMuting(); - this.mutingClock = setInterval(this.updateMuting, 5000); - this.updateFollowingChannels(); - this.followingChannelsClock = setInterval(this.updateFollowingChannels, 5000); - this.updateUserProfile(); - this.userProfileClock = setInterval(this.updateUserProfile, 5000); + + this.subscriber.on(`user:${this.user.id}`, ({ type, body }) => { + this.onUserEvent(type, body); + }); + } + } + + @autobind + private onUserEvent(type: string, body: any) { + switch (type) { + case 'follow': + this.following.add(body.id); + break; + + case 'unfollow': + this.following.delete(body.id); + break; + + case 'mute': + this.muting.add(body.id); + break; + + case 'unmute': + this.muting.delete(body.id); + break; + + case 'followChannel': + this.followingChannels.add(body.id); + break; + + case 'unfollowChannel': + this.followingChannels.delete(body.id); + break; + + case 'updateUserProfile': + this.userProfile = body; + break; + + default: + break; } } @@ -354,10 +382,5 @@ export default class Connection { for (const c of this.channels.filter(c => c.dispose)) { if (c.dispose) c.dispose(); } - - if (this.followingClock) clearInterval(this.followingClock); - if (this.mutingClock) clearInterval(this.mutingClock); - if (this.followingChannelsClock) clearInterval(this.followingChannelsClock); - if (this.userProfileClock) clearInterval(this.userProfileClock); } } diff --git a/src/services/blocking/create.ts b/src/services/blocking/create.ts index def4f33585..4f0238db91 100644 --- a/src/services/blocking/create.ts +++ b/src/services/blocking/create.ts @@ -1,4 +1,4 @@ -import { publishMainStream } from '../stream'; +import { publishMainStream, publishUserEvent } from '../stream'; import { renderActivity } from '../../remote/activitypub/renderer'; import renderFollow from '../../remote/activitypub/renderer/follow'; import renderUndo from '../../remote/activitypub/renderer/undo'; @@ -55,7 +55,10 @@ async function cancelRequest(follower: User, followee: User) { if (Users.isLocalUser(follower)) { Users.pack(followee, follower, { detail: true - }).then(packed => publishMainStream(follower.id, 'unfollow', packed)); + }).then(packed => { + publishUserEvent(follower.id, 'unfollow', packed); + publishMainStream(follower.id, 'unfollow', packed); + }); } // リモートにフォローリクエストをしていたらUndoFollow送信 @@ -97,7 +100,10 @@ async function unFollow(follower: User, followee: User) { if (Users.isLocalUser(follower)) { Users.pack(followee, follower, { detail: true - }).then(packed => publishMainStream(follower.id, 'unfollow', packed)); + }).then(packed => { + publishUserEvent(follower.id, 'unfollow', packed); + publishMainStream(follower.id, 'unfollow', packed); + }); } // リモートにフォローをしていたらUndoFollow送信 diff --git a/src/services/following/create.ts b/src/services/following/create.ts index 6bc98aee87..eb6699b0bf 100644 --- a/src/services/following/create.ts +++ b/src/services/following/create.ts @@ -1,4 +1,4 @@ -import { publishMainStream } from '../stream'; +import { publishMainStream, publishUserEvent } from '../stream'; import { renderActivity } from '../../remote/activitypub/renderer'; import renderFollow from '../../remote/activitypub/renderer/follow'; import renderAccept from '../../remote/activitypub/renderer/accept'; @@ -88,7 +88,10 @@ export async function insertFollowingDoc(followee: User, follower: User) { if (Users.isLocalUser(follower)) { Users.pack(followee, follower, { detail: true - }).then(packed => publishMainStream(follower.id, 'follow', packed)); + }).then(packed => { + publishUserEvent(follower.id, 'follow', packed); + publishMainStream(follower.id, 'follow', packed); + }); } // Publish followed event diff --git a/src/services/following/delete.ts b/src/services/following/delete.ts index 8821611515..32c47ea7f4 100644 --- a/src/services/following/delete.ts +++ b/src/services/following/delete.ts @@ -1,4 +1,4 @@ -import { publishMainStream } from '../stream'; +import { publishMainStream, publishUserEvent } from '../stream'; import { renderActivity } from '../../remote/activitypub/renderer'; import renderFollow from '../../remote/activitypub/renderer/follow'; import renderUndo from '../../remote/activitypub/renderer/undo'; @@ -30,7 +30,10 @@ export default async function(follower: User, followee: User, silent = false) { if (!silent && Users.isLocalUser(follower)) { Users.pack(followee, follower, { detail: true - }).then(packed => publishMainStream(follower.id, 'unfollow', packed)); + }).then(packed => { + publishUserEvent(follower.id, 'unfollow', packed); + publishMainStream(follower.id, 'unfollow', packed); + }); } if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) { diff --git a/src/services/following/requests/reject.ts b/src/services/following/requests/reject.ts index 9a8b14bbfd..d8d3788088 100644 --- a/src/services/following/requests/reject.ts +++ b/src/services/following/requests/reject.ts @@ -2,7 +2,7 @@ import { renderActivity } from '../../../remote/activitypub/renderer'; import renderFollow from '../../../remote/activitypub/renderer/follow'; import renderReject from '../../../remote/activitypub/renderer/reject'; import { deliver } from '../../../queue'; -import { publishMainStream } from '../../stream'; +import { publishMainStream, publishUserEvent } from '../../stream'; import { User, ILocalUser } from '../../../models/entities/user'; import { Users, FollowRequests, Followings } from '../../../models'; import { decrementFollowing } from '../delete'; @@ -39,5 +39,8 @@ export default async function(followee: User, follower: User) { Users.pack(followee, follower, { detail: true - }).then(packed => publishMainStream(follower.id, 'unfollow', packed)); + }).then(packed => { + publishUserEvent(follower.id, 'unfollow', packed); + publishMainStream(follower.id, 'unfollow', packed); + }); } diff --git a/src/services/stream.ts b/src/services/stream.ts index d833d700fe..75385847ce 100644 --- a/src/services/stream.ts +++ b/src/services/stream.ts @@ -20,6 +20,10 @@ class Publisher { })); } + public publishUserEvent = (userId: User['id'], type: string, value?: any): void => { + this.publish(`user:${userId}`, type, typeof value === 'undefined' ? null : value); + } + public publishBroadcastStream = (type: string, value?: any): void => { this.publish('broadcast', type, typeof value === 'undefined' ? null : value); } @@ -84,6 +88,7 @@ const publisher = new Publisher(); export default publisher; +export const publishUserEvent = publisher.publishUserEvent; export const publishBroadcastStream = publisher.publishBroadcastStream; export const publishMainStream = publisher.publishMainStream; export const publishDriveStream = publisher.publishDriveStream; From 630464f38d2524ddc5d11d2abd4fddcccc4240d4 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 15:35:02 +0900 Subject: [PATCH 56/76] Revert "perf: Reduce database query" This reverts commit 87c8f9ff953499340496e9c5db09c93eaff08851. --- src/client/components/note-detailed.vue | 10 +- src/client/components/note.vue | 10 +- src/client/ui/chat/note.vue | 10 +- src/server/api/endpoints/notes/mentions.ts | 6 +- src/server/api/stream/index.ts | 38 +++----- src/services/note/read-mention.ts | 29 ------ src/services/note/read-specified-note.ts | 29 ------ src/services/note/read.ts | 105 +++++++++++++++++++++ 8 files changed, 123 insertions(+), 114 deletions(-) delete mode 100644 src/services/note/read-mention.ts delete mode 100644 src/services/note/read-specified-note.ts create mode 100644 src/services/note/read.ts diff --git a/src/client/components/note-detailed.vue b/src/client/components/note-detailed.vue index 4ad3d2d898..1ef3f43389 100644 --- a/src/client/components/note-detailed.vue +++ b/src/client/components/note-detailed.vue @@ -350,15 +350,7 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send('sn', { id: this.appearNote.id }); - if (this.appearNote.userId !== this.$i.id) { - if (this.appearNote.mentions && this.appearNote.mentions.includes(this.$i.id)) { - this.connection.send('readMention', { id: this.appearNote.id }); - } - if (this.appearNote.visibleUserIds && this.appearNote.visibleUserIds.includes(this.$i.id)) { - this.connection.send('readSpecifiedNote', { id: this.appearNote.id }); - } - } + this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 3b59afd71d..65e09b7802 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -325,15 +325,7 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send('sn', { id: this.appearNote.id }); - if (this.appearNote.userId !== this.$i.id) { - if (this.appearNote.mentions && this.appearNote.mentions.includes(this.$i.id)) { - this.connection.send('readMention', { id: this.appearNote.id }); - } - if (this.appearNote.visibleUserIds && this.appearNote.visibleUserIds.includes(this.$i.id)) { - this.connection.send('readSpecifiedNote', { id: this.appearNote.id }); - } - } + this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/client/ui/chat/note.vue b/src/client/ui/chat/note.vue index 29bc61d9c5..5a4a13d889 100644 --- a/src/client/ui/chat/note.vue +++ b/src/client/ui/chat/note.vue @@ -325,15 +325,7 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send('sn', { id: this.appearNote.id }); - if (this.appearNote.userId !== this.$i.id) { - if (this.appearNote.mentions && this.appearNote.mentions.includes(this.$i.id)) { - this.connection.send('readMention', { id: this.appearNote.id }); - } - if (this.appearNote.visibleUserIds && this.appearNote.visibleUserIds.includes(this.$i.id)) { - this.connection.send('readSpecifiedNote', { id: this.appearNote.id }); - } - } + this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 56640ec1ab..30844774e0 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -1,12 +1,12 @@ import $ from 'cafy'; import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; +import read from '../../../../services/note/read'; import { Notes, Followings } from '../../../../models'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Brackets } from 'typeorm'; -import { readMention } from '../../../../services/note/read-mention'; export const meta = { desc: { @@ -83,7 +83,9 @@ export default define(meta, async (ps, user) => { const mentions = await query.take(ps.limit!).getMany(); - readMention(user.id, mentions.map(n => n.id)); + for (const note of mentions) { + read(user.id, note.id); + } return await Notes.packMany(mentions, user); }); diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts index 748e894f83..f67faee1ce 100644 --- a/src/server/api/stream/index.ts +++ b/src/server/api/stream/index.ts @@ -2,6 +2,7 @@ import autobind from 'autobind-decorator'; import * as websocket from 'websocket'; import { readNotification } from '../common/read-notification'; import call from '../call'; +import readNote from '../../../services/note/read'; import Channel from './channel'; import channels from './channels'; import { EventEmitter } from 'events'; @@ -13,8 +14,6 @@ import { AccessToken } from '../../../models/entities/access-token'; import { UserProfile } from '../../../models/entities/user-profile'; import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '../../../services/stream'; import { UserGroup } from '../../../models/entities/user-group'; -import { readMention } from '../../../services/note/read-mention'; -import { readSpecifiedNote } from '../../../services/note/read-specified-note'; /** * Main stream connection @@ -116,10 +115,9 @@ export default class Connection { switch (type) { case 'api': this.onApiRequest(body); break; case 'readNotification': this.onReadNotification(body); break; - case 'readMention': this.onReadMention(body); break; - case 'readSpecifiedNote': this.onReadSpecifiedNote(body); break; - case 'subNote': this.onSubscribeNote(body); break; - case 'sn': this.onSubscribeNote(body); break; // alias + case 'subNote': this.onSubscribeNote(body, true); break; + case 'sn': this.onSubscribeNote(body, true); break; // alias + case 's': this.onSubscribeNote(body, false); break; case 'unsubNote': this.onUnsubscribeNote(body); break; case 'un': this.onUnsubscribeNote(body); break; // alias case 'connect': this.onChannelConnectRequested(body); break; @@ -172,31 +170,11 @@ export default class Connection { readNotification(this.user!.id, [payload.id]); } - @autobind - private onReadMention(payload: any) { - if (!payload.id) return; - if (this.user) { - // TODO: ある程度まとめてreadMentionするようにする - // 具体的には、この箇所ではキュー的な配列にread予定ノートを溜めておくに留めて、別の箇所で定期的にキューにあるノートを配列でreadMentionに渡すような実装にする - readMention(this.user.id, [payload.id]); - } - } - - @autobind - private onReadSpecifiedNote(payload: any) { - if (!payload.id) return; - if (this.user) { - // TODO: ある程度まとめてreadSpecifiedNoteするようにする - // 具体的には、この箇所ではキュー的な配列にread予定ノートを溜めておくに留めて、別の箇所で定期的にキューにあるノートを配列でreadSpecifiedNoteに渡すような実装にする - readSpecifiedNote(this.user.id, [payload.id]); - } - } - /** * 投稿購読要求時 */ @autobind - private onSubscribeNote(payload: any) { + private onSubscribeNote(payload: any, read: boolean) { if (!payload.id) return; if (this.subscribingNotes[payload.id] == null) { @@ -208,6 +186,12 @@ export default class Connection { if (this.subscribingNotes[payload.id] === 1) { this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage); } + + if (this.user && read) { + // TODO: クライアントでタイムライン読み込みなどすると、一度に大量のreadNoteが発生しクエリ数がすごいことになるので、ある程度まとめてreadNoteするようにする + // 具体的には、この箇所ではキュー的な配列にread予定ノートを溜めておくに留めて、別の箇所で定期的にキューにあるノートを配列でreadNoteに渡すような実装にする + readNote(this.user.id, payload.id); + } } /** diff --git a/src/services/note/read-mention.ts b/src/services/note/read-mention.ts deleted file mode 100644 index 2a668ecd6c..0000000000 --- a/src/services/note/read-mention.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { publishMainStream } from '../stream'; -import { Note } from '../../models/entities/note'; -import { User } from '../../models/entities/user'; -import { NoteUnreads } from '../../models'; -import { In } from 'typeorm'; - -/** - * Mark a mention note as read - */ -export async function readMention( - userId: User['id'], - noteIds: Note['id'][] -) { - // Remove the records - await NoteUnreads.delete({ - userId: userId, - noteId: In(noteIds), - }); - - const mentionsCount = await NoteUnreads.count({ - userId: userId, - isMentioned: true - }); - - if (mentionsCount === 0) { - // 全て既読になったイベントを発行 - publishMainStream(userId, 'readAllUnreadMentions'); - } -} diff --git a/src/services/note/read-specified-note.ts b/src/services/note/read-specified-note.ts deleted file mode 100644 index 0fcb66bf98..0000000000 --- a/src/services/note/read-specified-note.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { publishMainStream } from '../stream'; -import { Note } from '../../models/entities/note'; -import { User } from '../../models/entities/user'; -import { NoteUnreads } from '../../models'; -import { In } from 'typeorm'; - -/** - * Mark a specified note as read - */ -export async function readSpecifiedNote( - userId: User['id'], - noteIds: Note['id'][] -) { - // Remove the records - await NoteUnreads.delete({ - userId: userId, - noteId: In(noteIds), - }); - - const specifiedCount = await NoteUnreads.count({ - userId: userId, - isSpecified: true - }); - - if (specifiedCount === 0) { - // 全て既読になったイベントを発行 - publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); - } -} diff --git a/src/services/note/read.ts b/src/services/note/read.ts new file mode 100644 index 0000000000..5a39ab30b7 --- /dev/null +++ b/src/services/note/read.ts @@ -0,0 +1,105 @@ +import { publishMainStream } from '../stream'; +import { Note } from '../../models/entities/note'; +import { User } from '../../models/entities/user'; +import { NoteUnreads, Antennas, AntennaNotes, Users } from '../../models'; +import { Not, IsNull } from 'typeorm'; + +/** + * Mark a note as read + */ +export default async function( + userId: User['id'], + noteId: Note['id'] +) { + async function careNoteUnreads() { + const exist = await NoteUnreads.findOne({ + userId: userId, + noteId: noteId, + }); + + if (!exist) return; + + // Remove the record + await NoteUnreads.delete({ + userId: userId, + noteId: noteId, + }); + + if (exist.isMentioned) { + NoteUnreads.count({ + userId: userId, + isMentioned: true + }).then(mentionsCount => { + if (mentionsCount === 0) { + // 全て既読になったイベントを発行 + publishMainStream(userId, 'readAllUnreadMentions'); + } + }); + } + + if (exist.isSpecified) { + NoteUnreads.count({ + userId: userId, + isSpecified: true + }).then(specifiedCount => { + if (specifiedCount === 0) { + // 全て既読になったイベントを発行 + publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); + } + }); + } + + if (exist.noteChannelId) { + NoteUnreads.count({ + userId: userId, + noteChannelId: Not(IsNull()) + }).then(channelNoteCount => { + if (channelNoteCount === 0) { + // 全て既読になったイベントを発行 + publishMainStream(userId, 'readAllChannels'); + } + }); + } + } + + async function careAntenna() { + const beforeUnread = await Users.getHasUnreadAntenna(userId); + if (!beforeUnread) return; + + const antennas = await Antennas.find({ userId }); + + await Promise.all(antennas.map(async antenna => { + const countBefore = await AntennaNotes.count({ + antennaId: antenna.id, + read: false + }); + + if (countBefore === 0) return; + + await AntennaNotes.update({ + antennaId: antenna.id, + noteId: noteId + }, { + read: true + }); + + const countAfter = await AntennaNotes.count({ + antennaId: antenna.id, + read: false + }); + + if (countAfter === 0) { + publishMainStream(userId, 'readAntenna', antenna); + } + })); + + Users.getHasUnreadAntenna(userId).then(unread => { + if (!unread) { + publishMainStream(userId, 'readAllAntennas'); + } + }); + } + + careNoteUnreads(); + careAntenna(); +} From 667d58bad4544d6e9dc75cfc4e6216179e2bc1aa Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 17:38:09 +0900 Subject: [PATCH 57/76] better note read handling --- src/client/components/note-detailed.vue | 3 +- src/client/components/note.vue | 3 +- src/client/ui/chat/note.vue | 3 +- src/daemons/janitor.ts | 2 + src/server/api/endpoints/notes/mentions.ts | 4 +- src/server/api/stream/channels/antenna.ts | 2 + src/server/api/stream/channels/channel.ts | 2 + .../api/stream/channels/global-timeline.ts | 2 + src/server/api/stream/channels/hashtag.ts | 2 + .../api/stream/channels/home-timeline.ts | 2 + .../api/stream/channels/hybrid-timeline.ts | 2 + .../api/stream/channels/local-timeline.ts | 2 + src/server/api/stream/channels/main.ts | 8 +- src/server/api/stream/index.ts | 58 +++++++++++--- src/services/note/read.ts | 80 ++++++++----------- 15 files changed, 109 insertions(+), 66 deletions(-) diff --git a/src/client/components/note-detailed.vue b/src/client/components/note-detailed.vue index 1ef3f43389..ea26d31100 100644 --- a/src/client/components/note-detailed.vue +++ b/src/client/components/note-detailed.vue @@ -350,7 +350,8 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); + // TODO: このノートがストリーミング経由で流れてきた場合のみ sr する + this.connection.send(document.body.contains(this.$el) ? 'sr' : 's', { id: this.appearNote.id }); if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 65e09b7802..70f49fef7e 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -325,7 +325,8 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); + // TODO: このノートがストリーミング経由で流れてきた場合のみ sr する + this.connection.send(document.body.contains(this.$el) ? 'sr' : 's', { id: this.appearNote.id }); if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/client/ui/chat/note.vue b/src/client/ui/chat/note.vue index 5a4a13d889..97275875ca 100644 --- a/src/client/ui/chat/note.vue +++ b/src/client/ui/chat/note.vue @@ -325,7 +325,8 @@ export default defineComponent({ capture(withHandler = false) { if (this.$i) { - this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id }); + // TODO: このノートがストリーミング経由で流れてきた場合のみ sr する + this.connection.send(document.body.contains(this.$el) ? 'sr' : 's', { id: this.appearNote.id }); if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); } }, diff --git a/src/daemons/janitor.ts b/src/daemons/janitor.ts index 462ebf915c..c079086427 100644 --- a/src/daemons/janitor.ts +++ b/src/daemons/janitor.ts @@ -1,3 +1,5 @@ +// TODO: 消したい + const interval = 30 * 60 * 1000; import { AttestationChallenges } from '../models'; import { LessThan } from 'typeorm'; diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 30844774e0..30368ea578 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -83,9 +83,7 @@ export default define(meta, async (ps, user) => { const mentions = await query.take(ps.limit!).getMany(); - for (const note of mentions) { - read(user.id, note.id); - } + read(user.id, mentions.map(note => note.id)); return await Notes.packMany(mentions, user); }); diff --git a/src/server/api/stream/channels/antenna.ts b/src/server/api/stream/channels/antenna.ts index b5a792f814..36a474f2ac 100644 --- a/src/server/api/stream/channels/antenna.ts +++ b/src/server/api/stream/channels/antenna.ts @@ -27,6 +27,8 @@ export default class extends Channel { // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isMutedUserRelated(note, this.muting)) return; + this.connection.cacheNote(note); + this.send('note', note); } else { this.send(type, body); diff --git a/src/server/api/stream/channels/channel.ts b/src/server/api/stream/channels/channel.ts index aa570d1ef4..47a52465b2 100644 --- a/src/server/api/stream/channels/channel.ts +++ b/src/server/api/stream/channels/channel.ts @@ -43,6 +43,8 @@ export default class extends Channel { // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isMutedUserRelated(note, this.muting)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/global-timeline.ts b/src/server/api/stream/channels/global-timeline.ts index 8c97e67226..8353f45323 100644 --- a/src/server/api/stream/channels/global-timeline.ts +++ b/src/server/api/stream/channels/global-timeline.ts @@ -56,6 +56,8 @@ export default class extends Channel { // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/hashtag.ts b/src/server/api/stream/channels/hashtag.ts index 41447039d5..1b7f8efcc1 100644 --- a/src/server/api/stream/channels/hashtag.ts +++ b/src/server/api/stream/channels/hashtag.ts @@ -37,6 +37,8 @@ export default class extends Channel { // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isMutedUserRelated(note, this.muting)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts index 6cfa6eae7b..59ba31c316 100644 --- a/src/server/api/stream/channels/home-timeline.ts +++ b/src/server/api/stream/channels/home-timeline.ts @@ -64,6 +64,8 @@ export default class extends Channel { // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/hybrid-timeline.ts b/src/server/api/stream/channels/hybrid-timeline.ts index a9e577cacb..9715e9973f 100644 --- a/src/server/api/stream/channels/hybrid-timeline.ts +++ b/src/server/api/stream/channels/hybrid-timeline.ts @@ -73,6 +73,8 @@ export default class extends Channel { // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/local-timeline.ts b/src/server/api/stream/channels/local-timeline.ts index a3a5e491fc..e159c72d60 100644 --- a/src/server/api/stream/channels/local-timeline.ts +++ b/src/server/api/stream/channels/local-timeline.ts @@ -58,6 +58,8 @@ export default class extends Channel { // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + this.connection.cacheNote(note); + this.send('note', note); } diff --git a/src/server/api/stream/channels/main.ts b/src/server/api/stream/channels/main.ts index b69c2ec355..780bc0b89f 100644 --- a/src/server/api/stream/channels/main.ts +++ b/src/server/api/stream/channels/main.ts @@ -18,18 +18,22 @@ export default class extends Channel { case 'notification': { if (this.muting.has(body.userId)) return; if (body.note && body.note.isHidden) { - body.note = await Notes.pack(body.note.id, this.user, { + const note = await Notes.pack(body.note.id, this.user, { detail: true }); + this.connection.cacheNote(note); + body.note = note; } break; } case 'mention': { if (this.muting.has(body.userId)) return; if (body.isHidden) { - body = await Notes.pack(body.id, this.user, { + const note = await Notes.pack(body.id, this.user, { detail: true }); + this.connection.cacheNote(note); + body = note; } break; } diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts index f67faee1ce..99ae558696 100644 --- a/src/server/api/stream/index.ts +++ b/src/server/api/stream/index.ts @@ -14,6 +14,7 @@ import { AccessToken } from '../../../models/entities/access-token'; import { UserProfile } from '../../../models/entities/user-profile'; import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '../../../services/stream'; import { UserGroup } from '../../../models/entities/user-group'; +import { PackedNote } from '../../../models/repositories/note'; /** * Main stream connection @@ -29,6 +30,7 @@ export default class Connection { public subscriber: EventEmitter; private channels: Channel[] = []; private subscribingNotes: any = {}; + private cachedNotes: PackedNote[] = []; constructor( wsConnection: websocket.connection, @@ -115,9 +117,9 @@ export default class Connection { switch (type) { case 'api': this.onApiRequest(body); break; case 'readNotification': this.onReadNotification(body); break; - case 'subNote': this.onSubscribeNote(body, true); break; - case 'sn': this.onSubscribeNote(body, true); break; // alias - case 's': this.onSubscribeNote(body, false); break; + case 'subNote': this.onSubscribeNote(body); break; + case 's': this.onSubscribeNote(body); break; // alias + case 'sr': this.onSubscribeNote(body); this.readNote(body); break; case 'unsubNote': this.onUnsubscribeNote(body); break; case 'un': this.onUnsubscribeNote(body); break; // alias case 'connect': this.onChannelConnectRequested(body); break; @@ -138,6 +140,48 @@ export default class Connection { this.sendMessageToWs(type, body); } + @autobind + public cacheNote(note: PackedNote) { + const add = (note: PackedNote) => { + const existIndex = this.cachedNotes.findIndex(n => n.id === note.id); + if (existIndex > -1) { + this.cachedNotes[existIndex] = note; + return; + } + + this.cachedNotes.unshift(note); + if (this.cachedNotes.length > 32) { + this.cachedNotes.splice(32); + } + }; + + add(note); + if (note.reply) add(note.reply); + if (note.renote) add(note.renote); + } + + @autobind + private readNote(body: any) { + const id = body.id; + + const note = this.cachedNotes.find(n => n.id === id); + if (note == null) return; + + if (this.user && (note.userId !== this.user.id)) { + if (note.mentions && note.mentions.includes(this.user.id)) { + readNote(this.user.id, [note]); + } else if (note.visibleUserIds && note.visibleUserIds.includes(this.user.id)) { + readNote(this.user.id, [note]); + } + + if (this.followingChannels.has(note.channelId)) { + // TODO + } + + // TODO: アンテナの既読処理 + } + } + /** * APIリクエスト要求時 */ @@ -174,7 +218,7 @@ export default class Connection { * 投稿購読要求時 */ @autobind - private onSubscribeNote(payload: any, read: boolean) { + private onSubscribeNote(payload: any) { if (!payload.id) return; if (this.subscribingNotes[payload.id] == null) { @@ -186,12 +230,6 @@ export default class Connection { if (this.subscribingNotes[payload.id] === 1) { this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage); } - - if (this.user && read) { - // TODO: クライアントでタイムライン読み込みなどすると、一度に大量のreadNoteが発生しクエリ数がすごいことになるので、ある程度まとめてreadNoteするようにする - // 具体的には、この箇所ではキュー的な配列にread予定ノートを溜めておくに留めて、別の箇所で定期的にキューにあるノートを配列でreadNoteに渡すような実装にする - readNote(this.user.id, payload.id); - } } /** diff --git a/src/services/note/read.ts b/src/services/note/read.ts index 5a39ab30b7..35279db411 100644 --- a/src/services/note/read.ts +++ b/src/services/note/read.ts @@ -2,70 +2,54 @@ import { publishMainStream } from '../stream'; import { Note } from '../../models/entities/note'; import { User } from '../../models/entities/user'; import { NoteUnreads, Antennas, AntennaNotes, Users } from '../../models'; -import { Not, IsNull } from 'typeorm'; +import { Not, IsNull, In } from 'typeorm'; /** - * Mark a note as read + * Mark notes as read */ export default async function( userId: User['id'], - noteId: Note['id'] + noteIds: Note['id'][] ) { async function careNoteUnreads() { - const exist = await NoteUnreads.findOne({ - userId: userId, - noteId: noteId, - }); - - if (!exist) return; - // Remove the record await NoteUnreads.delete({ userId: userId, - noteId: noteId, + noteId: In(noteIds), }); - if (exist.isMentioned) { - NoteUnreads.count({ - userId: userId, - isMentioned: true - }).then(mentionsCount => { - if (mentionsCount === 0) { - // 全て既読になったイベントを発行 - publishMainStream(userId, 'readAllUnreadMentions'); - } - }); - } + NoteUnreads.count({ + userId: userId, + isMentioned: true + }).then(mentionsCount => { + if (mentionsCount === 0) { + // 全て既読になったイベントを発行 + publishMainStream(userId, 'readAllUnreadMentions'); + } + }); - if (exist.isSpecified) { - NoteUnreads.count({ - userId: userId, - isSpecified: true - }).then(specifiedCount => { - if (specifiedCount === 0) { - // 全て既読になったイベントを発行 - publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); - } - }); - } + NoteUnreads.count({ + userId: userId, + isSpecified: true + }).then(specifiedCount => { + if (specifiedCount === 0) { + // 全て既読になったイベントを発行 + publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); + } + }); - if (exist.noteChannelId) { - NoteUnreads.count({ - userId: userId, - noteChannelId: Not(IsNull()) - }).then(channelNoteCount => { - if (channelNoteCount === 0) { - // 全て既読になったイベントを発行 - publishMainStream(userId, 'readAllChannels'); - } - }); - } + NoteUnreads.count({ + userId: userId, + noteChannelId: Not(IsNull()) + }).then(channelNoteCount => { + if (channelNoteCount === 0) { + // 全て既読になったイベントを発行 + publishMainStream(userId, 'readAllChannels'); + } + }); } async function careAntenna() { - const beforeUnread = await Users.getHasUnreadAntenna(userId); - if (!beforeUnread) return; - const antennas = await Antennas.find({ userId }); await Promise.all(antennas.map(async antenna => { @@ -78,7 +62,7 @@ export default async function( await AntennaNotes.update({ antennaId: antenna.id, - noteId: noteId + noteId: In(noteIds) }, { read: true }); From 41b491fa7c04900667672cc1832a512568567c55 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 21:00:59 +0900 Subject: [PATCH 58/76] refactor: Use Set --- src/remote/activitypub/deliver-manager.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/remote/activitypub/deliver-manager.ts b/src/remote/activitypub/deliver-manager.ts index d147b3c9b0..92721f5525 100644 --- a/src/remote/activitypub/deliver-manager.ts +++ b/src/remote/activitypub/deliver-manager.ts @@ -76,7 +76,7 @@ export default class DeliverManager { public async execute() { if (!Users.isLocalUser(this.actor)) return; - const inboxes: string[] = []; + const inboxes = new Set<string>(); // build inbox list for (const recipe of this.recipes) { @@ -89,13 +89,13 @@ export default class DeliverManager { for (const following of followers) { if (Followings.isRemoteFollower(following)) { const inbox = following.followerSharedInbox || following.followerInbox; - if (!inboxes.includes(inbox)) inboxes.push(inbox); + inboxes.add(inbox); } } } else if (isDirect(recipe)) { // direct deliver const inbox = recipe.to.inbox; - if (inbox && !inboxes.includes(inbox)) inboxes.push(inbox); + if (inbox) inboxes.add(inbox); } } From c4c20bee7c58ea7330dbc890b9564bd100ac6e25 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 21:27:09 +0900 Subject: [PATCH 59/76] wip #6441 --- src/models/entities/note-reaction.ts | 4 ++-- src/server/api/endpoints/admin/invite.ts | 2 +- src/server/api/endpoints/admin/promo/create.ts | 2 +- src/server/api/endpoints/auth/accept.ts | 2 +- src/server/api/endpoints/channels/follow.ts | 2 +- src/server/api/endpoints/clips/add-note.ts | 2 +- src/server/api/endpoints/i/read-announcement.ts | 2 +- src/server/api/endpoints/miauth/gen-token.ts | 2 +- .../api/endpoints/notes/favorites/create.ts | 2 +- src/server/api/endpoints/pages/like.ts | 2 +- src/server/api/endpoints/promo/read.ts | 2 +- src/server/api/endpoints/sw/register.ts | 2 +- src/server/api/endpoints/users/groups/create.ts | 2 +- .../endpoints/users/groups/invitations/accept.ts | 2 +- src/server/api/private/signin.ts | 4 ++-- src/services/add-note-to-antenna.ts | 2 +- src/services/blocking/create.ts | 2 +- src/services/following/create.ts | 2 +- src/services/i/pin.ts | 2 +- src/services/insert-moderation-log.ts | 2 +- src/services/messages/create.ts | 6 ++++-- src/services/note/create.ts | 2 +- src/services/note/polls/vote.ts | 2 +- src/services/note/reaction/create.ts | 16 ++++++++-------- src/services/note/unread.ts | 6 ++++-- src/services/note/watch.ts | 2 +- src/services/update-hashtag.ts | 4 ++-- src/services/user-list/push.ts | 2 +- 28 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/models/entities/note-reaction.ts b/src/models/entities/note-reaction.ts index 69bb663fd3..674dc3639e 100644 --- a/src/models/entities/note-reaction.ts +++ b/src/models/entities/note-reaction.ts @@ -23,7 +23,7 @@ export class NoteReaction { onDelete: 'CASCADE' }) @JoinColumn() - public user: User | null; + public user?: User | null; @Index() @Column(id()) @@ -33,7 +33,7 @@ export class NoteReaction { onDelete: 'CASCADE' }) @JoinColumn() - public note: Note | null; + public note?: Note | null; // TODO: 対象noteのuserIdを非正規化したい(「受け取ったリアクション一覧」のようなものを(JOIN無しで)実装したいため) diff --git a/src/server/api/endpoints/admin/invite.ts b/src/server/api/endpoints/admin/invite.ts index 4529d16adf..987105791f 100644 --- a/src/server/api/endpoints/admin/invite.ts +++ b/src/server/api/endpoints/admin/invite.ts @@ -38,7 +38,7 @@ export default define(meta, async () => { chars: '2-9A-HJ-NP-Z', // [0-9A-Z] w/o [01IO] (32 patterns) }); - await RegistrationTickets.save({ + await RegistrationTickets.insert({ id: genId(), createdAt: new Date(), code, diff --git a/src/server/api/endpoints/admin/promo/create.ts b/src/server/api/endpoints/admin/promo/create.ts index 8b96d563c2..aa22e68ebd 100644 --- a/src/server/api/endpoints/admin/promo/create.ts +++ b/src/server/api/endpoints/admin/promo/create.ts @@ -53,7 +53,7 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.alreadyPromoted); } - await PromoNotes.save({ + await PromoNotes.insert({ noteId: note.id, createdAt: new Date(), expiresAt: new Date(ps.expiresAt), diff --git a/src/server/api/endpoints/auth/accept.ts b/src/server/api/endpoints/auth/accept.ts index 6d4d31fa1e..444053a946 100644 --- a/src/server/api/endpoints/auth/accept.ts +++ b/src/server/api/endpoints/auth/accept.ts @@ -58,7 +58,7 @@ export default define(meta, async (ps, user) => { const now = new Date(); // Insert access token doc - await AccessTokens.save({ + await AccessTokens.insert({ id: genId(), createdAt: now, lastUsedAt: now, diff --git a/src/server/api/endpoints/channels/follow.ts b/src/server/api/endpoints/channels/follow.ts index 11c6e37ff7..c5976a8a34 100644 --- a/src/server/api/endpoints/channels/follow.ts +++ b/src/server/api/endpoints/channels/follow.ts @@ -37,7 +37,7 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.noSuchChannel); } - await ChannelFollowings.save({ + await ChannelFollowings.insert({ id: genId(), createdAt: new Date(), followerId: user.id, diff --git a/src/server/api/endpoints/clips/add-note.ts b/src/server/api/endpoints/clips/add-note.ts index 4f5cc649e3..ee6a117b2d 100644 --- a/src/server/api/endpoints/clips/add-note.ts +++ b/src/server/api/endpoints/clips/add-note.ts @@ -68,7 +68,7 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.alreadyClipped); } - await ClipNotes.save({ + await ClipNotes.insert({ id: genId(), noteId: note.id, clipId: clip.id diff --git a/src/server/api/endpoints/i/read-announcement.ts b/src/server/api/endpoints/i/read-announcement.ts index 4a4a021af9..d6acb3d2e6 100644 --- a/src/server/api/endpoints/i/read-announcement.ts +++ b/src/server/api/endpoints/i/read-announcement.ts @@ -52,7 +52,7 @@ export default define(meta, async (ps, user) => { } // Create read - await AnnouncementReads.save({ + await AnnouncementReads.insert({ id: genId(), createdAt: new Date(), announcementId: ps.announcementId, diff --git a/src/server/api/endpoints/miauth/gen-token.ts b/src/server/api/endpoints/miauth/gen-token.ts index 0634debb1e..401ed16389 100644 --- a/src/server/api/endpoints/miauth/gen-token.ts +++ b/src/server/api/endpoints/miauth/gen-token.ts @@ -52,7 +52,7 @@ export default define(meta, async (ps, user) => { const now = new Date(); // Insert access token doc - await AccessTokens.save({ + await AccessTokens.insert({ id: genId(), createdAt: now, lastUsedAt: now, diff --git a/src/server/api/endpoints/notes/favorites/create.ts b/src/server/api/endpoints/notes/favorites/create.ts index 952bbfd0eb..d66ce37a46 100644 --- a/src/server/api/endpoints/notes/favorites/create.ts +++ b/src/server/api/endpoints/notes/favorites/create.ts @@ -61,7 +61,7 @@ export default define(meta, async (ps, user) => { } // Create favorite - await NoteFavorites.save({ + await NoteFavorites.insert({ id: genId(), createdAt: new Date(), noteId: note.id, diff --git a/src/server/api/endpoints/pages/like.ts b/src/server/api/endpoints/pages/like.ts index 5c7e13f1c8..3fc2b6ca23 100644 --- a/src/server/api/endpoints/pages/like.ts +++ b/src/server/api/endpoints/pages/like.ts @@ -68,7 +68,7 @@ export default define(meta, async (ps, user) => { } // Create like - await PageLikes.save({ + await PageLikes.insert({ id: genId(), createdAt: new Date(), pageId: page.id, diff --git a/src/server/api/endpoints/promo/read.ts b/src/server/api/endpoints/promo/read.ts index 57eb0681e5..63c90e5d7f 100644 --- a/src/server/api/endpoints/promo/read.ts +++ b/src/server/api/endpoints/promo/read.ts @@ -46,7 +46,7 @@ export default define(meta, async (ps, user) => { return; } - await PromoReads.save({ + await PromoReads.insert({ id: genId(), createdAt: new Date(), noteId: note.id, diff --git a/src/server/api/endpoints/sw/register.ts b/src/server/api/endpoints/sw/register.ts index ceb70a9274..9fc70b5609 100644 --- a/src/server/api/endpoints/sw/register.ts +++ b/src/server/api/endpoints/sw/register.ts @@ -58,7 +58,7 @@ export default define(meta, async (ps, user) => { }; } - await SwSubscriptions.save({ + await SwSubscriptions.insert({ id: genId(), createdAt: new Date(), userId: user.id, diff --git a/src/server/api/endpoints/users/groups/create.ts b/src/server/api/endpoints/users/groups/create.ts index ca011d5cd6..78d2714874 100644 --- a/src/server/api/endpoints/users/groups/create.ts +++ b/src/server/api/endpoints/users/groups/create.ts @@ -39,7 +39,7 @@ export default define(meta, async (ps, user) => { } as UserGroup); // Push the owner - await UserGroupJoinings.save({ + await UserGroupJoinings.insert({ id: genId(), createdAt: new Date(), userId: user.id, diff --git a/src/server/api/endpoints/users/groups/invitations/accept.ts b/src/server/api/endpoints/users/groups/invitations/accept.ts index e86709f83b..2fa22bcf7e 100644 --- a/src/server/api/endpoints/users/groups/invitations/accept.ts +++ b/src/server/api/endpoints/users/groups/invitations/accept.ts @@ -52,7 +52,7 @@ export default define(meta, async (ps, user) => { } // Push the user - await UserGroupJoinings.save({ + await UserGroupJoinings.insert({ id: genId(), createdAt: new Date(), userId: user.id, diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts index 7a5efc6cc9..d8f2e6d516 100644 --- a/src/server/api/private/signin.ts +++ b/src/server/api/private/signin.ts @@ -53,7 +53,7 @@ export default async (ctx: Koa.Context) => { async function fail(status?: number, failure?: { error: string }) { // Append signin history - await Signins.save({ + await Signins.insert({ id: genId(), createdAt: new Date(), userId: user.id, @@ -198,7 +198,7 @@ export default async (ctx: Koa.Context) => { const challengeId = genId(); - await AttestationChallenges.save({ + await AttestationChallenges.insert({ userId: user.id, id: challengeId, challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), diff --git a/src/services/add-note-to-antenna.ts b/src/services/add-note-to-antenna.ts index 2c893488c3..3ba3d1eef5 100644 --- a/src/services/add-note-to-antenna.ts +++ b/src/services/add-note-to-antenna.ts @@ -10,7 +10,7 @@ export async function addNoteToAntenna(antenna: Antenna, note: Note, noteUser: U // 通知しない設定になっているか、自分自身の投稿なら既読にする const read = !antenna.notify || (antenna.userId === noteUser.id); - AntennaNotes.save({ + AntennaNotes.insert({ id: genId(), antennaId: antenna.id, noteId: note.id, diff --git a/src/services/blocking/create.ts b/src/services/blocking/create.ts index 4f0238db91..dec48d26de 100644 --- a/src/services/blocking/create.ts +++ b/src/services/blocking/create.ts @@ -18,7 +18,7 @@ export default async function(blocker: User, blockee: User) { unFollow(blockee, blocker) ]); - await Blockings.save({ + await Blockings.insert({ id: genId(), createdAt: new Date(), blockerId: blocker.id, diff --git a/src/services/following/create.ts b/src/services/following/create.ts index eb6699b0bf..1ce75caca0 100644 --- a/src/services/following/create.ts +++ b/src/services/following/create.ts @@ -22,7 +22,7 @@ export async function insertFollowingDoc(followee: User, follower: User) { let alreadyFollowed = false; - await Followings.save({ + await Followings.insert({ id: genId(), createdAt: new Date(), followerId: follower.id, diff --git a/src/services/i/pin.ts b/src/services/i/pin.ts index 1ff5476b40..f727a10fb6 100644 --- a/src/services/i/pin.ts +++ b/src/services/i/pin.ts @@ -37,7 +37,7 @@ export async function addPinned(user: User, noteId: Note['id']) { throw new IdentifiableError('23f0cf4e-59a3-4276-a91d-61a5891c1514', 'That note has already been pinned.'); } - await UserNotePinings.save({ + await UserNotePinings.insert({ id: genId(), createdAt: new Date(), userId: user.id, diff --git a/src/services/insert-moderation-log.ts b/src/services/insert-moderation-log.ts index 33dab97259..87587d3bed 100644 --- a/src/services/insert-moderation-log.ts +++ b/src/services/insert-moderation-log.ts @@ -3,7 +3,7 @@ import { ModerationLogs } from '../models'; import { genId } from '../misc/gen-id'; export async function insertModerationLog(moderator: ILocalUser, type: string, info?: Record<string, any>) { - await ModerationLogs.save({ + await ModerationLogs.insert({ id: genId(), createdAt: new Date(), userId: moderator.id, diff --git a/src/services/messages/create.ts b/src/services/messages/create.ts index 8646ce37fc..413266d029 100644 --- a/src/services/messages/create.ts +++ b/src/services/messages/create.ts @@ -14,7 +14,7 @@ import { renderActivity } from '../../remote/activitypub/renderer'; import { deliver } from '../../queue'; export async function createMessage(user: User, recipientUser: User | undefined, recipientGroup: UserGroup | undefined, text: string | undefined, file: DriveFile | null, uri?: string) { - const message = await MessagingMessages.save({ + const message = { id: genId(), createdAt: new Date(), fileId: file ? file.id : null, @@ -25,7 +25,9 @@ export async function createMessage(user: User, recipientUser: User | undefined, isRead: false, reads: [] as any[], uri - } as MessagingMessage); + } as MessagingMessage; + + await MessagingMessages.insert(message); const messageObj = await MessagingMessages.pack(message); diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 563eaac758..7c7e8d9a08 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -247,7 +247,7 @@ export default async (user: User, data: Option, silent = false) => new Promise<N for (const u of us) { checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => { if (shouldMute) { - MutedNotes.save({ + MutedNotes.insert({ id: genId(), userId: u.userId, noteId: note.id, diff --git a/src/services/note/polls/vote.ts b/src/services/note/polls/vote.ts index bfcaaa09be..b4ce03ab60 100644 --- a/src/services/note/polls/vote.ts +++ b/src/services/note/polls/vote.ts @@ -29,7 +29,7 @@ export default async function(user: User, note: Note, choice: number) { } // Create vote - await PollVotes.save({ + await PollVotes.insert({ id: genId(), createdAt: new Date(), noteId: note.id, diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts index 6c0a852f34..897c816de8 100644 --- a/src/services/note/reaction/create.ts +++ b/src/services/note/reaction/create.ts @@ -18,17 +18,17 @@ export default async (user: User, note: Note, reaction?: string) => { // TODO: cache reaction = await toDbReaction(reaction, user.host); - let record: NoteReaction; + let record: NoteReaction = { + id: genId(), + createdAt: new Date(), + noteId: note.id, + userId: user.id, + reaction + }; // Create reaction try { - record = await NoteReactions.save({ - id: genId(), - createdAt: new Date(), - noteId: note.id, - userId: user.id, - reaction - }); + await NoteReactions.insert(record); } catch (e) { if (isDuplicateKeyValueError(e)) { record = await NoteReactions.findOneOrFail({ diff --git a/src/services/note/unread.ts b/src/services/note/unread.ts index 6fd9ee2cfe..8e6fb4abe8 100644 --- a/src/services/note/unread.ts +++ b/src/services/note/unread.ts @@ -17,7 +17,7 @@ export default async function(userId: User['id'], note: Note, params: { if (mute.map(m => m.muteeId).includes(note.userId)) return; //#endregion - const unread = await NoteUnreads.save({ + const unread = { id: genId(), noteId: note.id, userId: userId, @@ -25,7 +25,9 @@ export default async function(userId: User['id'], note: Note, params: { isMentioned: params.isMentioned, noteChannelId: note.channelId, noteUserId: note.userId, - }); + }; + + await NoteUnreads.insert(unread); // 2秒経っても既読にならなかったら「未読の投稿がありますよ」イベントを発行する setTimeout(async () => { diff --git a/src/services/note/watch.ts b/src/services/note/watch.ts index d3c9553696..966b7f0054 100644 --- a/src/services/note/watch.ts +++ b/src/services/note/watch.ts @@ -10,7 +10,7 @@ export default async (me: User['id'], note: Note) => { return; } - await NoteWatchings.save({ + await NoteWatchings.insert({ id: genId(), createdAt: new Date(), noteId: note.id, diff --git a/src/services/update-hashtag.ts b/src/services/update-hashtag.ts index 1dcb582791..3e22846731 100644 --- a/src/services/update-hashtag.ts +++ b/src/services/update-hashtag.ts @@ -86,7 +86,7 @@ export async function updateHashtag(user: User, tag: string, isUserAttached = fa } } else { if (isUserAttached) { - Hashtags.save({ + Hashtags.insert({ id: genId(), name: tag, mentionedUserIds: [], @@ -103,7 +103,7 @@ export async function updateHashtag(user: User, tag: string, isUserAttached = fa attachedRemoteUsersCount: Users.isRemoteUser(user) ? 1 : 0, } as Hashtag); } else { - Hashtags.save({ + Hashtags.insert({ id: genId(), name: tag, mentionedUserIds: [user.id], diff --git a/src/services/user-list/push.ts b/src/services/user-list/push.ts index e67be4b027..ba54c04475 100644 --- a/src/services/user-list/push.ts +++ b/src/services/user-list/push.ts @@ -8,7 +8,7 @@ import { fetchProxyAccount } from '../../misc/fetch-proxy-account'; import createFollowing from '../following/create'; export async function pushUserToUserList(target: User, list: UserList) { - await UserListJoinings.save({ + await UserListJoinings.insert({ id: genId(), createdAt: new Date(), userId: target.id, From a4a9b8707d2d8100e3601679d479dd3b13e73c9a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 21:27:49 +0900 Subject: [PATCH 60/76] perf(server): Reduce database query --- src/services/note/reaction/create.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts index 897c816de8..181099cc2d 100644 --- a/src/services/note/reaction/create.ts +++ b/src/services/note/reaction/create.ts @@ -53,12 +53,11 @@ export default async (user: User, note: Note, reaction?: string) => { await Notes.createQueryBuilder().update() .set({ reactions: () => sql, + score: () => '"score" + 1' }) .where('id = :id', { id: note.id }) .execute(); - Notes.increment({ id: note.id }, 'score', 1); - perUserReactionsChart.update(user, note); // カスタム絵文字リアクションだったら絵文字情報も送る From fb194b855b7548672d95071adfb5f354e78d3f32 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 22:09:32 +0900 Subject: [PATCH 61/76] perf(server): Reduce database query --- src/services/note/create.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 7c7e8d9a08..a85e72c5f9 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -594,10 +594,13 @@ function saveReply(reply: Note, note: Note) { } function incNotesCountOfUser(user: User) { - Users.increment({ id: user.id }, 'notesCount', 1); - Users.update({ id: user.id }, { - updatedAt: new Date() - }); + Users.createQueryBuilder().update() + .set({ + updatedAt: new Date(), + notesCount: () => '"notesCount" + 1' + }) + .where('id = :id', { id: user.id }) + .execute(); } async function extractMentionedUsers(user: User, tokens: ReturnType<typeof parse>): Promise<User[]> { From 82de8b7c50767e71f5414481fbb4e5ff7a449593 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 22:15:45 +0900 Subject: [PATCH 62/76] perf(server): Reduce database query --- src/services/note/create.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/services/note/create.ts b/src/services/note/create.ts index a85e72c5f9..96177e9758 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -444,8 +444,13 @@ async function renderNoteOrRenoteActivity(data: Option, note: Note) { } function incRenoteCount(renote: Note) { - Notes.increment({ id: renote.id }, 'renoteCount', 1); - Notes.increment({ id: renote.id }, 'score', 1); + Notes.createQueryBuilder().update() + .set({ + renoteCount: () => '"renoteCount" + 1', + score: () => '"score" + 1' + }) + .where('id = :id', { id: renote.id }) + .execute(); } async function insertNote(user: User, data: Option, tags: string[], emojis: string[], mentionedUsers: User[]) { @@ -525,7 +530,7 @@ async function insertNote(user: User, data: Option, tags: string[], emojis: stri await Notes.insert(insert); } - return await Notes.findOneOrFail(insert.id); + return insert; } catch (e) { // duplicate key error if (isDuplicateKeyValueError(e)) { From 2f2a8e537d03cea8d43b1fa84b8f1a48934f5d63 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 21 Mar 2021 22:26:45 +0900 Subject: [PATCH 63/76] fix bug --- src/models/repositories/notification.ts | 2 +- src/services/create-notification.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index 1027155873..986ddb1d42 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -16,7 +16,7 @@ export class NotificationRepository extends Repository<Notification> { public async pack( src: Notification['id'] | Notification, options: { - _hintForEachNotes_: { + _hintForEachNotes_?: { emojis: Emoji[] | null; myReactions: Map<Note['id'], NoteReaction | null>; }; diff --git a/src/services/create-notification.ts b/src/services/create-notification.ts index 6cd116040a..dedb8eac8d 100644 --- a/src/services/create-notification.ts +++ b/src/services/create-notification.ts @@ -30,7 +30,7 @@ export async function createNotification( ...data } as Partial<Notification>); - const packed = await Notifications.pack(notification); + const packed = await Notifications.pack(notification, {}); // Publish notification event publishMainStream(notifieeId, 'notification', packed); From d1efe1d2085dbae14f85ab6a993e755926067446 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Mon, 22 Mar 2021 00:44:38 +0900 Subject: [PATCH 64/76] =?UTF-8?q?populateEmojis=E3=81=AE=E3=83=AA=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=AF=E3=82=BF=E3=81=A8=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E6=83=85=E5=A0=B1=E3=81=AE=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7?= =?UTF-8?q?=E3=83=A5=20(#7378)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * revert * Refactor populateEmojis, Cache emojis * ん * fix typo * コメント --- src/misc/cache.ts | 23 +++- src/misc/populate-emojis.ts | 58 ++++++++++ src/models/repositories/note.ts | 140 +----------------------- src/models/repositories/notification.ts | 45 +------- src/models/repositories/user.ts | 39 +------ 5 files changed, 89 insertions(+), 216 deletions(-) create mode 100644 src/misc/populate-emojis.ts diff --git a/src/misc/cache.ts b/src/misc/cache.ts index 5b7017a3b9..71fbbd8a4c 100644 --- a/src/misc/cache.ts +++ b/src/misc/cache.ts @@ -14,13 +14,30 @@ export class Cache<T> { }); } - public get(key: string | null): T | null { + public get(key: string | null): T | undefined { const cached = this.cache.get(key); - if (cached == null) return null; + if (cached == null) return undefined; if ((Date.now() - cached.date) > this.lifetime) { this.cache.delete(key); - return null; + return undefined; } return cached.value; } + + public delete(key: string | null) { + this.cache.delete(key); + } + + public async fetch(key: string | null, fetcher: () => Promise<T>): Promise<T> { + const cachedValue = this.get(key); + if (cachedValue !== undefined) { + // Cache HIT + return cachedValue; + } + + // Cache MISS + const value = await fetcher(); + this.set(key, value); + return value; + } } diff --git a/src/misc/populate-emojis.ts b/src/misc/populate-emojis.ts new file mode 100644 index 0000000000..6300cfb95e --- /dev/null +++ b/src/misc/populate-emojis.ts @@ -0,0 +1,58 @@ +import { Emojis } from '../models'; +import { Emoji } from '../models/entities/emoji'; +import { Cache } from './cache'; +import { isSelfHost, toPunyNullable } from './convert-host'; + +const cache = new Cache<Emoji | null>(1000 * 60 * 60); + +/** + * 添付用絵文字情報 + */ +type PopulatedEmoji = { + name: string; + url: string; +}; + +/** + * 添付用絵文字情報を解決する + * @param emojiName ノートやユーザープロフィールに添付された、またはリアクションのカスタム絵文字名 (:は含めない, リアクションでローカルホストの場合は@.を付ける (これはdecodeReactionで可能)) + * @param noteUserHost ノートやユーザープロフィールの所有者 + * @returns 絵文字情報, nullは未マッチを意味する + */ +export async function populateEmoji(emojiName: string, noteUserHost: string | null): Promise<PopulatedEmoji | null> { + const match = emojiName.match(/^(\w+)(?:@([\w.-]+))?$/); + if (!match) return null; + + const name = match[1]; + + // クエリに使うホスト + let host = match[2] === '.' ? null // .はローカルホスト (ここがマッチするのはリアクションのみ) + : match[2] === undefined ? noteUserHost // ノートなどでホスト省略表記の場合はローカルホスト (ここがリアクションにマッチすることはない) + : isSelfHost(match[2]) ? null // 自ホスト指定 + : (match[2] || noteUserHost); // 指定されたホスト || ノートなどの所有者のホスト (こっちがリアクションにマッチすることはない) + + host = toPunyNullable(host); + + const queryOrNull = async () => (await Emojis.findOne({ + name, + host + })) || null; + + const emoji = await cache.fetch(`${name} ${host}`, queryOrNull); + + if (emoji == null) return null; + + return { + name: emojiName, + url: emoji.url, + }; +} + +/** + * 複数の添付用絵文字情報を解決する (キャシュ付き, 存在しないものは結果から除外される) + */ +export async function populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise<PopulatedEmoji[]> { + const emojis = await Promise.all(emojiNames.map(x => populateEmoji(x, noteUserHost))); + return emojis.filter((x): x is PopulatedEmoji => x != null); +} + diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts index 1fcedbd56f..9771f66704 100644 --- a/src/models/repositories/note.ts +++ b/src/models/repositories/note.ts @@ -1,15 +1,14 @@ import { EntityRepository, Repository, In } from 'typeorm'; import { Note } from '../entities/note'; import { User } from '../entities/user'; -import { Emojis, Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls, Channels } from '..'; +import { Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls, Channels } from '..'; import { SchemaType } from '../../misc/schema'; import { awaitAll } from '../../prelude/await-all'; import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '../../misc/reaction-lib'; import { toString } from '../../mfm/to-string'; import { parse } from '../../mfm/parse'; -import { Emoji } from '../entities/emoji'; -import { concat } from '../../prelude/array'; import { NoteReaction } from '../entities/note-reaction'; +import { populateEmojis } from '../../misc/populate-emojis'; export type PackedNote = SchemaType<typeof packedNoteSchema>; @@ -85,7 +84,6 @@ export class NoteRepository extends Repository<Note> { detail?: boolean; skipHide?: boolean; _hint_?: { - emojis: Emoji[] | null; myReactions: Map<Note['id'], NoteReaction | null>; }; } @@ -135,93 +133,6 @@ export class NoteRepository extends Repository<Note> { }; } - /** - * 添付用emojisを解決する - * @param emojiNames Note等に添付されたカスタム絵文字名 (:は含めない) - * @param noteUserHost Noteのホスト - * @param reactionNames Note等にリアクションされたカスタム絵文字名 (:は含めない) - */ - async function populateEmojis(emojiNames: string[], noteUserHost: string | null, reactionNames: string[]) { - const customReactions = reactionNames?.map(x => decodeReaction(x)).filter(x => x.name); - - let all = [] as { - name: string, - url: string - }[]; - - // 与えられたhintだけで十分(=新たにクエリする必要がない)かどうかを表すフラグ - let enough = true; - if (options?._hint_?.emojis) { - for (const name of emojiNames) { - const matched = options._hint_.emojis.find(x => x.name === name && x.host === noteUserHost); - if (matched) { - all.push({ - name: matched.name, - url: matched.url, - }); - } else { - enough = false; - } - } - for (const customReaction of customReactions) { - const matched = options._hint_.emojis.find(x => x.name === customReaction.name && x.host === customReaction.host); - if (matched) { - all.push({ - name: `${matched.name}@${matched.host || '.'}`, // @host付きでローカルは. - url: matched.url, - }); - } else { - enough = false; - } - } - } else { - enough = false; - } - if (enough) return all; - - // カスタム絵文字 - if (emojiNames?.length > 0) { - const tmp = await Emojis.find({ - where: { - name: In(emojiNames), - host: noteUserHost - }, - select: ['name', 'host', 'url'] - }).then(emojis => emojis.map((emoji: Emoji) => { - return { - name: emoji.name, - url: emoji.url, - }; - })); - - all = concat([all, tmp]); - } - - if (customReactions?.length > 0) { - const where = [] as {}[]; - - for (const customReaction of customReactions) { - where.push({ - name: customReaction.name, - host: customReaction.host - }); - } - - const tmp = await Emojis.find({ - where, - select: ['name', 'host', 'url'] - }).then(emojis => emojis.map((emoji: Emoji) => { - return { - name: `${emoji.name}@${emoji.host || '.'}`, // @host付きでローカルは. - url: emoji.url, - }; - })); - all = concat([all, tmp]); - } - - return all; - } - async function populateMyReaction() { if (options?._hint_?.myReactions) { const reaction = options._hint_.myReactions.get(note.id); @@ -257,15 +168,14 @@ export class NoteRepository extends Repository<Note> { : await Channels.findOne(note.channelId) : null; + const reactionEmojiNames = Object.keys(note.reactions).filter(x => x?.startsWith(':')).map(x => decodeReaction(x).reaction).map(x => x.replace(/:/g, '')); + const packed = await awaitAll({ id: note.id, createdAt: note.createdAt.toISOString(), userId: note.userId, user: Users.pack(note.user || note.userId, meId, { detail: false, - _hint_: { - emojis: options?._hint_?.emojis || null - } }), text: text, cw: note.cw, @@ -277,7 +187,7 @@ export class NoteRepository extends Repository<Note> { repliesCount: note.repliesCount, reactions: convertLegacyReactions(note.reactions), tags: note.tags.length > 0 ? note.tags : undefined, - emojis: populateEmojis(note.emojis, host, Object.keys(note.reactions)), + emojis: populateEmojis(note.emojis.concat(reactionEmojiNames), host), fileIds: note.fileIds, files: DriveFiles.packMany(note.fileIds), replyId: note.replyId, @@ -350,48 +260,10 @@ export class NoteRepository extends Repository<Note> { } } - // TODO: ここら辺の処理をaggregateEmojisみたいな関数に切り出したい - let emojisWhere: any[] = []; - for (const note of notes) { - if (typeof note !== 'object') continue; - emojisWhere.push({ - name: In(note.emojis), - host: note.userHost - }); - if (note.renote) { - emojisWhere.push({ - name: In(note.renote.emojis), - host: note.renote.userHost - }); - if (note.renote.user) { - emojisWhere.push({ - name: In(note.renote.user.emojis), - host: note.renote.userHost - }); - } - } - const customReactions = Object.keys(note.reactions).map(x => decodeReaction(x)).filter(x => x.name); - emojisWhere = emojisWhere.concat(customReactions.map(x => ({ - name: x.name, - host: x.host - }))); - if (note.user) { - emojisWhere.push({ - name: In(note.user.emojis), - host: note.userHost - }); - } - } - const emojis = emojisWhere.length > 0 ? await Emojis.find({ - where: emojisWhere, - select: ['name', 'host', 'url'] - }) : null; - return await Promise.all(notes.map(n => this.pack(n, me, { ...options, _hint_: { - myReactions: myReactionsMap, - emojis: emojis + myReactions: myReactionsMap } }))); } diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index 986ddb1d42..4f6e797ef9 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -1,13 +1,11 @@ import { EntityRepository, In, Repository } from 'typeorm'; -import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions, Emojis } from '..'; +import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '..'; import { Notification } from '../entities/notification'; import { awaitAll } from '../../prelude/await-all'; import { SchemaType } from '../../misc/schema'; import { Note } from '../entities/note'; import { NoteReaction } from '../entities/note-reaction'; import { User } from '../entities/user'; -import { decodeReaction } from '../../misc/reaction-lib'; -import { Emoji } from '../entities/emoji'; export type PackedNotification = SchemaType<typeof packedNotificationSchema>; @@ -17,7 +15,6 @@ export class NotificationRepository extends Repository<Notification> { src: Notification['id'] | Notification, options: { _hintForEachNotes_?: { - emojis: Emoji[] | null; myReactions: Map<Note['id'], NoteReaction | null>; }; } @@ -101,47 +98,9 @@ export class NotificationRepository extends Repository<Notification> { myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); } - // TODO: ここら辺の処理をaggregateEmojisみたいな関数に切り出したい - let emojisWhere: any[] = []; - for (const note of notes) { - if (typeof note !== 'object') continue; - emojisWhere.push({ - name: In(note.emojis), - host: note.userHost - }); - if (note.renote) { - emojisWhere.push({ - name: In(note.renote.emojis), - host: note.renote.userHost - }); - if (note.renote.user) { - emojisWhere.push({ - name: In(note.renote.user.emojis), - host: note.renote.userHost - }); - } - } - const customReactions = Object.keys(note.reactions).map(x => decodeReaction(x)).filter(x => x.name); - emojisWhere = emojisWhere.concat(customReactions.map(x => ({ - name: x.name, - host: x.host - }))); - if (note.user) { - emojisWhere.push({ - name: In(note.user.emojis), - host: note.userHost - }); - } - } - const emojis = emojisWhere.length > 0 ? await Emojis.find({ - where: emojisWhere, - select: ['name', 'host', 'url'] - }) : null; - return await Promise.all(notifications.map(x => this.pack(x, { _hintForEachNotes_: { - myReactions: myReactionsMap, - emojis: emojis, + myReactions: myReactionsMap } }))); } diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index 19b0e54239..ffece291d6 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -1,11 +1,11 @@ import $ from 'cafy'; import { EntityRepository, Repository, In, Not } from 'typeorm'; import { User, ILocalUser, IRemoteUser } from '../entities/user'; -import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances } from '..'; +import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances } from '..'; import config from '../../config'; import { SchemaType } from '../../misc/schema'; import { awaitAll } from '../../prelude/await-all'; -import { Emoji } from '../entities/emoji'; +import { populateEmojis } from '../../misc/populate-emojis'; export type PackedUser = SchemaType<typeof packedUserSchema>; @@ -150,9 +150,6 @@ export class UserRepository extends Repository<User> { options?: { detail?: boolean, includeSecrets?: boolean, - _hint_?: { - emojis: Emoji[] | null; - }; } ): Promise<PackedUser> { const opts = Object.assign({ @@ -170,34 +167,6 @@ export class UserRepository extends Repository<User> { }) : []; const profile = opts.detail ? await UserProfiles.findOneOrFail(user.id) : null; - let emojis: Emoji[] = []; - if (user.emojis.length > 0) { - // 与えられたhintだけで十分(=新たにクエリする必要がない)かどうかを表すフラグ - let enough = true; - if (options?._hint_?.emojis) { - for (const name of user.emojis) { - const matched = options._hint_.emojis.find(x => x.name === name && x.host === user.host); - if (matched) { - emojis.push(matched); - } else { - enough = false; - } - } - } else { - enough = false; - } - - if (!enough) { - emojis = await Emojis.find({ - where: { - name: In(user.emojis), - host: user.host - }, - select: ['name', 'host', 'url', 'aliases'] - }); - } - } - const falsy = opts.detail ? false : undefined; const packed = { @@ -220,9 +189,7 @@ export class UserRepository extends Repository<User> { faviconUrl: instance.faviconUrl, themeColor: instance.themeColor, } : undefined) : undefined, - - // カスタム絵文字添付 - emojis: emojis, + emojis: populateEmojis(user.emojis, user.host), ...(opts.detail ? { url: profile!.url, From 9e6cdd6106358f703dc8ce89cd1ae223a261f0cb Mon Sep 17 00:00:00 2001 From: mei23 <m@m544.net> Date: Mon, 22 Mar 2021 00:45:14 +0900 Subject: [PATCH 65/76] =?UTF-8?q?=E3=81=AE=E3=83=9B=E3=82=B9=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/misc/populate-emojis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc/populate-emojis.ts b/src/misc/populate-emojis.ts index 6300cfb95e..f3056fdebd 100644 --- a/src/misc/populate-emojis.ts +++ b/src/misc/populate-emojis.ts @@ -16,7 +16,7 @@ type PopulatedEmoji = { /** * 添付用絵文字情報を解決する * @param emojiName ノートやユーザープロフィールに添付された、またはリアクションのカスタム絵文字名 (:は含めない, リアクションでローカルホストの場合は@.を付ける (これはdecodeReactionで可能)) - * @param noteUserHost ノートやユーザープロフィールの所有者 + * @param noteUserHost ノートやユーザープロフィールの所有者のホスト * @returns 絵文字情報, nullは未マッチを意味する */ export async function populateEmoji(emojiName: string, noteUserHost: string | null): Promise<PopulatedEmoji | null> { From e881e1bfb3d1bf0e1a453235b5fe104f276c1392 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 22 Mar 2021 10:45:07 +0900 Subject: [PATCH 66/76] perf(server): Reduce database query --- src/services/note/create.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 96177e9758..4a737e8516 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -259,21 +259,21 @@ export default async (user: User, data: Option, silent = false) => new Promise<N }); // Antenna - Antennas.find().then(async antennas => { - const followings = await Followings.createQueryBuilder('following') - .andWhere(`following.followeeId = :userId`, { userId: note.userId }) - .getMany(); - - const followers = followings.map(f => f.followerId); - - for (const antenna of antennas) { - checkHitAntenna(antenna, note, user, followers).then(hit => { - if (hit) { - addNoteToAntenna(antenna, note, user); + Followings.createQueryBuilder('following') + .andWhere(`following.followeeId = :userId`, { userId: note.userId }) + .getMany() + .then(followings => { + const followers = followings.map(f => f.followerId); + Antennas.find().then(async antennas => { + for (const antenna of antennas) { + checkHitAntenna(antenna, note, user, followers).then(hit => { + if (hit) { + addNoteToAntenna(antenna, note, user); + } + }); } }); - } - }); + }); // Channel if (note.channelId) { From 0e6fbdfa5bd891cad051902d0fa5bcad4f5f8e2a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 22 Mar 2021 11:38:32 +0900 Subject: [PATCH 67/76] refactoring --- src/models/repositories/note.ts | 7 +++---- src/models/repositories/user.ts | 11 ++++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts index 9771f66704..354ea4dc70 100644 --- a/src/models/repositories/note.ts +++ b/src/models/repositories/note.ts @@ -235,7 +235,7 @@ export class NoteRepository extends Repository<Note> { } public async packMany( - notes: (Note['id'] | Note)[], + notes: Note[], me?: User['id'] | User | null | undefined, options?: { detail?: boolean; @@ -245,11 +245,10 @@ export class NoteRepository extends Repository<Note> { if (notes.length === 0) return []; const meId = me ? typeof me === 'string' ? me : me.id : null; - const noteIds = notes.map(n => typeof n === 'object' ? n.id : n); const myReactionsMap = new Map<Note['id'], NoteReaction | null>(); if (meId) { - const renoteIds = notes.filter(n => (typeof n === 'object') && (n.renoteId != null)).map(n => (n as Note).renoteId!); - const targets = [...noteIds, ...renoteIds]; + const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); + const targets = [...notes.map(n => n.id), ...renoteIds]; const myReactions = await NoteReactions.find({ userId: meId, noteId: In(targets), diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index ffece291d6..267e443cff 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -161,10 +161,11 @@ export class UserRepository extends Repository<User> { const meId = me ? typeof me === 'string' ? me : me.id : null; const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null; - const pins = opts.detail ? await UserNotePinings.find({ - where: { userId: user.id }, - order: { id: 'DESC' } - }) : []; + const pins = opts.detail ? await UserNotePinings.createQueryBuilder('pin') + .where('pin.userId = :userId', { userId: user.id }) + .innerJoinAndSelect('pin.note', 'note') + .orderBy('id', 'DESC') + .getMany() : []; const profile = opts.detail ? await UserProfiles.findOneOrFail(user.id) : null; const falsy = opts.detail ? false : undefined; @@ -211,7 +212,7 @@ export class UserRepository extends Repository<User> { followingCount: user.followingCount, notesCount: user.notesCount, pinnedNoteIds: pins.map(pin => pin.noteId), - pinnedNotes: Notes.packMany(pins.map(pin => pin.noteId), meId, { + pinnedNotes: Notes.packMany(pins.map(pin => pin.note!), meId, { detail: true }), pinnedPageId: profile!.pinnedPageId, From 1f0abef0843a3da364965a3327e7c0eba00b015a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 22 Mar 2021 12:36:57 +0900 Subject: [PATCH 68/76] refactor: extract functions --- src/misc/populate-emojis.ts | 38 +++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/misc/populate-emojis.ts b/src/misc/populate-emojis.ts index f3056fdebd..f21e24b219 100644 --- a/src/misc/populate-emojis.ts +++ b/src/misc/populate-emojis.ts @@ -13,6 +13,30 @@ type PopulatedEmoji = { url: string; }; +function normalizeHost(src: string | undefined, noteUserHost: string | null): string | null { + // クエリに使うホスト + let host = src === '.' ? null // .はローカルホスト (ここがマッチするのはリアクションのみ) + : src === undefined ? noteUserHost // ノートなどでホスト省略表記の場合はローカルホスト (ここがリアクションにマッチすることはない) + : isSelfHost(src) ? null // 自ホスト指定 + : (src || noteUserHost); // 指定されたホスト || ノートなどの所有者のホスト (こっちがリアクションにマッチすることはない) + + host = toPunyNullable(host); + + return host; +} + +function parseEmojiStr(emojiName: string, noteUserHost: string | null) { + const match = emojiName.match(/^(\w+)(?:@([\w.-]+))?$/); + if (!match) return { name: null, host: null }; + + const name = match[1]; + + // ホスト正規化 + const host = toPunyNullable(normalizeHost(match[2], noteUserHost)); + + return { name, host }; +} + /** * 添付用絵文字情報を解決する * @param emojiName ノートやユーザープロフィールに添付された、またはリアクションのカスタム絵文字名 (:は含めない, リアクションでローカルホストの場合は@.を付ける (これはdecodeReactionで可能)) @@ -20,18 +44,8 @@ type PopulatedEmoji = { * @returns 絵文字情報, nullは未マッチを意味する */ export async function populateEmoji(emojiName: string, noteUserHost: string | null): Promise<PopulatedEmoji | null> { - const match = emojiName.match(/^(\w+)(?:@([\w.-]+))?$/); - if (!match) return null; - - const name = match[1]; - - // クエリに使うホスト - let host = match[2] === '.' ? null // .はローカルホスト (ここがマッチするのはリアクションのみ) - : match[2] === undefined ? noteUserHost // ノートなどでホスト省略表記の場合はローカルホスト (ここがリアクションにマッチすることはない) - : isSelfHost(match[2]) ? null // 自ホスト指定 - : (match[2] || noteUserHost); // 指定されたホスト || ノートなどの所有者のホスト (こっちがリアクションにマッチすることはない) - - host = toPunyNullable(host); + const { name, host } = parseEmojiStr(emojiName, noteUserHost); + if (name == null) return null; const queryOrNull = async () => (await Emojis.findOne({ name, From 8f41dfec2e27c35809f97399cee7e4ad6be21056 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 22 Mar 2021 12:41:33 +0900 Subject: [PATCH 69/76] perf(server): Reduce database query --- src/misc/populate-emojis.ts | 47 +++++++++++++++++++++++++ src/models/repositories/note.ts | 4 ++- src/models/repositories/notification.ts | 3 ++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/misc/populate-emojis.ts b/src/misc/populate-emojis.ts index f21e24b219..6b74430182 100644 --- a/src/misc/populate-emojis.ts +++ b/src/misc/populate-emojis.ts @@ -1,7 +1,10 @@ +import { In } from 'typeorm'; import { Emojis } from '../models'; import { Emoji } from '../models/entities/emoji'; +import { Note } from '../models/entities/note'; import { Cache } from './cache'; import { isSelfHost, toPunyNullable } from './convert-host'; +import { decodeReaction } from './reaction-lib'; const cache = new Cache<Emoji | null>(1000 * 60 * 60); @@ -70,3 +73,47 @@ export async function populateEmojis(emojiNames: string[], noteUserHost: string return emojis.filter((x): x is PopulatedEmoji => x != null); } +export function aggregateNoteEmojis(notes: Note[]) { + let emojis: { name: string | null; host: string | null; }[] = []; + for (const note of notes) { + emojis = emojis.concat(note.emojis + .map(e => parseEmojiStr(e, note.userHost))); + if (note.renote) { + emojis = emojis.concat(note.renote.emojis + .map(e => parseEmojiStr(e, note.renote!.userHost))); + if (note.renote.user) { + emojis = emojis.concat(note.renote.user.emojis + .map(e => parseEmojiStr(e, note.renote!.userHost))); + } + } + const customReactions = Object.keys(note.reactions).map(x => decodeReaction(x)).filter(x => x.name != null) as typeof emojis; + emojis = emojis.concat(customReactions); + if (note.user) { + emojis = emojis.concat(note.user.emojis + .map(e => parseEmojiStr(e, note.userHost))); + } + } + return emojis.filter(x => x.name != null) as { name: string; host: string | null; }[]; +} + +/** + * 与えられた絵文字のリストをデータベースから取得し、キャッシュに追加します + */ +export async function prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise<void> { + const notCachedEmojis = emojis.filter(emoji => cache.get(`${emoji.name} ${emoji.host}`) == null); + const emojisQuery: any[] = []; + const hosts = new Set(notCachedEmojis.map(e => e.host)); + for (const host of hosts) { + emojisQuery.push({ + name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)), + host: host + }); + } + const _emojis = emojisQuery.length > 0 ? await Emojis.find({ + where: emojisQuery, + select: ['name', 'host', 'url'] + }) : []; + for (const emoji of _emojis) { + cache.set(`${emoji.name} ${emoji.host}`, emoji); + } +} diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts index 354ea4dc70..73e18f6c5b 100644 --- a/src/models/repositories/note.ts +++ b/src/models/repositories/note.ts @@ -8,7 +8,7 @@ import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '. import { toString } from '../../mfm/to-string'; import { parse } from '../../mfm/parse'; import { NoteReaction } from '../entities/note-reaction'; -import { populateEmojis } from '../../misc/populate-emojis'; +import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '../../misc/populate-emojis'; export type PackedNote = SchemaType<typeof packedNoteSchema>; @@ -259,6 +259,8 @@ export class NoteRepository extends Repository<Note> { } } + await prefetchEmojis(aggregateNoteEmojis(notes)); + return await Promise.all(notes.map(n => this.pack(n, me, { ...options, _hint_: { diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index 4f6e797ef9..83fe11d5f7 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -6,6 +6,7 @@ import { SchemaType } from '../../misc/schema'; import { Note } from '../entities/note'; import { NoteReaction } from '../entities/note-reaction'; import { User } from '../entities/user'; +import { aggregateNoteEmojis, prefetchEmojis } from '../../misc/populate-emojis'; export type PackedNotification = SchemaType<typeof packedNotificationSchema>; @@ -98,6 +99,8 @@ export class NotificationRepository extends Repository<Notification> { myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); } + await prefetchEmojis(aggregateNoteEmojis(notes)); + return await Promise.all(notifications.map(x => this.pack(x, { _hintForEachNotes_: { myReactions: myReactionsMap From 967fae142b119de82dd6603f0e56b7e88fbb8c58 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 22 Mar 2021 12:41:38 +0900 Subject: [PATCH 70/76] fix bug --- src/models/repositories/user.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index 267e443cff..53c06f3f16 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -164,7 +164,7 @@ export class UserRepository extends Repository<User> { const pins = opts.detail ? await UserNotePinings.createQueryBuilder('pin') .where('pin.userId = :userId', { userId: user.id }) .innerJoinAndSelect('pin.note', 'note') - .orderBy('id', 'DESC') + .orderBy('pin.id', 'DESC') .getMany() : []; const profile = opts.detail ? await UserProfiles.findOneOrFail(user.id) : null; From 36bcaaff55c6efda313eeb761fce2e2f564338d2 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 22 Mar 2021 12:43:21 +0900 Subject: [PATCH 71/76] Resolve #6533 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 622edcc5da..d05d0aa02c 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@fortawesome/vue-fontawesome": "3.0.0-3", "@koa/cors": "3.1.0", "@koa/multer": "3.0.0", - "@koa/router": "10.0.0", + "@koa/router": "9.0.1", "@sentry/browser": "5.29.2", "@sentry/tracing": "5.29.2", "@sinonjs/fake-timers": "7.0.2", diff --git a/yarn.lock b/yarn.lock index 6318a9b52c..eecd562aac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -251,10 +251,10 @@ resolved "https://registry.yarnpkg.com/@koa/multer/-/multer-3.0.0.tgz#439777949f28097d7b329c0b4ce3048074c862f8" integrity sha512-y+OQBmex5D1jIl723gAEUYcAWPEicIXppaAKw/zCMfpllQ08ZNweDPwoCLxEoatqd5pCu2XG6V8dl67JRq3RJw== -"@koa/router@10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@koa/router/-/router-10.0.0.tgz#699115561afbd2740e2848ba299fc76b9e058ad6" - integrity sha512-z9ytrKWn/j/qUApMSJzZbUwkbLcN2ZXGq6UsqWkZb50Us+/Qpu0RwgZ6ytawVOhfFBZ1ai5iVWeD2Dcu0qcnJw== +"@koa/router@9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@koa/router/-/router-9.0.1.tgz#4090a14223ea7e78aa13b632761209cba69acd95" + integrity sha512-OI+OU49CJV4px0WkIMmayBeqVXB/JS1ZMq7UoGlTZt6Y7ijK7kdeQ18+SEHHJPytmtI1y6Hf8XLrpxva3mhv5Q== dependencies: debug "^4.1.1" http-errors "^1.7.3" From 202e943d559d7c3a50a63aaca810dd3141ab70c4 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 22 Mar 2021 12:46:46 +0900 Subject: [PATCH 72/76] tweak cache lifetime --- src/misc/populate-emojis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc/populate-emojis.ts b/src/misc/populate-emojis.ts index 6b74430182..8052c71489 100644 --- a/src/misc/populate-emojis.ts +++ b/src/misc/populate-emojis.ts @@ -6,7 +6,7 @@ import { Cache } from './cache'; import { isSelfHost, toPunyNullable } from './convert-host'; import { decodeReaction } from './reaction-lib'; -const cache = new Cache<Emoji | null>(1000 * 60 * 60); +const cache = new Cache<Emoji | null>(1000 * 60 * 60 * 12); /** * 添付用絵文字情報 From 7c3086e9d9508d5df03d7859932c766a26b9664e Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 22 Mar 2021 15:14:54 +0900 Subject: [PATCH 73/76] perf(server): Cache user keypair --- src/misc/keypair-store.ts | 10 ++++++++++ src/remote/activitypub/renderer/index.ts | 6 ++---- src/remote/activitypub/renderer/person.ts | 5 +++-- src/remote/activitypub/request.ts | 10 +++------- src/server/activitypub.ts | 5 +++-- 5 files changed, 21 insertions(+), 15 deletions(-) create mode 100644 src/misc/keypair-store.ts diff --git a/src/misc/keypair-store.ts b/src/misc/keypair-store.ts new file mode 100644 index 0000000000..43f451110c --- /dev/null +++ b/src/misc/keypair-store.ts @@ -0,0 +1,10 @@ +import { UserKeypairs } from '../models'; +import { User } from '../models/entities/user'; +import { UserKeypair } from '../models/entities/user-keypair'; +import { Cache } from './cache'; + +const cache = new Cache<UserKeypair>(Infinity); + +export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> { + return await cache.fetch(userId, async () => await UserKeypairs.findOneOrFail(userId)); +} diff --git a/src/remote/activitypub/renderer/index.ts b/src/remote/activitypub/renderer/index.ts index e74affdadf..4c33fdafb1 100644 --- a/src/remote/activitypub/renderer/index.ts +++ b/src/remote/activitypub/renderer/index.ts @@ -3,7 +3,7 @@ import { v4 as uuid } from 'uuid'; import { IActivity } from '../type'; import { LdSignature } from '../misc/ld-signature'; import { ILocalUser } from '../../../models/entities/user'; -import { UserKeypairs } from '../../../models'; +import { getUserKeypair } from '../../../misc/keypair-store'; export const renderActivity = (x: any): IActivity | null => { if (x == null) return null; @@ -23,9 +23,7 @@ export const renderActivity = (x: any): IActivity | null => { export const attachLdSignature = async (activity: any, user: ILocalUser): Promise<IActivity | null> => { if (activity == null) return null; - const keypair = await UserKeypairs.findOneOrFail({ - userId: user.id - }); + const keypair = await getUserKeypair(user.id); const obj = { // as non-standards diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts index 4907e3bc6f..479e6d76bf 100644 --- a/src/remote/activitypub/renderer/person.ts +++ b/src/remote/activitypub/renderer/person.ts @@ -8,7 +8,8 @@ import { getEmojis } from './note'; import renderEmoji from './emoji'; import { IIdentifier } from '../models/identifier'; import renderHashtag from './hashtag'; -import { DriveFiles, UserProfiles, UserKeypairs } from '../../../models'; +import { DriveFiles, UserProfiles } from '../../../models'; +import { getUserKeypair } from '../../../misc/keypair-store'; export async function renderPerson(user: ILocalUser) { const id = `${config.url}/users/${user.id}`; @@ -49,7 +50,7 @@ export async function renderPerson(user: ILocalUser) { ...hashtagTags, ]; - const keypair = await UserKeypairs.findOneOrFail(user.id); + const keypair = await getUserKeypair(user.id); const person = { type: isSystem ? 'Application' : user.isBot ? 'Service' : 'Person', diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts index 2f07351635..5f15d5480c 100644 --- a/src/remote/activitypub/request.ts +++ b/src/remote/activitypub/request.ts @@ -5,11 +5,11 @@ import * as crypto from 'crypto'; import config from '../../config'; import { ILocalUser } from '../../models/entities/user'; -import { UserKeypairs } from '../../models'; import { getAgentByUrl } from '../../misc/fetch'; import { URL } from 'url'; import got from 'got'; import * as Got from 'got'; +import { getUserKeypair } from '../../misc/keypair-store'; export default async (user: ILocalUser, url: string, object: any) => { const timeout = 10 * 1000; @@ -22,9 +22,7 @@ export default async (user: ILocalUser, url: string, object: any) => { sha256.update(data); const hash = sha256.digest('base64'); - const keypair = await UserKeypairs.findOneOrFail({ - userId: user.id - }); + const keypair = await getUserKeypair(user.id); await new Promise((resolve, reject) => { const req = https.request({ @@ -74,9 +72,7 @@ export default async (user: ILocalUser, url: string, object: any) => { export async function signedGet(url: string, user: ILocalUser) { const timeout = 10 * 1000; - const keypair = await UserKeypairs.findOneOrFail({ - userId: user.id - }); + const keypair = await getUserKeypair(user.id); const req = got.get<any>(url, { headers: { diff --git a/src/server/activitypub.ts b/src/server/activitypub.ts index bf71258625..694807239b 100644 --- a/src/server/activitypub.ts +++ b/src/server/activitypub.ts @@ -13,10 +13,11 @@ import Following from './activitypub/following'; import Featured from './activitypub/featured'; import { inbox as processInbox } from '../queue'; import { isSelfHost } from '../misc/convert-host'; -import { Notes, Users, Emojis, UserKeypairs, NoteReactions } from '../models'; +import { Notes, Users, Emojis, NoteReactions } from '../models'; import { ILocalUser, User } from '../models/entities/user'; import { In } from 'typeorm'; import { renderLike } from '../remote/activitypub/renderer/like'; +import { getUserKeypair } from '../misc/keypair-store'; // Init router const router = new Router(); @@ -135,7 +136,7 @@ router.get('/users/:user/publickey', async ctx => { return; } - const keypair = await UserKeypairs.findOneOrFail(user.id); + const keypair = await getUserKeypair(user.id); if (Users.isLocalUser(user)) { ctx.body = renderActivity(renderKey(user, keypair)); From 0e3e90f94f684ab0dcd8039dd2087241b85ac612 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 22 Mar 2021 15:16:45 +0900 Subject: [PATCH 74/76] refactor --- src/misc/keypair-store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc/keypair-store.ts b/src/misc/keypair-store.ts index 43f451110c..c78fdd7555 100644 --- a/src/misc/keypair-store.ts +++ b/src/misc/keypair-store.ts @@ -6,5 +6,5 @@ import { Cache } from './cache'; const cache = new Cache<UserKeypair>(Infinity); export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> { - return await cache.fetch(userId, async () => await UserKeypairs.findOneOrFail(userId)); + return await cache.fetch(userId, () => UserKeypairs.findOneOrFail(userId)); } From 82dd88821c51b69ab1d18919ca09a64e1f71023f Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 22 Mar 2021 15:26:26 +0900 Subject: [PATCH 75/76] New Crowdin updates (#7320) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations theme.md (French) * New translations pages.md (French) * New translations theme.md (French) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations follow.md (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations keyboard-shortcut.md (Italian) * New translations timelines.md (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations timelines.md (Italian) * New translations timelines.md (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations api.md (French) * New translations api.md (French) * New translations ja-JP.yml (Italian) * New translations api.md (French) * New translations api.md (French) * New translations api.md (French) * New translations api.md (French) * New translations api.md (French) * New translations api.md (French) * New translations api.md (French) * New translations api.md (French) * New translations api.md (French) * New translations api.md (French) * New translations api.md (French) * New translations api.md (French) * New translations create-plugin.md (French) * New translations stream.md (French) * New translations ja-JP.yml (Italian) * New translations follow.md (French) * New translations ja-JP.yml (Italian) * New translations follow.md (Italian) * New translations follow.md (Italian) * New translations custom-emoji.md (Italian) * New translations custom-emoji.md (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations reaction.md (Italian) * New translations reaction.md (Italian) * New translations reaction.md (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations reaction.md (Italian) * New translations reaction.md (Italian) * New translations reaction.md (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations pages.md (French) * New translations pages.md (French) * New translations pages.md (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (French) * New translations ja-JP.yml (French) * New translations ja-JP.yml (French) * New translations ja-JP.yml (French) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations pages.md (Italian) * New translations ja-JP.yml (Italian) * New translations keyboard-shortcut.md (Italian) * New translations timelines.md (Italian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Russian) * New translations mute.md (Italian) * New translations mute.md (Italian) * New translations ja-JP.yml (Russian) * New translations mute.md (Italian) * New translations mute.md (Italian) * New translations mute.md (Italian) * New translations mute.md (Italian) * New translations mute.md (Italian) * New translations keyboard-shortcut.md (Italian) * New translations keyboard-shortcut.md (Italian) * New translations keyboard-shortcut.md (Italian) * New translations keyboard-shortcut.md (Italian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Russian) * New translations theme.md (Italian) * New translations theme.md (Italian) * New translations ja-JP.yml (Italian) * New translations theme.md (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Chinese Simplified) --- locales/fr-FR.yml | 69 ++-- locales/it-IT.yml | 595 +++++++++++++++++++++++++--- locales/ja-KS.yml | 6 +- locales/ru-RU.yml | 36 +- locales/zh-CN.yml | 2 +- locales/zh-TW.yml | 6 + src/docs/fr-FR/api.md | 80 ++-- src/docs/fr-FR/create-plugin.md | 2 +- src/docs/fr-FR/follow.md | 2 +- src/docs/fr-FR/pages.md | 4 +- src/docs/fr-FR/stream.md | 2 +- src/docs/fr-FR/theme.md | 4 +- src/docs/it-IT/custom-emoji.md | 4 +- src/docs/it-IT/follow.md | 4 +- src/docs/it-IT/keyboard-shortcut.md | 34 +- src/docs/it-IT/mute.md | 16 +- src/docs/it-IT/pages.md | 2 +- src/docs/it-IT/reaction.md | 16 +- src/docs/it-IT/theme.md | 18 +- src/docs/it-IT/timelines.md | 12 +- 20 files changed, 700 insertions(+), 214 deletions(-) diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 16815ab25d..1b3ccbbb0c 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -110,7 +110,7 @@ attachCancel: "Supprimer le fichier attaché" markAsSensitive: "Marquer comme sensible" unmarkAsSensitive: "Supprimer le marquage comme sensible" enterFileName: "Entrer le nom du fichier" -mute: "Mettre en sourdine" +mute: "Masquer" unmute: "Ne plus masquer" block: "Bloquer" unblock: "Débloquer" @@ -206,7 +206,7 @@ all: "Tous" subscribing: "Abonné" publishing: "Publié" notResponding: "Ne répond pas" -instanceFollowing: "Suivre une instance" +instanceFollowing: "Abonnements de l'instance" instanceFollowers: "Abonné·e·s de l’instance" instanceUsers: "Utilisateur·rice·s de cette l’instance" changePassword: "Modifier votre mot de passe" @@ -317,12 +317,12 @@ disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur registration: "S’inscrire" enableRegistration: "Autoriser les nouvelles inscriptions" invite: "Inviter" -proxyRemoteFiles: "Proxy fichiers distants" +proxyRemoteFiles: "Utiliser les fichiers distants comme proxy" proxyRemoteFilesDescription: "Si vous activez ce paramètre, les fichiers distants non stockés ou supprimés en raison d'une capacité excédentaire seront affichés via un proxy local et généreront une miniature. Cela n'affectera pas le stockage du serveur." driveCapacityPerLocalAccount: "Volume du Drive par utilisateur local" driveCapacityPerRemoteAccount: "Volume du Drive par utilisateur distant" inMb: "en mégaoctets" -iconUrl: "URL de l’image de l’avatar" +iconUrl: "URL de l'icône" bannerUrl: "URL de l’image de la bannière" basicInfo: "Informations basiques" pinnedUsers: "Utilisateur·rice épinglé·e" @@ -491,7 +491,7 @@ objectStorageUseProxyDesc: "Désactivez cette option si vous n'utilisez pas Prox objectStorageSetPublicRead: "Régler sur « public » lors de l'envoi" serverLogs: "Journal du serveur" deleteAll: "Supprimer tout" -showFixedPostForm: "Afficher le formulaire en haut du fil d'actualité" +showFixedPostForm: "Afficher le formulaire de publication en haut du fil d'actualité" newNoteRecived: "Vous avez reçu une nouvelle note" sounds: "Sons" listen: "Écouter" @@ -616,12 +616,14 @@ openInNewTab: "Ouvrir dans un nouvel onglet" openInSideView: "Ouvrir en vue latérale" defaultNavigationBehaviour: "Navigation par défaut" editTheseSettingsMayBreakAccount: "La modification de ces paramètres peut endommager votre compte." +instanceTicker: "Nom de l'instance d'origine des notes" waitingFor: "En attente de {x}" random: "Aléatoire" system: "Système" switchUi: "Modifier l'interface utilisateur" desktop: "Bureau" clip: "Clip" +createNew: "Créer nouveau" optional: "Facultatif" createNewClip: "Créer un nouveau clip" public: "Public" @@ -806,6 +808,7 @@ _reversi: canPutEverywhere: "Les pions peuvent être placés partout " _instanceTicker: none: "Cacher " + remote: "Montrer pour les utilisateur·ice·s distant·e·s" always: "Toujours afficher" _serverDisconnectedBehavior: reload: "Rechargement automatique" @@ -823,11 +826,12 @@ _channel: notesCount: "{n} Notes" _sidebar: full: "Complet" - icon: "Avatar" + icon: "Icônes" hide: "Masquer" _wordMute: muteWords: "Mots à filtrer" muteWordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec un saut de ligne pour une condition OR." + muteWordsDescription2: "Pour utiliser des expressions régulières (regex), mettez les mots-clés entre barres obliques." softDescription: "Masquez les notes de votre fil selon les paramètres que vous définissez." hardDescription: "Empêchez votre fil de charger les notes selon les paramètres que vous définissez. Cette action est irréversible : si vous modifiez ces paramètres plus tard, les notes précédemment filtrées ne seront pas récupérées." soft: "Doux" @@ -902,6 +906,8 @@ _sfx: chatBg: "Discuter (De fond)" antenna: "Réception de l’antenne" channel: "Notifications de canal" + reversiPutBlack: "Reversi : les pions noirs ont joué" + reversiPutWhite: "Reversi : les pions blancs ont joué" _ago: unknown: "Inconnu" future: "Futur" @@ -953,12 +959,12 @@ _2fa: _permissions: "read:account": "Afficher les informations du compte" "write:account": "Mettre à jour les informations de votre compte" - "read:blocks": "Voir les blocs" - "write:blocks": "Écrire des blocs" + "read:blocks": "Voir les comptes bloqués" + "write:blocks": "Gérer les comptes bloqués" "read:drive": "Parcourir le Drive" "write:drive": "Écrire sur le Drive" "read:favorites": "Afficher les favoris" - "write:favorites": "Écrire des favoris" + "write:favorites": "Gérer les favoris" "read:following": "Voir les informations de vos abonnements" "write:following": "Abonnements/Se désabonner" "read:messaging": "Cherche à discuter" @@ -1012,7 +1018,7 @@ _widgets: photos: "Photos" digitalClock: "Horloge numérique" federation: "Fédération" - postForm: "Formulaire à publier" + postForm: "Formulaire de publication" slideshow: "Diaporama" button: "Bouton" onlineUsers: "Utilisateurs en ligne" @@ -1083,8 +1089,8 @@ _profile: _exportOrImport: allNotes: "Toutes les notes" followingList: "Abonnements" - muteList: "Liste des comptes maqués" - blockingList: "Bloquer" + muteList: "Comptes masqués" + blockingList: "Comptes bloqués" userLists: "Listes" _charts: federationInstancesIncDec: "Variation du nombre des instances fédérées" @@ -1228,17 +1234,17 @@ _pages: if: "Si" _if: variable: "Variables" - post: "Formulaire à publier" + post: "Formulaire de publication" _post: text: "Contenu" attachCanvasImage: "Publier avec Toile comme image" canvasId: "Toile ID" - textInput: "Entrée de textuelle" + textInput: "Entrée textuelle" _textInput: name: "Nom de la variable" text: "Titre" default: "Valeur par défaut" - textareaInput: "Entrée de textuelle multiligne" + textareaInput: "Entrée textuelle multi-ligne" _textareaInput: name: "Nom de la variable" text: "Titre" @@ -1253,10 +1259,12 @@ _pages: id: "Toile ID" width: "Largeur" height: "Hauteur" + note: "Note intégrée" _note: id: "Identifiant de la note" + idDescription: "Pour configurer la note, vous pouvez aussi coller ici l'URL correspondante." detailed: "Afficher les détails" - switch: "Basculer" + switch: "Interrupteur" _switch: name: "Nom de la variable" text: "Titre" @@ -1265,16 +1273,16 @@ _pages: _counter: name: "Nom de la variable" text: "Titre" - inc: "Augmenter le chiffre" + inc: "Augmenter de" _button: text: "Titre" colored: "Coloré" - action: "L'opération lorsque le bouton sera pressé" + action: "Opération à effectuer lorsque le bouton est pressé" _action: dialog: "Afficher une fenêtre de dialogue" _dialog: content: "Contenu" - resetRandom: "Réinitialiser le nombre aléatoire" + resetRandom: "Réinitialiser un nombre aléatoire" pushEvent: "Envoyer un évènement" _pushEvent: event: "Nom de l’évènement" @@ -1288,7 +1296,7 @@ _pages: _radioButton: name: "Nom de la variable" title: "Titre" - values: "Choix séparés par des sauts de ligne" + values: "Liste des choix (un par ligne)" default: "Valeur par défaut" script: categories: @@ -1304,7 +1312,7 @@ _pages: list: "Listes" blocks: text: "Texte" - multiLineText: "Texte (Multi-lignes)" + multiLineText: "Texte (multi-ligne)" textList: "Liste de texte" _textList: info: "Veuillez séparer chaque entrée avec un saut de ligne" @@ -1347,10 +1355,10 @@ _pages: _mod: arg1: "A" arg2: "B" - round: "Décimal rond" + round: "Arrondir les décimales" _round: arg1: "Numérique" - eq: "A et B sont équivalents" + eq: "A et B sont égaux" _eq: arg1: "A" arg2: "B" @@ -1366,7 +1374,7 @@ _pages: _or: arg1: "A" arg2: "B" - lt: "A est plus petit que B" + lt: "A est inférieur à B" _lt: arg1: "A" arg2: "B" @@ -1374,7 +1382,7 @@ _pages: _gt: arg1: "A" arg2: "B" - ltEq: "A est plus petit ou égal à B" + ltEq: "A est inférieur ou égal à B" _ltEq: arg1: "A" arg2: "B" @@ -1440,7 +1448,7 @@ _pages: numberToString: "Convertir du numérique en texte" _numberToString: arg1: "Numérique" - splitStrByLine: "Séparer le texte par lignes" + splitStrByLine: "Séparer le texte par des sauts de lignes" _splitStrByLine: arg1: "Texte" ref: "Variables" @@ -1448,7 +1456,7 @@ _pages: fn: "Fonction" _fn: slots: "Slots" - slots-info: "Veuillez délimiter chaque slot par un saut de ligne" + slots-info: "Veuillez insérer un seul slot par ligne" arg1: "Sortie" for: "Répéter" _for: @@ -1482,18 +1490,19 @@ _notification: youWereFollowed: "Vous suit" youReceivedFollowRequest: "Vous avez reçu une demande d’abonnement" yourFollowRequestAccepted: "Votre demande d’abonnement a été accepté" - youWereInvitedToGroup: "Invité au groupe" + youWereInvitedToGroup: "Invité·e au groupe" _types: all: "Toutes" follow: "Abonnements" - mention: "Mentionner" + mention: "Mentions" reply: "Réponses" renote: "Partager" quote: "Citer" reaction: "Réactions" + pollVote: "Votes dans des sondages" receiveFollowRequest: "Demande d'abonnement reçue" followRequestAccepted: "Demande d'abonnement acceptée" - groupInvited: "Invité aux groupes" + groupInvited: "Invitation à un groupe" app: "Notifications provenant des apps" _deck: alwaysShowMainColumn: "Toujours afficher la colonne principale" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 4e2e4a5353..25ee8c6f6b 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -1,6 +1,7 @@ --- _lang_: "Italiano" headlineMisskey: "Rete collegata tramite note" +introMisskey: "Benvenut@! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \nScrivi \"note\" per condividere ciò che sta succedendo adesso o per dire a tutti qualcosa di te. 📡\nGrazie alla funzione \"reazioni\" puoi anche mandare reazioni rapide alle note delle altre persone del Fediverso. 👍\nEsplora un nuovo mondo! 🚀" monthAndDay: "{day}/{month}" search: "Cerca" notifications: "Notifiche" @@ -11,7 +12,7 @@ ok: "OK" gotIt: "Capito!" cancel: "Annulla" enterUsername: "Inserisci un nome utente" -renotedBy: "Condiviso da {user}" +renotedBy: "Rinotato da {user}" noNotes: "Nessuna nota!" noNotifications: "Nessuna notifica" instance: "Istanza" @@ -21,6 +22,7 @@ otherSettings: "Altre impostazioni" openInWindow: "Apri in una finestra" profile: "Profilo" timeline: "Timeline" +noAccountDescription: "L'utente non ha ancora scritto niente nella biografia di profilo." login: "Accedi" loggingIn: "Accesso in corso..." logout: "Esci" @@ -32,15 +34,16 @@ addUser: "Aggiungi utente" favorite: "Preferiti" favorites: "Preferiti" unfavorite: "Rimuovi nota dai preferiti" -favorited: "Aggiunta ai preferiti." -alreadyFavorited: "Già tra i preferiti." -cantFavorite: "Impossibile aggiungere ai Preferiti." +favorited: "Aggiunta ai tuoi preferiti." +alreadyFavorited: "Già tra i tuoi preferiti." +cantFavorite: "Impossibile aggiungere la nota ai preferiti." pin: "Fissa sul profilo" unpin: "Non fissare sul profilo" copyContent: "Copia il contenuto" -copyLink: "Copia link" +copyLink: "Copia il link" delete: "Elimina" -deleteAndEdit: "Elimina & Modifica" +deleteAndEdit: "Elimina e modifica" +deleteAndEditConfirm: "Vuoi davvero cancellare questa nota e scriverla di nuovo? Verrano eliminate anche tutte le reazioni, Rinote e risposte collegate." addToList: "Aggiungi alla lista" sendMessage: "Invia messaggio" copyUsername: "Copia nome utente" @@ -49,65 +52,104 @@ reply: "Rispondi" loadMore: "Mostra di più" showMore: "Mostra di più" youGotNewFollower: "Ha iniziato a seguirti" -receiveFollowRequest: "Nuova richiesta di essere seguito" +receiveFollowRequest: "Hai ricevuto una richiesta di follow." +followRequestAccepted: "Richiesta di follow accettata" mention: "Menzioni" mentions: "Menzioni" directNotes: "Note dirette" -importAndExport: "Importa ed Esporta" +importAndExport: "Importa ed esporta" import: "Importa" export: "Esporta" -files: "Allegato" +files: "Allegati" download: "Scarica" +driveFileDeleteConfirm: "Vuoi davvero eliminare il file「{name}? Anche gli allegati verranno eliminati." +unfollowConfirm: "Vuoi davvero smettere di seguire {name}?" +exportRequested: "Hai richiesto un'esportazione, e potrebbe volerci tempo. Quando sarà compiuta, il file verrà aggiunto direttamente al Drive." +importRequested: "Hai richiesto un'importazione. Può volerci tempo. " lists: "Liste" -noLists: "Qui non c'è ancora niente" +noLists: "Nessuna lista" note: "Nota" notes: "Note" -following: "Seiguiti" -followers: "Seguaci" +following: "Follows" +followers: "Followers" followsYou: "Ti segue" -createList: "Crea una nuova lista" -manageLists: "Modifica lista" +createList: "Aggiungi una nuova lista" +manageLists: "Gestisci liste" error: "Errore" -somethingHappened: "Qualcosa è andato storto." +somethingHappened: "Si è verificato un problema" retry: "Riprova" -enterListName: "Inserisci il nome della lista" +pageLoadError: "Caricamento pagina non riuscito. " +enterListName: "Nome della lista" privacy: "Privacy" +makeFollowManuallyApprove: "Richiedi di approvare i follower manualmente" +defaultNoteVisibility: "Privacy predefinita delle note" follow: "Segui" -followRequest: "Richiesta di seguire" -followRequests: "Richiesta di seguire" +followRequest: "Richiesta di follow" +followRequests: "Richieste di follow" unfollow: "Smetti di seguire" -followRequestPending: "In sospeso" +followRequestPending: "La richiesta di follow deve essere approvata" enterEmoji: "Inserisci emoji" renote: "Rinota" unrenote: "Annulla rinota" -renoted: "Condiviso!" -cantReRenote: "È impossibile rinota una condivisione." +renoted: "Rinotato!" +cantRenote: "È impossibile rinotare questa nota." +cantReRenote: "È impossibile rinotare una Rinota." quote: "Cita" +pinnedNote: "Nota fissata" pinned: "Fissa sul profilo" you: "Tu" clickToShow: "Clicca per visualizzare" sensitive: "Contenuto sensibile" add: "Aggiungi" reaction: "Reazione" +reactionSettingDescription: "Scegli le reazioni che preferisci e fissale nel pannello di reazioni." +reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere." +rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note" attachCancel: "Rimuovi allegato" markAsSensitive: "Segna come sensibile" unmarkAsSensitive: "Segna come non sensibile" +enterFileName: "Nome del file" mute: "Silenzia" unmute: "Riattiva" block: "Blocca" unblock: "Sblocca" suspend: "Sospendi" unsuspend: "Annulla la sospensione dell'account" -blockConfirm: "Vuoi bloccare?" -unblockConfirm: "Vuoi sbloccare?" +blockConfirm: "Vuoi davvero bloccare l'account?" +unblockConfirm: "Vuoi davvero sbloccare l'account?" +suspendConfirm: "Vuoi davvero sospendere questo account?" +unsuspendConfirm: "Vuoi annullare la sospensione dell'account?" +selectList: "Seleziona una lista" +selectAntenna: "Scegli un'antenna" +selectWidget: "Seleziona widget" +editWidgets: "Modifica i widget" editWidgetsExit: "Modifica fine" +customEmojis: "Emoji personalizzati" emoji: "Emoji" -addAcount: "Aggiungi un account esistente" +emojiName: "Nome dell'emoji" +emojiUrl: "URL dell'emoji" +addEmoji: "Aggiungi un emoji" +settingGuide: "Configurazione suggerita" +cacheRemoteFiles: "Memorizzazione nella cache dei file remoti" +flagAsBot: "Io sono un robot" +flagAsBotDescription: "Se l'account esegue principalmente operazioni automatiche, attiva quest'opzione. Quando attivata, opera come un segnalatore per gli altri sviluppatori allo scopo di prevenire catene d’interazione senza fine con altri bot, e di adeguare i sistemi interni di Misskey perché trattino questo account come un bot." +flagAsCat: "Io sono un gatto" +flagAsCatDescription: "Abilita l'opzione \"Io sono un gatto\" per l'account." +autoAcceptFollowed: "Accetta automaticamente le richieste di follow da utenti che già segui" +addAcount: "Aggiungi account" +loginFailed: "Accesso non riuscito" +showOnRemote: "Sfoglia sull'istanza remota" general: "Generali" wallpaper: "Sfondo" setWallpaper: "Imposta sfondo" removeWallpaper: "Elimina lo sfondo" searchWith: "Cerca: {q}" +youHaveNoLists: "Non hai ancora creato nessuna lista" +followConfirm: "Sei sicur@ di voler seguire {name}?" +proxyAccount: "Account proxy" +host: "Server remoto" +selectUser: "Seleziona utente" +recipient: "Destinatario" annotation: "Descrizione" federation: "Federazione" instances: "Istanza" @@ -115,36 +157,62 @@ storageUsage: "Volume di dischi" charts: "Grafici" perHour: "All'ora" perDay: "al giorno" +blockThisInstance: "Blocca l'istanza" +operations: "Operazioni" software: "Software" version: "Versione" metadata: "Metadato" +withNFiles: "{n} file in allegato" +monitor: "Monitorare" +jobQueue: "Coda di lavoro" cpuAndMemory: "CPU e Memoria" network: "Rete" disk: "Disco" -instanceInfo: "Informazioni di istanza" +instanceInfo: "Informazioni sull'istanza" statistics: "Statistiche" -clearQueue: "Cancella coda" -clearQueueConfirmTitle: "Cancella coda?" -blockedInstances: "Istanza bloccati" -muteAndBlock: "Silenziamento e blocco" +clearQueue: "Svuota coda" +clearQueueConfirmTitle: "Vuoi davvero svuotare la coda?" +clearCachedFiles: "Svuota cache" +clearCachedFilesConfirm: "Vuoi davvero svuotare la cache da tutti i file remoti?" +blockedInstances: "Istanze bloccate" +blockedInstancesDescription: "Elenca le istanze che vuoi bloccare, una per riga. Esse non potranno più interagire con la tua istanza." +muteAndBlock: "Silenziati / Bloccati" mutedUsers: "Account silenziati" blockedUsers: "Account bloccati" +noUsers: "Nessun utente trovato" editProfile: "Modifica profilo" noteDeleteConfirm: "Eliminare questo Nota?" +pinLimitExceeded: "Non puoi fissare altre note " +intro: "L'installazione di Misskey è finita! Si prega di creare un account amministratore." done: "Fine" processing: "In elaborazione" -blocked: "Bloccati" +preview: "Anteprima" +default: "Predefinito" +noCustomEmojis: "Nessun emoji" +noJobs: "Nessun lavoro" +federating: "Federando" +blocked: "Bloccato" +suspended: "Sospes@" all: "Tutti" +subscribing: "Iscrivendo" +publishing: "Pubblicando" notResponding: "Nessuna risposta" +instanceFollowing: "Seguiti dall'istanza" +instanceFollowers: "Followers dell'istanza" +instanceUsers: "Utenti dell'istanza" changePassword: "Aggiorna Password" security: "Sicurezza" retypedNotMatch: "Le password non corrispondono." currentPassword: "Password attuale" newPassword: "Nuova Password" -newPasswordRetype: "Conferma nuova password" +newPasswordRetype: "Conferma password" +attachFile: "Allega file" more: "Altri!" +featured: "Tendenze" +usernameOrUserId: "Nome utente o ID utente" +noSuchUser: "Nessun utente trovato" lookup: "Cercare" -announcements: "Annuncio" +announcements: "Annunci" imageUrl: "URL dell'immagine" remove: "Elimina" removed: "Il tuo Tweet è stato eliminato" @@ -154,31 +222,66 @@ resetAreYouSure: "Reimposta" saved: "Salvato" messaging: "Messaggi" upload: "Carica" +fromDrive: "Dal Drive" +fromUrl: "Dall'URL" uploadFromUrl: "Incolla URL immagine" +uploadFromUrlDescription: "URL del file che vuoi caricare" +uploadFromUrlRequested: "Caricamento richiesto" +uploadFromUrlMayTakeTime: "Il caricamento del file può richiedere tempo." explore: "Esplora" games: "Misskey Giochi" messageRead: "Visualizzato" +noMoreHistory: "Non c'è più cronologia da visualizzare" startMessaging: "Nuovo messaggio" +nUsersRead: "Letto da {n} persone" +agreeTo: "Sono d'accordo con {0}" tos: "Termini di servizio" +start: "Inizia!" home: "Home" +remoteUserCaution: "Può darsi che le informazioni siano incomplete perché questo è un utente remoto." +activity: "Attività" images: "Immagini" birthday: "Compleanno" yearsOld: "{age}Anni" registeredDate: "Iscrizione a.." location: "Posizione" theme: "Tema" +themeForLightMode: "Tema da utilizzare per il modo chiaro" +themeForDarkMode: "Tema da utilizzare per il modo scuro" light: "Chiaro" dark: "Scuro" lightThemes: "Tema Chiaro" darkThemes: "Tema Scuro" +syncDeviceDarkMode: "Sincronizza il tema scuro con le impostazioni del dispositivo" drive: "Drive" fileName: "Nome dell'allegato" +selectFile: "Scelta allegato" +selectFiles: "Scelta allegato" +selectFolder: "Seleziona cartella" +selectFolders: "Seleziona cartella" +renameFile: "Rinomina file" +folderName: "Nome della cartella" +createFolder: "Nuova cartella" +renameFolder: "Rinominare cartella" +deleteFolder: "Elimina cartella" +addFile: "Allega" +emptyDrive: "Il Drive è vuoto" +emptyFolder: "La cartella è vuota" +unableToDelete: "Eliminazione impossibile" +inputNewFileName: "Inserisci nome del nuovo file" +inputNewFolderName: "Inserisci nome della nuova cartella" +circularReferenceFolder: "La cartella di destinazione è una sottocartella della cartella che vuoi spostare." +hasChildFilesOrFolders: "Impossibile eliminare la cartella perché non è vuota" copyUrl: "Copia URL" rename: "Modifica nome" avatar: "Foto del profilo" -banner: "Foto d'intestazione" +banner: "Intestazione" nsfw: "Contenuti sensibili" +whenServerDisconnected: "Quando la connessione col server è persa" +disconnectedFromServer: "Disconness@ dal server" reload: "Ricarica" +doNothing: "Nessun'azione" +reloadConfirm: "Vuoi ricaricare?" watch: "Osserva" unwatch: "Smetti di Osserva" accept: "Accetta" @@ -195,21 +298,64 @@ today: "Oggi" dayX: "{day}" monthX: "{month}" yearX: "{year}" +pages: "Pagine" integration: "App collegate" connectSerice: "Connetti" disconnectSerice: "Disconnetti" +enableLocalTimeline: "Abilita Timeline locale" +enableGlobalTimeline: "Abilita Timeline federata" +disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e i moderatori potranno sempre accederci." registration: "Iscriviti" +enableRegistration: "Permettere nuove registrazioni" invite: "Invita" -bannerUrl: "indirizzo Foto d'intestazione" +proxyRemoteFiles: "Usare file remoti come proxy" +driveCapacityPerLocalAccount: "Volume del Drive per utente locale" +driveCapacityPerRemoteAccount: "Volume del Drive per utente remoto" +inMb: "in Megabytes" +iconUrl: "URL di icona (favicon, ecc.)" +bannerUrl: "URL dell'immagine d'intestazione" basicInfo: "Informazioni fondamentali" +pinnedUsers: "Utenti in evidenza" +pinnedUsersDescription: "Elenca gli/le utenti che vuoi fissare in cima alla pagina \"Esplora\", un@ per riga." +pinnedPages: "Pagine in evidenza" +pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima alla pagina dell'istanza. Una pagina per riga." +pinnedClipId: "ID della clip in evidenza" +pinnedNotes: "Nota fissata" hcaptcha: "hCaptcha" enableHcaptcha: "Abilita hCaptcha" +hcaptchaSiteKey: "Chiave del sito" +hcaptchaSecretKey: "Chiave segreta" recaptcha: "reCAPTCHA" enableRecaptcha: "Abilita reCAPTCHA" +recaptchaSiteKey: "Chiave del sito" +recaptchaSecretKey: "Chiave segreta" +antennas: "Antenne" +manageAntennas: "Gestore delle antenne" name: "Nome" +antennaSource: "Fonte dell'antenna" +antennaKeywords: "Parole chiavi da ricevere" +antennaExcludeKeywords: "Parole chiavi da escludere" +antennaKeywordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con un'interruzzione riga indica la condizione \"O\"." +notifyAntenna: "Invia notifiche delle nuove note" +withFileAntenna: "Solo note con file in allegato" serviceworker: "ServiceWorker" +enableServiceworker: "Abilita ServiceWorker" +antennaUsersDescription: "Inserisci solo un nome utente per riga" +caseSensitive: "Sensibile alla distinzione tra maiuscole e minuscole" +withReplies: "Includere le risposte" +connectedTo: "Sei conness@ agli account qui sotto:" notesAndReplies: "Note e risposte" -exploreFediverse: "Esplora Fediverse" +withFiles: "Con file in allegato" +silence: "Silenzia" +silenceConfirm: "Vuoi davvero silenziare l'utente?" +unsilence: "Riattiva" +unsilenceConfirm: "Vuoi davvero riattivare l'utente?" +popularUsers: "Utenti popolari" +recentlyUpdatedUsers: "Utenti attivi di recente" +recentlyRegisteredUsers: "Utenti registrati di recente" +recentlyDiscoveredUsers: "Utenti scoperti di recente" +exploreUsersCount: "Ci sono {count} utenti" +exploreFediverse: "Esplora il Fediverso" popularTags: "Tag di tendenza" userList: "Liste" about: "Informazioni" @@ -218,96 +364,243 @@ administrator: "Amministratore" token: "Token" twoStepAuthentication: "Autenticazione a due fattori" moderator: "Moderatore" +nUsersMentioned: "{n} utenti menzionatə" +securityKey: "Chiave di sicurezza" +securityKeyName: "Nome della chiave" +registerSecurityKey: "Registra una chiave di sicurezza" lastUsed: "Ultima attività" -unregister: "Disattiva account" +unregister: "Annulla l'iscrizione" +passwordLessLogin: "Accedi senza password" resetPassword: "Reimposta password" -share: "Renota" +newPasswordIs: "La tua nuova password è「{password}」" +reduceUiAnimation: "Ridurre le animazioni dell'interfaccia" +share: "Condividi" +notFound: "Non trovato" +notFoundDescription: "Nessuna pagina corrisponde all'URL indicata." +uploadFolder: "Destinazione caricamento predefinita" cacheClear: "Svuota cache" +markAsReadAllNotifications: "Segna tutte le notifiche come lette" +markAsReadAllUnreadNotes: "Segna tutte le note come lette" +markAsReadAllTalkMessages: "Segna tutte le chat come lette" help: "Guida" +inputMessageHere: "Scrivi messaggio qui" close: "Chiudi" group: "Gruppo" groups: "Gruppi" createGroup: "Nuovo gruppo" -invites: "Invita" +ownedGroups: "I miei gruppi" +joinedGroups: "Gruppi a cui mi sono unit@" +invites: "Inviti" +groupName: "Nome del gruppo" +members: "Membri" transfer: "Trasferisci" +messagingWithUser: "Iniziare una chat con un altr@ utente" +messagingWithGroup: "Chattare in gruppo" title: "Titolo" +text: "Testo" +enable: "Abilita" next: "Avanti" +retype: "Conferma" noteOf: "Note di {user}" +inviteToGroup: "Invitare al gruppo" +maxNoteTextLength: "Lunghezza massima delle note" +quoteAttached: "Citazione allegata" +quoteQuestion: "Vuoi aggiungere una citazione?" +noMessagesYet: "Ancora nessuna chat" +newMessageExists: "Hai ricevuto un nuovo messaggio" +onlyOneFileCanBeAttached: "È possibile allegare al messaggio soltanto uno file" +signinRequired: "Devi essere registrat@ nel tuo account" invitations: "Invita" invitationCode: "Codice di invito" +checking: "Confermando" available: "Consigliati" unavailable: "Il nome utente è già in uso" usernameInvalidFormat: "Il nome utente può contenere solo lettere, numeri e '_'" tooShort: "Troppo breve" tooLong: "Troppo lungo" +weakPassword: "Password debole" +normalPassword: "Password buona" +strongPassword: "Password forte" +passwordMatched: "Corretta" passwordNotMatched: "Le password non corrispondono." +signinWith: "Accedi con {x}" +signinFailed: "Autenticazione non riuscita. Controlla la tua password e nome utente." +tapSecurityKey: "Premi la chiave di sicurezza" +or: "oppure" +language: "Lingua" +uiLanguage: "Lingua di visualizzazione dell'interfaccia" +groupInvited: "Invitat@ al gruppo" +aboutX: "Informazioni su {x}" +useOsNativeEmojis: "Usare le emoji native del sistema operativo" +youHaveNoGroups: "Nessun gruppo" +joinOrCreateGroup: "Puoi creare il tuo gruppo o essere invitat@ a gruppi che già esistono." +noHistory: "Nessuna cronologia" signinHistory: "Cronologia di accesso all'account" +category: "Categoria" tags: "Tag" +docSource: "Sorgente della scheda" createAccount: "Crea il tuo account" existingAcount: "Account esistente" +regenerate: "Generare di nuovo" +fontSize: "Dimensione carattere" +noFollowRequests: "Non hai alcuna richiesta di follow" +openImageInNewTab: "Aprire immagini in una nuova scheda" +dashboard: "Pannello di controllo" local: "Locale" remote: "Remoto" -accountSettings: "Impostazioni Account" +total: "Totale" +weekOverWeekChanges: "Settimanale" +dayOverDayChanges: "Giornaliero" +appearance: "Aspetto" +clientSettings: "Impostazioni client" +accountSettings: "Impostazioni account" +promotion: "Promossa" promote: "Pubblicizza" +numberOfDays: "Numero di giorni" +hideThisNote: "Nasconda la nota" +showFeaturedNotesInTimeline: "Mostrare le note di tendenza nella tua timeline" +objectStorage: "Stoccaggio oggetti" +useObjectStorage: "Utilizza stoccaggio oggetti" objectStorageBaseUrl: "Base URL" objectStorageBucket: "Bucket" +objectStoragePrefix: "Prefix" +objectStoragePrefixDesc: "I file saranno conservati sotto la directory di questo prefisso." objectStorageEndpoint: "Endpoint" objectStorageRegion: "Region" objectStorageUseSSL: "Usare SSL" serverLogs: "Log del server" deleteAll: "Cancella cronologia" -sounds: "Effetti sonori" +showFixedPostForm: "Visualizzare la finestra di pubblicazione in cima alla timeline" +newNoteRecived: "Nuova nota ricevuta" +sounds: "Impostazioni suoni" listen: "Ascolta" none: "Niente" +showInPage: "Visualizza in pagina" +popout: "Finestra pop-out" volume: "Volume" +masterVolume: "Volume principale" details: "Dettagli" +chooseEmoji: "Scegli emoji" +unableToProcess: "Impossibile compiere l'operazione" +recentUsed: "Usato di recente" install: "Installa" uninstall: "Disinstalla" +installedApps: "Applicazioni installate" +nothing: "Niente da visualizzare" installedDate: "Data installazione" +lastUsedDate: "Data di ultimo uso" state: "Stato" sort: "Ordina per" -visibility: "Privacy dei post" +ascendingOrder: "Ascendente" +descendingOrder: "Discendente" +scratchpad: "ScratchPad" +output: "Uscita" +script: "Script" +disablePagesScript: "Disabilita AiScript nelle pagine" +updateRemoteUser: "Aggiornare le informazioni di utente remoto" +deleteAllFiles: "Elimina tutti i file" +deleteAllFilesConfirm: "Vuoi davvero eliminare tutti i file?" +removeAllFollowing: "Cancella tutti i follows" +removeAllFollowingDescription: "Cancella tutti i follows del server {host}. Per favore, esegui se, ad esempio, l'istanza non esiste più." +userSuspended: "L'utente è sospes@." +userSilenced: "L'utente è silenziat@." +sidebar: "Barra laterale" +divider: "Linea di separazione" +addItem: "Aggiungi elemento" +rooms: "Camera" +serviceworkerInfo: "Deve essere abilitato per le notifiche push. " +deletedNote: "Nota eliminata" +invisibleNote: "Nota invisibile" +enableInfiniteScroll: "Abilita scorrimento infinito" +visibility: "Visibilità" poll: "Sondaggio" useCw: "Nascondere media" +expandTweet: "Espandi tweet" +themeEditor: "Editor di temi" description: "Descrizione" author: "Autore" +leaveConfirm: "Ci sono delle modifiche ancora non salvate. Vuoi cancellarle?" +manage: "Gestione" +useFullReactionPicker: "Usa la totalità del pannello di reazioni" width: "Larghezza" height: "Altezza" large: "Grande" medium: "Predefinito" small: "Piccolo" +enableAll: "Abilita tutto" +disableAll: "Disabilita tutto" +tokenRequested: "Autorizza accesso all'account" +notificationType: "Tipo di notifiche" edit: "Modifica" +useStarForReactionFallback: "Se è sconosciuto l'emoji di reazione, usare la ★ come alternativa." +emailConfig: "Impostazioni server email" email: "Email" +smtpHost: "Server remoto" smtpUser: "Nome utente" smtpPass: "Password" -wordMute: "Parole silenziate" +wordMute: "Filtri parole" +userSaysSomething: "{name} ha detto qualcosa" display: "Visualizza" copy: "Copia" logs: "Log" database: "Base di dati" channel: "Canale" -notificationSetting: "impostazioni delle notifiche" +create: "Crea" +notificationSetting: "Impostazioni notifiche" +notificationSettingDesc: "Seleziona il tipo di notifiche da visualizzare." other: "Avanzate" +fileIdOrUrl: "ID o URL del file" abuseReports: "Segnala" reportAbuse: "Segnala" reportAbuseOf: "Segnala {name}" send: "Inviare" openInNewTab: "Apri in una nuova scheda" +editTheseSettingsMayBreakAccount: "Modificare queste impostazioni può danneggiare l'account." +waitingFor: "Aspettando {x}" random: "Casuale" system: "Sistema" +switchUi: "Cambiare interfaccia utente" desktop: "Desktop" +clip: "Clip" +createNew: "Crea nuov@" optional: "Opzionale" -public: "Pubblico" +createNewClip: "Nuova clip" +public: "Pubblica" +i18nInfo: "Misskey è tradotto in diverse lingue da volontari. Anche tu puoi contribuire su {link}." +notesCount: "Conteggio note" +repliesCount: "Numero di risposte inviate" +renotesCount: "Numero di note che hai ricondiviso" +repliedCount: "Numero di risposte ricevute" +renotedCount: "Numero delle tue note ricondivise" +followingCount: "Numero di account seguiti" +followersCount: "Numero di account che ti seguono" +sentReactionsCount: "Numero di reazioni inviate" +receivedReactionsCount: "Numero di reazioni ricevute" +pollVotesCount: "Numero di voti inviati" +pollVotedCount: "Numero di voti ricevuti" yes: "Sì" no: "No" +driveFilesCount: "Numero di file nel Drive" +noteFavoritesCount: "Conteggio note tra i preferiti" +pageLikesCount: "Numero di pagine che ti piacciono" +pageLikedCount: "Numero delle tue pagine che hanno ricevuto \"Mi piace\"" +reversiCount: "Numero di partite a Reversi" contact: "Contatti" +clips: "Clip" +experimentalFeatures: "Funzioni sperimentali" developer: "Sviluppatore" +showGapBetweenNotesInTimeline: "Mostrare un intervallo tra le note sulla timeline" duplicate: "Duplica" left: "Sinistra" center: "Centro" wide: "Largo" +clearCache: "Svuota cache" +onlineUsersCount: "{n} utenti online" +nUsers: "{n} utenti" nNotes: "{n}Note" +myTheme: "I miei temi" backgroundColor: "Sfondo" +textColor: "Testo" value: "Valore" saveConfirm: "Vuoi salvare le modifiche?" deleteConfirm: "Rimuovere?" @@ -317,45 +610,103 @@ currentVersion: "Versione attuale" latestVersion: "Ultima versione" editCode: "Modifica codice" apply: "Applica" +emailNotification: "Eventi per notifiche via mail" +inChannelSearch: "Cerca in canale" +useReactionPickerForContextMenu: "Cliccare sul tasto destro per aprire il pannello di reazioni" +typingUsers: "{users} sta(nno) scrivendo" +showingPastTimeline: "Stai visualizzando una vecchia timeline" _email: _follow: title: "Ha iniziato a seguirti" + _receiveFollowRequest: + title: "Hai ricevuto una richiesta di follow" _registry: key: "Dati" keys: "Dati" + createKey: "Crea chiave" _aboutMisskey: + source: "Codice sorgente" morePatrons: "Ci sono molti altri che ci sostengono. Grazie 🥰" _mfm: mention: "Menzioni" + mentionDescription: "Si può menzionare un utente specifico digitando il suo nome utente subito dopo il segno @." + hashtag: "Hashtag" url: "URL" link: "Link" bold: "Grassetto" - blockCode: "Codice(blocco)" + blockCode: "Codice (blocco)" inlineMath: "Espressione matematica(Immersione)" - blockMath: "Espressione matematica(blocco)" + blockMath: "Formula matematica (blocco)" quote: "Cita il nota" + emoji: "Emoji personalizzati" search: "Cerca" blur: "Sfocatura" font: "Tipo di carattere" _reversi: + gameSettings: "Impostazioni di gioco" + botSettings: "Opzioni del bot" black: "Nero" white: "Bianco" + total: "Totale" ended: "Esci" +_instanceTicker: + none: "Nascondi" + remote: "Mostra solo per gli/le utenti remotə" + always: "Mostra sempre" _channel: + create: "Nuovo canale" + edit: "Gerisci canale" + setBanner: "Scegli intestazione" + removeBanner: "Rimuovi intestazione" featured: "Tendenze" + owned: "I miei canali" + following: "Seguiti" + usersCount: "{n} partecipanti" + notesCount: "{n} note" _sidebar: - icon: "Foto del profilo" + icon: "Icone" hide: "Nascondere" +_wordMute: + muteWords: "Parole da silenziare" + muteWordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con un'interruzzione riga indica la condizione \"O\"." + muteWordsDescription2: "Metti le parole chiavi tra slash per usare espressioni regolari (regexp)." + mutedNotes: "Note silenziate" _theme: + explore: "Esplora temi" + install: "Installa un tema" + manage: "Gerisci temi" + code: "Codice tema" + installed: "{name} è installato" + installedThemes: "Temi installati" + builtinThemes: "Temi integrati" + alreadyInstalled: "Questo tema è già installato" + invalid: "Il formato tema non è valido" + make: "Crea un tema" + base: "Base" + addConstant: "Aggiungi costante" constant: "Costante" defaultValue: "Valore predefinito" color: "Colore" + key: "Chiave" func: "Funzione" + argument: "Argomento" darken: "Scuro" lighten: "Chiaro" keys: bg: "Sfondo" + fg: "Testo" + focus: "Focalizzazione" + indicator: "Indicatore" + panel: "Pannello" shadow: "Ombra" + header: "Intestazione" + navBg: "Sfondo della barra laterale" + navFg: "Testo della barra laterale" + navHoverFg: "Testo della barra laterale (al passaggio del mouse)" + navActive: "Testo della barra laterale (attivo)" + navIndicator: "Indicatore di barra laterale" + link: "Link" + hashtag: "Hashtag" mention: "Menzioni" renote: "Rinota" divider: "Interruzione di linea" @@ -363,6 +714,8 @@ _sfx: note: "Nota" notification: "Notifiche" chat: "Messaggi" + antenna: "Ricezione dell'antenna" + channel: "Notifiche di canale" _ago: unknown: "Sconosciuto" future: "Futuro" @@ -381,14 +734,51 @@ _time: day: "giorni" _tutorial: title: "Come usare Misskey" - step1_1: "Benvenuto" + step1_1: "Benvenuto/a!" + step1_2: "Questa pagina si chiama una \" Timeline \". Mostra in ordine cronologico le \" note \" delle persone che segui." + step1_3: "Attualmente la tua Timeline è vuota perché non segui alcun account e non hai pubblicato alcuna nota ancora." + step2_1: "Prima di scrivere una nota o di seguire un account, imposta il tuo profilo!" + step2_2: "Aggiungere qualche informazione su di te aumenterà le tue possibilità di essere seguit@ da altre persone. " + step3_1: "Hai finito di impostare il tuo profilo?" + step3_2: "Ora, puoi pubblicare una nota. Facciamo una prova! Premi il pulsante a forma di penna in cima allo schermo per aprire una finestra di dialogo. " + step3_3: "Scritto il testo della nota, puoi pubblicarla premendo il pulsante nella parte superiore destra della finestra di dialogo." + step3_4: "Non ti viene niente in mente? Perché non scrivi semplicemente \"Ho appena cominciato a usare Misskey\"?" + step4_1: "Hai pubblicato qualcosa?" + step4_2: "Se puoi visualizzare la tua nota sulla timeline, ce l'hai fatta!" + step5_1: "Adesso, cerca di seguire altre persone per vivacizzare la tua timeline. " + step5_2: "La pagina {featured} mostra le note di tendenza su questa istanza e, sfogliandole, magari toverai degli account che ti piacciono e che vorrai seguire. Oppure, potrai trovare utenti popolari usando {explore}." + step5_3: "Per seguire altrə utenti, clicca sul loro avatar per aprire la pagina di profilo dove puoi premere il pulsante \"Seguire\". " + step5_4: "Alcunə utenti scelgono di confermare manualmente le richieste di follow che ricevono, quindi a seconda delle persone potrebbe volerci un pò prima che la tua richiesta sia accolta." + step6_1: "Ora, se puoi visualizzare le note di altrə utenti sulla tua timeline, ce l'hai fatta!" + step6_2: "Puoi inviare una risposta rapida alle note di altrə utenti mandando loro \"reazioni\"." + step6_3: "Per inviare una reazione, premi l'icona + della nota e scegli l'emoji che vuoi mandare." + step7_1: "Complimenti! Sei arrivat@ alla fine dell'esercitazione di base su come usare Misskey. " + step7_2: "Se vuoi saperne di più su Misskey, puoi dare un'occhiata alla sezione {help}." + step7_3: "Da ultimo, buon divertimento su Misskey! 🚀" _permissions: - "read:blocks": "Visualizza gli account che hai bloccato." - "write:blocks": "Gestisci gli account che hai bloccato." - "read:favorites": "Visualizza Preferiti" - "write:favorites": "Gestisci Preferiti" + "read:blocks": "Visualizza gli account bloccati" + "write:blocks": "Gestisci gli account bloccati" + "read:favorites": "Visualizza i tuoi preferiti" + "write:favorites": "Gestisci i tuoi preferiti" + "read:following": "Vedi le informazioni di follow" "write:following": "Seguiti/ Smetti di seguire" + "read:mutes": "Vedi account silenziati" + "write:mutes": "Gerisci account silenziati" + "write:notes": "Creare / Eliminare note" "read:notifications": "Visualizza notifiche" + "write:notifications": "Gerisci notifiche" + "read:reactions": "Vedi reazioni" + "write:reactions": "Gerisci reazioni" + "read:user-groups": "Vedi gruppi di utenti" + "write:user-groups": "Gestisci gruppi di utenti" + "read:channels": "Visualizza canali" + "write:channels": "Gerisci canali" +_antennaSources: + all: "Tutte le note" + homeTimeline: "Note dagli utenti che segui" + users: "Note dagli utenti selezionati" + userList: "Note dagli utenti della lista selezionata" + userGroup: "Note dagli utenti del gruppo selezionato" _weekday: sunday: "Domenica" monday: "Lunedì" @@ -409,6 +799,9 @@ _widgets: photos: "Foto" digitalClock: "Orologio digitale" federation: "Federazione" + button: "Pulsante" + onlineUsers: "Utenti online" + jobQueue: "Coda di lavoro" _cw: hide: "Nascondere" show: "Mostra di più" @@ -422,14 +815,20 @@ _poll: voted: "Votato" closed: "Terminato" _visibility: - public: "Pubblico" + public: "Pubblica" + publicDescription: "Visibile per tutti sul Fediverso" home: "Home" - followers: "Seguaci" - localOnly: "Solo Locale" - localOnlyDescription: "Solo locale" + homeDescription: "Visibile solo sulla timeline \"Home\"" + followers: "Followers" + followersDescription: "Visibile solo per i tuoi followers" + specified: "Diretta" + specifiedDescription: "Visibile solo per gli/le utenti menzionatə" + localOnly: "Soltanto locale" + localOnlyDescription: "Nascosta per gli/le utenti remotə" _postForm: replyPlaceholder: "Nota la tua risposta.." quotePlaceholder: "Cita Nota..." + channelPlaceholder: "Pubblica in canale" _profile: name: "Nome" username: "Nome utente" @@ -437,16 +836,29 @@ _profile: metadata: "Metadati" metadataLabel: "Etichetta" metadataContent: "Contenuto" + changeBanner: "Cambia intestazione" _exportOrImport: - followingList: "Seiguiti" - muteList: "Silenzia" - blockingList: "Blocca" + allNotes: "Tutte le note" + followingList: "Follows" + muteList: "Account silenziati" + blockingList: "Account bloccati" userLists: "Liste" +_charts: + usersIncDec: "Variazione del numero di utenti" + usersTotal: "Numero totale di utenti" + activeUsers: "Numero di utenti attivi" + notesTotal: "Conteggio totale di note" +_instanceCharts: + users: "Variazione del numero di utenti" + usersTotal: "Totale cumulativo di utenti" _timelines: home: "Home" local: "Locale" + social: "Sociale" _rooms: + roomOf: "Camera di {user}" _roomType: + default: "Predefinito" washitsu: "Washitsu" _furnitures: milk: "Cartone del latte" @@ -477,31 +889,59 @@ _rooms: photoframe: "Cornice" cube: "Cubo" tv: "Televisore" - pinguin: "Pinguini" + pinguin: "Pinguino" bin: "Cestino" cup-noodle: "Noodle istantanei" _pages: + created: "Pagina creata!" + pageSetting: "Impostazioni pagina" + viewSource: "Visualizza sorgente" like: "Mi piace" unlike: "Togli Mi piace" + featured: "Popolari" + content: "Blocco di pagina" variables: "Variabili" title: "Titolo" + hideTitleWhenPinned: "Nascondere il titolo pagina quando è fissata in cima al profilo." font: "Tipo di carattere" + chooseBlock: "Aggiungi blocco" blocks: + text: "Testo" + textarea: "Area di testo" + section: "Sezione" image: "Immagini" + button: "Pulsante" if: "Se" _if: variable: "Variabili" _post: text: "Contenuto" _textInput: + name: "Nome della variabile" text: "Titolo" + default: "Valore predefinito" _textareaInput: + name: "Nome della variabile" text: "Titolo" + default: "Valore predefinito" _numberInput: + name: "Nome della variabile" text: "Titolo" + default: "Valore predefinito" + _canvas: + width: "Larghezza" + height: "Altezza" + note: "Nota integrata" + _note: + id: "ID nota" + idDescription: "Qui puoi anche incollare l'URL della nota che vuoi impostare." + detailed: "Visualizzazione dettagliata" _switch: + name: "Nome della variabile" text: "Titolo" + default: "Valore predefinito" _counter: + name: "Nome della variabile" text: "Titolo" _button: text: "Titolo" @@ -509,7 +949,9 @@ _pages: _dialog: content: "Contenuto" _radioButton: + name: "Nome della variabile" title: "Titolo" + default: "Valore predefinito" script: categories: comparison: "Metodo comparativo" @@ -518,6 +960,15 @@ _pages: fn: "Funzione" list: "Liste" blocks: + text: "Testo" + _strLen: + arg1: "Testo" + _strPick: + arg1: "Testo" + _strReplace: + arg1: "Testo" + _strReverse: + arg1: "Testo" _join: arg1: "Liste" _add: @@ -575,26 +1026,46 @@ _pages: arg1: "Liste" _listLen: arg1: "Liste" + _stringToNumber: + arg1: "Testo" + _splitStrByLine: + arg1: "Testo" ref: "Variabili" fn: "Funzione" types: + string: "Testo" array: "Liste" _notification: + fileUploaded: "File caricato correttamente" + youGotMention: "{name} ti ha menzionato" + youGotReply: "{name} ti ha risposto" youGotQuote: "{name} ha citato il tuo Nota e ha detto" - youRenoted: "{name} ha rinota" + youRenoted: "{name} ha rinotato" youGotPoll: "{name} ha volluto." + youGotMessagingMessageFromUser: "{name} ti ha mandato un messaggio" + youGotMessagingMessageFromGroup: "{name} ti ha mandato un messaggio nella chat" youWereFollowed: "Ha iniziato a seguirti" + youReceivedFollowRequest: "Hai ricevuto una richiesta di follow" + yourFollowRequestAccepted: "La tua richiesta di follow è stata accettata" + youWereInvitedToGroup: "Invitat@ al gruppo" _types: all: "Tutto" - follow: "Seiguiti" + follow: "Follows" mention: "Menzioni" reply: "Rispondi" renote: "Rinota" quote: "Cita" reaction: "Reazione" + pollVote: "Voti ricevuti" + receiveFollowRequest: "Richiesta di follow ricevuta" + followRequestAccepted: "Richiesta di follow accettata" + groupInvited: "Invito a un gruppo" + app: "Notifiche da applicazioni" _deck: _columns: notifications: "Notifiche" tl: "Timeline" + antenna: "Antenne" list: "Liste" mentions: "Menzioni" + direct: "Diretta" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 0015db07b9..1a7001269a 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -81,7 +81,7 @@ pageLoadError: "ページの読み込みに失敗してしもうたで…" pageLoadErrorDescription: "これは普通、ネットワークかブラウザキャッシュが原因やからね。キャッシュをクリアするか、もうちっとだけ待ってくれへんか?" enterListName: "リスト名を入れてや" privacy: "プライバシー" -makeFollowManuallyApprove: "ええって言わなフォローできへんようにする" +makeFollowManuallyApprove: "自分が認めた人だけがこのアカウントをフォローできるようにする" defaultNoteVisibility: "もとからの公開範囲" follow: "フォロー" followRequest: "フォローを頼む" @@ -308,7 +308,7 @@ monthX: "{month}月" yearX: "{year}年" pages: "ページ" integration: "連携" -connectSerice: "つなげる" +connectSerice: "つなぐ" disconnectSerice: "切ってまう" enableLocalTimeline: "ローカルタイムラインを使えるようにする" enableGlobalTimeline: "グローバルタイムラインを使えるようにする" @@ -392,7 +392,7 @@ markAsReadAllUnreadNotes: "投稿は全て読んだわっ" markAsReadAllTalkMessages: "チャットはもうぜんぶ読んだわっ" help: "ヘルプ" inputMessageHere: "ここにメッセージ書いてや" -close: "さいなら" +close: "閉じる" group: "グループ" groups: "グループ" createGroup: "グループを作るで" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index eb392a27bb..6893213409 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -241,7 +241,7 @@ explore: "Обзор" games: "Игры Misskey" messageRead: "Прочитали" noMoreHistory: "История закончилась" -startMessaging: "Отправить сообщение" +startMessaging: "Начать общение" nUsersRead: "Прочитали {n}" agreeTo: "Я соглашаюсь с {0}" tos: "Пользовательское соглашение" @@ -329,7 +329,7 @@ pinnedUsers: "Прикреплённый пользователь" pinnedUsersDescription: "Перечислите по одному имени пользователя в строке. Пользователи, перечисленные здесь, будут привязаны к закладке \"Изучение\"." pinnedPages: "Закрепленные страницы" pinnedPagesDescription: "Если хотите закрепить страницы на главной сайта, сюда можно добавить пути к ним, каждый в отдельной строке." -pinnedClipId: "Идентификатор закреплённой памятки" +pinnedClipId: "Идентификатор закреплённой подборки" pinnedNotes: "Закреплённая заметка" hcaptcha: "hCaptcha" enableHcaptcha: "Включить hCaptcha" @@ -405,8 +405,8 @@ invites: "Приглашения" groupName: "Название группы" members: "Участники" transfer: "Отдать" -messagingWithUser: "Сообщения пользователей" -messagingWithGroup: "Чат в группе" +messagingWithUser: "Общение с другим пользователем" +messagingWithGroup: "Общение в группе" title: "Заголовок" text: "Текст" enable: "Включить" @@ -471,7 +471,7 @@ promotion: "Продвинуто" promote: "Продвинуть" numberOfDays: "Количество дней" hideThisNote: "Спрятать эту запись" -showFeaturedNotesInTimeline: "Показывать в ленте заметки из подборки сайта" +showFeaturedNotesInTimeline: "Показывать в ленте заметки из «Горячего»" objectStorage: "Хранилище" useObjectStorage: "Занято в хранилище" objectStorageBaseUrl: "Базовый адрес" @@ -524,7 +524,7 @@ deleteAllFiles: "Удалить все файлы" deleteAllFilesConfirm: "Вы хотите удалить все файлы?" removeAllFollowing: "Удалить всех подписчиков" removeAllFollowingDescription: "Отменить все подписки с домена {host}? Пожалуйста, применяйте это действие, если инстанс больше не существует." -userSuspended: "Этот пользователь был заморожен" +userSuspended: "Эта учётная запись заморожена" userSilenced: "Этот пользователь был заглушен" sidebar: "Боковая панель" divider: "Линия-разделитель" @@ -623,30 +623,30 @@ random: "Случайные" system: "Система" switchUi: "Выбор вида" desktop: "Стол" -clip: "В памятку" +clip: "В подборку" createNew: "Новый документ" optional: "Необязательно" -createNewClip: "Новая памятка" +createNewClip: "Новая подборка" public: "Общедоступно" i18nInfo: "Misskey переводят на разные языки добровольцы со всего света. Ваша помощь тоже пригодится здесь: {link}." manageAccessTokens: "Управление токенами доступа" accountInfo: "Сведения об учётной записи" notesCount: "Количество заметок" repliesCount: "Сколько раз пользователь кому-то ответил" -renotesCount: "Сколько раз пользователь передал чужие заметки" +renotesCount: "Сколько раз пользователь делился заметками" repliedCount: "Сколько раз ответили пользователю" -renotedCount: "Сколько раз передавали заметки пользователя" +renotedCount: "Сколько раз делились заметками пользователя" followingCount: "Количество подписок" followersCount: "Количество подписавшихся" -sentReactionsCount: "Сколько раз пользователь отреагировал" -receivedReactionsCount: "Сколько раз отреагировали на заметки пользователя" -pollVotesCount: "Сколько раз участвовал в опросах" +sentReactionsCount: "Количество реакций пользователя" +receivedReactionsCount: "Количество реакций на заметки пользователя" +pollVotesCount: "Сколько раз пользователь участвовал в опросах" pollVotedCount: "Сколько раз участвовали в опросах пользователя" yes: "Да" no: "Нет" driveFilesCount: "Количество файлов на диске" -driveUsage: "Сколько места занято на диске" -noCrawle: "Паукам вход воспрещён" +driveUsage: "Занято места на диске" +noCrawle: "Запретить паукам индексировать сайт" noCrawleDescription: "Просьба поисковым системам не ходить по вашему профилю, по заметкам, страницам и не индексировать их." lockedAccountInfo: "Даже если вы вручную подтверждаете подписки, кто угодно может читать ваши заметки, если вы не отмечаете их «для подписчиков»." alwaysMarkSensitive: "Отмечать файлы как «содержимое не для всех» по умолчанию" @@ -661,7 +661,7 @@ pageLikedCount: "Количество страниц, понравившихся reversiCount: "Количество сыгранных игр в реверси" contact: "Как связаться" useSystemFont: "Использовать шрифт, предлагаемый системой" -clips: "Памятки" +clips: "Подборки" experimentalFeatures: "Экспериментальные функции" developer: "Разработчик" makeExplorable: "Опубликовать профиль в «Обзоре»." @@ -983,7 +983,7 @@ _tutorial: step7_2: "Хотите изучить Misskey глубже — добро пожаловать в раздел «{help}»." step7_3: "Приятно вам провести время с Misskey🚀" _2fa: - alreadyRegistered: "Настройка завершена" + alreadyRegistered: "Двухфакторная аутентификация уже настроена." registerDevice: "Зарегистрируйте ваше устройство" registerKey: "Зарегистрировать ключ" step1: "Прежде всего, установите на устройство приложение для аутентификации, например, {a} или {b}." @@ -1240,7 +1240,7 @@ _pages: liked: "Понравившиеся страницы" featured: "Популярные" inspector: "Инспектор" - contents: "Содержательные" + contents: "Содержимое" content: "Содержимое" variables: "Переменные" title: "Заголовок" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index c13cf12220..d6da577982 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -579,7 +579,7 @@ smtpPort: "端口" smtpUser: "用户名" smtpPass: "密码" emptyToDisableSmtpAuth: "用户名和密码留空可以禁用SMTP验证" -smtpSecure: "在 SMTP 连接中默认使用 SSL / TLS" +smtpSecure: "在 SMTP 连接中使用隐式 SSL / TLS" smtpSecureInfo: "使用STARTTLS时关闭。" testEmail: "邮件发送测试" wordMute: "文字屏蔽" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 9d95c958d1..dfae988f1b 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -438,6 +438,7 @@ signinWith: "以{x}登錄" signinFailed: "登入失敗。 請檢查用戶名和密碼。" tapSecurityKey: "點擊安全密鑰" or: "或者" +language: "語言" uiLanguage: "介面語言" groupInvited: "您有新的群組邀請" aboutX: "關於{x}" @@ -677,9 +678,12 @@ newVersionOfClientAvailable: "新版本的用戶端可用。" usageAmount: "使用量" capacity: "容量" inUse: "已使用" +clear: "清除" _email: _follow: title: "您有新的追隨者" +_plugin: + manage: "管理插件" _registry: scope: "範圍" key: "機碼" @@ -702,7 +706,9 @@ _nsfw: _mfm: cheatSheet: "MFM代碼小抄" intro: "MFM是Misskey專用的標記語言,可以在Misskey中的各個位置使用。 您可以這裏看到MFM可用語法列表。" + dummy: "通過Misskey擴展Fediverse的世界" mention: "提及" + mentionDescription: "透過 @+用戶名 來標示特定使用者。" hashtag: "#tag" url: "URL" link: "鏈接" diff --git a/src/docs/fr-FR/api.md b/src/docs/fr-FR/api.md index 76019b6145..0147f95bac 100644 --- a/src/docs/fr-FR/api.md +++ b/src/docs/fr-FR/api.md @@ -1,58 +1,58 @@ -# Misskey API +# API de Misskey -MisskeyAPIを使ってMisskeyクライアント、Misskey連携Webサービス、Bot等(以下「アプリケーション」と呼びます)を開発できます。 ストリーミングAPIもあるので、リアルタイム性のあるアプリケーションを作ることも可能です。 +Vous pouvez utiliser l'API de Misskey pour développer des clients Misskey, des services web s'intégrant à Misskey, des Bots (que nous appellerons plus loin "Applications"), etc. Comme l'API streaming est aussi implémenté, vous avez la possibilité de créer des applications de temps réel. -APIを使い始めるには、まずアクセストークンを取得する必要があります。 このドキュメントでは、アクセストークンを取得する手順を説明した後、基本的なAPIの使い方を説明します。 +Pour pouvoir vous servir de l'API, il vous faudra d'abord obtenir un jeton d'accès. Ce guide a été conçu pour vous accompagner dans le processus d'obtention du jeton d'accès, puis donner des instructions de base sur l'utilisation de l'API. -## アクセストークンの取得 -基本的に、APIはリクエストにはアクセストークンが必要となります。 APIにリクエストするのが自分自身なのか、不特定の利用者に使ってもらうアプリケーションなのかによって取得手順は異なります。 +## Obtenir le jeton d'accès +Une requête d'API, par essence, nécessite un jeton d'accès. La procédure d'acquisition du jeton diffère selon que vous effectuez la requête vous-même, ou qu'elle est envoyée via une application par un utilisateur final non défini. -* 前者の場合: [「自分自身のアクセストークンを手動発行する」](#自分自身のアクセストークンを手動発行する)に進む -* 後者の場合: [「アプリケーション利用者にアクセストークンの発行をリクエストする」](#アプリケーション利用者にアクセストークンの発行をリクエストする)に進む +* Dans le premier cas : allez à [« Générer manuellement un jeton d'accès pour son propre compte »](#自分自身のアクセストークンを手動発行する). +* Dans le second cas : allez à [« Demander la génération du jeton d'accès via un utilisateur d'application »](#アプリケーション利用者にアクセストークンの発行をリクエストする). -### 自分自身のアクセストークンを手動発行する -「設定 > API」で、自分のアクセストークンを発行できます。 +### Générer manuellement un jeton d'accès pour son propre compte +Vous pouvez générer votre propre jeton d'accès en allant dans { Paramètres > API }. -[「APIの使い方」へ進む](#APIの使い方) +[Continuer avec « Utiliser l'API ».](#APIの使い方) -### アプリケーション利用者にアクセストークンの発行をリクエストする -アプリケーション利用者のアクセストークンを取得するには、以下の手順で発行をリクエストします。 +### Demander la génération du jeton d'accès via un utilisateur d'application +Pour obtenir un jeton d'accès pour le compte utilisateur final de votre application, suivez la procédure de génération ci-dessous. -#### Step 1 +#### Étape 1 -UUIDを生成する。以後これをセッションIDと呼びます。 +Générez un UUID. Nous l'appellerons « ID de session » dans la suite de ce guide. -> このセッションIDは毎回生成し、使いまわさないようにしてください。 +> Un même ID de session ne devrait pas être utilisé plusieurs fois ; veillez à en générer un nouveau pour chaque jeton d'accès. -#### Step 2 +#### Étape 2 -`{_URL_}/miauth/{session}`をユーザーのブラウザで表示させる。`{session}`の部分は、セッションIDに置き換えてください。 -> 例: `{_URL_}/miauth/c1f6d42b-468b-4fd2-8274-e58abdedef6f` +Ouvrez l'adresse `{_URL_}/miauth/{session}` dans le navigateur de l'utilisateur. Remplacez alors la partie `{session}` de l'URL par l'ID de session que vous venez de générer. +> Par ex. : `{_URL_}/miauth/c1f6d42b-468b-4fd2-8274-e58abdedef6f` -表示する際、URLにクエリパラメータとしていくつかのオプションを設定できます: -* `name` ... アプリケーション名 - * > 例: `MissDeck` -* `icon` ... アプリケーションのアイコン画像URL - * > 例: `https://missdeck.example.com/icon.png` -* `callback` ... 認証が終わった後にリダイレクトするURL - * > 例: `https://missdeck.example.com/callback` - * リダイレクト時には、`session`というクエリパラメータでセッションIDが付きます -* `permission` ... アプリケーションが要求する権限 - * > 例: `write:notes,write:following,read:drive` - * 要求する権限を`,`で区切って列挙します - * どのような権限があるかは[APIリファレンス](/api-doc)で確認できます +En ouvrant cette URL, vous pourrez configurer un certain nombre d'options pour les paramètres de requête : +* `name` : nom de l'application + * > Ex. : `MissDeck` +* `icon` : URL de l'icône de l'application + * > Ex. : `https://missdeck.example.com/icon.png` +* `callback` : URL de redirection après l'authentification + * > Ex. : `https://missdeck.example.com/callback` + * Lors de la redirection, un paramètre de requête `session` contenant l'ID de session sera joint. +* `permission` : permissions requises par l'application + * > Ex. : `write:notes,write:following,read:drive` + * Listez les permissions requises en utilisant une `,` pour les séparer. + * Vous pouvez vérifier quelles sont les permissions disponibles sur [les références API de Misskey](/api-doc). -#### Step 3 -ユーザーが発行を許可した後、`{_URL_}/api/miauth/{session}/check`にPOSTリクエストすると、レスポンスとしてアクセストークンを含むJSONが返ります。 +#### Étape 3 +Si vous envoyez une requête POST à `{_URL_}/api/miauth/{session}/check` une fois que l'utilisateur a validé le jeton d'accès, la réponse arrivera sous forme de fichier JSON contenant ce jeton. -レスポンスに含まれるプロパティ: -* `token` ... ユーザーのアクセストークン -* `user` ... ユーザーの情報 +Propriétés incluses dans la réponse : +* `token` : jeton d'accès de l'utilisateur +* `user` : données de l'utilisateur -[「APIの使い方」へ進む](#APIの使い方) +[Continuer avec « Utiliser l'API ».](#APIの使い方) -## APIの使い方 -**APIはすべてPOSTで、リクエスト/レスポンスともにJSON形式です。RESTではありません。** アクセストークンは、`i`というパラメータ名でリクエストに含めます。 +## Utiliser l'API +**L'API utilise seulement la méthode POST, et toutes les requêtes / réponses sont au format JSON. REST n'est pas pris en charge. ** Le jeton d'accès s'insère dans le paramètre de requête nommé `i`. -* [APIリファレンス](/api-doc) -* [ストリーミングAPI](./stream) +* [Références API de Misskey](/api-doc) +* [API streaming](./stream) diff --git a/src/docs/fr-FR/create-plugin.md b/src/docs/fr-FR/create-plugin.md index 540909de42..c0d5fa9c57 100644 --- a/src/docs/fr-FR/create-plugin.md +++ b/src/docs/fr-FR/create-plugin.md @@ -34,7 +34,7 @@ Misskey Webクライアントのプラグイン機能を使うと、クライア #### default 設定のデフォルト値 -## APIリファレンス +## Références API de Misskey AiScript標準で組み込まれているAPIは掲載しません。 ### Mk:dialog(title text type) diff --git a/src/docs/fr-FR/follow.md b/src/docs/fr-FR/follow.md index 5ef74c0903..fde5c5e471 100644 --- a/src/docs/fr-FR/follow.md +++ b/src/docs/fr-FR/follow.md @@ -1,2 +1,2 @@ # Abonnements -Lorsque vous suivez un·e utilisateur·rice, ses publications apparaissent dans votre fil.Cela n'inclut toutefois pas ses réponses aux autres utilisateur·ice·s. Vous pouvez vous désabonner du compte en cliquant une seconde fois. +Lorsque vous suivez un·e utilisateur·rice, ses publications apparaissent dans votre fil.Cela n'inclut toutefois pas ses réponses aux autres utilisateur·ice·s. Pour suivre un compte, rendez-vous sur sa page et cliquez sur le bouton « s'abonner ». Vous pouvez vous désabonner du compte en cliquant une seconde fois. diff --git a/src/docs/fr-FR/pages.md b/src/docs/fr-FR/pages.md index 9021d41b1c..0dbafb806e 100644 --- a/src/docs/fr-FR/pages.md +++ b/src/docs/fr-FR/pages.md @@ -3,8 +3,8 @@ ## Variables Vous pouvez créer des pages dynamiques en utilisant des variables.Vous pouvez incorporer la valeur d'une variable en insérant le <b>{ variablename }</b> dans votre texte.Par exemple, si la valeur de la variable "thing" dans le texte <b>Hello { thing } world!</b> est <b>ai</b>, votre trexte devient alors : <b>Hello ai world!</b>. -Les variables sont évaluées du haut vers le bas, il n'est donc pas possible de référencer une variable située plus bas que celle en cours.Par exemple, si vous définissez, dans l'ordre, 3 variables telles que <b>A、B、C</b>, vous pourrez référencer en <b>C</b> aussi bien <b>A</b> que <b>B</b> ; par contre, vous ne pourrez référencer en <b>A</b> ni <b>B</b> ni <b>C</b>. +Les variables sont prises en compte dans l'ordre chronologique, de haut en bas. Il n'est donc pas possible d'appeler une variable située plus bas dans le code. Par exemple, si vous définissez, dans l'ordre, 3 variables telles que <b>A, B, C</b>, vous pourrez appeler en <b>C</b> aussi bien <b>A</b> que <b>B</b> ; par contre, vous ne pourrez appeler en <b>A</b> ni <b>B</b> ni <b>C</b>. Pour recevoir une entrée utilisateur, ajoutez un bloc "Entrée" sur la page et définissez le nom des variables que vous souhaitez stocker dans le champ "Nom de la variable" (les variables seront créées automatiquement).Vous pourrez alors exécuter les actions en fonction de l'entrée utilisateur de ces variables. -Utiliser des fonctions vous permettra de mettre en place une façon de calculer des valeurs que vous pourrez réutiliser.Pour créer des fonctions, il faut d'abord définir une variable du type "fonction".Ensuite, vous pouvez configurer des arguments dont la valeur sera utilisable comme une variable à l'intérieur de la fonction. Par ailleurs, il existe ce que l'on appelle des "fonctions d'ordre supérieur" dont les arguments sont aussi des fonctions. En plus de paramétrer des fonctions à l'avance, vous avez également la possibilité de définir des fonctions à l'improviste directement dans les arguments de ces "fonctions d'ordre supérieur". +Appeler des fonctions vous permet de définir des valeurs que vous pourrez réutiliser. Pour créer des fonctions, il faut d'abord définir une variable du type "fonction". Vous pouvez y configurer des « slots » (arguments), dont la valeur devient alors disponible en tant que variable à l'intérieur de la fonction. Par ailleurs, il existe ce que l'on appelle des "fonctions d'ordre supérieur" dont les arguments sont aussi des fonctions. En plus de paramétrer des fonctions à l'avance, vous avez également la possibilité de définir des fonctions à l'improviste directement dans les « slots » de ces fonctions d'ordre supérieur. diff --git a/src/docs/fr-FR/stream.md b/src/docs/fr-FR/stream.md index 04dad66799..55e6bc6806 100644 --- a/src/docs/fr-FR/stream.md +++ b/src/docs/fr-FR/stream.md @@ -1,4 +1,4 @@ -# ストリーミングAPI +# API streaming ストリーミングAPIを使うと、リアルタイムで様々な情報(例えばタイムラインに新しい投稿が流れてきた、メッセージが届いた、フォローされた、など)を受け取ったり、様々な操作を行ったりすることができます。 diff --git a/src/docs/fr-FR/theme.md b/src/docs/fr-FR/theme.md index 72c60f1c6b..cf15c921e4 100644 --- a/src/docs/fr-FR/theme.md +++ b/src/docs/fr-FR/theme.md @@ -6,7 +6,7 @@ Vous pouvez modifier l'apparence de votre client Misskey à l'aide de thèmes. Paramètres > Thèmes ## Créer un thème -Les codes des thèmes sont écrits sous forme d'objets JSON5. Les thèmes comprennent les objets suivants : +Le code des thèmes est écrit sous forme d'objets JSON5. Les thèmes comprennent les objets suivants : ``` js { id: '17587283-dd92-4a2c-a22c-be0637c9e22a', @@ -43,7 +43,7 @@ Les codes des thèmes sont écrits sous forme d'objets JSON5. Les thèmes compre * `props` ... Définir un style de thème.Voir les explications ci-après. ### Définir un style de thème -C'est dans `props` que vous définirez le style de thème. Les propriétés deviendront des variables CSS et les valeurs spécifieront le contenu. Par ailleurs, les objets présents par défaut dans `props` sont hérités du thème de base. Ainsi, si le thème de `base` est clair `light` ce sera l'objet [_light.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_light.json5) ; et s'il est sombre `dark` ce sera l'objet [_dark.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_dark.json5). Cela signifie, par exemple, que s'il n'y pas de propriété `panel` définie dans les `props` du thème, alors ce sera la valeur `panel` du thème de base qui sera prise en compte. +C'est dans `props` que vous définirez le style de thème. Les propriétés deviendront des variables CSS et les valeurs associées spécifieront le contenu de ces variables. Par ailleurs, les objets présents par défaut dans `props` sont hérités du thème de base. Ainsi, si le thème de `base` est clair `light` ce sera l'objet [_light.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_light.json5) ; et s'il est sombre `dark` ce sera l'objet [_dark.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_dark.json5). Cela signifie, par exemple, que s'il n'y pas de propriété `panel` définie dans les `props` du thème, alors ce sera la valeur `panel` du thème de base qui sera prise en compte. #### Syntaxe des valeurs * Codes de couleur Hex diff --git a/src/docs/it-IT/custom-emoji.md b/src/docs/it-IT/custom-emoji.md index ed2e92be16..900f115d3c 100644 --- a/src/docs/it-IT/custom-emoji.md +++ b/src/docs/it-IT/custom-emoji.md @@ -1,2 +1,2 @@ -# カスタム絵文字 -カスタム絵文字は、インスタンスで用意された画像を絵文字のように使える機能です。 ノート、リアクション、チャット、自己紹介、名前などの場所で使うことができます。 カスタム絵文字をそれらの場所で使うには、絵文字ピッカーボタン(ある場合)を押すか、`:`を入力して絵文字サジェストを表示します。 テキスト内に`:foo:`のような形式の文字列が見つかると、`foo`の部分がカスタム絵文字名と解釈され、表示時には対応したカスタム絵文字に置き換わります。 +# Emoji personalizzati +Gli emoji personalizzati sono una funzionalità che ti permette di usare delle immagini preparate dalla tua istanza come emoji. Si possono usare in note, reazioni, chat, nella biografia di profilo, nel tuo nome, e altrove su Misskey. Per usare gli emoji personalizzati, puoi aprire la tastiera emoji (quando c'è), oppure visualizzare suggerimenti emoji scrivendo `:`. Quando una sequenza di caratteri del tipo `:foo:` è trovata in un testo, la parte centrale `foo` viene interpretata come un nome di emoji personalizzato e quindi viene sostituita dall'emoji corrispondente. diff --git a/src/docs/it-IT/follow.md b/src/docs/it-IT/follow.md index f636a710f3..c54099a361 100644 --- a/src/docs/it-IT/follow.md +++ b/src/docs/it-IT/follow.md @@ -1,2 +1,2 @@ -# Seiguiti -ユーザーをフォローすると、タイムラインにそのユーザーの投稿が表示されるようになります。ただし、他のユーザーに対する返信は含まれません。 ユーザーをフォローするには、ユーザーページの「フォロー」ボタンをクリックします。フォローを解除するには、もう一度クリックします。 +# Follow +Se segui un utente, le sue pubblicazioni verranno mostrate sulla tua timeline. A esclusione delle sue risposte ad altrə utenti. Per seguire un utente, bisogna premere il pulsante "seguire" della sua pagina. Premi una seconda volta sul pulsante per smettere di seguire l'account. diff --git a/src/docs/it-IT/keyboard-shortcut.md b/src/docs/it-IT/keyboard-shortcut.md index a547e6115e..c9bb815bae 100644 --- a/src/docs/it-IT/keyboard-shortcut.md +++ b/src/docs/it-IT/keyboard-shortcut.md @@ -1,17 +1,17 @@ -# キーボードショートカット +# Scorciatoie da tastiera -## グローバル -これらのショートカットは基本的にどこでも使えます。 +## Generali +Le scorciatoie da tastiera sotto citate si possono usare praticamente ovunque. <table> <thead> - <tr><th>ショートカット</th><th>効果</th><th>由来</th></tr> + <tr><th>Scorciatoia</th><th>Effetto</th><th>Accesso universale</th></tr> </thead> <tbody> - <tr><td><kbd class="key">P</kbd>, <kbd class="key">N</kbd></td><td>新規投稿</td><td><b>P</b>ost, <b>N</b>ew, <b>N</b>ote</td></tr> - <tr><td><kbd class="key">T</kbd></td><td>タイムラインの最も新しい投稿にフォーカス</td><td><b>T</b>imeline, <b>T</b>op</td></tr> - <tr><td><kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">N</kbd></kbd></td><td>通知を表示/隠す</td><td><b>N</b>otifications</td></tr> + <tr><td><kbd class="key">P</kbd>, <kbd class="key">N</kbd></td><td>Nuova pubblicazione</td><td><b>P</b>ost, <b>N</b>ew, <b>N</b>ote</td></tr> + <tr><td><kbd class="key">T</kbd></td><td>Evidenziare l'ultima pubblicazione sulla timeline</td><td><b>T</b>imeline, <b>T</b>op</td></tr> + <tr><td><kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">N</kbd></kbd></td><td>Mostrare/nascondere notifiche</td><td><b>N</b>otifications</td></tr> <tr><td><kbd class="key">S</kbd></td><td>Cerca</td><td><b>S</b>earch</td></tr> - <tr><td><kbd class="key">H</kbd>, <kbd class="key">?</kbd></td><td>ヘルプを表示</td><td><b>H</b>elp</td></tr> + <tr><td><kbd class="key">H</kbd>, <kbd class="key">?</kbd></td><td>Visualizzare l'aiuto</td><td><b>H</b>elp</td></tr> </tbody> </table> @@ -19,7 +19,7 @@ <table> <thead> - <tr><th>ショートカット</th><th>効果</th><th>由来</th></tr> + <tr><th>Scorciatoia</th><th>Effetto</th><th>Accesso universale</th></tr> </thead> <tbody> <tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</kbd>, <kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">Tab</kbd></kbd></td><td>上の投稿にフォーカスを移動</td><td>-</td></tr> @@ -37,24 +37,24 @@ </tbody> </table> -## Renoteフォーム +## Finestra Rinota <table> <thead> - <tr><th>ショートカット</th><th>効果</th><th>由来</th></tr> + <tr><th>Scorciatoia</th><th>Effetto</th><th>Accesso universale</th></tr> </thead> <tbody> - <tr><td><kbd class="key">Enter</kbd></td><td>Renoteする</td><td>-</td></tr> - <tr><td><kbd class="key">Q</kbd></td><td>フォームを展開する</td><td><b>Q</b>uote</td></tr> - <tr><td><kbd class="key">Esc</kbd></td><td>フォームを閉じる</td><td>-</td></tr> + <tr><td><kbd class="key">Enter</kbd></td><td>Rinotare</td><td>-</td></tr> + <tr><td><kbd class="key">Q</kbd></td><td>Aprire finestra</td><td><b>Q</b>uote</td></tr> + <tr><td><kbd class="key">Esc</kbd></td><td>Chiudere finestra</td><td>-</td></tr> </tbody> </table> -## リアクションフォーム -デフォルトで「👍」にフォーカスが当たっている状態です。 +## Pannello reazioni +La reazione "👍" è impostata come reazione predefinita. <table> <thead> - <tr><th>ショートカット</th><th>効果</th><th>由来</th></tr> + <tr><th>Scorciatoia</th><th>Effetto</th><th>Accesso universale</th></tr> </thead> <tbody> <tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</kbd></td><td>上のリアクションにフォーカスを移動</td><td>-</td></tr> diff --git a/src/docs/it-IT/mute.md b/src/docs/it-IT/mute.md index a6cb073755..d0fa672f4a 100644 --- a/src/docs/it-IT/mute.md +++ b/src/docs/it-IT/mute.md @@ -1,13 +1,13 @@ -# Silenzia +# Silenziare -ユーザーをミュートすると、そのユーザーに関する次のコンテンツがMisskeyに表示されなくなります: +Quando si silenzia un utente, i successivi contenuti che lo riguardano non saranno più visualizzati su Misskey: -* タイムラインや投稿の検索結果内の、そのユーザーの投稿(およびそれらの投稿に対する返信やRenote) -* そのユーザーからの通知 -* メッセージ履歴一覧内の、そのユーザーとのメッセージ履歴 +* le pubblicazioni dell'utente sia nelle timeline che nei risultati di ricerca, così come le sue risposte e Rinote; +* le notifiche riguardo all'utente; +* la cronologia dei messaggi scambiati con l'utente nella chat. -ユーザーをミュートするには、対象のユーザーのユーザーページに表示されている「ミュート」ボタンを押します。 +Per silenziare un utente, premi il pulsante "Silenzia" che si trova sulla sua pagina profilo. -ミュートを行ったことは相手に通知されず、ミュートされていることを知ることもできません。 +Gli utenti silenziati da te non vengono informati; nello stesso modo, tu non sarai infomat@ se vieni silenziat@ da un altr@ utente. -設定>ミュート から、自分がミュートしているユーザー一覧を確認することができます。 +Puoi controllare la lista di utenti che hai silenziato nelle Impostazioni account > "Silenziati / Bloccati". diff --git a/src/docs/it-IT/pages.md b/src/docs/it-IT/pages.md index 81c19dd20a..5a34971f1a 100644 --- a/src/docs/it-IT/pages.md +++ b/src/docs/it-IT/pages.md @@ -1,4 +1,4 @@ -# Pages +# Pagine ## Variabili 変数を使うことで動的なページを作成できます。テキスト内で <b>{ 変数名 }</b> と書くとそこに変数の値を埋め込めます。例えば <b>Hello { thing } world!</b> というテキストで、変数(thing)の値が <b>ai</b> だった場合、テキストは <b>Hello ai world!</b> になります。 diff --git a/src/docs/it-IT/reaction.md b/src/docs/it-IT/reaction.md index eac6cc88e9..f88dd99c3e 100644 --- a/src/docs/it-IT/reaction.md +++ b/src/docs/it-IT/reaction.md @@ -1,11 +1,11 @@ -# Reazione -他の人のノートに、絵文字を付けて簡単にあなたの反応を伝えられる機能です。 リアクションするには、ノートの + アイコンをクリックしてピッカーを表示し、絵文字を選択します。 リアクションには[カスタム絵文字](./custom-emoji)も使用できます。 +# Reazioni +Puoi mandare una reazione rapida alle note degli/delle altrə utenti apponendoci emoji. Per reagire, premi il pulsante "+" della nota per aprire il pannello reazioni e scegliere un emoji. Si possono anche usare gli [emoji personalizzati](./custom-emoji). -## リアクションピッカーのカスタマイズ -ピッカーに表示される絵文字を自分好みにカスタマイズすることができます。 設定の「リアクション」で設定します。 +## Personalizzare il pannello reazioni +È possibile personalizzare il pannello reazioni selezionando gli emoji che vuoi usare. Puoi cambiare i predefiniti nella scheda "Reazioni" delle impostazioni. -## リモート投稿へのリアクションについて -リアクションはMisskeyオリジナルの機能であるため、リモートインスタンスがMisskeyでない限りは、ほとんどの場合「Like」としてアクティビティが送信されます。一般的にはLikeは「お気に入り」として実装されているようです。 また、相手がMisskeyであったとしても、カスタム絵文字リアクションは伝わらず、自動的に「👍」等にフォールバックされます。 +## Inviare reazioni a server remoti +Siccome le reazioni sono una funzionalità originale di Misskey, vengono ricevute come semplici "Mi piace" dalla maggior parte delle istanze remote del Fediverso, a meno che non sia un'altra istanza Misskey.In genere sul Fediverso, la funzionalità "Mi piace" viene implementata come funzione "Preferiti". Inoltre, se reagisci con un emoji personalizzato verrà automaticamente inoltrato come un "👍" o simile, anche se quella destinataria è un'istanza Misskey. -## リモートからのリアクションについて -リモートから「Like」アクティビティを受信したとき、Misskeyでは「👍」のリアクションとして解釈されます。 +## Ricevere reazioni da server remoti +I "Mi piace" ricevuti da utenti di istanze remote vengono interpretati su Misskey come reazioni a forma di "👍". diff --git a/src/docs/it-IT/theme.md b/src/docs/it-IT/theme.md index b877bcd467..ad21299e89 100644 --- a/src/docs/it-IT/theme.md +++ b/src/docs/it-IT/theme.md @@ -1,12 +1,12 @@ # Tema -テーマを設定して、Misskeyクライアントの見た目を変更できます。 +Puoi utilizzare i temi per cambiare l'aspetto del client Misskey. -## テーマの設定 -設定 > テーマ +## Impostazioni tema +Impostazioni > Tema -## テーマを作成する -テーマコードはJSON5で記述されたテーマオブジェクトです。 テーマは以下のようなオブジェクトです。 +## Creare un tema +Il codice dei temi è scritto a forma di oggetti JSON5. I temi contengono gli oggetti sotto citati: ``` js { id: '17587283-dd92-4a2c-a22c-be0637c9e22a', @@ -42,10 +42,10 @@ * テーマはここで設定されたベーステーマを継承します。 * `props` ... テーマのスタイル定義。これから説明します。 -### テーマのスタイル定義 +### Impostare uno stile di tema `props`下にはテーマのスタイルを定義します。 キーがCSSの変数名になり、バリューで中身を指定します。 なお、この`props`オブジェクトはベーステーマから継承されます。 ベーステーマは、このテーマの`base`が`light`なら[_light.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_light.json5)で、`dark`なら[_dark.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_dark.json5)です。 つまり、このテーマ内の`props`に`panel`というキーが無くても、そこにはベーステーマの`panel`があると見なされます。 -#### バリューで使える構文 +#### Sintassi dei valori * 16進数で表された色 * 例: `#00ff00` * `rgb(r, g, b)`形式で表された色 @@ -61,8 +61,8 @@ * 関数(後述) * `:{関数名}<{引数}<{色}` -#### Costante +#### Costanti 「CSS変数として出力はしたくないが、他のCSS変数の値として使いまわしたい」値があるときは、定数を使うと便利です。 キー名を`$`で始めると、そのキーはCSS変数として出力されません。 -#### Funzione +#### Funzioni wip diff --git a/src/docs/it-IT/timelines.md b/src/docs/it-IT/timelines.md index eb2bb65cad..bd462541d2 100644 --- a/src/docs/it-IT/timelines.md +++ b/src/docs/it-IT/timelines.md @@ -1,15 +1,15 @@ -# タイムラインの比較 +# Confronto delle timeline https://docs.google.com/spreadsheets/d/1lxQ2ugKrhz58Bg96HTDK_2F98BUritkMyIiBkOByjHA/edit?usp=sharing ## Home -自分のフォローしているユーザーの投稿 +Pubblicazioni degli utenti che segui. ## Locale -全てのローカルユーザーの「ホーム」指定されていない投稿 +Pubblicazioni degli utenti della tua istanza. Non vengono mostrate le note pubblicate con lo stato "principale". -## ソーシャル -自分のフォローしているユーザーの投稿と、全てのローカルユーザーの「ホーム」指定されていない投稿 +## Sociale +Raggruppa le timeline "home" e "locale". ## グローバル -全てのローカルユーザーの「ホーム」指定されていない投稿と、サーバーに届いた全てのリモートユーザーの「ホーム」指定されていない投稿 +Tutte le pubblicazioni ricevute dall'istanza, sia locali che altre. Non vengono mostrate le note pubblicate con lo stato "home". From cf757ed01e2636c4ece134664c18732fcc009658 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 22 Mar 2021 15:26:53 +0900 Subject: [PATCH 76/76] 12.75.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d05d0aa02c..0f40837a6f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo <syuilotan@yahoo.co.jp>", - "version": "12.74.1", + "version": "12.75.0", "codename": "indigo", "repository": { "type": "git",