Merge branch 'develop'

This commit is contained in:
syuilo 2019-04-24 15:23:48 +09:00
commit dfebb967b0
146 changed files with 1813 additions and 899 deletions

4
.github/CODEOWNERS vendored
View File

@ -1,7 +1,7 @@
# PATH OWNERS # PATH OWNERS
/.autogen/ @acid-chicken /.autogen/ @acid-chicken
/.circleci/ @syuilo @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 # /.config/mongo_initdb_example.js @khws4v1
/.github/ @syuilo @AyaMorisawa @acid-chicken /.github/ @syuilo @AyaMorisawa @acid-chicken
/.vscode/ @acid-chicken /.vscode/ @acid-chicken
@ -12,7 +12,7 @@
# /docs/*.fr.md @BoFFire # /docs/*.fr.md @BoFFire
# /docs/docker.*.md @khws4v1 # /docs/docker.*.md @khws4v1
/locales/ @syuilo /locales/ @syuilo
/src/ @syuilo @AyaMorisawa @mei23 @acid-chicken /src/ @syuilo @AyaMorisawa @mei23 @acid-chicken @rinsuki
# /src/crypto_key.cc @akihikodaki # /src/crypto_key.cc @akihikodaki
# /src/crypto_key.d.ts @akihikodaki # /src/crypto_key.d.ts @akihikodaki
/.dockerignore @syuilo # @khws4v1 /.dockerignore @syuilo # @khws4v1

View File

@ -35,6 +35,17 @@ mongodb:
8. master ブランチに戻す 8. master ブランチに戻す
9. enjoy 9. enjoy
11.3.0 (2019/04/24)
-------------------
### Improvements
* お知らせにMFMを使えるように
* お知らせに画像を添付できるように
### Fixes
* 投稿のタグ検索APIで大文字小文字が区別されていたのを修正
* 公開範囲がホームの投稿がグローバルTLに流れる問題を修正
* モバイルビューの投稿詳細にて acct が長いとアイコンが圧迫面接される問題を修正
11.2.2 (2019/04/22) 11.2.2 (2019/04/22)
------------------- -------------------
### Fixes ### Fixes

View File

@ -88,12 +88,14 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://avatars0.githubusercontent.com/u/10798641?s=460&v=4" alt="AyaMorisawa" width="100"></td> <td><img src="https://avatars0.githubusercontent.com/u/10798641?s=460&v=4" alt="AyaMorisawa" width="100"></td>
<td><img src="https://avatars1.githubusercontent.com/u/30769358?s=460&v=4" alt="mei23" width="100"></td> <td><img src="https://avatars1.githubusercontent.com/u/30769358?s=460&v=4" alt="mei23" width="100"></td>
<td><img src="https://avatars2.githubusercontent.com/u/20679825?s=460&v=4" alt="acid-chicken" width="100"></td> <td><img src="https://avatars2.githubusercontent.com/u/20679825?s=460&v=4" alt="acid-chicken" width="100"></td>
<td><img src="https://avatars2.githubusercontent.com/u/6533808?s=460&v=4" alt="rinsuki" width="100"></td>
</tr> </tr>
<tr> <tr>
<td align="center"><a href="https://github.com/syuilo">@syuilo</a></td> <td align="center"><a href="https://github.com/syuilo">@syuilo</a></td>
<td align="center"><a href="https://github.com/AyaMorisawa">@AyaMorisawa</a></td> <td align="center"><a href="https://github.com/AyaMorisawa">@AyaMorisawa</a></td>
<td align="center"><a href="https://github.com/mei23">@mei23</a></td> <td align="center"><a href="https://github.com/mei23">@mei23</a></td>
<td align="center"><a href="https://github.com/acid-chicken">@acid-chicken</a></td> <td align="center"><a href="https://github.com/acid-chicken">@acid-chicken</a></td>
<td align="center"><a href="https://github.com/rinsuki">@rinsuki</a></td>
</tr> </tr>
</table> </table>
@ -102,14 +104,14 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<!-- PATREON_START --> <!-- PATREON_START -->
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weep" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td> <td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td> <td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
<td><a href="https://www.patreon.com/weepjp">weep</a></td> <td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td> <td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td> <td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
<td><a href="https://www.patreon.com/osapon">osapon</a></td> <td><a href="https://www.patreon.com/osapon">osapon</a></td>
@ -124,7 +126,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin" width="100"></td> <td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td> <td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/1.jpeg?token-time=2145916800&token-hash=3aMtpAjhwf01G3Uf8iIKYL8FUXXgxV7NvoQLne7lAKE%3D" alt="Ryosuke Yamamoto" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td> <td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
@ -135,7 +137,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin</a></td> <td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin</a></td>
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td> <td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
<td><a href="https://www.patreon.com/user?u=17463605">Sampot</a></td> <td><a href="https://www.patreon.com/user?u=17463605">Sampot</a></td>
<td><a href="https://www.patreon.com/user?u=19356899">Ryosuke Yamamoto</a></td> <td><a href="https://www.patreon.com/user?u=19356899">sheeta.s</a></td>
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td> <td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
</tr></table> </tr></table>
<table><tr> <table><tr>
@ -165,7 +167,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td> <td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table> </tr></table>
**Last updated:** Thu, 18 Apr 2019 23:38:06 UTC **Last updated:** Wed, 24 Apr 2019 05:56:07 UTC
<!-- PATREON_END --> <!-- PATREON_END -->
:four_leaf_clover: Copyright :four_leaf_clover: Copyright

View File

@ -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." 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: "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 »." 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." 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: "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." 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" signin: "Se connecter"
signup: "S'enregistrer" signup: "S'enregistrer"
signout: "Se déconnecter" 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: "Jai compris !" got-it: "Jai compris !"
customization-tips: customization-tips:
title: "Conseils de personnalisation" title: "Conseils de personnalisation"
@ -73,6 +74,8 @@ common:
"read:drive": "Parcourir le Drive" "read:drive": "Parcourir le Drive"
"write:drive": "Écrire sur le Drive" "write:drive": "Écrire sur le Drive"
"read:favorites": "Afficher les favoris" "read:favorites": "Afficher les favoris"
"write:notes": "Créer ou supprimer des publications"
"read:notifications": "Afficher les notifications"
"read:reactions": "Lire les réactions" "read:reactions": "Lire les réactions"
"write:votes": "Vote" "write:votes": "Vote"
empty-timeline-info: empty-timeline-info:
@ -209,16 +212,16 @@ common:
debug-mode: "Activer le mode débogage" debug-mode: "Activer le mode débogage"
debug-mode-desc: "Ce paramètre est stocké dans le navigateur." debug-mode-desc: "Ce paramètre est stocké dans le navigateur."
navbar-position: "Position de la barre de navigation" navbar-position: "Position de la barre de navigation"
navbar-position-top: "en haut" navbar-position-top: "En haut"
navbar-position-left: "À gauche" navbar-position-left: "À gauche"
navbar-position-right: "à droite" navbar-position-right: "À droite"
i-am-under-limited-internet: "J'ai un accès Internet limité" i-am-under-limited-internet: "J'ai un accès Internet limité"
post-style: "Style d'affichage des notes" post-style: "Style d'affichage des notes"
post-style-standard: "Standard" post-style-standard: "Standard"
post-style-smart: "Intelligent" post-style-smart: "Intelligent"
notification-position: "Afficher les notifications" notification-position: "Afficher les notifications"
notification-position-bottom: "en bas" notification-position-bottom: "en bas"
notification-position-top: "en haut" notification-position-top: "En haut"
disable-via-mobile: "Enlever la mention publié via 'mobile'" disable-via-mobile: "Enlever la mention publié via 'mobile'"
load-raw-images: "Afficher les photos jointes dans leur qualité originale" load-raw-images: "Afficher les photos jointes dans leur qualité originale"
load-remote-media: "Afficher les médias depuis le serveur distant" load-remote-media: "Afficher les médias depuis le serveur distant"
@ -307,6 +310,7 @@ common/views/pages/explore.vue:
explore: "Explorer {host}" explore: "Explorer {host}"
users-info: "Actuellement, {users} utilisateurs se sont inscrit ici" users-info: "Actuellement, {users} utilisateurs se sont inscrit ici"
common/views/components/url-preview.vue: common/views/components/url-preview.vue:
enable-player: "Activer la lecture"
disable-player: "Fermer le lecteur" disable-player: "Fermer le lecteur"
common/views/components/user-list.vue: common/views/components/user-list.vue:
no-users: "Il n'y a aucun utilisateur" 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" blocking-list: "Liste des comptes bloqués"
user-lists: "Listes" 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." 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" enter-password: "Veuillez saisir votre mot de passe"
danger-zone: "Zone de danger" danger-zone: "Zone de danger"
delete-account: "Supprimer le compte" 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 » ?" delete-are-you-sure: "Voulez-vous vraiment supprimer la liste « $1 » ?"
deleted: "Supprimé" deleted: "Supprimé"
common/views/components/user-lists.vue: common/views/components/user-lists.vue:
create-list: "Créer une liste"
list-name: "Nom de la liste" list-name: "Nom de la liste"
common/views/widgets/broadcast.vue: common/views/widgets/broadcast.vue:
fetching: "Récupération" fetching: "Récupération"

View File

@ -34,6 +34,7 @@ common:
signup: "注册" signup: "注册"
signout: "退出" signout: "退出"
reload-to-apply-the-setting: "必须重新加载页面以应用此设置。 确实要立即重新加载吗?" reload-to-apply-the-setting: "必须重新加载页面以应用此设置。 确实要立即重新加载吗?"
fetching-as-ap-object: "联合查询"
got-it: "知道了" got-it: "知道了"
customization-tips: customization-tips:
title: "自定义提示" title: "自定义提示"

View File

@ -1,7 +1,7 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "11.2.2", "version": "11.3.0",
"codename": "daybreak", "codename": "daybreak",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,6 @@
"@fortawesome/free-solid-svg-icons": "5.7.2", "@fortawesome/free-solid-svg-icons": "5.7.2",
"@fortawesome/vue-fontawesome": "0.1.5", "@fortawesome/vue-fontawesome": "0.1.5",
"@koa/cors": "2.2.3", "@koa/cors": "2.2.3",
"@prezzemolo/rap": "0.1.2",
"@prezzemolo/zip": "0.0.3", "@prezzemolo/zip": "0.0.3",
"@types/bcryptjs": "2.4.2", "@types/bcryptjs": "2.4.2",
"@types/bull": "3.5.11", "@types/bull": "3.5.11",

View File

