Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
72b85fc09f | |||
6c27412c9c | |||
46bddfc9c2 | |||
56275bcfcb | |||
f35688bab8 | |||
93541f83c8 | |||
ea0d114833 | |||
7f6a3ec828 | |||
732804b6fa | |||
aba85b977d | |||
e6612f610c | |||
5a28632af7 | |||
4099db0d42 | |||
9d50a06d9c | |||
dd7bf9b2a3 | |||
c463284c2f | |||
c1d728a616 | |||
e43c9c0e21 |
@ -1,6 +1,12 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
10.83.0
|
||||
----------
|
||||
* 特定のインスタンスをブロックをできるように
|
||||
* 特定のインスタンスからのフォローを全解除できるように
|
||||
* インスタンスごとのチャートを追加
|
||||
|
||||
10.82.4
|
||||
----------
|
||||
* 起動できなくなることがある問題を修正
|
||||
|
@ -56,3 +56,19 @@ Good:
|
||||
``` ts
|
||||
export function something(foo: string): string {
|
||||
```
|
||||
|
||||
## Directory structure
|
||||
```
|
||||
src ... ソースコード
|
||||
@types ... 外部ライブラリなどの型定義
|
||||
prelude ... Misskeyに関係ないかつ副作用なし
|
||||
misc ... 副作用なしのユーティリティ処理
|
||||
service ... 副作用ありの共通処理
|
||||
queue ... ジョブキューとジョブ
|
||||
server ... Webサーバー
|
||||
client ... クライアント
|
||||
mfm ... MFM
|
||||
|
||||
test ... テスト
|
||||
|
||||
```
|
||||
|
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "削除しました"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "連合"
|
||||
host: "ホスト"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "ソート"
|
||||
sorts:
|
||||
caughtAtAsc: "登録日時が古い順"
|
||||
caughtAtDesc: "登録日時が新しい順"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "投稿が多い順"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "フォローが少ない順"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
|
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "削除しました"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "連合"
|
||||
host: "ホスト"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "ソート"
|
||||
sorts:
|
||||
caughtAtAsc: "登録日時が古い順"
|
||||
caughtAtDesc: "登録日時が新しい順"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "投稿が多い順"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "フォローが少ない順"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
|
@ -1019,7 +1019,7 @@ admin/views/dashboard.vue:
|
||||
federated: "Federated"
|
||||
admin/views/queue.vue:
|
||||
operation: "Action(s)"
|
||||
remove-all-jobs: "すべてのジョブをクリア"
|
||||
remove-all-jobs: "Clear all queued jobs"
|
||||
admin/views/abuse.vue:
|
||||
title: "Abuse"
|
||||
target: "Target"
|
||||
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "Deleted"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "Federation"
|
||||
host: "Host"
|
||||
notes: "Notes"
|
||||
users: "Users"
|
||||
following: "Following"
|
||||
followers: "Followers"
|
||||
status: "Status"
|
||||
latest-request-sent-at: "Time of last request sent"
|
||||
latest-request-received-at: "Last request received at"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "Block"
|
||||
lookup: "Look up"
|
||||
instances: "Instances"
|
||||
instance-not-registered: "The instance has not been discovered"
|
||||
sort: "Sort by"
|
||||
sorts:
|
||||
caughtAtAsc: "Date of discovery (Ascending)"
|
||||
caughtAtDesc: "Date of discovery (Descending)"
|
||||
notesAsc: "Order by least Notes posted"
|
||||
notesDesc: "Order by most Notes posted"
|
||||
usersAsc: "Less followers"
|
||||
usersDesc: "More followers"
|
||||
followingAsc: "Least followed"
|
||||
followingDesc: "Has more followers"
|
||||
followersAsc: "Sort by having less followers"
|
||||
followersDesc: "Sort by the larger number of followers"
|
||||
state: "Status"
|
||||
states:
|
||||
all: "All"
|
||||
blocked: "Blocked"
|
||||
result-is-truncated: "Displaying the top {n} items."
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "More details..."
|
||||
gotit: "Got it!"
|
||||
|
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "削除しました"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "連合"
|
||||
host: "ホスト"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "ソート"
|
||||
sorts:
|
||||
caughtAtAsc: "登録日時が古い順"
|
||||
caughtAtDesc: "登録日時が新しい順"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "投稿が多い順"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "フォローが少ない順"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
|
@ -345,8 +345,8 @@ common/views/components/note-menu.vue:
|
||||
copy-link: "Copier le lien"
|
||||
favorite: "Mettre cette note en favoris"
|
||||
unfavorite: "Retirer des favoris"
|
||||
watch: "ウォッチ"
|
||||
unwatch: "ウォッチ解除"
|
||||
watch: "Surveiller"
|
||||
unwatch: "Ne plus surveiller"
|
||||
pin: "Épingler sur votre profil"
|
||||
unpin: "Désépingler"
|
||||
delete: "Supprimer"
|
||||
@ -363,10 +363,10 @@ common/views/components/user-menu.vue:
|
||||
report-abuse: "Signaler un abus"
|
||||
report-abuse-detail: "Détail du signalement"
|
||||
report-abuse-reported: "Transmit à l’administrateur. Merci de votre collaboration."
|
||||
silence: "サイレンス"
|
||||
unsilence: "サイレンス解除"
|
||||
silence: "Mettre en sourdine"
|
||||
unsilence: "Enlever la sourdine"
|
||||
suspend: "Suspendre"
|
||||
unsuspend: "凍結解除"
|
||||
unsuspend: "Ne plus suspendre"
|
||||
common/views/components/poll.vue:
|
||||
vote-to: "Voter pour '{}'"
|
||||
vote-count: "{} votes"
|
||||
@ -509,12 +509,12 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "Adresse de courrier électronique"
|
||||
email-verified: "L’adresse du courrier électronique a été vérifiée."
|
||||
email-not-verified: "Adresse de courriel n’est pas confirmée. Veuillez vérifier votre boite de réception."
|
||||
export: "エクスポート"
|
||||
export: "Exporter"
|
||||
export-targets:
|
||||
all-notes: "すべての投稿データ"
|
||||
following-list: "フォロー"
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
all-notes: "Toutes les notes publiées"
|
||||
following-list: "Liste des abonnements"
|
||||
mute-list: "Liste des comptes mis en sourdine"
|
||||
blocking-list: "Liste des comptes bloqués"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Utilisateur·rice"
|
||||
@ -1007,7 +1007,7 @@ admin/views/index.vue:
|
||||
announcements: "Annonces"
|
||||
hashtags: "Hashtags"
|
||||
abuse: "Abus"
|
||||
queue: "ジョブキュー"
|
||||
queue: "File d’attente"
|
||||
back-to-misskey: "Retour vers Misskey"
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "Tableau de bord"
|
||||
@ -1018,7 +1018,7 @@ admin/views/dashboard.vue:
|
||||
this-instance: "Cette instance"
|
||||
federated: "Fédérées"
|
||||
admin/views/queue.vue:
|
||||
operation: "操作"
|
||||
operation: "Action(s)"
|
||||
remove-all-jobs: "すべてのジョブをクリア"
|
||||
admin/views/abuse.vue:
|
||||
title: "Abus"
|
||||
@ -1161,8 +1161,8 @@ admin/views/users.vue:
|
||||
unsuspend: "Suspension levée"
|
||||
unsuspend-confirm: "Souhaiteriez-vous ne plus suspendre ce compte ?"
|
||||
unsuspended: "La suspension de l’utilisateur a été levée avec succès"
|
||||
make-silence: "サイレンス"
|
||||
unmake-silence: "サイレンスの解除"
|
||||
make-silence: "Mettre en sourdine"
|
||||
unmake-silence: "Enlever la sourdine"
|
||||
verify: "Vérification du compte"
|
||||
verify-confirm: "Souhaiteriez-vous rendre votre compte comme étant un compte vérifié ?"
|
||||
verified: "Le compte a été vérifié"
|
||||
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "Supprimé"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Tags cachés"
|
||||
admin/views/federation.vue:
|
||||
federation: "Fédération"
|
||||
host: "Hôte"
|
||||
notes: "Notes"
|
||||
users: "Utilisateur·rice·s"
|
||||
following: "Abonnements"
|
||||
followers: "Abonné·e·s"
|
||||
status: "Statuts"
|
||||
latest-request-sent-at: "Dernière requête envoyée"
|
||||
latest-request-received-at: "Dernière requête reçue"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "Instances"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "Trier par"
|
||||
sorts:
|
||||
caughtAtAsc: "Date d’inscription (Ascendant)"
|
||||
caughtAtDesc: "Date d’inscription (Descendant)"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "Description des notes"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "Les moins suivies"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "Ayant le moins d'abonné·e·s"
|
||||
followersDesc: "Ayant le plus d'abonné·e·s"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "à propos"
|
||||
gotit: "J'ai compris !"
|
||||
|
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "削除しました"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "連合"
|
||||
host: "ホスト"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "ソート"
|
||||
sorts:
|
||||
caughtAtAsc: "登録日時が古い順"
|
||||
caughtAtDesc: "登録日時が新しい順"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "投稿が多い順"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "フォローが少ない順"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
|
@ -1381,6 +1381,9 @@ admin/views/federation.vue:
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
@ -1396,6 +1399,31 @@ admin/views/federation.vue:
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
driveUsageAsc: "ドライブ使用量が少ない順"
|
||||
driveUsageDesc: "ドライブ使用量が多い順"
|
||||
driveFilesAsc: "ドライブのファイル数が少ない順"
|
||||
driveFilesDesc: "ドライブのファイル数が多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
charts: "チャート"
|
||||
chart-srcs:
|
||||
requests: "リクエスト"
|
||||
users: "ユーザーの増減"
|
||||
users-total: "ユーザーの積算"
|
||||
notes: "投稿の増減"
|
||||
notes-total: "投稿の積算"
|
||||
ff: "フォロー/フォロワーの増減"
|
||||
ff-total: "フォロー/フォロワーの積算"
|
||||
drive-usage: "ドライブ使用量の増減"
|
||||
drive-usage-total: "ドライブ使用量の増減"
|
||||
drive-files: "ドライブファイル数の増減"
|
||||
drive-files-total: "ドライブファイル数の増減"
|
||||
chart-spans:
|
||||
hour: "1時間ごと"
|
||||
day: "1日ごと"
|
||||
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
|
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "削除しました"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "連合"
|
||||
host: "ホスト"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "ソート"
|
||||
sorts:
|
||||
caughtAtAsc: "登録日時が古い順"
|
||||
caughtAtDesc: "登録日時が新しい順"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "投稿が多い順"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "フォローが少ない順"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "もうちょい……"
|
||||
gotit: "ほい"
|
||||
|
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "삭제하였습니다"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "連合"
|
||||
host: "ホスト"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "ソート"
|
||||
sorts:
|
||||
caughtAtAsc: "登録日時が古い順"
|
||||
caughtAtDesc: "登録日時が新しい順"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "投稿が多い順"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "フォローが少ない順"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "자세히..."
|
||||
gotit: "알겠습니다"
|
||||
|
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "削除しました"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "連合"
|
||||
host: "ホスト"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "ソート"
|
||||
sorts:
|
||||
caughtAtAsc: "登録日時が古い順"
|
||||
caughtAtDesc: "登録日時が新しい順"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "投稿が多い順"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "フォローが少ない順"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
|
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "削除しました"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "連合"
|
||||
host: "ホスト"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "ソート"
|
||||
sorts:
|
||||
caughtAtAsc: "登録日時が古い順"
|
||||
caughtAtDesc: "登録日時が新しい順"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "投稿が多い順"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "フォローが少ない順"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "Skjønner!"
|
||||
|
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "Usunięto"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "連合"
|
||||
host: "ホスト"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "ソート"
|
||||
sorts:
|
||||
caughtAtAsc: "登録日時が古い順"
|
||||
caughtAtDesc: "登録日時が新しい順"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "投稿が多い順"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "フォローが少ない順"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "O Misskey"
|
||||
gotit: "Rozumiem!"
|
||||
|
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "削除しました"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "連合"
|
||||
host: "ホスト"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "ソート"
|
||||
sorts:
|
||||
caughtAtAsc: "登録日時が古い順"
|
||||
caughtAtDesc: "登録日時が新しい順"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "投稿が多い順"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "フォローが少ない順"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
|
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "削除しました"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
admin/views/federation.vue:
|
||||
federation: "連合"
|
||||
host: "ホスト"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
status: "ステータス"
|
||||
latest-request-sent-at: "直近のリクエスト送信"
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
block: "ブロック"
|
||||
lookup: "照会"
|
||||
instances: "インスタンス"
|
||||
instance-not-registered: "そのインスタンスは登録されていません"
|
||||
sort: "ソート"
|
||||
sorts:
|
||||
caughtAtAsc: "登録日時が古い順"
|
||||
caughtAtDesc: "登録日時が新しい順"
|
||||
notesAsc: "投稿が少ない順"
|
||||
notesDesc: "投稿が多い順"
|
||||
usersAsc: "ユーザーが少ない順"
|
||||
usersDesc: "ユーザーが多い順"
|
||||
followingAsc: "フォローが少ない順"
|
||||
followingDesc: "フォローが多い順"
|
||||
followersAsc: "フォロワーが少ない順"
|
||||
followersDesc: "フォロワーが多い順"
|
||||
state: "状態"
|
||||
states:
|
||||
all: "すべて"
|
||||
blocked: "ブロック"
|
||||
result-is-truncated: "上位{n}件を表示しています。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
|
@ -354,10 +354,10 @@ common/views/components/note-menu.vue:
|
||||
remote: "显示原始投稿"
|
||||
common/views/components/user-menu.vue:
|
||||
mention: "提到"
|
||||
mute: "免打扰"
|
||||
unmute: "解除免打扰"
|
||||
block: "屏蔽"
|
||||
unblock: "取消屏蔽"
|
||||
mute: "屏蔽"
|
||||
unmute: "解除屏蔽"
|
||||
block: "拉黑"
|
||||
unblock: "取消拉黑"
|
||||
push-to-list: "添加至列表"
|
||||
select-list: "请选择一个列表"
|
||||
report-abuse: "举报骚扰"
|
||||
@ -511,10 +511,10 @@ common/views/components/profile-editor.vue:
|
||||
email-not-verified: "邮件地址尚未验证。 请检查您的邮箱。"
|
||||
export: "导出"
|
||||
export-targets:
|
||||
all-notes: "すべての投稿データ"
|
||||
following-list: "フォロー"
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
all-notes: "所有发帖"
|
||||
following-list: "关注列表"
|
||||
mute-list: "屏蔽列表"
|
||||
blocking-list: "黑名单"
|
||||
export-requested: "导出请求已提交。可能需要花一些时间。导出的文件将保存到网盘中。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "用户"
|
||||
@ -780,8 +780,8 @@ desktop/views/components/settings.vue:
|
||||
notification: "通知"
|
||||
apps: "应用程序"
|
||||
tags: "标签"
|
||||
mute-and-block: "静音/屏蔽"
|
||||
blocking: "屏蔽中"
|
||||
mute-and-block: "屏蔽/拉黑"
|
||||
blocking: "已拉黑"
|
||||
security: "安全性"
|
||||
signin: "登录历史"
|
||||
password: "密码"
|
||||
@ -912,13 +912,13 @@ common/views/components/drive-settings.vue:
|
||||
in-use: "正在使用"
|
||||
stats: "统计"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "静音/封锁"
|
||||
mute: "静音"
|
||||
block: "封锁中"
|
||||
no-muted-users: "没有静音的用户"
|
||||
no-blocked-users: "没有封锁的用户"
|
||||
word-mute: "文字静音"
|
||||
muted-words: "静音的关键字"
|
||||
mute-and-block: "屏蔽/拉黑"
|
||||
mute: "屏蔽"
|
||||
block: "拉黑中"
|
||||
no-muted-users: "无屏蔽用户"
|
||||
no-blocked-users: "无拉黑的用户"
|
||||
word-mute: "文字屏蔽"
|
||||
muted-words: "屏蔽关键字"
|
||||
muted-words-description: "使用空格分隔会产生AND规范,并且使用换行符分隔会产生OR规范"
|
||||
save: "保存"
|
||||
common/views/components/password-settings.vue:
|
||||
@ -1007,7 +1007,7 @@ admin/views/index.vue:
|
||||
announcements: "公告"
|
||||
hashtags: "标签"
|
||||
abuse: "举报垃圾信息"
|
||||
queue: "ジョブキュー"
|
||||
queue: "作业队列"
|
||||
back-to-misskey: "返回 Misskey"
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "Dashboard"
|
||||
@ -1019,7 +1019,7 @@ admin/views/dashboard.vue:
|
||||
federated: "联合"
|
||||
admin/views/queue.vue:
|
||||
operation: "操作"
|
||||
remove-all-jobs: "すべてのジョブをクリア"
|
||||
remove-all-jobs: "清除所有作业"
|
||||
admin/views/abuse.vue:
|
||||
title: "举报垃圾信息"
|
||||
target: "目标"
|
||||
@ -1233,6 +1233,39 @@ admin/views/announcements.vue:
|
||||
removed: "已删除"
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "隐藏标签"
|
||||
admin/views/federation.vue:
|
||||
federation: "联合"
|
||||
host: "主机名"
|
||||
notes: "帖子"
|
||||
users: "用户"
|
||||
following: "正在关注"
|
||||
followers: "关注者"
|
||||
status: "状态"
|
||||
latest-request-sent-at: "上次发送的请求"
|
||||
latest-request-received-at: "上次收到的请求"
|
||||
remove-all-following: "取消所有关注"
|
||||
remove-all-following-info: "取消{host}的所有关注者。当实例不存在时执行。"
|
||||
block: "拉黑"
|
||||
lookup: "查询"
|
||||
instances: "实例"
|
||||
instance-not-registered: "实例未注册"
|
||||
sort: "排序"
|
||||
sorts:
|
||||
caughtAtAsc: "注册时间从旧到新"
|
||||
caughtAtDesc: "注册时间从新到旧"
|
||||
notesAsc: "发帖数量从少到多"
|
||||
notesDesc: "发帖数量从多到少"
|
||||
usersAsc: "用户数从少到多"
|
||||
usersDesc: "用户数从多到少"
|
||||
followingAsc: "关注数从少到多"
|
||||
followingDesc: "关注数从多到少"
|
||||
followersAsc: "粉丝数从少到多"
|
||||
followersDesc: "粉丝数从多到少"
|
||||
state: "状态"
|
||||
states:
|
||||
all: "所有"
|
||||
blocked: "已拉黑"
|
||||
result-is-truncated: "显示最前面的{n}项。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "更多信息..."
|
||||
gotit: "没问题! "
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "10.82.4",
|
||||
"clientVersion": "2.0.14193",
|
||||
"version": "10.83.0",
|
||||
"clientVersion": "2.0.14211",
|
||||
"codename": "nighthike",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -39,12 +39,41 @@
|
||||
<ui-input :value="instance.latestRequestReceivedAt" type="text" readonly>
|
||||
<span>{{ $t('latest-request-received-at') }}</span>
|
||||
</ui-input>
|
||||
<ui-switch v-model="instance.isBlocked" @change="updateInstance()">{{ $t('block') }}</ui-switch>
|
||||
<details>
|
||||
<summary>{{ $t('charts') }}</summary>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-select v-model="chartSrc">
|
||||
<option value="requests">{{ $t('chart-srcs.requests') }}</option>
|
||||
<option value="users">{{ $t('chart-srcs.users') }}</option>
|
||||
<option value="users-total">{{ $t('chart-srcs.users-total') }}</option>
|
||||
<option value="notes">{{ $t('chart-srcs.notes') }}</option>
|
||||
<option value="notes-total">{{ $t('chart-srcs.notes-total') }}</option>
|
||||
<option value="ff">{{ $t('chart-srcs.ff') }}</option>
|
||||
<option value="ff-total">{{ $t('chart-srcs.ff-total') }}</option>
|
||||
<option value="drive-usage">{{ $t('chart-srcs.drive-usage') }}</option>
|
||||
<option value="drive-usage-total">{{ $t('chart-srcs.drive-usage-total') }}</option>
|
||||
<option value="drive-files">{{ $t('chart-srcs.drive-files') }}</option>
|
||||
<option value="drive-files-total">{{ $t('chart-srcs.drive-files-total') }}</option>
|
||||
</ui-select>
|
||||
<ui-select v-model="chartSpan">
|
||||
<option value="hour">{{ $t('chart-spans.hour') }}</option>
|
||||
<option value="day">{{ $t('chart-spans.day') }}</option>
|
||||
</ui-select>
|
||||
</ui-horizon-group>
|
||||
<div ref="chart"></div>
|
||||
</details>
|
||||
<details>
|
||||
<summary>{{ $t('remove-all-following') }}</summary>
|
||||
<ui-button @click="removeAllFollowing()" style="margin-top: 16px;"><fa :icon="faMinusCircle"/> {{ $t('remove-all-following') }}</ui-button>
|
||||
<ui-info warn>{{ $t('remove-all-following-info', { host: instance.host }) }}</ui-info>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<div slot="title"><fa :icon="faUsers"/> {{ $t('instances') }}</div>
|
||||
<div slot="title"><fa :icon="faServer"/> {{ $t('instances') }}</div>
|
||||
<section class="fit-top">
|
||||
<ui-horizon-group inputs>
|
||||
<ui-select v-model="sort">
|
||||
@ -59,6 +88,15 @@
|
||||
<option value="+following">{{ $t('sorts.followingDesc') }}</option>
|
||||
<option value="-followers">{{ $t('sorts.followersAsc') }}</option>
|
||||
<option value="+followers">{{ $t('sorts.followersDesc') }}</option>
|
||||
<option value="-driveUsage">{{ $t('sorts.driveUsageAsc') }}</option>
|
||||
<option value="+driveUsage">{{ $t('sorts.driveUsageDesc') }}</option>
|
||||
<option value="-driveFiles">{{ $t('sorts.driveFilesAsc') }}</option>
|
||||
<option value="+driveFiles">{{ $t('sorts.driveFilesDesc') }}</option>
|
||||
</ui-select>
|
||||
<ui-select v-model="state">
|
||||
<span slot="label">{{ $t('state') }}</span>
|
||||
<option value="all">{{ $t('states.all') }}</option>
|
||||
<option value="blocked">{{ $t('states.blocked') }}</option>
|
||||
</ui-select>
|
||||
</ui-horizon-group>
|
||||
|
||||
@ -80,6 +118,8 @@
|
||||
<span>{{ instance.latestStatus }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ui-info v-if="instances.length == limit">{{ $t('result-is-truncated', { n: limit }) }}</ui-info>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
@ -88,7 +128,13 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import { faGlobe, faTerminal, faSearch } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faGlobe, faTerminal, faSearch, faMinusCircle, faServer } from '@fortawesome/free-solid-svg-icons';
|
||||
import ApexCharts from 'apexcharts';
|
||||
import * as tinycolor from 'tinycolor2';
|
||||
|
||||
const chartLimit = 90;
|
||||
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
|
||||
const negate = arr => arr.map(x => -x);
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/federation.vue'),
|
||||
@ -98,23 +144,89 @@ export default Vue.extend({
|
||||
instance: null,
|
||||
target: null,
|
||||
sort: '+caughtAt',
|
||||
state: 'all',
|
||||
limit: 50,
|
||||
instances: [],
|
||||
faGlobe, faTerminal, faSearch
|
||||
chart: null,
|
||||
chartSrc: 'requests',
|
||||
chartSpan: 'hour',
|
||||
chartInstance: null,
|
||||
faGlobe, faTerminal, faSearch, faMinusCircle, faServer
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
data(): any {
|
||||
if (this.chart == null) return null;
|
||||
switch (this.chartSrc) {
|
||||
case 'requests': return this.requestsChart();
|
||||
case 'users': return this.usersChart(false);
|
||||
case 'users-total': return this.usersChart(true);
|
||||
case 'notes': return this.notesChart(false);
|
||||
case 'notes-total': return this.notesChart(true);
|
||||
case 'ff': return this.ffChart(false);
|
||||
case 'ff-total': return this.ffChart(true);
|
||||
case 'drive-usage': return this.driveUsageChart(false);
|
||||
case 'drive-usage-total': return this.driveUsageChart(true);
|
||||
case 'drive-files': return this.driveFilesChart(false);
|
||||
case 'drive-files-total': return this.driveFilesChart(true);
|
||||
}
|
||||
},
|
||||
|
||||
stats(): any[] {
|
||||
const stats =
|
||||
this.chartSpan == 'day' ? this.chart.perDay :
|
||||
this.chartSpan == 'hour' ? this.chart.perHour :
|
||||
null;
|
||||
|
||||
return stats;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
sort() {
|
||||
this.instances = [];
|
||||
this.fetchInstances();
|
||||
},
|
||||
|
||||
state() {
|
||||
this.fetchInstances();
|
||||
},
|
||||
|
||||
async instance() {
|
||||
this.now = new Date();
|
||||
|
||||
const [perHour, perDay] = await Promise.all([
|
||||
this.$root.api('charts/instance', { host: this.instance.host, limit: chartLimit, span: 'hour' }),
|
||||
this.$root.api('charts/instance', { host: this.instance.host, limit: chartLimit, span: 'day' }),
|
||||
]);
|
||||
|
||||
const chart = {
|
||||
perHour: perHour,
|
||||
perDay: perDay
|
||||
};
|
||||
|
||||
this.chart = chart;
|
||||
|
||||
this.renderChart();
|
||||
},
|
||||
|
||||
chartSrc() {
|
||||
this.renderChart();
|
||||
},
|
||||
|
||||
chartSpan() {
|
||||
this.renderChart();
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchInstances();
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.chartInstance.destroy();
|
||||
},
|
||||
|
||||
methods: {
|
||||
showInstance() {
|
||||
this.$root.api('federation/show-instance', {
|
||||
@ -133,12 +245,207 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
fetchInstances() {
|
||||
this.instances = [];
|
||||
this.$root.api('federation/instances', {
|
||||
sort: this.sort
|
||||
state: this.state,
|
||||
sort: this.sort,
|
||||
limit: this.limit
|
||||
}).then(instances => {
|
||||
this.instances = instances;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
removeAllFollowing() {
|
||||
this.$root.api('admin/federation/remove-all-following', {
|
||||
host: this.instance.host
|
||||
}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
updateInstance() {
|
||||
this.$root.api('admin/federation/update-instance', {
|
||||
host: this.instance.host,
|
||||
isBlocked: this.instance.isBlocked,
|
||||
});
|
||||
},
|
||||
|
||||
setSrc(src) {
|
||||
this.chartSrc = src;
|
||||
},
|
||||
|
||||
renderChart() {
|
||||
if (this.chartInstance) {
|
||||
this.chartInstance.destroy();
|
||||
}
|
||||
|
||||
this.chartInstance = new ApexCharts(this.$refs.chart, {
|
||||
chart: {
|
||||
type: 'area',
|
||||
height: 300,
|
||||
animations: {
|
||||
dynamicAnimation: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
toolbar: {
|
||||
show: false
|
||||
},
|
||||
zoom: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
grid: {
|
||||
clipMarkers: false,
|
||||
borderColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight',
|
||||
width: 2
|
||||
},
|
||||
legend: {
|
||||
labels: {
|
||||
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
style: {
|
||||
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
|
||||
}
|
||||
},
|
||||
axisBorder: {
|
||||
color: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
axisTicks: {
|
||||
color: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
formatter: this.data.bytes ? v => Vue.filter('bytes')(v, 0) : v => Vue.filter('number')(v),
|
||||
style: {
|
||||
color: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
|
||||
}
|
||||
}
|
||||
},
|
||||
series: this.data.series
|
||||
});
|
||||
|
||||
this.chartInstance.render();
|
||||
},
|
||||
|
||||
getDate(i: number) {
|
||||
const y = this.now.getFullYear();
|
||||
const m = this.now.getMonth();
|
||||
const d = this.now.getDate();
|
||||
const h = this.now.getHours();
|
||||
|
||||
return (
|
||||
this.chartSpan == 'day' ? new Date(y, m, d - i) :
|
||||
this.chartSpan == 'hour' ? new Date(y, m, d, h - i) :
|
||||
null
|
||||
);
|
||||
},
|
||||
|
||||
format(arr) {
|
||||
return arr.map((v, i) => ({ x: this.getDate(i).getTime(), y: v }));
|
||||
},
|
||||
|
||||
requestsChart(): any {
|
||||
return {
|
||||
series: [{
|
||||
name: 'Incoming',
|
||||
data: this.format(this.stats.requests.received)
|
||||
}, {
|
||||
name: 'Outgoing (succeeded)',
|
||||
data: this.format(this.stats.requests.succeeded)
|
||||
}, {
|
||||
name: 'Outgoing (failed)',
|
||||
data: this.format(this.stats.requests.failed)
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
usersChart(total: boolean): any {
|
||||
return {
|
||||
series: [{
|
||||
name: 'Users',
|
||||
type: 'area',
|
||||
data: this.format(total
|
||||
? this.stats.users.total
|
||||
: sum(this.stats.users.inc, negate(this.stats.users.dec))
|
||||
)
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
notesChart(total: boolean): any {
|
||||
return {
|
||||
series: [{
|
||||
name: 'Notes',
|
||||
type: 'area',
|
||||
data: this.format(total
|
||||
? this.stats.notes.total
|
||||
: sum(this.stats.notes.inc, negate(this.stats.notes.dec))
|
||||
)
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
ffChart(total: boolean): any {
|
||||
return {
|
||||
series: [{
|
||||
name: 'Following',
|
||||
type: 'area',
|
||||
data: this.format(total
|
||||
? this.stats.following.total
|
||||
: sum(this.stats.following.inc, negate(this.stats.following.dec))
|
||||
)
|
||||
}, {
|
||||
name: 'Followers',
|
||||
type: 'area',
|
||||
data: this.format(total
|
||||
? this.stats.followers.total
|
||||
: sum(this.stats.followers.inc, negate(this.stats.followers.dec))
|
||||
)
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
driveUsageChart(total: boolean): any {
|
||||
return {
|
||||
bytes: true,
|
||||
series: [{
|
||||
name: 'Drive usage',
|
||||
type: 'area',
|
||||
data: this.format(total
|
||||
? this.stats.drive.totalUsage
|
||||
: sum(this.stats.drive.incUsage, negate(this.stats.drive.decUsage))
|
||||
)
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
driveFilesChart(total: boolean): any {
|
||||
return {
|
||||
series: [{
|
||||
name: 'Drive files',
|
||||
type: 'area',
|
||||
data: this.format(total
|
||||
? this.stats.drive.totalFiles
|
||||
: sum(this.stats.drive.incFiles, negate(this.stats.drive.decFiles))
|
||||
)
|
||||
}]
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -6,8 +6,10 @@
|
||||
<ui-input v-model="username" type="text">
|
||||
<span slot="prefix">@</span>
|
||||
</ui-input>
|
||||
<ui-button @click="add" :disabled="changing">{{ $t('add-moderator.add') }}</ui-button>
|
||||
<ui-button @click="remove" :disabled="changing">{{ $t('add-moderator.remove') }}</ui-button>
|
||||
<ui-horizon-group>
|
||||
<ui-button @click="add" :disabled="changing">{{ $t('add-moderator.add') }}</ui-button>
|
||||
<ui-button @click="remove" :disabled="changing">{{ $t('add-moderator.remove') }}</ui-button>
|
||||
</ui-horizon-group>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
|
@ -28,7 +28,7 @@
|
||||
suspendedInfoBg: '$secondary',
|
||||
suspendedInfoFg: '$primary',
|
||||
remoteInfoBg: '$secondary',
|
||||
remoteInfoFg: '#$primary',
|
||||
remoteInfoFg: '$primary',
|
||||
desktopHeaderBg: '#1c2938',
|
||||
desktopHeaderFg: '#a9adae',
|
||||
desktopHeaderHoverFg: '#fff',
|
||||
|
@ -21,6 +21,7 @@ export default class Logger {
|
||||
|
||||
private log(level: string, message: string, important = false, subDomains: string[] = []): void {
|
||||
if (program.quiet) return;
|
||||
if (process.env.NODE_ENV === 'test') return;
|
||||
const domain = this.color ? chalk.keyword(this.color)(this.domain) : chalk.white(this.domain);
|
||||
const domains = [domain].concat(subDomains);
|
||||
if (this.parentLogger) {
|
||||
|
@ -11,6 +11,7 @@ DriveFile.createIndex('md5');
|
||||
DriveFile.createIndex('metadata.uri');
|
||||
DriveFile.createIndex('metadata.userId');
|
||||
DriveFile.createIndex('metadata.folderId');
|
||||
DriveFile.createIndex('metadata._user.host');
|
||||
export default DriveFile;
|
||||
|
||||
export const DriveFileChunk = monkDb.get('driveFiles.chunks');
|
||||
|
@ -43,6 +43,16 @@ export interface IInstance {
|
||||
*/
|
||||
followersCount: number;
|
||||
|
||||
/**
|
||||
* ドライブ使用量
|
||||
*/
|
||||
driveUsage: number;
|
||||
|
||||
/**
|
||||
* ドライブのファイル数
|
||||
*/
|
||||
driveFiles: number;
|
||||
|
||||
/**
|
||||
* 直近のリクエスト送信日時
|
||||
*/
|
||||
@ -57,4 +67,9 @@ export interface IInstance {
|
||||
* 直近のリクエスト受信日時
|
||||
*/
|
||||
latestRequestReceivedAt?: Date;
|
||||
|
||||
/**
|
||||
* このインスタンスをブロックしているか
|
||||
*/
|
||||
isBlocked: boolean;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ const User = db.get<IUser>('users');
|
||||
|
||||
User.createIndex('username');
|
||||
User.createIndex('usernameLower');
|
||||
User.createIndex('host');
|
||||
User.createIndex(['username', 'host'], { unique: true });
|
||||
User.createIndex(['usernameLower', 'host'], { unique: true });
|
||||
User.createIndex('token', { sparse: true, unique: true });
|
||||
|
@ -4,31 +4,38 @@ import request from '../../../remote/activitypub/request';
|
||||
import { queueLogger } from '../../logger';
|
||||
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
||||
import Instance from '../../../models/instance';
|
||||
import instanceChart from '../../../services/chart/instance';
|
||||
|
||||
export default async (job: bq.Job, done: any): Promise<void> => {
|
||||
const { host } = new URL(job.data.to);
|
||||
|
||||
try {
|
||||
await request(job.data.user, job.data.to, job.data.content);
|
||||
|
||||
// Update stats
|
||||
registerOrFetchInstanceDoc(job.data.user.host).then(i => {
|
||||
registerOrFetchInstanceDoc(host).then(i => {
|
||||
Instance.update({ _id: i._id }, {
|
||||
$set: {
|
||||
latestRequestSentAt: new Date(),
|
||||
latestStatus: 200
|
||||
}
|
||||
});
|
||||
|
||||
instanceChart.requestSent(i.host, true);
|
||||
});
|
||||
|
||||
done();
|
||||
} catch (res) {
|
||||
// Update stats
|
||||
registerOrFetchInstanceDoc(job.data.user.host).then(i => {
|
||||
registerOrFetchInstanceDoc(host).then(i => {
|
||||
Instance.update({ _id: i._id }, {
|
||||
$set: {
|
||||
latestRequestSentAt: new Date(),
|
||||
latestStatus: res != null && res.hasOwnProperty('statusCode') ? res.statusCode : null
|
||||
}
|
||||
});
|
||||
|
||||
instanceChart.requestSent(i.host, false);
|
||||
});
|
||||
|
||||
if (res != null && res.hasOwnProperty('statusCode')) {
|
||||
|
@ -10,6 +10,7 @@ import { publishApLogStream } from '../../../services/stream';
|
||||
import Logger from '../../../misc/logger';
|
||||
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
||||
import Instance from '../../../models/instance';
|
||||
import instanceChart from '../../../services/chart/instance';
|
||||
|
||||
const logger = new Logger('inbox');
|
||||
|
||||
@ -45,6 +46,15 @@ export default async (job: bq.Job, done: any): Promise<void> => {
|
||||
return;
|
||||
}
|
||||
|
||||
// ブロックしてたら中断
|
||||
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
|
||||
const instance = await Instance.findOne({ host: host.toLowerCase() });
|
||||
if (instance && instance.isBlocked) {
|
||||
logger.warn(`Blocked request: ${host}`);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
user = await User.findOne({ usernameLower: username, host: host.toLowerCase() }) as IRemoteUser;
|
||||
} else {
|
||||
// アクティビティ内のホストの検証
|
||||
@ -57,6 +67,15 @@ export default async (job: bq.Job, done: any): Promise<void> => {
|
||||
return;
|
||||
}
|
||||
|
||||
// ブロックしてたら中断
|
||||
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
|
||||
const instance = await Instance.findOne({ host: host.toLowerCase() });
|
||||
if (instance && instance.isBlocked) {
|
||||
logger.warn(`Blocked request: ${host}`);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
user = await User.findOne({
|
||||
host: { $ne: null },
|
||||
'publicKey.id': signature.keyId
|
||||
@ -110,6 +129,8 @@ export default async (job: bq.Job, done: any): Promise<void> => {
|
||||
latestRequestReceivedAt: new Date()
|
||||
}
|
||||
});
|
||||
|
||||
instanceChart.requestReceived(i.host);
|
||||
});
|
||||
|
||||
// アクティビティを処理
|
||||
|
@ -9,7 +9,8 @@ import { isCollectionOrOrderedCollection, isCollection, IPerson } from '../type'
|
||||
import { IDriveFile } from '../../../models/drive-file';
|
||||
import Meta from '../../../models/meta';
|
||||
import { fromHtml } from '../../../mfm/fromHtml';
|
||||
import usersChart from '../../../chart/users';
|
||||
import usersChart from '../../../services/chart/users';
|
||||
import instanceChart from '../../../services/chart/instance';
|
||||
import { URL } from 'url';
|
||||
import { resolveNote, extractEmojis } from './note';
|
||||
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
||||
@ -195,8 +196,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
|
||||
}
|
||||
});
|
||||
|
||||
// TODO
|
||||
//perInstanceChart.newUser();
|
||||
instanceChart.newUser(i.host);
|
||||
});
|
||||
|
||||
//#region Increment users count
|
||||
|
@ -4,11 +4,13 @@ import { URL } from 'url';
|
||||
import * as crypto from 'crypto';
|
||||
import { lookup, IRunOptions } from 'lookup-dns-cache';
|
||||
import * as promiseAny from 'promise-any';
|
||||
import { toUnicode } from 'punycode';
|
||||
|
||||
import config from '../../config';
|
||||
import { ILocalUser } from '../../models/user';
|
||||
import { publishApLogStream } from '../../services/stream';
|
||||
import { apLogger } from './logger';
|
||||
import Instance from '../../models/instance';
|
||||
|
||||
export const logger = apLogger.createSubLogger('deliver');
|
||||
|
||||
@ -19,6 +21,11 @@ export default (user: ILocalUser, url: string, object: any) => new Promise(async
|
||||
|
||||
const { protocol, host, hostname, port, pathname, search } = new URL(url);
|
||||
|
||||
// ブロックしてたら中断
|
||||
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
|
||||
const instance = await Instance.findOne({ host: toUnicode(host) });
|
||||
if (instance && instance.isBlocked) return;
|
||||
|
||||
const data = JSON.stringify(object);
|
||||
|
||||
const sha256 = crypto.createHash('sha256');
|
||||
|
@ -0,0 +1,33 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../../define';
|
||||
import Following from '../../../../../models/following';
|
||||
import User from '../../../../../models/user';
|
||||
import deleteFollowing from '../../../../../services/following/delete';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
params: {
|
||||
host: {
|
||||
validator: $.str
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||
const followings = await Following.find({
|
||||
'_follower.host': ps.host
|
||||
});
|
||||
|
||||
const pairs = await Promise.all(followings.map(f => Promise.all([
|
||||
User.findOne({ _id: f.followerId }),
|
||||
User.findOne({ _id: f.followeeId })
|
||||
])));
|
||||
|
||||
for (const pair of pairs) {
|
||||
deleteFollowing(pair[0], pair[1]);
|
||||
}
|
||||
|
||||
res();
|
||||
}));
|
34
src/server/api/endpoints/admin/federation/update-instance.ts
Normal file
34
src/server/api/endpoints/admin/federation/update-instance.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../../define';
|
||||
import Instance from '../../../../../models/instance';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
params: {
|
||||
host: {
|
||||
validator: $.str
|
||||
},
|
||||
|
||||
isBlocked: {
|
||||
validator: $.bool
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||
const instance = await Instance.findOne({ host: ps.host });
|
||||
|
||||
if (instance == null) {
|
||||
return rej('instance not found');
|
||||
}
|
||||
|
||||
Instance.update({ host: ps.host }, {
|
||||
$set: {
|
||||
isBlocked: ps.isBlocked
|
||||
}
|
||||
});
|
||||
|
||||
res();
|
||||
}));
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import activeUsersChart from '../../../../chart/active-users';
|
||||
import activeUsersChart from '../../../../services/chart/active-users';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import driveChart from '../../../../chart/drive';
|
||||
import driveChart from '../../../../services/chart/drive';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import federationChart from '../../../../chart/federation';
|
||||
import federationChart from '../../../../services/chart/federation';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import hashtagChart from '../../../../chart/hashtag';
|
||||
import hashtagChart from '../../../../services/chart/hashtag';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
42
src/server/api/endpoints/charts/instance.ts
Normal file
42
src/server/api/endpoints/charts/instance.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import instanceChart from '../../../../services/chart/instance';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'インスタンスごとのチャートを取得します。'
|
||||
},
|
||||
|
||||
params: {
|
||||
span: {
|
||||
validator: $.str.or(['day', 'hour']),
|
||||
desc: {
|
||||
'ja-JP': '集計のスパン (day または hour)'
|
||||
}
|
||||
},
|
||||
|
||||
limit: {
|
||||
validator: $.num.optional.range(1, 500),
|
||||
default: 30,
|
||||
desc: {
|
||||
'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
|
||||
}
|
||||
},
|
||||
|
||||
host: {
|
||||
validator: $.str,
|
||||
desc: {
|
||||
'ja-JP': '対象のインスタンスのホスト',
|
||||
'en-US': 'Target instance host'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default define(meta, (ps) => new Promise(async (res, rej) => {
|
||||
const stats = await instanceChart.getChart(ps.span as any, ps.limit, ps.host);
|
||||
|
||||
res(stats);
|
||||
}));
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import networkChart from '../../../../chart/network';
|
||||
import networkChart from '../../../../services/chart/network';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import notesChart from '../../../../chart/notes';
|
||||
import notesChart from '../../../../services/chart/notes';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../../define';
|
||||
import perUserDriveChart from '../../../../../chart/per-user-drive';
|
||||
import perUserDriveChart from '../../../../../services/chart/per-user-drive';
|
||||
import ID, { transform } from '../../../../../misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../../define';
|
||||
import perUserFollowingChart from '../../../../../chart/per-user-following';
|
||||
import perUserFollowingChart from '../../../../../services/chart/per-user-following';
|
||||
import ID, { transform } from '../../../../../misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../../define';
|
||||
import perUserNotesChart from '../../../../../chart/per-user-notes';
|
||||
import perUserNotesChart from '../../../../../services/chart/per-user-notes';
|
||||
import ID, { transform } from '../../../../../misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../../define';
|
||||
import perUserReactionsChart from '../../../../../chart/per-user-reactions';
|
||||
import perUserReactionsChart from '../../../../../services/chart/per-user-reactions';
|
||||
import ID, { transform } from '../../../../../misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import usersChart from '../../../../chart/users';
|
||||
import usersChart from '../../../../services/chart/users';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
@ -6,6 +6,10 @@ export const meta = {
|
||||
requireCredential: false,
|
||||
|
||||
params: {
|
||||
state: {
|
||||
validator: $.str.optional,
|
||||
},
|
||||
|
||||
limit: {
|
||||
validator: $.num.optional.range(1, 100),
|
||||
default: 30
|
||||
@ -66,6 +70,22 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||
sort = {
|
||||
caughtAt: 1
|
||||
};
|
||||
} else if (ps.sort == '+driveUsage') {
|
||||
sort = {
|
||||
driveUsage: -1
|
||||
};
|
||||
} else if (ps.sort == '-driveUsage') {
|
||||
sort = {
|
||||
driveUsage: 1
|
||||
};
|
||||
} else if (ps.sort == '+driveFiles') {
|
||||
sort = {
|
||||
driveFiles: -1
|
||||
};
|
||||
} else if (ps.sort == '-driveFiles') {
|
||||
sort = {
|
||||
driveFiles: 1
|
||||
};
|
||||
}
|
||||
} else {
|
||||
sort = {
|
||||
@ -73,8 +93,14 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||
};
|
||||
}
|
||||
|
||||
const q = {} as any;
|
||||
|
||||
if (ps.state === 'blocked') {
|
||||
q.isBlocked = true;
|
||||
}
|
||||
|
||||
const instances = await Instance
|
||||
.find({}, {
|
||||
.find(q, {
|
||||
limit: ps.limit,
|
||||
sort: sort,
|
||||
skip: ps.offset
|
||||
|
@ -6,7 +6,7 @@ import { packMany } from '../../../../models/note';
|
||||
import define from '../../define';
|
||||
import { countIf } from '../../../../prelude/array';
|
||||
import fetchMeta from '../../../../misc/fetch-meta';
|
||||
import activeUsersChart from '../../../../chart/active-users';
|
||||
import activeUsersChart from '../../../../services/chart/active-users';
|
||||
import { getHideUserIds } from '../../common/get-hide-users';
|
||||
|
||||
export const meta = {
|
||||
|
@ -5,7 +5,7 @@ import { packMany } from '../../../../models/note';
|
||||
import define from '../../define';
|
||||
import { countIf } from '../../../../prelude/array';
|
||||
import fetchMeta from '../../../../misc/fetch-meta';
|
||||
import activeUsersChart from '../../../../chart/active-users';
|
||||
import activeUsersChart from '../../../../services/chart/active-users';
|
||||
import { getHideUserIds } from '../../common/get-hide-users';
|
||||
|
||||
export const meta = {
|
||||
|
@ -5,7 +5,7 @@ import { getFriends } from '../../common/get-friends';
|
||||
import { packMany } from '../../../../models/note';
|
||||
import define from '../../define';
|
||||
import { countIf } from '../../../../prelude/array';
|
||||
import activeUsersChart from '../../../../chart/active-users';
|
||||
import activeUsersChart from '../../../../services/chart/active-users';
|
||||
import { getHideUserIds } from '../../common/get-hide-users';
|
||||
|
||||
export const meta = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import define from '../define';
|
||||
import driveChart from '../../../chart/drive';
|
||||
import federationChart from '../../../chart/federation';
|
||||
import driveChart from '../../../services/chart/drive';
|
||||
import federationChart from '../../../services/chart/federation';
|
||||
import fetchMeta from '../../../misc/fetch-meta';
|
||||
|
||||
export const meta = {
|
||||
|
@ -6,7 +6,7 @@ import generateUserToken from '../common/generate-native-user-token';
|
||||
import config from '../../../config';
|
||||
import Meta from '../../../models/meta';
|
||||
import RegistrationTicket from '../../../models/registration-tickets';
|
||||
import usersChart from '../../../chart/users';
|
||||
import usersChart from '../../../services/chart/users';
|
||||
import fetchMeta from '../../../misc/fetch-meta';
|
||||
import * as recaptcha from 'recaptcha-promise';
|
||||
|
||||
|
@ -19,7 +19,7 @@ import activityPub from './activitypub';
|
||||
import nodeinfo from './nodeinfo';
|
||||
import wellKnown from './well-known';
|
||||
import config from '../config';
|
||||
import networkChart from '../chart/network';
|
||||
import networkChart from '../services/chart/network';
|
||||
import apiServer from './api';
|
||||
import { sum } from '../prelude/array';
|
||||
import User from '../models/user';
|
||||
|
@ -8,7 +8,7 @@ import renderUndo from '../../remote/activitypub/renderer/undo';
|
||||
import renderBlock from '../../remote/activitypub/renderer/block';
|
||||
import { deliver } from '../../queue';
|
||||
import renderReject from '../../remote/activitypub/renderer/reject';
|
||||
import perUserFollowingChart from '../../chart/per-user-following';
|
||||
import perUserFollowingChart from '../../services/chart/per-user-following';
|
||||
import Blocking from '../../models/blocking';
|
||||
|
||||
export default async function(blocker: IUser, blockee: IUser) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Chart, { Obj } from '.';
|
||||
import { IUser, isLocalUser } from '../models/user';
|
||||
import { IUser, isLocalUser } from '../../models/user';
|
||||
|
||||
/**
|
||||
* アクティブユーザーに関するチャート
|
@ -1,7 +1,7 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Chart, { Obj } from './';
|
||||
import DriveFile, { IDriveFile } from '../models/drive-file';
|
||||
import { isLocalUser } from '../models/user';
|
||||
import DriveFile, { IDriveFile } from '../../models/drive-file';
|
||||
import { isLocalUser } from '../../models/user';
|
||||
|
||||
/**
|
||||
* ドライブに関するチャート
|
@ -1,6 +1,6 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Chart, { Obj } from '.';
|
||||
import Instance from '../models/instance';
|
||||
import Instance from '../../models/instance';
|
||||
|
||||
/**
|
||||
* フェデレーションに関するチャート
|
@ -1,7 +1,7 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Chart, { Obj } from './';
|
||||
import { IUser, isLocalUser } from '../models/user';
|
||||
import db from '../db/mongodb';
|
||||
import { IUser, isLocalUser } from '../../models/user';
|
||||
import db from '../../db/mongodb';
|
||||
|
||||
/**
|
||||
* ハッシュタグに関するチャート
|
@ -6,9 +6,9 @@ import * as moment from 'moment';
|
||||
import * as nestedProperty from 'nested-property';
|
||||
import autobind from 'autobind-decorator';
|
||||
import * as mongo from 'mongodb';
|
||||
import db from '../db/mongodb';
|
||||
import db from '../../db/mongodb';
|
||||
import { ICollection } from 'monk';
|
||||
import Logger from '../misc/logger';
|
||||
import Logger from '../../misc/logger';
|
||||
|
||||
const logger = new Logger('chart');
|
||||
|
302
src/services/chart/instance.ts
Normal file
302
src/services/chart/instance.ts
Normal file
@ -0,0 +1,302 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Chart, { Obj } from '.';
|
||||
import User from '../../models/user';
|
||||
import Note from '../../models/note';
|
||||
import Following from '../../models/following';
|
||||
import DriveFile, { IDriveFile } from '../../models/drive-file';
|
||||
|
||||
/**
|
||||
* インスタンスごとのチャート
|
||||
*/
|
||||
type InstanceLog = {
|
||||
requests: {
|
||||
/**
|
||||
* 失敗したリクエスト数
|
||||
*/
|
||||
failed: number;
|
||||
|
||||
/**
|
||||
* 成功したリクエスト数
|
||||
*/
|
||||
succeeded: number;
|
||||
|
||||
/**
|
||||
* 受信したリクエスト数
|
||||
*/
|
||||
received: number;
|
||||
};
|
||||
|
||||
notes: {
|
||||
/**
|
||||
* 集計期間時点での、全投稿数
|
||||
*/
|
||||
total: number;
|
||||
|
||||
/**
|
||||
* 増加した投稿数
|
||||
*/
|
||||
inc: number;
|
||||
|
||||
/**
|
||||
* 減少した投稿数
|
||||
*/
|
||||
dec: number;
|
||||
};
|
||||
|
||||
users: {
|
||||
/**
|
||||
* 集計期間時点での、全ユーザー数
|
||||
*/
|
||||
total: number;
|
||||
|
||||
/**
|
||||
* 増加したユーザー数
|
||||
*/
|
||||
inc: number;
|
||||
|
||||
/**
|
||||
* 減少したユーザー数
|
||||
*/
|
||||
dec: number;
|
||||
};
|
||||
|
||||
following: {
|
||||
/**
|
||||
* 集計期間時点での、全フォロー数
|
||||
*/
|
||||
total: number;
|
||||
|
||||
/**
|
||||
* 増加したフォロー数
|
||||
*/
|
||||
inc: number;
|
||||
|
||||
/**
|
||||
* 減少したフォロー数
|
||||
*/
|
||||
dec: number;
|
||||
};
|
||||
|
||||
followers: {
|
||||
/**
|
||||
* 集計期間時点での、全フォロワー数
|
||||
*/
|
||||
total: number;
|
||||
|
||||
/**
|
||||
* 増加したフォロワー数
|
||||
*/
|
||||
inc: number;
|
||||
|
||||
/**
|
||||
* 減少したフォロワー数
|
||||
*/
|
||||
dec: number;
|
||||
};
|
||||
|
||||
drive: {
|
||||
/**
|
||||
* 集計期間時点での、全ドライブファイル数
|
||||
*/
|
||||
totalFiles: number;
|
||||
|
||||
/**
|
||||
* 集計期間時点での、全ドライブファイルの合計サイズ
|
||||
*/
|
||||
totalUsage: number;
|
||||
|
||||
/**
|
||||
* 増加したドライブファイル数
|
||||
*/
|
||||
incFiles: number;
|
||||
|
||||
/**
|
||||
* 増加したドライブ使用量
|
||||
*/
|
||||
incUsage: number;
|
||||
|
||||
/**
|
||||
* 減少したドライブファイル数
|
||||
*/
|
||||
decFiles: number;
|
||||
|
||||
/**
|
||||
* 減少したドライブ使用量
|
||||
*/
|
||||
decUsage: number;
|
||||
};
|
||||
};
|
||||
|
||||
class InstanceChart extends Chart<InstanceLog> {
|
||||
constructor() {
|
||||
super('instance', true);
|
||||
}
|
||||
|
||||
@autobind
|
||||
protected async getTemplate(init: boolean, latest?: InstanceLog, group?: any): Promise<InstanceLog> {
|
||||
const calcUsage = () => DriveFile
|
||||
.aggregate([{
|
||||
$match: {
|
||||
'metadata._user.host': group,
|
||||
'metadata.deletedAt': { $exists: false }
|
||||
}
|
||||
}, {
|
||||
$project: {
|
||||
length: true
|
||||
}
|
||||
}, {
|
||||
$group: {
|
||||
_id: null,
|
||||
usage: { $sum: '$length' }
|
||||
}
|
||||
}])
|
||||
.then(res => res.length > 0 ? res[0].usage : 0);
|
||||
|
||||
const [
|
||||
notesCount,
|
||||
usersCount,
|
||||
followingCount,
|
||||
followersCount,
|
||||
driveFiles,
|
||||
driveUsage,
|
||||
] = init ? await Promise.all([
|
||||
Note.count({ '_user.host': group }),
|
||||
User.count({ host: group }),
|
||||
Following.count({ '_follower.host': group }),
|
||||
Following.count({ '_followee.host': group }),
|
||||
DriveFile.count({ 'metadata._user.host': group }),
|
||||
calcUsage(),
|
||||
]) : [
|
||||
latest ? latest.notes.total : 0,
|
||||
latest ? latest.users.total : 0,
|
||||
latest ? latest.following.total : 0,
|
||||
latest ? latest.followers.total : 0,
|
||||
latest ? latest.drive.totalFiles : 0,
|
||||
latest ? latest.drive.totalUsage : 0,
|
||||
];
|
||||
|
||||
return {
|
||||
requests: {
|
||||
failed: 0,
|
||||
succeeded: 0,
|
||||
received: 0
|
||||
},
|
||||
notes: {
|
||||
total: notesCount,
|
||||
inc: 0,
|
||||
dec: 0
|
||||
},
|
||||
users: {
|
||||
total: usersCount,
|
||||
inc: 0,
|
||||
dec: 0
|
||||
},
|
||||
following: {
|
||||
total: followingCount,
|
||||
inc: 0,
|
||||
dec: 0
|
||||
},
|
||||
followers: {
|
||||
total: followersCount,
|
||||
inc: 0,
|
||||
dec: 0
|
||||
},
|
||||
drive: {
|
||||
totalFiles: driveFiles,
|
||||
totalUsage: driveUsage,
|
||||
incFiles: 0,
|
||||
incUsage: 0,
|
||||
decFiles: 0,
|
||||
decUsage: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@autobind
|
||||
public async requestReceived(host: string) {
|
||||
await this.inc({
|
||||
requests: {
|
||||
received: 1
|
||||
}
|
||||
}, host);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public async requestSent(host: string, isSucceeded: boolean) {
|
||||
const update: Obj = {};
|
||||
|
||||
if (isSucceeded) {
|
||||
update.succeeded = 1;
|
||||
} else {
|
||||
update.failed = 1;
|
||||
}
|
||||
|
||||
await this.inc({
|
||||
requests: update
|
||||
}, host);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public async newUser(host: string) {
|
||||
await this.inc({
|
||||
users: {
|
||||
total: 1,
|
||||
inc: 1
|
||||
}
|
||||
}, host);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public async updateNote(host: string, isAdditional: boolean) {
|
||||
await this.inc({
|
||||
notes: {
|
||||
total: isAdditional ? 1 : -1,
|
||||
inc: isAdditional ? 1 : 0,
|
||||
dec: isAdditional ? 0 : 1,
|
||||
}
|
||||
}, host);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public async updateFollowing(host: string, isAdditional: boolean) {
|
||||
await this.inc({
|
||||
following: {
|
||||
total: isAdditional ? 1 : -1,
|
||||
inc: isAdditional ? 1 : 0,
|
||||
dec: isAdditional ? 0 : 1,
|
||||
}
|
||||
}, host);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public async updateFollowers(host: string, isAdditional: boolean) {
|
||||
await this.inc({
|
||||
followers: {
|
||||
total: isAdditional ? 1 : -1,
|
||||
inc: isAdditional ? 1 : 0,
|
||||
dec: isAdditional ? 0 : 1,
|
||||
}
|
||||
}, host);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public async updateDrive(file: IDriveFile, isAdditional: boolean) {
|
||||
const update: Obj = {};
|
||||
|
||||
update.totalFiles = isAdditional ? 1 : -1;
|
||||
update.totalUsage = isAdditional ? file.length : -file.length;
|
||||
if (isAdditional) {
|
||||
update.incFiles = 1;
|
||||
update.incUsage = file.length;
|
||||
} else {
|
||||
update.decFiles = 1;
|
||||
update.decUsage = file.length;
|
||||
}
|
||||
|
||||
await this.inc({
|
||||
drive: update
|
||||
}, file.metadata._user.host);
|
||||
}
|
||||
}
|
||||
|
||||
export default new InstanceChart();
|
@ -1,7 +1,7 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Chart, { Obj } from '.';
|
||||
import Note, { INote } from '../models/note';
|
||||
import { isLocalUser } from '../models/user';
|
||||
import Note, { INote } from '../../models/note';
|
||||
import { isLocalUser } from '../../models/user';
|
||||
|
||||
/**
|
||||
* 投稿に関するチャート
|
@ -1,6 +1,6 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Chart, { Obj } from './';
|
||||
import DriveFile, { IDriveFile } from '../models/drive-file';
|
||||
import DriveFile, { IDriveFile } from '../../models/drive-file';
|
||||
|
||||
/**
|
||||
* ユーザーごとのドライブに関するチャート
|
@ -1,7 +1,7 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Chart, { Obj } from './';
|
||||
import Following from '../models/following';
|
||||
import { IUser, isLocalUser } from '../models/user';
|
||||
import Following from '../../models/following';
|
||||
import { IUser, isLocalUser } from '../../models/user';
|
||||
|
||||
/**
|
||||
* ユーザーごとのフォローに関するチャート
|
@ -1,7 +1,7 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Chart, { Obj } from './';
|
||||
import Note, { INote } from '../models/note';
|
||||
import { IUser } from '../models/user';
|
||||
import Note, { INote } from '../../models/note';
|
||||
import { IUser } from '../../models/user';
|
||||
|
||||
/**
|
||||
* ユーザーごとの投稿に関するチャート
|
@ -1,7 +1,7 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Chart from './';
|
||||
import { IUser, isLocalUser } from '../models/user';
|
||||
import { INote } from '../models/note';
|
||||
import { IUser, isLocalUser } from '../../models/user';
|
||||
import { INote } from '../../models/note';
|
||||
|
||||
/**
|
||||
* ユーザーごとのリアクションに関するチャート
|
@ -1,6 +1,6 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Chart, { Obj } from './';
|
||||
import User, { IUser, isLocalUser } from '../models/user';
|
||||
import User, { IUser, isLocalUser } from '../../models/user';
|
||||
|
||||
/**
|
||||
* ユーザーに関するチャート
|
@ -13,17 +13,19 @@ import DriveFile, { IMetadata, getDriveFileBucket, IDriveFile } from '../../mode
|
||||
import DriveFolder from '../../models/drive-folder';
|
||||
import { pack } from '../../models/drive-file';
|
||||
import { publishMainStream, publishDriveStream } from '../stream';
|
||||
import { isLocalUser, IUser, IRemoteUser } from '../../models/user';
|
||||
import { isLocalUser, IUser, IRemoteUser, isRemoteUser } from '../../models/user';
|
||||
import delFile from './delete-file';
|
||||
import config from '../../config';
|
||||
import { getDriveFileWebpublicBucket } from '../../models/drive-file-webpublic';
|
||||
import { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail';
|
||||
import driveChart from '../../chart/drive';
|
||||
import perUserDriveChart from '../../chart/per-user-drive';
|
||||
import driveChart from '../../services/chart/drive';
|
||||
import perUserDriveChart from '../../services/chart/per-user-drive';
|
||||
import instanceChart from '../../services/chart/instance';
|
||||
import fetchMeta from '../../misc/fetch-meta';
|
||||
import { GenerateVideoThumbnail } from './generate-video-thumbnail';
|
||||
import { driveLogger } from './logger';
|
||||
import { IImage, ConvertToJpeg, ConvertToWebp, ConvertToPng } from './image-processor';
|
||||
import Instance from '../../models/instance';
|
||||
|
||||
const logger = driveLogger.createSubLogger('register', 'yellow');
|
||||
|
||||
@ -523,6 +525,15 @@ export default async function(
|
||||
// 統計を更新
|
||||
driveChart.update(driveFile, true);
|
||||
perUserDriveChart.update(driveFile, true);
|
||||
if (isRemoteUser(driveFile.metadata._user)) {
|
||||
instanceChart.updateDrive(driveFile, true);
|
||||
Instance.update({ host: driveFile.metadata._user.host }, {
|
||||
$inc: {
|
||||
driveUsage: driveFile.length,
|
||||
driveFiles: 1
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return driveFile;
|
||||
}
|
||||
|
@ -2,9 +2,12 @@ import * as Minio from 'minio';
|
||||
import DriveFile, { DriveFileChunk, IDriveFile } from '../../models/drive-file';
|
||||
import DriveFileThumbnail, { DriveFileThumbnailChunk } from '../../models/drive-file-thumbnail';
|
||||
import config from '../../config';
|
||||
import driveChart from '../../chart/drive';
|
||||
import perUserDriveChart from '../../chart/per-user-drive';
|
||||
import driveChart from '../../services/chart/drive';
|
||||
import perUserDriveChart from '../../services/chart/per-user-drive';
|
||||
import instanceChart from '../../services/chart/instance';
|
||||
import DriveFileWebpublic, { DriveFileWebpublicChunk } from '../../models/drive-file-webpublic';
|
||||
import Instance from '../../models/instance';
|
||||
import { isRemoteUser } from '../../models/user';
|
||||
|
||||
export default async function(file: IDriveFile, isExpired = false) {
|
||||
if (file.metadata.storage == 'minio') {
|
||||
@ -84,4 +87,13 @@ export default async function(file: IDriveFile, isExpired = false) {
|
||||
// 統計を更新
|
||||
driveChart.update(file, false);
|
||||
perUserDriveChart.update(file, false);
|
||||
if (isRemoteUser(file.metadata._user)) {
|
||||
instanceChart.updateDrive(file, false);
|
||||
Instance.update({ host: file.metadata._user.host }, {
|
||||
$inc: {
|
||||
driveUsage: -file.length,
|
||||
driveFiles: -1
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,10 @@ import renderAccept from '../../remote/activitypub/renderer/accept';
|
||||
import renderReject from '../../remote/activitypub/renderer/reject';
|
||||
import { deliver } from '../../queue';
|
||||
import createFollowRequest from './requests/create';
|
||||
import perUserFollowingChart from '../../chart/per-user-following';
|
||||
import perUserFollowingChart from '../../services/chart/per-user-following';
|
||||
import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc';
|
||||
import Instance from '../../models/instance';
|
||||
import instanceChart from '../../services/chart/instance';
|
||||
|
||||
export default async function(follower: IUser, followee: IUser, requestId?: string) {
|
||||
// check blocking
|
||||
@ -108,8 +109,7 @@ export default async function(follower: IUser, followee: IUser, requestId?: stri
|
||||
}
|
||||
});
|
||||
|
||||
// TODO
|
||||
//perInstanceChart.newFollowing();
|
||||
instanceChart.updateFollowing(i.host, true);
|
||||
});
|
||||
} else if (isLocalUser(follower) && isRemoteUser(followee)) {
|
||||
registerOrFetchInstanceDoc(followee.host).then(i => {
|
||||
@ -119,8 +119,7 @@ export default async function(follower: IUser, followee: IUser, requestId?: stri
|
||||
}
|
||||
});
|
||||
|
||||
// TODO
|
||||
//perInstanceChart.newFollower();
|
||||
instanceChart.updateFollowers(i.host, true);
|
||||
});
|
||||
}
|
||||
//#endregion
|
||||
|
@ -5,8 +5,11 @@ import { renderActivity } from '../../remote/activitypub/renderer';
|
||||
import renderFollow from '../../remote/activitypub/renderer/follow';
|
||||
import renderUndo from '../../remote/activitypub/renderer/undo';
|
||||
import { deliver } from '../../queue';
|
||||
import perUserFollowingChart from '../../chart/per-user-following';
|
||||
import perUserFollowingChart from '../../services/chart/per-user-following';
|
||||
import Logger from '../../misc/logger';
|
||||
import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc';
|
||||
import Instance from '../../models/instance';
|
||||
import instanceChart from '../../services/chart/instance';
|
||||
|
||||
const logger = new Logger('following/delete');
|
||||
|
||||
@ -41,6 +44,30 @@ export default async function(follower: IUser, followee: IUser) {
|
||||
});
|
||||
//#endregion
|
||||
|
||||
//#region Update instance stats
|
||||
if (isRemoteUser(follower) && isLocalUser(followee)) {
|
||||
registerOrFetchInstanceDoc(follower.host).then(i => {
|
||||
Instance.update({ _id: i._id }, {
|
||||
$inc: {
|
||||
followingCount: -1
|
||||
}
|
||||
});
|
||||
|
||||
instanceChart.updateFollowing(i.host, false);
|
||||
});
|
||||
} else if (isLocalUser(follower) && isRemoteUser(followee)) {
|
||||
registerOrFetchInstanceDoc(followee.host).then(i => {
|
||||
Instance.update({ _id: i._id }, {
|
||||
$inc: {
|
||||
followersCount: -1
|
||||
}
|
||||
});
|
||||
|
||||
instanceChart.updateFollowers(i.host, false);
|
||||
});
|
||||
}
|
||||
//#endregion
|
||||
|
||||
perUserFollowingChart.update(follower, followee, false);
|
||||
|
||||
// Publish unfollow event
|
||||
|
@ -6,7 +6,7 @@ import renderAccept from '../../../remote/activitypub/renderer/accept';
|
||||
import { deliver } from '../../../queue';
|
||||
import Following from '../../../models/following';
|
||||
import { publishMainStream } from '../../stream';
|
||||
import perUserFollowingChart from '../../../chart/per-user-following';
|
||||
import perUserFollowingChart from '../../../services/chart/per-user-following';
|
||||
import Logger from '../../../misc/logger';
|
||||
|
||||
const logger = new Logger('following/requests/accept');
|
||||
|
@ -21,9 +21,10 @@ import Meta from '../../models/meta';
|
||||
import config from '../../config';
|
||||
import registerHashtag from '../register-hashtag';
|
||||
import isQuote from '../../misc/is-quote';
|
||||
import notesChart from '../../chart/notes';
|
||||
import perUserNotesChart from '../../chart/per-user-notes';
|
||||
import activeUsersChart from '../../chart/active-users';
|
||||
import notesChart from '../../services/chart/notes';
|
||||
import perUserNotesChart from '../../services/chart/per-user-notes';
|
||||
import activeUsersChart from '../../services/chart/active-users';
|
||||
import instanceChart from '../../services/chart/instance';
|
||||
|
||||
import { erase, concat } from '../../prelude/array';
|
||||
import insertNoteUnread from './unread';
|
||||
@ -229,8 +230,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
|
||||
}
|
||||
});
|
||||
|
||||
// TODO
|
||||
//perInstanceChart.newNote();
|
||||
instanceChart.updateNote(i.host, true);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,20 @@
|
||||
import Note, { INote } from '../../models/note';
|
||||
import { IUser, isLocalUser } from '../../models/user';
|
||||
import { IUser, isLocalUser, isRemoteUser } from '../../models/user';
|
||||
import { publishNoteStream } from '../stream';
|
||||
import renderDelete from '../../remote/activitypub/renderer/delete';
|
||||
import { renderActivity } from '../../remote/activitypub/renderer';
|
||||
import { deliver } from '../../queue';
|
||||
import Following from '../../models/following';
|
||||
import renderTombstone from '../../remote/activitypub/renderer/tombstone';
|
||||
import notesChart from '../../chart/notes';
|
||||
import perUserNotesChart from '../../chart/per-user-notes';
|
||||
import notesChart from '../../services/chart/notes';
|
||||
import perUserNotesChart from '../../services/chart/per-user-notes';
|
||||
import config from '../../config';
|
||||
import NoteUnread from '../../models/note-unread';
|
||||
import read from './read';
|
||||
import DriveFile from '../../models/drive-file';
|
||||
import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc';
|
||||
import Instance from '../../models/instance';
|
||||
import instanceChart from '../../services/chart/instance';
|
||||
|
||||
/**
|
||||
* 投稿を削除します。
|
||||
@ -91,4 +94,16 @@ export default async function(user: IUser, note: INote) {
|
||||
// 統計を更新
|
||||
notesChart.update(note, false);
|
||||
perUserNotesChart.update(user, note, false);
|
||||
|
||||
if (isRemoteUser(user)) {
|
||||
registerOrFetchInstanceDoc(user.host).then(i => {
|
||||
Instance.update({ _id: i._id }, {
|
||||
$inc: {
|
||||
notesCount: -1
|
||||
}
|
||||
});
|
||||
|
||||
instanceChart.updateNote(i.host, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import watch from '../watch';
|
||||
import renderLike from '../../../remote/activitypub/renderer/like';
|
||||
import { deliver } from '../../../queue';
|
||||
import { renderActivity } from '../../../remote/activitypub/renderer';
|
||||
import perUserReactionsChart from '../../../chart/per-user-reactions';
|
||||
import perUserReactionsChart from '../../../services/chart/per-user-reactions';
|
||||
|
||||
export default async (user: IUser, note: INote, reaction: string) => new Promise(async (res, rej) => {
|
||||
// Myself
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { IUser } from '../models/user';
|
||||
import Hashtag from '../models/hashtag';
|
||||
import hashtagChart from '../chart/hashtag';
|
||||
import hashtagChart from '../services/chart/hashtag';
|
||||
|
||||
export default async function(user: IUser, tag: string) {
|
||||
tag = tag.toLowerCase();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Instance, { IInstance } from '../models/instance';
|
||||
import federationChart from '../chart/federation';
|
||||
import federationChart from '../services/chart/federation';
|
||||
|
||||
export async function registerOrFetchInstanceDoc(host: string): Promise<IInstance> {
|
||||
if (host == null) return null;
|
||||
|
Reference in New Issue
Block a user