diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2a70b076ba..5dd72cc460 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,7 +1,7 @@ # PATH OWNERS /.autogen/ @acid-chicken /.circleci/ @syuilo @acid-chicken -/.config/ @syuilo @AyaMorisawa @mei23 @acid-chicken +/.config/ @syuilo @AyaMorisawa @mei23 @acid-chicken @rinsuki # /.config/mongo_initdb_example.js @khws4v1 /.github/ @syuilo @AyaMorisawa @acid-chicken /.vscode/ @acid-chicken @@ -12,7 +12,7 @@ # /docs/*.fr.md @BoFFire # /docs/docker.*.md @khws4v1 /locales/ @syuilo -/src/ @syuilo @AyaMorisawa @mei23 @acid-chicken +/src/ @syuilo @AyaMorisawa @mei23 @acid-chicken @rinsuki # /src/crypto_key.cc @akihikodaki # /src/crypto_key.d.ts @akihikodaki /.dockerignore @syuilo # @khws4v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 17fbe781c5..96fb4c2098 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,17 @@ mongodb: 8. master ブランチに戻す 9. enjoy +11.3.0 (2019/04/24) +------------------- +### Improvements +* お知らせにMFMを使えるように +* お知らせに画像を添付できるように + +### Fixes +* 投稿のタグ検索APIで大文字小文字が区別されていたのを修正 +* 公開範囲がホームの投稿がグローバルTLに流れる問題を修正 +* モバイルビューの投稿詳細にて acct が長いとアイコンが圧迫面接される問題を修正 + 11.2.2 (2019/04/22) ------------------- ### Fixes diff --git a/README.md b/README.md index 172de33e72..f69c503a90 100644 --- a/README.md +++ b/README.md @@ -88,12 +88,14 @@ Please see the [Contribution Guide](./CONTRIBUTING.md). AyaMorisawa mei23 acid-chicken + rinsuki @syuilo @AyaMorisawa @mei23 @acid-chicken + @rinsuki @@ -102,14 +104,14 @@ Please see the [Contribution Guide](./CONTRIBUTING.md). - + - + @@ -124,7 +126,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md). - + @@ -135,7 +137,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md). - +
Hiroshi Sekiweepweepjp ne_moni Melilot osapon 見当かなみ
Hiroshi Sekiweepweepjp ne_moni Melilot osaponsikyosyounin YUKIMOCHI SampotRyosuke Yamamotosheeta.s Satsuki Yanagi
gutfuckllcsikyosyounin YUKIMOCHI SampotRyosuke Yamamotosheeta.s Satsuki Yanagi
@@ -165,7 +167,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
Takashi Shibuya
-**Last updated:** Thu, 18 Apr 2019 23:38:06 UTC +**Last updated:** Wed, 24 Apr 2019 05:56:07 UTC :four_leaf_clover: Copyright diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 88c55958dd..f4a02be68e 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -13,7 +13,7 @@ common: rich-contents-desc: "Partagez vos idées, les événements et les sujets qui vous tiennent à cœur ainsi que tout autre chose que vous souhaitez partager avec les autres. Si vous le désirez, vous pouvez décorer vos messages en utilisant une syntaxe différente ou en y joignant des sondages et des fichiers, tels que les photos ou les vidéos que vous aimez." reaction: "Réactions" reaction-desc: "Une manière simple d'exprimer vos émotions. Misskey peut attacher diverses réactions aux publications des autres utilisateurs. Si vous essayez les réactions sur Misskey, vous ne pourrez plus retourner sur une autre plateforme de réseaux sociaux n'offrant que des « J'aime »." - ui: "Interface utilisateur" + ui: "Interface" ui-desc: "Aucune interface graphique ne peut plaire à tout le monde. Par conséquent, Misskey possède une interface utilisateur hautement personnalisable selon vos goûts. Vous pouvez rendre votre page d'accueil originale en modifiant la mise en page de votre fil et en déplaçant les widgets que vous pouvez facilement ajuster pour vous approprier cet espace." drive: "Drive" drive-desc: "Vous voulez poster une photo que vous avez déjà transférée ? Vous souhaitez organiser, nommer et créer un dossier pour vos fichiers téléversés ? Misskey Drive est la meilleure solution pour vous. Très facile de partager vos fichiers en ligne." @@ -33,6 +33,7 @@ common: signin: "Se connecter" signup: "S'enregistrer" signout: "Se déconnecter" + reload-to-apply-the-setting: "Le rechargement de la page est nécessaire pour appliquer ces paramètres. Désirez-vous la recharger maintenant ?" got-it: "J’ai compris !" customization-tips: title: "Conseils de personnalisation" @@ -73,6 +74,8 @@ common: "read:drive": "Parcourir le Drive" "write:drive": "Écrire sur le Drive" "read:favorites": "Afficher les favoris" + "write:notes": "Créer ou supprimer des publications" + "read:notifications": "Afficher les notifications" "read:reactions": "Lire les réactions" "write:votes": "Vote" empty-timeline-info: @@ -209,16 +212,16 @@ common: debug-mode: "Activer le mode débogage" debug-mode-desc: "Ce paramètre est stocké dans le navigateur." navbar-position: "Position de la barre de navigation" - navbar-position-top: "en haut" + navbar-position-top: "En haut" navbar-position-left: "À gauche" - navbar-position-right: "à droite" + navbar-position-right: "À droite" i-am-under-limited-internet: "J'ai un accès Internet limité" post-style: "Style d'affichage des notes" post-style-standard: "Standard" post-style-smart: "Intelligent" notification-position: "Afficher les notifications" notification-position-bottom: "en bas" - notification-position-top: "en haut" + notification-position-top: "En haut" disable-via-mobile: "Enlever la mention publié via 'mobile'" load-raw-images: "Afficher les photos jointes dans leur qualité originale" load-remote-media: "Afficher les médias depuis le serveur distant" @@ -307,6 +310,7 @@ common/views/pages/explore.vue: explore: "Explorer {host}" users-info: "Actuellement, {users} utilisateurs se sont inscrit ici" common/views/components/url-preview.vue: + enable-player: "Activer la lecture" disable-player: "Fermer le lecteur" common/views/components/user-list.vue: no-users: "Il n'y a aucun utilisateur" @@ -647,6 +651,7 @@ common/views/components/profile-editor.vue: blocking-list: "Liste des comptes bloqués" user-lists: "Listes" export-requested: "Vous avez demandé une exportation. Cela peut prendre un certain temps. Une fois l'exportation terminée, le fichier résultant sera ajouté dans le Drive." + import-requested: "Vous avez initié un import. Ceci peut prendre un peu de temps." enter-password: "Veuillez saisir votre mot de passe" danger-zone: "Zone de danger" delete-account: "Supprimer le compte" @@ -659,6 +664,7 @@ common/views/components/user-list-editor.vue: delete-are-you-sure: "Voulez-vous vraiment supprimer la liste « $1 » ?" deleted: "Supprimé" common/views/components/user-lists.vue: + create-list: "Créer une liste" list-name: "Nom de la liste" common/views/widgets/broadcast.vue: fetching: "Récupération" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 815686cfbe..8908cf156d 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -34,6 +34,7 @@ common: signup: "注册" signout: "退出" reload-to-apply-the-setting: "必须重新加载页面以应用此设置。 确实要立即重新加载吗?" + fetching-as-ap-object: "联合查询" got-it: "知道了" customization-tips: title: "自定义提示" diff --git a/package.json b/package.json index 61ace88c1c..75532da2f9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo ", - "version": "11.2.2", + "version": "11.3.0", "codename": "daybreak", "repository": { "type": "git", @@ -29,7 +29,6 @@ "@fortawesome/free-solid-svg-icons": "5.7.2", "@fortawesome/vue-fontawesome": "0.1.5", "@koa/cors": "2.2.3", - "@prezzemolo/rap": "0.1.2", "@prezzemolo/zip": "0.0.3", "@types/bcryptjs": "2.4.2", "@types/bull": "3.5.11", diff --git a/src/boot/master.ts b/src/boot/master.ts index 4d360c7265..503f09c1f2 100644 --- a/src/boot/master.ts +++ b/src/boot/master.ts @@ -27,7 +27,7 @@ function greet() { console.log(' ' + chalk.gray(v) + (' |___|\n'.substr(v.length))); //#endregion - console.log(' Misskey is maintained by @syuilo, @AyaMorisawa, @mei23, and @acid-chicken.'); + console.log(' Misskey is maintained by @syuilo, @AyaMorisawa, @mei23, @acid-chicken, and @rinsuki.'); console.log(chalk.keyword('orange')(' If you like Misskey, please donate to support development. https://www.patreon.com/syuilo')); console.log(''); diff --git a/src/client/app/admin/views/announcements.vue b/src/client/app/admin/views/announcements.vue index 2e64e47d95..c1b2d6778d 100644 --- a/src/client/app/admin/views/announcements.vue +++ b/src/client/app/admin/views/announcements.vue @@ -9,6 +9,9 @@ {{ $t('text') }} + + {{ $t('image-url') }} + {{ $t('save') }} {{ $t('remove') }} @@ -43,7 +46,8 @@ export default Vue.extend({ add() { this.announcements.unshift({ title: '', - text: '' + text: '', + image: null }); }, diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue index 04f3ed9f78..cd02c6957d 100644 --- a/src/client/app/common/views/components/avatar.vue +++ b/src/client/app/common/views/components/avatar.vue @@ -78,6 +78,7 @@ export default Vue.extend({ .mk-avatar display inline-block vertical-align bottom + flex-shrink 0 &:not(.cat) overflow hidden diff --git a/src/client/app/common/views/components/user-list-editor.vue b/src/client/app/common/views/components/user-list-editor.vue index 8d2e04d045..86024c4da3 100644 --- a/src/client/app/common/views/components/user-list-editor.vue +++ b/src/client/app/common/views/components/user-list-editor.vue @@ -77,11 +77,11 @@ export default Vue.extend({ input: { default: this.list.name } - }).then(({ canceled, result: title }) => { + }).then(({ canceled, result: name }) => { if (canceled) return; this.$root.api('users/lists/update', { listId: this.list.id, - title: title + name: name }); }); }, diff --git a/src/client/app/common/views/components/user-lists.vue b/src/client/app/common/views/components/user-lists.vue index 786a6766d3..699251b313 100644 --- a/src/client/app/common/views/components/user-lists.vue +++ b/src/client/app/common/views/components/user-lists.vue @@ -28,10 +28,10 @@ export default Vue.extend({ this.$root.dialog({ title: this.$t('list-name'), input: true - }).then(async ({ canceled, result: title }) => { + }).then(async ({ canceled, result: name }) => { if (canceled) return; const list = await this.$root.api('users/lists/create', { - title + name }); this.lists.push(list) diff --git a/src/client/app/common/views/widgets/broadcast.vue b/src/client/app/common/views/widgets/broadcast.vue index f8897da253..7049945d55 100644 --- a/src/client/app/common/views/widgets/broadcast.vue +++ b/src/client/app/common/views/widgets/broadcast.vue @@ -18,7 +18,8 @@

{{ $t('fetching') }}

{{ announcements.length == 0 ? $t('no-broadcasts') : announcements[i].title }}

- + +

