Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
de57dd7c97 | |||
9985c010bc | |||
f7a328d66e | |||
50598bcefb | |||
9c38e9722a | |||
b241967fb9 | |||
1f86a6d329 | |||
7a6b5b0bfc | |||
e406791b7b | |||
4ce2f596ee | |||
567f71fe61 | |||
70bb5879f9 | |||
cfd2d84b14 | |||
44ab428803 | |||
b34b728fbb | |||
8ada1725bf | |||
873444c3c6 | |||
8bdd4fd061 | |||
f3b518fb62 |
20
CHANGELOG.md
20
CHANGELOG.md
@ -1,6 +1,26 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
11.36.0 (2019/11/24)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* ジョブキューで試行回数等を表示するように
|
||||
* deliverエラー等の同じようなログが複数出てこないように、上でまとめて出すように
|
||||
* deliverエラーのログではキューの詳細情報を格納しないように
|
||||
* inbox/deliverのログに試行回数とキューが作られてからの時間を表示するように
|
||||
* 無駄なAP deliverをしないように
|
||||
* boot: remove setAttribute() calls and translate reload msg
|
||||
|
||||
### 🐛Fixes
|
||||
* メンションの通知が届かない可能性がある問題を修正
|
||||
* ブロックや閉鎖済みインスタンスのステータスが更新されてしまう問題を修正
|
||||
* 「フォロワーを解除」アクティビティを正しく受け取らない問題を修正
|
||||
* inboxのジョブキューが表示されない問題を修正
|
||||
* ローカルにフォロワー限定投稿が流れてくる問題を修正
|
||||
* リモートユーザーのアイコンがサムネイルで表示されない問題を修正
|
||||
* DBとオブジェクトストレージのジョブキューが表示されない問題を修正
|
||||
* エラーが発生したときにサーバーがクラッシュすることがある問題を修正
|
||||
|
||||
11.35.1 (2019/11/05)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
|
@ -1,6 +1,6 @@
|
||||
<a href="https://xn--931a.moe/"><img src="https://github.com/syuilo/misskey/blob/develop/assets/ai-orig.png?raw=true" align="right" height="320px"/></a>
|
||||
|
||||
[](https://misskey.io/)
|
||||
[](https://join.misskey.page/)
|
||||
================================================================
|
||||
|
||||
[](https://circleci.com/gh/syuilo/misskey)
|
||||
@ -10,7 +10,7 @@
|
||||
**A forever evolving, sophisticated microblogging platform.**
|
||||
|
||||
<p align="justify">
|
||||
<a href="https://misskey.io">Misskey</a> is a decentralized microblogging platform born on Earth.
|
||||
<a href="https://join.misskey.page/">Misskey</a> is a decentralized microblogging platform born on Earth.
|
||||
Since it exists within the Fediverse (a universe where various social media platforms are organized),
|
||||
it is mutually linked with other social media platforms.
|
||||
Why don't you take a short break from the hustle and bustle of the city, and dive into a new Internet? <a href="https://join.misskey.page/">Find an instance!</a>
|
||||
|
@ -11,7 +11,7 @@ const cssnano = require('gulp-cssnano');
|
||||
const stylus = require('gulp-stylus');
|
||||
import * as uglifyComposer from 'gulp-uglify/composer';
|
||||
import * as rimraf from 'rimraf';
|
||||
import chalk from 'chalk';
|
||||
import * as chalk from 'chalk';
|
||||
import * as rename from 'gulp-rename';
|
||||
import * as mocha from 'gulp-mocha';
|
||||
import * as replace from 'gulp-replace';
|
||||
|
@ -289,6 +289,7 @@ common:
|
||||
sync: "Synchronizace"
|
||||
save: "Uložit"
|
||||
saved: "Uloženo"
|
||||
preview: "Náhled"
|
||||
room: "Místnost"
|
||||
_room:
|
||||
graphicsQuality: "Kvalita grafiky"
|
||||
@ -399,6 +400,7 @@ common/views/components/games/reversi/reversi.index.vue:
|
||||
invite: "Pozvat"
|
||||
rule: "Jak hrát"
|
||||
mode-invite: "Pozvat"
|
||||
invitations: "Jste pozvaní ke hře!"
|
||||
my-games: "Moje hra"
|
||||
all-games: "Všechny hry"
|
||||
enter-username: "Zadejte své uživatelské jméno"
|
||||
|
@ -258,6 +258,7 @@ common:
|
||||
load-remote-media: "Vis medie-materiale fra en ekstern server"
|
||||
save: "Gem"
|
||||
saved: "Gemt"
|
||||
preview: "Før-visning"
|
||||
search: "Søg"
|
||||
delete: "Slet"
|
||||
loading: "Henter"
|
||||
|
@ -251,6 +251,7 @@ common:
|
||||
load-remote-media: "Zeige Inhalte von fremden Servern"
|
||||
save: "Speichern"
|
||||
saved: "Gespeichert"
|
||||
preview: "Vorschau"
|
||||
search: "Suche"
|
||||
delete: "Löschen"
|
||||
loading: "Laden"
|
||||
|
@ -300,6 +300,7 @@ common:
|
||||
sync: "Sync"
|
||||
save: "Save"
|
||||
saved: "Saved"
|
||||
preview: "Preview"
|
||||
home-profile: "Home profile"
|
||||
deck-profile: "Deck profile"
|
||||
room: "Room"
|
||||
@ -1352,9 +1353,9 @@ admin/views/charts.vue:
|
||||
charts:
|
||||
federation-instances: "The number of instances: increase/decrease"
|
||||
federation-instances-total: "Total number of instances"
|
||||
notes: "The number of posts: increase/decrease (Combined)"
|
||||
local-notes: "The number of posts: increase/decrease (Local)"
|
||||
remote-notes: "The number of posts: increase/decrease (Remote)"
|
||||
notes: "Increase, or decrease in the number of posts (Combined)"
|
||||
local-notes: "Increase, or decrease in the number of posts (Local)"
|
||||
remote-notes: "Increase, or decrease in the number of posts (Remote)"
|
||||
notes-total: "Total posts"
|
||||
users: "The number of users: increase/decrease"
|
||||
users-total: "Total users"
|
||||
@ -1533,7 +1534,7 @@ admin/views/federation.vue:
|
||||
chart-srcs:
|
||||
requests: "Requests"
|
||||
users: "Increase, or decrease in the number of users"
|
||||
users-total: "Total number of users"
|
||||
users-total: "Users in total"
|
||||
notes: "Increase, or decrease in the number of notes"
|
||||
notes-total: "Total number of notes"
|
||||
ff: "Increase of followers"
|
||||
|
@ -201,6 +201,7 @@ common:
|
||||
navbar-position-left: "Izquierda"
|
||||
save: "Guardar"
|
||||
saved: "Guardado"
|
||||
preview: "Vista previa"
|
||||
search: "Buscar"
|
||||
delete: "eliminar"
|
||||
loading: "cargando"
|
||||
|
@ -100,6 +100,11 @@ common:
|
||||
"write:reactions": "Gérer vos réactions"
|
||||
"write:votes": "Vote"
|
||||
"read:pages": "Afficher la page"
|
||||
"write:pages": "Mettre à jour les Pages"
|
||||
"read:page-likes": "Lire les favoris sur les Pages"
|
||||
"write:page-likes": "Mettre à jour les favoris sur les Pages"
|
||||
"read:user-groups": "Voir les groupes d'utilisateur·rice·s"
|
||||
"write:user-groups": "Éditer les groupes des utilisateur·rice·s"
|
||||
empty-timeline-info:
|
||||
follow-users-to-make-your-timeline: "Les utilisateur·rice·s suivant·e·s afficheront leurs publications sur votre fil."
|
||||
explore: "Trouver des utilisateur·rice·s"
|
||||
@ -126,6 +131,7 @@ common:
|
||||
geolocation-alert: "Votre appareil ne prend pas en charge les services de localisation"
|
||||
error: "Erreur"
|
||||
enter-username: "Saisir un nom d'utilisateur"
|
||||
specified-recipient: "Correspondant·e"
|
||||
add-visible-user: "Ajouter un utilisateur"
|
||||
cw-placeholder: "Commenter le contenu (optionnel)"
|
||||
username-prompt: "Saisir un nom d'utilisateur"
|
||||
@ -284,6 +290,7 @@ common:
|
||||
sync: "Synchroniser"
|
||||
save: "Enregistrer"
|
||||
saved: "enregistré"
|
||||
preview: "Prévisualisation"
|
||||
home-profile: "Profil principal"
|
||||
deck-profile: "Profil deck"
|
||||
room: "Pièce"
|
||||
@ -379,6 +386,7 @@ common/views/pages/explore.vue:
|
||||
popular-tags: "Mots-clés populaires"
|
||||
federated: "Du Fédiverse"
|
||||
explore: "Explorer {host}"
|
||||
explore-fediverse: "Explorer le Fédiverse"
|
||||
users-info: "Actuellement, {users} utilisateur·rice·s se sont inscrit ici"
|
||||
common/views/components/reactions-viewer.details.vue:
|
||||
few-users: "{users} ont réagit avec {reaction}"
|
||||
@ -606,7 +614,9 @@ common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "Envoyer une réaction"
|
||||
input-reaction-placeholder: "ou insérez un émoji"
|
||||
common/views/components/emoji-picker.vue:
|
||||
recent-emoji: "Utilisés récemment"
|
||||
custom-emoji: "Émoji personnalisé"
|
||||
no-category: "Sans catégorie"
|
||||
people: "Personnes"
|
||||
animals-and-nature: "Animaux et nature"
|
||||
food-and-drink: "Nourriture et boisson"
|
||||
@ -1350,6 +1360,7 @@ admin/views/drive.vue:
|
||||
marked-as-sensitive: "Marqué comme sensible"
|
||||
unmarked-as-sensitive: "Marqué comme non sensible"
|
||||
clean-remote-files: "Nettoyer le cache des fichiers distants"
|
||||
clean-remote-files-are-you-sure: "Êtes-vous sûr de vouloir effacer tout les fichiers distants mis en cache ?"
|
||||
clean-up: "Nettoyage"
|
||||
admin/views/users.vue:
|
||||
operation: "Actions"
|
||||
@ -1416,6 +1427,7 @@ admin/views/emoji.vue:
|
||||
title: "Ajouter un émoji"
|
||||
name: "Nom de l’émoji"
|
||||
name-desc: "Vous pouvez utiliser les caractères a~z 0~9 _"
|
||||
category: "Catégories"
|
||||
aliases: "Aliases"
|
||||
aliases-desc: "Vous pouvez définir plus d’un, séparés par des espaces."
|
||||
url: "URL de l’image"
|
||||
|
@ -130,6 +130,7 @@ common:
|
||||
timeline: "タイムライン"
|
||||
save: "保存"
|
||||
saved: "保存したで!"
|
||||
preview: "試してみる"
|
||||
search: "検索"
|
||||
delete: "削除"
|
||||
loading: "読み込み中"
|
||||
|
@ -300,6 +300,7 @@ common:
|
||||
sync: "동기화"
|
||||
save: "저장"
|
||||
saved: "저장하였습니다"
|
||||
preview: "미리보기"
|
||||
home-profile: "홈 프로필"
|
||||
deck-profile: "덱 프로필"
|
||||
room: "룸"
|
||||
|
@ -162,6 +162,7 @@ common:
|
||||
note-visibility: "Widoczność wpisów"
|
||||
remember-note-visibility: "Zapamiętaj widoczność wpisów"
|
||||
web-search-engine: "Wyszukiwarka internetowa"
|
||||
web-search-engine-desc: "Wzór: https://www.google.com/?#q={{query}}"
|
||||
paste: "Wklej"
|
||||
line-width: "Szerokości linii"
|
||||
line-width-thin: "Cienka"
|
||||
@ -193,6 +194,7 @@ common:
|
||||
navbar-position-left: "Z lewej"
|
||||
save: "Zapisz"
|
||||
saved: "Zapisano"
|
||||
preview: "Pokaż podgląd"
|
||||
search: "Szukaj"
|
||||
delete: "Usuń"
|
||||
loading: "Ładowanie"
|
||||
|
@ -12,7 +12,7 @@ common:
|
||||
rich-contents: "Посты"
|
||||
rich-contents-desc: "Просто выложи свою идею, актуальные темы и всё, что тебе хочется показать миру. Ты можешь декорировать свои слова, прикреплять свои любимые картинки, отправлять файлы с фильмами и создать голосование - это те вещи, которые ты можешь сделать с помощью Misskey!"
|
||||
reaction: "Реакции"
|
||||
reaction-desc: "Самый лёгкий способ выразить свои эмоции. Misskey позволяет добавлять различные виды реакций к постам других людей. Эмоциональный опыт из Misskey никогда не появится в других социальных сетях, позволяющих только жать “лайки”."
|
||||
reaction-desc: "あなたの気持ちを伝える最も簡単な方法です。Misskeyは、他のユーザーの投稿に様々なリアクションを付けることができます。いちどMisskeyのリアクション機能を体験してしまうと、もう「いいね」の概念しか存在しないSNSには戻れなくなるかもしれません。"
|
||||
ui: "Интерфейс"
|
||||
ui-desc: "Нет такого интерфейса, понравившегося всем. Поэтому у Misskey имеется пользовательский интерфейс, широко настраиваемый под ваши вкусы. Создай себе уникальную домашнюю страницу редактируя, подстраивая оформление ленты и размещая виджеты, которые тоже можно кастомизировать."
|
||||
drive: "Хранилище файлов"
|
||||
|
@ -300,6 +300,7 @@ common:
|
||||
sync: "同步"
|
||||
save: "保存"
|
||||
saved: "已保存"
|
||||
preview: "预览"
|
||||
home-profile: "定制首页数据"
|
||||
deck-profile: "定制Deck数据"
|
||||
room: "房间"
|
||||
|
126
package.json
126
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "11.35.1",
|
||||
"version": "11.36.0",
|
||||
"codename": "daybreak",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -36,12 +36,12 @@
|
||||
"@fortawesome/free-brands-svg-icons": "5.11.2",
|
||||
"@fortawesome/free-regular-svg-icons": "5.11.2",
|
||||
"@fortawesome/free-solid-svg-icons": "5.11.2",
|
||||
"@fortawesome/vue-fontawesome": "0.1.7",
|
||||
"@fortawesome/vue-fontawesome": "0.1.8",
|
||||
"@koa/cors": "3.0.0",
|
||||
"@koa/multer": "2.0.0",
|
||||
"@koa/multer": "2.0.1",
|
||||
"@koa/router": "8.0.2",
|
||||
"@types/bcryptjs": "2.4.2",
|
||||
"@types/bull": "3.10.3",
|
||||
"@types/bull": "3.10.6",
|
||||
"@types/cbor": "2.0.0",
|
||||
"@types/dateformat": "3.0.1",
|
||||
"@types/deep-equal": "1.0.1",
|
||||
@ -56,22 +56,22 @@
|
||||
"@types/js-yaml": "3.12.1",
|
||||
"@types/jsdom": "12.2.4",
|
||||
"@types/katex": "0.10.2",
|
||||
"@types/koa": "2.0.50",
|
||||
"@types/koa-bodyparser": "5.0.2",
|
||||
"@types/koa": "2.0.52",
|
||||
"@types/koa-bodyparser": "4.3.0",
|
||||
"@types/koa-compress": "2.0.9",
|
||||
"@types/koa-cors": "0.0.0",
|
||||
"@types/koa-favicon": "2.0.19",
|
||||
"@types/koa-logger": "3.1.1",
|
||||
"@types/koa-mount": "4.0.0",
|
||||
"@types/koa-send": "4.1.2",
|
||||
"@types/koa-views": "2.0.3",
|
||||
"@types/koa-views": "2.0.4",
|
||||
"@types/koa__cors": "2.2.3",
|
||||
"@types/koa__multer": "2.0.0",
|
||||
"@types/koa__router": "8.0.0",
|
||||
"@types/lolex": "3.1.1",
|
||||
"@types/koa__multer": "2.0.1",
|
||||
"@types/koa__router": "8.0.2",
|
||||
"@types/lolex": "5.1.0",
|
||||
"@types/mocha": "5.2.7",
|
||||
"@types/node": "12.7.12",
|
||||
"@types/nodemailer": "6.2.1",
|
||||
"@types/node": "12.12.12",
|
||||
"@types/nodemailer": "6.2.2",
|
||||
"@types/nprogress": "0.2.0",
|
||||
"@types/oauth": "0.9.1",
|
||||
"@types/parse5": "5.0.2",
|
||||
@ -86,51 +86,51 @@
|
||||
"@types/request": "2.48.3",
|
||||
"@types/request-promise-native": "1.0.17",
|
||||
"@types/request-stats": "3.0.0",
|
||||
"@types/rimraf": "2.0.2",
|
||||
"@types/rimraf": "2.0.3",
|
||||
"@types/seedrandom": "2.4.28",
|
||||
"@types/sharp": "0.22.3",
|
||||
"@types/sharp": "0.23.0",
|
||||
"@types/showdown": "1.9.3",
|
||||
"@types/speakeasy": "2.0.5",
|
||||
"@types/systeminformation": "3.23.1",
|
||||
"@types/systeminformation": "3.54.1",
|
||||
"@types/tinycolor2": "1.4.2",
|
||||
"@types/tmp": "0.1.0",
|
||||
"@types/uuid": "3.4.5",
|
||||
"@types/uuid": "3.4.6",
|
||||
"@types/web-push": "3.3.0",
|
||||
"@types/webpack": "4.39.3",
|
||||
"@types/webpack": "4.41.0",
|
||||
"@types/webpack-stream": "3.2.10",
|
||||
"@types/websocket": "0.0.40",
|
||||
"@types/websocket": "1.0.0",
|
||||
"@types/ws": "6.0.3",
|
||||
"@typescript-eslint/parser": "2.3.3",
|
||||
"@typescript-eslint/parser": "2.8.0",
|
||||
"agentkeepalive": "4.1.0",
|
||||
"animejs": "3.1.0",
|
||||
"apexcharts": "3.10.1",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
"aws-sdk": "2.548.0",
|
||||
"aws-sdk": "2.578.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bootstrap": "4.3.1",
|
||||
"bootstrap-vue": "2.0.4",
|
||||
"bull": "3.11.0",
|
||||
"cafy": "15.1.1",
|
||||
"bootstrap-vue": "2.1.0",
|
||||
"bull": "3.12.1",
|
||||
"cafy": "15.2.0",
|
||||
"cbor": "5.0.1",
|
||||
"chai": "4.2.0",
|
||||
"chalk": "2.4.2",
|
||||
"cli-highlight": "2.1.1",
|
||||
"commander": "3.0.2",
|
||||
"chalk": "3.0.0",
|
||||
"cli-highlight": "2.1.4",
|
||||
"commander": "4.0.1",
|
||||
"content-disposition": "0.5.3",
|
||||
"crc-32": "1.2.0",
|
||||
"css-loader": "3.2.0",
|
||||
"cssnano": "4.1.10",
|
||||
"dateformat": "3.0.3",
|
||||
"deep-equal": "1.1.0",
|
||||
"deep-equal": "1.1.1",
|
||||
"diskusage": "1.1.3",
|
||||
"double-ended-queue": "2.1.0-0",
|
||||
"eslint": "6.5.1",
|
||||
"eslint-plugin-vue": "5.2.3",
|
||||
"eslint": "6.7.0",
|
||||
"eslint-plugin-vue": "6.0.1",
|
||||
"eventemitter3": "4.0.0",
|
||||
"feed": "4.0.0",
|
||||
"file-type": "12.3.0",
|
||||
"file-type": "12.4.0",
|
||||
"fluent-ffmpeg": "2.1.2",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-cssnano": "2.1.3",
|
||||
@ -145,18 +145,18 @@
|
||||
"gulp-util": "3.0.8",
|
||||
"hard-source-webpack-plugin": "0.13.1",
|
||||
"html-minifier": "4.0.0",
|
||||
"http-signature": "1.2.0",
|
||||
"https-proxy-agent": "3.0.0",
|
||||
"http-signature": "1.3.1",
|
||||
"https-proxy-agent": "3.0.1",
|
||||
"insert-text-at-cursor": "0.3.0",
|
||||
"is-root": "2.1.0",
|
||||
"is-svg": "4.2.0",
|
||||
"js-yaml": "3.13.1",
|
||||
"jsdom": "15.1.1",
|
||||
"jsdom": "15.2.1",
|
||||
"json5": "2.1.1",
|
||||
"json5-loader": "3.0.0",
|
||||
"jsrsasign": "8.0.12",
|
||||
"katex": "0.11.1",
|
||||
"koa": "2.10.0",
|
||||
"koa": "2.11.0",
|
||||
"koa-bodyparser": "4.2.1",
|
||||
"koa-compress": "3.0.0",
|
||||
"koa-favicon": "2.0.1",
|
||||
@ -168,21 +168,21 @@
|
||||
"koa-views": "6.2.1",
|
||||
"langmap": "0.0.16",
|
||||
"loader-utils": "1.2.3",
|
||||
"lolex": "4.2.0",
|
||||
"lolex": "5.1.1",
|
||||
"lookup-dns-cache": "2.1.0",
|
||||
"mocha": "6.2.1",
|
||||
"mocha": "6.2.2",
|
||||
"moji": "0.5.1",
|
||||
"ms": "2.1.2",
|
||||
"multer": "1.4.2",
|
||||
"nested-property": "1.0.1",
|
||||
"nested-property": "1.0.2",
|
||||
"node-fetch": "2.6.0",
|
||||
"nodemailer": "6.3.1",
|
||||
"nprogress": "0.2.0",
|
||||
"object-assign-deep": "0.4.0",
|
||||
"os-utils": "0.0.14",
|
||||
"parse5": "5.1.0",
|
||||
"parse5": "5.1.1",
|
||||
"parsimmon": "1.13.0",
|
||||
"pg": "7.12.1",
|
||||
"pg": "7.14.0",
|
||||
"portscanner": "2.2.0",
|
||||
"postcss-loader": "3.0.0",
|
||||
"prismjs": "1.17.1",
|
||||
@ -192,10 +192,10 @@
|
||||
"pug": "2.0.4",
|
||||
"punycode": "2.1.1",
|
||||
"pureimage": "0.1.6",
|
||||
"qrcode": "1.4.2",
|
||||
"qrcode": "1.4.4",
|
||||
"random-seed": "0.3.0",
|
||||
"randomcolor": "0.5.4",
|
||||
"ratelimiter": "3.3.1",
|
||||
"ratelimiter": "3.4.0",
|
||||
"recaptcha-promise": "0.1.3",
|
||||
"reconnecting-websocket": "4.2.0",
|
||||
"redis": "2.8.0",
|
||||
@ -203,14 +203,14 @@
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rename": "1.0.4",
|
||||
"request": "2.88.0",
|
||||
"request-promise-native": "1.0.7",
|
||||
"request-promise-native": "1.0.8",
|
||||
"request-stats": "3.0.0",
|
||||
"require-all": "3.0.0",
|
||||
"rimraf": "3.0.0",
|
||||
"rndstr": "1.0.0",
|
||||
"s-age": "1.1.2",
|
||||
"seedrandom": "3.0.5",
|
||||
"sharp": "0.23.1",
|
||||
"sharp": "0.23.3",
|
||||
"showdown": "1.9.0",
|
||||
"showdown-highlightjs-extension": "0.1.2",
|
||||
"speakeasy": "2.0.0",
|
||||
@ -220,52 +220,52 @@
|
||||
"stylus-loader": "3.0.2",
|
||||
"summaly": "2.3.1",
|
||||
"syslog-pro": "1.0.0",
|
||||
"systeminformation": "4.14.11",
|
||||
"systeminformation": "4.15.3",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"terser-webpack-plugin": "2.1.3",
|
||||
"terser-webpack-plugin": "2.2.1",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.109.0",
|
||||
"three": "0.110.0",
|
||||
"tinycolor2": "1.4.1",
|
||||
"tmp": "0.1.0",
|
||||
"ts-loader": "6.2.0",
|
||||
"ts-node": "8.4.1",
|
||||
"tslint": "5.20.0",
|
||||
"ts-loader": "6.2.1",
|
||||
"ts-node": "8.5.2",
|
||||
"tslint": "5.20.1",
|
||||
"tslint-sonarts": "1.9.0",
|
||||
"typeorm": "0.2.19",
|
||||
"typescript": "3.6.4",
|
||||
"typeorm": "0.2.20",
|
||||
"typescript": "3.7.2",
|
||||
"uglify-es": "3.3.9",
|
||||
"ulid": "2.3.0",
|
||||
"url-loader": "2.2.0",
|
||||
"url-loader": "2.3.0",
|
||||
"uuid": "3.3.3",
|
||||
"v-animate-css": "0.0.3",
|
||||
"v-debounce": "0.1.2",
|
||||
"vue": "2.6.10",
|
||||
"vue-color": "2.7.0",
|
||||
"vue-content-loading": "1.6.0",
|
||||
"vue-cropperjs": "4.0.0",
|
||||
"vue-i18n": "8.14.1",
|
||||
"vue-cropperjs": "4.0.1",
|
||||
"vue-i18n": "8.15.0",
|
||||
"vue-js-modal": "1.3.31",
|
||||
"vue-json-pretty": "1.6.2",
|
||||
"vue-loader": "15.7.1",
|
||||
"vue-loader": "15.7.2",
|
||||
"vue-marquee-text-component": "1.1.1",
|
||||
"vue-prism-component": "1.1.1",
|
||||
"vue-router": "3.1.3",
|
||||
"vue-sequential-entrance": "1.1.3",
|
||||
"vue-style-loader": "4.1.2",
|
||||
"vue-svg-inline-loader": "1.3.3",
|
||||
"vue-svg-inline-loader": "1.4.3",
|
||||
"vue-template-compiler": "2.6.10",
|
||||
"vuedraggable": "2.23.2",
|
||||
"vuewordcloud": "18.7.11",
|
||||
"vuex": "3.1.1",
|
||||
"vuex-persistedstate": "2.5.4",
|
||||
"web-push": "3.4.0",
|
||||
"webpack": "4.41.1",
|
||||
"webpack-cli": "3.3.9",
|
||||
"vuex": "3.1.2",
|
||||
"vuex-persistedstate": "2.7.0",
|
||||
"web-push": "3.4.1",
|
||||
"webpack": "4.41.2",
|
||||
"webpack-cli": "3.3.10",
|
||||
"websocket": "1.0.30",
|
||||
"ws": "7.1.2",
|
||||
"ws": "7.2.0",
|
||||
"xev": "2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fluent-ffmpeg": "2.1.10"
|
||||
"@types/fluent-ffmpeg": "2.1.11"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as cluster from 'cluster';
|
||||
import chalk from 'chalk';
|
||||
import * as chalk from 'chalk';
|
||||
import Xev from 'xev';
|
||||
|
||||
import Logger from '../services/logger';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as os from 'os';
|
||||
import * as cluster from 'cluster';
|
||||
import chalk from 'chalk';
|
||||
import * as chalk from 'chalk';
|
||||
import * as portscanner from 'portscanner';
|
||||
import * as isRoot from 'is-root';
|
||||
|
||||
@ -11,14 +11,15 @@ import { lessThan } from '../prelude/array';
|
||||
import { program } from '../argv';
|
||||
import { showMachineInfo } from '../misc/show-machine-info';
|
||||
import { initDb } from '../db/postgre';
|
||||
import * as meta from '../meta.json';
|
||||
|
||||
const logger = new Logger('core', 'cyan');
|
||||
const bootLogger = logger.createSubLogger('boot', 'magenta', false);
|
||||
|
||||
function greet(config: Config) {
|
||||
function greet() {
|
||||
if (!program.quiet) {
|
||||
//#region Misskey logo
|
||||
const v = `v${config.version}`;
|
||||
const v = `v${meta.version}`;
|
||||
console.log(' _____ _ _ ');
|
||||
console.log(' | |_|___ ___| |_ ___ _ _ ');
|
||||
console.log(' | | | | |_ -|_ -| \'_| -_| | |');
|
||||
@ -34,7 +35,7 @@ function greet(config: Config) {
|
||||
}
|
||||
|
||||
bootLogger.info('Welcome to Misskey!');
|
||||
bootLogger.info(`Misskey v${config.version}`, null, true);
|
||||
bootLogger.info(`Misskey v${meta.version}`, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,11 +45,11 @@ export async function masterMain() {
|
||||
let config!: Config;
|
||||
|
||||
try {
|
||||
greet();
|
||||
|
||||
// initialize app
|
||||
config = await init();
|
||||
|
||||
greet(config);
|
||||
|
||||
if (config.port == null || Number.isNaN(config.port)) {
|
||||
bootLogger.error('The port is not configured. Please configure port.', null, true);
|
||||
process.exit(1);
|
||||
|
@ -48,14 +48,15 @@
|
||||
</ui-select>
|
||||
</ui-horizon-group>
|
||||
<sequential-entrance animation="entranceFromTop" delay="25">
|
||||
<div class="xvvuvgsv" v-for="job in jobs">
|
||||
<div class="xvvuvgsv" v-for="job in jobs" :key="job.id">
|
||||
<b>{{ job.id }}</b>
|
||||
<template v-if="domain === 'deliver'">
|
||||
<span>{{ job.data.to }}</span>
|
||||
</template>
|
||||
<template v-if="domain === 'inbox'">
|
||||
<span>{{ job.activity.id }}</span>
|
||||
<span>{{ job.data.activity.id }}</span>
|
||||
</template>
|
||||
<span>{{ `(${job.attempts}/${job.maxAttempts}, ${Math.floor((jobsFetched - job.timestamp) / 1000 / 60)}min)` }}</span>
|
||||
</div>
|
||||
</sequential-entrance>
|
||||
<ui-info v-if="jobs.length == jobsLimit">{{ $t('result-is-truncated', { n: jobsLimit }) }}</ui-info>
|
||||
@ -84,6 +85,7 @@ export default Vue.extend({
|
||||
chartLimit: 200,
|
||||
jobs: [],
|
||||
jobsLimit: 50,
|
||||
jobsFetched: Date.now(),
|
||||
domain: 'deliver',
|
||||
state: 'delayed',
|
||||
faTasks, faPaperPlane, faInbox, faChartBar, faDatabase, faCloud
|
||||
@ -140,6 +142,7 @@ export default Vue.extend({
|
||||
state: this.state,
|
||||
limit: this.jobsLimit
|
||||
}).then(jobs => {
|
||||
this.jobsFetched = Date.now(),
|
||||
this.jobs = jobs;
|
||||
});
|
||||
},
|
||||
@ -149,7 +152,8 @@ export default Vue.extend({
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.xvvuvgsv
|
||||
> b
|
||||
margin-right 16px
|
||||
margin-left -6px
|
||||
> b, span
|
||||
margin 0 6px
|
||||
|
||||
</style>
|
||||
|
@ -72,13 +72,17 @@
|
||||
//#region Fetch locale data
|
||||
const cachedLocale = localStorage.getItem('locale');
|
||||
const localeKey = localStorage.getItem('localeKey');
|
||||
let localeData = null;
|
||||
|
||||
if (cachedLocale == null || localeKey != `${ver}.${lang}`) {
|
||||
const locale = await fetch(`/assets/locales/${lang}.json?ver=${ver}`)
|
||||
.then(response => response.json());
|
||||
localeData = locale;
|
||||
|
||||
localStorage.setItem('locale', JSON.stringify(locale));
|
||||
localStorage.setItem('localeKey', `${ver}.${lang}`);
|
||||
} else {
|
||||
localeData = JSON.parse(cachedLocale);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
@ -99,8 +103,7 @@
|
||||
// If mobile, insert the viewport meta tag
|
||||
if (isMobile) {
|
||||
const viewport = document.getElementsByName("viewport").item(0);
|
||||
viewport.setAttribute('content',
|
||||
`${viewport.getAttribute('content')},minimum-scale=1,maximum-scale=1,user-scalable=no`);
|
||||
viewport.content = `${viewport.content},minimum-scale=1,maximum-scale=1,user-scalable=no`;
|
||||
head.appendChild(viewport);
|
||||
}
|
||||
|
||||
@ -113,9 +116,9 @@
|
||||
// Note: 'async' make it possible to load the script asyncly.
|
||||
// 'defer' make it possible to run the script when the dom loaded.
|
||||
const script = document.createElement('script');
|
||||
script.setAttribute('src', `/assets/${app}.${ver}.js`);
|
||||
script.setAttribute('async', 'true');
|
||||
script.setAttribute('defer', 'true');
|
||||
script.src = `/assets/${app}.${ver}.js`;
|
||||
script.async = true;
|
||||
script.defer = true;
|
||||
head.appendChild(script);
|
||||
|
||||
// 3秒経ってもスクリプトがロードされない場合はバージョンが古くて
|
||||
@ -138,10 +141,10 @@
|
||||
localStorage.setItem('v', meta.version);
|
||||
|
||||
alert(
|
||||
'Misskeyの新しいバージョンがあります。ページを再度読み込みします。' +
|
||||
'\n\n' +
|
||||
'New version of Misskey available. The page will be reloaded.');
|
||||
|
||||
localeData.common._settings["update-available"] +
|
||||
'\n' +
|
||||
localeData.common._settings["update-available-desc"]
|
||||
);
|
||||
refresh();
|
||||
}
|
||||
}, 3000);
|
||||
|
@ -76,13 +76,15 @@ export default Vue.extend({
|
||||
this.$root.api('ap/show', {
|
||||
uri: acct
|
||||
}).then((res: { type: string, object: any }) => {
|
||||
if (res.type !== 'User') {
|
||||
if (res.type === 'User') {
|
||||
this.user = res.object;
|
||||
} else if (res.type === 'Note') {
|
||||
this.$router.replace(`/notes/${res.object.id}`);
|
||||
} else {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: 'acct is not an user'
|
||||
text: 'Not supported'
|
||||
});
|
||||
} else {
|
||||
this.user = res.object;
|
||||
}
|
||||
}).catch((e: any) => {
|
||||
this.$root.dialog({
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as fs from 'fs';
|
||||
import * as request from 'request';
|
||||
import config from '../config';
|
||||
import chalk from 'chalk';
|
||||
import * as chalk from 'chalk';
|
||||
import Logger from '../services/logger';
|
||||
|
||||
export async function downloadUrl(url: string, path: string) {
|
||||
|
15
src/queue/get-job-info.ts
Normal file
15
src/queue/get-job-info.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import * as Bull from 'bull';
|
||||
|
||||
export function getJobInfo(job: Bull.Job, increment = false) {
|
||||
const age = Date.now() - job.timestamp;
|
||||
|
||||
const formated = age > 60000 ? `${Math.floor(age / 1000 / 60)}m`
|
||||
: age > 10000 ? `${Math.floor(age / 1000)}s`
|
||||
: `${age}ms`;
|
||||
|
||||
// onActiveとかonCompletedのattemptsMadeがなぜか0始まりなのでインクリメントする
|
||||
const currentAttempts = job.attemptsMade + (increment ? 1 : 0);
|
||||
const maxAttempts = job.opts ? job.opts.attempts : 0;
|
||||
|
||||
return `id=${job.id} attempts=${currentAttempts}/${maxAttempts} age=${formated}`;
|
||||
}
|
@ -11,6 +11,7 @@ import processDb from './processors/db';
|
||||
import procesObjectStorage from './processors/object-storage';
|
||||
import { queueLogger } from './logger';
|
||||
import { DriveFile } from '../models/entities/drive-file';
|
||||
import { getJobInfo } from './get-job-info';
|
||||
|
||||
function initializeQueue(name: string) {
|
||||
return new Queue(name, {
|
||||
@ -44,19 +45,19 @@ const objectStorageLogger = queueLogger.createSubLogger('objectStorage');
|
||||
|
||||
deliverQueue
|
||||
.on('waiting', (jobId) => deliverLogger.debug(`waiting id=${jobId}`))
|
||||
.on('active', (job) => deliverLogger.debug(`active id=${job.id} to=${job.data.to}`))
|
||||
.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) id=${job.id} to=${job.data.to}`))
|
||||
.on('failed', (job, err) => deliverLogger.warn(`failed(${err}) id=${job.id} to=${job.data.to}`, { job, e: renderError(err) }))
|
||||
.on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
|
||||
.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
|
||||
.on('failed', (job, err) => deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`))
|
||||
.on('error', (job: any, err: Error) => deliverLogger.error(`error ${err}`, { job, e: renderError(err) }))
|
||||
.on('stalled', (job) => deliverLogger.warn(`stalled id=${job.id} to=${job.data.to}`));
|
||||
.on('stalled', (job) => deliverLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`));
|
||||
|
||||
inboxQueue
|
||||
.on('waiting', (jobId) => inboxLogger.debug(`waiting id=${jobId}`))
|
||||
.on('active', (job) => inboxLogger.debug(`active id=${job.id}`))
|
||||
.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) id=${job.id}`))
|
||||
.on('failed', (job, err) => inboxLogger.warn(`failed(${err}) id=${job.id} activity=${job.data.activity ? job.data.activity.id : 'none'}`, { job, e: renderError(err) }))
|
||||
.on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`))
|
||||
.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`))
|
||||
.on('failed', (job, err) => inboxLogger.warn(`failed(${err}) ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`, { job, e: renderError(err) }))
|
||||
.on('error', (job: any, err: Error) => inboxLogger.error(`error ${err}`, { job, e: renderError(err) }))
|
||||
.on('stalled', (job) => inboxLogger.warn(`stalled id=${job.id} activity=${job.data.activity ? job.data.activity.id : 'none'}`));
|
||||
.on('stalled', (job) => inboxLogger.warn(`stalled ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`));
|
||||
|
||||
dbQueue
|
||||
.on('waiting', (jobId) => dbLogger.debug(`waiting id=${jobId}`))
|
||||
|
@ -5,6 +5,8 @@ import Logger from '../../services/logger';
|
||||
import { Instances } from '../../models';
|
||||
import { instanceChart } from '../../services/chart';
|
||||
import { fetchNodeinfo } from '../../services/fetch-nodeinfo';
|
||||
import { fetchMeta } from '../../misc/fetch-meta';
|
||||
import { toPuny } from '../../misc/convert-host';
|
||||
|
||||
const logger = new Logger('deliver');
|
||||
|
||||
@ -13,6 +15,23 @@ let latest: string | null = null;
|
||||
export default async (job: Bull.Job) => {
|
||||
const { host } = new URL(job.data.to);
|
||||
|
||||
// ブロックしてたら中断
|
||||
const meta = await fetchMeta();
|
||||
if (meta.blockedHosts.includes(toPuny(host))) {
|
||||
return 'skip (blocked)';
|
||||
}
|
||||
|
||||
// closedなら中断
|
||||
const closedHosts = await Instances.find({
|
||||
where: {
|
||||
isMarkedAsClosed: true
|
||||
},
|
||||
cache: 60 * 1000
|
||||
});
|
||||
if (closedHosts.map(x => x.host).includes(toPuny(host))) {
|
||||
return 'skip (closed)';
|
||||
}
|
||||
|
||||
try {
|
||||
if (latest !== (latest = JSON.stringify(job.data.content, null, 2))) {
|
||||
logger.debug(`delivering ${latest}`);
|
||||
@ -48,8 +67,6 @@ export default async (job: Bull.Job) => {
|
||||
});
|
||||
|
||||
if (res != null && res.hasOwnProperty('statusCode')) {
|
||||
logger.warn(`deliver failed: ${res.statusCode} ${res.statusMessage} to=${job.data.to}`);
|
||||
|
||||
// 4xx
|
||||
if (res.statusCode >= 400 && res.statusCode < 500) {
|
||||
// HTTPステータスコード4xxはクライアントエラーであり、それはつまり
|
||||
@ -61,7 +78,6 @@ export default async (job: Bull.Job) => {
|
||||
throw `${res.statusCode} ${res.statusMessage}`;
|
||||
} else {
|
||||
// DNS error, socket error, timeout ...
|
||||
logger.warn(`deliver failed: ${res} to=${job.data.to}`);
|
||||
throw res;
|
||||
}
|
||||
}
|
||||
|
131
src/remote/activitypub/deliver-manager.ts
Normal file
131
src/remote/activitypub/deliver-manager.ts
Normal file
@ -0,0 +1,131 @@
|
||||
import { Users, Followings } from '../../models';
|
||||
import { ILocalUser, IRemoteUser } from '../../models/entities/user';
|
||||
import { deliver } from '../../queue';
|
||||
|
||||
//#region types
|
||||
interface IRecipe {
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface IFollowersRecipe extends IRecipe {
|
||||
type: 'Followers';
|
||||
}
|
||||
|
||||
interface IDirectRecipe extends IRecipe {
|
||||
type: 'Direct';
|
||||
to: IRemoteUser;
|
||||
}
|
||||
|
||||
const isFollowers = (recipe: any): recipe is IFollowersRecipe =>
|
||||
recipe.type === 'Followers';
|
||||
|
||||
const isDirect = (recipe: any): recipe is IDirectRecipe =>
|
||||
recipe.type === 'Direct';
|
||||
//#endregion
|
||||
|
||||
export default class DeliverManager {
|
||||
private actor: ILocalUser;
|
||||
private activity: any;
|
||||
private recipes: IRecipe[] = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param actor Actor
|
||||
* @param activity Activity to deliver
|
||||
*/
|
||||
constructor(actor: ILocalUser, activity: any) {
|
||||
this.actor = actor;
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add recipe for followers deliver
|
||||
*/
|
||||
public addFollowersRecipe() {
|
||||
const deliver = {
|
||||
type: 'Followers'
|
||||
} as IFollowersRecipe;
|
||||
|
||||
this.addRecipe(deliver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add recipe for direct deliver
|
||||
* @param to To
|
||||
*/
|
||||
public addDirectRecipe(to: IRemoteUser) {
|
||||
const recipe = {
|
||||
type: 'Direct',
|
||||
to
|
||||
} as IDirectRecipe;
|
||||
|
||||
this.addRecipe(recipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add recipe
|
||||
* @param recipe Recipe
|
||||
*/
|
||||
public addRecipe(recipe: IRecipe) {
|
||||
this.recipes.push(recipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute delivers
|
||||
*/
|
||||
public async execute() {
|
||||
if (!Users.isLocalUser(this.actor)) return;
|
||||
|
||||
const inboxes: string[] = [];
|
||||
|
||||
// build inbox list
|
||||
for (const recipe of this.recipes) {
|
||||
if (isFollowers(recipe)) {
|
||||
// followers deliver
|
||||
const followers = await Followings.find({
|
||||
followeeId: this.actor.id
|
||||
});
|
||||
|
||||
for (const following of followers) {
|
||||
if (Followings.isRemoteFollower(following)) {
|
||||
const inbox = following.followerSharedInbox || following.followerInbox;
|
||||
if (!inboxes.includes(inbox)) inboxes.push(inbox);
|
||||
}
|
||||
}
|
||||
} else if (isDirect(recipe)) {
|
||||
// direct deliver
|
||||
const inbox = recipe.to.inbox;
|
||||
if (inbox && !inboxes.includes(inbox)) inboxes.push(inbox);
|
||||
}
|
||||
}
|
||||
|
||||
// deliver
|
||||
for (const inbox of inboxes) {
|
||||
deliver(this.actor, this.activity, inbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#region Utilities
|
||||
/**
|
||||
* Deliver activity to followers
|
||||
* @param activity Activity
|
||||
* @param from Followee
|
||||
*/
|
||||
export async function deliverToFollowers(actor: ILocalUser, activity: any) {
|
||||
const manager = new DeliverManager(actor, activity);
|
||||
manager.addFollowersRecipe();
|
||||
await manager.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deliver activity to user
|
||||
* @param activity Activity
|
||||
* @param to Target user
|
||||
*/
|
||||
export async function deliverToUser(actor: ILocalUser, activity: any, to: IRemoteUser) {
|
||||
const manager = new DeliverManager(actor, activity);
|
||||
manager.addDirectRecipe(to);
|
||||
await manager.execute();
|
||||
}
|
||||
//#endregion
|
@ -13,14 +13,10 @@ export default async (actor: IRemoteUser, activity: IAccept): Promise<void> => {
|
||||
|
||||
const resolver = new Resolver();
|
||||
|
||||
let object;
|
||||
|
||||
try {
|
||||
object = await resolver.resolve(activity.object);
|
||||
} catch (e) {
|
||||
const object = await resolver.resolve(activity.object).catch(e => {
|
||||
logger.error(`Resolution failed: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
switch (object.type) {
|
||||
case 'Follow':
|
||||
|
@ -13,14 +13,10 @@ export default async (actor: IRemoteUser, activity: IAnnounce): Promise<void> =>
|
||||
|
||||
const resolver = new Resolver();
|
||||
|
||||
let object;
|
||||
|
||||
try {
|
||||
object = await resolver.resolve(activity.object);
|
||||
} catch (e) {
|
||||
const object = await resolver.resolve(activity.object).catch(e => {
|
||||
logger.error(`Resolution failed: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
if (validPost.includes(object.type)) {
|
||||
announceNote(resolver, actor, activity, object);
|
||||
|
@ -13,14 +13,10 @@ export default async (actor: IRemoteUser, activity: ICreate): Promise<void> => {
|
||||
|
||||
const resolver = new Resolver();
|
||||
|
||||
let object;
|
||||
|
||||
try {
|
||||
object = await resolver.resolve(activity.object);
|
||||
} catch (e) {
|
||||
const object = await resolver.resolve(activity.object).catch(e => {
|
||||
logger.error(`Resolution failed: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
if (validPost.includes(object.type)) {
|
||||
createNote(resolver, actor, object);
|
||||
|
@ -13,14 +13,10 @@ export default async (actor: IRemoteUser, activity: IReject): Promise<void> => {
|
||||
|
||||
const resolver = new Resolver();
|
||||
|
||||
let object;
|
||||
|
||||
try {
|
||||
object = await resolver.resolve(activity.object);
|
||||
} catch (e) {
|
||||
const object = await resolver.resolve(activity.object).catch(e => {
|
||||
logger.error(`Resolution failed: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
switch (object.type) {
|
||||
case 'Follow':
|
||||
|
@ -20,14 +20,10 @@ export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => {
|
||||
|
||||
const resolver = new Resolver();
|
||||
|
||||
let object;
|
||||
|
||||
try {
|
||||
object = await resolver.resolve(activity.object);
|
||||
} catch (e) {
|
||||
const object = await resolver.resolve(activity.object).catch(e => {
|
||||
logger.error(`Resolution failed: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
switch (object.type) {
|
||||
case 'Follow':
|
||||
|
@ -4,7 +4,6 @@ import config from '../../../config';
|
||||
import Resolver from '../resolver';
|
||||
import { resolveImage } from './image';
|
||||
import { isCollectionOrOrderedCollection, isCollection, IPerson, getApId } from '../type';
|
||||
import { DriveFile } from '../../../models/entities/drive-file';
|
||||
import { fromHtml } from '../../../mfm/fromHtml';
|
||||
import { resolveNote, extractEmojis } from './note';
|
||||
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
||||
@ -201,18 +200,18 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
||||
updateUsertags(user!, tags);
|
||||
|
||||
//#region アバターとヘッダー画像をフェッチ
|
||||
const [avatar, banner] = (await Promise.all<DriveFile | null>([
|
||||
const [avatar, banner] = await Promise.all([
|
||||
person.icon,
|
||||
person.image
|
||||
].map(img =>
|
||||
img == null
|
||||
? Promise.resolve(null)
|
||||
: resolveImage(user!, img).catch(() => null)
|
||||
)));
|
||||
));
|
||||
|
||||
const avatarId = avatar ? avatar.id : null;
|
||||
const bannerId = banner ? banner.id : null;
|
||||
const avatarUrl = avatar ? DriveFiles.getPublicUrl(avatar) : null;
|
||||
const avatarUrl = avatar ? DriveFiles.getPublicUrl(avatar, true) : null;
|
||||
const bannerUrl = banner ? DriveFiles.getPublicUrl(banner) : null;
|
||||
const avatarColor = avatar && avatar.properties.avgColor ? avatar.properties.avgColor : null;
|
||||
const bannerColor = banner && banner.properties.avgColor ? banner.properties.avgColor : null;
|
||||
@ -290,14 +289,14 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
||||
logger.info(`Updating the Person: ${person.id}`);
|
||||
|
||||
// アバターとヘッダー画像をフェッチ
|
||||
const [avatar, banner] = (await Promise.all<DriveFile | null>([
|
||||
const [avatar, banner] = await Promise.all([
|
||||
person.icon,
|
||||
person.image
|
||||
].map(img =>
|
||||
img == null
|
||||
? Promise.resolve(null)
|
||||
: resolveImage(exist, img).catch(() => null)
|
||||
)));
|
||||
));
|
||||
|
||||
// カスタム絵文字取得
|
||||
const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => {
|
||||
@ -326,7 +325,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
||||
|
||||
if (avatar) {
|
||||
updates.avatarId = avatar.id;
|
||||
updates.avatarUrl = DriveFiles.getPublicUrl(avatar);
|
||||
updates.avatarUrl = DriveFiles.getPublicUrl(avatar, true);
|
||||
updates.avatarColor = avatar.properties.avgColor ? avatar.properties.avgColor : null;
|
||||
}
|
||||
|
||||
|
@ -6,15 +6,10 @@ import * as cache from 'lookup-dns-cache';
|
||||
import config from '../../config';
|
||||
import { ILocalUser } from '../../models/entities/user';
|
||||
import { publishApLogStream } from '../../services/stream';
|
||||
import { apLogger } from './logger';
|
||||
import { UserKeypairs, Instances } from '../../models';
|
||||
import { fetchMeta } from '../../misc/fetch-meta';
|
||||
import { toPuny } from '../../misc/convert-host';
|
||||
import { UserKeypairs } from '../../models';
|
||||
import { ensure } from '../../prelude/ensure';
|
||||
import * as httpsProxyAgent from 'https-proxy-agent';
|
||||
|
||||
export const logger = apLogger.createSubLogger('deliver');
|
||||
|
||||
const agent = config.proxy
|
||||
? new httpsProxyAgent(config.proxy)
|
||||
: new https.Agent({
|
||||
@ -24,28 +19,7 @@ const agent = config.proxy
|
||||
export default async (user: ILocalUser, url: string, object: any) => {
|
||||
const timeout = 10 * 1000;
|
||||
|
||||
const { protocol, host, hostname, port, pathname, search } = new URL(url);
|
||||
|
||||
// ブロックしてたら中断
|
||||
const meta = await fetchMeta();
|
||||
if (meta.blockedHosts.includes(toPuny(host))) {
|
||||
logger.info(`skip (blocked) ${url}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// closedなら中断
|
||||
const closedHosts = await Instances.find({
|
||||
where: {
|
||||
isMarkedAsClosed: true
|
||||
},
|
||||
cache: 60 * 1000
|
||||
});
|
||||
if (closedHosts.map(x => x.host).includes(toPuny(host))) {
|
||||
logger.info(`skip (closed) ${url}`);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`--> ${url}`);
|
||||
const { protocol, hostname, port, pathname, search } = new URL(url);
|
||||
|
||||
const data = JSON.stringify(object);
|
||||
|
||||
@ -73,10 +47,8 @@ export default async (user: ILocalUser, url: string, object: any) => {
|
||||
}
|
||||
}, res => {
|
||||
if (res.statusCode! >= 400) {
|
||||
logger.warn(`${url} --> ${res.statusCode}`);
|
||||
reject(res);
|
||||
} else {
|
||||
logger.succ(`${url} --> ${res.statusCode}`);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
@ -2,7 +2,7 @@ import webFinger from './webfinger';
|
||||
import config from '../config';
|
||||
import { createPerson, updatePerson } from './activitypub/models/person';
|
||||
import { remoteLogger } from './logger';
|
||||
import chalk from 'chalk';
|
||||
import * as chalk from 'chalk';
|
||||
import { User, IRemoteUser } from '../models/entities/user';
|
||||
import { Users } from '../models';
|
||||
import { toPuny } from '../misc/convert-host';
|
||||
|
@ -5,7 +5,7 @@ import authenticate from './authenticate';
|
||||
import call from './call';
|
||||
import { ApiError } from './error';
|
||||
|
||||
export default (endpoint: IEndpoint, ctx: Koa.BaseContext) => new Promise((res) => {
|
||||
export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res) => {
|
||||
const body = ctx.request.body;
|
||||
|
||||
const reply = (x?: any, y?: ApiError) => {
|
||||
|
@ -6,7 +6,7 @@ import { Signins } from '../../../models';
|
||||
import { genId } from '../../../misc/gen-id';
|
||||
import { publishMainStream } from '../../../services/stream';
|
||||
|
||||
export default function(ctx: Koa.BaseContext, user: ILocalUser, redirect = false) {
|
||||
export default function(ctx: Koa.Context, user: ILocalUser, redirect = false) {
|
||||
if (redirect) {
|
||||
//#region Cookie
|
||||
const expires = 1000 * 60 * 60 * 24 * 365; // One Year
|
||||
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../../define';
|
||||
import { deliverQueue, inboxQueue } from '../../../../../queue';
|
||||
import { deliverQueue, inboxQueue, dbQueue, objectStorageQueue } from '../../../../../queue';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
@ -10,11 +10,11 @@ export const meta = {
|
||||
|
||||
params: {
|
||||
domain: {
|
||||
validator: $.str,
|
||||
validator: $.str.or(['deliver', 'inbox', 'db', 'objectStorage']),
|
||||
},
|
||||
|
||||
state: {
|
||||
validator: $.str,
|
||||
validator: $.str.or(['active', 'waiting', 'delayed']),
|
||||
},
|
||||
|
||||
limit: {
|
||||
@ -28,13 +28,22 @@ export default define(meta, async (ps) => {
|
||||
const queue =
|
||||
ps.domain === 'deliver' ? deliverQueue :
|
||||
ps.domain === 'inbox' ? inboxQueue :
|
||||
ps.domain === 'db' ? dbQueue :
|
||||
ps.domain === 'objectStorage' ? objectStorageQueue :
|
||||
null as never;
|
||||
|
||||
const jobs = await queue.getJobs([ps.state], 0, ps.limit!);
|
||||
|
||||
return jobs.map(job => ({
|
||||
return jobs.map(job => {
|
||||
const data = job.data;
|
||||
delete data.content;
|
||||
delete data.user;
|
||||
return {
|
||||
id: job.id,
|
||||
data: job.data,
|
||||
data,
|
||||
attempts: job.attemptsMade,
|
||||
}));
|
||||
maxAttempts: job.opts ? job.opts.attempts : 0,
|
||||
timestamp: job.timestamp,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
@ -10,7 +10,7 @@ import { ensure } from '../../../prelude/ensure';
|
||||
import { verifyLogin, hash } from '../2fa';
|
||||
import { randomBytes } from 'crypto';
|
||||
|
||||
export default async (ctx: Koa.BaseContext) => {
|
||||
export default async (ctx: Koa.Context) => {
|
||||
ctx.set('Access-Control-Allow-Origin', config.url);
|
||||
ctx.set('Access-Control-Allow-Credentials', 'true');
|
||||
|
||||
|
@ -15,8 +15,8 @@ import { UserProfile } from '../../../models/entities/user-profile';
|
||||
import { getConnection } from 'typeorm';
|
||||
import { UsedUsername } from '../../../models/entities/used-username';
|
||||
|
||||
export default async (ctx: Koa.BaseContext) => {
|
||||
const body = ctx.request.body as any;
|
||||
export default async (ctx: Koa.Context) => {
|
||||
const body = ctx.request.body;
|
||||
|
||||
const instance = await fetchMeta(true);
|
||||
|
||||
|
@ -12,11 +12,11 @@ import { Users, UserProfiles } from '../../../models';
|
||||
import { ILocalUser } from '../../../models/entities/user';
|
||||
import { ensure } from '../../../prelude/ensure';
|
||||
|
||||
function getUserToken(ctx: Koa.BaseContext) {
|
||||
function getUserToken(ctx: Koa.Context) {
|
||||
return ((ctx.headers['cookie'] || '').match(/i=(\w+)/) || [null, null])[1];
|
||||
}
|
||||
|
||||
function compareOrigin(ctx: Koa.BaseContext) {
|
||||
function compareOrigin(ctx: Koa.Context) {
|
||||
function normalizeUrl(url: string) {
|
||||
return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : '';
|
||||
}
|
||||
|
@ -12,11 +12,11 @@ import { Users, UserProfiles } from '../../../models';
|
||||
import { ILocalUser } from '../../../models/entities/user';
|
||||
import { ensure } from '../../../prelude/ensure';
|
||||
|
||||
function getUserToken(ctx: Koa.BaseContext) {
|
||||
function getUserToken(ctx: Koa.Context) {
|
||||
return ((ctx.headers['cookie'] || '').match(/i=(\w+)/) || [null, null])[1];
|
||||
}
|
||||
|
||||
function compareOrigin(ctx: Koa.BaseContext) {
|
||||
function compareOrigin(ctx: Koa.Context) {
|
||||
function normalizeUrl(url: string) {
|
||||
return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : '';
|
||||
}
|
||||
|
@ -11,11 +11,11 @@ import { Users, UserProfiles } from '../../../models';
|
||||
import { ILocalUser } from '../../../models/entities/user';
|
||||
import { ensure } from '../../../prelude/ensure';
|
||||
|
||||
function getUserToken(ctx: Koa.BaseContext) {
|
||||
function getUserToken(ctx: Koa.Context) {
|
||||
return ((ctx.headers['cookie'] || '').match(/i=(\w+)/) || [null, null])[1];
|
||||
}
|
||||
|
||||
function compareOrigin(ctx: Koa.BaseContext) {
|
||||
function compareOrigin(ctx: Koa.Context) {
|
||||
function normalizeUrl(url: string) {
|
||||
return url.endsWith('/') ? url.substr(0, url.length - 1) : url;
|
||||
}
|
||||
|
@ -25,17 +25,8 @@ export default class extends Channel {
|
||||
@autobind
|
||||
private async onNote(note: PackedNote) {
|
||||
if ((note.user as PackedUser).host !== null) return;
|
||||
if (note.visibility === 'home') return;
|
||||
if (note.visibility !== 'public') return;
|
||||
|
||||
if (['followers', 'specified'].includes(note.visibility)) {
|
||||
note = await Notes.pack(note.id, this.user, {
|
||||
detail: true
|
||||
});
|
||||
|
||||
if (note.isHidden) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// リプライなら再pack
|
||||
if (note.replyId != null) {
|
||||
note.reply = await Notes.pack(note.replyId, this.user, {
|
||||
@ -48,7 +39,6 @@ export default class extends Channel {
|
||||
detail: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
||||
if (shouldMuteThisNote(note, this.muting)) return;
|
||||
|
@ -8,12 +8,12 @@ import { InternalStorage } from '../../services/drive/internal-storage';
|
||||
|
||||
const assets = `${__dirname}/../../server/file/assets/`;
|
||||
|
||||
const commonReadableHandlerGenerator = (ctx: Koa.BaseContext) => (e: Error): void => {
|
||||
const commonReadableHandlerGenerator = (ctx: Koa.Context) => (e: Error): void => {
|
||||
serverLogger.error(e);
|
||||
ctx.status = 500;
|
||||
};
|
||||
|
||||
export default async function(ctx: Koa.BaseContext) {
|
||||
export default async function(ctx: Koa.Context) {
|
||||
const key = ctx.params.key;
|
||||
|
||||
// Fetch drive file
|
||||
|
@ -6,7 +6,7 @@ import { createTemp } from '../../misc/create-temp';
|
||||
import { downloadUrl } from '../../misc/donwload-url';
|
||||
import { detectMine } from '../../misc/detect-mine';
|
||||
|
||||
export async function proxyMedia(ctx: Koa.BaseContext) {
|
||||
export async function proxyMedia(ctx: Koa.Context) {
|
||||
const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url;
|
||||
|
||||
// Create temp file
|
||||
|
@ -2,7 +2,7 @@ import * as Koa from 'koa';
|
||||
import * as manifest from '../../client/assets/manifest.json';
|
||||
import { fetchMeta } from '../../misc/fetch-meta';
|
||||
|
||||
module.exports = async (ctx: Koa.BaseContext) => {
|
||||
module.exports = async (ctx: Koa.Context) => {
|
||||
const json = JSON.parse(JSON.stringify(manifest));
|
||||
|
||||
const instance = await fetchMeta(true);
|
||||
|
@ -8,7 +8,7 @@ import { query } from '../../prelude/url';
|
||||
|
||||
const logger = new Logger('url-preview');
|
||||
|
||||
module.exports = async (ctx: Koa.BaseContext) => {
|
||||
module.exports = async (ctx: Koa.Context) => {
|
||||
const meta = await fetchMeta();
|
||||
|
||||
logger.info(meta.summalyProxy
|
||||
|
@ -24,6 +24,22 @@ export default async function(follower: User, followee: User, silent = false) {
|
||||
|
||||
await Followings.delete(following.id);
|
||||
|
||||
decrementFollowing(follower, followee);
|
||||
|
||||
// Publish unfollow event
|
||||
if (!silent && Users.isLocalUser(follower)) {
|
||||
Users.pack(followee, follower, {
|
||||
detail: true
|
||||
}).then(packed => publishMainStream(follower.id, 'unfollow', packed));
|
||||
}
|
||||
|
||||
if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) {
|
||||
const content = renderActivity(renderUndo(renderFollow(follower, followee), follower));
|
||||
deliver(follower, content, followee.inbox);
|
||||
}
|
||||
}
|
||||
|
||||
export async function decrementFollowing(follower: User, followee: User) {
|
||||
//#region Decrement following count
|
||||
Users.decrement({ id: follower.id }, 'followingCount', 1);
|
||||
//#endregion
|
||||
@ -47,16 +63,4 @@ export default async function(follower: User, followee: User, silent = false) {
|
||||
//#endregion
|
||||
|
||||
perUserFollowingChart.update(follower, followee, false);
|
||||
|
||||
// Publish unfollow event
|
||||
if (!silent && Users.isLocalUser(follower)) {
|
||||
Users.pack(followee, follower, {
|
||||
detail: true
|
||||
}).then(packed => publishMainStream(follower.id, 'unfollow', packed));
|
||||
}
|
||||
|
||||
if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) {
|
||||
const content = renderActivity(renderUndo(renderFollow(follower, followee), follower));
|
||||
deliver(follower, content, followee.inbox);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ import renderReject from '../../../remote/activitypub/renderer/reject';
|
||||
import { deliver } from '../../../queue';
|
||||
import { publishMainStream } from '../../stream';
|
||||
import { User, ILocalUser } from '../../../models/entities/user';
|
||||
import { Users, FollowRequests } from '../../../models';
|
||||
import { Users, FollowRequests, Followings } from '../../../models';
|
||||
import { decrementFollowing } from '../delete';
|
||||
|
||||
export default async function(followee: User, follower: User) {
|
||||
if (Users.isRemoteUser(follower)) {
|
||||
@ -17,11 +18,25 @@ export default async function(followee: User, follower: User) {
|
||||
deliver(followee as ILocalUser, content, follower.inbox);
|
||||
}
|
||||
|
||||
await FollowRequests.delete({
|
||||
const request = await FollowRequests.findOne({
|
||||
followeeId: followee.id,
|
||||
followerId: follower.id
|
||||
});
|
||||
|
||||
if (request) {
|
||||
await FollowRequests.delete(request.id);
|
||||
} else {
|
||||
const following = await Followings.findOne({
|
||||
followeeId: followee.id,
|
||||
followerId: follower.id
|
||||
});
|
||||
|
||||
if (following) {
|
||||
await Followings.delete(following.id);
|
||||
decrementFollowing(follower, followee);
|
||||
}
|
||||
}
|
||||
|
||||
Users.pack(followee, follower, {
|
||||
detail: true
|
||||
}).then(packed => publishMainStream(follower.id, 'unfollow', packed));
|
||||
|
@ -2,13 +2,13 @@ import config from '../../config';
|
||||
import renderAdd from '../../remote/activitypub/renderer/add';
|
||||
import renderRemove from '../../remote/activitypub/renderer/remove';
|
||||
import { renderActivity } from '../../remote/activitypub/renderer';
|
||||
import { deliver } from '../../queue';
|
||||
import { IdentifiableError } from '../../misc/identifiable-error';
|
||||
import { User, ILocalUser } from '../../models/entities/user';
|
||||
import { User } from '../../models/entities/user';
|
||||
import { Note } from '../../models/entities/note';
|
||||
import { Notes, UserNotePinings, Users, Followings } from '../../models';
|
||||
import { Notes, UserNotePinings, Users } from '../../models';
|
||||
import { UserNotePining } from '../../models/entities/user-note-pinings';
|
||||
import { genId } from '../../misc/gen-id';
|
||||
import { deliverToFollowers } from '../../remote/activitypub/deliver-manager';
|
||||
|
||||
/**
|
||||
* 指定した投稿をピン留めします
|
||||
@ -82,36 +82,9 @@ export async function deliverPinnedChange(userId: User['id'], noteId: Note['id']
|
||||
|
||||
if (!Users.isLocalUser(user)) return;
|
||||
|
||||
const queue = await CreateRemoteInboxes(user);
|
||||
|
||||
if (queue.length < 1) return;
|
||||
|
||||
const target = `${config.url}/users/${user.id}/collections/featured`;
|
||||
|
||||
const item = `${config.url}/notes/${noteId}`;
|
||||
const content = renderActivity(isAddition ? renderAdd(user, target, item) : renderRemove(user, target, item));
|
||||
for (const inbox of queue) {
|
||||
deliver(user, content, inbox);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ローカルユーザーのリモートフォロワーのinboxリストを作成する
|
||||
* @param user ローカルユーザー
|
||||
*/
|
||||
async function CreateRemoteInboxes(user: ILocalUser): Promise<string[]> {
|
||||
const followers = await Followings.find({
|
||||
followeeId: user.id
|
||||
});
|
||||
|
||||
const queue: string[] = [];
|
||||
|
||||
for (const following of followers) {
|
||||
if (Followings.isRemoteFollower(following)) {
|
||||
const inbox = following.followerSharedInbox || following.followerInbox;
|
||||
if (!queue.includes(inbox)) queue.push(inbox);
|
||||
}
|
||||
}
|
||||
|
||||
return queue;
|
||||
deliverToFollowers(user, content);
|
||||
}
|
||||
|
@ -1,34 +1,17 @@
|
||||
import renderUpdate from '../../remote/activitypub/renderer/update';
|
||||
import { renderActivity } from '../../remote/activitypub/renderer';
|
||||
import { deliver } from '../../queue';
|
||||
import { Followings, Users } from '../../models';
|
||||
import { Users } from '../../models';
|
||||
import { User } from '../../models/entities/user';
|
||||
import { renderPerson } from '../../remote/activitypub/renderer/person';
|
||||
import { deliverToFollowers } from '../../remote/activitypub/deliver-manager';
|
||||
|
||||
export async function publishToFollowers(userId: User['id']) {
|
||||
const user = await Users.findOne(userId);
|
||||
if (user == null) throw new Error('user not found');
|
||||
|
||||
const followers = await Followings.find({
|
||||
followeeId: user.id
|
||||
});
|
||||
|
||||
const queue: string[] = [];
|
||||
|
||||
// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
|
||||
if (Users.isLocalUser(user)) {
|
||||
for (const following of followers) {
|
||||
if (Followings.isRemoteFollower(following)) {
|
||||
const inbox = following.followerSharedInbox || following.followerInbox;
|
||||
if (!queue.includes(inbox)) queue.push(inbox);
|
||||
}
|
||||
}
|
||||
|
||||
if (queue.length > 0) {
|
||||
const content = renderActivity(renderUpdate(await renderPerson(user), user));
|
||||
for (const inbox of queue) {
|
||||
deliver(user, content, inbox);
|
||||
}
|
||||
}
|
||||
deliverToFollowers(user, content);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as cluster from 'cluster';
|
||||
import * as os from 'os';
|
||||
import chalk from 'chalk';
|
||||
import * as chalk from 'chalk';
|
||||
import * as dateformat from 'dateformat';
|
||||
import { program } from '../argv';
|
||||
import { getRepository } from 'typeorm';
|
||||
@ -103,7 +103,7 @@ export default class Logger {
|
||||
worker: worker.toString(),
|
||||
domain: [this.domain].concat(subDomains).map(d => d.name),
|
||||
level: level,
|
||||
message: message,
|
||||
message: message.substr(0, 1000), // 1024を超えるとログが挿入できずエラーになり無限ループする
|
||||
data: data,
|
||||
} as Log);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import es from '../../db/elasticsearch';
|
||||
import { publishMainStream, publishNotesStream } from '../stream';
|
||||
import { deliver } from '../../queue';
|
||||
import DeliverManager from '../../remote/activitypub/deliver-manager';
|
||||
import renderNote from '../../remote/activitypub/renderer/note';
|
||||
import renderCreate from '../../remote/activitypub/renderer/create';
|
||||
import renderAnnounce from '../../remote/activitypub/renderer/announce';
|
||||
@ -17,7 +17,7 @@ import extractMentions from '../../misc/extract-mentions';
|
||||
import extractEmojis from '../../misc/extract-emojis';
|
||||
import extractHashtags from '../../misc/extract-hashtags';
|
||||
import { Note, IMentionedRemoteUsers } from '../../models/entities/note';
|
||||
import { Mutings, Users, NoteWatchings, Followings, Notes, Instances, UserProfiles } from '../../models';
|
||||
import { Mutings, Users, NoteWatchings, Notes, Instances, UserProfiles } from '../../models';
|
||||
import { DriveFile } from '../../models/entities/drive-file';
|
||||
import { App } from '../../models/entities/app';
|
||||
import { Not, getConnection, In } from 'typeorm';
|
||||
@ -244,13 +244,7 @@ export default async (user: User, data: Option, silent = false) => new Promise<N
|
||||
const nm = new NotificationManager(user, note);
|
||||
const nmRelatedPromises = [];
|
||||
|
||||
createMentionedEvents(mentionedUsers, note, nm);
|
||||
|
||||
const noteActivity = await renderNoteOrRenoteActivity(data, note);
|
||||
|
||||
if (Users.isLocalUser(user)) {
|
||||
deliverNoteToMentionedRemoteUsers(mentionedUsers, user, noteActivity);
|
||||
}
|
||||
await createMentionedEvents(mentionedUsers, note, nm);
|
||||
|
||||
const profile = await UserProfiles.findOne(user.id).then(ensure);
|
||||
|
||||
@ -294,11 +288,42 @@ export default async (user: User, data: Option, silent = false) => new Promise<N
|
||||
}
|
||||
}
|
||||
|
||||
publish(user, note, data.reply, data.renote, noteActivity);
|
||||
|
||||
Promise.all(nmRelatedPromises).then(() => {
|
||||
nm.deliver();
|
||||
});
|
||||
|
||||
//#region AP deliver
|
||||
if (Users.isLocalUser(user)) {
|
||||
(async () => {
|
||||
const noteActivity = await renderNoteOrRenoteActivity(data, note);
|
||||
const dm = new DeliverManager(user, noteActivity);
|
||||
|
||||
// メンションされたリモートユーザーに配送
|
||||
for (const u of mentionedUsers.filter(u => Users.isRemoteUser(u))) {
|
||||
dm.addDirectRecipe(u as IRemoteUser);
|
||||
}
|
||||
|
||||
// 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送
|
||||
if (data.reply && data.reply.userHost !== null) {
|
||||
const u = await Users.findOne(data.reply.userId);
|
||||
if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u);
|
||||
}
|
||||
|
||||
// 投稿がRenoteかつ投稿者がローカルユーザーかつRenote元の投稿の投稿者がリモートユーザーなら配送
|
||||
if (data.renote && data.renote.userHost !== null) {
|
||||
const u = await Users.findOne(data.renote.userId);
|
||||
if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u);
|
||||
}
|
||||
|
||||
// フォロワーに配送
|
||||
if (['public', 'home', 'followers'].includes(note.visibility)) {
|
||||
dm.addFollowersRecipe();
|
||||
}
|
||||
|
||||
dm.execute();
|
||||
})();
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
|
||||
// Register to search database
|
||||
@ -320,29 +345,6 @@ function incRenoteCount(renote: Note) {
|
||||
Notes.increment({ id: renote.id }, 'score', 1);
|
||||
}
|
||||
|
||||
async function publish(user: User, note: Note, reply: Note | null | undefined, renote: Note | null | undefined, noteActivity: any) {
|
||||
if (Users.isLocalUser(user)) {
|
||||
// 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送
|
||||
if (reply && reply.userHost !== null) {
|
||||
Users.findOne(reply.userId).then(ensure).then(u => {
|
||||
deliver(user, noteActivity, u.inbox);
|
||||
});
|
||||
}
|
||||
|
||||
// 投稿がRenoteかつ投稿者がローカルユーザーかつRenote元の投稿の投稿者がリモートユーザーなら配送
|
||||
if (renote && renote.userHost !== null) {
|
||||
Users.findOne(renote.userId).then(ensure).then(u => {
|
||||
deliver(user, noteActivity, u.inbox);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (['public', 'home', 'followers'].includes(note.visibility)) {
|
||||
// フォロワーに配信
|
||||
publishToFollowers(note, user, noteActivity);
|
||||
}
|
||||
}
|
||||
|
||||
async function insertNote(user: User, data: Option, tags: string[], emojis: string[], mentionedUsers: User[]) {
|
||||
const insert = new Note({
|
||||
id: genId(data.createdAt!),
|
||||
@ -472,34 +474,6 @@ async function notifyToWatchersOfReplyee(reply: Note, user: User, nm: Notificati
|
||||
}
|
||||
}
|
||||
|
||||
async function publishToFollowers(note: Note, user: User, noteActivity: any) {
|
||||
const followers = await Followings.find({
|
||||
followeeId: note.userId
|
||||
});
|
||||
|
||||
const queue: string[] = [];
|
||||
|
||||
for (const following of followers) {
|
||||
if (Followings.isRemoteFollower(following)) {
|
||||
// フォロワーがリモートユーザーかつ投稿者がローカルユーザーなら投稿を配信
|
||||
if (Users.isLocalUser(user)) {
|
||||
const inbox = following.followerSharedInbox || following.followerInbox;
|
||||
if (!queue.includes(inbox)) queue.push(inbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const inbox of queue) {
|
||||
deliver(user as any, noteActivity, inbox);
|
||||
}
|
||||
}
|
||||
|
||||
function deliverNoteToMentionedRemoteUsers(mentionedUsers: User[], user: ILocalUser, noteActivity: any) {
|
||||
for (const u of mentionedUsers.filter(u => Users.isRemoteUser(u))) {
|
||||
deliver(user, noteActivity, (u as IRemoteUser).inbox);
|
||||
}
|
||||
}
|
||||
|
||||
async function createMentionedEvents(mentionedUsers: User[], note: Note, nm: NotificationManager) {
|
||||
for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) {
|
||||
const detailPackedNote = await Notes.pack(note, u, {
|
||||
|
@ -3,14 +3,14 @@ import renderDelete from '../../remote/activitypub/renderer/delete';
|
||||
import renderAnnounce from '../../remote/activitypub/renderer/announce';
|
||||
import renderUndo from '../../remote/activitypub/renderer/undo';
|
||||
import { renderActivity } from '../../remote/activitypub/renderer';
|
||||
import { deliver } from '../../queue';
|
||||
import renderTombstone from '../../remote/activitypub/renderer/tombstone';
|
||||
import config from '../../config';
|
||||
import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc';
|
||||
import { User } from '../../models/entities/user';
|
||||
import { Note } from '../../models/entities/note';
|
||||
import { Notes, Users, Followings, Instances } from '../../models';
|
||||
import { Notes, Users, Instances } from '../../models';
|
||||
import { notesChart, perUserNotesChart, instanceChart } from '../chart';
|
||||
import { deliverToFollowers } from '../../remote/activitypub/deliver-manager';
|
||||
|
||||
/**
|
||||
* 投稿を削除します。
|
||||
@ -49,22 +49,7 @@ export default async function(user: User, note: Note, quiet = false) {
|
||||
? renderUndo(renderAnnounce(renote.uri || `${config.url}/notes/${renote.id}`, note), user)
|
||||
: renderDelete(renderTombstone(`${config.url}/notes/${note.id}`), user));
|
||||
|
||||
const queue: string[] = [];
|
||||
|
||||
const followers = await Followings.find({
|
||||
followeeId: note.userId
|
||||
});
|
||||
|
||||
for (const following of followers) {
|
||||
if (Followings.isRemoteFollower(following)) {
|
||||
const inbox = following.followerSharedInbox || following.followerInbox;
|
||||
if (!queue.includes(inbox)) queue.push(inbox);
|
||||
}
|
||||
}
|
||||
|
||||
for (const inbox of queue) {
|
||||
deliver(user as any, content, inbox);
|
||||
}
|
||||
deliverToFollowers(user, content);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import renderUpdate from '../../../remote/activitypub/renderer/update';
|
||||
import { renderActivity } from '../../../remote/activitypub/renderer';
|
||||
import { deliver } from '../../../queue';
|
||||
import renderNote from '../../../remote/activitypub/renderer/note';
|
||||
import { Users, Notes, Followings } from '../../../models';
|
||||
import { Users, Notes } from '../../../models';
|
||||
import { Note } from '../../../models/entities/note';
|
||||
import { deliverToFollowers } from '../../../remote/activitypub/deliver-manager';
|
||||
|
||||
export async function deliverQuestionUpdate(noteId: Note['id']) {
|
||||
const note = await Notes.findOne(noteId);
|
||||
@ -12,26 +12,9 @@ export async function deliverQuestionUpdate(noteId: Note['id']) {
|
||||
const user = await Users.findOne(note.userId);
|
||||
if (user == null) throw new Error('note not found');
|
||||
|
||||
const followers = await Followings.find({
|
||||
followeeId: user.id
|
||||
});
|
||||
|
||||
const queue: string[] = [];
|
||||
|
||||
// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
|
||||
if (Users.isLocalUser(user)) {
|
||||
for (const following of followers) {
|
||||
if (Followings.isRemoteFollower(following)) {
|
||||
const inbox = following.followerSharedInbox || following.followerInbox;
|
||||
if (!queue.includes(inbox)) queue.push(inbox);
|
||||
}
|
||||
}
|
||||
|
||||
if (queue.length > 0) {
|
||||
const content = renderActivity(renderUpdate(await renderNote(note, false), user));
|
||||
for (const inbox of queue) {
|
||||
deliver(user, content, inbox);
|
||||
}
|
||||
}
|
||||
deliverToFollowers(user, content);
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"typeRoots": [
|
||||
"node_modules/@types",
|
||||
"src/@types"
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as webpack from 'webpack';
|
||||
import chalk from 'chalk';
|
||||
import * as chalk from 'chalk';
|
||||
const { VueLoaderPlugin } = require('vue-loader');
|
||||
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
|
||||
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
|
||||
|
Reference in New Issue
Block a user