diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 8f8c5a13ab723f46722312a8429bfa45bad497a2..2809cd2ca4694e61bd889ecc664f9bde470d0296 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.8' services: app: - build: + build: context: . dockerfile: Dockerfile diff --git a/.editorconfig b/.editorconfig index a6f988f8d7c725f3e8872a0e558b61ff66031cf2..def7baa1a8f117e4f9107d04b69192c6f668fb06 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,6 +6,10 @@ indent_size = 2 charset = utf-8 insert_final_newline = true end_of_line = lf +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false [*.{yml,yaml}] indent_style = space diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f6b3804f84f988df1fb755f214e167c783f82fc3..896fb6b08955b0093a613a80a7114fc875c41f1c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -106,7 +106,7 @@ If your language is not listed in Crowdin, please open an issue.  ## Development -During development, it is useful to use the +During development, it is useful to use the ``` pnpm dev @@ -150,7 +150,7 @@ Prepare DB/Redis for testing. ``` docker compose -f packages/backend/test/docker-compose.yml up ``` -Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`. +Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`. Run all test. ``` diff --git a/README.md b/README.md index 2aae4bb865baf1a4f80da40280145467a41cca67..ab4388c2ebd2bcf14701db9db2c351549b125c49 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ <a href="https://misskey-hub.net"> <img src="./assets/title_float.svg" alt="Misskey logo" style="border-radius:50%" width="400"/> </a> - + **🌎 **[Misskey](https://misskey-hub.net/)** is an open source, decentralized social media platform that's free forever! 🚀** - + --- <a href="https://misskey-hub.net/instances.html"> @@ -21,7 +21,7 @@ <a href="https://www.patreon.com/syuilo"> <img src="https://custom-icon-badges.herokuapp.com/badge/become_a-patron-F96854?logoColor=F96854&style=for-the-badge&logo=patreon&labelColor=363B40" alt="become a patron"/></a> - + --- [](https://codecov.io/gh/misskey-dev/misskey) diff --git a/assets/title_float.svg b/assets/title_float.svg index 43205ac1c48243da4624f3d6d6f7f8dfbecc4c80..ed1749e321fbbed6d9df3f2f39fbfe3d6cf2830c 100644 Binary files a/assets/title_float.svg and b/assets/title_float.svg differ diff --git a/cypress/e2e/basic.cy.js b/cypress/e2e/basic.cy.js index 2515c14ad6ac68505b9b6344046f3136df023b84..2bf91cb00904f7be939c77ffda17b0dcedcfdb89 100644 --- a/cypress/e2e/basic.cy.js +++ b/cypress/e2e/basic.cy.js @@ -56,7 +56,7 @@ describe('After setup instance', () => { cy.get('[data-cy-signup-rules-notes-agree] [data-cy-switch-toggle]').click(); cy.get('[data-cy-signup-rules-continue]').should('not.be.disabled'); cy.get('[data-cy-signup-rules-continue]').click(); - + cy.get('[data-cy-signup-submit]').should('be.disabled'); cy.get('[data-cy-signup-username] input').type('alice'); cy.get('[data-cy-signup-submit]').should('be.disabled'); diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index ab11785e28ad3f5e802ed6cddddd9b59bf13775e..69d83b13b07e057141355d53d6f8da93af8241f4 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -295,7 +295,7 @@ export class AccountMoveService { * dstユーザーã®alsoKnownAsã‚’fetchPersonã—ã¦ã„ãã€æœ¬å½“ã«movedToUrlã‚’dstã«æŒ‡å®šã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒå˜åœ¨ã™ã‚‹ã®ã‹ã‚’調ã¹ã‚‹ * * @param dst movedToUrlを指定ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ - * @param check + * @param check * @param instant checkãŒtrueã§ã‚るユーザーãŒæœ€åˆã«è¦‹ã¤ã‹ã£ãŸã‚‰å³åº§ã«returnã™ã‚‹ã‹ã©ã†ã‹ * @returns Promise<LocalUser | RemoteUser | null> */ diff --git a/packages/backend/src/core/AiService.ts b/packages/backend/src/core/AiService.ts index 059e335effa720dc9f9e1df916f888e50d2c015e..02501b832b319e1f267148b74645317e56946048 100644 --- a/packages/backend/src/core/AiService.ts +++ b/packages/backend/src/core/AiService.ts @@ -31,16 +31,16 @@ export class AiService { const cpuFlags = await this.getCpuFlags(); isSupportedCpu = REQUIRED_CPU_FLAGS.every(required => cpuFlags.includes(required)); } - + if (!isSupportedCpu) { console.error('This CPU cannot use TensorFlow.'); return null; } - + const tf = await import('@tensorflow/tfjs-node'); - + if (this.model == null) this.model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, { size: 299 }); - + const buffer = await fs.promises.readFile(path); const image = await tf.node.decodeImage(buffer, 3) as any; try { diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index d8df371916bdab8d75a7e1f41ed4935a0d3ca2aa..9310fd8b52cf0e79d49e0e59d7977c7e5d790322 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -99,7 +99,7 @@ export class AntennaService implements OnApplicationShutdown { 'MAXLEN', '~', '200', '*', 'note', note.id); - + this.globalEventService.publishAntennaStream(antenna.id, 'note', note); } @@ -112,16 +112,16 @@ export class AntennaService implements OnApplicationShutdown { public async checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }): Promise<boolean> { if (note.visibility === 'specified') return false; if (note.visibility === 'followers') return false; - + if (!antenna.withReplies && note.replyId != null) return false; - + if (antenna.src === 'home') { // TODO } else if (antenna.src === 'list') { const listUsers = (await this.userListJoiningsRepository.findBy({ userListId: antenna.userListId!, })).map(x => x.userId); - + if (!listUsers.includes(note.userId)) return false; } else if (antenna.src === 'users') { const accts = antenna.users.map(x => { @@ -130,32 +130,32 @@ export class AntennaService implements OnApplicationShutdown { }); if (!accts.includes(this.utilityService.getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false; } - + const keywords = antenna.keywords // Clean up .map(xs => xs.filter(x => x !== '')) .filter(xs => xs.length > 0); - + if (keywords.length > 0) { if (note.text == null && note.cw == null) return false; const _text = (note.text ?? '') + '\n' + (note.cw ?? ''); - + const matched = keywords.some(and => and.every(keyword => antenna.caseSensitive ? _text.includes(keyword) : _text.toLowerCase().includes(keyword.toLowerCase()), )); - + if (!matched) return false; } - + const excludeKeywords = antenna.excludeKeywords // Clean up .map(xs => xs.filter(x => x !== '')) .filter(xs => xs.length > 0); - + if (excludeKeywords.length > 0) { if (note.text == null && note.cw == null) return false; @@ -167,16 +167,16 @@ export class AntennaService implements OnApplicationShutdown { ? _text.includes(keyword) : _text.toLowerCase().includes(keyword.toLowerCase()), )); - + if (matched) return false; } - + if (antenna.withFile) { if (note.fileIds && note.fileIds.length === 0) return false; } - + // TODO: eval expression - + return true; } @@ -188,7 +188,7 @@ export class AntennaService implements OnApplicationShutdown { }); this.antennasFetched = true; } - + return this.antennas; } diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index 1a52a229c57f44609c9a928a25501eeffbc476b5..10cfdba254d8d7fcec98bcddfe98c4baf02441b2 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -20,7 +20,7 @@ export class CaptchaService { secret, response, }); - + const res = await this.httpRequestService.send(url, { method: 'POST', body: params.toString(), @@ -28,14 +28,14 @@ export class CaptchaService { 'Content-Type': 'application/x-www-form-urlencoded', }, }, { throwErrorWhenResponseNotOk: false }); - + if (!res.ok) { throw new Error(`${res.status}`); } - + return await res.json() as CaptchaResponse; - } - + } + @bindThis public async verifyRecaptcha(secret: string, response: string | null | undefined): Promise<void> { if (response == null) { @@ -73,7 +73,7 @@ export class CaptchaService { if (response == null) { throw new Error('turnstile-failed: no response provided'); } - + const result = await this.getCaptchaResponse('https://challenges.cloudflare.com/turnstile/v0/siteverify', secret, response).catch(err => { throw new Error(`turnstile-request-failed: ${err}`); }); diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts index 8f887d90f9ce8842938fbb574486bfdca602396d..0bfbe2b173a68f73cc7e13068d43e62f24464a5d 100644 --- a/packages/backend/src/core/CreateSystemUserService.ts +++ b/packages/backend/src/core/CreateSystemUserService.ts @@ -25,27 +25,27 @@ export class CreateSystemUserService { @bindThis public async createSystemUser(username: string): Promise<User> { const password = uuid(); - + // Generate hash of password const salt = await bcrypt.genSalt(8); const hash = await bcrypt.hash(password, salt); - + // Generate secret const secret = generateNativeUserToken(); - + const keyPair = await genRsaKeyPair(4096); - + let account!: User; - + // Start transaction await this.db.transaction(async transactionalEntityManager => { const exist = await transactionalEntityManager.findOneBy(User, { usernameLower: username.toLowerCase(), host: IsNull(), }); - + if (exist) throw new Error('the user is already exists'); - + account = await transactionalEntityManager.insert(User, { id: this.idService.genId(), createdAt: new Date(), @@ -58,25 +58,25 @@ export class CreateSystemUserService { isExplorable: false, isBot: true, }).then(x => transactionalEntityManager.findOneByOrFail(User, x.identifiers[0])); - + await transactionalEntityManager.insert(UserKeypair, { publicKey: keyPair.publicKey, privateKey: keyPair.privateKey, userId: account.id, }); - + await transactionalEntityManager.insert(UserProfile, { userId: account.id, autoAcceptFollowed: false, password: hash, }); - + await transactionalEntityManager.insert(UsedUsername, { createdAt: new Date(), username: username.toLowerCase(), }); }); - + return account; } } diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 5f2ced77eb61a0c94d9b6b2947a4b5afb8cb7191..661d956bd6980d85134db375e7a8bc6abbf911fd 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -140,7 +140,7 @@ export class CustomEmojiService implements OnApplicationShutdown { this.globalEventService.publishBroadcastStream('emojiAdded', { emoji: updated, - }); + }); } } @@ -194,7 +194,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } this.localEmojisCache.refresh(); - + this.globalEventService.publishBroadcastStream('emojiUpdated', { emojis: await this.emojiEntityService.packDetailedMany(ids), }); @@ -215,7 +215,7 @@ export class CustomEmojiService implements OnApplicationShutdown { emojis: await this.emojiEntityService.packDetailedMany(ids), }); } - + @bindThis public async setLicenseBulk(ids: Emoji['id'][], license: string | null) { await this.emojisRepository.update({ diff --git a/packages/backend/src/core/DeleteAccountService.ts b/packages/backend/src/core/DeleteAccountService.ts index 327283106fcce742fb956299b2d92ba1e5e8c4ad..3a0592441bd7f3995efcf0186c17eaf0bcd2662c 100644 --- a/packages/backend/src/core/DeleteAccountService.ts +++ b/packages/backend/src/core/DeleteAccountService.ts @@ -28,11 +28,11 @@ export class DeleteAccountService { // 物ç†å‰Šé™¤ã™ã‚‹å‰ã«Delete activityã‚’é€ä¿¡ã™ã‚‹ await this.userSuspendService.doPostSuspend(user).catch(e => {}); - + this.queueService.createDeleteAccountJob(user, { soft: false, }); - + await this.usersRepository.update(user.id, { isDeleted: true, }); diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index 59932a5b88f03ba89820410cce11edac6296e66e..a04e9c12257392685efe2d948280e13485298b37 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -29,12 +29,12 @@ export class EmailService { @bindThis public async sendEmail(to: string, subject: string, html: string, text: string) { const meta = await this.metaService.fetch(true); - + const iconUrl = `${this.config.url}/static-assets/mi-white.png`; const emailSettingUrl = `${this.config.url}/settings/email`; - + const enableAuth = meta.smtpUser != null && meta.smtpUser !== ''; - + const transporter = nodemailer.createTransport({ host: meta.smtpHost, port: meta.smtpPort, @@ -46,7 +46,7 @@ export class EmailService { pass: meta.smtpPass, } : undefined, } as any); - + try { // TODO: htmlサニタイズ const info = await transporter.sendMail({ @@ -135,7 +135,7 @@ export class EmailService { </body> </html>`, }); - + this.logger.info(`Message sent: ${info.messageId}`); } catch (err) { this.logger.error(err as Error); @@ -149,12 +149,12 @@ export class EmailService { reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp'; }> { const meta = await this.metaService.fetch(); - + const exist = await this.userProfilesRepository.countBy({ emailVerified: true, email: emailAddress, }); - + const validated = meta.enableActiveEmailValidation ? await validateEmail({ email: emailAddress, validateRegex: true, @@ -163,9 +163,9 @@ export class EmailService { validateDisposable: true, // æ¨ã¦ã‚¢ãƒ‰ã‹ã©ã†ã‹ãƒã‚§ãƒƒã‚¯ validateSMTP: false, // 日本ã ã¨25ãƒãƒ¼ãƒˆãŒæ®†ã©ã®ãƒ—ãƒãƒã‚¤ãƒ€ãƒ¼ã§å¡žãŒã‚Œã¦ã„ã¦ã‚¿ã‚¤ãƒ アウトã«ãªã‚‹ã®ã§ }) : { valid: true, reason: null }; - + const available = exist === 0 && validated.valid; - + return { available, reason: available ? null : diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts index 3603d59dcc2e263c1eefa6b19249e4af143807d2..a762038942ed2cadf0ee42e970a46e2994069411 100644 --- a/packages/backend/src/core/FederatedInstanceService.ts +++ b/packages/backend/src/core/FederatedInstanceService.ts @@ -43,19 +43,19 @@ export class FederatedInstanceService implements OnApplicationShutdown { @bindThis public async fetch(host: string): Promise<Instance> { host = this.utilityService.toPuny(host); - + const cached = await this.federatedInstanceCache.get(host); if (cached) return cached; - + const index = await this.instancesRepository.findOneBy({ host }); - + if (index == null) { const i = await this.instancesRepository.insert({ id: this.idService.genId(), host, firstRetrievedAt: new Date(), }).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0])); - + this.federatedInstanceCache.set(host, i); return i; } else { @@ -74,7 +74,7 @@ export class FederatedInstanceService implements OnApplicationShutdown { .then((response) => { return response.raw[0]; }); - + this.federatedInstanceCache.set(result.host, result); } diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index 96ee77d0563e62c5224bf4a3b3815b84fd24ce3c..127c4c3155011c3bf6219bb0ca1cbd9b00605b42 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -58,6 +58,8 @@ export class FetchInstanceMetadataService { @bindThis public async fetchInstanceMetadata(instance: Instance, force = false): Promise<void> { + try { + const host = instance.host; // Acquire mutex to ensure no parallel runs if (!await this.tryLock(host)) return; @@ -72,13 +74,13 @@ export class FetchInstanceMetadataService { } this.logger.info(`Fetching metadata of ${instance.host} ...`); - + const [info, dom, manifest] = await Promise.all([ this.fetchNodeinfo(instance).catch(() => null), this.fetchDom(instance).catch(() => null), this.fetchManifest(instance).catch(() => null), ]); - + const [favicon, icon, themeColor, name, description] = await Promise.all([ this.fetchFaviconUrl(instance, dom).catch(() => null), this.fetchIconUrl(instance, dom, manifest).catch(() => null), @@ -86,13 +88,13 @@ export class FetchInstanceMetadataService { this.getSiteName(info, dom, manifest).catch(() => null), this.getDescription(info, dom, manifest).catch(() => null), ]); - + this.logger.succ(`Successfuly fetched metadata of ${instance.host}`); - + const updates = { infoUpdatedAt: new Date(), } as Record<string, any>; - + if (info) { updates.softwareName = typeof info.software?.name === 'string' ? info.software.name.toLowerCase() : '?'; updates.softwareVersion = info.software?.version; @@ -100,15 +102,15 @@ export class FetchInstanceMetadataService { updates.maintainerName = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.name ?? null) : null : null; updates.maintainerEmail = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.email ?? null) : null : null; } - + if (name) updates.name = name; if (description) updates.description = description; if (icon || favicon) updates.iconUrl = icon ?? favicon; if (favicon) updates.faviconUrl = favicon; if (themeColor) updates.themeColor = themeColor; - + await this.federatedInstanceService.update(instance.id, updates); - + this.logger.succ(`Successfuly updated metadata of ${instance.host}`); } catch (e) { this.logger.error(`Failed to update metadata of ${instance.host}: ${e}`); @@ -120,7 +122,7 @@ export class FetchInstanceMetadataService { @bindThis private async fetchNodeinfo(instance: Instance): Promise<NodeInfo> { this.logger.info(`Fetching nodeinfo of ${instance.host} ...`); - + try { const wellknown = await this.httpRequestService.getJson('https://' + instance.host + '/.well-known/nodeinfo') .catch(err => { @@ -130,33 +132,33 @@ export class FetchInstanceMetadataService { throw err.statusCode ?? err.message; } }) as Record<string, unknown>; - + if (wellknown.links == null || !Array.isArray(wellknown.links)) { throw new Error('No wellknown links'); } - + const links = wellknown.links as any[]; - + const lnik1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0'); const lnik2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0'); const lnik2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1'); const link = lnik2_1 ?? lnik2_0 ?? lnik1_0; - + if (link == null) { throw new Error('No nodeinfo link provided'); } - + const info = await this.httpRequestService.getJson(link.href) .catch(err => { throw err.statusCode ?? err.message; }); - + this.logger.succ(`Successfuly fetched nodeinfo of ${instance.host}`); - + return info as NodeInfo; } catch (err) { this.logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${err}`); - + throw err; } } @@ -164,51 +166,51 @@ export class FetchInstanceMetadataService { @bindThis private async fetchDom(instance: Instance): Promise<DOMWindow['document']> { this.logger.info(`Fetching HTML of ${instance.host} ...`); - + const url = 'https://' + instance.host; - + const html = await this.httpRequestService.getHtml(url); - + const { window } = new JSDOM(html); const doc = window.document; - + return doc; } @bindThis private async fetchManifest(instance: Instance): Promise<Record<string, unknown> | null> { const url = 'https://' + instance.host; - + const manifestUrl = url + '/manifest.json'; - + const manifest = await this.httpRequestService.getJson(manifestUrl) as Record<string, unknown>; - + return manifest; } @bindThis private async fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise<string | null> { const url = 'https://' + instance.host; - + if (doc) { // https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043 const href = Array.from(doc.getElementsByTagName('link')).reverse().find(link => link.relList.contains('icon'))?.href; - + if (href) { return (new URL(href, url)).href; } } - + const faviconUrl = url + '/favicon.ico'; - + const favicon = await this.httpRequestService.send(faviconUrl, { method: 'HEAD', }, { throwErrorWhenResponseNotOk: false }); - + if (favicon.ok) { return faviconUrl; } - + return null; } @@ -218,38 +220,38 @@ export class FetchInstanceMetadataService { const url = 'https://' + instance.host; return (new URL(manifest.icons[0].src, url)).href; } - + if (doc) { const url = 'https://' + instance.host; - + // https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043 const links = Array.from(doc.getElementsByTagName('link')).reverse(); // https://github.com/misskey-dev/misskey/pull/8220/files/0ec4eba22a914e31b86874f12448f88b3e58dd5a#r796487559 - const href = + const href = [ links.find(link => link.relList.contains('apple-touch-icon-precomposed'))?.href, links.find(link => link.relList.contains('apple-touch-icon'))?.href, links.find(link => link.relList.contains('icon'))?.href, ] .find(href => href); - + if (href) { return (new URL(href, url)).href; } } - + return null; } @bindThis private async getThemeColor(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { const themeColor = info?.metadata?.themeColor ?? doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') ?? manifest?.theme_color; - + if (themeColor) { const color = new tinycolor(themeColor); if (color.isValid()) return color.toHexString(); } - + return null; } @@ -262,19 +264,19 @@ export class FetchInstanceMetadataService { return info.metadata.name; } } - + if (doc) { const og = doc.querySelector('meta[property="og:title"]')?.getAttribute('content'); - + if (og) { return og; } } - + if (manifest) { return manifest.name ?? manifest.short_name; } - + return null; } @@ -287,23 +289,23 @@ export class FetchInstanceMetadataService { return info.metadata.description; } } - + if (doc) { const meta = doc.querySelector('meta[name="description"]')?.getAttribute('content'); if (meta) { return meta; } - + const og = doc.querySelector('meta[property="og:description"]')?.getAttribute('content'); if (og) { return og; } } - + if (manifest) { return manifest.name ?? manifest.short_name; } - + return null; } } diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts index d6c51ad559798d9c8501089ea6f40fe690799158..d43575b336708a5183ba023fe474d1d5414a64e4 100644 --- a/packages/backend/src/core/FileInfoService.ts +++ b/packages/backend/src/core/FileInfoService.ts @@ -161,20 +161,20 @@ export class FileInfoService { private async detectSensitivity(source: string, mime: string, sensitiveThreshold: number, sensitiveThresholdForPorn: number, analyzeVideo: boolean): Promise<[sensitive: boolean, porn: boolean]> { let sensitive = false; let porn = false; - + function judgePrediction(result: readonly predictionType[]): [sensitive: boolean, porn: boolean] { let sensitive = false; let porn = false; - + if ((result.find(x => x.className === 'Sexy')?.probability ?? 0) > sensitiveThreshold) sensitive = true; if ((result.find(x => x.className === 'Hentai')?.probability ?? 0) > sensitiveThreshold) sensitive = true; if ((result.find(x => x.className === 'Porn')?.probability ?? 0) > sensitiveThreshold) sensitive = true; - + if ((result.find(x => x.className === 'Porn')?.probability ?? 0) > sensitiveThresholdForPorn) porn = true; - + return [sensitive, porn]; } - + if ([ 'image/jpeg', 'image/png', @@ -253,10 +253,10 @@ export class FileInfoService { disposeOutDir(); } } - + return [sensitive, porn]; } - + private async *asyncIterateFrames(cwd: string, command: FFmpeg.FfmpegCommand): AsyncGenerator<string, void> { const watcher = new FSWatcher({ cwd, @@ -295,7 +295,7 @@ export class FileInfoService { } } } - + @bindThis private exists(path: string): Promise<boolean> { return fs.promises.access(path).then(() => true, () => false); diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index 375aa846cb36ea37f431e16efe48ec163e11850f..4f2c261140788676e47fb1eee0c6baf89721c36c 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -42,21 +42,21 @@ export class HttpRequestService { errorTtl: 30, // 30secs lookup: false, // nativeã®dns.lookupã«fallbackã—ãªã„ }); - + this.http = new http.Agent({ keepAlive: true, keepAliveMsecs: 30 * 1000, lookup: cache.lookup, } as http.AgentOptions); - + this.https = new https.Agent({ keepAlive: true, keepAliveMsecs: 30 * 1000, lookup: cache.lookup, } as https.AgentOptions); - + const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 128); - + this.httpAgent = config.proxy ? new HttpProxyAgent({ keepAlive: true, diff --git a/packages/backend/src/core/IdService.ts b/packages/backend/src/core/IdService.ts index 60098bc81c259e51ff493f16ccda3e1026fcc3c6..4d129407cb2ff5634826ac198f42ff612e7bff55 100644 --- a/packages/backend/src/core/IdService.ts +++ b/packages/backend/src/core/IdService.ts @@ -23,7 +23,7 @@ export class IdService { @bindThis public genId(date?: Date): string { if (!date || (date > new Date())) date = new Date(); - + switch (this.method) { case 'aid': return genAid(date); case 'meid': return genMeid(date); diff --git a/packages/backend/src/core/InstanceActorService.ts b/packages/backend/src/core/InstanceActorService.ts index 4fb3fc5b4f3588d3000af48afbbc28456f150efe..2e047dc5c1ea94987c9d9a27d2c78badcb92125a 100644 --- a/packages/backend/src/core/InstanceActorService.ts +++ b/packages/backend/src/core/InstanceActorService.ts @@ -26,12 +26,12 @@ export class InstanceActorService { public async getInstanceActor(): Promise<LocalUser> { const cached = this.cache.get(); if (cached) return cached; - + const user = await this.usersRepository.findOneBy({ host: IsNull(), username: ACTOR_USERNAME, }) as LocalUser | undefined; - + if (user) { this.cache.set(user); return user; diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts index 5acc9ad9adbd3d850db90ed90fb1550c4dee71ea..aae0a9134b5cedfb8a8b0e3b25c78bc8d8b4a7a9 100644 --- a/packages/backend/src/core/MetaService.ts +++ b/packages/backend/src/core/MetaService.ts @@ -56,7 +56,7 @@ export class MetaService implements OnApplicationShutdown { @bindThis public async fetch(noCache = false): Promise<Meta> { if (!noCache && this.cache) return this.cache; - + return await this.db.transaction(async transactionalEntityManager => { // éŽåŽ»ã®ãƒã‚°ã§ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒè¤‡æ•°å‡ºæ¥ã¦ã—ã¾ã£ã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚‹ã®ã§æ–°ã—ã„IDを優先ã™ã‚‹ const metas = await transactionalEntityManager.find(Meta, { @@ -64,9 +64,9 @@ export class MetaService implements OnApplicationShutdown { id: 'DESC', }, }); - + const meta = metas[0]; - + if (meta) { this.cache = meta; return meta; @@ -81,7 +81,7 @@ export class MetaService implements OnApplicationShutdown { ['id'], ) .then((x) => transactionalEntityManager.findOneByOrFail(Meta, x.identifiers[0])); - + this.cache = saved; return saved; } diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index dffee16e08ca28ceb4bddba1c61e4745cf199079..38aaa84524787bda03bae40d6c139d68e2db1c82 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -27,29 +27,29 @@ export class MfmService { public fromHtml(html: string, hashtagNames?: string[]): string { // some AP servers like Pixelfed use br tags as well as newlines html = html.replace(/<br\s?\/?>\r?\n/gi, '\n'); - + const dom = parse5.parseFragment(html); - + let text = ''; - + for (const n of dom.childNodes) { analyze(n); } - + return text.trim(); - + function getText(node: TreeAdapter.Node): string { if (treeAdapter.isTextNode(node)) return node.value; if (!treeAdapter.isElementNode(node)) return ''; if (node.nodeName === 'br') return '\n'; - + if (node.childNodes) { return node.childNodes.map(n => getText(n)).join(''); } - + return ''; } - + function appendChildren(childNodes: TreeAdapter.ChildNode[]): void { if (childNodes) { for (const n of childNodes) { @@ -57,35 +57,35 @@ export class MfmService { } } } - + function analyze(node: TreeAdapter.Node) { if (treeAdapter.isTextNode(node)) { text += node.value; return; } - + // Skip comment or document type node if (!treeAdapter.isElementNode(node)) return; - + switch (node.nodeName) { case 'br': { text += '\n'; break; } - + case 'a': { const txt = getText(node); const rel = node.attrs.find(x => x.name === 'rel'); const href = node.attrs.find(x => x.name === 'href'); - + // ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚° if (hashtagNames && href && hashtagNames.map(x => x.toLowerCase()).includes(txt.toLowerCase())) { text += txt; // メンション } else if (txt.startsWith('@') && !(rel && rel.value.startsWith('me '))) { const part = txt.split('@'); - + if (part.length === 2 && href) { //#region ホストå部分ãŒçœç•¥ã•ã‚Œã¦ã„ã‚‹ã®ã§å¾©å…ƒã™ã‚‹ const acct = `${txt}@${(new URL(href.value)).hostname}`; @@ -116,12 +116,12 @@ export class MfmService { return `[${txt}](${href.value})`; } }; - + text += generateLink(); } break; } - + case 'h1': { text += 'ã€'; @@ -129,7 +129,7 @@ export class MfmService { text += '】\n'; break; } - + case 'b': case 'strong': { @@ -138,7 +138,7 @@ export class MfmService { text += '**'; break; } - + case 'small': { text += '<small>'; @@ -146,7 +146,7 @@ export class MfmService { text += '</small>'; break; } - + case 's': case 'del': { @@ -155,7 +155,7 @@ export class MfmService { text += '~~'; break; } - + case 'i': case 'em': { @@ -164,7 +164,7 @@ export class MfmService { text += '</i>'; break; } - + // block code (<pre><code>) case 'pre': { if (node.childNodes.length === 1 && node.childNodes[0].nodeName === 'code') { @@ -176,7 +176,7 @@ export class MfmService { } break; } - + // inline code (<code>) case 'code': { text += '`'; @@ -184,7 +184,7 @@ export class MfmService { text += '`'; break; } - + case 'blockquote': { const t = getText(node); if (t) { @@ -193,7 +193,7 @@ export class MfmService { } break; } - + case 'p': case 'h2': case 'h3': @@ -205,7 +205,7 @@ export class MfmService { appendChildren(node.childNodes); break; } - + // other block elements case 'div': case 'header': @@ -219,7 +219,7 @@ export class MfmService { appendChildren(node.childNodes); break; } - + default: // includes inline elements { appendChildren(node.childNodes); @@ -234,48 +234,48 @@ export class MfmService { if (nodes == null) { return null; } - + const { window } = new Window(); - + const doc = window.document; - + function appendChildren(children: mfm.MfmNode[], targetElement: any): void { if (children) { for (const child of children.map(x => (handlers as any)[x.type](x))) targetElement.appendChild(child); } } - + const handlers: { [K in mfm.MfmNode['type']]: (node: mfm.NodeType<K>) => any } = { bold: (node) => { const el = doc.createElement('b'); appendChildren(node.children, el); return el; }, - + small: (node) => { const el = doc.createElement('small'); appendChildren(node.children, el); return el; }, - + strike: (node) => { const el = doc.createElement('del'); appendChildren(node.children, el); return el; }, - + italic: (node) => { const el = doc.createElement('i'); appendChildren(node.children, el); return el; }, - + fn: (node) => { const el = doc.createElement('i'); appendChildren(node.children, el); return el; }, - + blockCode: (node) => { const pre = doc.createElement('pre'); const inner = doc.createElement('code'); @@ -283,21 +283,21 @@ export class MfmService { pre.appendChild(inner); return pre; }, - + center: (node) => { const el = doc.createElement('div'); appendChildren(node.children, el); return el; }, - + emojiCode: (node) => { return doc.createTextNode(`\u200B:${node.props.name}:\u200B`); }, - + unicodeEmoji: (node) => { return doc.createTextNode(node.props.emoji); }, - + hashtag: (node) => { const a = doc.createElement('a'); a.setAttribute('href', `${this.config.url}/tags/${node.props.hashtag}`); @@ -305,32 +305,32 @@ export class MfmService { a.setAttribute('rel', 'tag'); return a; }, - + inlineCode: (node) => { const el = doc.createElement('code'); el.textContent = node.props.code; return el; }, - + mathInline: (node) => { const el = doc.createElement('code'); el.textContent = node.props.formula; return el; }, - + mathBlock: (node) => { const el = doc.createElement('code'); el.textContent = node.props.formula; return el; }, - + link: (node) => { const a = doc.createElement('a'); a.setAttribute('href', node.props.url); appendChildren(node.children, a); return a; }, - + mention: (node) => { const a = doc.createElement('a'); const { username, host, acct } = node.props; @@ -340,47 +340,47 @@ export class MfmService { a.textContent = acct; return a; }, - + quote: (node) => { const el = doc.createElement('blockquote'); appendChildren(node.children, el); return el; }, - + text: (node) => { const el = doc.createElement('span'); const nodes = node.props.text.split(/\r\n|\r|\n/).map(x => doc.createTextNode(x)); - + for (const x of intersperse<FIXME | 'br'>('br', nodes)) { el.appendChild(x === 'br' ? doc.createElement('br') : x); } - + return el; }, - + url: (node) => { const a = doc.createElement('a'); a.setAttribute('href', node.props.url); a.textContent = node.props.url; return a; }, - + search: (node) => { const a = doc.createElement('a'); a.setAttribute('href', `https://www.google.com/search?q=${node.props.query}`); a.textContent = node.props.content; return a; }, - + plain: (node) => { const el = doc.createElement('span'); appendChildren(node.children, el); return el; }, }; - + appendChildren(nodes, doc.body); - + return `<p>${doc.body.innerHTML}</p>`; - } + } } diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 1c8491bf573b11078ea5f8c54d8adc8254edc589..fb36ca3b6aa4a6027d80baf47561837551bcf8b7 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -672,7 +672,7 @@ export class NoteCreateService implements OnApplicationShutdown { // Register to search database this.index(note); } - + @bindThis private isSensitive(note: Option, sensitiveWord: string[]): boolean { if (sensitiveWord.length > 0) { @@ -758,7 +758,7 @@ export class NoteCreateService implements OnApplicationShutdown { @bindThis private index(note: Note) { if (note.text == null && note.cw == null) return; - + this.searchService.indexNote(note); } diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 3612ac806f2ca26a1173a469062935f73f78eebd..10381c7e65a78168206e94c32e5d6783ce799cf0 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -45,7 +45,7 @@ export class NoteDeleteService { private perUserNotesChart: PerUserNotesChart, private instanceChart: InstanceChart, ) {} - + /** * 投稿を削除ã—ã¾ã™ã€‚ * @param user 投稿者 diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts index e57e57d310168836043fb069ae6f8181a4c0f424..b84591e26d0ffe3ecaff0282c39af63b3e093004 100644 --- a/packages/backend/src/core/NoteReadService.ts +++ b/packages/backend/src/core/NoteReadService.ts @@ -99,7 +99,7 @@ export class NoteReadService implements OnApplicationShutdown { }); // TODO: ↓ã¾ã¨ã‚ã¦ã‚¯ã‚¨ãƒªã—ãŸã„ - + this.noteUnreadsRepository.countBy({ userId: userId, isMentioned: true, @@ -109,7 +109,7 @@ export class NoteReadService implements OnApplicationShutdown { this.globalEventService.publishMainStream(userId, 'readAllUnreadMentions'); } }); - + this.noteUnreadsRepository.countBy({ userId: userId, isSpecified: true, diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index ed47165f7b4c3c090b3eb3dab2f9ba66fcae00d0..8e25f822842b3ba4abf7c9007c2b7813958b2dd0 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -46,7 +46,7 @@ export class NotificationService implements OnApplicationShutdown { force = false, ) { const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${userId}`); - + const latestNotificationIdsRes = await this.redisClient.xrevrange( `notificationTimeline:${userId}`, '+', diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index 368753d9a7a93e325294d12ca1a6e7d3492c2c26..be19400052ad3fac84c3ec79f1cbba2e043d008d 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -39,12 +39,12 @@ export class PollService { @bindThis public async vote(user: User, note: Note, choice: number) { const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); - + if (poll == null) throw new Error('poll not found'); - + // Check whether is valid choice if (poll.choices[choice] == null) throw new Error('invalid choice param'); - + // Check blocking if (note.userId !== user.id) { const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id); @@ -52,13 +52,13 @@ export class PollService { throw new Error('blocked'); } } - + // if already voted const exist = await this.pollVotesRepository.findBy({ noteId: note.id, userId: user.id, }); - + if (poll.multiple) { if (exist.some(x => x.choice === choice)) { throw new Error('already voted'); @@ -66,7 +66,7 @@ export class PollService { } else if (exist.length !== 0) { throw new Error('already voted'); } - + // Create vote await this.pollVotesRepository.insert({ id: this.idService.genId(), @@ -75,11 +75,11 @@ export class PollService { userId: user.id, choice: choice, }); - + // Increment votes count const index = choice + 1; // In SQL, array index is 1 based await this.pollsRepository.query(`UPDATE poll SET votes[${index}] = votes[${index}] + 1 WHERE "noteId" = '${poll.noteId}'`); - + this.globalEventService.publishNoteStream(note.id, 'pollVoted', { choice: choice, userId: user.id, @@ -90,10 +90,10 @@ export class PollService { public async deliverQuestionUpdate(noteId: Note['id']) { const note = await this.notesRepository.findOneBy({ id: noteId }); if (note == null) throw new Error('note not found'); - + const user = await this.usersRepository.findOneBy({ id: note.userId }); if (user == null) throw new Error('note not found'); - + if (this.userEntityService.isLocalUser(user)) { const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderNote(note, false), user)); this.apDeliverManagerService.deliverToFollowers(user, content); diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index 9ee83df644468313d4a5822fc6ae5097bef1cd5d..e1c3d3943cbcda6a5ea53a9347d9a89f45b3070e 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -31,7 +31,7 @@ function truncateBody<T extends keyof PushNotificationsTypes>(type: T, body: Pus ...body.note, // textã‚’getNoteSummaryã—ãŸã‚‚ã®ã«ç½®ãæ›ãˆã‚‹ text: getNoteSummary(('type' in body && body.type === 'renote') ? body.note.renote as Packed<'Note'> : body.note), - + cw: undefined, reply: undefined, renote: undefined, @@ -69,16 +69,16 @@ export class PushNotificationService implements OnApplicationShutdown { @bindThis public async pushNotification<T extends keyof PushNotificationsTypes>(userId: string, type: T, body: PushNotificationsTypes[T]) { const meta = await this.metaService.fetch(); - + if (!meta.enableServiceWorker || meta.swPublicKey == null || meta.swPrivateKey == null) return; - + // アプリケーションã®é€£çµ¡å…ˆã¨ã€ã‚µãƒ¼ãƒãƒ¼ã‚µã‚¤ãƒ‰ã®éµãƒšã‚¢ã®æƒ…å ±ã‚’ç™»éŒ² push.setVapidDetails(this.config.url, meta.swPublicKey, meta.swPrivateKey); - + const subscriptions = await this.subscriptionsCache.fetch(userId); - + for (const subscription of subscriptions) { if ([ 'readAllNotifications', @@ -103,7 +103,7 @@ export class PushNotificationService implements OnApplicationShutdown { //swLogger.info(err.statusCode); //swLogger.info(err.headers); //swLogger.info(err.body); - + if (err.statusCode === 410) { this.swSubscriptionsRepository.delete({ userId: userId, diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index bf50a1cdedb6ecb4fd782e403d563b534a207b8b..435d5d2389b83735b073e2081adb943a166f7a11 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -60,8 +60,8 @@ export class QueryService { q.orderBy(`${q.alias}.id`, 'DESC'); } return q; - } - + } + // ã“ã“ã§ã„ã†Blockedã¯è¢«Blockedã®æ„ @bindThis public generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { @@ -109,18 +109,18 @@ export class QueryService { q.andWhere('note.channelId IS NULL'); } else { q.leftJoinAndSelect('note.channel', 'channel'); - + const channelFollowingQuery = this.channelFollowingsRepository.createQueryBuilder('channelFollowing') .select('channelFollowing.followeeId') .where('channelFollowing.followerId = :followerId', { followerId: me.id }); - + q.andWhere(new Brackets(qb => { qb // ãƒãƒ£ãƒ³ãƒãƒ«ã®ãƒŽãƒ¼ãƒˆã§ã¯ãªã„ .where('note.channelId IS NULL') // ã¾ãŸã¯è‡ªåˆ†ãŒãƒ•ã‚©ãƒãƒ¼ã—ã¦ã„ã‚‹ãƒãƒ£ãƒ³ãƒãƒ«ã®ãƒŽãƒ¼ãƒˆ .orWhere(`note.channelId IN (${ channelFollowingQuery.getQuery() })`); })); - + q.setParameters(channelFollowingQuery.getParameters()); } } @@ -130,9 +130,9 @@ export class QueryService { const mutedQuery = this.mutedNotesRepository.createQueryBuilder('muted') .select('muted.noteId') .where('muted.userId = :userId', { userId: me.id }); - + q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`); - + q.setParameters(mutedQuery.getParameters()); } @@ -141,13 +141,13 @@ export class QueryService { const mutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted') .select('threadMuted.threadId') .where('threadMuted.userId = :userId', { userId: me.id }); - + q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`); q.andWhere(new Brackets(qb => { qb .where('note.threadId IS NULL') .orWhere(`note.threadId NOT IN (${ mutedQuery.getQuery() })`); })); - + q.setParameters(mutedQuery.getParameters()); } @@ -156,15 +156,15 @@ export class QueryService { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); - + if (exclude) { mutingQuery.andWhere('muting.muteeId != :excludeId', { excludeId: exclude.id }); } - + const mutingInstanceQuery = this.userProfilesRepository.createQueryBuilder('user_profile') .select('user_profile.mutedInstances') .where('user_profile.userId = :muterId', { muterId: me.id }); - + // 投稿ã®ä½œè€…をミュートã—ã¦ã„ãªã„ ã‹ã¤ // 投稿ã®è¿”ä¿¡å…ˆã®ä½œè€…をミュートã—ã¦ã„ãªã„ ã‹ã¤ // 投稿ã®å¼•ç”¨å…ƒã®ä½œè€…をミュートã—ã¦ã„ãªã„ @@ -191,7 +191,7 @@ export class QueryService { .where('note.renoteUserHost IS NULL') .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`); })); - + q.setParameters(mutingQuery.getParameters()); q.setParameters(mutingInstanceQuery.getParameters()); } @@ -201,9 +201,9 @@ export class QueryService { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); - + q.andWhere(`user.id NOT IN (${ mutingQuery.getQuery() })`); - + q.setParameters(mutingQuery.getParameters()); } @@ -245,7 +245,7 @@ export class QueryService { const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :meId'); - + q.andWhere(new Brackets(qb => { qb // 公開投稿ã§ã‚ã‚‹ .where(new Brackets(qb => { qb @@ -268,7 +268,7 @@ export class QueryService { })); })); })); - + q.setParameters({ meId: me.id }); } } @@ -278,10 +278,10 @@ export class QueryService { const mutingQuery = this.renoteMutingsRepository.createQueryBuilder('renote_muting') .select('renote_muting.muteeId') .where('renote_muting.muterId = :muterId', { muterId: me.id }); - + q.andWhere(new Brackets(qb => { qb - .where(new Brackets(qb => { + .where(new Brackets(qb => { qb.where('note.renoteId IS NOT NULL'); qb.andWhere('note.text IS NULL'); qb.andWhere(`note.userId NOT IN (${ mutingQuery.getQuery() })`); @@ -289,7 +289,7 @@ export class QueryService { .orWhere('note.renoteId IS NULL') .orWhere('note.text IS NOT NULL'); })); - + q.setParameters(mutingQuery.getParameters()); } } diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index 9d34d82be28a735d4afeb5cb7a90bf5ea44de2a6..c0113a21d74cc30af097a3d08833e256859de01a 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -39,9 +39,9 @@ export class RelayService { host: IsNull(), username: ACTOR_USERNAME, }); - + if (user) return user as LocalUser; - + const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME); return created as LocalUser; } @@ -53,12 +53,12 @@ export class RelayService { inbox, status: 'requesting', }).then(x => this.relaysRepository.findOneByOrFail(x.identifiers[0])); - + const relayActor = await this.getRelayActor(); const follow = await this.apRendererService.renderFollowRelay(relay, relayActor); const activity = this.apRendererService.addContext(follow); this.queueService.deliver(relayActor, activity, relay.inbox, false); - + return relay; } @@ -67,17 +67,17 @@ export class RelayService { const relay = await this.relaysRepository.findOneBy({ inbox, }); - + if (relay == null) { throw new Error('relay not found'); } - + const relayActor = await this.getRelayActor(); const follow = this.apRendererService.renderFollowRelay(relay, relayActor); const undo = this.apRendererService.renderUndo(follow, relayActor); const activity = this.apRendererService.addContext(undo); this.queueService.deliver(relayActor, activity, relay.inbox, false); - + await this.relaysRepository.delete(relay.id); } @@ -86,13 +86,13 @@ export class RelayService { const relays = await this.relaysRepository.find(); return relays; } - + @bindThis public async relayAccepted(id: string): Promise<string> { const result = await this.relaysRepository.update(id, { status: 'accepted', }); - + return JSON.stringify(result); } @@ -101,24 +101,24 @@ export class RelayService { const result = await this.relaysRepository.update(id, { status: 'rejected', }); - + return JSON.stringify(result); } @bindThis public async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise<void> { if (activity == null) return; - + const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({ status: 'accepted', })); if (relays.length === 0) return; - + const copy = deepClone(activity); if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public']; - + const signed = await this.apRendererService.attachLdSignature(copy, user); - + for (const relay of relays) { this.queueService.deliver(user, signed, relay.inbox, false); } diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts index ff68c242191dce10f799c0a803acf0aab5c2b4d6..ed15a1f1ce1f12b29ce1f36dd14dc52c553458bc 100644 --- a/packages/backend/src/core/RemoteUserResolveService.ts +++ b/packages/backend/src/core/RemoteUserResolveService.ts @@ -35,7 +35,7 @@ export class RemoteUserResolveService { @bindThis public async resolveUser(username: string, host: string | null): Promise<LocalUser | RemoteUser> { const usernameLower = username.toLowerCase(); - + if (host == null) { this.logger.info(`return local user: ${usernameLower}`); return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => { @@ -46,9 +46,9 @@ export class RemoteUserResolveService { } }) as LocalUser; } - + host = this.utilityService.toPuny(host); - + if (this.config.host === host) { this.logger.info(`return local user: ${usernameLower}`); return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => { @@ -59,39 +59,39 @@ export class RemoteUserResolveService { } }) as LocalUser; } - + const user = await this.usersRepository.findOneBy({ usernameLower, host }) as RemoteUser | null; - + const acctLower = `${usernameLower}@${host}`; - + if (user == null) { const self = await this.resolveSelf(acctLower); - + this.logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`); return await this.apPersonService.createPerson(self.href); } - + // ãƒ¦ãƒ¼ã‚¶ãƒ¼æƒ…å ±ãŒå¤ã„å ´åˆã¯ã€WebFilgerã‹ã‚‰ã‚„ã‚ŠãªãŠã—ã¦è¿”ã™ if (user.lastFetchedAt == null || Date.now() - user.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { // 繋ãŒã‚‰ãªã„インスタンスã«ä½•å›žã‚‚試行ã™ã‚‹ã®ã‚’防ã, 後続ã®åŒæ§˜å‡¦ç†ã®é€£ç¶šè©¦è¡Œã‚’防ã ãŸã‚ 試行å‰ã«ã‚‚æ›´æ–°ã™ã‚‹ await this.usersRepository.update(user.id, { lastFetchedAt: new Date(), }); - + this.logger.info(`try resync: ${acctLower}`); const self = await this.resolveSelf(acctLower); - + if (user.uri !== self.href) { // if uri mismatch, Fix (user@host <=> AP's Person id(RemoteUser.uri)) mapping. this.logger.info(`uri missmatch: ${acctLower}`); this.logger.info(`recovery missmatch uri for (username=${username}, host=${host}) from ${user.uri} to ${self.href}`); - + // validate uri const uri = new URL(self.href); if (uri.hostname !== host) { throw new Error('Invalid uri'); } - + await this.usersRepository.update({ usernameLower, host: host, @@ -101,9 +101,9 @@ export class RemoteUserResolveService { } else { this.logger.info(`uri is fine: ${acctLower}`); } - + await this.apPersonService.updatePerson(self.href); - + this.logger.info(`return resynced remote user: ${acctLower}`); return await this.usersRepository.findOneBy({ uri: self.href }).then(u => { if (u == null) { @@ -113,7 +113,7 @@ export class RemoteUserResolveService { } }); } - + this.logger.info(`return existing remote user: ${acctLower}`); return user; } diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 23ecf0157dac6e2d599c3f490eff38c0fd67f867..b0bfb44dc2e51e65d591b12cf6f318c739c29a33 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -392,7 +392,7 @@ export class RoleService implements OnApplicationShutdown { @bindThis public async unassign(userId: User['id'], roleId: Role['id']): Promise<void> { const now = new Date(); - + const existing = await this.roleAssignmentsRepository.findOneBy({ roleId, userId }); if (existing == null) { throw new RoleService.NotAssignedError(); diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 29eb65fda4e9a338e1d09f31f35fb77eed651539..e6b17a874107514d1dcaed334ecd406cff565fec 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -50,31 +50,31 @@ export class SignupService { }) { const { username, password, passwordHash, host } = opts; let hash = passwordHash; - + // Validate username if (!this.userEntityService.validateLocalUsername(username)) { throw new Error('INVALID_USERNAME'); } - + if (password != null && passwordHash == null) { // Validate password if (!this.userEntityService.validatePassword(password)) { throw new Error('INVALID_PASSWORD'); } - + // Generate hash of password const salt = await bcrypt.genSalt(8); hash = await bcrypt.hash(password, salt); } - + // Generate secret const secret = generateUserToken(); - + // Check username duplication if (await this.usersRepository.findOneBy({ usernameLower: username.toLowerCase(), host: IsNull() })) { throw new Error('DUPLICATED_USERNAME'); } - + // Check deleted username duplication if (await this.usedUsernamesRepository.findOneBy({ username: username.toLowerCase() })) { throw new Error('USED_USERNAME'); @@ -106,18 +106,18 @@ export class SignupService { }, (err, publicKey, privateKey) => err ? rej(err) : res([publicKey, privateKey]), )); - + let account!: User; - + // Start transaction await this.db.transaction(async transactionalEntityManager => { const exist = await transactionalEntityManager.findOneBy(User, { usernameLower: username.toLowerCase(), host: IsNull(), }); - + if (exist) throw new Error(' the username is already used'); - + account = await transactionalEntityManager.save(new User({ id: this.idService.genId(), createdAt: new Date(), @@ -127,27 +127,27 @@ export class SignupService { token: secret, isRoot: isTheFirstUser, })); - + await transactionalEntityManager.save(new UserKeypair({ publicKey: keyPair[0], privateKey: keyPair[1], userId: account.id, })); - + await transactionalEntityManager.save(new UserProfile({ userId: account.id, autoAcceptFollowed: true, password: hash, })); - + await transactionalEntityManager.save(new UsedUsername({ createdAt: new Date(), username: username.toLowerCase(), })); }); - + this.usersChart.update(account, true); - + return { account, secret }; } } diff --git a/packages/backend/src/core/TwoFactorAuthenticationService.ts b/packages/backend/src/core/TwoFactorAuthenticationService.ts index dda78236e9099b45c91c292bbbf36c1ceb26a9a2..d4cf186a247abcfc9a2f78454bc17e898b7df7d0 100644 --- a/packages/backend/src/core/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/core/TwoFactorAuthenticationService.ts @@ -69,7 +69,7 @@ function verifyCertificateChain(certificates: string[]) { const certStruct = jsrsasign.ASN1HEX.getTLVbyList(certificate.hex!, 0, [0]); if (certStruct == null) throw new Error('certStruct is null'); - + const algorithm = certificate.getSignatureAlgorithmField(); const signatureHex = certificate.getSignatureValueHex(); @@ -143,19 +143,19 @@ export class TwoFactorAuthenticationService { if (clientData.type !== 'webauthn.get') { throw new Error('type is not webauthn.get'); } - + if (this.hash(clientData.challenge).toString('hex') !== challenge) { throw new Error('challenge mismatch'); } if (clientData.origin !== this.config.scheme + '://' + this.config.host) { throw new Error('origin mismatch'); } - + const verificationData = Buffer.concat( [authenticatorData, this.hash(clientDataJSON)], 32 + authenticatorData.length, ); - + return crypto .createVerify('SHA256') .update(verificationData) @@ -168,7 +168,7 @@ export class TwoFactorAuthenticationService { none: { verify({ publicKey }: { publicKey: Map<number, Buffer> }) { const negTwo = publicKey.get(-2); - + if (!negTwo || negTwo.length !== 32) { throw new Error('invalid or no -2 key given'); } @@ -176,12 +176,12 @@ export class TwoFactorAuthenticationService { if (!negThree || negThree.length !== 32) { throw new Error('invalid or no -3 key given'); } - + const publicKeyU2F = Buffer.concat( [ECC_PRELUDE, negTwo, negThree], 1 + 32 + 32, ); - + return { publicKey: publicKeyU2F, valid: true, @@ -207,16 +207,16 @@ export class TwoFactorAuthenticationService { if (attStmt.alg !== -7) { throw new Error('alg mismatch'); } - + const verificationData = Buffer.concat([ authenticatorData, clientDataHash, ]); - + const attCert: Buffer = attStmt.x5c[0]; - + const negTwo = publicKey.get(-2); - + if (!negTwo || negTwo.length !== 32) { throw new Error('invalid or no -2 key given'); } @@ -224,23 +224,23 @@ export class TwoFactorAuthenticationService { if (!negThree || negThree.length !== 32) { throw new Error('invalid or no -3 key given'); } - + const publicKeyData = Buffer.concat( [ECC_PRELUDE, negTwo, negThree], 1 + 32 + 32, ); - + if (!attCert.equals(publicKeyData)) { throw new Error('public key mismatch'); } - + const isValid = crypto .createVerify('SHA256') .update(verificationData) .verify(PEMString(attCert), attStmt.sig); - + // TODO: Check 'attestationChallenge' field in extension of cert matches hash(clientDataJSON) - + return { valid: isValid, publicKey: publicKeyData, @@ -267,43 +267,43 @@ export class TwoFactorAuthenticationService { const verificationData = this.hash( Buffer.concat([authenticatorData, clientDataHash]), ); - + const jwsParts = attStmt.response.toString('utf-8').split('.'); - + const header = JSON.parse(base64URLDecode(jwsParts[0]).toString('utf-8')); const response = JSON.parse( base64URLDecode(jwsParts[1]).toString('utf-8'), ); const signature = jwsParts[2]; - + if (!verificationData.equals(Buffer.from(response.nonce, 'base64'))) { throw new Error('invalid nonce'); } - + const certificateChain = header.x5c .map((key: any) => PEMString(key)) .concat([GSR2]); - + if (getCertSubject(certificateChain[0]).CN !== 'attest.android.com') { throw new Error('invalid common name'); } - + if (!verifyCertificateChain(certificateChain)) { throw new Error('Invalid certificate chain!'); } - + const signatureBase = Buffer.from( jwsParts[0] + '.' + jwsParts[1], 'utf-8', ); - + const valid = crypto .createVerify('sha256') .update(signatureBase) .verify(certificateChain[0], base64URLDecode(signature)); - + const negTwo = publicKey.get(-2); - + if (!negTwo || negTwo.length !== 32) { throw new Error('invalid or no -2 key given'); } @@ -311,7 +311,7 @@ export class TwoFactorAuthenticationService { if (!negThree || negThree.length !== 32) { throw new Error('invalid or no -3 key given'); } - + const publicKeyData = Buffer.concat( [ECC_PRELUDE, negTwo, negThree], 1 + 32 + 32, @@ -342,17 +342,17 @@ export class TwoFactorAuthenticationService { authenticatorData, clientDataHash, ]); - + if (attStmt.x5c) { const attCert = attStmt.x5c[0]; - + const validSignature = crypto .createVerify('SHA256') .update(verificationData) .verify(PEMString(attCert), attStmt.sig); - + const negTwo = publicKey.get(-2); - + if (!negTwo || negTwo.length !== 32) { throw new Error('invalid or no -2 key given'); } @@ -360,12 +360,12 @@ export class TwoFactorAuthenticationService { if (!negThree || negThree.length !== 32) { throw new Error('invalid or no -3 key given'); } - + const publicKeyData = Buffer.concat( [ECC_PRELUDE, negTwo, negThree], 1 + 32 + 32, ); - + return { valid: validSignature, publicKey: publicKeyData, @@ -375,12 +375,12 @@ export class TwoFactorAuthenticationService { throw new Error('ECDAA-Verify is not supported'); } else { if (attStmt.alg !== -7) throw new Error('alg mismatch'); - + throw new Error('self attestation is not supported'); } }, }, - + 'fido-u2f': { verify({ attStmt, @@ -401,13 +401,13 @@ export class TwoFactorAuthenticationService { if (x5c.length !== 1) { throw new Error('x5c length does not match expectation'); } - + const attCert = x5c[0]; - + // TODO: make sure attCert is an Elliptic Curve (EC) public key over the P-256 curve - + const negTwo: Buffer = publicKey.get(-2); - + if (!negTwo || negTwo.length !== 32) { throw new Error('invalid or no -2 key given'); } @@ -415,12 +415,12 @@ export class TwoFactorAuthenticationService { if (!negThree || negThree.length !== 32) { throw new Error('invalid or no -3 key given'); } - + const publicKeyU2F = Buffer.concat( [ECC_PRELUDE, negTwo, negThree], 1 + 32 + 32, ); - + const verificationData = Buffer.concat([ NULL_BYTE, rpIdHash, @@ -428,12 +428,12 @@ export class TwoFactorAuthenticationService { credentialId, publicKeyU2F, ]); - + const validSignature = crypto .createVerify('SHA256') .update(verificationData) .verify(PEMString(attCert), attStmt.sig); - + return { valid: validSignature, publicKey: publicKeyU2F, diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index b197d335d89c756fb3637a8be3619439aef8dd45..28ae32681d469563277848dac4572e8532a1ccf2 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -32,13 +32,13 @@ export class UserSuspendService { @bindThis public async doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise<void> { this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); - + if (this.userEntityService.isLocalUser(user)) { // 知り得る全SharedInboxã«Deleteé…ä¿¡ const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user)); - + const queue: string[] = []; - + const followings = await this.followingsRepository.find({ where: [ { followerSharedInbox: Not(IsNull()) }, @@ -46,13 +46,13 @@ export class UserSuspendService { ], select: ['followerSharedInbox', 'followeeSharedInbox'], }); - + const inboxes = followings.map(x => x.followerSharedInbox ?? x.followeeSharedInbox); - + for (const inbox of inboxes) { if (inbox != null && !queue.includes(inbox)) queue.push(inbox); } - + for (const inbox of queue) { this.queueService.deliver(user, content, inbox, true); } @@ -62,13 +62,13 @@ export class UserSuspendService { @bindThis public async doPostUnsuspend(user: User): Promise<void> { this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); - + if (this.userEntityService.isLocalUser(user)) { // 知り得る全SharedInboxã«Undo Deleteé…ä¿¡ const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user), user)); - + const queue: string[] = []; - + const followings = await this.followingsRepository.find({ where: [ { followerSharedInbox: Not(IsNull()) }, @@ -76,13 +76,13 @@ export class UserSuspendService { ], select: ['followerSharedInbox', 'followeeSharedInbox'], }); - + const inboxes = followings.map(x => x.followerSharedInbox ?? x.followeeSharedInbox); - + for (const inbox of inboxes) { if (inbox != null && !queue.includes(inbox)) queue.push(inbox); } - + for (const inbox of queue) { this.queueService.deliver(user as any, content, inbox, true); } diff --git a/packages/backend/src/core/VideoProcessingService.ts b/packages/backend/src/core/VideoProcessingService.ts index 5869905db02d5e29bc8341e66c54700b83453b8b..89681f337224ba96983f95777b6d483b298296cd 100644 --- a/packages/backend/src/core/VideoProcessingService.ts +++ b/packages/backend/src/core/VideoProcessingService.ts @@ -21,7 +21,7 @@ export class VideoProcessingService { @bindThis public async generateVideoThumbnail(source: string): Promise<IImage> { const [dir, cleanup] = await createTempDir(); - + try { await new Promise((res, rej) => { FFmpeg({ diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index 467755a0728c72fdf257afbec17bf615c2863d01..b6f526390140c7bd73e7860347f1565dfb6a5332 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -31,7 +31,7 @@ export class WebhookService implements OnApplicationShutdown { }); this.webhooksFetched = true; } - + return this.webhooks; } diff --git a/packages/backend/src/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts index 8282a6324c5d24b934bb0bb3230704887dc3c86c..0eab7fa335a4655d8de10b1a9e904dc2e0429fd2 100644 --- a/packages/backend/src/core/activitypub/ApAudienceService.ts +++ b/packages/backend/src/core/activitypub/ApAudienceService.ts @@ -27,14 +27,14 @@ export class ApAudienceService { public async parseAudience(actor: RemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> { const toGroups = this.groupingAudience(getApIds(to), actor); const ccGroups = this.groupingAudience(getApIds(cc), actor); - + const others = unique(concat([toGroups.other, ccGroups.other])); - + const limit = promiseLimit<User | null>(2); const mentionedUsers = (await Promise.all( others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))), )).filter((x): x is User => x != null); - + if (toGroups.public.length > 0) { return { visibility: 'public', @@ -42,7 +42,7 @@ export class ApAudienceService { visibleUsers: [], }; } - + if (ccGroups.public.length > 0) { return { visibility: 'home', @@ -50,7 +50,7 @@ export class ApAudienceService { visibleUsers: [], }; } - + if (toGroups.followers.length > 0) { return { visibility: 'followers', @@ -58,14 +58,14 @@ export class ApAudienceService { visibleUsers: [], }; } - + return { visibility: 'specified', mentionedUsers, visibleUsers: mentionedUsers, }; } - + @bindThis private groupingAudience(ids: string[], actor: RemoteUser) { const groups = { @@ -73,7 +73,7 @@ export class ApAudienceService { followers: [] as string[], other: [] as string[], }; - + for (const id of ids) { if (this.isPublic(id)) { groups.public.push(id); @@ -83,12 +83,12 @@ export class ApAudienceService { groups.other.push(id); } } - + groups.other = unique(groups.other); - + return groups; } - + @bindThis private isPublic(id: string) { return [ @@ -97,7 +97,7 @@ export class ApAudienceService { 'Public', ].includes(id); } - + @bindThis private isFollowers(id: string, actor: RemoteUser) { return ( diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts index ca148916dcc471a36b4834317b5b8b78b54ad3fa..20283a163cc8d48943dff62f99ae762d1e34fcc1 100644 --- a/packages/backend/src/core/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts @@ -121,7 +121,7 @@ export class ApDbResolverService implements OnApplicationShutdown { const key = await this.userPublickeysRepository.findOneBy({ keyId, }); - + if (key == null) return null; return key; @@ -147,7 +147,7 @@ export class ApDbResolverService implements OnApplicationShutdown { if (user == null) return null; - const key = await this.publicKeyByUserIdCache.fetch(user.id, () => this.userPublickeysRepository.findOneBy({ userId: user.id }), v => v != null); + const key = await this.publicKeyByUserIdCache.fetch(user.id, () => this.userPublickeysRepository.findOneBy({ userId: user.id }), v => v != null); return { user, diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts index 6e910eb538ed48c8ceff946f852042eb8b8a7ed3..82c2c9f71fcab3ebe737e97828670919ac1ceb0e 100644 --- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -90,7 +90,7 @@ export class ApDeliverManagerService { this.followingsRepository, this.queueService, - actor, + actor, activity, ); } diff --git a/packages/backend/src/core/activitypub/ApMfmService.ts b/packages/backend/src/core/activitypub/ApMfmService.ts index 6116822f7a937d6f14e547ebdd86d96cc6cedd6d..411cf17d197c2797ec94fe17dc4f8c1566a85eff 100644 --- a/packages/backend/src/core/activitypub/ApMfmService.ts +++ b/packages/backend/src/core/activitypub/ApMfmService.ts @@ -21,7 +21,7 @@ export class ApMfmService { @bindThis public htmlToMfm(html: string, tag?: IObject | IObject[]) { const hashtagNames = extractApHashtagObjects(tag).map(x => x.name).filter((x): x is string => x != null); - + return this.mfmService.fromHtml(html, hashtagNames); } @@ -29,5 +29,5 @@ export class ApMfmService { public getNoteHtml(note: Note) { if (!note.text) return ''; return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers)); - } + } } diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts index 0043907c212f5c299db14e13f6e5109e0dec6d7e..79c5ae958aa93f90be85f2587f2c3f797ea2a62f 100644 --- a/packages/backend/src/core/activitypub/models/ApImageService.ts +++ b/packages/backend/src/core/activitypub/models/ApImageService.ts @@ -32,7 +32,7 @@ export class ApImageService { ) { this.logger = this.apLoggerService.logger; } - + /** * Imageを作æˆã—ã¾ã™ã€‚ */ diff --git a/packages/backend/src/core/activitypub/models/ApMentionService.ts b/packages/backend/src/core/activitypub/models/ApMentionService.ts index c581840ca948cd491e0df98ccc75655ba0dcb707..9beefc8fcb1043c130fe7fbfe527cb584ed81aa6 100644 --- a/packages/backend/src/core/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/core/activitypub/models/ApMentionService.ts @@ -29,10 +29,10 @@ export class ApMentionService { const mentionedUsers = (await Promise.all( hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))), )).filter((x): x is User => x != null); - + return mentionedUsers; } - + @bindThis public extractApMentionObjects(tags: IObject | IObject[] | null | undefined): IApMention[] { if (tags == null) return []; diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 76757f530a6d1dac75fc45a9cd90b9ac02a1d6e2..60a3e56800fc355b4b452780677647bffca5dbb9 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -55,7 +55,7 @@ export class ApNoteService { // 循環å‚ç…§ã®ãŸã‚ / for circular dependency @Inject(forwardRef(() => ApPersonService)) private apPersonService: ApPersonService, - + private utilityService: UtilityService, private apAudienceService: ApAudienceService, private apMentionService: ApMentionService, @@ -74,15 +74,15 @@ export class ApNoteService { @bindThis public validateNote(object: IObject, uri: string) { const expectHost = this.utilityService.extractDbHost(uri); - + if (object == null) { return new Error('invalid Note: object is null'); } - + if (!validPost.includes(getApType(object))) { return new Error(`invalid Note: invalid object type ${getApType(object)}`); } - + if (object.id && this.utilityService.extractDbHost(object.id) !== expectHost) { return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`); } @@ -91,10 +91,10 @@ export class ApNoteService { if (object.attributedTo && actualHost !== expectHost) { return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`); } - + return null; } - + /** * Noteをフェッãƒã—ã¾ã™ã€‚ * @@ -104,16 +104,16 @@ export class ApNoteService { public async fetchNote(object: string | IObject): Promise<Note | null> { return await this.apDbResolverService.getNoteFromApId(object); } - + /** * Noteを作æˆã—ã¾ã™ã€‚ */ @bindThis public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> { if (resolver == null) resolver = this.apResolverService.createResolver(); - + const object = await resolver.resolve(value); - + const entryUri = getApId(value); const err = this.validateNote(object, entryUri); if (err) { @@ -126,9 +126,9 @@ export class ApNoteService { }); throw new Error('invalid note'); } - + const note = object as IPost; - + this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); if (note.id && !checkHttps(note.id)) { @@ -140,21 +140,21 @@ export class ApNoteService { if (url && !checkHttps(url)) { throw new Error('unexpected shcema of note url: ' + url); } - + this.logger.info(`Creating the Note: ${note.id}`); - + // 投稿者をフェッムconst actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo!), resolver) as RemoteUser; - + // 投稿者ãŒå‡çµã•ã‚Œã¦ã„ãŸã‚‰ã‚¹ã‚ップ if (actor.isSuspended) { throw new Error('actor has been suspended'); } - + const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver); let visibility = noteAudience.visibility; const visibleUsers = noteAudience.visibleUsers; - + // Audience (to, cc) ãŒæŒ‡å®šã•ã‚Œã¦ãªã‹ã£ãŸå ´åˆ if (visibility === 'specified' && visibleUsers.length === 0) { if (typeof value === 'string') { // 入力ãŒstringãªã‚‰ã°resolverã§GETãŒç™ºç”Ÿã—ã¦ã„ã‚‹ @@ -162,23 +162,23 @@ export class ApNoteService { visibility = 'public'; } } - + const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver); const apHashtags = await extractApHashtags(note.tag); - + // 添付ファイル // TODO: attachmentã¯å¿…ãšã—ã‚‚Imageã§ã¯ãªã„ // TODO: attachmentã¯å¿…ãšã—ã‚‚é…列ã§ã¯ãªã„ // NoteãŒsensitiveãªã‚‰æ·»ä»˜ã‚‚sensitiveã«ã™ã‚‹ const limit = promiseLimit(2); - + note.attachment = Array.isArray(note.attachment) ? note.attachment : note.attachment ? [note.attachment] : []; const files = note.attachment .map(attach => attach.sensitive = note.sensitive) ? (await Promise.all(note.attachment.map(x => limit(() => this.apImageService.resolveImage(actor, x)) as Promise<DriveFile>))) .filter(image => image != null) : []; - + // リプライ const reply: Note | null = note.inReplyTo ? await this.resolveNote(note.inReplyTo, resolver).then(x => { @@ -193,10 +193,10 @@ export class ApNoteService { throw err; }) : null; - + // 引用 let quote: Note | undefined | null; - + if (note._misskey_quote || note.quoteUrl) { const tryResolveNote = async (uri: string): Promise<{ status: 'ok'; @@ -223,10 +223,10 @@ export class ApNoteService { }; } }; - + const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string')); const results = await Promise.all(uris.map(uri => tryResolveNote(uri))); - + quote = results.filter((x): x is { status: 'ok', res: Note | null } => x.status === 'ok').map(x => x.res).find(x => x); if (!quote) { if (results.some(x => x.status === 'temperror')) { @@ -234,9 +234,9 @@ export class ApNoteService { } } } - + const cw = note.summary === '' ? null : note.summary; - + // テã‚ストã®ãƒ‘ース let text: string | null = null; if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') { @@ -246,38 +246,38 @@ export class ApNoteService { } else if (typeof note.content === 'string') { text = this.apMfmService.htmlToMfm(note.content, note.tag); } - + // vote if (reply && reply.hasPoll) { const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id }); - + const tryCreateVote = async (name: string, index: number): Promise<null> => { if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) { this.logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); } else if (index >= 0) { this.logger.info(`vote from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); await this.pollService.vote(actor, reply, index); - + // リモートフォãƒãƒ¯ãƒ¼ã«Updateé…ä¿¡ this.pollService.deliverQuestionUpdate(reply.id); } return null; }; - + if (note.name) { return await tryCreateVote(note.name, poll.choices.findIndex(x => x === note.name)); } } - + const emojis = await this.extractEmojis(note.tag ?? [], actor.host).catch(e => { this.logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; }); - + const apEmojis = emojis.map(emoji => emoji.name); - + const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); - + return await this.noteCreateService.create(actor, { createdAt: note.published ? new Date(note.published) : null, files, @@ -297,7 +297,7 @@ export class ApNoteService { url: url, }, silent); } - + /** * Noteを解決ã—ã¾ã™ã€‚ * @@ -308,26 +308,26 @@ export class ApNoteService { public async resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> { const uri = typeof value === 'string' ? value : value.id; if (uri == null) throw new Error('missing uri'); - + // ブãƒãƒƒã‚¯ã—ã¦ãŸã‚‰ä¸æ– const meta = await this.metaService.fetch(); if (this.utilityService.isBlockedHost(meta.blockedHosts, this.utilityService.extractDbHost(uri))) throw new StatusError('blocked host', 451); - + const unlock = await this.appLockService.getApLock(uri); - + try { //#region ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«æ—¢ã«ç™»éŒ²ã•ã‚Œã¦ã„ãŸã‚‰ãれを返㙠const exist = await this.fetchNote(uri); - + if (exist) { return exist; } //#endregion - + if (uri.startsWith(this.config.url)) { throw new StatusError('cannot resolve local note', 400, 'cannot resolve local note'); } - + // リモートサーãƒãƒ¼ã‹ã‚‰ãƒ•ã‚§ãƒƒãƒã—ã¦ãã¦ç™»éŒ² // ã“ã“ã§uriã®ä»£ã‚ã‚Šã«æ·»ä»˜ã•ã‚Œã¦ããŸNote ObjectãŒæŒ‡å®šã•ã‚Œã¦ã„ã‚‹ã¨ã€ã‚µãƒ¼ãƒãƒ¼ãƒ•ã‚§ãƒƒãƒã‚’経ãšã«ãƒŽãƒ¼ãƒˆãŒç”Ÿæˆã•ã‚Œã‚‹ãŒ // 添付ã•ã‚Œã¦ããŸNote Objectã¯å½è£…ã•ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚‹ãŸã‚ã€å¸¸ã«uriを指定ã—ã¦ã‚µãƒ¼ãƒãƒ¼ãƒ•ã‚§ãƒƒãƒã‚’è¡Œã†ã€‚ @@ -336,26 +336,26 @@ export class ApNoteService { unlock(); } } - + @bindThis public async extractEmojis(tags: IObject | IObject[], host: string): Promise<Emoji[]> { host = this.utilityService.toPuny(host); - + if (!tags) return []; - + const eomjiTags = toArray(tags).filter(isEmoji); const existingEmojis = await this.emojisRepository.findBy({ host, name: In(eomjiTags.map(tag => tag.name!.replaceAll(':', ''))), }); - + return await Promise.all(eomjiTags.map(async tag => { const name = tag.name!.replaceAll(':', ''); tag.icon = toSingle(tag.icon); - + const exists = existingEmojis.find(x => x.name === name); - + if (exists) { if ((tag.updated != null && exists.updatedAt == null) || (tag.id != null && exists.uri == null) @@ -371,18 +371,18 @@ export class ApNoteService { publicUrl: tag.icon!.url, updatedAt: new Date(), }); - + return await this.emojisRepository.findOneBy({ host, name, }) as Emoji; } - + return exists; } - + this.logger.info(`register emoji host=${host}, name=${name}`); - + return await this.emojisRepository.insert({ id: this.idService.genId(), host, diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index f52ebed107537bda65d3b90d29c6a2053452e9b5..afe89e5a110b38d755006e2b73852703d008c82b 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -415,7 +415,7 @@ export class ApPersonService implements OnModuleInit { * Personã®æƒ…å ±ã‚’æ›´æ–°ã—ã¾ã™ã€‚ * Misskeyã«å¯¾è±¡ã®PersonãŒç™»éŒ²ã•ã‚Œã¦ã„ãªã‘ã‚Œã°ç„¡è¦–ã—ã¾ã™ã€‚ * ã‚‚ã—アカウントã®ç§»è¡ŒãŒç¢ºèªã•ã‚ŒãŸå ´åˆã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆç§»è¡Œå‡¦ç†ã‚’è¡Œã„ã¾ã™ã€‚ - * + * * @param uri URI of Person * @param resolver Resolver * @param hint Hint of Person object (ã“ã®å€¤ãŒæ£å½“ãªPersonã®å ´åˆã€Remote resolveã‚’ã›ãšã«æ›´æ–°ã«åˆ©ç”¨ã—ã¾ã™) @@ -688,7 +688,7 @@ export class ApPersonService implements OnModuleInit { // (uriãŒå˜åœ¨ã—ãªã‹ã£ãŸã‚Šå¿œç”ãŒãªã‹ã£ãŸã‚Šã™ã‚‹å ´åˆresolvePersonã¯throw Errorã™ã‚‹) dst = await this.resolvePerson(src.movedToUri); } - + if (dst.movedToUri === dst.uri) return 'skip: movedTo itself (dst)'; // ??? if (src.movedToUri !== dst.uri) return 'skip: missmatch uri'; // ??? if (dst.movedToUri === src.uri) return 'skip: dst.movedToUri === src.uri'; diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index d82f36d9718b78c866412f79e4cc694b6f2e78ee..80442af09bb37a0868447866b565dc81b51f9167 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -47,7 +47,7 @@ export class DriveFileEntityService { private videoProcessingService: VideoProcessingService, ) { } - + @bindThis public validateFileName(name: string): boolean { return ( diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 32269a4101418cc1664e76a4f8880cfbc8cd0175..e730c0eb8f95e0de3718dd5156c1cf5eb5459fdf 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -24,7 +24,7 @@ export class NoteEntityService implements OnModuleInit { private driveFileEntityService: DriveFileEntityService; private customEmojiService: CustomEmojiService; private reactionService: ReactionService; - + constructor( private moduleRef: ModuleRef, @@ -68,7 +68,7 @@ export class NoteEntityService implements OnModuleInit { this.customEmojiService = this.moduleRef.get('CustomEmojiService'); this.reactionService = this.moduleRef.get('ReactionService'); } - + @bindThis private async hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) { // TODO: isVisibleForMe を使ã†ã‚ˆã†ã«ã—ã¦ã‚‚良ã•ãã†(åž‹é•ã†ã‘ã©) @@ -457,12 +457,12 @@ export class NoteEntityService implements OnModuleInit { const query = this.notesRepository.createQueryBuilder('note') .where('note.userId = :userId', { userId }) .andWhere('note.renoteId = :renoteId', { renoteId }); - + // 指定ã—ãŸæŠ•ç¨¿ã‚’除ã if (excludeNoteId) { query.andWhere('note.id != :excludeNoteId', { excludeNoteId }); } - + return await query.getCount(); - } + } } diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index 8f943ba24c2ef394ce38a6e4e9e5e35a176ffaa4..d454ddb70a95176f12b3cd9931797b65090fb747 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -17,7 +17,7 @@ export class NoteReactionEntityService implements OnModuleInit { private userEntityService: UserEntityService; private noteEntityService: NoteEntityService; private reactionService: ReactionService; - + constructor( private moduleRef: ModuleRef, diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index d76b863957393aa999c8fa60f91d8392d364bb58..02c6982847e3e438466e7364f28e0920deb2470e 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -59,7 +59,7 @@ export class NotificationEntityService implements OnModuleInit { meId: User['id'], // eslint-disable-next-line @typescript-eslint/ban-types options: { - + }, hint?: { packedNotes: Map<Note['id'], Packed<'Note'>>; diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index f1a4e56c025e2ab60615b286151b0674239f1b6f..6dacaa103228898a009441caa8c694420527d77d 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -113,7 +113,7 @@ export class UserEntityService implements OnModuleInit { @Inject(DI.pagesRepository) private pagesRepository: PagesRepository, - + @Inject(DI.userMemosRepository) private userMemosRepository: UserMemoRepository, diff --git a/packages/backend/src/daemons/QueueStatsService.ts b/packages/backend/src/daemons/QueueStatsService.ts index 53a0d14cd78d70ad44a8f062e10345f58f0a5f92..4d0cb96a2f798cc16b24638f1315e2f6e030fe7a 100644 --- a/packages/backend/src/daemons/QueueStatsService.ts +++ b/packages/backend/src/daemons/QueueStatsService.ts @@ -81,7 +81,7 @@ export class QueueStatsService implements OnApplicationShutdown { this.intervalId = setInterval(tick, interval); } - + @bindThis public dispose(): void { clearInterval(this.intervalId); diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index e748f93a26c5fbd6c94960aa0e3f7247f2160004..7579040c68e2dab1a1e8ea1a0cfd6e03230afbb6 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -131,7 +131,7 @@ type NullOrUndefined<p extends Schema, T> = | T; // https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection -// Get intersection from union +// Get intersection from union type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; type PartialIntersection<T> = Partial<UnionToIntersection<T>>; diff --git a/packages/backend/src/misc/prelude/url.ts b/packages/backend/src/misc/prelude/url.ts index 9b1dabc789be8475d8621fe68ecc04cf0d7ca9c7..52396782809aec730d030e6d5ac1e787ad7c9c5b 100644 --- a/packages/backend/src/misc/prelude/url.ts +++ b/packages/backend/src/misc/prelude/url.ts @@ -2,7 +2,7 @@ * 1. é…列ã«ä½•ã‚‚å…¥ã£ã¦ã„ãªã„時ã¯ã‚¯ã‚¨ãƒªã‚’付ã‘ãªã„ * 2. プãƒãƒ‘ティãŒundefinedã®æ™‚ã¯ã‚¯ã‚¨ãƒªã‚’付ã‘ãªã„ * (new URLSearchParams(obj)ã§ã¯ãã“ã¾ã§ä¸å¯§ãªã“ã¨ã‚’ã—ã¦ãã‚Œãªã„) - */ + */ export function query(obj: Record<string, unknown>): string { const params = Object.entries(obj) .filter(([, v]) => Array.isArray(v) ? v.length : v !== undefined) diff --git a/packages/backend/src/models/entities/UserProfile.ts b/packages/backend/src/models/entities/UserProfile.ts index 236ee8f9884f1b062b910c06518905005707fbef..c4ed9db9bb5cbbc8861793e1cf753d519ce1f043 100644 --- a/packages/backend/src/models/entities/UserProfile.ts +++ b/packages/backend/src/models/entities/UserProfile.ts @@ -207,7 +207,7 @@ export class UserProfile { public mutedInstances: string[]; @Column('enum', { - enum: [ + enum: [ ...notificationTypes, // マイグレーションã§å‰Šé™¤ãŒå›°é›£ãªã®ã§å¤ã„enumã¯æ®‹ã—ã¦ãŠã ...obsoleteNotificationTypes, diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index 42f9c1af7d7a72a911f7b14a948723a954016ef3..f575b1718e843a322db6464537c90be817b34717 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -283,7 +283,7 @@ export class QueueProcessorService implements OnApplicationShutdown { }); const relationshipLogger = this.logger.createSubLogger('relationship'); - + this.relationshipQueueWorker .on('active', (job) => relationshipLogger.debug(`active id=${job.id}`)) .on('completed', (job, result) => relationshipLogger.debug(`completed(${result}) id=${job.id}`)) diff --git a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts index ac52325c8d998368d778a8b86057c8fef9725138..21c0bfe80e19daa493609bbd555b1de668ec4cf6 100644 --- a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts @@ -30,7 +30,7 @@ export class ExportAntennasProcessorService { @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: UserListJoiningsRepository, - + private driveService: DriveService, private utilityService: UtilityService, private queueLoggerService: QueueLoggerService, diff --git a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts index 0c09f2796f46447314c981f3a19661583017e1af..74ef20fdd8b841e852c2725e060bbd855583c98f 100644 --- a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts @@ -17,11 +17,11 @@ const validate = new Ajv().compile({ properties: { name: { type: 'string', minLength: 1, maxLength: 100 }, src: { type: 'string', enum: ['home', 'all', 'users', 'list'] }, - userListAccts: { - type: 'array', + userListAccts: { + type: 'array', items: { type: 'string', - }, + }, nullable: true, }, keywords: { type: 'array', items: { diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index d862567871570f06f15e51e469fcc9c3a81fbc4c..4ba749ec5239b78ea6290c16fe613e4093a67c99 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -113,7 +113,7 @@ export class ImportCustomEmojisProcessorService { } cleanup(); - + this.logger.succ('Imported'); }); unzipStream.pipe(extractor); diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index 8b40c167496d1b52ab7e934477522558a6f7168f..25e91761ef284acbafdedfc550dc9cc04acada95 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -31,7 +31,7 @@ export class WebhookDeliverProcessorService { public async process(job: Bull.Job<WebhookDeliverJobData>): Promise<string> { try { this.logger.debug(`delivering ${job.data.webhookId}`); - + const res = await this.httpRequestService.send(job.data.to, { method: 'POST', headers: { @@ -50,25 +50,25 @@ export class WebhookDeliverProcessorService { body: job.data.content, }), }); - + this.webhooksRepository.update({ id: job.data.webhookId }, { latestSentAt: new Date(), latestStatus: res.status, }); - + return 'Success'; } catch (res) { this.webhooksRepository.update({ id: job.data.webhookId }, { latestSentAt: new Date(), latestStatus: res instanceof StatusError ? res.statusCode : 1, }); - + if (res instanceof StatusError) { // 4xx if (res.isClientError) { throw new Bull.UnrecoverableError(`${res.statusCode} ${res.statusMessage}`); } - + // 5xx etc. throw new Error(`${res.statusCode} ${res.statusMessage}`); } else { diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index 4ad0197d87d895b72409b3bea4c55fda62e2115c..8b0fff80d91aafc154eabffc156d0ec47f2c6201 100644 --- a/packages/backend/src/server/api/AuthenticateService.ts +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -40,15 +40,15 @@ export class AuthenticateService implements OnApplicationShutdown { if (token == null) { return [null, null]; } - + if (isNativeToken(token)) { const user = await this.cacheService.localUserByNativeTokenCache.fetch(token, () => this.usersRepository.findOneBy({ token }) as Promise<LocalUser | null>); - + if (user == null) { throw new AuthenticationError('user not found'); } - + return [user, null]; } else { const accessToken = await this.accessTokensRepository.findOne({ @@ -58,24 +58,24 @@ export class AuthenticateService implements OnApplicationShutdown { token: token, // miauth }], }); - + if (accessToken == null) { throw new AuthenticationError('invalid signature'); } - + this.accessTokensRepository.update(accessToken.id, { lastUsedAt: new Date(), }); - + const user = await this.cacheService.localUserByIdCache.fetch(accessToken.userId, () => this.usersRepository.findOneBy({ id: accessToken.userId, }) as Promise<LocalUser>); - + if (accessToken.appId) { const app = await this.appCache.fetch(accessToken.appId, () => this.appsRepository.findOneByOrFail({ id: accessToken.appId! })); - + return [user, { id: accessToken.id, permission: app.permission, diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts index fe2db1d66a676a18367895deaee6532a0c9a1594..f6ffbfab50cd81dc493bbe8dea0b53ffa92b4755 100644 --- a/packages/backend/src/server/api/RateLimiterService.ts +++ b/packages/backend/src/server/api/RateLimiterService.ts @@ -38,14 +38,14 @@ export class RateLimiterService { max: 1, db: this.redisClient, }); - + minIntervalLimiter.get((err, info) => { if (err) { return reject('ERR'); } - + this.logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`); - + if (info.remaining === 0) { reject('BRIEF_REQUEST_INTERVAL'); } else { @@ -57,7 +57,7 @@ export class RateLimiterService { } }); }; - + // Long term limit const max = (): void => { const limiter = new Limiter({ @@ -66,14 +66,14 @@ export class RateLimiterService { max: limitation.max! / factor, db: this.redisClient, }); - + limiter.get((err, info) => { if (err) { return reject('ERR'); } - + this.logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`); - + if (info.remaining === 0) { reject('RATE_LIMIT_EXCEEDED'); } else { @@ -81,13 +81,13 @@ export class RateLimiterService { } }); }; - + const hasShortTermLimit = typeof limitation.minInterval === 'number'; - + const hasLongTermLimit = typeof limitation.duration === 'number' && typeof limitation.max === 'number'; - + if (hasShortTermLimit) { min(); } else if (hasLongTermLimit) { diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index aaf1d10b42a39739f5dede7437369108938613b4..96666f1f49b5eecbe3ecf4377dca9dfdd91e111b 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -36,7 +36,7 @@ export class SigninService { headers: request.headers as any, success: true, }).then(x => this.signinsRepository.findOneByOrFail(x.identifiers[0])); - + // Publish signin event this.globalEventService.publishMainStream(user.id, 'signin', await this.signinEntityService.pack(record)); }); diff --git a/packages/backend/src/server/api/endpoint-base.ts b/packages/backend/src/server/api/endpoint-base.ts index 05141854c78c82b27d802bb21c73dae681b96da5..364fa7a19b972f91fe7c29c80f6465de7ae5721a 100644 --- a/packages/backend/src/server/api/endpoint-base.ts +++ b/packages/backend/src/server/api/endpoint-base.ts @@ -34,23 +34,23 @@ export abstract class Endpoint<T extends IEndpointMeta, Ps extends Schema> { this.exec = (params: any, user: T['requireCredential'] extends true ? LocalUser : LocalUser | null, token: AccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => { let cleanup: undefined | (() => void) = undefined; - + if (meta.requireFile) { cleanup = () => { if (file) fs.unlink(file.path, () => {}); }; - + if (file == null) return Promise.reject(new ApiError({ message: 'File required.', code: 'FILE_REQUIRED', id: '4267801e-70d1-416a-b011-4ee502885d8b', })); } - + const valid = validate(params); if (!valid) { if (file) cleanup!(); - + const errors = validate.errors!; const err = new ApiError({ message: 'Invalid param.', @@ -62,7 +62,7 @@ export abstract class Endpoint<T extends IEndpointMeta, Ps extends Schema> { }); return Promise.reject(err); } - + return cb(params as SchemaType<Ps>, user, token, file, cleanup, ip, headers); }; } diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts index 12db1f78fb689fca3f87aeb82fb4cc2d0f45a1db..8cf9341a71621123905c1f556bf9a2902bb8fc9d 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts @@ -47,7 +47,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { title: ps.title, text: ps.text, /* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空ã®æ–‡å—列ã®å ´åˆã€nullを渡ã™ã‚ˆã†ã«ã™ã‚‹ãŸã‚ */ - imageUrl: ps.imageUrl || null, + imageUrl: ps.imageUrl || null, }); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 4aa4ad82b4064b71fa79fa75cce07acd067f77be..1a6a361683aa44f99f4482ef21345dc70d362e8d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -90,7 +90,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const queryarry = ps.query.match(/\:([a-z0-9_]*)\:/g); if (queryarry) { - emojis = emojis.filter(emoji => + emojis = emojis.filter(emoji => queryarry.includes(`:${emoji.name}:`) ); } else { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index fb22bdc477e0a75894b9c3085fbbeb2087943feb..edc1af5a53219e964d52ad1c8f5f41b978e194ab 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); } - + await this.customEmojiService.update(ps.id, { driveFile, name: ps.name, diff --git a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts index 4e57e6613e8ccb29365b9e67d8b4ec83b1b28ae1..9140f392cdc36e6036dc7c37486b3e8692a27b9a 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts @@ -36,7 +36,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { await queue.promote(); } break; - + case 'inbox': delayedQueues = await this.queueService.inboxQueue.getDelayed(); for (let queueIndex = 0; queueIndex < delayedQueues.length; queueIndex++) { diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 5c9d8e3fac453f0a8c8e1206ba5a71bd19de35de..0b20b058fdc373b330549ce5607fe8dd8fe4e42d 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -136,7 +136,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (Array.isArray(ps.sensitiveWords)) { set.sensitiveWords = ps.sensitiveWords.filter(Boolean); } - + if (ps.themeColor !== undefined) { set.themeColor = ps.themeColor; } diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index c881074bab57afe3755c8de71d152675ebdc2490..833bd63c1c696603885503ef8017ad20972e4cc4 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -77,7 +77,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const limit = ps.limit + (ps.untilId ? 1 : 0); // untilIdã«æŒ‡å®šã—ãŸã‚‚ã®ã‚‚å«ã¾ã‚Œã‚‹ãŸã‚+1 let noteIdsRes: [string, string[]][] = []; - + if (!ps.sinceId && !ps.sinceDate) { noteIdsRes = await this.redisClient.xrevrange( `channelTimeline:${channel.id}`, diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index 3ecbba22b54d5b4e1dce1e838ce69e8dcc693f65..c43f812e2fb2a4257af7038fedddd133f5039c4a 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -40,7 +40,7 @@ export const meta = { code: 'NO_SUCH_FOLDER', id: 'ea8fb7a5-af77-4a08-b608-c0218176cd73', }, - + restrictedByRole: { message: 'This feature is restricted by your role.', code: 'RESTRICTED_BY_ROLE', diff --git a/packages/backend/src/server/api/endpoints/emoji.ts b/packages/backend/src/server/api/endpoints/emoji.ts index 681d3e649eb6054e0b19a059b01b603f0e54cc7e..51027f35c09d338cb1b029da3bdd66f337c497bb 100644 --- a/packages/backend/src/server/api/endpoints/emoji.ts +++ b/packages/backend/src/server/api/endpoints/emoji.ts @@ -36,7 +36,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.config) private config: Config, - + @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, diff --git a/packages/backend/src/server/api/endpoints/emojis.ts b/packages/backend/src/server/api/endpoints/emojis.ts index 13cc709d31793d73c044e6bb79f2ae9c9e7cb683..3c2d0ce4a42f3bb1abbced783841654fc39f1984 100644 --- a/packages/backend/src/server/api/endpoints/emojis.ts +++ b/packages/backend/src/server/api/endpoints/emojis.ts @@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.config) private config: Config, - + @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index dd3549020e6dcaaaed116e7209473a639499be64..a86faf41a4fba460d2409fbc13848fcd735c8aac 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -39,7 +39,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, - + private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index a3e3e02a124ec718eda2839931297a79dea4ab0a..743e3f8abcedadc2314ac6ed36ee12f83dd0cf58 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -68,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { }); userProfile.loggedInDates = [...userProfile.loggedInDates, today]; } - + return await this.userEntityService.pack<true, true>(userProfile.user!, userProfile.user!, { detail: true, includeSecrets: isSecure, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts index d98f60fa5f82f7a8f84d0a40858d2a3d5133898b..2ef5e5a279724caf23a0ca9e7fee5c1dee6a2fd2 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts @@ -61,7 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (key.userId !== me.id) { throw new ApiError(meta.errors.accessDenied); } - + await this.userSecurityKeysRepository.update(key.id, { name: ps.name, }); diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 3b3c5caa003b6ba68e1a282770d09945666f8e76..6ef5f0d5c80c55384f8d619d0229b5f4c3e145b8 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -250,7 +250,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.config) private config: Config, - + @Inject(DI.usersRepository) private usersRepository: UsersRepository, diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 5fbc7aba58fb21daf34cc6754a2b0cbc728bebe8..a6e9707d386b03f128862f0195885393ef0721af 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -53,34 +53,34 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') .leftJoinAndSelect('renote.user', 'renoteUser'); - + if (ps.local) { query.andWhere('note.userHost IS NULL'); } - + if (ps.reply !== undefined) { query.andWhere(ps.reply ? 'note.replyId IS NOT NULL' : 'note.replyId IS NULL'); } - + if (ps.renote !== undefined) { query.andWhere(ps.renote ? 'note.renoteId IS NOT NULL' : 'note.renoteId IS NULL'); } - + if (ps.withFiles !== undefined) { query.andWhere(ps.withFiles ? 'note.fileIds != \'{}\'' : 'note.fileIds = \'{}\''); } - + if (ps.poll !== undefined) { query.andWhere(ps.poll ? 'note.hasPoll = TRUE' : 'note.hasPoll = FALSE'); } - + // TODO //if (bot != undefined) { // query.isBot = bot; //} - + const notes = await query.take(ps.limit).getMany(); - + return await this.noteEntityService.packMany(notes); }); } diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index f6385400c3c778c2be6531332d621569244c8a65..cd0e351e4586780b33fea89ce78a819cdb35f77e 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -58,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.config) private config: Config, - + private noteEntityService: NoteEntityService, private searchService: SearchService, private roleService: RoleService, @@ -68,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (!policies.canSearchNotes) { throw new ApiError(meta.errors.unavailable); } - + const notes = await this.searchService.searchNote(ps.query, me, { userId: ps.userId, channelId: ps.channelId, diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index 66655234a157b8e4c613523eca1b7013b2c1299e..b91bc7b5ecf79a4526fd7eab653b756944721d0d 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -44,7 +44,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.config) private config: Config, - + @Inject(DI.notesRepository) private notesRepository: NotesRepository, diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index 42e36cb04a7e12bc5d7c3cc9bb25f01a923436d7..a30c31b727934f4c05a361092a6b5a838819972c 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (role == null) { throw new ApiError(meta.errors.noSuchRole); } - if (!role.isExplorable) { + if (!role.isExplorable) { return []; } const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdã«æŒ‡å®šã—ãŸã‚‚ã®ã‚‚å«ã¾ã‚Œã‚‹ãŸã‚+1 diff --git a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts index 8591e4ab965b20ddce2a2f453f89f98651449884..cccffcdad42c2e384129ff6a7771929080720a3e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts @@ -95,7 +95,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (currentCount > (await this.roleService.getUserPolicies(me.id)).userListLimit) { throw new ApiError(meta.errors.tooManyUserLists); } - + const userList = await this.userListsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), @@ -127,7 +127,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { userListId: userList.id, userId: currentUser.id, }); - + if (exist) { throw new ApiError(meta.errors.alreadyAdded); } diff --git a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts index 263852fde1ffe440596fb2ac6d3cff5c4d544abd..ea1a022bec446f770fa004c65945793d5df63607 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts @@ -41,7 +41,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const userList = await this.userListsRepository.findOneBy({ + const userList = await this.userListsRepository.findOneBy({ id: ps.listId, isPublic: true, }); diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index d7a60f0437ea87495534f407ed2e5c57ddefff9a..5ffe3e84aa427e832890fb3caaf837b894fc7dcd 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -78,7 +78,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { .getMany(); } else { const nameQuery = this.usersRepository.createQueryBuilder('user') - .where(new Brackets(qb => { + .where(new Brackets(qb => { qb.where('user.name ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' }); // Also search username if it qualifies as username diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index c77ba66028af12981083ee918c309a27e73b1c7f..4a544fadfe9b46f84bfe614fd170ca91af12c399 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -52,7 +52,7 @@ export class ChannelsService { case 'serverStats': return this.serverStatsChannelService; case 'queueStats': return this.queueStatsChannelService; case 'admin': return this.adminChannelService; - + default: throw new Error(`no such channel: ${name}`); } diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 0268fdeddeb3c2939dc135db2401407387c96180..94ebf864184ae04deea88c9a2df1e2b4c90cc8ae 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -49,7 +49,7 @@ class HashtagChannel extends Channel { if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // æµã‚Œã¦ããŸNoteãŒãƒ–ãƒãƒƒã‚¯ã•ã‚Œã¦ã„るユーザーãŒé–¢ã‚ã‚‹ã‚‚ã®ã ã£ãŸã‚‰ç„¡è¦–ã™ã‚‹ if (isUserRelated(note, this.userIdsWhoBlockingMe)) return; - + if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; this.connection.cacheNote(note); diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 1755aa94cf13545673d6f75ff58f79aa572b24f9..fe0cc37b6b9ef5ac8726930ba51906c18742e3f4 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -26,7 +26,7 @@ class HomeTimelineChannel extends Channel { @bindThis public async init(params: any) { this.withReplies = params.withReplies as boolean; - + this.subscriber.on('notesStream', this.onNote); } diff --git a/packages/backend/src/server/api/stream/channels/role-timeline.ts b/packages/backend/src/server/api/stream/channels/role-timeline.ts index ab9c1aa0b52aad31406ff82ab399d97a67d3739e..4f36832e420d421b9331282ab25791baafce76eb 100644 --- a/packages/backend/src/server/api/stream/channels/role-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/role-timeline.ts @@ -12,7 +12,7 @@ class RoleTimelineChannel extends Channel { public static shouldShare = false; public static requireCredential = false; private roleId: string; - + constructor( private noteEntityService: NoteEntityService, private roleservice: RoleService, diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index 8802fc5ab8b8e5995202188bc602b50e55577a62..d97d2b8ba9f5b99cf4e11156a1b4d5506c2fe137 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -20,7 +20,7 @@ class UserListChannel extends Channel { private userListsRepository: UserListsRepository, private userListJoiningsRepository: UserListJoiningsRepository, private noteEntityService: NoteEntityService, - + id: string, connection: Channel['connection'], ) { diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 0c0e92cc04ff26f04b96781c45d3c02cfc28879b..0bd0d3c6922a58a9fa72e1bf64b8e93f814ba4fd 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -38,9 +38,9 @@ export class FeedService { link: `${this.config.url}/@${user.username}`, name: user.name ?? user.username, }; - + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - + const notes = await this.notesRepository.find({ where: { userId: user.id, @@ -50,7 +50,7 @@ export class FeedService { order: { createdAt: -1 }, take: 20, }); - + const feed = new Feed({ id: author.link, title: `${author.name} (@${user.username}@${this.config.host})`, @@ -66,13 +66,13 @@ export class FeedService { author, copyright: user.name ?? user.username, }); - + for (const note of notes) { const files = note.fileIds.length > 0 ? await this.driveFilesRepository.findBy({ id: In(note.fileIds), }) : []; const file = files.find(file => file.type.startsWith('image/')); - + feed.addItem({ title: `New note by ${author.name}`, link: `${this.config.url}/notes/${note.id}`, @@ -82,7 +82,7 @@ export class FeedService { image: file ? this.driveFileEntityService.getPublicUrl(file) ?? undefined : undefined, }); } - + return feed; } } diff --git a/packages/backend/src/server/web/bios.js b/packages/backend/src/server/web/bios.js index c2ce5c38142ed12208aacd1fba11291e594a7659..51899dd3a3d8435198baee940e464614bd181d92 100644 --- a/packages/backend/src/server/web/bios.js +++ b/packages/backend/src/server/web/bios.js @@ -8,7 +8,7 @@ window.onload = async () => { const promise = new Promise((resolve, reject) => { // Append a credential if (i) data.i = i; - + // Send request window.fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, { method: 'POST', @@ -17,7 +17,7 @@ window.onload = async () => { cache: 'no-cache' }).then(async (res) => { const body = res.status === 204 ? null : await res.json(); - + if (res.status === 200) { resolve(body); } else if (res.status === 204) { @@ -27,7 +27,7 @@ window.onload = async () => { } }).catch(reject); }); - + return promise; }; diff --git a/packages/backend/src/server/web/cli.js b/packages/backend/src/server/web/cli.js index 3467f7ac2aa561f8e4c19ca676e6969b423d8408..5bb576a27bd2b65cd13ffb1212db913b890bd09e 100644 --- a/packages/backend/src/server/web/cli.js +++ b/packages/backend/src/server/web/cli.js @@ -8,7 +8,7 @@ window.onload = async () => { const promise = new Promise((resolve, reject) => { // Append a credential if (i) data.i = i; - + // Send request fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, { headers: { @@ -20,7 +20,7 @@ window.onload = async () => { cache: 'no-cache' }).then(async (res) => { const body = res.status === 204 ? null : await res.json(); - + if (res.status === 200) { resolve(body); } else if (res.status === 204) { @@ -30,7 +30,7 @@ window.onload = async () => { } }).catch(reject); }); - + return promise; }; diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index d23a1d49c98ff5ed7237f517969205b8e1cfaa55..69a4e373223bec9058b294558ace5a359bb00f6e 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -55,8 +55,8 @@ html block meta block og - meta(property='og:title' content= title || 'Misskey') - meta(property='og:description' content= desc || '✨🌎✨ A interplanetary communication platform ✨🚀✨') + meta(property='og:title' content= title || 'Misskey') + meta(property='og:description' content= desc || '✨🌎✨ A interplanetary communication platform ✨🚀✨') meta(property='og:image' content= img) meta(property='twitter:card' content='summary') diff --git a/packages/backend/src/server/web/views/error.pug b/packages/backend/src/server/web/views/error.pug index b177ae41104177d3769040173fd7b42f556ea575..44ebf53cf714e200f2cfa07594de6f7ef21db2b5 100644 --- a/packages/backend/src/server/web/views/error.pug +++ b/packages/backend/src/server/web/views/error.pug @@ -32,12 +32,12 @@ body path(stroke="none", d="M0 0h24v24H0z", fill="none") path(d="M12 9v2m0 4v.01") path(d="M5 19h14a2 2 0 0 0 1.84 -2.75l-7.1 -12.25a2 2 0 0 0 -3.5 0l-7.1 12.25a2 2 0 0 0 1.75 2.75") - + h1 An error has occurred! button.button-big(onclick="location.reload();") span.button-label-big Refresh - + p.dont-worry Don't worry, it's (probably) not your fault. p If reloading after a period of time does not resolve the problem, contact the server administrator with the following ERROR ID. diff --git a/packages/backend/src/server/web/views/note.pug b/packages/backend/src/server/web/views/note.pug index ea0917a80e1ade98729aca2bf1397f2439c3dd5b..98d0c9a7896af047c63db2654e2b88c3081f0250 100644 --- a/packages/backend/src/server/web/views/note.pug +++ b/packages/backend/src/server/web/views/note.pug @@ -43,7 +43,7 @@ block meta meta(name='misskey:user-username' content=user.username) meta(name='misskey:user-id' content=user.id) meta(name='misskey:note-id' content=note.id) - + // todo if user.twitter meta(name='twitter:creator' content=`@${user.twitter.screenName}`) diff --git a/packages/backend/test/prelude/get-api-validator.ts b/packages/backend/test/prelude/get-api-validator.ts index 1f4a2dbc95720638192d498f77118737eb30d7ea..f095774760840f1d0c348ddd945065b73371de6f 100644 --- a/packages/backend/test/prelude/get-api-validator.ts +++ b/packages/backend/test/prelude/get-api-validator.ts @@ -5,7 +5,7 @@ export const getValidator = (paramDef: Schema) => { const ajv = new Ajv({ useDefaults: true, }); - ajv.addFormat('misskey:id', /^[a-zA-Z0-9]+$/); + ajv.addFormat('misskey:id', /^[a-zA-Z0-9]+$/); return ajv.compile(paramDef); } diff --git a/packages/backend/test/unit/DriveService.ts b/packages/backend/test/unit/DriveService.ts index 4065665579556365f475e6cbb86ceae062c60f95..9ee6d4bcfb62b970911a27527598db6e40e24301 100644 --- a/packages/backend/test/unit/DriveService.ts +++ b/packages/backend/test/unit/DriveService.ts @@ -34,7 +34,7 @@ describe('DriveService', () => { test('delete a file', async () => { s3Mock.on(DeleteObjectCommand) .resolves({} as DeleteObjectCommandOutput); - + await driveService.deleteObjectStorageFile('peace of the world'); }); diff --git a/packages/backend/test/unit/FileInfoService.ts b/packages/backend/test/unit/FileInfoService.ts index f378184c74f3bf1a7333f2a44194e7af26848945..efb9bdacc30d0e49731f499ad85c68cd852715e2 100644 --- a/packages/backend/test/unit/FileInfoService.ts +++ b/packages/backend/test/unit/FileInfoService.ts @@ -94,7 +94,7 @@ describe('FileInfoService', () => { orientation: undefined, }); }); - + test('Generic APNG', async () => { const path = `${resources}/anime.png`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; @@ -114,7 +114,7 @@ describe('FileInfoService', () => { orientation: undefined, }); }); - + test('Generic AGIF', async () => { const path = `${resources}/anime.gif`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; @@ -134,7 +134,7 @@ describe('FileInfoService', () => { orientation: undefined, }); }); - + test('PNG with alpha', async () => { const path = `${resources}/with-alpha.png`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; @@ -154,7 +154,7 @@ describe('FileInfoService', () => { orientation: undefined, }); }); - + test('Generic SVG', async () => { const path = `${resources}/image.svg`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; @@ -174,7 +174,7 @@ describe('FileInfoService', () => { orientation: undefined, }); }); - + test('SVG with XML definition', async () => { // https://github.com/misskey-dev/misskey/issues/4413 const path = `${resources}/with-xml-def.svg`; @@ -195,7 +195,7 @@ describe('FileInfoService', () => { orientation: undefined, }); }); - + test('Dimension limit', async () => { const path = `${resources}/25000x25000.png`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; @@ -215,7 +215,7 @@ describe('FileInfoService', () => { orientation: undefined, }); }); - + test('Rotate JPEG', async () => { const path = `${resources}/rotate.jpg`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; @@ -257,7 +257,7 @@ describe('FileInfoService', () => { }, }); }); - + test('WAV', async () => { const path = `${resources}/kick_gaba7.wav`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; @@ -277,7 +277,7 @@ describe('FileInfoService', () => { }, }); }); - + test('AAC', async () => { const path = `${resources}/kick_gaba7.aac`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; @@ -297,7 +297,7 @@ describe('FileInfoService', () => { }, }); }); - + test('FLAC', async () => { const path = `${resources}/kick_gaba7.flac`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; @@ -317,7 +317,7 @@ describe('FileInfoService', () => { }, }); }); - + /* * video/webmã¨ã—ã¦æ¤œå‡ºã•ã‚Œã¦ã—ã¾ã† test('WEBM AUDIO', async () => { diff --git a/packages/backend/test/unit/RelayService.ts b/packages/backend/test/unit/RelayService.ts index c2280142a6d41abc7736f5383d464b198de4bc4f..6bf08f509104ad53e7371de53a9b1884d3e0d0e3 100644 --- a/packages/backend/test/unit/RelayService.ts +++ b/packages/backend/test/unit/RelayService.ts @@ -61,7 +61,7 @@ describe('RelayService', () => { await app.close(); }); - test('addRelay', async () => { + test('addRelay', async () => { const result = await relayService.addRelay('https://example.com'); expect(result.inbox).toBe('https://example.com'); @@ -72,7 +72,7 @@ describe('RelayService', () => { //expect(queueService.deliver.mock.lastCall![0].username).toBe('relay.actor'); }); - test('listRelay', async () => { + test('listRelay', async () => { const result = await relayService.listRelay(); expect(result.length).toBe(1); @@ -80,7 +80,7 @@ describe('RelayService', () => { expect(result[0].status).toBe('requesting'); }); - test('removeRelay: succ', async () => { + test('removeRelay: succ', async () => { await relayService.removeRelay('https://example.com'); expect(queueService.deliver).toHaveBeenCalled(); @@ -93,7 +93,7 @@ describe('RelayService', () => { expect(list.length).toBe(0); }); - test('removeRelay: fail', async () => { + test('removeRelay: fail', async () => { await expect(relayService.removeRelay('https://x.example.com')) .rejects.toThrow('relay not found'); }); diff --git a/packages/backend/test/unit/chart.ts b/packages/backend/test/unit/chart.ts index 5ac4cc18a26bdea8aebd32ff0efef4c6f19be323..40554d3a472b8eec5333b33010ae8d5133d74efe 100644 --- a/packages/backend/test/unit/chart.ts +++ b/packages/backend/test/unit/chart.ts @@ -475,16 +475,16 @@ describe('Chart', () => { await testIntersectionChart.addA('bob'); await testIntersectionChart.addB('carol'); await testIntersectionChart.save(); - + const chartHours = await testIntersectionChart.getChart('hour', 3, null); const chartDays = await testIntersectionChart.getChart('day', 3, null); - + assert.deepStrictEqual(chartHours, { a: [2, 0, 0], b: [1, 0, 0], aAndB: [0, 0, 0], }); - + assert.deepStrictEqual(chartDays, { a: [2, 0, 0], b: [1, 0, 0], @@ -498,16 +498,16 @@ describe('Chart', () => { await testIntersectionChart.addB('carol'); await testIntersectionChart.addB('alice'); await testIntersectionChart.save(); - + const chartHours = await testIntersectionChart.getChart('hour', 3, null); const chartDays = await testIntersectionChart.getChart('day', 3, null); - + assert.deepStrictEqual(chartHours, { a: [2, 0, 0], b: [2, 0, 0], aAndB: [1, 0, 0], }); - + assert.deepStrictEqual(chartDays, { a: [2, 0, 0], b: [2, 0, 0], diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 76e8c5072481e1ab1984fb80385efc63c8917932..d2db5e98bea28f854f74ccae24e769858e70fcbc 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -87,7 +87,7 @@ export async function mainBoot() { const now = new Date(); const m = now.getMonth() + 1; const d = now.getDate(); - + if ($i.birthday) { const bm = parseInt($i.birthday.split('-')[1]); const bd = parseInt($i.birthday.split('-')[2]); diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue index 1969342402627708dd3c677f4554493278fd8f94..3594978729441e018a914d767187a09e412615be 100644 --- a/packages/frontend/src/components/MkDrive.folder.vue +++ b/packages/frontend/src/components/MkDrive.folder.vue @@ -93,9 +93,9 @@ function onDragover(ev: DragEvent) { switch (ev.dataTransfer.effectAllowed) { case 'all': case 'uninitialized': - case 'copy': - case 'copyLink': - case 'copyMove': + case 'copy': + case 'copyLink': + case 'copyMove': ev.dataTransfer.dropEffect = 'copy'; break; case 'linkMove': diff --git a/packages/frontend/src/components/MkDrive.navFolder.vue b/packages/frontend/src/components/MkDrive.navFolder.vue index 3349603d3b98eafc32fe67cfa57eb4b0f16593cf..df4c209c2ba8ecab2ec82de1719e72d56b62ec13 100644 --- a/packages/frontend/src/components/MkDrive.navFolder.vue +++ b/packages/frontend/src/components/MkDrive.navFolder.vue @@ -61,9 +61,9 @@ function onDragover(ev: DragEvent) { switch (ev.dataTransfer.effectAllowed) { case 'all': case 'uninitialized': - case 'copy': - case 'copyLink': - case 'copyMove': + case 'copy': + case 'copyLink': + case 'copyMove': ev.dataTransfer.dropEffect = 'copy'; break; case 'linkMove': diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 19508fe4def12057d5e1c32057ba6d2dfbf119c1..917e785139b3e9eef4478dce537cd173b0c37c54 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -202,9 +202,9 @@ function onDragover(ev: DragEvent): any { switch (ev.dataTransfer.effectAllowed) { case 'all': case 'uninitialized': - case 'copy': - case 'copyLink': - case 'copyMove': + case 'copy': + case 'copyLink': + case 'copyMove': ev.dataTransfer.dropEffect = 'copy'; break; case 'linkMove': diff --git a/packages/frontend/src/components/MkFileListForAdmin.vue b/packages/frontend/src/components/MkFileListForAdmin.vue index 71a35ae6e8e0c3ac0a47f67f43e036b3b04f643f..77b38b4bbf11f698117083db0c18f2c5efeee3f3 100644 --- a/packages/frontend/src/components/MkFileListForAdmin.vue +++ b/packages/frontend/src/components/MkFileListForAdmin.vue @@ -89,7 +89,7 @@ const props = defineProps<{ > .file { position: relative; aspect-ratio: 1; - + > .thumbnail { width: 100%; height: 100%; diff --git a/packages/frontend/src/components/MkFlashPreview.vue b/packages/frontend/src/components/MkFlashPreview.vue index 7c9ae155abda37f6b5b6cec2d4f06a5aea9c6ef1..b5505ac8fd7b22a33e308fbd261bff1c1537cb21 100644 --- a/packages/frontend/src/components/MkFlashPreview.vue +++ b/packages/frontend/src/components/MkFlashPreview.vue @@ -87,7 +87,7 @@ const props = defineProps<{ @media (max-width: 500px) { font-size: 10px; - + > article { padding: 8px; diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue index 40bae90b5e49f2202f5f3562a5841cc137e8d45a..dc5807b2dd3e85e0b93f070f0e18bff370250588 100644 --- a/packages/frontend/src/components/MkMediaVideo.vue +++ b/packages/frontend/src/components/MkMediaVideo.vue @@ -17,8 +17,8 @@ controls @contextmenu.stop > - <source - :src="video.url" + <source + :src="video.url" :type="video.type" > </video> diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index a65039277b8ee62d9b02a8f6914a8b6fad229584..1f8a36b8de56106cc548a4258e5e4082938b9ebd 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -293,7 +293,7 @@ function renote(viaKeyboard = false) { const y = rect.top + (el.offsetHeight / 2); os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - + os.api('notes/create', { renoteId: appearNote.id, }).then(() => { diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue index 72b70416d9b9636f525055d06e494df7ff5972af..0bc9b0316081aed83b6f5c158d38773ca077bbfc 100644 --- a/packages/frontend/src/components/MkSuperMenu.vue +++ b/packages/frontend/src/components/MkSuperMenu.vue @@ -46,7 +46,7 @@ defineProps<{ margin: 0 0 8px 0; font-size: 0.9em; } - + > .items { > .item { display: flex; diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue index c3b777a12ecd8571ef6d10c9c88cd5950242231d..937e0f0798fda599076a8417a0aa91bcaf3fea8c 100644 --- a/packages/frontend/src/components/MkUserPopup.vue +++ b/packages/frontend/src/components/MkUserPopup.vue @@ -195,7 +195,7 @@ onMounted(() => { .mfm { display: -webkit-box; -webkit-line-clamp: 5; - -webkit-box-orient: vertical; + -webkit-box-orient: vertical; overflow: hidden; } diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts index 7d5a65f41a2f3a652ed92701a692527486819e83..67243b78f34c45d246f59063456fa9be693d1bf4 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts +++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts @@ -26,7 +26,7 @@ export const Default = { }; }, args: { - + }, parameters: { layout: 'centered', diff --git a/packages/frontend/src/components/MkUserSetupDialog.Privacy.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.Privacy.stories.impl.ts index 70817d83c3b49af50cab15ad94a4e5bc70d5be3c..0726289722ba87ab9000db4e1838d0fa3e28114e 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Privacy.stories.impl.ts +++ b/packages/frontend/src/components/MkUserSetupDialog.Privacy.stories.impl.ts @@ -23,7 +23,7 @@ export const Default = { }; }, args: { - + }, parameters: { layout: 'centered', diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts index f4930aa26b0ba2adf535c31986df9b223daef9fc..3444605e97b23b85921741c82e1a8cc2fb5e3b25 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts +++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.stories.impl.ts @@ -23,7 +23,7 @@ export const Default = { }; }, args: { - + }, parameters: { layout: 'centered', diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue index d66f34f1650b015bb78ddb8757ea4ad785a8f902..b35f27c5b0187bd2192fcc9767be5f622af275cc 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.User.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.User.vue @@ -90,7 +90,7 @@ async function follow() { .mfm { display: -webkit-box; -webkit-line-clamp: 5; - -webkit-box-orient: vertical; + -webkit-box-orient: vertical; overflow: hidden; } diff --git a/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts index 55790602d567923b82c792afc73a243c836e3977..f47f4c13d5034a966505f87f084eeb85fc1e298f 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts +++ b/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts @@ -26,7 +26,7 @@ export const Default = { }; }, args: { - + }, parameters: { layout: 'centered', diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 2a50a34390b243785fcc64247d17ea4a8843ba02..3e756cae904b758bbcf777311f002ffc5239370f 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -199,7 +199,7 @@ export default function(props: { } const x = Math.min(parseFloat(token.props.args.x ?? '1'), 5); const y = Math.min(parseFloat(token.props.args.y ?? '1'), 5); - style = `transform: scale(${x}, ${y});`; + style = `transform: scale(${x}, ${y});`; scale = scale * Math.max(x, y); break; } diff --git a/packages/frontend/src/directives/adaptive-bg.ts b/packages/frontend/src/directives/adaptive-bg.ts index 313aad79964cbabe0599229ec17cf74e6541beba..83bcd7089d5dc36142fb5c4b835c11d22aea272e 100644 --- a/packages/frontend/src/directives/adaptive-bg.ts +++ b/packages/frontend/src/directives/adaptive-bg.ts @@ -10,7 +10,7 @@ export default { return el.parentElement ? getBgColor(el.parentElement) : 'transparent'; } }; - + const parentBg = getBgColor(src.parentElement); const myBg = window.getComputedStyle(src).backgroundColor; diff --git a/packages/frontend/src/directives/adaptive-border.ts b/packages/frontend/src/directives/adaptive-border.ts index 619c9f0b6d500ac29092a4b1170c11d35b721ac0..5bd04024beefae5d957893095159f9ea31bf8b0b 100644 --- a/packages/frontend/src/directives/adaptive-border.ts +++ b/packages/frontend/src/directives/adaptive-border.ts @@ -10,7 +10,7 @@ export default { return el.parentElement ? getBgColor(el.parentElement) : 'transparent'; } }; - + const parentBg = getBgColor(src.parentElement); const myBg = window.getComputedStyle(src).backgroundColor; diff --git a/packages/frontend/src/directives/panel.ts b/packages/frontend/src/directives/panel.ts index d31dc41ed416a85e2f7234e67de6911854c7adc9..8727183d36522478201827a5ea60167d5a90b594 100644 --- a/packages/frontend/src/directives/panel.ts +++ b/packages/frontend/src/directives/panel.ts @@ -10,7 +10,7 @@ export default { return el.parentElement ? getBgColor(el.parentElement) : 'transparent'; } }; - + const parentBg = getBgColor(src.parentElement); const myBg = getComputedStyle(document.documentElement).getPropertyValue('--panel'); diff --git a/packages/frontend/src/filters/date.ts b/packages/frontend/src/filters/date.ts index 706b7d60cc12ef8fa32b0437b6245b8d02615523..9bc9bfe8abb7018c9146ab83899ef52e199a4526 100644 --- a/packages/frontend/src/filters/date.ts +++ b/packages/frontend/src/filters/date.ts @@ -1,4 +1,4 @@ import { dateTimeFormat } from '@/scripts/intl-const'; -export default (d: Date | number | undefined) => dateTimeFormat.format(d); +export default (d: Date | number | undefined) => dateTimeFormat.format(d); export const dateString = (d: string) => dateTimeFormat.format(new Date(d)); diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts index ca4f21f79b0681a2933112cc1d760825218e4f3e..f9d04f7950af460b2facdd8ea3beb29dc26f1ede 100644 --- a/packages/frontend/src/local-storage.ts +++ b/packages/frontend/src/local-storage.ts @@ -14,7 +14,7 @@ type Keys = 'wallpaper' | 'theme' | 'colorScheme' | - 'useSystemFont' | + 'useSystemFont' | 'fontSize' | 'ui' | 'ui_temp' | diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts index 68977ed796fad23f2d99c848b0e7f57d7a0f7440..40348ded17a1f8e9e13c33b15df72b644d618b89 100644 --- a/packages/frontend/src/nirax.ts +++ b/packages/frontend/src/nirax.ts @@ -159,11 +159,11 @@ export class Router extends EventEmitter<{ if (route.hash != null && hash != null) { props.set(route.hash, safeURIDecode(hash)); } - + if (route.query != null && queryString != null) { const queryObject = [...new URLSearchParams(queryString).entries()] .reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {}); - + for (const q in route.query) { const as = route.query[q]; if (queryObject[q]) { @@ -171,7 +171,7 @@ export class Router extends EventEmitter<{ } } } - + return { route, props, diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 3744bed10ff639266784cb27245050ffafe515cc..cc0bf2eed21c391da98988e1f573cb22bead1a02 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -20,7 +20,7 @@ <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji"/> </div> </MkFoldableSection> - + <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category"> <template #header>{{ category || i18n.ts.other }}</template> <div :class="$style.emojis"> @@ -56,7 +56,7 @@ function search() { const queryarry = q.match(/\:([a-z0-9_]*)\:/g); if (queryarry) { - searchEmojis = customEmojis.value.filter(emoji => + searchEmojis = customEmojis.value.filter(emoji => queryarry.includes(`:${emoji.name}:`), ); } else { diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index 8b083bc89654e08642baba45d7daa7460b0af5d2..226eb8d02672d355cd754e42d5e11a10e90c04fe 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -1,6 +1,6 @@ <template> <div ref="el" class="hiyeyicy" :class="{ wide: !narrow }"> - <div v-if="!narrow || currentPage?.route.name == null" class="nav"> + <div v-if="!narrow || currentPage?.route.name == null" class="nav"> <MkSpacer :contentMax="700" :marginMin="16"> <div class="lxpfedzu"> <div class="banner"> diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index e36c9ac91d64baf4ece3e8bd1ad40d0640ae1437..13789820a0f4bae6b891083d3c1c3f5a3332097d 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -24,7 +24,7 @@ <template #label>{{ i18n.ts.preservedUsernames }}</template> <template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template> </MkTextarea> - + <MkTextarea v-model="sensitiveWords"> <template #label>{{ i18n.ts.sensitiveWords }}</template> <template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template> diff --git a/packages/frontend/src/pages/admin/overview.ap-requests.vue b/packages/frontend/src/pages/admin/overview.ap-requests.vue index ad8e6234155155bc49a5fc48a1724fa2a2db00c9..bde5580366352913464ebfd4313cda75a6cf58f7 100644 --- a/packages/frontend/src/pages/admin/overview.ap-requests.vue +++ b/packages/frontend/src/pages/admin/overview.ap-requests.vue @@ -259,7 +259,7 @@ onMounted(async () => { }, plugins: [chartVLine(vLineColor)], }); - + fetching = false; }); </script> diff --git a/packages/frontend/src/pages/admin/overview.federation.vue b/packages/frontend/src/pages/admin/overview.federation.vue index ab78c4c393da07e334aa3c44f00d8be6be325399..469d2e6927ee4a41029d0d1e60b398564d882368 100644 --- a/packages/frontend/src/pages/admin/overview.federation.vue +++ b/packages/frontend/src/pages/admin/overview.federation.vue @@ -58,7 +58,7 @@ let federationSubActiveDiff = $ref<number | null>(null); let fetching = $ref(true); const { handler: externalTooltipHandler } = useChartTooltip(); - + onMounted(async () => { const chart = await os.apiGet('charts/federation', { limit: 2, span: 'day' }); federationPubActive = chart.pubActive[0]; diff --git a/packages/frontend/src/pages/admin/overview.queue.vue b/packages/frontend/src/pages/admin/overview.queue.vue index 69ca89e226a7f2ecadf2558c8a0e305ec1244dff..b08757aeb851ac950acc4a147863edce73b1a9ce 100644 --- a/packages/frontend/src/pages/admin/overview.queue.vue +++ b/packages/frontend/src/pages/admin/overview.queue.vue @@ -122,4 +122,4 @@ onUnmounted(() => { } } } -</style> +</style> diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue index e8295c81b5aa242f8ae4dcd50d08cf451349711e..838c197f059792d333824348fbe5f8ca3dc276a7 100644 --- a/packages/frontend/src/pages/admin/overview.vue +++ b/packages/frontend/src/pages/admin/overview.vue @@ -30,7 +30,7 @@ <template #header>Federation</template> <XFederation/> </MkFoldableSection> - + <MkFoldableSection class="item"> <template #header>Instances</template> <XInstances/> diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index a1fa9d2932d6973a314041cb995c1257877c9a4c..02a2d4366abe6d846a384d0b10649f20a08b0de8 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -210,7 +210,7 @@ </MkRange> </div> </MkFolder> - + <MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])"> <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> <template #suffix> diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue index 0a358a141b620ffe88837a75b739b72dee3e0f8f..cacdab040ff517435cebe006b58c86b8d21e78f0 100644 --- a/packages/frontend/src/pages/channel-editor.vue +++ b/packages/frontend/src/pages/channel-editor.vue @@ -25,11 +25,11 @@ <MkFolder :defaultOpen="true"> <template #label>{{ i18n.ts.pinnedNotes }}</template> - + <div class="_gaps"> <MkButton primary rounded @click="addPinnedNote()"><i class="ti ti-plus"></i></MkButton> - <Sortable + <Sortable v-model="pinnedNotes" itemKey="id" :handle="'.' + $style.pinnedNoteHandle" @@ -160,7 +160,7 @@ async function archive() { }); if (canceled) return; - + os.api('channels/update', { channelId: props.channelId, isArchived: true, diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue index d5313099da8d8eb714e16443e784a02c457ac549..b09f787b5b9bb650677dead6af64f3ed011a853a 100644 --- a/packages/frontend/src/pages/clip.vue +++ b/packages/frontend/src/pages/clip.vue @@ -55,7 +55,7 @@ watch(() => props.clipId, async () => { favorited = clip.isFavorited; }, { immediate: true, -}); +}); provide('currentClip', $$(clip)); diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index 901fbc1eea1b5c8741edfbbb5b7122f9e1b26e48..dde4d67f1801a5e980d3bbdde31074d143f4b729 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -311,13 +311,13 @@ definePageMetadata(computed(() => ({ .empty { margin: var(--margin); } - + .ldhfsamy { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); grid-gap: 12px; margin: var(--margin) 0; - + > .emoji { display: flex; align-items: center; diff --git a/packages/frontend/src/pages/follow.vue b/packages/frontend/src/pages/follow.vue index d14b66336476298be0619d5e7afbb4f23288ea81..2d08b66868f1836e2160a5c4d2cf68b67a526932 100644 --- a/packages/frontend/src/pages/follow.vue +++ b/packages/frontend/src/pages/follow.vue @@ -20,7 +20,7 @@ async function follow(user): Promise<void> { window.close(); return; } - + os.apiWithDialog('following/create', { userId: user.id, }); diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 83997b255549825d86b0e1e7de49b6092b162da1..ac765e88b744be8e7f9dde68c943df44ecc47216 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -49,7 +49,7 @@ <template #value><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></template> </MkKeyValue> </FormSection> - + <FormSection> <MkKeyValue oneline style="margin: 1em 0;"> <template #key>Following (Pub)</template> diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue index 40934fb71d5d590abe7d0a1c96b214cf70f56d47..3307eef3594bc986a4f28f4d570a7c0a1ee12e6a 100644 --- a/packages/frontend/src/pages/list.vue +++ b/packages/frontend/src/pages/list.vue @@ -112,7 +112,7 @@ definePageMetadata(computed(() => list ? { flex: 1; min-width: 0; margin-right: 8px; - + &:hover { text-decoration: none; } diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue index b1d41fe2c7790157be018bebd9470cc2f4fef2d1..59f1f7fdb5e92dda8684a2e98c0e93c838d160ce 100644 --- a/packages/frontend/src/pages/registry.keys.vue +++ b/packages/frontend/src/pages/registry.keys.vue @@ -13,7 +13,7 @@ <template #value>{{ scope.join('/') }}</template> </MkKeyValue> </FormSplit> - + <MkButton primary @click="createKey">{{ i18n.ts._registry.createKey }}</MkButton> <FormSection v-if="keys"> diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue index 513a2f8febc9bd26214cc3650bf3e59c6c49b563..ed01381d57686816829856460e7528384b6b4fce 100644 --- a/packages/frontend/src/pages/registry.value.vue +++ b/packages/frontend/src/pages/registry.value.vue @@ -20,7 +20,7 @@ <template #value>{{ key }}</template> </MkKeyValue> </FormSplit> - + <MkTextarea v-model="valueForEditor" tall class="_monospace"> <template #label>{{ i18n.ts.value }} (JSON)</template> </MkTextarea> diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue index 9d5730731433b38d0e3d4181b1feb36dd278d70d..fbd109b3f0c045a815b3550a3ef784adf491e26d 100644 --- a/packages/frontend/src/pages/reset-password.vue +++ b/packages/frontend/src/pages/reset-password.vue @@ -7,7 +7,7 @@ <template #prefix><i class="ti ti-lock"></i></template> <template #label>{{ i18n.ts.newPassword }}</template> </MkInput> - + <MkButton primary @click="save">{{ i18n.ts.save }}</MkButton> </div> </MkSpacer> diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue index 8178343bbb425dd282b463442a1bbd12608e8152..98471e94dbba501ee6e24fc0a6cbbb08f4057e93 100644 --- a/packages/frontend/src/pages/settings/drive-cleaner.vue +++ b/packages/frontend/src/pages/settings/drive-cleaner.vue @@ -80,7 +80,7 @@ watch(sortModeSelect, () => { sortMode.value = '+size'; fetchDriveInfo(); break; - + case 'createdAtAsc': sortMode.value = '-createdAt'; fetchDriveInfo(); diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index 8780bfbc1e4ecd4de037ef0f333a63bc665decc8..f0e9a1c3d989e01384cafb8011557b62fab189e1 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -3,7 +3,7 @@ <FormSlot> <template #label>{{ i18n.ts.navbar }}</template> <MkContainer :showHeader="false"> - <Sortable + <Sortable v-model="items" itemKey="id" :animation="150" diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index e34901cd11fe89540a22a64f7b2f716e948e7b8a..1aa1a5f81c82d9034d624af9a18faa5b10e9ab64 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -144,7 +144,7 @@ function validate(profile: unknown): void { if (!profile.name) throw new Error('Missing required prop: name'); if (!profile.misskeyVersion) throw new Error('Missing required prop: misskeyVersion'); - + // Check if createdAt and updatedAt is Date // https://zenn.dev/lollipop_onl/articles/eoz-judge-js-invalid-date if (!profile.createdAt || Number.isNaN(new Date(profile.createdAt).getTime())) throw new Error('createdAt is falsy or not Date'); @@ -273,7 +273,7 @@ async function applyProfile(id: string): Promise<void> { defaultStore.set(key, settings.hot[key]); } } - + // coldDeviceStorage for (const key of coldDeviceStorageSaveKeys) { if (settings.cold[key] !== undefined) { diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index 7fd4d6d34e1e94dd5eec06c6993325c60fd87724..88d109c021143e1e2d7d884e863e13a0ed303e3d 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -7,7 +7,7 @@ {{ i18n.ts.makeReactionsPublic }} <template #caption>{{ i18n.ts.makeReactionsPublicDescription }}</template> </MkSwitch> - + <MkSelect v-model="ffVisibility" @update:modelValue="save()"> <template #label>{{ i18n.ts.ffVisibility }}</template> <option value="public">{{ i18n.ts._ffVisibility.public }}</option> @@ -15,7 +15,7 @@ <option value="private">{{ i18n.ts._ffVisibility.private }}</option> <template #caption>{{ i18n.ts.ffVisibilityDescription }}</template> </MkSelect> - + <MkSwitch v-model="hideOnlineStatus" @update:modelValue="save()"> {{ i18n.ts.hideOnlineStatus }} <template #caption>{{ i18n.ts.hideOnlineStatusDescription }}</template> diff --git a/packages/frontend/src/pages/settings/roles.vue b/packages/frontend/src/pages/settings/roles.vue index 05753c9b60d7341f64576506f63ae07f9a7a54eb..4b842f56fd43034a132c0b6c23b70662cffbf0d3 100644 --- a/packages/frontend/src/pages/settings/roles.vue +++ b/packages/frontend/src/pages/settings/roles.vue @@ -37,7 +37,7 @@ import MkRolePreview from '@/components/MkRolePreview.vue'; function save() { os.apiWithDialog('i/update', { - + }); } diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue index 2da84763a3d68f7175a5a7df557cf9abe8255c4c..bd5e2e350aee967442e13b161332950e119fdc89 100644 --- a/packages/frontend/src/pages/settings/security.vue +++ b/packages/frontend/src/pages/settings/security.vue @@ -78,7 +78,7 @@ async function change() { }); return; } - + os.apiWithDialog('i/change-password', { currentPassword, newPassword, diff --git a/packages/frontend/src/pizzax.ts b/packages/frontend/src/pizzax.ts index d97bd4be62e9a996c077b06e150b1d393faa5e1b..996610602bf9e6177f4ad5b2a403fd42aef063d7 100644 --- a/packages/frontend/src/pizzax.ts +++ b/packages/frontend/src/pizzax.ts @@ -70,7 +70,7 @@ export class Storage<T extends StateDef> { this.state[k] = v.default; this.reactiveState[k] = ref(v.default); } - + this.ready = this.init(); this.loaded = this.ready.then(() => this.load()); } @@ -81,7 +81,7 @@ export class Storage<T extends StateDef> { const deviceState: State<T> = await get(this.deviceStateKeyName) || {}; const deviceAccountState = $i ? await get(this.deviceAccountStateKeyName) || {} : {}; const registryCache = $i ? await get(this.registryCacheKeyName) || {} : {}; - + for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) { if (v.where === 'device' && Object.prototype.hasOwnProperty.call(deviceState, k)) { this.reactiveState[k].value = this.state[k] = deviceState[k]; @@ -110,7 +110,7 @@ export class Storage<T extends StateDef> { if (!scope || scope.length !== 2 || scope[0] !== 'client' || scope[1] !== this.key || this.state[key] === value) return; this.reactiveState[key].value = this.state[key] = value; - + this.addIdbSetJob(async () => { const cache = await get(this.registryCacheKeyName); if (cache[key] !== value) { @@ -142,7 +142,7 @@ export class Storage<T extends StateDef> { } } } - + return set(this.registryCacheKeyName, cache); }) .then(() => resolve()); @@ -252,7 +252,7 @@ export class Storage<T extends StateDef> { // localStorage => indexedDBã®ãƒžã‚¤ã‚°ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ private async migrate() { const deviceState = localStorage.getItem(this.deviceStateKeyName); - if (deviceState) { + if (deviceState) { await set(this.deviceStateKeyName, JSON.parse(deviceState)); localStorage.removeItem(this.deviceStateKeyName); } diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts index 2ca1b164ae14316fd51f57d1b157e8a17d6874ac..c26ae5a4dff72d8252721cf697bd15600e44d918 100644 --- a/packages/frontend/src/scripts/aiscript/ui.ts +++ b/packages/frontend/src/scripts/aiscript/ui.ts @@ -510,7 +510,7 @@ export function registerAsUiLib(components: Ref<AsUiComponent>[], done: (root: R // Ui:root.update({ children: [...] }) ã®ç³–衣構文 'Ui:render': values.FN_NATIVE(([children], opts) => { utils.assertArray(children); - + rootComponent.value.children = children.value.map(v => { utils.assertObject(v); return v.value.get('id').value; diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index b055d264737b3fc58f4f365528b82410349cfe2a..c884ed76cb99939edc5dcbadae6f2890a4fdbb33 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -196,7 +196,7 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router default: 'indefinitely', }); if (canceled) return; - + const expiresAt = period === 'indefinitely' ? null : period === 'oneHour' ? Date.now() + (1000 * 60 * 60) : period === 'oneDay' ? Date.now() + (1000 * 60 * 60 * 24) diff --git a/packages/frontend/src/scripts/lookup.ts b/packages/frontend/src/scripts/lookup.ts index ce5b03fc38221a3cf0749915d06ddc247b3313a2..0b594e341216a9778be3eee56790554357eeeca5 100644 --- a/packages/frontend/src/scripts/lookup.ts +++ b/packages/frontend/src/scripts/lookup.ts @@ -10,7 +10,7 @@ export async function lookup(router?: Router) { title: i18n.ts.lookup, }); if (canceled) return; - + if (query.startsWith('@') && !query.includes(' ')) { _router.push(`/${query}`); return; diff --git a/packages/frontend/src/scripts/url.ts b/packages/frontend/src/scripts/url.ts index b6a997449a78bbf4ef3cc6ea5af0706a3fa86656..07737d6228412154a7e74a560dbf3748b193b3ca 100644 --- a/packages/frontend/src/scripts/url.ts +++ b/packages/frontend/src/scripts/url.ts @@ -2,7 +2,7 @@ * 1. é…列ã«ä½•ã‚‚å…¥ã£ã¦ã„ãªã„時ã¯ã‚¯ã‚¨ãƒªã‚’付ã‘ãªã„ * 2. プãƒãƒ‘ティãŒundefinedã®æ™‚ã¯ã‚¯ã‚¨ãƒªã‚’付ã‘ãªã„ * (new URLSearchParams(obj)ã§ã¯ãã“ã¾ã§ä¸å¯§ãªã“ã¨ã‚’ã—ã¦ãã‚Œãªã„) - */ + */ export function query(obj: Record<string, any>): string { const params = Object.entries(obj) .filter(([, v]) => Array.isArray(v) ? v.length : v !== undefined) diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts index 22a01e066a347e92cce8e3e5eb7ed7e0e008bf4f..d057386b13a9addb78e3c6b119f10d7c49e63dea 100644 --- a/packages/frontend/src/scripts/use-note-capture.ts +++ b/packages/frontend/src/scripts/use-note-capture.ts @@ -93,12 +93,12 @@ export function useNoteCapture(props: { function onStreamConnected() { capture(false); } - + capture(true); if (connection) { connection.on('_connected_', onStreamConnected); } - + onUnmounted(() => { decapture(true); if (connection) { diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index b376e4c42dd09ae2241aa0120d4d5659c4913bfd..b4b80a4bbe4a32dbaf7946b93cd919c72962a24c 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -7,7 +7,7 @@ --margin: var(--marginFull); --minBottomSpacing: 0px; - + @media (max-width: 500px) { --margin: var(--marginHalf); --minBottomSpacing: calc(72px + max(12px, env(safe-area-inset-bottom, 0px))); @@ -19,7 +19,7 @@ ::selection { color: var(--fgOnAccent); background-color: var(--accent); -} +} html { background-color: var(--bg); diff --git a/packages/frontend/src/themes/l-botanical.json5 b/packages/frontend/src/themes/l-botanical.json5 index 5c9892789641d8d03f260bd9f6d3c112aaa47b7d..17e9ca246f03fddb0a7a484dbedd94f3cf2028d6 100644 --- a/packages/frontend/src/themes/l-botanical.json5 +++ b/packages/frontend/src/themes/l-botanical.json5 @@ -5,7 +5,7 @@ author: 'ThinaticSystem', base: 'light', - + props: { accent: '#77b58c', bg: 'e2deda', diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue index 747d4edcb4495fe1dd5d310b76a3cc6ad0456aac..2c41e7759bff24e2bfa2c87ccfb8290c0e3842c0 100644 --- a/packages/frontend/src/ui/classic.header.vue +++ b/packages/frontend/src/ui/classic.header.vue @@ -82,7 +82,7 @@ function openAccountMenu(ev: MouseEvent) { onMounted(() => { window.addEventListener('resize', () => { settingsWindowed = (window.innerWidth >= WINDOW_THRESHOLD); - }, { passive: true }); + }, { passive: true }); }); </script> @@ -177,7 +177,7 @@ onMounted(() => { > .post { display: inline-block; - + > .button { width: 40px; height: 40px; diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue index d50f2b0454b8358ec577dbbd868e144cd00d4d7c..2c58c29840223eaee808b86b1ef09307ac3fff1e 100644 --- a/packages/frontend/src/ui/classic.vue +++ b/packages/frontend/src/ui/classic.vue @@ -219,7 +219,7 @@ onMounted(() => { &.fullView { margin: 0; - + > .sidebar { display: none; } diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue index 3b67972e400f7769b68aa9c1d3caaab4e1c520cc..94d2d922d2c039899cff976c33a3fed7afeedc84 100644 --- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue +++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue @@ -37,7 +37,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>; const props = defineProps<WidgetComponentProps<WidgetProps>>(); const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); - + const { widgetProps, configure } = useWidgetPropsManager(name, widgetPropsDef, props, diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue index b7be2e8c83a6a9de9ec79dbe99cb81ab22ba5c1c..6a997f7da81262274c06af1858c7cda0571f8694 100644 --- a/packages/frontend/src/widgets/WidgetClicker.vue +++ b/packages/frontend/src/widgets/WidgetClicker.vue @@ -25,7 +25,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>; const props = defineProps<WidgetComponentProps<WidgetProps>>(); const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); - + const { widgetProps, configure } = useWidgetPropsManager(name, widgetPropsDef, props, diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue index a24aa9b2e9a985a04ace759085e20196923efea3..d4f9a07cb5e88a6aacca596b415738fba77da694 100644 --- a/packages/frontend/src/widgets/WidgetNotifications.vue +++ b/packages/frontend/src/widgets/WidgetNotifications.vue @@ -41,7 +41,7 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>; const props = defineProps<WidgetComponentProps<WidgetProps>>(); const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); - + const { widgetProps, configure, save } = useWidgetPropsManager(name, widgetPropsDef, props, diff --git a/packages/misskey-js/src/streaming.ts b/packages/misskey-js/src/streaming.ts index 92a220b4963d36b41e1eaa622b6216f598be770d..c641706a4bfa37505bd8046e1633844f724836c1 100644 --- a/packages/misskey-js/src/streaming.ts +++ b/packages/misskey-js/src/streaming.ts @@ -218,7 +218,7 @@ class Pool { this.dec = this.dec.bind(this); this.connect = this.connect.bind(this); this.disconnect = this.disconnect.bind(this); - + this.channel = channel; this.stream = stream; this.id = id; diff --git a/packages/misskey-js/test-d/streaming.ts b/packages/misskey-js/test-d/streaming.ts index 7ff7f95999d5dc64f06c641fa6608b9ef0a7af90..853f376e6ca87a87fc2da35eee115ac00b8b8dbe 100644 --- a/packages/misskey-js/test-d/streaming.ts +++ b/packages/misskey-js/test-d/streaming.ts @@ -16,7 +16,7 @@ describe('Streaming', () => { // { // otherparty?: User['id'] | null; // group?: UserGroup['id'] | null; - // } + // } // ã«ãªã£ã¦ã„ã‚‹ã€ã¨ã„ã†ãƒ†ã‚¹ãƒˆã‚’è¡Œã„ãŸã„ã‘ã©tsdã§ã®æ›¸ãæ–¹ãŒã‚ã‹ã‚‰ãªã„ const messagingChannel = stream.useChannel('messaging', { otherparty: 'aaa' }); messagingChannel.on('message', message => { diff --git a/packages/misskey-js/test/api.ts b/packages/misskey-js/test/api.ts index 84b1fc09334c57281408d15c3fcd16afd726fbeb..6f9e656fefae4e9b75df570dcf5944af41ccac79 100644 --- a/packages/misskey-js/test/api.ts +++ b/packages/misskey-js/test/api.ts @@ -137,7 +137,7 @@ describe('API', () => { origin: 'https://misskey.test', credential: 'TOKEN', }); - + await cli.request('i', {}, null); } catch (e) { expect(isAPIError(e)).toEqual(true); @@ -165,7 +165,7 @@ describe('API', () => { origin: 'https://misskey.test', credential: 'TOKEN', }); - + await cli.request('i'); } catch (e: any) { expect(isAPIError(e)).toEqual(true); @@ -182,7 +182,7 @@ describe('API', () => { origin: 'https://misskey.test', credential: 'TOKEN', }); - + await cli.request('i'); } catch (e) { expect(isAPIError(e)).toEqual(false); @@ -203,7 +203,7 @@ describe('API', () => { origin: 'https://misskey.test', credential: 'TOKEN', }); - + await cli.request('i'); } catch (e) { expect(isAPIError(e)).toEqual(false); diff --git a/packages/misskey-js/test/streaming.ts b/packages/misskey-js/test/streaming.ts index 913db8b28765da032ff434238ff11f489a0ce3a0..9f6615a8d89c59b9bd66a06c848bc3964d80c084 100644 --- a/packages/misskey-js/test/streaming.ts +++ b/packages/misskey-js/test/streaming.ts @@ -131,7 +131,7 @@ describe('Streaming', () => { main.on('meUpdated', payload => { mainChannelReceived.push(payload); }); - + const ws = await server.connected; expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN');