diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2a70b076b..5dd72cc46 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 17fbe781c..96fb4c209 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 172de33e7..f69c503a9 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 88c55958d..f4a02be68 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 815686cfb..8908cf156 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 61ace88c1..75532da2f 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 4d360c726..503f09c1f 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 2e64e47d9..c1b2d6778 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 04f3ed9f7..cd02c6957 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 8d2e04d04..86024c4da 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 786a6766d..699251b31 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 f8897da25..7049945d5 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 1e6abac82..3f9681a04 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 5a5cd9c8e..d7af82cd8 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 e14e1beff..4b343491b 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 b26e2380a..1177cab4a 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 dd71a918d..48a6650b4 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 18ff82b9e..b16fcdff4 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 5b7441e1c..27bf742f3 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 800f0b9e6..db82adb2a 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 17b021e91..075873091 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 008991454..ced90ce78 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 7c17953d9..442c95ad9 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 1ce5d841f..a958e8957 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 f619d6e37..c708b265a 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 fa5ebf2ac..6562a470b 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 32f796823..a6a4d46de 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 e18aa591f..fd209bce1 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 f117b38b2..245db4b79 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 ce88adefa..ef920c432 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 3fff57866..aba6527fa 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 4d99c6ef7..86c920445 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 665927353..33f95bbd5 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 1812e2e71..1e8135a5c 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 28191d4ab..85e5b6b0a 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 1d2ce7a9c..1dbfabe88 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 4781d4c06..cf77b35a0 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 e591794b8..54231c7f8 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 afba15eb5..6b212203f 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 000000000..24795f3ae --- /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 e65785950..21a51c962 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 f9822c518..684304147 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 f8b35ea21..84a1040b2 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 884234234..850b5e65e 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 897dd9aca..da2113fae 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 990cbf2a8..1e2600add 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 3c6380acb..1f5dd5364 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 1b992eeaa..1bb15117d 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 9db60d266..833d5060c 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 ce9baed2a..e7d3e8438 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 bca8d3348..9bf27c8e7 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 7126ac52c..b7a58c475 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 97f353579..5ff1dc0c4 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 adf780301..4d4516bd8 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 4e4db6c78..493e14464 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 7214463dd..f770bc713 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 3a87a9497..ab19566f1 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 340a39a41..0f81a1da9 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 000000000..d56e63bc5 --- /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 265850f84..82b9a97b6 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 e8c0e683c..8e74361f9 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 08ae2ff70..dc3174cd2 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 f0989ec5a..0368d026c 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 60507e7d7..a020b46aa 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 96d9f8242..db17979a4 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 301338ed9..3c4e0037d 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 2998bc1a1..89cc92642 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 802f17799..0d2704d01 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 e01e9d698..84b750f2c 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 fa58f2f2c..b842f9de6 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 afad38c46..4ecd507e1 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 56074c9d0..41513e5da 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 e02f53a64..d4b9721d8 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 c2d746e48..27e38bbde 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 add21e5f1..0d5295bff 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 30ac0849a..388852b9c 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 785f21f22..793eb5a20 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 0fd8a4860..f9ea380c7 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 17ba96935..7aa19c9a2 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 2b4ae2a31..e8861eafa 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 6b26e31c0..acd3ac75e 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 994dfb4dc..6cd84b866 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 fa9ae39e3..64750815b 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 ceffb1cf4..3631208da 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 effee2b13..c05c8dedd 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 c10c0d748..ca84fc6ef 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 b7f614915..02e44492b 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 bcb0b6d1e..0773b4faa 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 74a8ea918..00dfac377 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 980ff2446..5fb0fd989 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 cba3724b6..0b49f896a 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 4d5ac6fbe..daf992b63 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 d41dc20c5..54b420813 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 c27f3df1b..5e692db38 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 05f171af8..c16018d43 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 f3ebaa16a..f85109b4b 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 559937ca2..a4838b456 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 18af0a268..c710706f0 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 0cb68353c..465b71e2e 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 2e273dc0c..2a7748ac6 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 a1d140c6c..420936c08 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 21dc6d331..79efffbf9 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 b05fc4552..684086b5c 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 1a997ec7c..395f9352d 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 4691a9939..fdc50e4da 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 9e16e34e3..67b646a35 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 96da221d9..2809465fd 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 a401166c5..820c44e81 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 0a4c8699d..9006dfbfa 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 d194c6c8a..de26b970e 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 65826d932..e54f989e7 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 f8dba2eb2..c75f8fb29 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 d8a979b5b..589579b8e 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 a4d274cc6..1b0ce6936 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 39fdfd865..36299b1f2 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 bfb7697ba..1271aae3a 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 36c56c7ab..e55a50832 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 61960657b..9aa4dc1c0 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 a8020bfcf..b9feb7025 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 4aec2d66b..24cbc0a7b 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 f5434b8f0..119bacf6e 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 686412383..d3ad90fab 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 5cadf1b12..1f87cd70f 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 4acfb22de..730082da2 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 7d0080b4d..cdb6f13f5 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 da8c63389..f10393bc3 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 47530e841..332795063 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 d1d275fc9..41ae977cb 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 c1904b670..918242f66 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 001f2428b..d97f49ea7 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 4ef530c07..819e2a145 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 133d1e373..aabe49edd 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 713bd7ed8..62e779516 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 d6ca1130e..e57869e79 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 3c448c4ce..763245007 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 1278184da..df7d5a6db 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 acf3fddb3..893458e90 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 8fcfbf3c7..2b3f445ff 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 b1344500b..9bdc05990 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 db7e2dd05..6ae613335 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 fe762f2b2..6a69a21b7 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 2f164a4d9..e56918035 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 02e33d678..ce229d639 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 をリスイン