{{ $t('next') }} >> diff --git a/src/client/app/desktop/views/home/timeline.vue b/src/client/app/desktop/views/home/timeline.vue index 1e6abac823..3f9681a047 100644 --- a/src/client/app/desktop/views/home/timeline.vue +++ b/src/client/app/desktop/views/home/timeline.vue @@ -123,10 +123,10 @@ export default Vue.extend({ this.$root.dialog({ title: this.$t('list-name'), input: true - }).then(async ({ canceled, result: title }) => { + }).then(async ({ canceled, result: name }) => { if (canceled) return; const list = await this.$root.api('users/lists/create', { - title + name }); this.list = list; diff --git a/src/client/app/desktop/views/pages/welcome.vue b/src/client/app/desktop/views/pages/welcome.vue index 5a5cd9c8e6..d7af82cd8d 100644 --- a/src/client/app/desktop/views/pages/welcome.vue +++ b/src/client/app/desktop/views/pages/welcome.vue @@ -44,7 +44,8 @@

-
+ +
diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue index e14e1beff8..4b343491b8 100644 --- a/src/client/app/mobile/views/components/note-detail.vue +++ b/src/client/app/mobile/views/components/note-detail.vue @@ -215,11 +215,6 @@ export default Vue.extend({ @media (min-width 500px) padding 28px 32px 18px 32px - &:after - content "" - display block - clear both - > header display flex line-height 1.1em @@ -236,6 +231,7 @@ export default Vue.extend({ height 60px > div + min-width 0 > .name display inline-block diff --git a/src/client/app/mobile/views/components/ui.nav.vue b/src/client/app/mobile/views/components/ui.nav.vue index b26e2380ab..1177cab4a0 100644 --- a/src/client/app/mobile/views/components/ui.nav.vue +++ b/src/client/app/mobile/views/components/ui.nav.vue @@ -43,7 +43,8 @@
-
+ +

{{ $t('about') }}

diff --git a/src/client/app/mobile/views/pages/welcome.vue b/src/client/app/mobile/views/pages/welcome.vue index dd71a918db..48a6650b4f 100644 --- a/src/client/app/mobile/views/pages/welcome.vue +++ b/src/client/app/mobile/views/pages/welcome.vue @@ -29,7 +29,8 @@
-
+ +
diff --git a/src/client/app/theme.ts b/src/client/app/theme.ts index 18ff82b9ec..b16fcdff4b 100644 --- a/src/client/app/theme.ts +++ b/src/client/app/theme.ts @@ -19,7 +19,7 @@ export const cafeTheme: Theme = require('../themes/cafe.json5'); export const japaneseSushiSetTheme: Theme = require('../themes/japanese-sushi-set.json5'); export const gruvboxDarkTheme: Theme = require('../themes/gruvbox-dark.json5'); export const monokaiTheme: Theme = require('../themes/monokai.json5'); -export const colorfulTheme: Theme = require('../themes/colorful.json5'); +export const vividTheme: Theme = require('../themes/vivid.json5'); export const rainyTheme: Theme = require('../themes/rainy.json5'); export const mauveTheme: Theme = require('../themes/mauve.json5'); export const grayTheme: Theme = require('../themes/gray.json5'); @@ -35,7 +35,7 @@ export const builtinThemes = [ japaneseSushiSetTheme, gruvboxDarkTheme, monokaiTheme, - colorfulTheme, + vividTheme, rainyTheme, mauveTheme, grayTheme, diff --git a/src/client/themes/colorful.json5 b/src/client/themes/vivid.json5 similarity index 95% rename from src/client/themes/colorful.json5 rename to src/client/themes/vivid.json5 index 5b7441e1cf..27bf742f3f 100644 --- a/src/client/themes/colorful.json5 +++ b/src/client/themes/vivid.json5 @@ -1,7 +1,7 @@ { id: '2d066d6e-bd39-4f23-bd48-686d5c1c6ae8', - name: 'Colorful', + name: 'Vivid', author: 'syuilo', base: 'light', diff --git a/src/misc/fetch-meta.ts b/src/misc/fetch-meta.ts index 800f0b9e63..db82adb2a4 100644 --- a/src/misc/fetch-meta.ts +++ b/src/misc/fetch-meta.ts @@ -1,7 +1,11 @@ import { Meta } from '../models/entities/meta'; import { getConnection } from 'typeorm'; -export default async function(): Promise { +let cache: Meta; + +export async function fetchMeta(noCache = false): Promise { + if (!noCache && cache) return cache; + return await getConnection().transaction(async transactionalEntityManager => { // バグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する const meta = await transactionalEntityManager.findOne(Meta, { @@ -11,11 +15,21 @@ export default async function(): Promise { }); if (meta) { + cache = meta; return meta; } else { - return await transactionalEntityManager.save(Meta, { + const saved = await transactionalEntityManager.save(Meta, { id: 'x' }) as Meta; + + cache = saved; + return saved; } }); } + +setInterval(() => { + fetchMeta(true).then(meta => { + cache = meta; + }); +}, 5000); diff --git a/src/misc/fetch-proxy-account.ts b/src/misc/fetch-proxy-account.ts index 17b021e91e..075873091c 100644 --- a/src/misc/fetch-proxy-account.ts +++ b/src/misc/fetch-proxy-account.ts @@ -1,4 +1,4 @@ -import fetchMeta from './fetch-meta'; +import { fetchMeta } from './fetch-meta'; import { ILocalUser } from '../models/entities/user'; import { Users } from '../models'; import { ensure } from '../prelude/ensure'; diff --git a/src/misc/reaction-lib.ts b/src/misc/reaction-lib.ts index 008991454b..ced90ce78f 100644 --- a/src/misc/reaction-lib.ts +++ b/src/misc/reaction-lib.ts @@ -1,5 +1,5 @@ import { emojiRegex } from './emoji-regex'; -import fetchMeta from './fetch-meta'; +import { fetchMeta } from './fetch-meta'; import { Emojis } from '../models'; const basic10: Record = { diff --git a/src/misc/schema.ts b/src/misc/schema.ts index 7c17953d97..442c95ad98 100644 --- a/src/misc/schema.ts +++ b/src/misc/schema.ts @@ -1,14 +1,46 @@ +export const types = { + boolean: 'boolean' as 'boolean', + string: 'string' as 'string', + number: 'number' as 'number', + array: 'array' as 'array', + object: 'object' as 'object', + any: 'any' as 'any', +}; + +export const bool = { + true: true as true, + false: false as false, +}; + export type Schema = { - type: 'number' | 'string' | 'array' | 'object' | any; - optional?: boolean; + type: 'boolean' | 'number' | 'string' | 'array' | 'object' | 'any'; + nullable: boolean; + optional: boolean; items?: Schema; properties?: Obj; description?: string; + example?: any; + format?: string; + ref?: string; + enum?: string[]; }; +type NonUndefinedPropertyNames = { + [K in keyof T]: T[K]['optional'] extends true ? never : K +}[keyof T]; + +type UndefinedPropertyNames = { + [K in keyof T]: T[K]['optional'] extends true ? K : never +}[keyof T]; + +type OnlyRequired = Pick>; +type OnlyOptional = Pick>; + export type Obj = { [key: string]: Schema }; -export type ObjType = { [P in keyof s]: SchemaType }; +export type ObjType = + { [P in keyof OnlyOptional]?: SchemaType } & + { [P in keyof OnlyRequired]: SchemaType }; // https://qiita.com/hrsh7th@github/items/84e8968c3601009cdcf2 type MyType = { @@ -16,26 +48,20 @@ type MyType = { 1: SchemaType; }[T extends Schema ? 1 : 0]; -export type SchemaType

= - p['type'] extends 'number' ? number : - p['type'] extends 'string' ? string : - p['type'] extends 'array' ? MyType>[] : - p['type'] extends 'object' ? ObjType> : - any; +type NullOrUndefined

= + p['nullable'] extends true + ? p['optional'] extends true + ? (T | null | undefined) + : (T | null) + : p['optional'] extends true + ? (T | undefined) + : T; -export function convertOpenApiSchema(schema: Schema) { - const x = JSON.parse(JSON.stringify(schema)); // copy - if (!['string', 'number', 'boolean', 'array', 'object'].includes(x.type)) { - x['$ref'] = `#/components/schemas/${x.type}`; - } - if (x.type === 'array' && x.items) { - x.items = convertOpenApiSchema(x.items); - } - if (x.type === 'object' && x.properties) { - x.required = Object.entries(x.properties).filter(([k, v]: any) => !v.isOptional).map(([k, v]: any) => k); - for (const k of Object.keys(x.properties)) { - x.properties[k] = convertOpenApiSchema(x.properties[k]); - } - } - return x; -} +export type SchemaType

= + p['type'] extends 'number' ? NullOrUndefined : + p['type'] extends 'string' ? NullOrUndefined : + p['type'] extends 'boolean' ? NullOrUndefined : + p['type'] extends 'array' ? NullOrUndefined>[]> : + p['type'] extends 'object' ? NullOrUndefined>> : + p['type'] extends 'any' ? NullOrUndefined : + any; diff --git a/src/models/entities/note-reaction.ts b/src/models/entities/note-reaction.ts index 1ce5d841fb..a958e89570 100644 --- a/src/models/entities/note-reaction.ts +++ b/src/models/entities/note-reaction.ts @@ -36,7 +36,7 @@ export class NoteReaction { public note: Note | null; @Column('varchar', { - length: 32 + length: 128 }) public reaction: string; } diff --git a/src/models/repositories/abuse-user-report.ts b/src/models/repositories/abuse-user-report.ts index f619d6e37f..c708b265a6 100644 --- a/src/models/repositories/abuse-user-report.ts +++ b/src/models/repositories/abuse-user-report.ts @@ -1,8 +1,8 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; -import rap from '@prezzemolo/rap'; import { AbuseUserReport } from '../entities/abuse-user-report'; import { ensure } from '../../prelude/ensure'; +import { awaitAll } from '../../prelude/await-all'; @EntityRepository(AbuseUserReport) export class AbuseUserReportRepository extends Repository { @@ -17,7 +17,7 @@ export class AbuseUserReportRepository extends Repository { ) { const report = typeof src === 'object' ? src : await this.findOne(src).then(ensure); - return await rap({ + return await awaitAll({ id: report.id, createdAt: report.createdAt, reporterId: report.reporterId, diff --git a/src/models/repositories/app.ts b/src/models/repositories/app.ts index fa5ebf2ac7..6562a470be 100644 --- a/src/models/repositories/app.ts +++ b/src/models/repositories/app.ts @@ -2,6 +2,9 @@ import { EntityRepository, Repository } from 'typeorm'; import { App } from '../entities/app'; import { AccessTokens } from '..'; import { ensure } from '../../prelude/ensure'; +import { types, bool, SchemaType } from '../../misc/schema'; + +export type PackedApp = SchemaType; @EntityRepository(App) export class AppRepository extends Repository { @@ -13,7 +16,7 @@ export class AppRepository extends Repository { includeSecret?: boolean, includeProfileImageIds?: boolean } - ) { + ): Promise { const opts = Object.assign({ detail: false, includeSecret: false, @@ -37,3 +40,40 @@ export class AppRepository extends Repository { }; } } + +export const packedAppSchema = { + type: types.object, + optional: bool.false, nullable: bool.false, + properties: { + id: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + description: 'The unique identifier for this Note.', + example: 'xxxxxxxxxx', + }, + name: { + type: types.string, + optional: bool.false, nullable: bool.false, + description: 'アプリケーションの名前' + }, + callbackUrl: { + type: types.string, + optional: bool.false, nullable: bool.true, + description: 'コールバックするURL' + }, + permission: { + type: types.array, + optional: bool.true, nullable: bool.false, + items: { + type: types.string, + optional: bool.false, nullable: bool.false, + } + }, + secret: { + type: types.string, + optional: bool.true, nullable: bool.false, + description: 'アプリケーションのシークレットキー' + } + }, +}; diff --git a/src/models/repositories/auth-session.ts b/src/models/repositories/auth-session.ts index 32f7968233..a6a4d46de6 100644 --- a/src/models/repositories/auth-session.ts +++ b/src/models/repositories/auth-session.ts @@ -1,8 +1,8 @@ import { EntityRepository, Repository } from 'typeorm'; import { Apps } from '..'; -import rap from '@prezzemolo/rap'; import { AuthSession } from '../entities/auth-session'; import { ensure } from '../../prelude/ensure'; +import { awaitAll } from '../../prelude/await-all'; @EntityRepository(AuthSession) export class AuthSessionRepository extends Repository { @@ -12,7 +12,7 @@ export class AuthSessionRepository extends Repository { ) { const session = typeof src === 'object' ? src : await this.findOne(src).then(ensure); - return await rap({ + return await awaitAll({ id: session.id, app: Apps.pack(session.appId, me), token: session.token diff --git a/src/models/repositories/blocking.ts b/src/models/repositories/blocking.ts index e18aa591f3..fd209bce1c 100644 --- a/src/models/repositories/blocking.ts +++ b/src/models/repositories/blocking.ts @@ -1,8 +1,11 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; -import rap from '@prezzemolo/rap'; import { Blocking } from '../entities/blocking'; import { ensure } from '../../prelude/ensure'; +import { awaitAll } from '../../prelude/await-all'; +import { SchemaType, types, bool } from '../../misc/schema'; + +export type PackedBlocking = SchemaType; @EntityRepository(Blocking) export class BlockingRepository extends Repository { @@ -16,14 +19,47 @@ export class BlockingRepository extends Repository { public async pack( src: Blocking['id'] | Blocking, me?: any - ) { + ): Promise { const blocking = typeof src === 'object' ? src : await this.findOne(src).then(ensure); - return await rap({ + return await awaitAll({ id: blocking.id, + createdAt: blocking.createdAt.toISOString(), + blockeeId: blocking.blockeeId, blockee: Users.pack(blocking.blockeeId, me, { detail: true }) }); } } + +export const packedBlockingSchema = { + type: types.object, + optional: bool.false, nullable: bool.false, + properties: { + id: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + description: 'The unique identifier for this blocking.', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'date-time', + description: 'The date that the blocking was created.' + }, + blockeeId: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + }, + blockee: { + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'User', + description: 'The blockee.' + }, + } +}; diff --git a/src/models/repositories/drive-file.ts b/src/models/repositories/drive-file.ts index f117b38b24..245db4b797 100644 --- a/src/models/repositories/drive-file.ts +++ b/src/models/repositories/drive-file.ts @@ -1,10 +1,13 @@ import { EntityRepository, Repository } from 'typeorm'; import { DriveFile } from '../entities/drive-file'; import { Users, DriveFolders } from '..'; -import rap from '@prezzemolo/rap'; import { User } from '../entities/user'; import { toPuny } from '../../misc/convert-host'; import { ensure } from '../../prelude/ensure'; +import { awaitAll } from '../../prelude/await-all'; +import { types, bool, SchemaType } from '../../misc/schema'; + +export type PackedDriveFile = SchemaType; @EntityRepository(DriveFile) export class DriveFileRepository extends Repository { @@ -82,7 +85,7 @@ export class DriveFileRepository extends Repository { self?: boolean, withUser?: boolean, } - ) { + ): Promise { const opts = Object.assign({ detail: false, self: false @@ -90,9 +93,9 @@ export class DriveFileRepository extends Repository { const file = typeof src === 'object' ? src : await this.findOne(src).then(ensure); - return await rap({ + return await awaitAll({ id: file.id, - createdAt: file.createdAt, + createdAt: file.createdAt.toISOString(), name: file.name, type: file.type, md5: file.md5, @@ -109,3 +112,66 @@ export class DriveFileRepository extends Repository { }); } } + +export const packedDriveFileSchema = { + type: types.object, + optional: bool.false, nullable: bool.false, + properties: { + id: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + description: 'The unique identifier for this Drive file.', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'date-time', + description: 'The date that the Drive file was created on Misskey.' + }, + name: { + type: types.string, + optional: bool.false, nullable: bool.false, + description: 'The file name with extension.', + example: 'lenna.jpg' + }, + type: { + type: types.string, + optional: bool.false, nullable: bool.false, + description: 'The MIME type of this Drive file.', + example: 'image/jpeg' + }, + md5: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'md5', + description: 'The MD5 hash of this Drive file.', + example: '15eca7fba0480996e2245f5185bf39f2' + }, + size: { + type: types.number, + optional: bool.false, nullable: bool.false, + description: 'The size of this Drive file. (bytes)', + example: 51469 + }, + url: { + type: types.string, + optional: bool.false, nullable: bool.true, + format: 'url', + description: 'The URL of this Drive file.', + }, + folderId: { + type: types.string, + optional: bool.false, nullable: bool.true, + format: 'id', + description: 'The parent folder ID of this Drive file.', + example: 'xxxxxxxxxx', + }, + isSensitive: { + type: types.boolean, + optional: bool.false, nullable: bool.false, + description: 'Whether this Drive file is sensitive.', + }, + }, +}; diff --git a/src/models/repositories/drive-folder.ts b/src/models/repositories/drive-folder.ts index ce88adefa4..ef920c4326 100644 --- a/src/models/repositories/drive-folder.ts +++ b/src/models/repositories/drive-folder.ts @@ -1,8 +1,11 @@ import { EntityRepository, Repository } from 'typeorm'; import { DriveFolders, DriveFiles } from '..'; -import rap from '@prezzemolo/rap'; import { DriveFolder } from '../entities/drive-folder'; import { ensure } from '../../prelude/ensure'; +import { awaitAll } from '../../prelude/await-all'; +import { SchemaType, types, bool } from '../../misc/schema'; + +export type PackedDriveFolder = SchemaType; @EntityRepository(DriveFolder) export class DriveFolderRepository extends Repository { @@ -18,16 +21,16 @@ export class DriveFolderRepository extends Repository { options?: { detail: boolean } - ): Promise> { + ): Promise { const opts = Object.assign({ detail: false }, options); const folder = typeof src === 'object' ? src : await this.findOne(src).then(ensure); - return await rap({ + return await awaitAll({ id: folder.id, - createdAt: folder.createdAt, + createdAt: folder.createdAt.toISOString(), name: folder.name, parentId: folder.parentId, @@ -48,3 +51,50 @@ export class DriveFolderRepository extends Repository { }); } } + +export const packedDriveFolderSchema = { + type: types.object, + optional: bool.false, nullable: bool.false, + properties: { + id: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + description: 'The unique identifier for this Drive folder.', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'date-time', + description: 'The date that the Drive folder was created.' + }, + name: { + type: types.string, + optional: bool.false, nullable: bool.false, + description: 'The folder name.', + }, + foldersCount: { + type: types.number, + optional: bool.true, nullable: bool.false, + description: 'The count of child folders.', + }, + filesCount: { + type: types.number, + optional: bool.true, nullable: bool.false, + description: 'The count of child files.', + }, + parentId: { + type: types.string, + optional: bool.false, nullable: bool.true, + format: 'id', + description: 'The parent folder ID of this folder.', + example: 'xxxxxxxxxx', + }, + parent: { + type: types.object, + optional: bool.true, nullable: bool.true, + ref: 'DriveFolder' + }, + }, +}; diff --git a/src/models/repositories/following.ts b/src/models/repositories/following.ts index 3fff57866f..aba6527fac 100644 --- a/src/models/repositories/following.ts +++ b/src/models/repositories/following.ts @@ -1,8 +1,9 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; -import rap from '@prezzemolo/rap'; import { Following } from '../entities/following'; import { ensure } from '../../prelude/ensure'; +import { awaitAll } from '../../prelude/await-all'; +import { SchemaType, types, bool } from '../../misc/schema'; type LocalFollowerFollowing = Following & { followerHost: null; @@ -28,6 +29,8 @@ type RemoteFolloweeFollowing = Following & { followeeSharedInbox: string; }; +export type PackedFollowing = SchemaType; + @EntityRepository(Following) export class FollowingRepository extends Repository { public isLocalFollower(following: Following): following is LocalFollowerFollowing { @@ -64,22 +67,64 @@ export class FollowingRepository extends Repository { populateFollowee?: boolean; populateFollower?: boolean; } - ) { + ): Promise { const following = typeof src === 'object' ? src : await this.findOne(src).then(ensure); if (opts == null) opts = {}; - return await rap({ + return await awaitAll({ id: following.id, - createdAt: following.createdAt, + createdAt: following.createdAt.toISOString(), followeeId: following.followeeId, followerId: following.followerId, followee: opts.populateFollowee ? Users.pack(following.followee || following.followeeId, me, { detail: true - }) : null, + }) : undefined, follower: opts.populateFollower ? Users.pack(following.follower || following.followerId, me, { detail: true - }) : null, + }) : undefined, }); } } + +export const packedFollowingSchema = { + type: types.object, + optional: bool.false, nullable: bool.false, + properties: { + id: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + description: 'The unique identifier for this following.', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'date-time', + description: 'The date that the following was created.' + }, + followeeId: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + }, + followee: { + type: types.object, + optional: bool.true, nullable: bool.false, + ref: 'User', + description: 'The followee.' + }, + followerId: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + }, + follower: { + type: types.object, + optional: bool.true, nullable: bool.false, + ref: 'User', + description: 'The follower.' + }, + } +}; diff --git a/src/models/repositories/games/reversi/matching.ts b/src/models/repositories/games/reversi/matching.ts index 4d99c6ef76..86c9204456 100644 --- a/src/models/repositories/games/reversi/matching.ts +++ b/src/models/repositories/games/reversi/matching.ts @@ -1,8 +1,8 @@ import { EntityRepository, Repository } from 'typeorm'; -import rap from '@prezzemolo/rap'; import { ReversiMatching } from '../../../entities/games/reversi/matching'; import { Users } from '../../..'; import { ensure } from '../../../../prelude/ensure'; +import { awaitAll } from '../../../../prelude/await-all'; @EntityRepository(ReversiMatching) export class ReversiMatchingRepository extends Repository { @@ -12,7 +12,7 @@ export class ReversiMatchingRepository extends Repository { ) { const matching = typeof src === 'object' ? src : await this.findOne(src).then(ensure); - return await rap({ + return await awaitAll({ id: matching.id, createdAt: matching.createdAt, parentId: matching.parentId, diff --git a/src/models/repositories/messaging-message.ts b/src/models/repositories/messaging-message.ts index 6659273539..33f95bbd5f 100644 --- a/src/models/repositories/messaging-message.ts +++ b/src/models/repositories/messaging-message.ts @@ -2,6 +2,9 @@ import { EntityRepository, Repository } from 'typeorm'; import { MessagingMessage } from '../entities/messaging-message'; import { Users, DriveFiles } from '..'; import { ensure } from '../../prelude/ensure'; +import { types, bool, SchemaType } from '../../misc/schema'; + +export type PackedMessagingMessage = SchemaType; @EntityRepository(MessagingMessage) export class MessagingMessageRepository extends Repository { @@ -15,7 +18,7 @@ export class MessagingMessageRepository extends Repository { options?: { populateRecipient: boolean } - ) { + ): Promise { const opts = options || { populateRecipient: true }; @@ -24,15 +27,73 @@ export class MessagingMessageRepository extends Repository { return { id: message.id, - createdAt: message.createdAt, + createdAt: message.createdAt.toISOString(), text: message.text, userId: message.userId, user: await Users.pack(message.user || message.userId, me), recipientId: message.recipientId, - recipient: opts.populateRecipient ? await Users.pack(message.recipient || message.recipientId, me) : null, + recipient: opts.populateRecipient ? await Users.pack(message.recipient || message.recipientId, me) : undefined, fileId: message.fileId, file: message.fileId ? await DriveFiles.pack(message.fileId) : null, isRead: message.isRead }; } } + +export const packedMessagingMessageSchema = { + type: types.object, + optional: bool.false, nullable: bool.false, + properties: { + id: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + description: 'The unique identifier for this MessagingMessage.', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'date-time', + description: 'The date that the MessagingMessage was created.' + }, + userId: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + }, + user: { + type: types.object, + ref: 'User', + optional: bool.true, nullable: bool.false, + }, + text: { + type: types.string, + optional: bool.false, nullable: bool.true, + }, + fileId: { + type: types.string, + optional: bool.true, nullable: bool.true, + format: 'id', + }, + file: { + type: types.object, + optional: bool.true, nullable: bool.true, + ref: 'DriveFile', + }, + recipientId: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + }, + recipient: { + type: types.object, + optional: bool.true, nullable: bool.false, + ref: 'User' + }, + isRead: { + type: types.boolean, + optional: bool.true, nullable: bool.false, + }, + }, +}; diff --git a/src/models/repositories/muting.ts b/src/models/repositories/muting.ts index 1812e2e713..1e8135a5c9 100644 --- a/src/models/repositories/muting.ts +++ b/src/models/repositories/muting.ts @@ -1,8 +1,11 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; -import rap from '@prezzemolo/rap'; import { Muting } from '../entities/muting'; import { ensure } from '../../prelude/ensure'; +import { awaitAll } from '../../prelude/await-all'; +import { types, bool, SchemaType } from '../../misc/schema'; + +export type PackedMuting = SchemaType; @EntityRepository(Muting) export class MutingRepository extends Repository { @@ -16,14 +19,47 @@ export class MutingRepository extends Repository { public async pack( src: Muting['id'] | Muting, me?: any - ) { + ): Promise { const muting = typeof src === 'object' ? src : await this.findOne(src).then(ensure); - return await rap({ + return await awaitAll({ id: muting.id, + createdAt: muting.createdAt.toISOString(), + muteeId: muting.muteeId, mutee: Users.pack(muting.muteeId, me, { detail: true }) }); } } + +export const packedMutingSchema = { + type: types.object, + optional: bool.false, nullable: bool.false, + properties: { + id: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + description: 'The unique identifier for this muting.', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'date-time', + description: 'The date that the muting was created.' + }, + muteeId: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + }, + mutee: { + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'User', + description: 'The mutee.' + }, + } +}; diff --git a/src/models/repositories/note-reaction.ts b/src/models/repositories/note-reaction.ts index 28191d4ab0..85e5b6b0a1 100644 --- a/src/models/repositories/note-reaction.ts +++ b/src/models/repositories/note-reaction.ts @@ -2,18 +2,54 @@ import { EntityRepository, Repository } from 'typeorm'; import { NoteReaction } from '../entities/note-reaction'; import { Users } from '..'; import { ensure } from '../../prelude/ensure'; +import { types, bool, SchemaType } from '../../misc/schema'; + +export type PackedNoteReaction = SchemaType; @EntityRepository(NoteReaction) export class NoteReactionRepository extends Repository { public async pack( src: NoteReaction['id'] | NoteReaction, me?: any - ) { + ): Promise { const reaction = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return { id: reaction.id, + createdAt: reaction.createdAt.toISOString(), user: await Users.pack(reaction.userId, me), + type: reaction.reaction, }; } } + +export const packedNoteReactionSchema = { + type: types.object, + optional: bool.false, nullable: bool.false, + properties: { + id: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + description: 'The unique identifier for this reaction.', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'date-time', + description: 'The date that the reaction was created.' + }, + user: { + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'User', + description: 'User who performed this reaction.' + }, + type: { + type: types.string, + optional: bool.false, nullable: bool.false, + description: 'The reaction type.' + }, + }, +}; diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts index 1d2ce7a9c9..1dbfabe88d 100644 --- a/src/models/repositories/note.ts +++ b/src/models/repositories/note.ts @@ -4,8 +4,11 @@ import { User } from '../entities/user'; import { unique, concat } from '../../prelude/array'; import { nyaize } from '../../misc/nyaize'; import { Emojis, Users, Apps, PollVotes, DriveFiles, NoteReactions, Followings, Polls } from '..'; -import rap from '@prezzemolo/rap'; import { ensure } from '../../prelude/ensure'; +import { SchemaType, types, bool } from '../../misc/schema'; +import { awaitAll } from '../../prelude/await-all'; + +export type PackedNote = SchemaType; @EntityRepository(Note) export class NoteRepository extends Repository { @@ -13,18 +16,18 @@ export class NoteRepository extends Repository { return x.trim().length <= 100; } - private async hideNote(packedNote: any, meId: User['id'] | null) { + private async hideNote(packedNote: PackedNote, meId: User['id'] | null) { let hide = false; // visibility が specified かつ自分が指定されていなかったら非表示 - if (packedNote.visibility == 'specified') { + if (packedNote.visibility === 'specified') { if (meId == null) { hide = true; } else if (meId === packedNote.userId) { hide = false; } else { // 指定されているかどうか - const specified = packedNote.visibleUserIds.some((id: any) => meId === id); + const specified = packedNote.visibleUserIds!.some((id: any) => meId === id); if (specified) { hide = false; @@ -40,10 +43,10 @@ export class NoteRepository extends Repository { hide = true; } else if (meId === packedNote.userId) { hide = false; - } else if (packedNote.reply && (meId === packedNote.reply.userId)) { + } else if (packedNote.reply && (meId === (packedNote.reply as PackedNote).userId)) { // 自分の投稿に対するリプライ hide = false; - } else if (packedNote.mentions && packedNote.mentions.some((id: any) => meId === id)) { + } else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) { // 自分へのメンション hide = false; } else { @@ -62,14 +65,13 @@ export class NoteRepository extends Repository { } if (hide) { - packedNote.visibleUserIds = null; + packedNote.visibleUserIds = undefined; packedNote.fileIds = []; packedNote.files = []; packedNote.text = null; - packedNote.poll = null; + packedNote.poll = undefined; packedNote.cw = null; - packedNote.tags = []; - packedNote.geo = null; + packedNote.geo = undefined; packedNote.isHidden = true; } } @@ -92,7 +94,7 @@ export class NoteRepository extends Repository { detail?: boolean; skipHide?: boolean; } - ): Promise> { + ): Promise { const opts = Object.assign({ detail: true, skipHide: false @@ -159,9 +161,9 @@ export class NoteRepository extends Repository { const reactionEmojis = unique(concat([note.emojis, Object.keys(note.reactions)])); - const packed = await rap({ + const packed = await awaitAll({ id: note.id, - createdAt: note.createdAt, + createdAt: note.createdAt.toISOString(), app: note.appId ? Apps.pack(note.appId) : undefined, userId: note.userId, user: Users.pack(note.user || note.userId, meId), @@ -213,3 +215,127 @@ export class NoteRepository extends Repository { return packed; } } + +export const packedNoteSchema = { + type: types.object, + optional: bool.false, nullable: bool.false, + properties: { + id: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + description: 'The unique identifier for this Note.', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'date-time', + description: 'The date that the Note was created on Misskey.' + }, + text: { + type: types.string, + optional: bool.false, nullable: bool.true, + }, + cw: { + type: types.string, + optional: bool.true, nullable: bool.true, + }, + userId: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + }, + user: { + type: types.object, + ref: 'User', + optional: bool.false, nullable: bool.false, + }, + replyId: { + type: types.string, + optional: bool.true, nullable: bool.true, + format: 'id', + example: 'xxxxxxxxxx', + }, + renoteId: { + type: types.string, + optional: bool.true, nullable: bool.true, + format: 'id', + example: 'xxxxxxxxxx', + }, + reply: { + type: types.object, + optional: bool.true, nullable: bool.true, + ref: 'Note' + }, + renote: { + type: types.object, + optional: bool.true, nullable: bool.true, + ref: 'Note' + }, + viaMobile: { + type: types.boolean, + optional: bool.true, nullable: bool.false, + }, + isHidden: { + type: types.boolean, + optional: bool.true, nullable: bool.false, + }, + visibility: { + type: types.string, + optional: bool.false, nullable: bool.false, + }, + mentions: { + type: types.array, + optional: bool.true, nullable: bool.false, + items: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id' + } + }, + visibleUserIds: { + type: types.array, + optional: bool.true, nullable: bool.false, + items: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id' + } + }, + fileIds: { + type: types.array, + optional: bool.true, nullable: bool.false, + items: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id' + } + }, + files: { + type: types.array, + optional: bool.true, nullable: bool.false, + items: { + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'DriveFile' + } + }, + tags: { + type: types.array, + optional: bool.true, nullable: bool.false, + items: { + type: types.string, + optional: bool.false, nullable: bool.false, + } + }, + poll: { + type: types.object, + optional: bool.true, nullable: bool.true, + }, + geo: { + type: types.object, + optional: bool.true, nullable: bool.true, + }, + }, +}; diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index 4781d4c065..cf77b35a08 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -1,8 +1,11 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users, Notes } from '..'; -import rap from '@prezzemolo/rap'; import { Notification } from '../entities/notification'; import { ensure } from '../../prelude/ensure'; +import { awaitAll } from '../../prelude/await-all'; +import { types, bool, SchemaType } from '../../misc/schema'; + +export type PackedNotification = SchemaType; @EntityRepository(Notification) export class NotificationRepository extends Repository { @@ -14,12 +17,12 @@ export class NotificationRepository extends Repository { public async pack( src: Notification['id'] | Notification, - ) { + ): Promise { const notification = typeof src === 'object' ? src : await this.findOne(src).then(ensure); - return await rap({ + return await awaitAll({ id: notification.id, - createdAt: notification.createdAt, + createdAt: notification.createdAt.toISOString(), type: notification.type, userId: notification.notifierId, user: Users.pack(notification.notifier || notification.notifierId), @@ -46,3 +49,39 @@ export class NotificationRepository extends Repository { }); } } + +export const packedNotificationSchema = { + type: types.object, + optional: bool.false, nullable: bool.false, + properties: { + id: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + description: 'The unique identifier for this notification.', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'date-time', + description: 'The date that the notification was created.' + }, + type: { + type: types.string, + optional: bool.false, nullable: bool.false, + enum: ['follow', 'receiveFollowRequest', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote'], + description: 'The type of the notification.' + }, + userId: { + type: types.string, + optional: bool.true, nullable: bool.true, + format: 'id', + }, + user: { + type: types.object, + ref: 'User', + optional: bool.true, nullable: bool.true, + }, + } +}; diff --git a/src/models/repositories/user-list.ts b/src/models/repositories/user-list.ts index e591794b8b..54231c7f85 100644 --- a/src/models/repositories/user-list.ts +++ b/src/models/repositories/user-list.ts @@ -2,12 +2,15 @@ import { EntityRepository, Repository } from 'typeorm'; import { UserList } from '../entities/user-list'; import { ensure } from '../../prelude/ensure'; import { UserListJoinings } from '..'; +import { bool, types, SchemaType } from '../../misc/schema'; + +export type PackedUserList = SchemaType; @EntityRepository(UserList) export class UserListRepository extends Repository { public async pack( src: UserList['id'] | UserList, - ) { + ): Promise { const userList = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const users = await UserListJoinings.find({ @@ -16,8 +19,43 @@ export class UserListRepository extends Repository { return { id: userList.id, + createdAt: userList.createdAt.toISOString(), name: userList.name, userIds: users.map(x => x.userId) }; } } + +export const packedUserListSchema = { + type: types.object, + optional: bool.false, nullable: bool.false, + properties: { + id: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'id', + description: 'The unique identifier for this UserList.', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'date-time', + description: 'The date that the UserList was created.' + }, + name: { + type: types.string, + optional: bool.false, nullable: bool.false, + description: 'The name of the UserList.' + }, + userIds: { + type: types.array, + nullable: bool.false, optional: bool.true, + items: { + type: types.string, + nullable: bool.false, optional: bool.false, + format: 'id', + } + }, + }, +}; diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index afba15eb53..6b212203f9 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -1,9 +1,12 @@ import { EntityRepository, Repository, In } from 'typeorm'; import { User, ILocalUser, IRemoteUser } from '../entities/user'; import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..'; -import rap from '@prezzemolo/rap'; import { ensure } from '../../prelude/ensure'; import config from '../../config'; +import { SchemaType, bool, types } from '../../misc/schema'; +import { awaitAll } from '../../prelude/await-all'; + +export type PackedUser = SchemaType; @EntityRepository(User) export class UserRepository extends Repository { @@ -71,7 +74,7 @@ export class UserRepository extends Repository { includeSecrets?: boolean, includeHasUnreadNotes?: boolean } - ): Promise> { + ): Promise { const opts = Object.assign({ detail: false, includeSecrets: false @@ -86,7 +89,7 @@ export class UserRepository extends Repository { const falsy = opts.detail ? false : undefined; - return await rap({ + const packed = { id: user.id, name: user.name, username: user.username, @@ -120,8 +123,8 @@ export class UserRepository extends Repository { ...(opts.detail ? { url: profile!.url, - createdAt: user.createdAt, - updatedAt: user.updatedAt, + createdAt: user.createdAt.toISOString(), + updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, bannerUrl: user.bannerUrl, bannerColor: user.bannerColor, isLocked: user.isLocked, @@ -179,7 +182,9 @@ export class UserRepository extends Repository { isBlocked: relation.isBlocked, isMuted: relation.isMuted, } : {}) - }); + }; + + return await awaitAll(packed); } public isLocalUser(user: User): user is ILocalUser { @@ -216,3 +221,156 @@ export class UserRepository extends Repository { } //#endregion } + +export const packedUserSchema = { + type: types.object, + nullable: bool.false, optional: bool.false, + properties: { + id: { + type: types.string, + nullable: bool.false, optional: bool.false, + format: 'id', + description: 'The unique identifier for this User.', + example: 'xxxxxxxxxx', + }, + username: { + type: types.string, + nullable: bool.false, optional: bool.false, + description: 'The screen name, handle, or alias that this user identifies themselves with.', + example: 'ai' + }, + name: { + type: types.string, + nullable: bool.true, optional: bool.false, + description: 'The name of the user, as they’ve defined it.', + example: '藍' + }, + url: { + type: types.string, + format: 'url', + nullable: bool.true, optional: bool.true, + }, + avatarUrl: { + type: types.string, + format: 'url', + nullable: bool.true, optional: bool.false, + }, + avatarColor: { + type: types.any, + nullable: bool.true, optional: bool.false, + }, + bannerUrl: { + type: types.string, + format: 'url', + nullable: bool.true, optional: bool.true, + }, + bannerColor: { + type: types.any, + nullable: bool.true, optional: bool.true, + }, + emojis: { + type: types.any, + nullable: bool.true, optional: bool.false, + }, + host: { + type: types.string, + nullable: bool.true, optional: bool.false, + example: 'misskey.example.com' + }, + description: { + type: types.string, + nullable: bool.true, optional: bool.true, + description: 'The user-defined UTF-8 string describing their account.', + example: 'Hi masters, I am Ai!' + }, + birthday: { + type: types.string, + nullable: bool.true, optional: bool.true, + example: '2018-03-12' + }, + createdAt: { + type: types.string, + nullable: bool.false, optional: bool.true, + format: 'date-time', + description: 'The date that the user account was created on Misskey.' + }, + updatedAt: { + type: types.string, + nullable: bool.true, optional: bool.true, + format: 'date-time', + }, + location: { + type: types.string, + nullable: bool.true, optional: bool.true, + }, + followersCount: { + type: types.number, + nullable: bool.false, optional: bool.true, + description: 'The number of followers this account currently has.' + }, + followingCount: { + type: types.number, + nullable: bool.false, optional: bool.true, + description: 'The number of users this account is following.' + }, + notesCount: { + type: types.number, + nullable: bool.false, optional: bool.true, + description: 'The number of Notes (including renotes) issued by the user.' + }, + isBot: { + type: types.boolean, + nullable: bool.false, optional: bool.true, + description: 'Whether this account is a bot.' + }, + pinnedNoteIds: { + type: types.array, + nullable: bool.false, optional: bool.true, + items: { + type: types.string, + nullable: bool.false, optional: bool.false, + format: 'id', + } + }, + pinnedNotes: { + type: types.array, + nullable: bool.false, optional: bool.true, + items: { + type: types.object, + nullable: bool.false, optional: bool.false, + ref: 'Note' + } + }, + isCat: { + type: types.boolean, + nullable: bool.false, optional: bool.true, + description: 'Whether this account is a cat.' + }, + isAdmin: { + type: types.boolean, + nullable: bool.false, optional: bool.true, + description: 'Whether this account is the admin.' + }, + isModerator: { + type: types.boolean, + nullable: bool.false, optional: bool.true, + description: 'Whether this account is a moderator.' + }, + isVerified: { + type: types.boolean, + nullable: bool.false, optional: bool.true, + }, + isLocked: { + type: types.boolean, + nullable: bool.false, optional: bool.true, + }, + hasUnreadSpecifiedNotes: { + type: types.boolean, + nullable: bool.false, optional: bool.true, + }, + hasUnreadMentions: { + type: types.boolean, + nullable: bool.false, optional: bool.true, + }, + }, +}; diff --git a/src/prelude/await-all.ts b/src/prelude/await-all.ts new file mode 100644 index 0000000000..24795f3ae5 --- /dev/null +++ b/src/prelude/await-all.ts @@ -0,0 +1,23 @@ +type Await = T extends Promise ? U : T; + +type AwaitAll = { + [P in keyof T]: Await; +}; + +export async function awaitAll(obj: T): Promise> { + const target = {} as any; + const keys = Object.keys(obj); + const values = Object.values(obj); + + const resolvedValues = await Promise.all(values.map(value => + (!value || !value.constructor || value.constructor.name !== 'Object') + ? value + : awaitAll(value) + )); + + for (let i = 0; i < keys.length; i++) { + target[keys[i]] = resolvedValues[i]; + } + + return target; +} diff --git a/src/queue/processors/inbox.ts b/src/queue/processors/inbox.ts index e657859507..21a51c962b 100644 --- a/src/queue/processors/inbox.ts +++ b/src/queue/processors/inbox.ts @@ -10,7 +10,7 @@ import { registerOrFetchInstanceDoc } from '../../services/register-or-fetch-ins import { Instances, Users, UserPublickeys } from '../../models'; import { instanceChart } from '../../services/chart'; import { UserPublickey } from '../../models/entities/user-publickey'; -import fetchMeta from '../../misc/fetch-meta'; +import { fetchMeta } from '../../misc/fetch-meta'; import { toPuny } from '../../misc/convert-host'; import { validActor } from '../../remote/activitypub/type'; import { ensure } from '../../prelude/ensure'; @@ -48,7 +48,6 @@ export default async (job: Bull.Job): Promise => { } // ブロックしてたら中断 - // TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく const meta = await fetchMeta(); if (meta.blockedHosts.includes(host)) { logger.info(`Blocked request: ${host}`); diff --git a/src/remote/activitypub/kernel/announce/note.ts b/src/remote/activitypub/kernel/announce/note.ts index f9822c5187..6843041475 100644 --- a/src/remote/activitypub/kernel/announce/note.ts +++ b/src/remote/activitypub/kernel/announce/note.ts @@ -6,7 +6,7 @@ import { fetchNote, resolveNote } from '../../models/note'; import { resolvePerson } from '../../models/person'; import { apLogger } from '../../logger'; import { extractDbHost } from '../../../../misc/convert-host'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; const logger = apLogger; @@ -26,7 +26,6 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity: } // アナウンス先をブロックしてたら中断 - // TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく const meta = await fetchMeta(); if (meta.blockedHosts.includes(extractDbHost(uri))) return; diff --git a/src/remote/activitypub/models/image.ts b/src/remote/activitypub/models/image.ts index f8b35ea21c..84a1040b2e 100644 --- a/src/remote/activitypub/models/image.ts +++ b/src/remote/activitypub/models/image.ts @@ -1,7 +1,7 @@ import uploadFromUrl from '../../../services/drive/upload-from-url'; import { IRemoteUser } from '../../../models/entities/user'; import Resolver from '../resolver'; -import fetchMeta from '../../../misc/fetch-meta'; +import { fetchMeta } from '../../../misc/fetch-meta'; import { apLogger } from '../logger'; import { DriveFile } from '../../../models/entities/drive-file'; import { DriveFiles } from '../../../models'; diff --git a/src/remote/activitypub/models/note.ts b/src/remote/activitypub/models/note.ts index 8842342342..850b5e65e6 100644 --- a/src/remote/activitypub/models/note.ts +++ b/src/remote/activitypub/models/note.ts @@ -20,7 +20,7 @@ import { Note } from '../../../models/entities/note'; import { IObject, INote } from '../type'; import { Emoji } from '../../../models/entities/emoji'; import { genId } from '../../../misc/gen-id'; -import fetchMeta from '../../../misc/fetch-meta'; +import { fetchMeta } from '../../../misc/fetch-meta'; import { ensure } from '../../../prelude/ensure'; const logger = apLogger; @@ -233,7 +233,6 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver): if (uri == null) throw new Error('missing uri'); // ブロックしてたら中断 - // TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく const meta = await fetchMeta(); if (meta.blockedHosts.includes(extractDbHost(uri))) throw { statusCode: 451 }; diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts index 897dd9acac..da2113faea 100644 --- a/src/remote/activitypub/request.ts +++ b/src/remote/activitypub/request.ts @@ -10,7 +10,7 @@ import { ILocalUser } from '../../models/entities/user'; import { publishApLogStream } from '../../services/stream'; import { apLogger } from './logger'; import { UserKeypairs } from '../../models'; -import fetchMeta from '../../misc/fetch-meta'; +import { fetchMeta } from '../../misc/fetch-meta'; import { toPuny } from '../../misc/convert-host'; import { ensure } from '../../prelude/ensure'; @@ -24,7 +24,6 @@ export default async (user: ILocalUser, url: string, object: any) => { const { protocol, host, hostname, port, pathname, search } = new URL(url); // ブロックしてたら中断 - // TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく const meta = await fetchMeta(); if (meta.blockedHosts.includes(toPuny(host))) return; diff --git a/src/server/api/define.ts b/src/server/api/define.ts index 990cbf2a86..1e2600add0 100644 --- a/src/server/api/define.ts +++ b/src/server/api/define.ts @@ -3,6 +3,7 @@ import { ILocalUser } from '../../models/entities/user'; import { IEndpointMeta } from './endpoints'; import { ApiError } from './error'; import { App } from '../../models/entities/app'; +import { SchemaType } from '../../misc/schema'; type Params = { [P in keyof T['params']]: NonNullable[P]['transform'] extends Function @@ -12,7 +13,11 @@ type Params = { export type Response = Record | void; -export default function (meta: T, cb: (params: Params, user: ILocalUser, app: App, file?: any, cleanup?: Function) => Promise): (params: any, user: ILocalUser, app: App, file?: any) => Promise { +type executor = + (params: Params, user: ILocalUser, app: App, file?: any, cleanup?: Function) => Promise>>; + +export default function (meta: T, cb: executor) + : (params: any, user: ILocalUser, app: App, file?: any) => Promise { return (params: any, user: ILocalUser, app: App, file?: any) => { function cleanup() { fs.unlink(file.path, () => {}); diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts index 3c6380acb4..1f5dd5364f 100644 --- a/src/server/api/endpoints/admin/update-meta.ts +++ b/src/server/api/endpoints/admin/update-meta.ts @@ -164,7 +164,7 @@ export const meta = { }, maintainerName: { - validator: $.optional.str, + validator: $.optional.nullable.str, desc: { 'ja-JP': 'インスタンスの管理者名' } diff --git a/src/server/api/endpoints/ap/show.ts b/src/server/api/endpoints/ap/show.ts index 1b992eeaa7..1bb15117dd 100644 --- a/src/server/api/endpoints/ap/show.ts +++ b/src/server/api/endpoints/ap/show.ts @@ -9,7 +9,7 @@ import { extractDbHost } from '../../../../misc/convert-host'; import { Users, Notes } from '../../../../models'; import { Note } from '../../../../models/entities/note'; import { User } from '../../../../models/entities/user'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; import { validActor } from '../../../../remote/activitypub/type'; export const meta = { diff --git a/src/server/api/endpoints/app/create.ts b/src/server/api/endpoints/app/create.ts index 9db60d2661..833d5060c5 100644 --- a/src/server/api/endpoints/app/create.ts +++ b/src/server/api/endpoints/app/create.ts @@ -4,12 +4,13 @@ import define from '../../define'; import { Apps } from '../../../../models'; import { genId } from '../../../../misc/gen-id'; import { unique } from '../../../../prelude/array'; +import { types, bool } from '../../../../misc/schema'; export const meta = { tags: ['app'], requireCredential: false, - + desc: { 'ja-JP': 'アプリを作成します。', 'en-US': 'Create a application.' @@ -50,29 +51,12 @@ export const meta = { } }, }, - + res: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'アプリケーションのID' - }, - name: { - type: 'string', - description: 'アプリケーションの名前' - }, - callbackUrl: { - type: 'string', - nullable: true, - description: 'コールバックするURL' - }, - secret: { - type: 'string', - description: 'アプリケーションのシークレットキー' - } - } - } + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'App', + }, }; export default define(meta, async (ps, user) => { diff --git a/src/server/api/endpoints/app/show.ts b/src/server/api/endpoints/app/show.ts index ce9baed2ae..e7d3e84388 100644 --- a/src/server/api/endpoints/app/show.ts +++ b/src/server/api/endpoints/app/show.ts @@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Apps } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { tags: ['app'], @@ -13,6 +14,12 @@ export const meta = { }, }, + res: { + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'App', + }, + errors: { noSuchApp: { message: 'No such app.', diff --git a/src/server/api/endpoints/auth/session/generate.ts b/src/server/api/endpoints/auth/session/generate.ts index bca8d33483..9bf27c8e77 100644 --- a/src/server/api/endpoints/auth/session/generate.ts +++ b/src/server/api/endpoints/auth/session/generate.ts @@ -5,12 +5,13 @@ import define from '../../../define'; import { ApiError } from '../../../error'; import { Apps, AuthSessions } from '../../../../../models'; import { genId } from '../../../../../misc/gen-id'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { tags: ['auth'], requireCredential: false, - + desc: { 'ja-JP': 'アプリを認証するためのトークンを作成します。', 'en-US': 'Generate a token for authorize application.' @@ -27,14 +28,18 @@ export const meta = { }, res: { - type: 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { token: { - type: 'string', + type: types.string, + optional: bool.false, nullable: bool.false, description: 'セッションのトークン' }, url: { - type: 'string', + type: types.string, + optional: bool.false, nullable: bool.false, + format: 'url', description: 'セッションのURL' }, } diff --git a/src/server/api/endpoints/auth/session/userkey.ts b/src/server/api/endpoints/auth/session/userkey.ts index 7126ac52c1..b7a58c4750 100644 --- a/src/server/api/endpoints/auth/session/userkey.ts +++ b/src/server/api/endpoints/auth/session/userkey.ts @@ -3,6 +3,7 @@ import define from '../../../define'; import { ApiError } from '../../../error'; import { Apps, AuthSessions, AccessTokens, Users } from '../../../../../models'; import { ensure } from '../../../../../prelude/ensure'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { tags: ['auth'], @@ -28,15 +29,19 @@ export const meta = { }, res: { - type: 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { accessToken: { - type: 'string', + type: types.string, + optional: bool.false, nullable: bool.false, description: 'ユーザーのアクセストークン', }, user: { - type: 'User', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'User', description: '認証したユーザー' }, } diff --git a/src/server/api/endpoints/blocking/list.ts b/src/server/api/endpoints/blocking/list.ts index 97f353579d..5ff1dc0c49 100644 --- a/src/server/api/endpoints/blocking/list.ts +++ b/src/server/api/endpoints/blocking/list.ts @@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { Blockings } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -32,9 +33,12 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Blocking', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Blocking', } }, }; diff --git a/src/server/api/endpoints/drive.ts b/src/server/api/endpoints/drive.ts index adf780301b..4d4516bd80 100644 --- a/src/server/api/endpoints/drive.ts +++ b/src/server/api/endpoints/drive.ts @@ -1,6 +1,7 @@ import define from '../define'; -import fetchMeta from '../../../misc/fetch-meta'; +import { fetchMeta } from '../../../misc/fetch-meta'; import { DriveFiles } from '../../../models'; +import { types, bool } from '../../../misc/schema'; export const meta = { desc: { @@ -15,20 +16,23 @@ export const meta = { kind: 'read:drive', res: { - type: 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { capacity: { - type: 'number' + type: types.number, + optional: bool.false, nullable: bool.false, }, usage: { - type: 'number' + type: types.number, + optional: bool.false, nullable: bool.false, } } } }; export default define(meta, async (ps, user) => { - const instance = await fetchMeta(); + const instance = await fetchMeta(true); // Calculate drive usage const usage = await DriveFiles.clacDriveUsageOf(user); diff --git a/src/server/api/endpoints/drive/files.ts b/src/server/api/endpoints/drive/files.ts index 4e4db6c780..493e14464c 100644 --- a/src/server/api/endpoints/drive/files.ts +++ b/src/server/api/endpoints/drive/files.ts @@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { DriveFiles } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -41,10 +42,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'DriveFile', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'DriveFile', + } }, }; diff --git a/src/server/api/endpoints/drive/files/attached-notes.ts b/src/server/api/endpoints/drive/files/attached-notes.ts index 7214463dde..f770bc7136 100644 --- a/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/src/server/api/endpoints/drive/files/attached-notes.ts @@ -2,7 +2,8 @@ import $ from 'cafy'; import { ID } from '../../../../../misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; -import { DriveFiles } from '../../../../../models'; +import { DriveFiles, Notes } from '../../../../../models'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { stability: 'stable', @@ -29,10 +30,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, errors: { @@ -55,8 +59,11 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.noSuchFile); } - /* v11 TODO - return await packMany(file.metadata.attachedNoteIds || [], user, { + const notes = await Notes.createQueryBuilder('note') + .where(':file = ANY(note.fileIds)', { file: file.id }) + .getMany(); + + return await Notes.packMany(notes, user, { detail: true - });*/ + }); }); diff --git a/src/server/api/endpoints/drive/files/check-existence.ts b/src/server/api/endpoints/drive/files/check-existence.ts index 3a87a9497f..ab19566f1c 100644 --- a/src/server/api/endpoints/drive/files/check-existence.ts +++ b/src/server/api/endpoints/drive/files/check-existence.ts @@ -1,6 +1,7 @@ import $ from 'cafy'; import define from '../../../define'; import { DriveFiles } from '../../../../../models'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { desc: { @@ -24,7 +25,8 @@ export const meta = { }, res: { - type: 'DriveFile', + type: types.boolean, + optional: bool.false, nullable: bool.false, }, }; @@ -34,7 +36,5 @@ export default define(meta, async (ps, user) => { userId: user.id, }); - return { - file: file ? await DriveFiles.pack(file, { self: true }) : null - }; + return file != null; }); diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts index 340a39a41c..0f81a1da99 100644 --- a/src/server/api/endpoints/drive/files/create.ts +++ b/src/server/api/endpoints/drive/files/create.ts @@ -6,6 +6,7 @@ import define from '../../../define'; import { apiLogger } from '../../../logger'; import { ApiError } from '../../../error'; import { DriveFiles } from '../../../../../models'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { desc: { @@ -56,7 +57,9 @@ export const meta = { }, res: { - type: 'DriveFile', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'DriveFile', }, errors: { @@ -87,7 +90,7 @@ export default define(meta, async (ps, user, app, file, cleanup) => { try { // Create file const driveFile = await create(user, file.path, name, null, ps.folderId, ps.force, false, null, null, ps.isSensitive); - return DriveFiles.pack(driveFile, { self: true }); + return await DriveFiles.pack(driveFile, { self: true }); } catch (e) { apiLogger.error(e); throw new ApiError(); diff --git a/src/server/api/endpoints/drive/files/find-by-hash.ts b/src/server/api/endpoints/drive/files/find-by-hash.ts new file mode 100644 index 0000000000..d56e63bc59 --- /dev/null +++ b/src/server/api/endpoints/drive/files/find-by-hash.ts @@ -0,0 +1,44 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { DriveFiles } from '../../../../../models'; +import { types, bool } from '../../../../../misc/schema'; + +export const meta = { + desc: { + 'ja-JP': '与えられたMD5ハッシュ値を持つファイルを取得します。', + }, + + tags: ['drive'], + + requireCredential: true, + + kind: 'read:drive', + + params: { + md5: { + validator: $.str, + desc: { + 'ja-JP': 'ファイルのMD5ハッシュ' + } + } + }, + + res: { + type: types.array, + optional: bool.false, nullable: bool.false, + items: { + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'DriveFile', + } + }, +}; + +export default define(meta, async (ps, user) => { + const files = await DriveFiles.find({ + md5: ps.md5, + userId: user.id, + }); + + return await DriveFiles.packMany(files, { self: true }); +}); diff --git a/src/server/api/endpoints/drive/files/find.ts b/src/server/api/endpoints/drive/files/find.ts index 265850f84c..82b9a97b6d 100644 --- a/src/server/api/endpoints/drive/files/find.ts +++ b/src/server/api/endpoints/drive/files/find.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import { ID } from '../../../../../misc/cafy-id'; import define from '../../../define'; import { DriveFiles } from '../../../../../models'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { requireCredential: true, @@ -22,7 +23,17 @@ export const meta = { 'ja-JP': 'フォルダID' } }, - } + }, + + res: { + type: types.array, + optional: bool.false, nullable: bool.false, + items: { + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'DriveFile', + } + }, }; export default define(meta, async (ps, user) => { diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts index e8c0e683c9..8e74361f9c 100644 --- a/src/server/api/endpoints/drive/files/show.ts +++ b/src/server/api/endpoints/drive/files/show.ts @@ -4,6 +4,7 @@ import define from '../../../define'; import { ApiError } from '../../../error'; import { DriveFile } from '../../../../../models/entities/drive-file'; import { DriveFiles } from '../../../../../models'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { stability: 'stable', @@ -38,7 +39,9 @@ export const meta = { }, res: { - type: 'DriveFile', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'DriveFile', }, errors: { diff --git a/src/server/api/endpoints/drive/folders.ts b/src/server/api/endpoints/drive/folders.ts index 08ae2ff709..dc3174cd2a 100644 --- a/src/server/api/endpoints/drive/folders.ts +++ b/src/server/api/endpoints/drive/folders.ts @@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { DriveFolders } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -37,10 +38,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'DriveFolder', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'DriveFolder', + } }, }; diff --git a/src/server/api/endpoints/drive/folders/find.ts b/src/server/api/endpoints/drive/folders/find.ts index f0989ec5ae..0368d026c3 100644 --- a/src/server/api/endpoints/drive/folders/find.ts +++ b/src/server/api/endpoints/drive/folders/find.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import { ID } from '../../../../../misc/cafy-id'; import define from '../../../define'; import { DriveFolders } from '../../../../../models'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { tags: ['drive'], @@ -25,10 +26,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'DriveFolder', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'DriveFolder', + } }, }; diff --git a/src/server/api/endpoints/drive/folders/show.ts b/src/server/api/endpoints/drive/folders/show.ts index 60507e7d7f..a020b46aa9 100644 --- a/src/server/api/endpoints/drive/folders/show.ts +++ b/src/server/api/endpoints/drive/folders/show.ts @@ -3,6 +3,7 @@ import { ID } from '../../../../../misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { DriveFolders } from '../../../../../models'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { stability: 'stable', @@ -29,7 +30,9 @@ export const meta = { }, res: { - type: 'DriveFolder', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'DriveFolder', }, errors: { diff --git a/src/server/api/endpoints/drive/stream.ts b/src/server/api/endpoints/drive/stream.ts index 96d9f82421..db17979a4b 100644 --- a/src/server/api/endpoints/drive/stream.ts +++ b/src/server/api/endpoints/drive/stream.ts @@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { DriveFiles } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; +import { types, bool } from '../../../../misc/schema'; export const meta = { tags: ['drive'], @@ -31,10 +32,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'DriveFile', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'DriveFile', + } }, }; diff --git a/src/server/api/endpoints/federation/instances.ts b/src/server/api/endpoints/federation/instances.ts index 301338ed96..3c4e0037d6 100644 --- a/src/server/api/endpoints/federation/instances.ts +++ b/src/server/api/endpoints/federation/instances.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import define from '../../define'; import { Instances } from '../../../../models'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; export const meta = { tags: ['federation'], @@ -62,7 +62,7 @@ export default define(meta, async (ps, me) => { } if (typeof ps.blocked === 'boolean') { - const meta = await fetchMeta(); + const meta = await fetchMeta(true); if (ps.blocked) { query.andWhere('instance.host IN (:...blocks)', { blocks: meta.blockedHosts }); } else { diff --git a/src/server/api/endpoints/hashtags/list.ts b/src/server/api/endpoints/hashtags/list.ts index 2998bc1a13..89cc926422 100644 --- a/src/server/api/endpoints/hashtags/list.ts +++ b/src/server/api/endpoints/hashtags/list.ts @@ -1,6 +1,7 @@ import $ from 'cafy'; import define from '../../define'; import { Hashtags } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { tags: ['hashtags'], @@ -47,9 +48,12 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Hashtag' + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Hashtag', } }, }; diff --git a/src/server/api/endpoints/hashtags/search.ts b/src/server/api/endpoints/hashtags/search.ts index 802f177993..0d2704d01c 100644 --- a/src/server/api/endpoints/hashtags/search.ts +++ b/src/server/api/endpoints/hashtags/search.ts @@ -1,6 +1,7 @@ import $ from 'cafy'; import define from '../../define'; import { Hashtags } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -37,9 +38,11 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'string' + type: types.string, + optional: bool.false, nullable: bool.false, } }, }; diff --git a/src/server/api/endpoints/hashtags/trend.ts b/src/server/api/endpoints/hashtags/trend.ts index e01e9d698f..84b750f2c1 100644 --- a/src/server/api/endpoints/hashtags/trend.ts +++ b/src/server/api/endpoints/hashtags/trend.ts @@ -1,5 +1,5 @@ import define from '../../define'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; import { Notes } from '../../../../models'; import { Note } from '../../../../models/entities/note'; @@ -24,7 +24,7 @@ export const meta = { }; export default define(meta, async () => { - const instance = await fetchMeta(); + const instance = await fetchMeta(true); const hiddenTags = instance.hiddenTags.map(t => t.toLowerCase()); const tagNotes = await Notes.createQueryBuilder('note') diff --git a/src/server/api/endpoints/hashtags/users.ts b/src/server/api/endpoints/hashtags/users.ts index fa58f2f2c0..b842f9de64 100644 --- a/src/server/api/endpoints/hashtags/users.ts +++ b/src/server/api/endpoints/hashtags/users.ts @@ -1,6 +1,7 @@ import $ from 'cafy'; import define from '../../define'; import { Users } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { requireCredential: false, @@ -47,9 +48,12 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'User' + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'User', } }, }; diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts index afad38c469..4ecd507e16 100644 --- a/src/server/api/endpoints/i.ts +++ b/src/server/api/endpoints/i.ts @@ -1,5 +1,6 @@ import define from '../define'; import { Users } from '../../../models'; +import { types, bool } from '../../../misc/schema'; export const meta = { stability: 'stable', @@ -15,8 +16,10 @@ export const meta = { params: {}, res: { - type: 'User', - } + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'User', + }, }; export default define(meta, async (ps, user, app) => { diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 56074c9d00..41513e5daa 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -4,6 +4,7 @@ import { readNotification } from '../../common/read-notification'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notifications, Followings, Mutings } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -53,10 +54,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Notification', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Notification', + } }, }; diff --git a/src/server/api/endpoints/i/update-email.ts b/src/server/api/endpoints/i/update-email.ts index e02f53a643..d4b9721d82 100644 --- a/src/server/api/endpoints/i/update-email.ts +++ b/src/server/api/endpoints/i/update-email.ts @@ -2,7 +2,7 @@ import $ from 'cafy'; import { publishMainStream } from '../../../../services/stream'; import define from '../../define'; import * as nodemailer from 'nodemailer'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; import rndstr from 'rndstr'; import config from '../../../../config'; import * as ms from 'ms'; @@ -63,7 +63,7 @@ export default define(meta, async (ps, user) => { emailVerifyCode: code }); - const meta = await fetchMeta(); + const meta = await fetchMeta(true); const enableAuth = meta.smtpUser != null && meta.smtpUser !== ''; diff --git a/src/server/api/endpoints/messaging/history.ts b/src/server/api/endpoints/messaging/history.ts index c2d746e481..27e38bbdec 100644 --- a/src/server/api/endpoints/messaging/history.ts +++ b/src/server/api/endpoints/messaging/history.ts @@ -3,6 +3,7 @@ import define from '../../define'; import { MessagingMessage } from '../../../../models/entities/messaging-message'; import { MessagingMessages, Mutings } from '../../../../models'; import { Brackets } from 'typeorm'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -24,10 +25,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'MessagingMessage', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'MessagingMessage', + } }, }; diff --git a/src/server/api/endpoints/messaging/messages.ts b/src/server/api/endpoints/messaging/messages.ts index add21e5f19..0d5295bff3 100644 --- a/src/server/api/endpoints/messaging/messages.ts +++ b/src/server/api/endpoints/messaging/messages.ts @@ -6,6 +6,7 @@ import { ApiError } from '../../error'; import { getUser } from '../../common/getters'; import { MessagingMessages } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -48,10 +49,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'MessagingMessage', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'MessagingMessage', + } }, errors: { diff --git a/src/server/api/endpoints/messaging/messages/create.ts b/src/server/api/endpoints/messaging/messages/create.ts index 30ac0849a3..388852b9cd 100644 --- a/src/server/api/endpoints/messaging/messages/create.ts +++ b/src/server/api/endpoints/messaging/messages/create.ts @@ -9,6 +9,7 @@ import { getUser } from '../../../common/getters'; import { MessagingMessages, DriveFiles, Mutings } from '../../../../../models'; import { MessagingMessage } from '../../../../../models/entities/messaging-message'; import { genId } from '../../../../../misc/gen-id'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { desc: { @@ -41,7 +42,9 @@ export const meta = { }, res: { - type: 'MessagingMessage', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'MessagingMessage', }, errors: { diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts index 785f21f22b..793eb5a204 100644 --- a/src/server/api/endpoints/meta.ts +++ b/src/server/api/endpoints/meta.ts @@ -2,9 +2,10 @@ import $ from 'cafy'; import * as os from 'os'; import config from '../../../config'; import define from '../define'; -import fetchMeta from '../../../misc/fetch-meta'; +import { fetchMeta } from '../../../misc/fetch-meta'; import * as pkg from '../../../../package.json'; import { Emojis } from '../../../models'; +import { types, bool } from '../../../misc/schema'; export const meta = { stability: 'stable', @@ -26,32 +27,40 @@ export const meta = { }, res: { - type: 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { version: { - type: 'string', + type: types.string, + optional: bool.false, nullable: bool.false, description: 'The version of Misskey of this instance.', example: pkg.version }, name: { - type: 'string', + type: types.string, + optional: bool.false, nullable: bool.false, description: 'The name of this instance.', }, description: { - type: 'string', + type: types.string, + optional: bool.false, nullable: bool.false, description: 'The description of this instance.', }, announcements: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { title: { - type: 'string', + type: types.string, + optional: bool.false, nullable: bool.false, description: 'The title of the announcement.', }, text: { - type: 'string', + type: types.string, + optional: bool.false, nullable: bool.false, description: 'The text of the announcement. (can be HTML)', }, } @@ -59,19 +68,23 @@ export const meta = { description: 'The announcements of this instance.', }, disableRegistration: { - type: 'boolean', + type: types.boolean, + optional: bool.false, nullable: bool.false, description: 'Whether disabled open registration.', }, disableLocalTimeline: { - type: 'boolean', + type: types.boolean, + optional: bool.false, nullable: bool.false, description: 'Whether disabled LTL and STL.', }, disableGlobalTimeline: { - type: 'boolean', + type: types.boolean, + optional: bool.false, nullable: bool.false, description: 'Whether disabled GTL.', }, enableEmojiReaction: { - type: 'boolean', + type: types.boolean, + optional: bool.false, nullable: bool.false, description: 'Whether enabled emoji reaction.', }, } @@ -79,7 +92,7 @@ export const meta = { }; export default define(meta, async (ps, me) => { - const instance = await fetchMeta(); + const instance = await fetchMeta(true); const emojis = await Emojis.find({ host: null }); diff --git a/src/server/api/endpoints/mute/list.ts b/src/server/api/endpoints/mute/list.ts index 0fd8a4860d..f9ea380c76 100644 --- a/src/server/api/endpoints/mute/list.ts +++ b/src/server/api/endpoints/mute/list.ts @@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Mutings } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -32,9 +33,12 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Muting', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Muting', } }, }; diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index 17ba969350..7aa19c9a2b 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -3,6 +3,7 @@ import { ID } from '../../../misc/cafy-id'; import define from '../define'; import { makePaginationQuery } from '../common/make-pagination-query'; import { Notes } from '../../../models'; +import { types, bool } from '../../../misc/schema'; export const meta = { desc: { @@ -62,9 +63,12 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', } }, }; diff --git a/src/server/api/endpoints/notes/children.ts b/src/server/api/endpoints/notes/children.ts index 2b4ae2a312..e8861eafa1 100644 --- a/src/server/api/endpoints/notes/children.ts +++ b/src/server/api/endpoints/notes/children.ts @@ -6,6 +6,7 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query' import { generateMuteQuery } from '../../common/generate-mute-query'; import { Brackets } from 'typeorm'; import { Notes } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -41,10 +42,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, }; diff --git a/src/server/api/endpoints/notes/conversation.ts b/src/server/api/endpoints/notes/conversation.ts index 6b26e31c07..acd3ac75ef 100644 --- a/src/server/api/endpoints/notes/conversation.ts +++ b/src/server/api/endpoints/notes/conversation.ts @@ -5,6 +5,7 @@ import { ApiError } from '../../error'; import { getNote } from '../../common/getters'; import { Note } from '../../../../models/entities/note'; import { Notes } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -37,10 +38,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, errors: { diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts index 994dfb4dca..6cd84b866f 100644 --- a/src/server/api/endpoints/notes/create.ts +++ b/src/server/api/endpoints/notes/create.ts @@ -3,13 +3,14 @@ import * as ms from 'ms'; import { length } from 'stringz'; import create from '../../../../services/note/create'; import define from '../../define'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; import { ApiError } from '../../error'; import { ID } from '../../../../misc/cafy-id'; import { User } from '../../../../models/entities/user'; import { Users, DriveFiles, Notes } from '../../../../models'; import { DriveFile } from '../../../../models/entities/drive-file'; import { Note } from '../../../../models/entities/note'; +import { types, bool } from '../../../../misc/schema'; let maxNoteTextLength = 1000; @@ -174,10 +175,13 @@ export const meta = { }, res: { - type: 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { createdNote: { - type: 'Note', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', description: '作成した投稿' } } diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts index fa9ae39e3a..64750815b0 100644 --- a/src/server/api/endpoints/notes/featured.ts +++ b/src/server/api/endpoints/notes/featured.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import define from '../../define'; import { generateMuteQuery } from '../../common/generate-mute-query'; import { Notes } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -24,10 +25,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, }; diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index ceffb1cf4a..3631208da7 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -1,12 +1,13 @@ import $ from 'cafy'; import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; import { ApiError } from '../../error'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notes } from '../../../../models'; import { generateMuteQuery } from '../../common/generate-mute-query'; import { activeUsersChart } from '../../../../services/chart'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -46,10 +47,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, errors: { @@ -62,7 +66,6 @@ export const meta = { }; export default define(meta, async (ps, user) => { - // TODO どっかにキャッシュ const m = await fetchMeta(); if (m.disableGlobalTimeline) { if (user == null || (!user.isAdmin && !user.isModerator)) { diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index effee2b134..c05c8dedd6 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; import { ApiError } from '../../error'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Followings, Notes } from '../../../../models'; @@ -9,6 +9,7 @@ import { Brackets } from 'typeorm'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { generateMuteQuery } from '../../common/generate-mute-query'; import { activeUsersChart } from '../../../../services/chart'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -89,10 +90,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, errors: { @@ -105,7 +109,6 @@ export const meta = { }; export default define(meta, async (ps, user) => { - // TODO どっかにキャッシュ const m = await fetchMeta(); if (m.disableLocalTimeline && !user.isAdmin && !user.isModerator) { throw new ApiError(meta.errors.stlDisabled); diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index c10c0d7482..ca84fc6ef9 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; import { ApiError } from '../../error'; import { Notes } from '../../../../models'; import { generateMuteQuery } from '../../common/generate-mute-query'; @@ -9,6 +9,7 @@ import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { activeUsersChart } from '../../../../services/chart'; import { Brackets } from 'typeorm'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -63,10 +64,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, errors: { @@ -79,7 +83,6 @@ export const meta = { }; export default define(meta, async (ps, user) => { - // TODO どっかにキャッシュ const m = await fetchMeta(); if (m.disableLocalTimeline) { if (user == null || (!user.isAdmin && !user.isModerator)) { diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index b7f614915b..02e44492ba 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -7,6 +7,7 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query' import { generateMuteQuery } from '../../common/generate-mute-query'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Brackets } from 'typeorm'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -43,10 +44,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, }; diff --git a/src/server/api/endpoints/notes/reactions.ts b/src/server/api/endpoints/notes/reactions.ts index bcb0b6d1ec..0773b4faa2 100644 --- a/src/server/api/endpoints/notes/reactions.ts +++ b/src/server/api/endpoints/notes/reactions.ts @@ -4,6 +4,7 @@ import define from '../../define'; import { getNote } from '../../common/getters'; import { ApiError } from '../../error'; import { NoteReactions } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -44,9 +45,12 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Reaction' + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'NoteReaction', } }, diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts index 74a8ea918e..00dfac3770 100644 --- a/src/server/api/endpoints/notes/renotes.ts +++ b/src/server/api/endpoints/notes/renotes.ts @@ -7,6 +7,7 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query' import { generateMuteQuery } from '../../common/generate-mute-query'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notes } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -42,10 +43,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, errors: { diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 980ff2446e..5fb0fd989f 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -5,6 +5,7 @@ import { Notes } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { generateMuteQuery } from '../../common/generate-mute-query'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -46,10 +47,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, }; diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts index cba3724b6f..0b49f896ad 100644 --- a/src/server/api/endpoints/notes/search-by-tag.ts +++ b/src/server/api/endpoints/notes/search-by-tag.ts @@ -6,6 +6,7 @@ import { Notes } from '../../../../models'; import { generateMuteQuery } from '../../common/generate-mute-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { Brackets } from 'typeorm'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -81,10 +82,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, }; @@ -96,14 +100,14 @@ export default define(meta, async (ps, me) => { if (me) generateMuteQuery(query, me); if (ps.tag) { - query.andWhere(':tag = ANY(note.tags)', { tag: ps.tag }); + query.andWhere(':tag = ANY(note.tags)', { tag: ps.tag.toLowerCase() }); } else { let i = 0; query.andWhere(new Brackets(qb => { for (const tags of ps.query!) { qb.orWhere(new Brackets(qb => { for (const tag of tags) { - qb.andWhere(`:tag${i} = ANY(note.tags)`, { [`tag${i}`]: tag }); + qb.andWhere(`:tag${i} = ANY(note.tags)`, { [`tag${i}`]: tag.toLowerCase() }); i++; } })); diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index 4d5ac6fbe0..daf992b639 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -4,6 +4,7 @@ import define from '../../define'; import { ApiError } from '../../error'; import { Notes } from '../../../../models'; import { In } from 'typeorm'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -32,10 +33,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, errors: { diff --git a/src/server/api/endpoints/notes/show.ts b/src/server/api/endpoints/notes/show.ts index d41dc20c54..54b420813d 100644 --- a/src/server/api/endpoints/notes/show.ts +++ b/src/server/api/endpoints/notes/show.ts @@ -4,6 +4,7 @@ import define from '../../define'; import { getNote } from '../../common/getters'; import { ApiError } from '../../error'; import { Notes } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { stability: 'stable', @@ -28,7 +29,9 @@ export const meta = { }, res: { - type: 'Note', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', }, errors: { diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index c27f3df1b7..5e692db389 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -7,6 +7,7 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query' import { generateMuteQuery } from '../../common/generate-mute-query'; import { activeUsersChart } from '../../../../services/chart'; import { Brackets } from 'typeorm'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -88,10 +89,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, }; diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index 05f171af8b..c16018d434 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -6,6 +6,7 @@ import { UserLists, UserListJoinings, Notes } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { activeUsersChart } from '../../../../services/chart'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -94,10 +95,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, errors: { diff --git a/src/server/api/endpoints/stats.ts b/src/server/api/endpoints/stats.ts index f3ebaa16ad..f85109b4b4 100644 --- a/src/server/api/endpoints/stats.ts +++ b/src/server/api/endpoints/stats.ts @@ -1,6 +1,7 @@ import define from '../define'; import { Notes, Users } from '../../../models'; import { federationChart, driveChart } from '../../../services/chart'; +import { bool, types } from '../../../misc/schema'; export const meta = { requireCredential: false, @@ -15,26 +16,32 @@ export const meta = { }, res: { - type: 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { notesCount: { - type: 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'The count of all (local/remote) notes of this instance.', }, originalNotesCount: { - type: 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'The count of all local notes of this instance.', }, usersCount: { - type: 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'The count of all (local/remote) accounts of this instance.', }, originalUsersCount: { - type: 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'The count of all local accounts of this instance.', }, instances: { - type: 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'The count of federated instances.', }, } @@ -42,7 +49,14 @@ export const meta = { }; export default define(meta, async () => { - const [notesCount, originalNotesCount, usersCount, originalUsersCount, instances, driveUsageLocal, driveUsageRemote] = await Promise.all([ + const [notesCount, + originalNotesCount, + usersCount, + originalUsersCount, + instances, + driveUsageLocal, + driveUsageRemote + ] = await Promise.all([ Notes.count(), Notes.count({ userHost: null }), Users.count(), @@ -53,6 +67,12 @@ export default define(meta, async () => { ]); return { - notesCount, originalNotesCount, usersCount, originalUsersCount, instances, driveUsageLocal, driveUsageRemote + notesCount, + originalNotesCount, + usersCount, + originalUsersCount, + instances, + driveUsageLocal, + driveUsageRemote }; }); diff --git a/src/server/api/endpoints/sw/register.ts b/src/server/api/endpoints/sw/register.ts index 559937ca2f..a4838b4565 100644 --- a/src/server/api/endpoints/sw/register.ts +++ b/src/server/api/endpoints/sw/register.ts @@ -1,6 +1,6 @@ import $ from 'cafy'; import define from '../../define'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; import { genId } from '../../../../misc/gen-id'; import { SwSubscriptions } from '../../../../models'; @@ -33,7 +33,7 @@ export default define(meta, async (ps, user) => { publickey: ps.publickey, }); - const instance = await fetchMeta(); + const instance = await fetchMeta(true); if (exist != null) { return { diff --git a/src/server/api/endpoints/users.ts b/src/server/api/endpoints/users.ts index 18af0a2685..c710706f0c 100644 --- a/src/server/api/endpoints/users.ts +++ b/src/server/api/endpoints/users.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import define from '../define'; import { Users } from '../../../models'; import { generateMuteQueryForUsers } from '../common/generate-mute-query'; +import { types, bool } from '../../../misc/schema'; export const meta = { tags: ['users'], @@ -53,9 +54,12 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'User', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'User', } }, }; diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts index 0cb68353ca..465b71e2e6 100644 --- a/src/server/api/endpoints/users/followers.ts +++ b/src/server/api/endpoints/users/followers.ts @@ -5,6 +5,7 @@ import { ApiError } from '../../error'; import { Users, Followings } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { toPunyNullable } from '../../../../misc/convert-host'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -48,10 +49,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Following', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Following', + } }, errors: { diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts index 2e273dc0c2..2a7748ac64 100644 --- a/src/server/api/endpoints/users/following.ts +++ b/src/server/api/endpoints/users/following.ts @@ -5,6 +5,7 @@ import { ApiError } from '../../error'; import { Users, Followings } from '../../../../models'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { toPunyNullable } from '../../../../misc/convert-host'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -48,10 +49,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Following', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Following', + } }, errors: { diff --git a/src/server/api/endpoints/users/get-frequently-replied-users.ts b/src/server/api/endpoints/users/get-frequently-replied-users.ts index a1d140c6c9..420936c089 100644 --- a/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -6,6 +6,7 @@ import { ApiError } from '../../error'; import { getUser } from '../../common/getters'; import { Not, In } from 'typeorm'; import { Notes, Users } from '../../../../models'; +import { types, bool } from '../../../../misc/schema'; export const meta = { tags: ['users'], @@ -28,9 +29,12 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'User', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'User', } }, diff --git a/src/server/api/endpoints/users/lists/create.ts b/src/server/api/endpoints/users/lists/create.ts index 21dc6d331d..79efffbf9e 100644 --- a/src/server/api/endpoints/users/lists/create.ts +++ b/src/server/api/endpoints/users/lists/create.ts @@ -3,6 +3,7 @@ import define from '../../../define'; import { UserLists } from '../../../../../models'; import { genId } from '../../../../../misc/gen-id'; import { UserList } from '../../../../../models/entities/user-list'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { desc: { @@ -17,10 +18,16 @@ export const meta = { kind: 'write:account', params: { - title: { + name: { validator: $.str.range(1, 100) } - } + }, + + res: { + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'UserList', + }, }; export default define(meta, async (ps, user) => { @@ -28,7 +35,7 @@ export default define(meta, async (ps, user) => { id: genId(), createdAt: new Date(), userId: user.id, - name: ps.title, + name: ps.name, } as UserList); return await UserLists.pack(userList); diff --git a/src/server/api/endpoints/users/lists/list.ts b/src/server/api/endpoints/users/lists/list.ts index b05fc45527..684086b5c6 100644 --- a/src/server/api/endpoints/users/lists/list.ts +++ b/src/server/api/endpoints/users/lists/list.ts @@ -1,5 +1,6 @@ import define from '../../../define'; import { UserLists } from '../../../../../models'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { desc: { @@ -13,10 +14,13 @@ export const meta = { kind: 'read:account', res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'UserList', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'UserList', + } }, }; diff --git a/src/server/api/endpoints/users/lists/show.ts b/src/server/api/endpoints/users/lists/show.ts index 1a997ec7c5..395f9352d4 100644 --- a/src/server/api/endpoints/users/lists/show.ts +++ b/src/server/api/endpoints/users/lists/show.ts @@ -3,6 +3,7 @@ import { ID } from '../../../../../misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { UserLists } from '../../../../../models'; +import { types, bool } from '../../../../../misc/schema'; export const meta = { desc: { @@ -23,7 +24,9 @@ export const meta = { }, res: { - type: 'UserList' + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'UserList', }, errors: { diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index 4691a99394..fdc50e4dae 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -8,6 +8,7 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query' import { Notes } from '../../../../models'; import { generateMuteQuery } from '../../common/generate-mute-query'; import { Brackets } from 'typeorm'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -119,10 +120,13 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'Note', - }, + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'Note', + } }, errors: { diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts index 9e16e34e39..67b646a35f 100644 --- a/src/server/api/endpoints/users/recommendation.ts +++ b/src/server/api/endpoints/users/recommendation.ts @@ -3,6 +3,7 @@ import $ from 'cafy'; import define from '../../define'; import { Users, Followings } from '../../../../models'; import { generateMuteQueryForUsers } from '../../common/generate-mute-query'; +import { types, bool } from '../../../../misc/schema'; export const meta = { desc: { @@ -28,9 +29,12 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'User', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'User', } }, }; diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts index 96da221d97..2809465fd7 100644 --- a/src/server/api/endpoints/users/search.ts +++ b/src/server/api/endpoints/users/search.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import define from '../../define'; import { Users } from '../../../../models'; import { User } from '../../../../models/entities/user'; +import { bool, types } from '../../../../misc/schema'; export const meta = { desc: { @@ -54,9 +55,12 @@ export const meta = { }, res: { - type: 'array', + type: types.array, + optional: bool.false, nullable: bool.false, items: { - type: 'User', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'User', } }, }; diff --git a/src/server/api/endpoints/users/show.ts b/src/server/api/endpoints/users/show.ts index a401166c55..820c44e81b 100644 --- a/src/server/api/endpoints/users/show.ts +++ b/src/server/api/endpoints/users/show.ts @@ -6,6 +6,7 @@ import { ApiError } from '../../error'; import { ID } from '../../../../misc/cafy-id'; import { Users } from '../../../../models'; import { In } from 'typeorm'; +import { bool, types } from '../../../../misc/schema'; export const meta = { desc: { @@ -42,7 +43,9 @@ export const meta = { }, res: { - type: 'User', + type: types.object, + optional: bool.false, nullable: bool.false, + ref: 'User', }, errors: { diff --git a/src/server/api/openapi/description.ts b/src/server/api/openapi/description.ts index 0a4c8699d1..9006dfbfa4 100644 --- a/src/server/api/openapi/description.ts +++ b/src/server/api/openapi/description.ts @@ -44,20 +44,20 @@ export function getDescription(lang = 'ja-JP'): string { const descriptions = { 'ja-JP': `**Misskey is a decentralized microblogging platform.** -## Usage +# Usage **APIはすべてPOSTでリクエスト/レスポンスともにJSON形式です。** 一部のAPIはリクエストに認証情報(APIキー)が必要です。リクエストの際に\`i\`というパラメータでAPIキーを添付してください。 -### 自分のアカウントのAPIキーを取得する +## 自分のアカウントのAPIキーを取得する 「設定 > API」で、自分のAPIキーを取得できます。 > アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。 -### アプリケーションとしてAPIキーを取得する +## アプリケーションとしてAPIキーを取得する 直接ユーザーのAPIキーをアプリケーションが扱うのはセキュリティ上のリスクがあるので、 アプリケーションからAPIを利用する際には、アプリケーションとアプリケーションを利用するユーザーが結び付けられた専用のAPIキーを発行します。 -#### 1.アプリケーションを登録する +### 1.アプリケーションを登録する まず、あなたのアプリケーションやWebサービス(以後、あなたのアプリと呼びます)をMisskeyに登録します。 [デベロッパーセンター](/dev)にアクセスし、「アプリ > アプリ作成」からアプリを作成してください。 @@ -65,7 +65,7 @@ export function getDescription(lang = 'ja-JP'): string { > アプリに成りすまされる可能性があるため、極力このシークレットキーは公開しないようにしてください。

-#### 2.ユーザーに認証させる +### 2.ユーザーに認証させる アプリを使ってもらうには、ユーザーにアカウントへのアクセスの許可をもらう必要があります。 認証セッションを開始するには、[${config.apiUrl}/auth/session/generate](#operation/auth/session/generate) へパラメータに\`appSecret\`としてシークレットキーを含めたリクエストを送信します。 @@ -76,7 +76,7 @@ export function getDescription(lang = 'ja-JP'): string { あなたのアプリがコールバックURLを設定していない場合、ユーザーがあなたのアプリの連携を許可したことを(何らかの方法で(たとえばボタンを押させるなど))確認出来るようにしてください。 -#### 3.アクセストークンを取得する +### 3.アクセストークンを取得する ユーザーが連携を許可したら、[${config.apiUrl}/auth/session/userkey](#operation/auth/session/userkey) へリクエストを送信します。 上手くいけば、認証したユーザーのアクセストークンがレスポンスとして取得できます。おめでとうございます! @@ -88,7 +88,7 @@ APIキーの生成方法を擬似コードで表すと次のようになりま const i = sha256(userToken + secretKey); \`\`\` -## Permissions +# Permissions |Permisson (kind)|Description|Endpoints| |:--|:--|:--| ${permissionTable} diff --git a/src/server/api/openapi/gen-spec.ts b/src/server/api/openapi/gen-spec.ts index d194c6c8a8..de26b970ea 100644 --- a/src/server/api/openapi/gen-spec.ts +++ b/src/server/api/openapi/gen-spec.ts @@ -2,9 +2,8 @@ import endpoints from '../endpoints'; import { Context } from 'cafy'; import config from '../../../config'; import { errors as basicErrors } from './errors'; -import { schemas } from './schemas'; +import { schemas, convertSchemaToOpenApiSchema } from './schemas'; import { getDescription } from './description'; -import { convertOpenApiSchema } from '../../../misc/schema'; export function genOpenapiSpec(lang = 'ja-JP') { const spec = { @@ -59,7 +58,7 @@ export function genOpenapiSpec(lang = 'ja-JP') { deprecated: (param.data || {}).deprecated, ...((param.data || {}).default ? { default: (param.data || {}).default } : {}), type: param.name === 'ID' ? 'string' : param.name.toLowerCase(), - ...(param.name === 'ID' ? { example: 'xxxxxxxxxxxxxxxxxxxxxxxx', format: 'id' } : {}), + ...(param.name === 'ID' ? { example: 'xxxxxxxxxx', format: 'id' } : {}), nullable: param.isNullable, ...(param.name === 'String' ? { ...((param as any).enum ? { enum: (param as any).enum } : {}), @@ -106,7 +105,7 @@ export function genOpenapiSpec(lang = 'ja-JP') { const required = endpoint.meta.params ? Object.entries(endpoint.meta.params).filter(([k, v]) => !v.validator.isOptional).map(([k, v]) => k) : []; - const resSchema = endpoint.meta.res ? convertOpenApiSchema(endpoint.meta.res) : {}; + const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res) : {}; let desc = (endpoint.meta.desc ? endpoint.meta.desc[lang] : 'No description provided.') + '\n\n'; desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`; diff --git a/src/server/api/openapi/schemas.ts b/src/server/api/openapi/schemas.ts index 65826d9321..e54f989e74 100644 --- a/src/server/api/openapi/schemas.ts +++ b/src/server/api/openapi/schemas.ts @@ -1,3 +1,39 @@ +import { packedUserSchema } from '../../../models/repositories/user'; +import { Schema } from '../../../misc/schema'; +import { packedNoteSchema } from '../../../models/repositories/note'; +import { packedUserListSchema } from '../../../models/repositories/user-list'; +import { packedAppSchema } from '../../../models/repositories/app'; +import { packedMessagingMessageSchema } from '../../../models/repositories/messaging-message'; +import { packedNotificationSchema } from '../../../models/repositories/notification'; +import { packedDriveFileSchema } from '../../../models/repositories/drive-file'; +import { packedDriveFolderSchema } from '../../../models/repositories/drive-folder'; +import { packedFollowingSchema } from '../../../models/repositories/following'; +import { packedMutingSchema } from '../../../models/repositories/muting'; +import { packedBlockingSchema } from '../../../models/repositories/blocking'; +import { packedNoteReactionSchema } from '../../../models/repositories/note-reaction'; + +export function convertSchemaToOpenApiSchema(schema: Schema) { + const res: any = schema; + + if (schema.type === 'object' && schema.properties) { + res.required = Object.entries(schema.properties).filter(([k, v]) => v.optional !== true).map(([k]) => k); + + for (const k of Object.keys(schema.properties)) { + res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k]); + } + } + + if (schema.type === 'array' && schema.items) { + res.items = convertSchemaToOpenApiSchema(schema.items); + } + + if (schema.ref) { + res.$ref = `#/components/schemas/${schema.ref}`; + } + + return res; +} + export const schemas = { Error: { type: 'object', @@ -26,413 +62,18 @@ export const schemas = { required: ['error'] }, - User: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this User.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - username: { - type: 'string', - description: 'The screen name, handle, or alias that this user identifies themselves with.', - example: 'ai' - }, - name: { - type: 'string', - nullable: true, - description: 'The name of the user, as they’ve defined it.', - example: '藍' - }, - host: { - type: 'string', - nullable: true, - example: 'misskey.example.com' - }, - description: { - type: 'string', - nullable: true, - description: 'The user-defined UTF-8 string describing their account.', - example: 'Hi masters, I am Ai!' - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the user account was created on Misskey.' - }, - followersCount: { - type: 'number', - description: 'The number of followers this account currently has.' - }, - followingCount: { - type: 'number', - description: 'The number of users this account is following.' - }, - notesCount: { - type: 'number', - description: 'The number of Notes (including renotes) issued by the user.' - }, - isBot: { - type: 'boolean', - description: 'Whether this account is a bot.' - }, - isCat: { - type: 'boolean', - description: 'Whether this account is a cat.' - }, - isAdmin: { - type: 'boolean', - description: 'Whether this account is the admin.' - }, - isVerified: { - type: 'boolean' - }, - isLocked: { - type: 'boolean' - }, - }, - required: ['id', 'name', 'username', 'createdAt'] - }, - - UserList: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this UserList.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the UserList was created.' - }, - title: { - type: 'string', - description: 'The name of the UserList.' - }, - }, - required: ['id', 'createdAt', 'title'] - }, - - MessagingMessage: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this MessagingMessage.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the MessagingMessage was created.' - }, - text: { - type: 'string', - nullable: true - }, - file: { - type: 'DriveFile', - nullable: true - }, - recipientId: { - type: 'string', - format: 'id', - }, - recipient: { - $ref: '#/components/schemas/User' - }, - }, - required: ['id', 'createdAt'] - }, - - Note: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this Note.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the Note was created on Misskey.' - }, - text: { - type: 'string' - }, - cw: { - type: 'string' - }, - userId: { - type: 'string', - format: 'id', - }, - user: { - $ref: '#/components/schemas/User' - }, - replyId: { - type: 'string', - format: 'id', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - renoteId: { - type: 'string', - format: 'id', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - reply: { - $ref: '#/components/schemas/Note' - }, - renote: { - $ref: '#/components/schemas/Note' - }, - viaMobile: { - type: 'boolean' - }, - visibility: { - type: 'string' - }, - }, - required: ['id', 'userId', 'createdAt'] - }, - - Notification: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this notification.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the notification was created.' - }, - type: { - type: 'string', - enum: ['follow', 'receiveFollowRequest', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote'], - description: 'The type of the notification.' - }, - }, - required: ['id', 'createdAt', 'type'] - }, - - DriveFile: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this Drive file.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the Drive file was created on Misskey.' - }, - name: { - type: 'string', - description: 'The file name with extension.', - example: 'lenna.jpg' - }, - type: { - type: 'string', - description: 'The MIME type of this Drive file.', - example: 'image/jpeg' - }, - md5: { - type: 'string', - format: 'md5', - description: 'The MD5 hash of this Drive file.', - example: '15eca7fba0480996e2245f5185bf39f2' - }, - size: { - type: 'number', - description: 'The size of this Drive file. (bytes)', - example: 51469 - }, - folderId: { - type: 'string', - format: 'id', - nullable: true, - description: 'The parent folder ID of this Drive file.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - isSensitive: { - type: 'boolean', - description: 'Whether this Drive file is sensitive.', - }, - }, - required: ['id', 'createdAt', 'name', 'type', 'size', 'md5'] - }, - - DriveFolder: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this Drive folder.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the Drive folder was created.' - }, - name: { - type: 'string', - description: 'The folder name.', - }, - foldersCount: { - type: 'number', - description: 'The count of child folders.', - }, - filesCount: { - type: 'number', - description: 'The count of child files.', - }, - parentId: { - type: 'string', - format: 'id', - nullable: true, - description: 'The parent folder ID of this folder.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - parent: { - $ref: '#/components/schemas/DriveFolder' - }, - }, - required: ['id', 'createdAt', 'name'] - }, - - Following: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this following.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the following was created.' - }, - followeeId: { - type: 'string', - format: 'id', - }, - followee: { - $ref: '#/components/schemas/User', - description: 'The followee.' - }, - followerId: { - type: 'string', - format: 'id', - }, - follower: { - $ref: '#/components/schemas/User', - description: 'The follower.' - }, - }, - required: ['id', 'createdAt', 'followeeId', 'followerId'] - }, - - Muting: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this mute.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the mute was created.' - }, - mutee: { - $ref: '#/components/schemas/User', - description: 'The mutee.' - }, - }, - required: ['id', 'createdAt', 'mutee'] - }, - - Blocking: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this block.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the block was created.' - }, - blockee: { - $ref: '#/components/schemas/User', - description: 'The blockee.' - }, - }, - required: ['id', 'createdAt', 'blockee'] - }, - - Reaction: { - type: 'object', - properties: { - id: { - type: 'string', - format: 'id', - description: 'The unique identifier for this reaction.', - example: 'xxxxxxxxxxxxxxxxxxxxxxxx', - }, - createdAt: { - type: 'string', - format: 'date-time', - description: 'The date that the reaction was created.' - }, - user: { - $ref: '#/components/schemas/User', - description: 'User who performed this reaction.' - }, - type: { - type: 'string', - enum: [ - 'like', - 'love', - 'laugh', - 'hmm', - 'surprise', - 'congrats', - 'angry', - 'confused', - 'rip', - 'pudding', - 'star' - ], - description: 'The reaction type.' - }, - }, - required: ['id', 'createdAt', 'user', 'type'] - }, + User: convertSchemaToOpenApiSchema(packedUserSchema), + UserList: convertSchemaToOpenApiSchema(packedUserListSchema), + App: convertSchemaToOpenApiSchema(packedAppSchema), + MessagingMessage: convertSchemaToOpenApiSchema(packedMessagingMessageSchema), + Note: convertSchemaToOpenApiSchema(packedNoteSchema), + Notification: convertSchemaToOpenApiSchema(packedNotificationSchema), + DriveFile: convertSchemaToOpenApiSchema(packedDriveFileSchema), + DriveFolder: convertSchemaToOpenApiSchema(packedDriveFolderSchema), + Following: convertSchemaToOpenApiSchema(packedFollowingSchema), + Muting: convertSchemaToOpenApiSchema(packedMutingSchema), + Blocking: convertSchemaToOpenApiSchema(packedBlockingSchema), + NoteReaction: convertSchemaToOpenApiSchema(packedNoteReactionSchema), Hashtag: { type: 'object', diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts index f8dba2eb29..c75f8fb296 100644 --- a/src/server/api/private/signup.ts +++ b/src/server/api/private/signup.ts @@ -3,7 +3,7 @@ import * as bcrypt from 'bcryptjs'; import { generateKeyPair } from 'crypto'; import generateUserToken from '../common/generate-native-user-token'; import config from '../../../config'; -import fetchMeta from '../../../misc/fetch-meta'; +import { fetchMeta } from '../../../misc/fetch-meta'; import * as recaptcha from 'recaptcha-promise'; import { Users, RegistrationTickets } from '../../../models'; import { genId } from '../../../misc/gen-id'; @@ -17,7 +17,7 @@ import { getConnection } from 'typeorm'; export default async (ctx: Koa.BaseContext) => { const body = ctx.request.body as any; - const instance = await fetchMeta(); + const instance = await fetchMeta(true); // Verify recaptcha // ただしテスト時はこの機構は障害となるため無効にする @@ -135,7 +135,7 @@ export default async (ctx: Koa.BaseContext) => { includeSecrets: true }); - res.token = secret; + (res as any).token = secret; ctx.body = res; }; diff --git a/src/server/api/service/discord.ts b/src/server/api/service/discord.ts index d8a979b5b2..589579b8e0 100644 --- a/src/server/api/service/discord.ts +++ b/src/server/api/service/discord.ts @@ -7,7 +7,7 @@ import { publishMainStream } from '../../../services/stream'; import redis from '../../../db/redis'; import * as uuid from 'uuid'; import signin from '../common/signin'; -import fetchMeta from '../../../misc/fetch-meta'; +import { fetchMeta } from '../../../misc/fetch-meta'; import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; import { ensure } from '../../../prelude/ensure'; @@ -68,7 +68,7 @@ router.get('/disconnect/discord', async ctx => { }); async function getOAuth2() { - const meta = await fetchMeta(); + const meta = await fetchMeta(true); if (meta.enableDiscordIntegration) { return new OAuth2( diff --git a/src/server/api/service/github.ts b/src/server/api/service/github.ts index a4d274cc62..1b0ce6936c 100644 --- a/src/server/api/service/github.ts +++ b/src/server/api/service/github.ts @@ -7,7 +7,7 @@ import { publishMainStream } from '../../../services/stream'; import redis from '../../../db/redis'; import * as uuid from 'uuid'; import signin from '../common/signin'; -import fetchMeta from '../../../misc/fetch-meta'; +import { fetchMeta } from '../../../misc/fetch-meta'; import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; import { ensure } from '../../../prelude/ensure'; @@ -65,7 +65,7 @@ router.get('/disconnect/github', async ctx => { }); async function getOath2() { - const meta = await fetchMeta(); + const meta = await fetchMeta(true); if (meta.enableGithubIntegration && meta.githubClientId && meta.githubClientSecret) { return new OAuth2( diff --git a/src/server/api/service/twitter.ts b/src/server/api/service/twitter.ts index 39fdfd8654..36299b1f29 100644 --- a/src/server/api/service/twitter.ts +++ b/src/server/api/service/twitter.ts @@ -6,7 +6,7 @@ import redis from '../../../db/redis'; import { publishMainStream } from '../../../services/stream'; import config from '../../../config'; import signin from '../common/signin'; -import fetchMeta from '../../../misc/fetch-meta'; +import { fetchMeta } from '../../../misc/fetch-meta'; import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; import { ensure } from '../../../prelude/ensure'; @@ -65,7 +65,7 @@ router.get('/disconnect/twitter', async ctx => { }); async function getTwAuth() { - const meta = await fetchMeta(); + const meta = await fetchMeta(true); if (meta.enableTwitterIntegration && meta.twitterConsumerKey && meta.twitterConsumerSecret) { return autwh({ diff --git a/src/server/api/stream/channels/global-timeline.ts b/src/server/api/stream/channels/global-timeline.ts index bfb7697ba7..1271aae3a2 100644 --- a/src/server/api/stream/channels/global-timeline.ts +++ b/src/server/api/stream/channels/global-timeline.ts @@ -1,8 +1,9 @@ import autobind from 'autobind-decorator'; import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; import Channel from '../channel'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; import { Notes } from '../../../../models'; +import { PackedNote } from '../../../../models/repositories/note'; export default class extends Channel { public readonly chName = 'globalTimeline'; @@ -21,7 +22,9 @@ export default class extends Channel { } @autobind - private async onNote(note: any) { + private async onNote(note: PackedNote) { + if (note.visibility !== 'public') return; + // リプライなら再pack if (note.replyId != null) { note.reply = await Notes.pack(note.replyId, this.user, { diff --git a/src/server/api/stream/channels/hashtag.ts b/src/server/api/stream/channels/hashtag.ts index 36c56c7ab6..e55a508328 100644 --- a/src/server/api/stream/channels/hashtag.ts +++ b/src/server/api/stream/channels/hashtag.ts @@ -2,6 +2,7 @@ import autobind from 'autobind-decorator'; import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; import Channel from '../channel'; import { Notes } from '../../../../models'; +import { PackedNote } from '../../../../models/repositories/note'; export default class extends Channel { public readonly chName = 'hashtag'; @@ -20,8 +21,8 @@ export default class extends Channel { } @autobind - private async onNote(note: any) { - const noteTags = note.tags.map((t: string) => t.toLowerCase()); + private async onNote(note: PackedNote) { + const noteTags = note.tags ? note.tags.map((t: string) => t.toLowerCase()) : []; const matched = this.q.some(tags => tags.every(tag => noteTags.includes(tag.toLowerCase()))); if (!matched) return; diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts index 61960657b4..9aa4dc1c0f 100644 --- a/src/server/api/stream/channels/home-timeline.ts +++ b/src/server/api/stream/channels/home-timeline.ts @@ -2,6 +2,7 @@ import autobind from 'autobind-decorator'; import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; import Channel from '../channel'; import { Notes } from '../../../../models'; +import { PackedNote } from '../../../../models/repositories/note'; export default class extends Channel { public readonly chName = 'homeTimeline'; @@ -15,7 +16,7 @@ export default class extends Channel { } @autobind - private async onNote(note: any) { + private async onNote(note: PackedNote) { // その投稿のユーザーをフォローしていなかったら弾く if (this.user!.id !== note.userId && !this.following.includes(note.userId)) return; diff --git a/src/server/api/stream/channels/hybrid-timeline.ts b/src/server/api/stream/channels/hybrid-timeline.ts index a8020bfcfa..b9feb70258 100644 --- a/src/server/api/stream/channels/hybrid-timeline.ts +++ b/src/server/api/stream/channels/hybrid-timeline.ts @@ -1,8 +1,10 @@ import autobind from 'autobind-decorator'; import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; import Channel from '../channel'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; import { Notes } from '../../../../models'; +import { PackedNote } from '../../../../models/repositories/note'; +import { PackedUser } from '../../../../models/repositories/user'; export default class extends Channel { public readonly chName = 'hybridTimeline'; @@ -19,12 +21,12 @@ export default class extends Channel { } @autobind - private async onNote(note: any) { + private async onNote(note: PackedNote) { // 自分自身の投稿 または その投稿のユーザーをフォローしている または 全体公開のローカルの投稿 の場合だけ if (!( this.user!.id === note.userId || this.following.includes(note.userId) || - (note.user.host == null && note.visibility === 'public') + ((note.user as PackedUser).host == null && note.visibility === 'public') )) return; if (['followers', 'specified'].includes(note.visibility)) { diff --git a/src/server/api/stream/channels/local-timeline.ts b/src/server/api/stream/channels/local-timeline.ts index 4aec2d66b4..24cbc0a7bf 100644 --- a/src/server/api/stream/channels/local-timeline.ts +++ b/src/server/api/stream/channels/local-timeline.ts @@ -1,8 +1,10 @@ import autobind from 'autobind-decorator'; import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; import Channel from '../channel'; -import fetchMeta from '../../../../misc/fetch-meta'; +import { fetchMeta } from '../../../../misc/fetch-meta'; import { Notes } from '../../../../models'; +import { PackedNote } from '../../../../models/repositories/note'; +import { PackedUser } from '../../../../models/repositories/user'; export default class extends Channel { public readonly chName = 'localTimeline'; @@ -21,8 +23,8 @@ export default class extends Channel { } @autobind - private async onNote(note: any) { - if (note.user.host !== null) return; + private async onNote(note: PackedNote) { + if ((note.user as PackedUser).host !== null) return; if (note.visibility === 'home') return; if (['followers', 'specified'].includes(note.visibility)) { diff --git a/src/server/api/stream/channels/user-list.ts b/src/server/api/stream/channels/user-list.ts index f5434b8f08..119bacf6ec 100644 --- a/src/server/api/stream/channels/user-list.ts +++ b/src/server/api/stream/channels/user-list.ts @@ -3,6 +3,7 @@ import Channel from '../channel'; import { Notes, UserListJoinings } from '../../../../models'; import shouldMuteThisNote from '../../../../misc/should-mute-this-note'; import { User } from '../../../../models/entities/user'; +import { PackedNote } from '../../../../models/repositories/note'; export default class extends Channel { public readonly chName = 'userList'; @@ -38,7 +39,7 @@ export default class extends Channel { } @autobind - private async onNote(note: any) { + private async onNote(note: PackedNote) { if (!this.listUsers.includes(note.userId)) return; if (['followers', 'specified'].includes(note.visibility)) { diff --git a/src/server/nodeinfo.ts b/src/server/nodeinfo.ts index 686412383e..d3ad90fab5 100644 --- a/src/server/nodeinfo.ts +++ b/src/server/nodeinfo.ts @@ -1,6 +1,6 @@ import * as Router from 'koa-router'; import config from '../config'; -import fetchMeta from '../misc/fetch-meta'; +import { fetchMeta } from '../misc/fetch-meta'; // import User from '../models/user'; import { name as softwareName, version, repository } from '../../package.json'; // import Note from '../models/note'; @@ -44,7 +44,7 @@ const nodeinfo2 = async () => { // localPosts, // localComments ] = await Promise.all([ - fetchMeta(), + fetchMeta(true), // User.count({ host: null }), // User.count({ host: null, updatedAt: { $gt: new Date(Date.now() - 15552000000) } }), // User.count({ host: null, updatedAt: { $gt: new Date(Date.now() - 2592000000) } }), diff --git a/src/server/web/index.ts b/src/server/web/index.ts index 5cadf1b124..1f87cd70f8 100644 --- a/src/server/web/index.ts +++ b/src/server/web/index.ts @@ -12,7 +12,7 @@ import * as views from 'koa-views'; import docs from './docs'; import packFeed from './feed'; -import fetchMeta from '../../misc/fetch-meta'; +import { fetchMeta } from '../../misc/fetch-meta'; import * as pkg from '../../../package.json'; import { genOpenapiSpec } from '../api/openapi/gen-spec'; import config from '../../config'; @@ -206,7 +206,7 @@ router.get('/notes/:note', async ctx => { //#endregion router.get('/info', async ctx => { - const meta = await fetchMeta(); + const meta = await fetchMeta(true); const emojis = await Emojis.find({ where: { host: null } }); diff --git a/src/server/web/manifest.ts b/src/server/web/manifest.ts index 4acfb22de5..730082da28 100644 --- a/src/server/web/manifest.ts +++ b/src/server/web/manifest.ts @@ -1,11 +1,11 @@ import * as Koa from 'koa'; import * as manifest from '../../client/assets/manifest.json'; -import fetchMeta from '../../misc/fetch-meta'; +import { fetchMeta } from '../../misc/fetch-meta'; module.exports = async (ctx: Koa.BaseContext) => { const json = JSON.parse(JSON.stringify(manifest)); - const instance = await fetchMeta(); + const instance = await fetchMeta(true); json.short_name = instance.name || 'Misskey'; json.name = instance.name || 'Misskey'; diff --git a/src/server/web/url-preview.ts b/src/server/web/url-preview.ts index 7d0080b4d2..cdb6f13f59 100644 --- a/src/server/web/url-preview.ts +++ b/src/server/web/url-preview.ts @@ -1,7 +1,7 @@ import * as Koa from 'koa'; import * as request from 'request-promise-native'; import summaly from 'summaly'; -import fetchMeta from '../../misc/fetch-meta'; +import { fetchMeta } from '../../misc/fetch-meta'; import Logger from '../../services/logger'; import config from '../../config'; import { query } from '../../prelude/url'; diff --git a/src/services/chart/charts/schemas/active-users.ts b/src/services/chart/charts/schemas/active-users.ts index da8c63389c..f10393bc36 100644 --- a/src/services/chart/charts/schemas/active-users.ts +++ b/src/services/chart/charts/schemas/active-users.ts @@ -1,9 +1,12 @@ +import { types, bool } from '../../../../misc/schema'; + export const logSchema = { /** * アクティブユーザー数 */ count: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'アクティブユーザー数', }, }; @@ -12,14 +15,17 @@ export const logSchema = { * アクティブユーザーに関するチャート */ export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { local: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, remote: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, } diff --git a/src/services/chart/charts/schemas/drive.ts b/src/services/chart/charts/schemas/drive.ts index 47530e8417..3327950635 100644 --- a/src/services/chart/charts/schemas/drive.ts +++ b/src/services/chart/charts/schemas/drive.ts @@ -1,9 +1,12 @@ +import { types, bool } from '../../../../misc/schema'; + const logSchema = { /** * 集計期間時点での、全ドライブファイル数 */ totalCount: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全ドライブファイル数' }, @@ -11,7 +14,8 @@ const logSchema = { * 集計期間時点での、全ドライブファイルの合計サイズ */ totalSize: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全ドライブファイルの合計サイズ' }, @@ -19,7 +23,8 @@ const logSchema = { * 増加したドライブファイル数 */ incCount: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加したドライブファイル数' }, @@ -27,7 +32,8 @@ const logSchema = { * 増加したドライブ使用量 */ incSize: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加したドライブ使用量' }, @@ -35,7 +41,8 @@ const logSchema = { * 減少したドライブファイル数 */ decCount: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少したドライブファイル数' }, @@ -43,20 +50,24 @@ const logSchema = { * 減少したドライブ使用量 */ decSize: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少したドライブ使用量' }, }; export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { local: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, remote: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, } diff --git a/src/services/chart/charts/schemas/federation.ts b/src/services/chart/charts/schemas/federation.ts index d1d275fc95..41ae977cbd 100644 --- a/src/services/chart/charts/schemas/federation.ts +++ b/src/services/chart/charts/schemas/federation.ts @@ -1,22 +1,29 @@ +import { types, bool } from '../../../../misc/schema'; + /** * フェデレーションに関するチャート */ export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { instance: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'インスタンス数の合計' }, inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加インスタンス数' }, dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少インスタンス数' }, } diff --git a/src/services/chart/charts/schemas/hashtag.ts b/src/services/chart/charts/schemas/hashtag.ts index c1904b6701..918242f66e 100644 --- a/src/services/chart/charts/schemas/hashtag.ts +++ b/src/services/chart/charts/schemas/hashtag.ts @@ -1,9 +1,12 @@ +import { types, bool } from '../../../../misc/schema'; + export const logSchema = { /** * 投稿された数 */ count: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '投稿された数', }, }; @@ -12,14 +15,17 @@ export const logSchema = { * ハッシュタグに関するチャート */ export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { local: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, remote: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, } diff --git a/src/services/chart/charts/schemas/instance.ts b/src/services/chart/charts/schemas/instance.ts index 001f2428b5..d97f49ea72 100644 --- a/src/services/chart/charts/schemas/instance.ts +++ b/src/services/chart/charts/schemas/instance.ts @@ -1,58 +1,73 @@ +import { types, bool } from '../../../../misc/schema'; + /** * インスタンスごとのチャート */ export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { requests: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { failed: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '失敗したリクエスト数' }, succeeded: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '成功したリクエスト数' }, received: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '受信したリクエスト数' }, } }, notes: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全投稿数' }, inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加した投稿数' }, dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少した投稿数' }, diffs: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { normal: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '通常の投稿数の差分' }, reply: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'リプライの投稿数の差分' }, renote: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'Renoteの投稿数の差分' }, } @@ -61,84 +76,103 @@ export const schema = { }, users: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全ユーザー数' }, inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加したユーザー数' }, dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少したユーザー数' }, } }, following: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全フォロー数' }, inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加したフォロー数' }, dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少したフォロー数' }, } }, followers: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全フォロワー数' }, inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加したフォロワー数' }, dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少したフォロワー数' }, } }, drive: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { totalFiles: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全ドライブファイル数' }, totalUsage: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全ドライブファイルの合計サイズ' }, incFiles: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加したドライブファイル数' }, incUsage: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加したドライブ使用量' }, decFiles: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少したドライブファイル数' }, decUsage: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少したドライブ使用量' }, } diff --git a/src/services/chart/charts/schemas/network.ts b/src/services/chart/charts/schemas/network.ts index 4ef530c07c..819e2a145e 100644 --- a/src/services/chart/charts/schemas/network.ts +++ b/src/services/chart/charts/schemas/network.ts @@ -1,27 +1,35 @@ +import { types, bool } from '../../../../misc/schema'; + /** * ネットワークに関するチャート */ export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { incomingRequests: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '受信したリクエスト数' }, outgoingRequests: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '送信したリクエスト数' }, totalTime: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '応答時間の合計' // TIP: (totalTime / incomingRequests) でひとつのリクエストに平均でどれくらいの時間がかかったか知れる }, incomingBytes: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '合計受信データ量' }, outgoingBytes: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '合計送信データ量' }, } diff --git a/src/services/chart/charts/schemas/notes.ts b/src/services/chart/charts/schemas/notes.ts index 133d1e3730..aabe49edd9 100644 --- a/src/services/chart/charts/schemas/notes.ts +++ b/src/services/chart/charts/schemas/notes.ts @@ -1,34 +1,43 @@ +import { types, bool } from '../../../../misc/schema'; + const logSchema = { total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全投稿数' }, inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加した投稿数' }, dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少した投稿数' }, diffs: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { normal: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '通常の投稿数の差分' }, reply: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'リプライの投稿数の差分' }, renote: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'Renoteの投稿数の差分' }, } @@ -36,14 +45,17 @@ const logSchema = { }; export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { local: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, remote: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, } diff --git a/src/services/chart/charts/schemas/per-user-drive.ts b/src/services/chart/charts/schemas/per-user-drive.ts index 713bd7ed84..62e7795167 100644 --- a/src/services/chart/charts/schemas/per-user-drive.ts +++ b/src/services/chart/charts/schemas/per-user-drive.ts @@ -1,11 +1,15 @@ +import { types, bool } from '../../../../misc/schema'; + export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { /** * 集計期間時点での、全ドライブファイル数 */ totalCount: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全ドライブファイル数' }, @@ -13,7 +17,8 @@ export const schema = { * 集計期間時点での、全ドライブファイルの合計サイズ */ totalSize: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全ドライブファイルの合計サイズ' }, @@ -21,7 +26,8 @@ export const schema = { * 増加したドライブファイル数 */ incCount: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加したドライブファイル数' }, @@ -29,7 +35,8 @@ export const schema = { * 増加したドライブ使用量 */ incSize: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加したドライブ使用量' }, @@ -37,7 +44,8 @@ export const schema = { * 減少したドライブファイル数 */ decCount: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少したドライブファイル数' }, @@ -45,7 +53,8 @@ export const schema = { * 減少したドライブ使用量 */ decSize: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少したドライブ使用量' }, } diff --git a/src/services/chart/charts/schemas/per-user-following.ts b/src/services/chart/charts/schemas/per-user-following.ts index d6ca1130e0..e57869e79a 100644 --- a/src/services/chart/charts/schemas/per-user-following.ts +++ b/src/services/chart/charts/schemas/per-user-following.ts @@ -1,15 +1,19 @@ +import { types, bool } from '../../../../misc/schema'; + export const logSchema = { /** * フォローしている */ followings: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { /** * フォローしている合計 */ total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'フォローしている合計', }, @@ -17,7 +21,8 @@ export const logSchema = { * フォローした数 */ inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'フォローした数', }, @@ -25,7 +30,8 @@ export const logSchema = { * フォロー解除した数 */ dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'フォロー解除した数', }, } @@ -35,13 +41,15 @@ export const logSchema = { * フォローされている */ followers: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { /** * フォローされている合計 */ total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'フォローされている合計', }, @@ -49,7 +57,8 @@ export const logSchema = { * フォローされた数 */ inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'フォローされた数', }, @@ -57,7 +66,8 @@ export const logSchema = { * フォロー解除された数 */ dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'フォロー解除された数', }, } @@ -65,14 +75,17 @@ export const logSchema = { }; export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { local: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, remote: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, } diff --git a/src/services/chart/charts/schemas/per-user-notes.ts b/src/services/chart/charts/schemas/per-user-notes.ts index 3c448c4cee..7632450073 100644 --- a/src/services/chart/charts/schemas/per-user-notes.ts +++ b/src/services/chart/charts/schemas/per-user-notes.ts @@ -1,36 +1,46 @@ +import { types, bool } from '../../../../misc/schema'; + export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全投稿数' }, inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加した投稿数' }, dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少した投稿数' }, diffs: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { normal: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '通常の投稿数の差分' }, reply: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'リプライの投稿数の差分' }, renote: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'Renoteの投稿数の差分' }, } diff --git a/src/services/chart/charts/schemas/per-user-reactions.ts b/src/services/chart/charts/schemas/per-user-reactions.ts index 1278184da6..df7d5a6dbb 100644 --- a/src/services/chart/charts/schemas/per-user-reactions.ts +++ b/src/services/chart/charts/schemas/per-user-reactions.ts @@ -1,9 +1,12 @@ +import { types, bool } from '../../../../misc/schema'; + export const logSchema = { /** * フォローしている合計 */ count: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: 'リアクションされた数', }, }; @@ -12,14 +15,17 @@ export const logSchema = { * ユーザーごとのリアクションに関するチャート */ export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { local: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, remote: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, } diff --git a/src/services/chart/charts/schemas/test-grouped.ts b/src/services/chart/charts/schemas/test-grouped.ts index acf3fddb31..893458e90a 100644 --- a/src/services/chart/charts/schemas/test-grouped.ts +++ b/src/services/chart/charts/schemas/test-grouped.ts @@ -1,21 +1,28 @@ +import { types, bool } from '../../../../misc/schema'; + export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { foo: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '' }, inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '' }, dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '' }, } diff --git a/src/services/chart/charts/schemas/test-unique.ts b/src/services/chart/charts/schemas/test-unique.ts index 8fcfbf3c72..2b3f445ff0 100644 --- a/src/services/chart/charts/schemas/test-unique.ts +++ b/src/services/chart/charts/schemas/test-unique.ts @@ -1,8 +1,12 @@ +import { types, bool } from '../../../../misc/schema'; + export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { foo: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '' }, } diff --git a/src/services/chart/charts/schemas/test.ts b/src/services/chart/charts/schemas/test.ts index b1344500bf..9bdc05990b 100644 --- a/src/services/chart/charts/schemas/test.ts +++ b/src/services/chart/charts/schemas/test.ts @@ -1,21 +1,28 @@ +import { types, bool } from '../../../../misc/schema'; + export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { foo: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '' }, inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '' }, dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '' }, } diff --git a/src/services/chart/charts/schemas/users.ts b/src/services/chart/charts/schemas/users.ts index db7e2dd057..6ae6133350 100644 --- a/src/services/chart/charts/schemas/users.ts +++ b/src/services/chart/charts/schemas/users.ts @@ -1,9 +1,12 @@ +import { types, bool } from '../../../../misc/schema'; + const logSchema = { /** * 集計期間時点での、全ユーザー数 */ total: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '集計期間時点での、全ユーザー数' }, @@ -11,7 +14,8 @@ const logSchema = { * 増加したユーザー数 */ inc: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '増加したユーザー数' }, @@ -19,20 +23,24 @@ const logSchema = { * 減少したユーザー数 */ dec: { - type: 'number' as 'number', + type: types.number, + optional: bool.false, nullable: bool.false, description: '減少したユーザー数' }, }; export const schema = { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: { local: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, remote: { - type: 'object' as 'object', + type: types.object, + optional: bool.false, nullable: bool.false, properties: logSchema }, } diff --git a/src/services/chart/core.ts b/src/services/chart/core.ts index fe762f2b24..6a69a21b7e 100644 --- a/src/services/chart/core.ts +++ b/src/services/chart/core.ts @@ -8,7 +8,7 @@ import * as moment from 'moment'; import * as nestedProperty from 'nested-property'; import autobind from 'autobind-decorator'; import Logger from '../logger'; -import { Schema } from '../../misc/schema'; +import { Schema, bool, types } from '../../misc/schema'; import { EntitySchema, getRepository, Repository, LessThan, MoreThanOrEqual } from 'typeorm'; import { isDuplicateKeyValueError } from '../../misc/is-duplicate-key-value-error'; @@ -449,7 +449,8 @@ export function convertLog(logSchema: Schema): Schema { if (v.type === 'number') { v.type = 'array'; v.items = { - type: 'number' + type: types.number, + optional: bool.false, nullable: bool.false, }; } else if (v.type === 'object') { for (const k of Object.keys(v.properties!)) { diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index 2f164a4d92..e569180354 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -9,7 +9,7 @@ import * as sharp from 'sharp'; import { publishMainStream, publishDriveStream } from '../stream'; import delFile from './delete-file'; import config from '../../config'; -import fetchMeta from '../../misc/fetch-meta'; +import { fetchMeta } from '../../misc/fetch-meta'; import { GenerateVideoThumbnail } from './generate-video-thumbnail'; import { driveLogger } from './logger'; import { IImage, ConvertToJpeg, ConvertToWebp, ConvertToPng } from './image-processor'; diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 02e33d6789..ce229d6393 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -237,7 +237,7 @@ export default async (user: User, data: Option, silent = false) => new Promise { text: 'foo' }); })); + + it('ホーム投稿は流れない', () => new Promise(async done => { + const alice = await signup({ username: 'alice' }); + const bob = await signup({ username: 'bob' }); + + let fired = false; + + const ws = await connectStream(alice, 'globalTimeline', ({ type, body }) => { + if (type == 'note') { + fired = true; + } + }); + + // ホーム投稿 + post(bob, { + text: 'foo', + visibility: 'home' + }); + + setTimeout(() => { + assert.strictEqual(fired, false); + ws.close(); + done(); + }, 3000); + })); }); describe('UserList Timeline', () => { @@ -603,7 +628,7 @@ describe('Streaming', () => { // リスト作成 const list = await request('/users/lists/create', { - title: 'my list' + name: 'my list' }, alice).then(x => x.body); // Alice が Bob をリスイン @@ -633,7 +658,7 @@ describe('Streaming', () => { // リスト作成 const list = await request('/users/lists/create', { - title: 'my list' + name: 'my list' }, alice).then(x => x.body); let fired = false; @@ -664,7 +689,7 @@ describe('Streaming', () => { // リスト作成 const list = await request('/users/lists/create', { - title: 'my list' + name: 'my list' }, alice).then(x => x.body); // Alice が Bob をリスイン @@ -699,7 +724,7 @@ describe('Streaming', () => { // リスト作成 const list = await request('/users/lists/create', { - title: 'my list' + name: 'my list' }, alice).then(x => x.body); // Alice が Bob をリスイン