@ -27,7 +27,7 @@ function greet() {
console.log(' ' + chalk.gray(v) + (' |___|\n'.substr(v.length))); console.log(' ' + chalk.gray(v) + (' |___|\n'.substr(v.length)));
//#endregion //#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(chalk.keyword('orange')(' If you like Misskey, please donate to support development. https://www.patreon.com/syuilo'));
console.log(''); console.log('');

View File

@ -9,6 +9,9 @@
<ui-textarea v-model="announcement.text"> <ui-textarea v-model="announcement.text">
<span>{{ $t('text') }}</span> <span>{{ $t('text') }}</span>
</ui-textarea> </ui-textarea>
<ui-input v-model="announcement.image">
<span>{{ $t('image-url') }}</span>
</ui-input>
<ui-horizon-group class="fit-bottom"> <ui-horizon-group class="fit-bottom">
<ui-button @click="save()"><fa :icon="['far', 'save']"/> {{ $t('save') }}</ui-button> <ui-button @click="save()"><fa :icon="['far', 'save']"/> {{ $t('save') }}</ui-button>
<ui-button @click="remove(i)"><fa :icon="['far', 'trash-alt']"/> {{ $t('remove') }}</ui-button> <ui-button @click="remove(i)"><fa :icon="['far', 'trash-alt']"/> {{ $t('remove') }}</ui-button>
@ -43,7 +46,8 @@ export default Vue.extend({
add() { add() {
this.announcements.unshift({ this.announcements.unshift({
title: '', title: '',
text: '' text: '',
image: null
}); });
}, },

View File

@ -78,6 +78,7 @@ export default Vue.extend({
.mk-avatar .mk-avatar
display inline-block display inline-block
vertical-align bottom vertical-align bottom
flex-shrink 0
&:not(.cat) &:not(.cat)
overflow hidden overflow hidden

View File

@ -77,11 +77,11 @@ export default Vue.extend({
input: { input: {
default: this.list.name default: this.list.name
} }
}).then(({ canceled, result: title }) => { }).then(({ canceled, result: name }) => {
if (canceled) return; if (canceled) return;
this.$root.api('users/lists/update', { this.$root.api('users/lists/update', {
listId: this.list.id, listId: this.list.id,
title: title name: name
}); });
}); });
}, },

View File

@ -28,10 +28,10 @@ export default Vue.extend({
this.$root.dialog({ this.$root.dialog({
title: this.$t('list-name'), title: this.$t('list-name'),
input: true input: true
}).then(async ({ canceled, result: title }) => { }).then(async ({ canceled, result: name }) => {
if (canceled) return; if (canceled) return;
const list = await this.$root.api('users/lists/create', { const list = await this.$root.api('users/lists/create', {
title name
}); });
this.lists.push(list) this.lists.push(list)

View File

@ -18,7 +18,8 @@
<p class="fetching" v-if="fetching">{{ $t('fetching') }}<mk-ellipsis/></p> <p class="fetching" v-if="fetching">{{ $t('fetching') }}<mk-ellipsis/></p>
<h1 v-if="!fetching">{{ announcements.length == 0 ? $t('no-broadcasts') : announcements[i].title }}</h1> <h1 v-if="!fetching">{{ announcements.length == 0 ? $t('no-broadcasts') : announcements[i].title }}</h1>
<p v-if="!fetching"> <p v-if="!fetching">
<span v-if="announcements.length != 0" v-html="announcements[i].text"></span> <mfm v-if="announcements.length != 0" :text="announcements[i].text"/>
<img v-if="announcements.length != 0 && announcements[i].image" :src="announcements[i].image" alt="" style="display: block; max-height: 130px; max-width: 100%;"/>
<template v-if="announcements.length == 0">{{ $t('have-a-nice-day') }}</template> <template v-if="announcements.length == 0">{{ $t('have-a-nice-day') }}</template>
</p> </p>
<a v-if="announcements.length > 1" @click="next">{{ $t('next') }} &gt;&gt;</a> <a v-if="announcements.length > 1" @click="next">{{ $t('next') }} &gt;&gt;</a>

View File

@ -123,10 +123,10 @@ export default Vue.extend({
this.$root.dialog({ this.$root.dialog({
title: this.$t('list-name'), title: this.$t('list-name'),
input: true input: true
}).then(async ({ canceled, result: title }) => { }).then(async ({ canceled, result: name }) => {
if (canceled) return; if (canceled) return;
const list = await this.$root.api('users/lists/create', { const list = await this.$root.api('users/lists/create', {
title name
}); });
this.list = list; this.list = list;

View File

@ -44,7 +44,8 @@
<div v-if="announcements && announcements.length > 0"> <div v-if="announcements && announcements.length > 0">
<div v-for="announcement in announcements"> <div v-for="announcement in announcements">
<h1 v-html="announcement.title"></h1> <h1 v-html="announcement.title"></h1>
<div v-html="announcement.text"></div> <mfm :text="announcement.text"/>
<img v-if="announcement.image" :src="announcement.image" alt="" style="display: block; max-height: 130px; max-width: 100%;"/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -215,11 +215,6 @@ export default Vue.extend({
@media (min-width 500px) @media (min-width 500px)
padding 28px 32px 18px 32px padding 28px 32px 18px 32px
&:after
content ""
display block
clear both
> header > header
display flex display flex
line-height 1.1em line-height 1.1em
@ -236,6 +231,7 @@ export default Vue.extend({
height 60px height 60px
> div > div
min-width 0
> .name > .name
display inline-block display inline-block

View File

@ -43,7 +43,8 @@
<div class="announcements" v-if="announcements && announcements.length > 0"> <div class="announcements" v-if="announcements && announcements.length > 0">
<article v-for="announcement in announcements"> <article v-for="announcement in announcements">
<span v-html="announcement.title" class="title"></span> <span v-html="announcement.title" class="title"></span>
<div v-html="announcement.text"></div> <mfm :text="announcement.text"/>
<img v-if="announcement.image" :src="announcement.image" alt="" style="display: block; max-height: 120px; max-width: 100%;"/>
</article> </article>
</div> </div>
<a :href="aboutUrl"><p class="about">{{ $t('about') }}</p></a> <a :href="aboutUrl"><p class="about">{{ $t('about') }}</p></a>

View File

@ -29,7 +29,8 @@
<div class="announcements" v-if="announcements && announcements.length > 0"> <div class="announcements" v-if="announcements && announcements.length > 0">
<article v-for="announcement in announcements"> <article v-for="announcement in announcements">
<span class="title" v-html="announcement.title"></span> <span class="title" v-html="announcement.title"></span>
<div v-html="announcement.text"></div> <mfm :text="announcement.text"/>
<img v-if="announcement.image" :src="announcement.image" alt="" style="display: block; max-height: 120px; max-width: 100%;"/>
</article> </article>
</div> </div>
<article class="about-misskey"> <article class="about-misskey">

View File

@ -19,7 +19,7 @@ export const cafeTheme: Theme = require('../themes/cafe.json5');
export const japaneseSushiSetTheme: Theme = require('../themes/japanese-sushi-set.json5'); export const japaneseSushiSetTheme: Theme = require('../themes/japanese-sushi-set.json5');
export const gruvboxDarkTheme: Theme = require('../themes/gruvbox-dark.json5'); export const gruvboxDarkTheme: Theme = require('../themes/gruvbox-dark.json5');
export const monokaiTheme: Theme = require('../themes/monokai.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 rainyTheme: Theme = require('../themes/rainy.json5');
export const mauveTheme: Theme = require('../themes/mauve.json5'); export const mauveTheme: Theme = require('../themes/mauve.json5');
export const grayTheme: Theme = require('../themes/gray.json5'); export const grayTheme: Theme = require('../themes/gray.json5');
@ -35,7 +35,7 @@ export const builtinThemes = [
japaneseSushiSetTheme, japaneseSushiSetTheme,
gruvboxDarkTheme, gruvboxDarkTheme,
monokaiTheme, monokaiTheme,
colorfulTheme, vividTheme,
rainyTheme, rainyTheme,
mauveTheme, mauveTheme,
grayTheme, grayTheme,

View File

@ -1,7 +1,7 @@
{ {
id: '2d066d6e-bd39-4f23-bd48-686d5c1c6ae8', id: '2d066d6e-bd39-4f23-bd48-686d5c1c6ae8',
name: 'Colorful', name: 'Vivid',
author: 'syuilo', author: 'syuilo',
base: 'light', base: 'light',

View File

@ -1,7 +1,11 @@
import { Meta } from '../models/entities/meta'; import { Meta } from '../models/entities/meta';
import { getConnection } from 'typeorm'; import { getConnection } from 'typeorm';
export default async function(): Promise<Meta> { let cache: Meta;
export async function fetchMeta(noCache = false): Promise<Meta> {
if (!noCache && cache) return cache;
return await getConnection().transaction(async transactionalEntityManager => { return await getConnection().transaction(async transactionalEntityManager => {
// バグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する // バグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する
const meta = await transactionalEntityManager.findOne(Meta, { const meta = await transactionalEntityManager.findOne(Meta, {
@ -11,11 +15,21 @@ export default async function(): Promise<Meta> {
}); });
if (meta) { if (meta) {
cache = meta;
return meta; return meta;
} else { } else {
return await transactionalEntityManager.save(Meta, { const saved = await transactionalEntityManager.save(Meta, {
id: 'x' id: 'x'
}) as Meta; }) as Meta;
cache = saved;
return saved;
} }
}); });
} }
setInterval(() => {
fetchMeta(true).then(meta => {
cache = meta;
});
}, 5000);

View File

@ -1,4 +1,4 @@
import fetchMeta from './fetch-meta'; import { fetchMeta } from './fetch-meta';
import { ILocalUser } from '../models/entities/user'; import { ILocalUser } from '../models/entities/user';
import { Users } from '../models'; import { Users } from '../models';
import { ensure } from '../prelude/ensure'; import { ensure } from '../prelude/ensure';

View File

@ -1,5 +1,5 @@
import { emojiRegex } from './emoji-regex'; import { emojiRegex } from './emoji-regex';
import fetchMeta from './fetch-meta'; import { fetchMeta } from './fetch-meta';
import { Emojis } from '../models'; import { Emojis } from '../models';
const basic10: Record<string, string> = { const basic10: Record<string, string> = {

View File

@ -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 = { export type Schema = {
type: 'number' | 'string' | 'array' | 'object' | any; type: 'boolean' | 'number' | 'string' | 'array' | 'object' | 'any';
optional?: boolean; nullable: boolean;
optional: boolean;
items?: Schema; items?: Schema;
properties?: Obj; properties?: Obj;
description?: string; description?: string;
example?: any;
format?: string;
ref?: string;
enum?: string[];
}; };
type NonUndefinedPropertyNames<T extends Obj> = {
[K in keyof T]: T[K]['optional'] extends true ? never : K
}[keyof T];
type UndefinedPropertyNames<T extends Obj> = {
[K in keyof T]: T[K]['optional'] extends true ? K : never
}[keyof T];
type OnlyRequired<T extends Obj> = Pick<T, NonUndefinedPropertyNames<T>>;
type OnlyOptional<T extends Obj> = Pick<T, UndefinedPropertyNames<T>>;
export type Obj = { [key: string]: Schema }; export type Obj = { [key: string]: Schema };
export type ObjType<s extends Obj> = { [P in keyof s]: SchemaType<s[P]> }; export type ObjType<s extends Obj> =
{ [P in keyof OnlyOptional<s>]?: SchemaType<s[P]> } &
{ [P in keyof OnlyRequired<s>]: SchemaType<s[P]> };
// https://qiita.com/hrsh7th@github/items/84e8968c3601009cdcf2 // https://qiita.com/hrsh7th@github/items/84e8968c3601009cdcf2
type MyType<T extends Schema> = { type MyType<T extends Schema> = {
@ -16,26 +48,20 @@ type MyType<T extends Schema> = {
1: SchemaType<T>; 1: SchemaType<T>;
}[T extends Schema ? 1 : 0]; }[T extends Schema ? 1 : 0];
export type SchemaType<p extends Schema> = type NullOrUndefined<p extends Schema, T> =
p['type'] extends 'number' ? number : p['nullable'] extends true
p['type'] extends 'string' ? string : ? p['optional'] extends true
p['type'] extends 'array' ? MyType<NonNullable<p['items']>>[] : ? (T | null | undefined)
p['type'] extends 'object' ? ObjType<NonNullable<p['properties']>> : : (T | null)
any; : p['optional'] extends true
? (T | undefined)
: T;
export function convertOpenApiSchema(schema: Schema) { export type SchemaType<p extends Schema> =
const x = JSON.parse(JSON.stringify(schema)); // copy p['type'] extends 'number' ? NullOrUndefined<p, number> :
if (!['string', 'number', 'boolean', 'array', 'object'].includes(x.type)) { p['type'] extends 'string' ? NullOrUndefined<p, string> :
x['$ref'] = `#/components/schemas/${x.type}`; p['type'] extends 'boolean' ? NullOrUndefined<p, boolean> :
} p['type'] extends 'array' ? NullOrUndefined<p, MyType<NonNullable<p['items']>>[]> :
if (x.type === 'array' && x.items) { p['type'] extends 'object' ? NullOrUndefined<p, ObjType<NonNullable<p['properties']>>> :
x.items = convertOpenApiSchema(x.items); p['type'] extends 'any' ? NullOrUndefined<p, any> :
} any;
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;
}

View File

@ -36,7 +36,7 @@ export class NoteReaction {
public note: Note | null; public note: Note | null;
@Column('varchar', { @Column('varchar', {
length: 32 length: 128
}) })
public reaction: string; public reaction: string;
} }

View File

@ -1,8 +1,8 @@
import { EntityRepository, Repository } from 'typeorm'; import { EntityRepository, Repository } from 'typeorm';
import { Users } from '..'; import { Users } from '..';
import rap from '@prezzemolo/rap';
import { AbuseUserReport } from '../entities/abuse-user-report'; import { AbuseUserReport } from '../entities/abuse-user-report';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all';
@EntityRepository(AbuseUserReport) @EntityRepository(AbuseUserReport)
export class AbuseUserReportRepository extends Repository<AbuseUserReport> { export class AbuseUserReportRepository extends Repository<AbuseUserReport> {
@ -17,7 +17,7 @@ export class AbuseUserReportRepository extends Repository<AbuseUserReport> {
) { ) {
const report = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const report = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({ return await awaitAll({
id: report.id, id: report.id,
createdAt: report.createdAt, createdAt: report.createdAt,
reporterId: report.reporterId, reporterId: report.reporterId,

View File

@ -2,6 +2,9 @@ import { EntityRepository, Repository } from 'typeorm';
import { App } from '../entities/app'; import { App } from '../entities/app';
import { AccessTokens } from '..'; import { AccessTokens } from '..';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { types, bool, SchemaType } from '../../misc/schema';
export type PackedApp = SchemaType<typeof packedAppSchema>;
@EntityRepository(App) @EntityRepository(App)
export class AppRepository extends Repository<App> { export class AppRepository extends Repository<App> {
@ -13,7 +16,7 @@ export class AppRepository extends Repository<App> {
includeSecret?: boolean, includeSecret?: boolean,
includeProfileImageIds?: boolean includeProfileImageIds?: boolean
} }
) { ): Promise<PackedApp> {
const opts = Object.assign({ const opts = Object.assign({
detail: false, detail: false,
includeSecret: false, includeSecret: false,
@ -37,3 +40,40 @@ export class AppRepository extends Repository<App> {
}; };
} }
} }
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: 'アプリケーションのシークレットキー'
}
},
};

View File

@ -1,8 +1,8 @@
import { EntityRepository, Repository } from 'typeorm'; import { EntityRepository, Repository } from 'typeorm';
import { Apps } from '..'; import { Apps } from '..';
import rap from '@prezzemolo/rap';
import { AuthSession } from '../entities/auth-session'; import { AuthSession } from '../entities/auth-session';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all';
@EntityRepository(AuthSession) @EntityRepository(AuthSession)
export class AuthSessionRepository extends Repository<AuthSession> { export class AuthSessionRepository extends Repository<AuthSession> {
@ -12,7 +12,7 @@ export class AuthSessionRepository extends Repository<AuthSession> {
) { ) {
const session = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const session = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({ return await awaitAll({
id: session.id, id: session.id,
app: Apps.pack(session.appId, me), app: Apps.pack(session.appId, me),
token: session.token token: session.token

View File

@ -1,8 +1,11 @@
import { EntityRepository, Repository } from 'typeorm'; import { EntityRepository, Repository } from 'typeorm';
import { Users } from '..'; import { Users } from '..';
import rap from '@prezzemolo/rap';
import { Blocking } from '../entities/blocking'; import { Blocking } from '../entities/blocking';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all';
import { SchemaType, types, bool } from '../../misc/schema';
export type PackedBlocking = SchemaType<typeof packedBlockingSchema>;
@EntityRepository(Blocking) @EntityRepository(Blocking)
export class BlockingRepository extends Repository<Blocking> { export class BlockingRepository extends Repository<Blocking> {
@ -16,14 +19,47 @@ export class BlockingRepository extends Repository<Blocking> {
public async pack( public async pack(
src: Blocking['id'] | Blocking, src: Blocking['id'] | Blocking,
me?: any me?: any
) { ): Promise<PackedBlocking> {
const blocking = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const blocking = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({ return await awaitAll({
id: blocking.id, id: blocking.id,
createdAt: blocking.createdAt.toISOString(),
blockeeId: blocking.blockeeId,
blockee: Users.pack(blocking.blockeeId, me, { blockee: Users.pack(blocking.blockeeId, me, {
detail: true 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.'
},
}
};

View File

@ -1,10 +1,13 @@
import { EntityRepository, Repository } from 'typeorm'; import { EntityRepository, Repository } from 'typeorm';
import { DriveFile } from '../entities/drive-file'; import { DriveFile } from '../entities/drive-file';
import { Users, DriveFolders } from '..'; import { Users, DriveFolders } from '..';
import rap from '@prezzemolo/rap';
import { User } from '../entities/user'; import { User } from '../entities/user';
import { toPuny } from '../../misc/convert-host'; import { toPuny } from '../../misc/convert-host';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all';
import { types, bool, SchemaType } from '../../misc/schema';
export type PackedDriveFile = SchemaType<typeof packedDriveFileSchema>;
@EntityRepository(DriveFile) @EntityRepository(DriveFile)
export class DriveFileRepository extends Repository<DriveFile> { export class DriveFileRepository extends Repository<DriveFile> {
@ -82,7 +85,7 @@ export class DriveFileRepository extends Repository<DriveFile> {
self?: boolean, self?: boolean,
withUser?: boolean, withUser?: boolean,
} }
) { ): Promise<PackedDriveFile> {
const opts = Object.assign({ const opts = Object.assign({
detail: false, detail: false,
self: false self: false
@ -90,9 +93,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
const file = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const file = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({ return await awaitAll({
id: file.id, id: file.id,
createdAt: file.createdAt, createdAt: file.createdAt.toISOString(),
name: file.name, name: file.name,
type: file.type, type: file.type,
md5: file.md5, md5: file.md5,
@ -109,3 +112,66 @@ export class DriveFileRepository extends Repository<DriveFile> {
}); });
} }
} }
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.',
},
},
};

View File

@ -1,8 +1,11 @@
import { EntityRepository, Repository } from 'typeorm'; import { EntityRepository, Repository } from 'typeorm';
import { DriveFolders, DriveFiles } from '..'; import { DriveFolders, DriveFiles } from '..';
import rap from '@prezzemolo/rap';
import { DriveFolder } from '../entities/drive-folder'; import { DriveFolder } from '../entities/drive-folder';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all';
import { SchemaType, types, bool } from '../../misc/schema';
export type PackedDriveFolder = SchemaType<typeof packedDriveFolderSchema>;
@EntityRepository(DriveFolder) @EntityRepository(DriveFolder)
export class DriveFolderRepository extends Repository<DriveFolder> { export class DriveFolderRepository extends Repository<DriveFolder> {
@ -18,16 +21,16 @@ export class DriveFolderRepository extends Repository<DriveFolder> {
options?: { options?: {
detail: boolean detail: boolean
} }
): Promise<Record<string, any>> { ): Promise<PackedDriveFolder> {
const opts = Object.assign({ const opts = Object.assign({
detail: false detail: false
}, options); }, options);
const folder = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const folder = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({ return await awaitAll({
id: folder.id, id: folder.id,
createdAt: folder.createdAt, createdAt: folder.createdAt.toISOString(),
name: folder.name, name: folder.name,
parentId: folder.parentId, parentId: folder.parentId,
@ -48,3 +51,50 @@ export class DriveFolderRepository extends Repository<DriveFolder> {
}); });
} }
} }
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'
},
},
};

View File

@ -1,8 +1,9 @@
import { EntityRepository, Repository } from 'typeorm'; import { EntityRepository, Repository } from 'typeorm';
import { Users } from '..'; import { Users } from '..';
import rap from '@prezzemolo/rap';
import { Following } from '../entities/following'; import { Following } from '../entities/following';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all';
import { SchemaType, types, bool } from '../../misc/schema';
type LocalFollowerFollowing = Following & { type LocalFollowerFollowing = Following & {
followerHost: null; followerHost: null;
@ -28,6 +29,8 @@ type RemoteFolloweeFollowing = Following & {
followeeSharedInbox: string; followeeSharedInbox: string;
}; };
export type PackedFollowing = SchemaType<typeof packedFollowingSchema>;
@EntityRepository(Following) @EntityRepository(Following)
export class FollowingRepository extends Repository<Following> { export class FollowingRepository extends Repository<Following> {
public isLocalFollower(following: Following): following is LocalFollowerFollowing { public isLocalFollower(following: Following): following is LocalFollowerFollowing {
@ -64,22 +67,64 @@ export class FollowingRepository extends Repository<Following> {
populateFollowee?: boolean; populateFollowee?: boolean;
populateFollower?: boolean; populateFollower?: boolean;
} }
) { ): Promise<PackedFollowing> {
const following = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const following = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
if (opts == null) opts = {}; if (opts == null) opts = {};
return await rap({ return await awaitAll({
id: following.id, id: following.id,
createdAt: following.createdAt, createdAt: following.createdAt.toISOString(),
followeeId: following.followeeId, followeeId: following.followeeId,
followerId: following.followerId, followerId: following.followerId,
followee: opts.populateFollowee ? Users.pack(following.followee || following.followeeId, me, { followee: opts.populateFollowee ? Users.pack(following.followee || following.followeeId, me, {
detail: true detail: true
}) : null, }) : undefined,
follower: opts.populateFollower ? Users.pack(following.follower || following.followerId, me, { follower: opts.populateFollower ? Users.pack(following.follower || following.followerId, me, {
detail: true 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.'
},
}
};

View File

@ -1,8 +1,8 @@
import { EntityRepository, Repository } from 'typeorm'; import { EntityRepository, Repository } from 'typeorm';
import rap from '@prezzemolo/rap';
import { ReversiMatching } from '../../../entities/games/reversi/matching'; import { ReversiMatching } from '../../../entities/games/reversi/matching';
import { Users } from '../../..'; import { Users } from '../../..';
import { ensure } from '../../../../prelude/ensure'; import { ensure } from '../../../../prelude/ensure';
import { awaitAll } from '../../../../prelude/await-all';
@EntityRepository(ReversiMatching) @EntityRepository(ReversiMatching)
export class ReversiMatchingRepository extends Repository<ReversiMatching> { export class ReversiMatchingRepository extends Repository<ReversiMatching> {
@ -12,7 +12,7 @@ export class ReversiMatchingRepository extends Repository<ReversiMatching> {
) { ) {
const matching = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const matching = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({ return await awaitAll({
id: matching.id, id: matching.id,
createdAt: matching.createdAt, createdAt: matching.createdAt,
parentId: matching.parentId, parentId: matching.parentId,

View File

@ -2,6 +2,9 @@ import { EntityRepository, Repository } from 'typeorm';
import { MessagingMessage } from '../entities/messaging-message'; import { MessagingMessage } from '../entities/messaging-message';
import { Users, DriveFiles } from '..'; import { Users, DriveFiles } from '..';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { types, bool, SchemaType } from '../../misc/schema';
export type PackedMessagingMessage = SchemaType<typeof packedMessagingMessageSchema>;
@EntityRepository(MessagingMessage) @EntityRepository(MessagingMessage)
export class MessagingMessageRepository extends Repository<MessagingMessage> { export class MessagingMessageRepository extends Repository<MessagingMessage> {
@ -15,7 +18,7 @@ export class MessagingMessageRepository extends Repository<MessagingMessage> {
options?: { options?: {
populateRecipient: boolean populateRecipient: boolean
} }
) { ): Promise<PackedMessagingMessage> {
const opts = options || { const opts = options || {
populateRecipient: true populateRecipient: true
}; };
@ -24,15 +27,73 @@ export class MessagingMessageRepository extends Repository<MessagingMessage> {
return { return {
id: message.id, id: message.id,
createdAt: message.createdAt, createdAt: message.createdAt.toISOString(),
text: message.text, text: message.text,
userId: message.userId, userId: message.userId,
user: await Users.pack(message.user || message.userId, me), user: await Users.pack(message.user || message.userId, me),
recipientId: message.recipientId, 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, fileId: message.fileId,
file: message.fileId ? await DriveFiles.pack(message.fileId) : null, file: message.fileId ? await DriveFiles.pack(message.fileId) : null,
isRead: message.isRead 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,
},
},
};

View File

@ -1,8 +1,11 @@
import { EntityRepository, Repository } from 'typeorm'; import { EntityRepository, Repository } from 'typeorm';
import { Users } from '..'; import { Users } from '..';
import rap from '@prezzemolo/rap';
import { Muting } from '../entities/muting'; import { Muting } from '../entities/muting';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all';
import { types, bool, SchemaType } from '../../misc/schema';
export type PackedMuting = SchemaType<typeof packedMutingSchema>;
@EntityRepository(Muting) @EntityRepository(Muting)
export class MutingRepository extends Repository<Muting> { export class MutingRepository extends Repository<Muting> {
@ -16,14 +19,47 @@ export class MutingRepository extends Repository<Muting> {
public async pack( public async pack(
src: Muting['id'] | Muting, src: Muting['id'] | Muting,
me?: any me?: any
) { ): Promise<PackedMuting> {
const muting = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const muting = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({ return await awaitAll({
id: muting.id, id: muting.id,
createdAt: muting.createdAt.toISOString(),
muteeId: muting.muteeId,
mutee: Users.pack(muting.muteeId, me, { mutee: Users.pack(muting.muteeId, me, {
detail: true 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.'
},
}
};

View File

@ -2,18 +2,54 @@ import { EntityRepository, Repository } from 'typeorm';
import { NoteReaction } from '../entities/note-reaction'; import { NoteReaction } from '../entities/note-reaction';
import { Users } from '..'; import { Users } from '..';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { types, bool, SchemaType } from '../../misc/schema';
export type PackedNoteReaction = SchemaType<typeof packedNoteReactionSchema>;
@EntityRepository(NoteReaction) @EntityRepository(NoteReaction)
export class NoteReactionRepository extends Repository<NoteReaction> { export class NoteReactionRepository extends Repository<NoteReaction> {
public async pack( public async pack(
src: NoteReaction['id'] | NoteReaction, src: NoteReaction['id'] | NoteReaction,
me?: any me?: any
) { ): Promise<PackedNoteReaction> {
const reaction = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const reaction = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return { return {
id: reaction.id, id: reaction.id,
createdAt: reaction.createdAt.toISOString(),
user: await Users.pack(reaction.userId, me), 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.'
},
},
};

View File

@ -4,8 +4,11 @@ import { User } from '../entities/user';
import { unique, concat } from '../../prelude/array'; import { unique, concat } from '../../prelude/array';
import { nyaize } from '../../misc/nyaize'; import { nyaize } from '../../misc/nyaize';
import { Emojis, Users, Apps, PollVotes, DriveFiles, NoteReactions, Followings, Polls } from '..'; import { Emojis, Users, Apps, PollVotes, DriveFiles, NoteReactions, Followings, Polls } from '..';
import rap from '@prezzemolo/rap';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { SchemaType, types, bool } from '../../misc/schema';
import { awaitAll } from '../../prelude/await-all';
export type PackedNote = SchemaType<typeof packedNoteSchema>;
@EntityRepository(Note) @EntityRepository(Note)
export class NoteRepository extends Repository<Note> { export class NoteRepository extends Repository<Note> {
@ -13,18 +16,18 @@ export class NoteRepository extends Repository<Note> {
return x.trim().length <= 100; 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; let hide = false;
// visibility が specified かつ自分が指定されていなかったら非表示 // visibility が specified かつ自分が指定されていなかったら非表示
if (packedNote.visibility == 'specified') { if (packedNote.visibility === 'specified') {
if (meId == null) { if (meId == null) {
hide = true; hide = true;
} else if (meId === packedNote.userId) { } else if (meId === packedNote.userId) {
hide = false; hide = false;
} else { } else {
// 指定されているかどうか // 指定されているかどうか
const specified = packedNote.visibleUserIds.some((id: any) => meId === id); const specified = packedNote.visibleUserIds!.some((id: any) => meId === id);
if (specified) { if (specified) {
hide = false; hide = false;
@ -40,10 +43,10 @@ export class NoteRepository extends Repository<Note> {
hide = true; hide = true;
} else if (meId === packedNote.userId) { } else if (meId === packedNote.userId) {
hide = false; hide = false;
} else if (packedNote.reply && (meId === packedNote.reply.userId)) { } else if (packedNote.reply && (meId === (packedNote.reply as PackedNote).userId)) {
// 自分の投稿に対するリプライ // 自分の投稿に対するリプライ
hide = false; 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; hide = false;
} else { } else {
@ -62,14 +65,13 @@ export class NoteRepository extends Repository<Note> {
} }
if (hide) { if (hide) {
packedNote.visibleUserIds = null; packedNote.visibleUserIds = undefined;
packedNote.fileIds = []; packedNote.fileIds = [];
packedNote.files = []; packedNote.files = [];
packedNote.text = null; packedNote.text = null;
packedNote.poll = null; packedNote.poll = undefined;
packedNote.cw = null; packedNote.cw = null;
packedNote.tags = []; packedNote.geo = undefined;
packedNote.geo = null;
packedNote.isHidden = true; packedNote.isHidden = true;
} }
} }
@ -92,7 +94,7 @@ export class NoteRepository extends Repository<Note> {
detail?: boolean; detail?: boolean;
skipHide?: boolean; skipHide?: boolean;
} }
): Promise<Record<string, any>> { ): Promise<PackedNote> {
const opts = Object.assign({ const opts = Object.assign({
detail: true, detail: true,
skipHide: false skipHide: false
@ -159,9 +161,9 @@ export class NoteRepository extends Repository<Note> {
const reactionEmojis = unique(concat([note.emojis, Object.keys(note.reactions)])); const reactionEmojis = unique(concat([note.emojis, Object.keys(note.reactions)]));
const packed = await rap({ const packed = await awaitAll({
id: note.id, id: note.id,
createdAt: note.createdAt, createdAt: note.createdAt.toISOString(),
app: note.appId ? Apps.pack(note.appId) : undefined, app: note.appId ? Apps.pack(note.appId) : undefined,
userId: note.userId, userId: note.userId,
user: Users.pack(note.user || note.userId, meId), user: Users.pack(note.user || note.userId, meId),
@ -213,3 +215,127 @@ export class NoteRepository extends Repository<Note> {
return packed; 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,
},
},
};

View File

@ -1,8 +1,11 @@
import { EntityRepository, Repository } from 'typeorm'; import { EntityRepository, Repository } from 'typeorm';
import { Users, Notes } from '..'; import { Users, Notes } from '..';
import rap from '@prezzemolo/rap';
import { Notification } from '../entities/notification'; import { Notification } from '../entities/notification';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { awaitAll } from '../../prelude/await-all';
import { types, bool, SchemaType } from '../../misc/schema';
export type PackedNotification = SchemaType<typeof packedNotificationSchema>;
@EntityRepository(Notification) @EntityRepository(Notification)
export class NotificationRepository extends Repository<Notification> { export class NotificationRepository extends Repository<Notification> {
@ -14,12 +17,12 @@ export class NotificationRepository extends Repository<Notification> {
public async pack( public async pack(
src: Notification['id'] | Notification, src: Notification['id'] | Notification,
) { ): Promise<PackedNotification> {
const notification = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const notification = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({ return await awaitAll({
id: notification.id, id: notification.id,
createdAt: notification.createdAt, createdAt: notification.createdAt.toISOString(),
type: notification.type, type: notification.type,
userId: notification.notifierId, userId: notification.notifierId,
user: Users.pack(notification.notifier || notification.notifierId), user: Users.pack(notification.notifier || notification.notifierId),
@ -46,3 +49,39 @@ export class NotificationRepository extends Repository<Notification> {
}); });
} }
} }
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,
},
}
};

View File

@ -2,12 +2,15 @@ import { EntityRepository, Repository } from 'typeorm';
import { UserList } from '../entities/user-list'; import { UserList } from '../entities/user-list';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import { UserListJoinings } from '..'; import { UserListJoinings } from '..';
import { bool, types, SchemaType } from '../../misc/schema';
export type PackedUserList = SchemaType<typeof packedUserListSchema>;
@EntityRepository(UserList) @EntityRepository(UserList)
export class UserListRepository extends Repository<UserList> { export class UserListRepository extends Repository<UserList> {
public async pack( public async pack(
src: UserList['id'] | UserList, src: UserList['id'] | UserList,
) { ): Promise<PackedUserList> {
const userList = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const userList = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
const users = await UserListJoinings.find({ const users = await UserListJoinings.find({
@ -16,8 +19,43 @@ export class UserListRepository extends Repository<UserList> {
return { return {
id: userList.id, id: userList.id,
createdAt: userList.createdAt.toISOString(),
name: userList.name, name: userList.name,
userIds: users.map(x => x.userId) 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',
}
},
},
};

View File

@ -1,9 +1,12 @@
import { EntityRepository, Repository, In } from 'typeorm'; import { EntityRepository, Repository, In } from 'typeorm';
import { User, ILocalUser, IRemoteUser } from '../entities/user'; import { User, ILocalUser, IRemoteUser } from '../entities/user';
import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..'; import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..';
import rap from '@prezzemolo/rap';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
import config from '../../config'; import config from '../../config';
import { SchemaType, bool, types } from '../../misc/schema';
import { awaitAll } from '../../prelude/await-all';
export type PackedUser = SchemaType<typeof packedUserSchema>;
@EntityRepository(User) @EntityRepository(User)
export class UserRepository extends Repository<User> { export class UserRepository extends Repository<User> {
@ -71,7 +74,7 @@ export class UserRepository extends Repository<User> {
includeSecrets?: boolean, includeSecrets?: boolean,
includeHasUnreadNotes?: boolean includeHasUnreadNotes?: boolean
} }
): Promise<Record<string, any>> { ): Promise<PackedUser> {
const opts = Object.assign({ const opts = Object.assign({
detail: false, detail: false,
includeSecrets: false includeSecrets: false
@ -86,7 +89,7 @@ export class UserRepository extends Repository<User> {
const falsy = opts.detail ? false : undefined; const falsy = opts.detail ? false : undefined;
return await rap({ const packed = {
id: user.id, id: user.id,
name: user.name, name: user.name,
username: user.username, username: user.username,
@ -120,8 +123,8 @@ export class UserRepository extends Repository<User> {
...(opts.detail ? { ...(opts.detail ? {
url: profile!.url, url: profile!.url,
createdAt: user.createdAt, createdAt: user.createdAt.toISOString(),
updatedAt: user.updatedAt, updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null,
bannerUrl: user.bannerUrl, bannerUrl: user.bannerUrl,
bannerColor: user.bannerColor, bannerColor: user.bannerColor,
isLocked: user.isLocked, isLocked: user.isLocked,
@ -179,7 +182,9 @@ export class UserRepository extends Repository<User> {
isBlocked: relation.isBlocked, isBlocked: relation.isBlocked,
isMuted: relation.isMuted, isMuted: relation.isMuted,
} : {}) } : {})
}); };
return await awaitAll(packed);
} }
public isLocalUser(user: User): user is ILocalUser { public isLocalUser(user: User): user is ILocalUser {
@ -216,3 +221,156 @@ export class UserRepository extends Repository<User> {
} }
//#endregion //#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 theyve 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,
},
},
};

23
src/prelude/await-all.ts Normal file
View File

@ -0,0 +1,23 @@
type Await<T> = T extends Promise<infer U> ? U : T;
type AwaitAll<T> = {
[P in keyof T]: Await<T[P]>;
};
export async function awaitAll<T>(obj: T): Promise<AwaitAll<T>> {
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;
}

View File

@ -10,7 +10,7 @@ import { registerOrFetchInstanceDoc } from '../../services/register-or-fetch-ins
import { Instances, Users, UserPublickeys } from '../../models'; import { Instances, Users, UserPublickeys } from '../../models';
import { instanceChart } from '../../services/chart'; import { instanceChart } from '../../services/chart';
import { UserPublickey } from '../../models/entities/user-publickey'; 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 { toPuny } from '../../misc/convert-host';
import { validActor } from '../../remote/activitypub/type'; import { validActor } from '../../remote/activitypub/type';
import { ensure } from '../../prelude/ensure'; import { ensure } from '../../prelude/ensure';
@ -48,7 +48,6 @@ export default async (job: Bull.Job): Promise<void> => {
} }
// ブロックしてたら中断 // ブロックしてたら中断
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
const meta = await fetchMeta(); const meta = await fetchMeta();
if (meta.blockedHosts.includes(host)) { if (meta.blockedHosts.includes(host)) {
logger.info(`Blocked request: ${host}`); logger.info(`Blocked request: ${host}`);

View File

@ -6,7 +6,7 @@ import { fetchNote, resolveNote } from '../../models/note';
import { resolvePerson } from '../../models/person'; import { resolvePerson } from '../../models/person';
import { apLogger } from '../../logger'; import { apLogger } from '../../logger';
import { extractDbHost } from '../../../../misc/convert-host'; import { extractDbHost } from '../../../../misc/convert-host';
import fetchMeta from '../../../../misc/fetch-meta'; import { fetchMeta } from '../../../../misc/fetch-meta';
const logger = apLogger; const logger = apLogger;
@ -26,7 +26,6 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity:
} }
// アナウンス先をブロックしてたら中断 // アナウンス先をブロックしてたら中断
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
const meta = await fetchMeta(); const meta = await fetchMeta();
if (meta.blockedHosts.includes(extractDbHost(uri))) return; if (meta.blockedHosts.includes(extractDbHost(uri))) return;

View File

@ -1,7 +1,7 @@
import uploadFromUrl from '../../../services/drive/upload-from-url'; import uploadFromUrl from '../../../services/drive/upload-from-url';
import { IRemoteUser } from '../../../models/entities/user'; import { IRemoteUser } from '../../../models/entities/user';
import Resolver from '../resolver'; import Resolver from '../resolver';
import fetchMeta from '../../../misc/fetch-meta'; import { fetchMeta } from '../../../misc/fetch-meta';
import { apLogger } from '../logger'; import { apLogger } from '../logger';
import { DriveFile } from '../../../models/entities/drive-file'; import { DriveFile } from '../../../models/entities/drive-file';
import { DriveFiles } from '../../../models'; import { DriveFiles } from '../../../models';

View File

@ -20,7 +20,7 @@ import { Note } from '../../../models/entities/note';
import { IObject, INote } from '../type'; import { IObject, INote } from '../type';
import { Emoji } from '../../../models/entities/emoji'; import { Emoji } from '../../../models/entities/emoji';
import { genId } from '../../../misc/gen-id'; import { genId } from '../../../misc/gen-id';
import fetchMeta from '../../../misc/fetch-meta'; import { fetchMeta } from '../../../misc/fetch-meta';
import { ensure } from '../../../prelude/ensure'; import { ensure } from '../../../prelude/ensure';
const logger = apLogger; const logger = apLogger;
@ -233,7 +233,6 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
if (uri == null) throw new Error('missing uri'); if (uri == null) throw new Error('missing uri');
// ブロックしてたら中断 // ブロックしてたら中断
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
const meta = await fetchMeta(); const meta = await fetchMeta();
if (meta.blockedHosts.includes(extractDbHost(uri))) throw { statusCode: 451 }; if (meta.blockedHosts.includes(extractDbHost(uri))) throw { statusCode: 451 };

View File

@ -10,7 +10,7 @@ import { ILocalUser } from '../../models/entities/user';
import { publishApLogStream } from '../../services/stream'; import { publishApLogStream } from '../../services/stream';
import { apLogger } from './logger'; import { apLogger } from './logger';
import { UserKeypairs } from '../../models'; import { UserKeypairs } from '../../models';
import fetchMeta from '../../misc/fetch-meta'; import { fetchMeta } from '../../misc/fetch-meta';
import { toPuny } from '../../misc/convert-host'; import { toPuny } from '../../misc/convert-host';
import { ensure } from '../../prelude/ensure'; 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); const { protocol, host, hostname, port, pathname, search } = new URL(url);
// ブロックしてたら中断 // ブロックしてたら中断
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
const meta = await fetchMeta(); const meta = await fetchMeta();
if (meta.blockedHosts.includes(toPuny(host))) return; if (meta.blockedHosts.includes(toPuny(host))) return;

View File

@ -3,6 +3,7 @@ import { ILocalUser } from '../../models/entities/user';
import { IEndpointMeta } from './endpoints'; import { IEndpointMeta } from './endpoints';
import { ApiError } from './error'; import { ApiError } from './error';
import { App } from '../../models/entities/app'; import { App } from '../../models/entities/app';
import { SchemaType } from '../../misc/schema';
type Params<T extends IEndpointMeta> = { type Params<T extends IEndpointMeta> = {
[P in keyof T['params']]: NonNullable<T['params']>[P]['transform'] extends Function [P in keyof T['params']]: NonNullable<T['params']>[P]['transform'] extends Function
@ -12,7 +13,11 @@ type Params<T extends IEndpointMeta> = {
export type Response = Record<string, any> | void; export type Response = Record<string, any> | void;
export default function <T extends IEndpointMeta>(meta: T, cb: (params: Params<T>, user: ILocalUser, app: App, file?: any, cleanup?: Function) => Promise<Response>): (params: any, user: ILocalUser, app: App, file?: any) => Promise<any> { type executor<T extends IEndpointMeta> =
(params: Params<T>, user: ILocalUser, app: App, file?: any, cleanup?: Function) => Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>;
export default function <T extends IEndpointMeta>(meta: T, cb: executor<T>)
: (params: any, user: ILocalUser, app: App, file?: any) => Promise<any> {
return (params: any, user: ILocalUser, app: App, file?: any) => { return (params: any, user: ILocalUser, app: App, file?: any) => {
function cleanup() { function cleanup() {
fs.unlink(file.path, () => {}); fs.unlink(file.path, () => {});

View File

@ -164,7 +164,7 @@ export const meta = {
}, },
maintainerName: { maintainerName: {
validator: $.optional.str, validator: $.optional.nullable.str,
desc: { desc: {
'ja-JP': 'インスタンスの管理者名' 'ja-JP': 'インスタンスの管理者名'
} }

View File

@ -9,7 +9,7 @@ import { extractDbHost } from '../../../../misc/convert-host';
import { Users, Notes } from '../../../../models'; import { Users, Notes } from '../../../../models';
import { Note } from '../../../../models/entities/note'; import { Note } from '../../../../models/entities/note';
import { User } from '../../../../models/entities/user'; import { User } from '../../../../models/entities/user';
import fetchMeta from '../../../../misc/fetch-meta'; import { fetchMeta } from '../../../../misc/fetch-meta';
import { validActor } from '../../../../remote/activitypub/type'; import { validActor } from '../../../../remote/activitypub/type';
export const meta = { export const meta = {

View File

@ -4,6 +4,7 @@ import define from '../../define';
import { Apps } from '../../../../models'; import { Apps } from '../../../../models';
import { genId } from '../../../../misc/gen-id'; import { genId } from '../../../../misc/gen-id';
import { unique } from '../../../../prelude/array'; import { unique } from '../../../../prelude/array';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
tags: ['app'], tags: ['app'],
@ -52,27 +53,10 @@ export const meta = {
}, },
res: { res: {
type: 'object', type: types.object,
properties: { optional: bool.false, nullable: bool.false,
id: { ref: 'App',
type: 'string', },
description: 'アプリケーションのID'
},
name: {
type: 'string',
description: 'アプリケーションの名前'
},
callbackUrl: {
type: 'string',
nullable: true,
description: 'コールバックするURL'
},
secret: {
type: 'string',
description: 'アプリケーションのシークレットキー'
}
}
}
}; };
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {

View File

@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import { ApiError } from '../../error'; import { ApiError } from '../../error';
import { Apps } from '../../../../models'; import { Apps } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
tags: ['app'], tags: ['app'],
@ -13,6 +14,12 @@ export const meta = {
}, },
}, },
res: {
type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'App',
},
errors: { errors: {
noSuchApp: { noSuchApp: {
message: 'No such app.', message: 'No such app.',

View File

@ -5,6 +5,7 @@ import define from '../../../define';
import { ApiError } from '../../../error'; import { ApiError } from '../../../error';
import { Apps, AuthSessions } from '../../../../../models'; import { Apps, AuthSessions } from '../../../../../models';
import { genId } from '../../../../../misc/gen-id'; import { genId } from '../../../../../misc/gen-id';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
tags: ['auth'], tags: ['auth'],
@ -27,14 +28,18 @@ export const meta = {
}, },
res: { res: {
type: 'object', type: types.object,
optional: bool.false, nullable: bool.false,
properties: { properties: {
token: { token: {
type: 'string', type: types.string,
optional: bool.false, nullable: bool.false,
description: 'セッションのトークン' description: 'セッションのトークン'
}, },
url: { url: {
type: 'string', type: types.string,
optional: bool.false, nullable: bool.false,
format: 'url',
description: 'セッションのURL' description: 'セッションのURL'
}, },
} }

View File

@ -3,6 +3,7 @@ import define from '../../../define';
import { ApiError } from '../../../error'; import { ApiError } from '../../../error';
import { Apps, AuthSessions, AccessTokens, Users } from '../../../../../models'; import { Apps, AuthSessions, AccessTokens, Users } from '../../../../../models';
import { ensure } from '../../../../../prelude/ensure'; import { ensure } from '../../../../../prelude/ensure';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
tags: ['auth'], tags: ['auth'],
@ -28,15 +29,19 @@ export const meta = {
}, },
res: { res: {
type: 'object', type: types.object,
optional: bool.false, nullable: bool.false,
properties: { properties: {
accessToken: { accessToken: {
type: 'string', type: types.string,
optional: bool.false, nullable: bool.false,
description: 'ユーザーのアクセストークン', description: 'ユーザーのアクセストークン',
}, },
user: { user: {
type: 'User', type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'User',
description: '認証したユーザー' description: '認証したユーザー'
}, },
} }

View File

@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import { Blockings } from '../../../../models'; import { Blockings } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -32,9 +33,12 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Blocking', type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'Blocking',
} }
}, },
}; };

View File

@ -1,6 +1,7 @@
import define from '../define'; import define from '../define';
import fetchMeta from '../../../misc/fetch-meta'; import { fetchMeta } from '../../../misc/fetch-meta';
import { DriveFiles } from '../../../models'; import { DriveFiles } from '../../../models';
import { types, bool } from '../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -15,20 +16,23 @@ export const meta = {
kind: 'read:drive', kind: 'read:drive',
res: { res: {
type: 'object', type: types.object,
optional: bool.false, nullable: bool.false,
properties: { properties: {
capacity: { capacity: {
type: 'number' type: types.number,
optional: bool.false, nullable: bool.false,
}, },
usage: { usage: {
type: 'number' type: types.number,
optional: bool.false, nullable: bool.false,
} }
} }
} }
}; };
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {
const instance = await fetchMeta(); const instance = await fetchMeta(true);
// Calculate drive usage // Calculate drive usage
const usage = await DriveFiles.clacDriveUsageOf(user); const usage = await DriveFiles.clacDriveUsageOf(user);

View File

@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import { DriveFiles } from '../../../../models'; import { DriveFiles } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -41,10 +42,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'DriveFile', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'DriveFile',
}
}, },
}; };

View File

@ -2,7 +2,8 @@ import $ from 'cafy';
import { ID } from '../../../../../misc/cafy-id'; import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define'; import define from '../../../define';
import { ApiError } from '../../../error'; import { ApiError } from '../../../error';
import { DriveFiles } from '../../../../../models'; import { DriveFiles, Notes } from '../../../../../models';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
stability: 'stable', stability: 'stable',
@ -29,10 +30,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
errors: { errors: {
@ -55,8 +59,11 @@ export default define(meta, async (ps, user) => {
throw new ApiError(meta.errors.noSuchFile); throw new ApiError(meta.errors.noSuchFile);
} }
/* v11 TODO const notes = await Notes.createQueryBuilder('note')
return await packMany(file.metadata.attachedNoteIds || [], user, { .where(':file = ANY(note.fileIds)', { file: file.id })
.getMany();
return await Notes.packMany(notes, user, {
detail: true detail: true
});*/ });
}); });

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import define from '../../../define'; import define from '../../../define';
import { DriveFiles } from '../../../../../models'; import { DriveFiles } from '../../../../../models';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -24,7 +25,8 @@ export const meta = {
}, },
res: { 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, userId: user.id,
}); });
return { return file != null;
file: file ? await DriveFiles.pack(file, { self: true }) : null
};
}); });

View File

@ -6,6 +6,7 @@ import define from '../../../define';
import { apiLogger } from '../../../logger'; import { apiLogger } from '../../../logger';
import { ApiError } from '../../../error'; import { ApiError } from '../../../error';
import { DriveFiles } from '../../../../../models'; import { DriveFiles } from '../../../../../models';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -56,7 +57,9 @@ export const meta = {
}, },
res: { res: {
type: 'DriveFile', type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'DriveFile',
}, },
errors: { errors: {
@ -87,7 +90,7 @@ export default define(meta, async (ps, user, app, file, cleanup) => {
try { try {
// Create file // Create file
const driveFile = await create(user, file.path, name, null, ps.folderId, ps.force, false, null, null, ps.isSensitive); 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) { } catch (e) {
apiLogger.error(e); apiLogger.error(e);
throw new ApiError(); throw new ApiError();

View File

@ -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 });
});

View File

@ -2,6 +2,7 @@ import $ from 'cafy';
import { ID } from '../../../../../misc/cafy-id'; import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define'; import define from '../../../define';
import { DriveFiles } from '../../../../../models'; import { DriveFiles } from '../../../../../models';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
@ -22,7 +23,17 @@ export const meta = {
'ja-JP': 'フォルダID' '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) => { export default define(meta, async (ps, user) => {

View File

@ -4,6 +4,7 @@ import define from '../../../define';
import { ApiError } from '../../../error'; import { ApiError } from '../../../error';
import { DriveFile } from '../../../../../models/entities/drive-file'; import { DriveFile } from '../../../../../models/entities/drive-file';
import { DriveFiles } from '../../../../../models'; import { DriveFiles } from '../../../../../models';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
stability: 'stable', stability: 'stable',
@ -38,7 +39,9 @@ export const meta = {
}, },
res: { res: {
type: 'DriveFile', type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'DriveFile',
}, },
errors: { errors: {

View File

@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import { DriveFolders } from '../../../../models'; import { DriveFolders } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -37,10 +38,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'DriveFolder', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'DriveFolder',
}
}, },
}; };

View File

@ -2,6 +2,7 @@ import $ from 'cafy';
import { ID } from '../../../../../misc/cafy-id'; import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define'; import define from '../../../define';
import { DriveFolders } from '../../../../../models'; import { DriveFolders } from '../../../../../models';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
tags: ['drive'], tags: ['drive'],
@ -25,10 +26,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'DriveFolder', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'DriveFolder',
}
}, },
}; };

View File

@ -3,6 +3,7 @@ import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define'; import define from '../../../define';
import { ApiError } from '../../../error'; import { ApiError } from '../../../error';
import { DriveFolders } from '../../../../../models'; import { DriveFolders } from '../../../../../models';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
stability: 'stable', stability: 'stable',
@ -29,7 +30,9 @@ export const meta = {
}, },
res: { res: {
type: 'DriveFolder', type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'DriveFolder',
}, },
errors: { errors: {

View File

@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import { DriveFiles } from '../../../../models'; import { DriveFiles } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
tags: ['drive'], tags: ['drive'],
@ -31,10 +32,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'DriveFile', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'DriveFile',
}
}, },
}; };

View File

@ -1,7 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import define from '../../define'; import define from '../../define';
import { Instances } from '../../../../models'; import { Instances } from '../../../../models';
import fetchMeta from '../../../../misc/fetch-meta'; import { fetchMeta } from '../../../../misc/fetch-meta';
export const meta = { export const meta = {
tags: ['federation'], tags: ['federation'],
@ -62,7 +62,7 @@ export default define(meta, async (ps, me) => {
} }
if (typeof ps.blocked === 'boolean') { if (typeof ps.blocked === 'boolean') {
const meta = await fetchMeta(); const meta = await fetchMeta(true);
if (ps.blocked) { if (ps.blocked) {
query.andWhere('instance.host IN (:...blocks)', { blocks: meta.blockedHosts }); query.andWhere('instance.host IN (:...blocks)', { blocks: meta.blockedHosts });
} else { } else {

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import define from '../../define'; import define from '../../define';
import { Hashtags } from '../../../../models'; import { Hashtags } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
tags: ['hashtags'], tags: ['hashtags'],
@ -47,9 +48,12 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Hashtag' type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'Hashtag',
} }
}, },
}; };

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import define from '../../define'; import define from '../../define';
import { Hashtags } from '../../../../models'; import { Hashtags } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -37,9 +38,11 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'string' type: types.string,
optional: bool.false, nullable: bool.false,
} }
}, },
}; };

View File

@ -1,5 +1,5 @@
import define from '../../define'; import define from '../../define';
import fetchMeta from '../../../../misc/fetch-meta'; import { fetchMeta } from '../../../../misc/fetch-meta';
import { Notes } from '../../../../models'; import { Notes } from '../../../../models';
import { Note } from '../../../../models/entities/note'; import { Note } from '../../../../models/entities/note';
@ -24,7 +24,7 @@ export const meta = {
}; };
export default define(meta, async () => { export default define(meta, async () => {
const instance = await fetchMeta(); const instance = await fetchMeta(true);
const hiddenTags = instance.hiddenTags.map(t => t.toLowerCase()); const hiddenTags = instance.hiddenTags.map(t => t.toLowerCase());
const tagNotes = await Notes.createQueryBuilder('note') const tagNotes = await Notes.createQueryBuilder('note')

View File

@ -1,6 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import define from '../../define'; import define from '../../define';
import { Users } from '../../../../models'; import { Users } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
requireCredential: false, requireCredential: false,
@ -47,9 +48,12 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'User' type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'User',
} }
}, },
}; };

View File

@ -1,5 +1,6 @@
import define from '../define'; import define from '../define';
import { Users } from '../../../models'; import { Users } from '../../../models';
import { types, bool } from '../../../misc/schema';
export const meta = { export const meta = {
stability: 'stable', stability: 'stable',
@ -15,8 +16,10 @@ export const meta = {
params: {}, params: {},
res: { res: {
type: 'User', type: types.object,
} optional: bool.false, nullable: bool.false,
ref: 'User',
},
}; };
export default define(meta, async (ps, user, app) => { export default define(meta, async (ps, user, app) => {

View File

@ -4,6 +4,7 @@ import { readNotification } from '../../common/read-notification';
import define from '../../define'; import define from '../../define';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { Notifications, Followings, Mutings } from '../../../../models'; import { Notifications, Followings, Mutings } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -53,10 +54,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Notification', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Notification',
}
}, },
}; };

View File

@ -2,7 +2,7 @@ import $ from 'cafy';
import { publishMainStream } from '../../../../services/stream'; import { publishMainStream } from '../../../../services/stream';
import define from '../../define'; import define from '../../define';
import * as nodemailer from 'nodemailer'; import * as nodemailer from 'nodemailer';
import fetchMeta from '../../../../misc/fetch-meta'; import { fetchMeta } from '../../../../misc/fetch-meta';
import rndstr from 'rndstr'; import rndstr from 'rndstr';
import config from '../../../../config'; import config from '../../../../config';
import * as ms from 'ms'; import * as ms from 'ms';
@ -63,7 +63,7 @@ export default define(meta, async (ps, user) => {
emailVerifyCode: code emailVerifyCode: code
}); });
const meta = await fetchMeta(); const meta = await fetchMeta(true);
const enableAuth = meta.smtpUser != null && meta.smtpUser !== ''; const enableAuth = meta.smtpUser != null && meta.smtpUser !== '';

View File

@ -3,6 +3,7 @@ import define from '../../define';
import { MessagingMessage } from '../../../../models/entities/messaging-message'; import { MessagingMessage } from '../../../../models/entities/messaging-message';
import { MessagingMessages, Mutings } from '../../../../models'; import { MessagingMessages, Mutings } from '../../../../models';
import { Brackets } from 'typeorm'; import { Brackets } from 'typeorm';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -24,10 +25,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'MessagingMessage', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'MessagingMessage',
}
}, },
}; };

View File

@ -6,6 +6,7 @@ import { ApiError } from '../../error';
import { getUser } from '../../common/getters'; import { getUser } from '../../common/getters';
import { MessagingMessages } from '../../../../models'; import { MessagingMessages } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -48,10 +49,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'MessagingMessage', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'MessagingMessage',
}
}, },
errors: { errors: {

View File

@ -9,6 +9,7 @@ import { getUser } from '../../../common/getters';
import { MessagingMessages, DriveFiles, Mutings } from '../../../../../models'; import { MessagingMessages, DriveFiles, Mutings } from '../../../../../models';
import { MessagingMessage } from '../../../../../models/entities/messaging-message'; import { MessagingMessage } from '../../../../../models/entities/messaging-message';
import { genId } from '../../../../../misc/gen-id'; import { genId } from '../../../../../misc/gen-id';
import { types, bool } from '../../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -41,7 +42,9 @@ export const meta = {
}, },
res: { res: {
type: 'MessagingMessage', type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'MessagingMessage',
}, },
errors: { errors: {

View File

@ -2,9 +2,10 @@ import $ from 'cafy';
import * as os from 'os'; import * as os from 'os';
import config from '../../../config'; import config from '../../../config';
import define from '../define'; import define from '../define';
import fetchMeta from '../../../misc/fetch-meta'; import { fetchMeta } from '../../../misc/fetch-meta';
import * as pkg from '../../../../package.json'; import * as pkg from '../../../../package.json';
import { Emojis } from '../../../models'; import { Emojis } from '../../../models';
import { types, bool } from '../../../misc/schema';
export const meta = { export const meta = {
stability: 'stable', stability: 'stable',
@ -26,32 +27,40 @@ export const meta = {
}, },
res: { res: {
type: 'object', type: types.object,
optional: bool.false, nullable: bool.false,
properties: { properties: {
version: { version: {
type: 'string', type: types.string,
optional: bool.false, nullable: bool.false,
description: 'The version of Misskey of this instance.', description: 'The version of Misskey of this instance.',
example: pkg.version example: pkg.version
}, },
name: { name: {
type: 'string', type: types.string,
optional: bool.false, nullable: bool.false,
description: 'The name of this instance.', description: 'The name of this instance.',
}, },
description: { description: {
type: 'string', type: types.string,
optional: bool.false, nullable: bool.false,
description: 'The description of this instance.', description: 'The description of this instance.',
}, },
announcements: { announcements: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'object', type: types.object,
optional: bool.false, nullable: bool.false,
properties: { properties: {
title: { title: {
type: 'string', type: types.string,
optional: bool.false, nullable: bool.false,
description: 'The title of the announcement.', description: 'The title of the announcement.',
}, },
text: { text: {
type: 'string', type: types.string,
optional: bool.false, nullable: bool.false,
description: 'The text of the announcement. (can be HTML)', description: 'The text of the announcement. (can be HTML)',
}, },
} }
@ -59,19 +68,23 @@ export const meta = {
description: 'The announcements of this instance.', description: 'The announcements of this instance.',
}, },
disableRegistration: { disableRegistration: {
type: 'boolean', type: types.boolean,
optional: bool.false, nullable: bool.false,
description: 'Whether disabled open registration.', description: 'Whether disabled open registration.',
}, },
disableLocalTimeline: { disableLocalTimeline: {
type: 'boolean', type: types.boolean,
optional: bool.false, nullable: bool.false,
description: 'Whether disabled LTL and STL.', description: 'Whether disabled LTL and STL.',
}, },
disableGlobalTimeline: { disableGlobalTimeline: {
type: 'boolean', type: types.boolean,
optional: bool.false, nullable: bool.false,
description: 'Whether disabled GTL.', description: 'Whether disabled GTL.',
}, },
enableEmojiReaction: { enableEmojiReaction: {
type: 'boolean', type: types.boolean,
optional: bool.false, nullable: bool.false,
description: 'Whether enabled emoji reaction.', description: 'Whether enabled emoji reaction.',
}, },
} }
@ -79,7 +92,7 @@ export const meta = {
}; };
export default define(meta, async (ps, me) => { export default define(meta, async (ps, me) => {
const instance = await fetchMeta(); const instance = await fetchMeta(true);
const emojis = await Emojis.find({ host: null }); const emojis = await Emojis.find({ host: null });

View File

@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { Mutings } from '../../../../models'; import { Mutings } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -32,9 +33,12 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Muting', type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'Muting',
} }
}, },
}; };

View File

@ -3,6 +3,7 @@ import { ID } from '../../../misc/cafy-id';
import define from '../define'; import define from '../define';
import { makePaginationQuery } from '../common/make-pagination-query'; import { makePaginationQuery } from '../common/make-pagination-query';
import { Notes } from '../../../models'; import { Notes } from '../../../models';
import { types, bool } from '../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -62,9 +63,12 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'Note',
} }
}, },
}; };

View File

@ -6,6 +6,7 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
import { generateMuteQuery } from '../../common/generate-mute-query'; import { generateMuteQuery } from '../../common/generate-mute-query';
import { Brackets } from 'typeorm'; import { Brackets } from 'typeorm';
import { Notes } from '../../../../models'; import { Notes } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -41,10 +42,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
}; };

View File

@ -5,6 +5,7 @@ import { ApiError } from '../../error';
import { getNote } from '../../common/getters'; import { getNote } from '../../common/getters';
import { Note } from '../../../../models/entities/note'; import { Note } from '../../../../models/entities/note';
import { Notes } from '../../../../models'; import { Notes } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -37,10 +38,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
errors: { errors: {

View File

@ -3,13 +3,14 @@ import * as ms from 'ms';
import { length } from 'stringz'; import { length } from 'stringz';
import create from '../../../../services/note/create'; import create from '../../../../services/note/create';
import define from '../../define'; import define from '../../define';
import fetchMeta from '../../../../misc/fetch-meta'; import { fetchMeta } from '../../../../misc/fetch-meta';
import { ApiError } from '../../error'; import { ApiError } from '../../error';
import { ID } from '../../../../misc/cafy-id'; import { ID } from '../../../../misc/cafy-id';
import { User } from '../../../../models/entities/user'; import { User } from '../../../../models/entities/user';
import { Users, DriveFiles, Notes } from '../../../../models'; import { Users, DriveFiles, Notes } from '../../../../models';
import { DriveFile } from '../../../../models/entities/drive-file'; import { DriveFile } from '../../../../models/entities/drive-file';
import { Note } from '../../../../models/entities/note'; import { Note } from '../../../../models/entities/note';
import { types, bool } from '../../../../misc/schema';
let maxNoteTextLength = 1000; let maxNoteTextLength = 1000;
@ -174,10 +175,13 @@ export const meta = {
}, },
res: { res: {
type: 'object', type: types.object,
optional: bool.false, nullable: bool.false,
properties: { properties: {
createdNote: { createdNote: {
type: 'Note', type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'Note',
description: '作成した投稿' description: '作成した投稿'
} }
} }

View File

@ -2,6 +2,7 @@ import $ from 'cafy';
import define from '../../define'; import define from '../../define';
import { generateMuteQuery } from '../../common/generate-mute-query'; import { generateMuteQuery } from '../../common/generate-mute-query';
import { Notes } from '../../../../models'; import { Notes } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -24,10 +25,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
}; };

View File

@ -1,12 +1,13 @@
import $ from 'cafy'; import $ from 'cafy';
import { ID } from '../../../../misc/cafy-id'; import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import fetchMeta from '../../../../misc/fetch-meta'; import { fetchMeta } from '../../../../misc/fetch-meta';
import { ApiError } from '../../error'; import { ApiError } from '../../error';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { Notes } from '../../../../models'; import { Notes } from '../../../../models';
import { generateMuteQuery } from '../../common/generate-mute-query'; import { generateMuteQuery } from '../../common/generate-mute-query';
import { activeUsersChart } from '../../../../services/chart'; import { activeUsersChart } from '../../../../services/chart';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -46,10 +47,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
errors: { errors: {
@ -62,7 +66,6 @@ export const meta = {
}; };
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {
// TODO どっかにキャッシュ
const m = await fetchMeta(); const m = await fetchMeta();
if (m.disableGlobalTimeline) { if (m.disableGlobalTimeline) {
if (user == null || (!user.isAdmin && !user.isModerator)) { if (user == null || (!user.isAdmin && !user.isModerator)) {

View File

@ -1,7 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import { ID } from '../../../../misc/cafy-id'; import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import fetchMeta from '../../../../misc/fetch-meta'; import { fetchMeta } from '../../../../misc/fetch-meta';
import { ApiError } from '../../error'; import { ApiError } from '../../error';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { Followings, Notes } from '../../../../models'; import { Followings, Notes } from '../../../../models';
@ -9,6 +9,7 @@ import { Brackets } from 'typeorm';
import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query';
import { generateMuteQuery } from '../../common/generate-mute-query'; import { generateMuteQuery } from '../../common/generate-mute-query';
import { activeUsersChart } from '../../../../services/chart'; import { activeUsersChart } from '../../../../services/chart';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -89,10 +90,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
errors: { errors: {
@ -105,7 +109,6 @@ export const meta = {
}; };
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {
// TODO どっかにキャッシュ
const m = await fetchMeta(); const m = await fetchMeta();
if (m.disableLocalTimeline && !user.isAdmin && !user.isModerator) { if (m.disableLocalTimeline && !user.isAdmin && !user.isModerator) {
throw new ApiError(meta.errors.stlDisabled); throw new ApiError(meta.errors.stlDisabled);

View File

@ -1,7 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import { ID } from '../../../../misc/cafy-id'; import { ID } from '../../../../misc/cafy-id';
import define from '../../define'; import define from '../../define';
import fetchMeta from '../../../../misc/fetch-meta'; import { fetchMeta } from '../../../../misc/fetch-meta';
import { ApiError } from '../../error'; import { ApiError } from '../../error';
import { Notes } from '../../../../models'; import { Notes } from '../../../../models';
import { generateMuteQuery } from '../../common/generate-mute-query'; 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 { generateVisibilityQuery } from '../../common/generate-visibility-query';
import { activeUsersChart } from '../../../../services/chart'; import { activeUsersChart } from '../../../../services/chart';
import { Brackets } from 'typeorm'; import { Brackets } from 'typeorm';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -63,10 +64,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
errors: { errors: {
@ -79,7 +83,6 @@ export const meta = {
}; };
export default define(meta, async (ps, user) => { export default define(meta, async (ps, user) => {
// TODO どっかにキャッシュ
const m = await fetchMeta(); const m = await fetchMeta();
if (m.disableLocalTimeline) { if (m.disableLocalTimeline) {
if (user == null || (!user.isAdmin && !user.isModerator)) { if (user == null || (!user.isAdmin && !user.isModerator)) {

View File

@ -7,6 +7,7 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
import { generateMuteQuery } from '../../common/generate-mute-query'; import { generateMuteQuery } from '../../common/generate-mute-query';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { Brackets } from 'typeorm'; import { Brackets } from 'typeorm';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -43,10 +44,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
}; };

View File

@ -4,6 +4,7 @@ import define from '../../define';
import { getNote } from '../../common/getters'; import { getNote } from '../../common/getters';
import { ApiError } from '../../error'; import { ApiError } from '../../error';
import { NoteReactions } from '../../../../models'; import { NoteReactions } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -44,9 +45,12 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Reaction' type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'NoteReaction',
} }
}, },

View File

@ -7,6 +7,7 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
import { generateMuteQuery } from '../../common/generate-mute-query'; import { generateMuteQuery } from '../../common/generate-mute-query';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { Notes } from '../../../../models'; import { Notes } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -42,10 +43,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
errors: { errors: {

View File

@ -5,6 +5,7 @@ import { Notes } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query';
import { generateMuteQuery } from '../../common/generate-mute-query'; import { generateMuteQuery } from '../../common/generate-mute-query';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -46,10 +47,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
}; };

View File

@ -6,6 +6,7 @@ import { Notes } from '../../../../models';
import { generateMuteQuery } from '../../common/generate-mute-query'; import { generateMuteQuery } from '../../common/generate-mute-query';
import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query';
import { Brackets } from 'typeorm'; import { Brackets } from 'typeorm';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -81,10 +82,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { 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 (me) generateMuteQuery(query, me);
if (ps.tag) { if (ps.tag) {
query.andWhere(':tag = ANY(note.tags)', { tag: ps.tag }); query.andWhere(':tag = ANY(note.tags)', { tag: ps.tag.toLowerCase() });
} else { } else {
let i = 0; let i = 0;
query.andWhere(new Brackets(qb => { query.andWhere(new Brackets(qb => {
for (const tags of ps.query!) { for (const tags of ps.query!) {
qb.orWhere(new Brackets(qb => { qb.orWhere(new Brackets(qb => {
for (const tag of tags) { 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++; i++;
} }
})); }));

View File

@ -4,6 +4,7 @@ import define from '../../define';
import { ApiError } from '../../error'; import { ApiError } from '../../error';
import { Notes } from '../../../../models'; import { Notes } from '../../../../models';
import { In } from 'typeorm'; import { In } from 'typeorm';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -32,10 +33,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
errors: { errors: {

View File

@ -4,6 +4,7 @@ import define from '../../define';
import { getNote } from '../../common/getters'; import { getNote } from '../../common/getters';
import { ApiError } from '../../error'; import { ApiError } from '../../error';
import { Notes } from '../../../../models'; import { Notes } from '../../../../models';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
stability: 'stable', stability: 'stable',
@ -28,7 +29,9 @@ export const meta = {
}, },
res: { res: {
type: 'Note', type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'Note',
}, },
errors: { errors: {

View File

@ -7,6 +7,7 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
import { generateMuteQuery } from '../../common/generate-mute-query'; import { generateMuteQuery } from '../../common/generate-mute-query';
import { activeUsersChart } from '../../../../services/chart'; import { activeUsersChart } from '../../../../services/chart';
import { Brackets } from 'typeorm'; import { Brackets } from 'typeorm';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -88,10 +89,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
}; };

View File

@ -6,6 +6,7 @@ import { UserLists, UserListJoinings, Notes } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { generateVisibilityQuery } from '../../common/generate-visibility-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query';
import { activeUsersChart } from '../../../../services/chart'; import { activeUsersChart } from '../../../../services/chart';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -94,10 +95,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Note', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Note',
}
}, },
errors: { errors: {

View File

@ -1,6 +1,7 @@
import define from '../define'; import define from '../define';
import { Notes, Users } from '../../../models'; import { Notes, Users } from '../../../models';
import { federationChart, driveChart } from '../../../services/chart'; import { federationChart, driveChart } from '../../../services/chart';
import { bool, types } from '../../../misc/schema';
export const meta = { export const meta = {
requireCredential: false, requireCredential: false,
@ -15,26 +16,32 @@ export const meta = {
}, },
res: { res: {
type: 'object', type: types.object,
optional: bool.false, nullable: bool.false,
properties: { properties: {
notesCount: { notesCount: {
type: 'number', type: types.number,
optional: bool.false, nullable: bool.false,
description: 'The count of all (local/remote) notes of this instance.', description: 'The count of all (local/remote) notes of this instance.',
}, },
originalNotesCount: { originalNotesCount: {
type: 'number', type: types.number,
optional: bool.false, nullable: bool.false,
description: 'The count of all local notes of this instance.', description: 'The count of all local notes of this instance.',
}, },
usersCount: { usersCount: {
type: 'number', type: types.number,
optional: bool.false, nullable: bool.false,
description: 'The count of all (local/remote) accounts of this instance.', description: 'The count of all (local/remote) accounts of this instance.',
}, },
originalUsersCount: { originalUsersCount: {
type: 'number', type: types.number,
optional: bool.false, nullable: bool.false,
description: 'The count of all local accounts of this instance.', description: 'The count of all local accounts of this instance.',
}, },
instances: { instances: {
type: 'number', type: types.number,
optional: bool.false, nullable: bool.false,
description: 'The count of federated instances.', description: 'The count of federated instances.',
}, },
} }
@ -42,7 +49,14 @@ export const meta = {
}; };
export default define(meta, async () => { 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(),
Notes.count({ userHost: null }), Notes.count({ userHost: null }),
Users.count(), Users.count(),
@ -53,6 +67,12 @@ export default define(meta, async () => {
]); ]);
return { return {
notesCount, originalNotesCount, usersCount, originalUsersCount, instances, driveUsageLocal, driveUsageRemote notesCount,
originalNotesCount,
usersCount,
originalUsersCount,
instances,
driveUsageLocal,
driveUsageRemote
}; };
}); });

View File

@ -1,6 +1,6 @@
import $ from 'cafy'; import $ from 'cafy';
import define from '../../define'; import define from '../../define';
import fetchMeta from '../../../../misc/fetch-meta'; import { fetchMeta } from '../../../../misc/fetch-meta';
import { genId } from '../../../../misc/gen-id'; import { genId } from '../../../../misc/gen-id';
import { SwSubscriptions } from '../../../../models'; import { SwSubscriptions } from '../../../../models';
@ -33,7 +33,7 @@ export default define(meta, async (ps, user) => {
publickey: ps.publickey, publickey: ps.publickey,
}); });
const instance = await fetchMeta(); const instance = await fetchMeta(true);
if (exist != null) { if (exist != null) {
return { return {

View File

@ -2,6 +2,7 @@ import $ from 'cafy';
import define from '../define'; import define from '../define';
import { Users } from '../../../models'; import { Users } from '../../../models';
import { generateMuteQueryForUsers } from '../common/generate-mute-query'; import { generateMuteQueryForUsers } from '../common/generate-mute-query';
import { types, bool } from '../../../misc/schema';
export const meta = { export const meta = {
tags: ['users'], tags: ['users'],
@ -53,9 +54,12 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'User', type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'User',
} }
}, },
}; };

View File

@ -5,6 +5,7 @@ import { ApiError } from '../../error';
import { Users, Followings } from '../../../../models'; import { Users, Followings } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { toPunyNullable } from '../../../../misc/convert-host'; import { toPunyNullable } from '../../../../misc/convert-host';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -48,10 +49,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Following', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Following',
}
}, },
errors: { errors: {

View File

@ -5,6 +5,7 @@ import { ApiError } from '../../error';
import { Users, Followings } from '../../../../models'; import { Users, Followings } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query'; import { makePaginationQuery } from '../../common/make-pagination-query';
import { toPunyNullable } from '../../../../misc/convert-host'; import { toPunyNullable } from '../../../../misc/convert-host';
import { types, bool } from '../../../../misc/schema';
export const meta = { export const meta = {
desc: { desc: {
@ -48,10 +49,13 @@ export const meta = {
}, },
res: { res: {
type: 'array', type: types.array,
optional: bool.false, nullable: bool.false,
items: { items: {
type: 'Following', type: types.object,
}, optional: bool.false, nullable: bool.false,
ref: 'Following',
}
}, },
errors: { errors: {

Some files were not shown because too many files have changed in this diff Show More