+
diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue
index cbb1890cc..3438462cd 100644
--- a/src/client/app/desktop/views/pages/admin/admin.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.vue
@@ -11,9 +11,7 @@
-
-
-
+
@@ -34,9 +32,7 @@ import XSuspendUser from "./admin.suspend-user.vue";
import XUnsuspendUser from "./admin.unsuspend-user.vue";
import XVerifyUser from "./admin.verify-user.vue";
import XUnverifyUser from "./admin.unverify-user.vue";
-import XUsersChart from "./admin.users-chart.vue";
-import XNotesChart from "./admin.notes-chart.vue";
-import XDriveChart from "./admin.drive-chart.vue";
+import XCharts from "../../components/charts.vue";
export default Vue.extend({
components: {
@@ -45,21 +41,13 @@ export default Vue.extend({
XUnsuspendUser,
XVerifyUser,
XUnverifyUser,
- XUsersChart,
- XNotesChart,
- XDriveChart
+ XCharts
},
data() {
return {
- page: 'dashboard',
- chart: null
+ page: 'dashboard'
};
},
- created() {
- (this as any).api('admin/chart').then(chart => {
- this.chart = chart;
- });
- },
methods: {
nav(page: string) {
this.page = page;
@@ -115,7 +103,7 @@ export default Vue.extend({
> div
max-width 800px
-.card
+.mk-admin-card
padding 32px
background #fff
box-shadow 0 2px 8px rgba(#000, 0.1)
diff --git a/src/client/app/desktop/views/pages/deck/deck.note.vue b/src/client/app/desktop/views/pages/deck/deck.note.vue
index c7df715a0..e6d062eac 100644
--- a/src/client/app/desktop/views/pages/deck/deck.note.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.note.vue
@@ -32,7 +32,7 @@
- %fa:map-marker-alt% %i18n:@location%
+ %fa:map-marker-alt% %i18n:@location%
diff --git a/src/client/app/desktop/views/pages/share.vue b/src/client/app/desktop/views/pages/share.vue
index 4dd608069..69ecbf115 100644
--- a/src/client/app/desktop/views/pages/share.vue
+++ b/src/client/app/desktop/views/pages/share.vue
@@ -16,7 +16,7 @@ import Vue from 'vue';
export default Vue.extend({
data() {
return {
- name: (this as any).os.instanceName,
+ name: null,
posted: false,
text: new URLSearchParams(location.search).get('text')
};
@@ -25,6 +25,11 @@ export default Vue.extend({
close() {
window.close();
}
+ },
+ mounted() {
+ (this as any).os.getMeta().then(meta => {
+ this.name = meta.name;
+ });
}
});
diff --git a/src/client/app/desktop/views/pages/stats/stats.vue b/src/client/app/desktop/views/pages/stats/stats.vue
new file mode 100644
index 000000000..41005b639
--- /dev/null
+++ b/src/client/app/desktop/views/pages/stats/stats.vue
@@ -0,0 +1,64 @@
+
+
+
+
%fa:user% {{ stats.originalUsersCount | number }}%i18n:@original-users%
+
%fa:user% {{ stats.usersCount | number }}%i18n:@all-users%
+
%fa:pencil-alt% {{ stats.originalNotesCount | number }}%i18n:@original-notes%
+
%fa:pencil-alt% {{ stats.notesCount | number }}%i18n:@all-notes%
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/app/desktop/views/pages/user/user.friends.vue b/src/client/app/desktop/views/pages/user/user.friends.vue
index 4af0f0bca..516eea028 100644
--- a/src/client/app/desktop/views/pages/user/user.friends.vue
+++ b/src/client/app/desktop/views/pages/user/user.friends.vue
@@ -40,10 +40,12 @@ export default Vue.extend({
diff --git a/src/client/app/desktop/views/pages/user/user.photos.vue b/src/client/app/desktop/views/pages/user/user.photos.vue
index ce7791a96..8397e5648 100644
--- a/src/client/app/desktop/views/pages/user/user.photos.vue
+++ b/src/client/app/desktop/views/pages/user/user.photos.vue
@@ -39,10 +39,12 @@ export default Vue.extend({
diff --git a/src/client/app/desktop/views/pages/user/user.vue b/src/client/app/desktop/views/pages/user/user.vue
index 300fd68f0..afb5e674d 100644
--- a/src/client/app/desktop/views/pages/user/user.vue
+++ b/src/client/app/desktop/views/pages/user/user.vue
@@ -138,7 +138,7 @@ root(isDark)
padding 16px
font-size 12px
color #aaa
- background #fff
+ background isDark ? #21242f : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
diff --git a/src/client/app/init.ts b/src/client/app/init.ts
index 18f510ea2..cf9795740 100644
--- a/src/client/app/init.ts
+++ b/src/client/app/init.ts
@@ -19,8 +19,8 @@ import { version, codename, lang } from './config';
let elementLocale;
switch (lang) {
- case 'ja': elementLocale = ElementLocaleJa; break;
- case 'en': elementLocale = ElementLocaleEn; break;
+ case 'ja-JP': elementLocale = ElementLocaleJa; break;
+ case 'en-US': elementLocale = ElementLocaleEn; break;
default: elementLocale = ElementLocaleEn; break;
}
diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue
index 317f08dcf..f9996f9da 100644
--- a/src/client/app/mobile/views/components/note-detail.vue
+++ b/src/client/app/mobile/views/components/note-detail.vue
@@ -45,7 +45,7 @@
-
%fa:map-marker-alt% %i18n:@location%
+
%fa:map-marker-alt% %i18n:@location%
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index 8fc8af7f8..d0cea135f 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -33,7 +33,7 @@
-
%fa:map-marker-alt% %i18n:@location%
+
%fa:map-marker-alt% %i18n:@location%
diff --git a/src/client/app/mobile/views/components/ui.nav.vue b/src/client/app/mobile/views/components/ui.nav.vue
index 74564a48b..39ea513b7 100644
--- a/src/client/app/mobile/views/components/ui.nav.vue
+++ b/src/client/app/mobile/views/components/ui.nav.vue
@@ -30,6 +30,7 @@
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 6b82be099..7437eb8b4 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -41,6 +41,12 @@
%i18n:@i-am-under-limited-internet%
+
+ %fa:volume-up% %i18n:@sound%
+
+ %i18n:@enable-sounds%
+
+
%fa:language% %i18n:@lang%
@@ -142,6 +148,11 @@ export default Vue.extend({
get() { return this.$store.state.device.lang; },
set(value) { this.$store.commit('device/set', { key: 'lang', value }); }
},
+
+ enableSounds: {
+ get() { return this.$store.state.device.enableSounds; },
+ set(value) { this.$store.commit('device/set', { key: 'enableSounds', value }); }
+ },
},
mounted() {
diff --git a/src/client/app/mobile/views/pages/share.vue b/src/client/app/mobile/views/pages/share.vue
index 588b0941e..d75763c52 100644
--- a/src/client/app/mobile/views/pages/share.vue
+++ b/src/client/app/mobile/views/pages/share.vue
@@ -16,7 +16,7 @@ import Vue from 'vue';
export default Vue.extend({
data() {
return {
- name: (this as any).os.instanceName,
+ name: null,
posted: false,
text: new URLSearchParams(location.search).get('text')
};
@@ -25,6 +25,11 @@ export default Vue.extend({
close() {
window.close();
}
+ },
+ mounted() {
+ (this as any).os.getMeta().then(meta => {
+ this.name = meta.name;
+ });
}
});
diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user.vue
index e72867352..8918847a8 100644
--- a/src/client/app/mobile/views/pages/user.vue
+++ b/src/client/app/mobile/views/pages/user.vue
@@ -11,7 +11,7 @@
-
+
diff --git a/src/client/app/store.ts b/src/client/app/store.ts
index ba91a11f2..469563495 100644
--- a/src/client/app/store.ts
+++ b/src/client/app/store.ts
@@ -13,6 +13,7 @@ const defaultSettings = {
showMaps: true,
showPostFormOnTopOfTl: false,
suggestRecentHashtags: true,
+ showClockOnHeader: true,
circleIcons: true,
gradientWindowHeader: false,
showReplyTarget: true,
diff --git a/src/config/load.ts b/src/config/load.ts
index 1c59f82b3..8929cf8d3 100644
--- a/src/config/load.ts
+++ b/src/config/load.ts
@@ -53,5 +53,5 @@ export default function load() {
}
function normalizeUrl(url: string) {
- return url[url.length - 1] === '/' ? url.substr(0, url.length - 1) : url;
+ return url.endsWith('/') ? url.substr(0, url.length - 1) : url;
}
diff --git a/src/config/types.ts b/src/config/types.ts
index f220e1582..a1dc9a5bd 100644
--- a/src/config/types.ts
+++ b/src/config/types.ts
@@ -62,6 +62,8 @@ export type Source = {
*/
ghost?: string;
+ summalyProxy?: string;
+
accesslog?: string;
twitter?: {
consumer_key: string;
diff --git a/src/docs/about.en.md b/src/docs/about.en-US.md
similarity index 100%
rename from src/docs/about.en.md
rename to src/docs/about.en-US.md
diff --git a/src/docs/about.ja.md b/src/docs/about.ja-JP.md
similarity index 100%
rename from src/docs/about.ja.md
rename to src/docs/about.ja-JP.md
diff --git a/src/docs/api.ja.md b/src/docs/api.ja-JP.md
similarity index 100%
rename from src/docs/api.ja.md
rename to src/docs/api.ja-JP.md
diff --git a/src/docs/api/endpoints/view.pug b/src/docs/api/endpoints/view.pug
index 76e118330..be7e84faa 100644
--- a/src/docs/api/endpoints/view.pug
+++ b/src/docs/api/endpoints/view.pug
@@ -15,7 +15,7 @@ block main
span.path= endpointUrl.path
if endpoint.desc
- p#desc= endpoint.desc[lang] || endpoint.desc['ja']
+ p#desc= endpoint.desc[lang] || endpoint.desc['ja-JP']
if endpoint.requireCredential
div.ui.info: p
diff --git a/src/docs/api/entities/drive-file.yaml b/src/docs/api/entities/drive-file.yaml
index 62dbec363..0c2195ac0 100644
--- a/src/docs/api/entities/drive-file.yaml
+++ b/src/docs/api/entities/drive-file.yaml
@@ -1,90 +1,90 @@
name: "DriveFile"
desc:
- ja: "ドライブのファイル。"
- en: "A file of Drive."
+ ja-JP: "ドライブのファイル。"
+ en-US: "A file of Drive."
props:
id:
type: "id"
optional: false
desc:
- ja: "ファイルID"
- en: "The ID of this file"
+ ja-JP: "ファイルID"
+ en-US: "The ID of this file"
createdAt:
type: "date"
optional: false
desc:
- ja: "アップロード日時"
- en: "The upload date of this file"
+ ja-JP: "アップロード日時"
+ en-US: "The upload date of this file"
userId:
type: "id(User)"
optional: false
desc:
- ja: "所有者ID"
- en: "The ID of the owner of this file"
+ ja-JP: "所有者ID"
+ en-US: "The ID of the owner of this file"
user:
type: "entity(User)"
optional: true
desc:
- ja: "所有者"
- en: "The owner of this file"
+ ja-JP: "所有者"
+ en-US: "The owner of this file"
name:
type: "string"
optional: false
desc:
- ja: "ファイル名"
- en: "The name of this file"
+ ja-JP: "ファイル名"
+ en-US: "The name of this file"
md5:
type: "string"
optional: false
desc:
- ja: "ファイルのMD5ハッシュ値"
- en: "The md5 hash value of this file"
+ ja-JP: "ファイルのMD5ハッシュ値"
+ en-US: "The md5 hash value of this file"
type:
type: "string"
optional: false
desc:
- ja: "ファイルの種類"
- en: "The type of this file"
+ ja-JP: "ファイルの種類"
+ en-US: "The type of this file"
datasize:
type: "number"
optional: false
desc:
- ja: "ファイルサイズ(bytes)"
- en: "The size of this file (bytes)"
+ ja-JP: "ファイルサイズ(bytes)"
+ en-US: "The size of this file (bytes)"
url:
type: "string"
optional: false
desc:
- ja: "ファイルのURL"
- en: "The URL of this file"
+ ja-JP: "ファイルのURL"
+ en-US: "The URL of this file"
folderId:
type: "id(DriveFolder)"
optional: true
desc:
- ja: "フォルダID"
- en: "The ID of the folder of this file"
+ ja-JP: "フォルダID"
+ en-US: "The ID of the folder of this file"
folder:
type: "entity(DriveFolder)"
optional: true
desc:
- ja: "フォルダ"
- en: "The folder of this file"
+ ja-JP: "フォルダ"
+ en-US: "The folder of this file"
isSensitive:
type: "boolean"
optional: true
desc:
- ja: "このメディアが「閲覧注意」(NSFW)かどうか"
- en: "Whether this media is NSFW"
+ ja-JP: "このメディアが「閲覧注意」(NSFW)かどうか"
+ en-US: "Whether this media is NSFW"
diff --git a/src/docs/api/entities/drive-folder.yaml b/src/docs/api/entities/drive-folder.yaml
index 0fb8308dd..e3dfd2ca0 100644
--- a/src/docs/api/entities/drive-folder.yaml
+++ b/src/docs/api/entities/drive-folder.yaml
@@ -1,41 +1,41 @@
name: "DriveFolder"
desc:
- ja: "ドライブのフォルダを表します。"
- en: "A folder of Drive."
+ ja-JP: "ドライブのフォルダを表します。"
+ en-US: "A folder of Drive."
props:
id:
type: "id"
optional: false
desc:
- ja: "フォルダID"
- en: "The ID of this folder"
+ ja-JP: "フォルダID"
+ en-US: "The ID of this folder"
createdAt:
type: "date"
optional: false
desc:
- ja: "作成日時"
- en: "The created date of this folder"
+ ja-JP: "作成日時"
+ en-US: "The created date of this folder"
userId:
type: "id(User)"
optional: false
desc:
- ja: "所有者ID"
- en: "The ID of the owner of this folder"
+ ja-JP: "所有者ID"
+ en-US: "The ID of the owner of this folder"
parentId:
type: "entity(DriveFolder)"
optional: false
desc:
- ja: "親フォルダのID (ルートなら null)"
- en: "The ID of parent folder"
+ ja-JP: "親フォルダのID (ルートなら null)"
+ en-US: "The ID of parent folder"
name:
type: "string"
optional: false
desc:
- ja: "フォルダ名"
- en: "The name of this folder"
+ ja-JP: "フォルダ名"
+ en-US: "The name of this folder"
diff --git a/src/docs/api/entities/note.yaml b/src/docs/api/entities/note.yaml
index 04cb3c982..cae9a53f8 100644
--- a/src/docs/api/entities/note.yaml
+++ b/src/docs/api/entities/note.yaml
@@ -1,190 +1,190 @@
name: "Note"
desc:
- ja: "投稿。"
- en: "A note."
+ ja-JP: "投稿。"
+ en-US: "A note."
props:
id:
type: "id"
optional: false
desc:
- ja: "投稿ID"
- en: "The ID of this note"
+ ja-JP: "投稿ID"
+ en-US: "The ID of this note"
createdAt:
type: "date"
optional: false
desc:
- ja: "投稿日時"
- en: "The posted date of this note"
+ ja-JP: "投稿日時"
+ en-US: "The posted date of this note"
viaMobile:
type: "boolean"
optional: true
desc:
- ja: "モバイル端末から投稿したか否か(自己申告であることに留意)"
- en: "Whether this note sent via a mobile device"
+ ja-JP: "モバイル端末から投稿したか否か(自己申告であることに留意)"
+ en-US: "Whether this note sent via a mobile device"
text:
type: "string"
optional: true
desc:
- ja: "投稿の本文"
- en: "The text of this note"
+ ja-JP: "投稿の本文"
+ en-US: "The text of this note"
mediaIds:
type: "id(DriveFile)[]"
optional: true
desc:
- ja: "添付されているメディアのID (なければレスポンスでは空配列)"
- en: "The IDs of the attached media (empty array for response if no media is attached)"
+ ja-JP: "添付されているメディアのID (なければレスポンスでは空配列)"
+ en-US: "The IDs of the attached media (empty array for response if no media is attached)"
media:
type: "entity(DriveFile)[]"
optional: true
desc:
- ja: "添付されているメディア"
- en: "The attached media"
+ ja-JP: "添付されているメディア"
+ en-US: "The attached media"
userId:
type: "id(User)"
optional: false
desc:
- ja: "投稿者ID"
- en: "The ID of author of this note"
+ ja-JP: "投稿者ID"
+ en-US: "The ID of author of this note"
user:
type: "entity(User)"
optional: true
desc:
- ja: "投稿者"
- en: "The author of this note"
+ ja-JP: "投稿者"
+ en-US: "The author of this note"
myReaction:
type: "string"
optional: true
desc:
- ja: "この投稿に対する自分の
リアクション"
- en: "The your
reaction of this note"
+ ja-JP: "この投稿に対する自分の
リアクション"
+ en-US: "The your
reaction of this note"
reactionCounts:
type: "object"
optional: false
desc:
- ja: "
リアクションをキーとし、この投稿に対するそのリアクションの数を値としたオブジェクト"
+ ja-JP: "
リアクションをキーとし、この投稿に対するそのリアクションの数を値としたオブジェクト"
replyId:
type: "id(Note)"
optional: true
desc:
- ja: "返信した投稿のID"
- en: "The ID of the replyed note"
+ ja-JP: "返信した投稿のID"
+ en-US: "The ID of the replyed note"
reply:
type: "entity(Note)"
optional: true
desc:
- ja: "返信した投稿"
- en: "The replyed note"
+ ja-JP: "返信した投稿"
+ en-US: "The replyed note"
renoteId:
type: "id(Note)"
optional: true
desc:
- ja: "引用した投稿のID"
- en: "The ID of the quoted note"
+ ja-JP: "引用した投稿のID"
+ en-US: "The ID of the quoted note"
renote:
type: "entity(Note)"
optional: true
desc:
- ja: "引用した投稿"
- en: "The quoted note"
+ ja-JP: "引用した投稿"
+ en-US: "The quoted note"
poll:
type: "object"
optional: true
desc:
- ja: "投票"
- en: "The poll"
+ ja-JP: "投票"
+ en-US: "The poll"
props:
choices:
type: "object[]"
optional: false
desc:
- ja: "投票の選択肢"
- en: "The choices of this poll"
+ ja-JP: "投票の選択肢"
+ en-US: "The choices of this poll"
props:
id:
type: "number"
optional: false
desc:
- ja: "選択肢ID"
- en: "The ID of this choice"
+ ja-JP: "選択肢ID"
+ en-US: "The ID of this choice"
isVoted:
type: "boolean"
optional: true
desc:
- ja: "自分がこの選択肢に投票したかどうか"
- en: "Whether you voted to this choice"
+ ja-JP: "自分がこの選択肢に投票したかどうか"
+ en-US: "Whether you voted to this choice"
text:
type: "string"
optional: false
desc:
- ja: "選択肢本文"
- en: "The text of this choice"
+ ja-JP: "選択肢本文"
+ en-US: "The text of this choice"
votes:
type: "number"
optional: false
desc:
- ja: "この選択肢に投票された数"
- en: "The number voted for this choice"
+ ja-JP: "この選択肢に投票された数"
+ en-US: "The number voted for this choice"
geo:
type: "object"
optional: true
desc:
- ja: "位置情報"
- en: "Geo location"
+ ja-JP: "位置情報"
+ en-US: "Geo location"
props:
coordinates:
type: "number[]"
optional: false
desc:
- ja: "座標。最初に経度:-180〜180で表す。最後に緯度:-90〜90で表す。"
+ ja-JP: "座標。最初に経度:-180〜180で表す。最後に緯度:-90〜90で表す。"
altitude:
type: "number"
optional: false
desc:
- ja: "高度。メートル単位で表す。"
+ ja-JP: "高度。メートル単位で表す。"
accuracy:
type: "number"
optional: false
desc:
- ja: "緯度、経度の精度。メートル単位で表す。"
+ ja-JP: "緯度、経度の精度。メートル単位で表す。"
altitudeAccuracy:
type: "number"
optional: false
desc:
- ja: "高度の精度。メートル単位で表す。"
+ ja-JP: "高度の精度。メートル単位で表す。"
heading:
type: "number"
optional: false
desc:
- ja: "方角。0〜360の角度で表す。0が北、90が東、180が南、270が西。"
+ ja-JP: "方角。0〜360の角度で表す。0が北、90が東、180が南、270が西。"
speed:
type: "number"
optional: false
desc:
- ja: "速度。メートル / 秒数で表す。"
+ ja-JP: "速度。メートル / 秒数で表す。"
diff --git a/src/docs/api/entities/user.yaml b/src/docs/api/entities/user.yaml
index c24597456..c90b55ee8 100644
--- a/src/docs/api/entities/user.yaml
+++ b/src/docs/api/entities/user.yaml
@@ -1,174 +1,174 @@
name: "User"
desc:
- ja: "ユーザー。"
- en: "A user."
+ ja-JP: "ユーザー。"
+ en-US: "A user."
props:
id:
type: "id"
optional: false
desc:
- ja: "ユーザーID"
- en: "The ID of this user"
+ ja-JP: "ユーザーID"
+ en-US: "The ID of this user"
createdAt:
type: "date"
optional: false
desc:
- ja: "アカウント作成日時"
- en: "The registered date of this user"
+ ja-JP: "アカウント作成日時"
+ en-US: "The registered date of this user"
username:
type: "string"
optional: false
desc:
- ja: "ユーザー名"
- en: "The username of this user"
+ ja-JP: "ユーザー名"
+ en-US: "The username of this user"
description:
type: "string"
optional: false
desc:
- ja: "アカウントの説明(自己紹介)"
- en: "The description of this user"
+ ja-JP: "アカウントの説明(自己紹介)"
+ en-US: "The description of this user"
avatarId:
type: "id(DriveFile)"
optional: true
desc:
- ja: "アバターのID"
- en: "The ID of the avatar of this user"
+ ja-JP: "アバターのID"
+ en-US: "The ID of the avatar of this user"
avatarUrl:
type: "string"
optional: false
desc:
- ja: "アバターのURL"
- en: "The URL of the avatar of this user"
+ ja-JP: "アバターのURL"
+ en-US: "The URL of the avatar of this user"
bannerId:
type: "id(DriveFile)"
optional: true
desc:
- ja: "バナーのID"
- en: "The ID of the banner of this user"
+ ja-JP: "バナーのID"
+ en-US: "The ID of the banner of this user"
bannerUrl:
type: "string"
optional: false
desc:
- ja: "バナーのURL"
- en: "The URL of the banner of this user"
+ ja-JP: "バナーのURL"
+ en-US: "The URL of the banner of this user"
followersCount:
type: "number"
optional: false
desc:
- ja: "フォロワーの数"
- en: "The number of the followers for this user"
+ ja-JP: "フォロワーの数"
+ en-US: "The number of the followers for this user"
followingCount:
type: "number"
optional: false
desc:
- ja: "フォローしているユーザーの数"
- en: "The number of the following users for this user"
+ ja-JP: "フォローしているユーザーの数"
+ en-US: "The number of the following users for this user"
isFollowing:
type: "boolean"
optional: true
desc:
- ja: "自分がこのユーザーをフォローしているか"
+ ja-JP: "自分がこのユーザーをフォローしているか"
isFollowed:
type: "boolean"
optional: true
desc:
- ja: "自分がこのユーザーにフォローされているか"
+ ja-JP: "自分がこのユーザーにフォローされているか"
isMuted:
type: "boolean"
optional: true
desc:
- ja: "自分がこのユーザーをミュートしているか"
- en: "Whether you muted this user"
+ ja-JP: "自分がこのユーザーをミュートしているか"
+ en-US: "Whether you muted this user"
notesCount:
type: "number"
optional: false
desc:
- ja: "投稿の数"
- en: "The number of the notes of this user"
+ ja-JP: "投稿の数"
+ en-US: "The number of the notes of this user"
pinnedNote:
type: "entity(Note)"
optional: true
desc:
- ja: "ピン留めされた投稿"
- en: "The pinned note of this user"
+ ja-JP: "ピン留めされた投稿"
+ en-US: "The pinned note of this user"
pinnedNoteId:
type: "id(Note)"
optional: true
desc:
- ja: "ピン留めされた投稿のID"
- en: "The ID of the pinned note of this user"
+ ja-JP: "ピン留めされた投稿のID"
+ en-US: "The ID of the pinned note of this user"
host:
type: "string | null"
optional: false
desc:
- ja: "ホスト (例: example.com:3000)"
- en: "Host (e.g. example.com:3000)"
+ ja-JP: "ホスト (例: example.com:3000)"
+ en-US: "Host (e.g. example.com:3000)"
twitter:
type: "object"
optional: true
desc:
- ja: "連携されているTwitterアカウント情報"
- en: "The info of the connected twitter account of this user"
+ ja-JP: "連携されているTwitterアカウント情報"
+ en-US: "The info of the connected twitter account of this user"
props:
userId:
type: "string"
optional: false
desc:
- ja: "ユーザーID"
- en: "The user ID"
+ ja-JP: "ユーザーID"
+ en-US: "The user ID"
screenName:
type: "string"
optional: false
desc:
- ja: "ユーザー名"
- en: "The screen name of this user"
+ ja-JP: "ユーザー名"
+ en-US: "The screen name of this user"
isBot:
type: "boolean"
optional: true
desc:
- ja: "botか否か(自己申告であることに留意)"
- en: "Whether is bot or not"
+ ja-JP: "botか否か(自己申告であることに留意)"
+ en-US: "Whether is bot or not"
profile:
type: "object"
optional: false
desc:
- ja: "プロフィール"
- en: "The profile of this user"
+ ja-JP: "プロフィール"
+ en-US: "The profile of this user"
props:
location:
type: "string"
optional: true
desc:
- ja: "場所"
- en: "The location of this user"
+ ja-JP: "場所"
+ en-US: "The location of this user"
birthday:
type: "string"
optional: true
desc:
- ja: "誕生日 (YYYY-MM-DD)"
- en: "The birthday of this user (YYYY-MM-DD)"
+ ja-JP: "誕生日 (YYYY-MM-DD)"
+ en-US: "The birthday of this user (YYYY-MM-DD)"
diff --git a/src/docs/api/entities/view.pug b/src/docs/api/entities/view.pug
index d5c192f43..1f166d053 100644
--- a/src/docs/api/entities/view.pug
+++ b/src/docs/api/entities/view.pug
@@ -7,7 +7,7 @@ block meta
block main
h1= name
- p#desc= desc[lang] || desc['ja']
+ p#desc= desc[lang] || desc['ja-JP']
section
h2= i18n('docs.api.entities.properties')
diff --git a/src/docs/api/mixins.pug b/src/docs/api/mixins.pug
index 925aab293..563739d52 100644
--- a/src/docs/api/mixins.pug
+++ b/src/docs/api/mixins.pug
@@ -31,4 +31,4 @@ mixin propTable(props)
td.name= prop.name
td.type
+type(prop)
- td.desc!= prop.desc ? prop.desc[lang] || prop.desc['ja'] : null
+ td.desc!= prop.desc ? prop.desc[lang] || prop.desc['ja-JP'] : null
diff --git a/src/docs/base.pug b/src/docs/base.pug
index aeafaefff..26f19ddf0 100644
--- a/src/docs/base.pug
+++ b/src/docs/base.pug
@@ -16,7 +16,7 @@ html(lang= lang)
nav
ul
each doc in docs
- li: a(href=`/docs/${lang}/${doc.name}`)= doc.title[lang] || doc.title['ja']
+ li: a(href=`/docs/${lang}/${doc.name}`)= doc.title[lang] || doc.title['ja-JP']
section
h2 API
ul
diff --git a/src/docs/follow.ja.md b/src/docs/follow.ja-JP.md
similarity index 100%
rename from src/docs/follow.ja.md
rename to src/docs/follow.ja-JP.md
diff --git a/src/docs/mute.ja.md b/src/docs/mute.ja-JP.md
similarity index 100%
rename from src/docs/mute.ja.md
rename to src/docs/mute.ja-JP.md
diff --git a/src/docs/reversi-bot.ja.md b/src/docs/reversi-bot.ja-JP.md
similarity index 100%
rename from src/docs/reversi-bot.ja.md
rename to src/docs/reversi-bot.ja-JP.md
diff --git a/src/docs/stream.ja.md b/src/docs/stream.ja-JP.md
similarity index 100%
rename from src/docs/stream.ja.md
rename to src/docs/stream.ja-JP.md
diff --git a/src/docs/timelines.ja.md b/src/docs/timelines.ja-JP.md
similarity index 100%
rename from src/docs/timelines.ja.md
rename to src/docs/timelines.ja-JP.md
diff --git a/src/mfm/parse/core/syntax-highlighter.ts b/src/mfm/parse/core/syntax-highlighter.ts
index 3fb7a3b73..2b13608d2 100644
--- a/src/mfm/parse/core/syntax-highlighter.ts
+++ b/src/mfm/parse/core/syntax-highlighter.ts
@@ -197,7 +197,7 @@ const elements: Element[] = [
if (thisIsNotARegexp) return null;
if (regexp == '') return null;
- if (regexp[0] == ' ' && regexp[regexp.length - 1] == ' ') return null;
+ if (regexp.startsWith(' ') && regexp.endsWith(' ')) return null;
return {
html: `
/${escape(regexp)}/`,
diff --git a/src/mfm/parse/elements/hashtag.ts b/src/mfm/parse/elements/hashtag.ts
index 129041774..f4b6a78fa 100644
--- a/src/mfm/parse/elements/hashtag.ts
+++ b/src/mfm/parse/elements/hashtag.ts
@@ -10,7 +10,7 @@ export type TextElementHashtag = {
export default function(text: string, i: number) {
if (!(/^\s#[^\s]+/.test(text) || (i == 0 && /^#[^\s]+/.test(text)))) return null;
- const isHead = text[0] == '#';
+ const isHead = text.startsWith('#');
const hashtag = text.match(/^\s?#[^\s]+/)[0];
const res: any[] = !isHead ? [{
type: 'text',
diff --git a/src/mfm/parse/elements/link.ts b/src/mfm/parse/elements/link.ts
index b353aebc5..796aeb1ab 100644
--- a/src/mfm/parse/elements/link.ts
+++ b/src/mfm/parse/elements/link.ts
@@ -13,7 +13,7 @@ export type TextElementLink = {
export default function(text: string) {
const match = text.match(/^\??\[([^\[\]]+?)\]\((https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.=\+\-]+?)\)/);
if (!match) return null;
- const silent = text[0] == '?';
+ const silent = text.startsWith('?');
const link = match[0];
const title = match[1];
const url = match[2];
diff --git a/src/misc/fa.ts b/src/misc/fa.ts
index 077bb51e6..8be06362c 100644
--- a/src/misc/fa.ts
+++ b/src/misc/fa.ts
@@ -25,9 +25,9 @@ export const replacement = (match: string, key: string) => {
arg == 'S' ? 'fas' :
arg == 'B' ? 'fab' :
'';
- } else if (arg[0] == '.') {
+ } else if (arg.startsWith('.')) {
classes.push('fa-' + arg.substr(1));
- } else if (arg[0] == '-') {
+ } else if (arg.startsWith('-')) {
transform = arg.substr(1).split('|').join(' ');
} else {
name = arg;
diff --git a/src/misc/i18n.ts b/src/misc/i18n.ts
index a07af3e93..3dbfd7fe7 100644
--- a/src/misc/i18n.ts
+++ b/src/misc/i18n.ts
@@ -27,10 +27,12 @@ export default class Replacer {
let text = texts;
if (path) {
+ path = path.replace('.ts', '');
+
if (text.hasOwnProperty(path)) {
text = text[path];
} else {
- if (this.lang === 'ja') console.warn(`path '${path}' not found`);
+ if (this.lang === 'ja-JP') console.warn(`path '${path}' not found`);
return key; // Fallback
}
}
@@ -46,10 +48,10 @@ export default class Replacer {
});
if (error) {
- if (this.lang === 'ja') console.warn(`key '${key}' not found in '${path}'`);
+ if (this.lang === 'ja-JP') console.warn(`key '${key}' not found in '${path}'`);
return key; // Fallback
} else if (typeof text !== 'string') {
- if (this.lang === 'ja') console.warn(`key '${key}' is not string in '${path}'`);
+ if (this.lang === 'ja-JP') console.warn(`key '${key}' is not string in '${path}'`);
return key; // Fallback
} else {
return text;
diff --git a/src/models/stats.ts b/src/models/stats.ts
index 7bff475c6..326bfacc8 100644
--- a/src/models/stats.ts
+++ b/src/models/stats.ts
@@ -2,40 +2,59 @@ import * as mongo from 'mongodb';
import db from '../db/mongodb';
const Stats = db.get
('stats');
-Stats.createIndex({ date: -1 }, { unique: true });
+Stats.dropIndex({ date: -1 }); // 後方互換性のため
+Stats.createIndex({ span: -1, date: -1 }, { unique: true });
export default Stats;
export interface IStats {
_id: mongo.ObjectID;
+ /**
+ * 集計日時
+ */
date: Date;
+ /**
+ * 集計期間
+ */
+ span: 'day' | 'hour';
+
/**
* ユーザーに関する統計
*/
users: {
local: {
/**
- * この日時点での、ローカルのユーザーの総計
+ * 集計期間時点での、全ユーザー数 (ローカル)
*/
total: number;
/**
- * ローカルのユーザー数の前日比
+ * 増加したユーザー数 (ローカル)
*/
- diff: number;
+ inc: number;
+
+ /**
+ * 減少したユーザー数 (ローカル)
+ */
+ dec: number;
};
remote: {
/**
- * この日時点での、リモートのユーザーの総計
+ * 集計期間時点での、全ユーザー数 (リモート)
*/
total: number;
/**
- * リモートのユーザー数の前日比
+ * 増加したユーザー数 (リモート)
*/
- diff: number;
+ inc: number;
+
+ /**
+ * 減少したユーザー数 (リモート)
+ */
+ dec: number;
};
};
@@ -45,28 +64,33 @@ export interface IStats {
notes: {
local: {
/**
- * この日時点での、ローカルの投稿の総計
+ * 集計期間時点での、全投稿数 (ローカル)
*/
total: number;
/**
- * ローカルの投稿数の前日比
+ * 増加した投稿数 (ローカル)
*/
- diff: number;
+ inc: number;
+
+ /**
+ * 減少した投稿数 (ローカル)
+ */
+ dec: number;
diffs: {
/**
- * ローカルの通常の投稿数の前日比
+ * 通常の投稿数の差分 (ローカル)
*/
normal: number;
/**
- * ローカルのリプライの投稿数の前日比
+ * リプライの投稿数の差分 (ローカル)
*/
reply: number;
/**
- * ローカルのRenoteの投稿数の前日比
+ * Renoteの投稿数の差分 (ローカル)
*/
renote: number;
};
@@ -74,28 +98,33 @@ export interface IStats {
remote: {
/**
- * この日時点での、リモートの投稿の総計
+ * 集計期間時点での、全投稿数 (リモート)
*/
total: number;
/**
- * リモートの投稿数の前日比
+ * 増加した投稿数 (リモート)
*/
- diff: number;
+ inc: number;
+
+ /**
+ * 減少した投稿数 (リモート)
+ */
+ dec: number;
diffs: {
/**
- * リモートの通常の投稿数の前日比
+ * 通常の投稿数の差分 (リモート)
*/
normal: number;
/**
- * リモートのリプライの投稿数の前日比
+ * リプライの投稿数の差分 (リモート)
*/
reply: number;
/**
- * リモートのRenoteの投稿数の前日比
+ * Renoteの投稿数の差分 (リモート)
*/
renote: number;
};
@@ -108,46 +137,66 @@ export interface IStats {
drive: {
local: {
/**
- * この日時点での、ローカルのドライブファイル数の総計
+ * 集計期間時点での、全ドライブファイル数 (ローカル)
*/
totalCount: number;
/**
- * この日時点での、ローカルのドライブファイルサイズの総計
+ * 集計期間時点での、全ドライブファイルの合計サイズ (ローカル)
*/
totalSize: number;
/**
- * ローカルのドライブファイル数の前日比
+ * 増加したドライブファイル数 (ローカル)
*/
- diffCount: number;
+ incCount: number;
/**
- * ローカルのドライブファイルサイズの前日比
+ * 増加したドライブ使用量 (ローカル)
*/
- diffSize: number;
+ incSize: number;
+
+ /**
+ * 減少したドライブファイル数 (ローカル)
+ */
+ decCount: number;
+
+ /**
+ * 減少したドライブ使用量 (ローカル)
+ */
+ decSize: number;
};
remote: {
/**
- * この日時点での、リモートのドライブファイル数の総計
+ * 集計期間時点での、全ドライブファイル数 (リモート)
*/
totalCount: number;
/**
- * この日時点での、リモートのドライブファイルサイズの総計
+ * 集計期間時点での、全ドライブファイルの合計サイズ (リモート)
*/
totalSize: number;
/**
- * リモートのドライブファイル数の前日比
+ * 増加したドライブファイル数 (リモート)
*/
- diffCount: number;
+ incCount: number;
/**
- * リモートのドライブファイルサイズの前日比
+ * 増加したドライブ使用量 (リモート)
*/
- diffSize: number;
+ incSize: number;
+
+ /**
+ * 減少したドライブファイル数 (リモート)
+ */
+ decCount: number;
+
+ /**
+ * 減少したドライブ使用量 (リモート)
+ */
+ decSize: number;
};
};
}
diff --git a/src/queue/processors/http/process-inbox.ts b/src/queue/processors/http/process-inbox.ts
index 0738853dd..c9c2fa72c 100644
--- a/src/queue/processors/http/process-inbox.ts
+++ b/src/queue/processors/http/process-inbox.ts
@@ -46,7 +46,7 @@ export default async (job: bq.Job, done: any): Promise => {
// アクティビティを送信してきたユーザーがまだMisskeyサーバーに登録されていなかったら登録する
if (user === null) {
- user = await resolvePerson(signature.keyId) as IRemoteUser;
+ user = await resolvePerson(activity.actor) as IRemoteUser;
}
}
diff --git a/src/remote/activitypub/models/note.ts b/src/remote/activitypub/models/note.ts
index 02bce6fec..1dfeebfdf 100644
--- a/src/remote/activitypub/models/note.ts
+++ b/src/remote/activitypub/models/note.ts
@@ -131,5 +131,7 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
//#endregion
// リモートサーバーからフェッチしてきて登録
- return await createNote(value, resolver);
+ // ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにノートが生成されるが
+ // 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
+ return await createNote(uri, resolver);
}
diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts
index 61bcf77c4..3bd4e1676 100644
--- a/src/remote/activitypub/models/person.ts
+++ b/src/remote/activitypub/models/person.ts
@@ -4,18 +4,25 @@ import * as debug from 'debug';
import config from '../../../config';
import User, { validateUsername, isValidName, IUser, IRemoteUser } from '../../../models/user';
-import webFinger from '../../webfinger';
import Resolver from '../resolver';
import { resolveImage } from './image';
-import { isCollectionOrOrderedCollection, IObject, IPerson } from '../type';
+import { isCollectionOrOrderedCollection, IPerson } from '../type';
import { IDriveFile } from '../../../models/drive-file';
import Meta from '../../../models/meta';
import htmlToMFM from '../../../mfm/html-to-mfm';
import { updateUserStats } from '../../../services/update-chart';
+import { URL } from 'url';
const log = debug('misskey:activitypub');
-function validatePerson(x: any) {
+/**
+ * Validate Person object
+ * @param x Fetched person object
+ * @param uri Fetch target URI
+ */
+function validatePerson(x: any, uri: string) {
+ const expectHost = toUnicode(new URL(uri).hostname.toLowerCase());
+
if (x == null) {
return new Error('invalid person: object is null');
}
@@ -40,6 +47,24 @@ function validatePerson(x: any) {
return new Error('invalid person: invalid name');
}
+ if (typeof x.id !== 'string') {
+ return new Error('invalid person: id is not a string');
+ }
+
+ const idHost = toUnicode(new URL(x.id).hostname.toLowerCase());
+ if (idHost !== expectHost) {
+ return new Error('invalid person: id has different host');
+ }
+
+ if (typeof x.publicKey.id !== 'string') {
+ return new Error('invalid person: publicKey.id is not a string');
+ }
+
+ const publicKeyIdHost = toUnicode(new URL(x.publicKey.id).hostname.toLowerCase());
+ if (publicKeyIdHost !== expectHost) {
+ return new Error('invalid person: publicKey.id has different host');
+ }
+
return null;
}
@@ -48,8 +73,8 @@ function validatePerson(x: any) {
*
* Misskeyに対象のPersonが登録されていればそれを返します。
*/
-export async function fetchPerson(value: string | IObject, resolver?: Resolver): Promise {
- const uri = typeof value == 'string' ? value : value.id;
+export async function fetchPerson(uri: string, resolver?: Resolver): Promise {
+ if (typeof uri !== 'string') throw 'uri is not string';
// URIがこのサーバーを指しているならデータベースからフェッチ
if (uri.startsWith(config.url + '/')) {
@@ -71,12 +96,14 @@ export async function fetchPerson(value: string | IObject, resolver?: Resolver):
/**
* Personを作成します。
*/
-export async function createPerson(value: any, resolver?: Resolver): Promise {
+export async function createPerson(uri: string, resolver?: Resolver): Promise {
+ if (typeof uri !== 'string') throw 'uri is not string';
+
if (resolver == null) resolver = new Resolver();
- const object = await resolver.resolve(value) as any;
+ const object = await resolver.resolve(uri) as any;
- const err = validatePerson(object);
+ const err = validatePerson(object, uri);
if (err) {
throw err;
@@ -86,7 +113,7 @@ export async function createPerson(value: any, resolver?: Resolver): Promise isCollectionOrOrderedCollection(resolved) ? resolved.totalItems : undefined,
() => undefined
@@ -98,11 +125,10 @@ export async function createPerson(value: any, resolver?: Resolver): Promise isCollectionOrOrderedCollection(resolved) ? resolved.totalItems : undefined,
() => undefined
- ),
- webFinger(person.id)
+ )
]);
- const host = toUnicode(finger.subject.replace(/^.*?@/, '')).toLowerCase();
+ const host = toUnicode(new URL(object.id).hostname.toLowerCase());
const isBot = object.type == 'Service';
@@ -166,8 +192,8 @@ export async function createPerson(value: any, resolver?: Resolver): Promise {
- const uri = typeof value == 'string' ? value : value.id;
+export async function updatePerson(uri: string, resolver?: Resolver): Promise {
+ if (typeof uri !== 'string') throw 'uri is not string';
// URIがこのサーバーを指しているならスキップ
if (uri.startsWith(config.url + '/')) {
@@ -210,9 +236,9 @@ export async function updatePerson(value: string | IObject, resolver?: Resolver)
if (resolver == null) resolver = new Resolver();
- const object = await resolver.resolve(value) as any;
+ const object = await resolver.resolve(uri) as any;
- const err = validatePerson(object);
+ const err = validatePerson(object, uri);
if (err) {
throw err;
@@ -255,7 +281,7 @@ export async function updatePerson(value: string | IObject, resolver?: Resolver)
sharedInbox: person.sharedInbox,
avatarId: avatar ? avatar._id : null,
bannerId: banner ? banner._id : null,
- avatarUrl: avatar && avatar.metadata.url ? avatar.metadata.url : null,
+ avatarUrl: (avatar && avatar.metadata.thumbnailUrl) ? avatar.metadata.thumbnailUrl : (avatar && avatar.metadata.url) ? avatar.metadata.url : null,
bannerUrl: banner && banner.metadata.url ? banner.metadata.url : null,
description: htmlToMFM(person.summary),
followersCount,
@@ -275,8 +301,8 @@ export async function updatePerson(value: string | IObject, resolver?: Resolver)
* Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
*/
-export async function resolvePerson(value: string | IObject, verifier?: string): Promise {
- const uri = typeof value == 'string' ? value : value.id;
+export async function resolvePerson(uri: string, verifier?: string): Promise {
+ if (typeof uri !== 'string') throw 'uri is not string';
//#region このサーバーに既に登録されていたらそれを返す
const exist = await fetchPerson(uri);
@@ -287,5 +313,5 @@ export async function resolvePerson(value: string | IObject, verifier?: string):
//#endregion
// リモートサーバーからフェッチしてきて登録
- return await createPerson(value);
+ return await createPerson(uri);
}
diff --git a/src/remote/activitypub/renderer/announce.ts b/src/remote/activitypub/renderer/announce.ts
index 6d5a67b5c..f6276ade0 100644
--- a/src/remote/activitypub/renderer/announce.ts
+++ b/src/remote/activitypub/renderer/announce.ts
@@ -6,6 +6,7 @@ export default (object: any, note: INote) => {
return {
id: `${config.url}/notes/${note._id}`,
+ actor: `${config.url}/users/${note.userId}`,
type: 'Announce',
published: note.createdAt.toISOString(),
to: ['https://www.w3.org/ns/activitystreams#Public'],
diff --git a/src/remote/activitypub/renderer/create.ts b/src/remote/activitypub/renderer/create.ts
index b8bf98a65..1ee1418fc 100644
--- a/src/remote/activitypub/renderer/create.ts
+++ b/src/remote/activitypub/renderer/create.ts
@@ -1,4 +1,17 @@
-export default (object: any) => ({
- type: 'Create',
- object
-});
+import config from '../../../config';
+import { INote } from '../../../models/note';
+
+export default (object: any, note: INote) => {
+ const activity = {
+ id: `${config.url}/notes/${note._id}/activity`,
+ actor: `${config.url}/users/${note.userId}`,
+ type: 'Create',
+ published: note.createdAt.toISOString(),
+ object
+ } as any;
+
+ if (object.to) activity.to = object.to;
+ if (object.cc) activity.cc = object.cc;
+
+ return activity;
+};
diff --git a/src/remote/activitypub/renderer/delete.ts b/src/remote/activitypub/renderer/delete.ts
index f468a22e2..2a4e70e25 100644
--- a/src/remote/activitypub/renderer/delete.ts
+++ b/src/remote/activitypub/renderer/delete.ts
@@ -1,4 +1,8 @@
-export default (object: any) => ({
+import config from '../../../config';
+import { ILocalUser } from "../../../models/user";
+
+export default (object: any, user: ILocalUser) => ({
type: 'Delete',
+ actor: `${config.url}/users/${user._id}`,
object
});
diff --git a/src/remote/activitypub/renderer/index.ts b/src/remote/activitypub/renderer/index.ts
index ee7f49616..55b2801ca 100644
--- a/src/remote/activitypub/renderer/index.ts
+++ b/src/remote/activitypub/renderer/index.ts
@@ -1,7 +1,16 @@
-export default (x: any) => Object.assign({
- '@context': [
- 'https://www.w3.org/ns/activitystreams',
- 'https://w3id.org/security/v1',
- { Hashtag: 'as:Hashtag' }
- ]
-}, x);
+import config from '../../../config';
+import * as uuid from 'uuid';
+
+export default (x: any) => {
+ if (x !== null && typeof x === 'object' && x.id == null) {
+ x.id = `${config.url}/${uuid.v4()}`;
+ }
+
+ return Object.assign({
+ '@context': [
+ 'https://www.w3.org/ns/activitystreams',
+ 'https://w3id.org/security/v1',
+ { Hashtag: 'as:Hashtag' }
+ ]
+ }, x);
+};
diff --git a/src/remote/activitypub/renderer/undo.ts b/src/remote/activitypub/renderer/undo.ts
index 4498409a5..bf90a3f28 100644
--- a/src/remote/activitypub/renderer/undo.ts
+++ b/src/remote/activitypub/renderer/undo.ts
@@ -1,4 +1,8 @@
-export default (object: any) => ({
+import config from '../../../config';
+import { ILocalUser, IUser } from "../../../models/user";
+
+export default (object: any, user: ILocalUser | IUser) => ({
type: 'Undo',
+ actor: `${config.url}/users/${user._id}`,
object
});
diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts
index 585c1c0ce..6238d3acb 100644
--- a/src/remote/activitypub/request.ts
+++ b/src/remote/activitypub/request.ts
@@ -19,6 +19,9 @@ export default (user: ILocalUser, url: string, object: any) => new Promise((reso
port,
method: 'POST',
path: pathname + search,
+ headers: {
+ 'Content-Type': 'application/activity+json'
+ }
}, res => {
log(`${url} --> ${res.statusCode}`);
@@ -32,7 +35,7 @@ export default (user: ILocalUser, url: string, object: any) => new Promise((reso
sign(req, {
authorizationHeaderName: 'Signature',
key: user.keypair,
- keyId: `acct:${user.username}@${config.host}`
+ keyId: `${config.url}/users/${user._id}/publickey`
});
// Signature: Signature ... => Signature: ...
diff --git a/src/remote/resolve-user.ts b/src/remote/resolve-user.ts
index 1e8fc5d75..e199b6f14 100644
--- a/src/remote/resolve-user.ts
+++ b/src/remote/resolve-user.ts
@@ -15,7 +15,7 @@ export default async (username: string, _host: string, option?: any): Promise token[0] == '!';
+export default (token: string) => token.startsWith('!');
diff --git a/src/server/api/endpoints/admin/chart.ts b/src/server/api/endpoints/admin/chart.ts
deleted file mode 100644
index a0566b11f..000000000
--- a/src/server/api/endpoints/admin/chart.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import Stats, { IStats } from '../../../../models/stats';
-
-type Omit = Pick>;
-
-export const meta = {
- requireCredential: true,
- requireAdmin: true
-};
-
-export default (params: any) => new Promise(async (res, rej) => {
- const now = new Date();
- const y = now.getFullYear();
- const m = now.getMonth();
- const d = now.getDate();
-
- const stats = await Stats.find({
- date: {
- $gt: new Date(y - 1, m, d)
- }
- }, {
- sort: {
- date: -1
- },
- fields: {
- _id: 0
- }
- });
-
- const chart: Array> = [];
-
- for (let i = 364; i >= 0; i--) {
- const day = new Date(y, m, d - i);
-
- const stat = stats.find(s => s.date.getTime() == day.getTime());
-
- if (stat) {
- chart.unshift(stat);
- } else { // 隙間埋め
- const mostRecent = stats.find(s => s.date.getTime() < day.getTime());
- if (mostRecent) {
- chart.unshift(Object.assign({}, mostRecent, {
- date: day
- }));
- } else {
- chart.unshift({
- date: day,
- users: {
- local: {
- total: 0,
- diff: 0
- },
- remote: {
- total: 0,
- diff: 0
- }
- },
- notes: {
- local: {
- total: 0,
- diff: 0,
- diffs: {
- normal: 0,
- reply: 0,
- renote: 0
- }
- },
- remote: {
- total: 0,
- diff: 0,
- diffs: {
- normal: 0,
- reply: 0,
- renote: 0
- }
- }
- },
- drive: {
- local: {
- totalCount: 0,
- totalSize: 0,
- diffCount: 0,
- diffSize: 0
- },
- remote: {
- totalCount: 0,
- totalSize: 0,
- diffCount: 0,
- diffSize: 0
- }
- }
- });
- }
- }
- }
-
- chart.forEach(x => {
- delete x.date;
- });
-
- res(chart);
-});
diff --git a/src/server/api/endpoints/admin/invite.ts b/src/server/api/endpoints/admin/invite.ts
index 77608e715..892b2579f 100644
--- a/src/server/api/endpoints/admin/invite.ts
+++ b/src/server/api/endpoints/admin/invite.ts
@@ -3,7 +3,7 @@ import RegistrationTicket from '../../../../models/registration-tickets';
export const meta = {
desc: {
- ja: '招待コードを発行します。'
+ 'ja-JP': '招待コードを発行します。'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/admin/suspend-user.ts b/src/server/api/endpoints/admin/suspend-user.ts
index 9b492c6e1..32c2416fb 100644
--- a/src/server/api/endpoints/admin/suspend-user.ts
+++ b/src/server/api/endpoints/admin/suspend-user.ts
@@ -5,8 +5,8 @@ import User from '../../../../models/user';
export const meta = {
desc: {
- ja: '指定したユーザーを凍結します。',
- en: 'Suspend a user.'
+ 'ja-JP': '指定したユーザーを凍結します。',
+ 'en-US': 'Suspend a user.'
},
requireCredential: true,
@@ -15,8 +15,8 @@ export const meta = {
params: {
userId: $.type(ID).note({
desc: {
- ja: '対象のユーザーID',
- en: 'The user ID which you want to suspend'
+ 'ja-JP': '対象のユーザーID',
+ 'en-US': 'The user ID which you want to suspend'
}
}),
}
diff --git a/src/server/api/endpoints/admin/unsuspend-user.ts b/src/server/api/endpoints/admin/unsuspend-user.ts
index 8409bd1b7..879c23ab1 100644
--- a/src/server/api/endpoints/admin/unsuspend-user.ts
+++ b/src/server/api/endpoints/admin/unsuspend-user.ts
@@ -5,8 +5,8 @@ import User from '../../../../models/user';
export const meta = {
desc: {
- ja: '指定したユーザーの凍結を解除します。',
- en: 'Unsuspend a user.'
+ 'ja-JP': '指定したユーザーの凍結を解除します。',
+ 'en-US': 'Unsuspend a user.'
},
requireCredential: true,
@@ -15,8 +15,8 @@ export const meta = {
params: {
userId: $.type(ID).note({
desc: {
- ja: '対象のユーザーID',
- en: 'The user ID which you want to unsuspend'
+ 'ja-JP': '対象のユーザーID',
+ 'en-US': 'The user ID which you want to unsuspend'
}
}),
}
diff --git a/src/server/api/endpoints/admin/unverify-user.ts b/src/server/api/endpoints/admin/unverify-user.ts
index 34653cd78..178049fa1 100644
--- a/src/server/api/endpoints/admin/unverify-user.ts
+++ b/src/server/api/endpoints/admin/unverify-user.ts
@@ -5,8 +5,8 @@ import User from '../../../../models/user';
export const meta = {
desc: {
- ja: '指定したユーザーの公式アカウントを解除します。',
- en: 'Mark a user as unverified.'
+ 'ja-JP': '指定したユーザーの公式アカウントを解除します。',
+ 'en-US': 'Mark a user as unverified.'
},
requireCredential: true,
@@ -15,8 +15,8 @@ export const meta = {
params: {
userId: $.type(ID).note({
desc: {
- ja: '対象のユーザーID',
- en: 'The user ID which you want to unverify'
+ 'ja-JP': '対象のユーザーID',
+ 'en-US': 'The user ID which you want to unverify'
}
}),
}
diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts
new file mode 100644
index 000000000..2c7929fab
--- /dev/null
+++ b/src/server/api/endpoints/admin/update-meta.ts
@@ -0,0 +1,37 @@
+import $ from 'cafy';
+import Meta from '../../../../models/meta';
+import getParams from '../../get-params';
+
+export const meta = {
+ desc: {
+ 'ja-JP': 'インスタンスの設定を更新します。'
+ },
+
+ requireCredential: true,
+ requireAdmin: true,
+
+ params: {
+ disableRegistration: $.bool.optional.nullable.note({
+ desc: {
+ 'ja-JP': '招待制か否か'
+ }
+ }),
+ }
+};
+
+export default (params: any) => new Promise(async (res, rej) => {
+ const [ps, psErr] = getParams(meta, params);
+ if (psErr) return rej(psErr);
+
+ const set = {} as any;
+
+ if (ps.disableRegistration === true || ps.disableRegistration === false) {
+ set.disableRegistration = ps.disableRegistration;
+ }
+
+ await Meta.update({}, {
+ $set: set
+ }, { upsert: true });
+
+ res();
+});
diff --git a/src/server/api/endpoints/admin/verify-user.ts b/src/server/api/endpoints/admin/verify-user.ts
index 5b826eb1c..dd07684de 100644
--- a/src/server/api/endpoints/admin/verify-user.ts
+++ b/src/server/api/endpoints/admin/verify-user.ts
@@ -5,8 +5,8 @@ import User from '../../../../models/user';
export const meta = {
desc: {
- ja: '指定したユーザーを公式アカウントにします。',
- en: 'Mark a user as verified.'
+ 'ja-JP': '指定したユーザーを公式アカウントにします。',
+ 'en-US': 'Mark a user as verified.'
},
requireCredential: true,
@@ -15,8 +15,8 @@ export const meta = {
params: {
userId: $.type(ID).note({
desc: {
- ja: '対象のユーザーID',
- en: 'The user ID which you want to verify'
+ 'ja-JP': '対象のユーザーID',
+ 'en-US': 'The user ID which you want to verify'
}
}),
}
diff --git a/src/server/api/endpoints/chart.ts b/src/server/api/endpoints/chart.ts
new file mode 100644
index 000000000..7da970131
--- /dev/null
+++ b/src/server/api/endpoints/chart.ts
@@ -0,0 +1,256 @@
+import $ from 'cafy';
+import Stats, { IStats } from '../../../models/stats';
+import getParams from '../get-params';
+
+type Omit = Pick>;
+
+function migrateStats(stats: IStats[]) {
+ stats.forEach(stat => {
+ const isOldData =
+ stat.users.local.inc == null ||
+ stat.users.local.dec == null ||
+ stat.users.remote.inc == null ||
+ stat.users.remote.dec == null ||
+ stat.notes.local.inc == null ||
+ stat.notes.local.dec == null ||
+ stat.notes.remote.inc == null ||
+ stat.notes.remote.dec == null ||
+ stat.drive.local.incCount == null ||
+ stat.drive.local.decCount == null ||
+ stat.drive.local.incSize == null ||
+ stat.drive.local.decSize == null ||
+ stat.drive.remote.incCount == null ||
+ stat.drive.remote.decCount == null ||
+ stat.drive.remote.incSize == null ||
+ stat.drive.remote.decSize == null;
+
+ if (!isOldData) return;
+
+ stat.users.local.inc = (stat as any).users.local.diff;
+ stat.users.local.dec = 0;
+ stat.users.remote.inc = (stat as any).users.remote.diff;
+ stat.users.remote.dec = 0;
+ stat.notes.local.inc = (stat as any).notes.local.diff;
+ stat.notes.local.dec = 0;
+ stat.notes.remote.inc = (stat as any).notes.remote.diff;
+ stat.notes.remote.dec = 0;
+ stat.drive.local.incCount = (stat as any).drive.local.diffCount;
+ stat.drive.local.decCount = 0;
+ stat.drive.local.incSize = (stat as any).drive.local.diffSize;
+ stat.drive.local.decSize = 0;
+ stat.drive.remote.incCount = (stat as any).drive.remote.diffCount;
+ stat.drive.remote.decCount = 0;
+ stat.drive.remote.incSize = (stat as any).drive.remote.diffSize;
+ stat.drive.remote.decSize = 0;
+ });
+}
+
+export const meta = {
+ desc: {
+ 'ja-JP': 'インスタンスの統計を取得します。'
+ },
+
+ params: {
+ limit: $.num.optional.range(1, 100).note({
+ default: 30,
+ desc: {
+ 'ja-JP': '最大数'
+ }
+ }),
+ }
+};
+
+export default (params: any) => new Promise(async (res, rej) => {
+ const [ps, psErr] = getParams(meta, params);
+ if (psErr) throw psErr;
+
+ const daysRange = ps.limit;
+ const hoursRange = ps.limit;
+
+ const now = new Date();
+ const y = now.getFullYear();
+ const m = now.getMonth();
+ const d = now.getDate();
+ const h = now.getHours();
+
+ const [statsPerDay, statsPerHour] = await Promise.all([
+ Stats.find({
+ span: 'day',
+ date: {
+ $gt: new Date(y, m, d - daysRange)
+ }
+ }, {
+ sort: {
+ date: -1
+ },
+ fields: {
+ _id: 0
+ }
+ }),
+ Stats.find({
+ span: 'hour',
+ date: {
+ $gt: new Date(y, m, d, h - hoursRange)
+ }
+ }, {
+ sort: {
+ date: -1
+ },
+ fields: {
+ _id: 0
+ }
+ }),
+ ]);
+
+ // 後方互換性のため
+ migrateStats(statsPerDay);
+ migrateStats(statsPerHour);
+
+ const format = (src: IStats[], span: 'day' | 'hour') => {
+ const chart: Array, 'span'>> = [];
+
+ const range =
+ span == 'day' ? daysRange :
+ span == 'hour' ? hoursRange :
+ null;
+
+ for (let i = (range - 1); i >= 0; i--) {
+ const current =
+ span == 'day' ? new Date(y, m, d - i) :
+ span == 'hour' ? new Date(y, m, d, h - i) :
+ null;
+
+ const stat = src.find(s => s.date.getTime() == current.getTime());
+
+ if (stat) {
+ chart.unshift(stat);
+ } else { // 隙間埋め
+ const mostRecent = src.find(s => s.date.getTime() < current.getTime());
+ if (mostRecent) {
+ chart.unshift({
+ date: current,
+ users: {
+ local: {
+ total: mostRecent.users.local.total,
+ inc: 0,
+ dec: 0
+ },
+ remote: {
+ total: mostRecent.users.remote.total,
+ inc: 0,
+ dec: 0
+ }
+ },
+ notes: {
+ local: {
+ total: mostRecent.notes.local.total,
+ inc: 0,
+ dec: 0,
+ diffs: {
+ normal: 0,
+ reply: 0,
+ renote: 0
+ }
+ },
+ remote: {
+ total: mostRecent.notes.remote.total,
+ inc: 0,
+ dec: 0,
+ diffs: {
+ normal: 0,
+ reply: 0,
+ renote: 0
+ }
+ }
+ },
+ drive: {
+ local: {
+ totalCount: mostRecent.drive.local.totalCount,
+ totalSize: mostRecent.drive.local.totalSize,
+ incCount: 0,
+ incSize: 0,
+ decCount: 0,
+ decSize: 0
+ },
+ remote: {
+ totalCount: mostRecent.drive.remote.totalCount,
+ totalSize: mostRecent.drive.remote.totalSize,
+ incCount: 0,
+ incSize: 0,
+ decCount: 0,
+ decSize: 0
+ }
+ }
+ });
+ } else {
+ chart.unshift({
+ date: current,
+ users: {
+ local: {
+ total: 0,
+ inc: 0,
+ dec: 0
+ },
+ remote: {
+ total: 0,
+ inc: 0,
+ dec: 0
+ }
+ },
+ notes: {
+ local: {
+ total: 0,
+ inc: 0,
+ dec: 0,
+ diffs: {
+ normal: 0,
+ reply: 0,
+ renote: 0
+ }
+ },
+ remote: {
+ total: 0,
+ inc: 0,
+ dec: 0,
+ diffs: {
+ normal: 0,
+ reply: 0,
+ renote: 0
+ }
+ }
+ },
+ drive: {
+ local: {
+ totalCount: 0,
+ totalSize: 0,
+ incCount: 0,
+ incSize: 0,
+ decCount: 0,
+ decSize: 0
+ },
+ remote: {
+ totalCount: 0,
+ totalSize: 0,
+ incCount: 0,
+ incSize: 0,
+ decCount: 0,
+ decSize: 0
+ }
+ }
+ });
+ }
+ }
+ }
+
+ chart.forEach(x => {
+ delete (x as any).span;
+ });
+
+ return chart;
+ };
+
+ res({
+ perDay: format(statsPerDay, 'day'),
+ perHour: format(statsPerHour, 'hour')
+ });
+});
diff --git a/src/server/api/endpoints/drive.ts b/src/server/api/endpoints/drive.ts
index 8ad961494..063cd475d 100644
--- a/src/server/api/endpoints/drive.ts
+++ b/src/server/api/endpoints/drive.ts
@@ -4,8 +4,8 @@ import config from '../../../config';
export const meta = {
desc: {
- ja: 'ドライブの情報を取得します。',
- en: 'Get drive information.'
+ 'ja-JP': 'ドライブの情報を取得します。',
+ 'en-US': 'Get drive information.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/drive/files.ts b/src/server/api/endpoints/drive/files.ts
index 063b4adde..dc6a602e1 100644
--- a/src/server/api/endpoints/drive/files.ts
+++ b/src/server/api/endpoints/drive/files.ts
@@ -4,8 +4,8 @@ import { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: 'ドライブのファイル一覧を取得します。',
- en: 'Get files of drive.'
+ 'ja-JP': 'ドライブのファイル一覧を取得します。',
+ 'en-US': 'Get files of drive.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts
index 41b7e04b4..dfbd11d0c 100644
--- a/src/server/api/endpoints/drive/files/create.ts
+++ b/src/server/api/endpoints/drive/files/create.ts
@@ -8,8 +8,8 @@ import getParams from '../../../get-params';
export const meta = {
desc: {
- ja: 'ドライブにファイルをアップロードします。',
- en: 'Upload a file to drive.'
+ 'ja-JP': 'ドライブにファイルをアップロードします。',
+ 'en-US': 'Upload a file to drive.'
},
requireCredential: true,
@@ -27,15 +27,15 @@ export const meta = {
folderId: $.type(ID).optional.nullable.note({
default: null,
desc: {
- ja: 'フォルダID'
+ 'ja-JP': 'フォルダID'
}
}),
isSensitive: $.bool.optional.note({
default: false,
desc: {
- ja: 'このメディアが「閲覧注意」(NSFW)かどうか',
- en: 'Whether this media is NSFW'
+ 'ja-JP': 'このメディアが「閲覧注意」(NSFW)かどうか',
+ 'en-US': 'Whether this media is NSFW'
}
})
}
diff --git a/src/server/api/endpoints/drive/files/delete.ts b/src/server/api/endpoints/drive/files/delete.ts
index 02cd96dd8..fb7340df3 100644
--- a/src/server/api/endpoints/drive/files/delete.ts
+++ b/src/server/api/endpoints/drive/files/delete.ts
@@ -6,8 +6,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: 'ドライブのファイルを削除します。',
- en: 'Delete a file of drive.'
+ 'ja-JP': 'ドライブのファイルを削除します。',
+ 'en-US': 'Delete a file of drive.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts
index 6a66c7a27..718fb8c2d 100644
--- a/src/server/api/endpoints/drive/files/show.ts
+++ b/src/server/api/endpoints/drive/files/show.ts
@@ -4,8 +4,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '指定したドライブのファイルの情報を取得します。',
- en: 'Get specified file of drive.'
+ 'ja-JP': '指定したドライブのファイルの情報を取得します。',
+ 'en-US': 'Get specified file of drive.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/drive/files/update.ts b/src/server/api/endpoints/drive/files/update.ts
index 9ae2719aa..ba9abfec6 100644
--- a/src/server/api/endpoints/drive/files/update.ts
+++ b/src/server/api/endpoints/drive/files/update.ts
@@ -7,8 +7,8 @@ import getParams from '../../../get-params';
export const meta = {
desc: {
- ja: '指定したドライブのファイルの情報を更新します。',
- en: 'Update specified file of drive.'
+ 'ja-JP': '指定したドライブのファイルの情報を更新します。',
+ 'en-US': 'Update specified file of drive.'
},
requireCredential: true,
@@ -18,30 +18,30 @@ export const meta = {
params: {
fileId: $.type(ID).note({
desc: {
- ja: '対象のファイルID'
+ 'ja-JP': '対象のファイルID'
}
}),
folderId: $.type(ID).optional.nullable.note({
default: undefined,
desc: {
- ja: 'フォルダID'
+ 'ja-JP': 'フォルダID'
}
}),
name: $.str.optional.pipe(validateFileName).note({
default: undefined,
desc: {
- ja: 'ファイル名',
- en: 'Name of the file'
+ 'ja-JP': 'ファイル名',
+ 'en-US': 'Name of the file'
}
}),
isSensitive: $.bool.optional.note({
default: undefined,
desc: {
- ja: 'このメディアが「閲覧注意」(NSFW)かどうか',
- en: 'Whether this media is NSFW'
+ 'ja-JP': 'このメディアが「閲覧注意」(NSFW)かどうか',
+ 'en-US': 'Whether this media is NSFW'
}
})
}
diff --git a/src/server/api/endpoints/drive/files/upload_from_url.ts b/src/server/api/endpoints/drive/files/upload_from_url.ts
index d634cf46d..783646feb 100644
--- a/src/server/api/endpoints/drive/files/upload_from_url.ts
+++ b/src/server/api/endpoints/drive/files/upload_from_url.ts
@@ -6,7 +6,7 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: 'ドライブに指定されたURLに存在するファイルをアップロードします。'
+ 'ja-JP': 'ドライブに指定されたURLに存在するファイルをアップロードします。'
},
limit: {
diff --git a/src/server/api/endpoints/drive/folders.ts b/src/server/api/endpoints/drive/folders.ts
index de398eb72..19c2ef7ac 100644
--- a/src/server/api/endpoints/drive/folders.ts
+++ b/src/server/api/endpoints/drive/folders.ts
@@ -4,8 +4,8 @@ import { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: 'ドライブのフォルダ一覧を取得します。',
- en: 'Get folders of drive.'
+ 'ja-JP': 'ドライブのフォルダ一覧を取得します。',
+ 'en-US': 'Get folders of drive.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/drive/folders/create.ts b/src/server/api/endpoints/drive/folders/create.ts
index 03f950477..5997dedf0 100644
--- a/src/server/api/endpoints/drive/folders/create.ts
+++ b/src/server/api/endpoints/drive/folders/create.ts
@@ -5,8 +5,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: 'ドライブのフォルダを作成します。',
- en: 'Create a folder of drive.'
+ 'ja-JP': 'ドライブのフォルダを作成します。',
+ 'en-US': 'Create a folder of drive.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/drive/folders/show.ts b/src/server/api/endpoints/drive/folders/show.ts
index 6a6c879a0..bb25bcba3 100644
--- a/src/server/api/endpoints/drive/folders/show.ts
+++ b/src/server/api/endpoints/drive/folders/show.ts
@@ -4,7 +4,7 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '指定したドライブのフォルダの情報を取得します。'
+ 'ja-JP': '指定したドライブのフォルダの情報を取得します。'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/drive/folders/update.ts b/src/server/api/endpoints/drive/folders/update.ts
index 1b449428a..259f373bf 100644
--- a/src/server/api/endpoints/drive/folders/update.ts
+++ b/src/server/api/endpoints/drive/folders/update.ts
@@ -5,8 +5,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '指定したドライブのフォルダの情報を更新します。',
- en: 'Update specified folder of drive.'
+ 'ja-JP': '指定したドライブのフォルダの情報を更新します。',
+ 'en-US': 'Update specified folder of drive.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/following/create.ts b/src/server/api/endpoints/following/create.ts
index ebe319e0c..c9bea0e3d 100644
--- a/src/server/api/endpoints/following/create.ts
+++ b/src/server/api/endpoints/following/create.ts
@@ -6,8 +6,8 @@ import create from '../../../../services/following/create';
export const meta = {
desc: {
- ja: '指定したユーザーをフォローします。',
- en: 'Follow a user.'
+ 'ja-JP': '指定したユーザーをフォローします。',
+ 'en-US': 'Follow a user.'
},
limit: {
diff --git a/src/server/api/endpoints/following/delete.ts b/src/server/api/endpoints/following/delete.ts
index 4806fe4e3..f3b4a73ae 100644
--- a/src/server/api/endpoints/following/delete.ts
+++ b/src/server/api/endpoints/following/delete.ts
@@ -6,8 +6,8 @@ import deleteFollowing from '../../../../services/following/delete';
export const meta = {
desc: {
- ja: '指定したユーザーのフォローを解除します。',
- en: 'Unfollow a user.'
+ 'ja-JP': '指定したユーザーのフォローを解除します。',
+ 'en-US': 'Unfollow a user.'
},
limit: {
diff --git a/src/server/api/endpoints/following/requests/accept.ts b/src/server/api/endpoints/following/requests/accept.ts
index b3bf2dd66..f6a7dcf12 100644
--- a/src/server/api/endpoints/following/requests/accept.ts
+++ b/src/server/api/endpoints/following/requests/accept.ts
@@ -4,8 +4,8 @@ import User, { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '自分に届いた、指定したフォローリクエストを承認します。',
- en: 'Accept a follow request.'
+ 'ja-JP': '自分に届いた、指定したフォローリクエストを承認します。',
+ 'en-US': 'Accept a follow request.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/following/requests/cancel.ts b/src/server/api/endpoints/following/requests/cancel.ts
index 9bfc40ce6..3da4f4734 100644
--- a/src/server/api/endpoints/following/requests/cancel.ts
+++ b/src/server/api/endpoints/following/requests/cancel.ts
@@ -4,8 +4,8 @@ import User, { pack, ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '自分が作成した、指定したフォローリクエストをキャンセルします。',
- en: 'Cancel a follow request.'
+ 'ja-JP': '自分が作成した、指定したフォローリクエストをキャンセルします。',
+ 'en-US': 'Cancel a follow request.'
},
requireCredential: true,
@@ -27,7 +27,11 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
return rej('followee not found');
}
- await cancelFollowRequest(followee, user);
+ try {
+ await cancelFollowRequest(followee, user);
+ } catch (e) {
+ return rej(e);
+ }
// Send response
res(await pack(followee._id, user));
diff --git a/src/server/api/endpoints/following/requests/list.ts b/src/server/api/endpoints/following/requests/list.ts
index b06a158c0..11a387cf1 100644
--- a/src/server/api/endpoints/following/requests/list.ts
+++ b/src/server/api/endpoints/following/requests/list.ts
@@ -4,8 +4,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '自分に届いたフォローリクエストの一覧を取得します。',
- en: 'Get all pending received follow requests.'
+ 'ja-JP': '自分に届いたフォローリクエストの一覧を取得します。',
+ 'en-US': 'Get all pending received follow requests.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/following/requests/reject.ts b/src/server/api/endpoints/following/requests/reject.ts
index a232549bb..98febe9e9 100644
--- a/src/server/api/endpoints/following/requests/reject.ts
+++ b/src/server/api/endpoints/following/requests/reject.ts
@@ -4,8 +4,8 @@ import User, { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '自分に届いた、指定したフォローリクエストを拒否します。',
- en: 'Reject a follow request.'
+ 'ja-JP': '自分に届いた、指定したフォローリクエストを拒否します。',
+ 'en-US': 'Reject a follow request.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/following/stalk.ts b/src/server/api/endpoints/following/stalk.ts
index 79a3fb976..d44cea2cc 100644
--- a/src/server/api/endpoints/following/stalk.ts
+++ b/src/server/api/endpoints/following/stalk.ts
@@ -4,8 +4,8 @@ import { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: '指定したユーザーをストーキングします。',
- en: 'Stalk a user.'
+ 'ja-JP': '指定したユーザーをストーキングします。',
+ 'en-US': 'Stalk a user.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/following/unstalk.ts b/src/server/api/endpoints/following/unstalk.ts
index 71a7a97ee..8b66f0727 100644
--- a/src/server/api/endpoints/following/unstalk.ts
+++ b/src/server/api/endpoints/following/unstalk.ts
@@ -4,8 +4,8 @@ import { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: '指定したユーザーのストーキングをやめます。',
- en: 'Unstalk a user.'
+ 'ja-JP': '指定したユーザーのストーキングをやめます。',
+ 'en-US': 'Unstalk a user.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/games/reversi/games/surrender.ts b/src/server/api/endpoints/games/reversi/games/surrender.ts
index 49821650e..8ca014367 100644
--- a/src/server/api/endpoints/games/reversi/games/surrender.ts
+++ b/src/server/api/endpoints/games/reversi/games/surrender.ts
@@ -6,7 +6,7 @@ import { publishReversiGameStream } from '../../../../../../stream';
export const meta = {
desc: {
- ja: '指定したリバーシの対局で投了します。'
+ 'ja-JP': '指定したリバーシの対局で投了します。'
},
requireCredential: true,
@@ -14,7 +14,7 @@ export const meta = {
params: {
gameId: $.type(ID).note({
desc: {
- ja: '投了したい対局'
+ 'ja-JP': '投了したい対局'
}
})
}
diff --git a/src/server/api/endpoints/hashtags/search.ts b/src/server/api/endpoints/hashtags/search.ts
index 262370cac..f6fb35b2f 100644
--- a/src/server/api/endpoints/hashtags/search.ts
+++ b/src/server/api/endpoints/hashtags/search.ts
@@ -5,7 +5,7 @@ const escapeRegexp = require('escape-regexp');
export const meta = {
desc: {
- ja: 'ハッシュタグを検索します。'
+ 'ja-JP': 'ハッシュタグを検索します。'
},
requireCredential: false,
@@ -14,20 +14,20 @@ export const meta = {
limit: $.num.optional.range(1, 100).note({
default: 10,
desc: {
- ja: '最大数'
+ 'ja-JP': '最大数'
}
}),
query: $.str.note({
desc: {
- ja: 'クエリ'
+ 'ja-JP': 'クエリ'
}
}),
offset: $.num.optional.min(0).note({
default: 0,
desc: {
- ja: 'オフセット'
+ 'ja-JP': 'オフセット'
}
})
}
diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts
index 7f25c0795..1f99ef2d8 100644
--- a/src/server/api/endpoints/i.ts
+++ b/src/server/api/endpoints/i.ts
@@ -3,7 +3,7 @@ import { IApp } from '../../../models/app';
export const meta = {
desc: {
- ja: '自分のアカウント情報を取得します。'
+ 'ja-JP': '自分のアカウント情報を取得します。'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/i/favorites.ts b/src/server/api/endpoints/i/favorites.ts
index 47c8a87fd..32c1a55fb 100644
--- a/src/server/api/endpoints/i/favorites.ts
+++ b/src/server/api/endpoints/i/favorites.ts
@@ -4,8 +4,8 @@ import { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: 'お気に入りに登録した投稿一覧を取得します。',
- en: 'Get favorited notes'
+ 'ja-JP': 'お気に入りに登録した投稿一覧を取得します。',
+ 'en-US': 'Get favorited notes'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts
index 922b39679..cdb4eb3f5 100644
--- a/src/server/api/endpoints/i/update.ts
+++ b/src/server/api/endpoints/i/update.ts
@@ -8,8 +8,8 @@ import config from '../../../../config';
export const meta = {
desc: {
- ja: 'アカウント情報を更新します。',
- en: 'Update myself'
+ 'ja-JP': 'アカウント情報を更新します。',
+ 'en-US': 'Update myself'
},
requireCredential: true,
@@ -84,7 +84,7 @@ export default async (params: any, user: ILocalUser, app: IApp) => new Promise(a
if (avatar == null) return rej('avatar not found');
- updates.avatarUrl = avatar.metadata.url || `${config.drive_url}/${avatar._id}`;
+ updates.avatarUrl = avatar.metadata.thumbnailUrl || avatar.metadata.url || `${config.drive_url}/${avatar._id}`;
if (avatar.metadata.properties.avgColor) {
updates.avatarColor = avatar.metadata.properties.avgColor;
diff --git a/src/server/api/endpoints/messaging/history.ts b/src/server/api/endpoints/messaging/history.ts
index 43cceacf9..1dd08cd13 100644
--- a/src/server/api/endpoints/messaging/history.ts
+++ b/src/server/api/endpoints/messaging/history.ts
@@ -6,8 +6,8 @@ import { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: 'Messagingの履歴を取得します。',
- en: 'Show messaging history.'
+ 'ja-JP': 'Messagingの履歴を取得します。',
+ 'en-US': 'Show messaging history.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/messaging/messages.ts b/src/server/api/endpoints/messaging/messages.ts
index ae26419bc..dec0638ee 100644
--- a/src/server/api/endpoints/messaging/messages.ts
+++ b/src/server/api/endpoints/messaging/messages.ts
@@ -6,8 +6,8 @@ import read from '../../common/read-messaging-message';
export const meta = {
desc: {
- ja: '指定したユーザーとのMessagingのメッセージ一覧を取得します。',
- en: 'Get messages of messaging.'
+ 'ja-JP': '指定したユーザーとのMessagingのメッセージ一覧を取得します。',
+ 'en-US': 'Get messages of messaging.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/messaging/messages/create.ts b/src/server/api/endpoints/messaging/messages/create.ts
index d33d9e7e7..a6fabcfa4 100644
--- a/src/server/api/endpoints/messaging/messages/create.ts
+++ b/src/server/api/endpoints/messaging/messages/create.ts
@@ -12,8 +12,8 @@ import pushSw from '../../../../../push-sw';
export const meta = {
desc: {
- ja: '指定したユーザーへMessagingのメッセージを送信します。',
- en: 'Create a message of messaging.'
+ 'ja-JP': '指定したユーザーへMessagingのメッセージを送信します。',
+ 'en-US': 'Create a message of messaging.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/messaging/messages/read.ts b/src/server/api/endpoints/messaging/messages/read.ts
index f60933752..581b57579 100644
--- a/src/server/api/endpoints/messaging/messages/read.ts
+++ b/src/server/api/endpoints/messaging/messages/read.ts
@@ -6,8 +6,8 @@ import getParams from '../../../get-params';
export const meta = {
desc: {
- ja: '指定した自分宛てのメッセージを既読にします。',
- en: 'Mark as read a message of messaging.'
+ 'ja-JP': '指定した自分宛てのメッセージを既読にします。',
+ 'en-US': 'Mark as read a message of messaging.'
},
requireCredential: true,
@@ -17,8 +17,8 @@ export const meta = {
params: {
messageId: $.type(ID).note({
desc: {
- ja: '既読にするメッセージのID',
- en: 'The ID of a message that you want to mark as read'
+ 'ja-JP': '既読にするメッセージのID',
+ 'en-US': 'The ID of a message that you want to mark as read'
}
})
}
diff --git a/src/server/api/endpoints/mute/create.ts b/src/server/api/endpoints/mute/create.ts
index bd70cd62e..5b2e7a8d7 100644
--- a/src/server/api/endpoints/mute/create.ts
+++ b/src/server/api/endpoints/mute/create.ts
@@ -4,8 +4,8 @@ import Mute from '../../../../models/mute';
export const meta = {
desc: {
- ja: 'ユーザーをミュートします。',
- en: 'Mute a user'
+ 'ja-JP': 'ユーザーをミュートします。',
+ 'en-US': 'Mute a user'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/mute/delete.ts b/src/server/api/endpoints/mute/delete.ts
index 3187c46f8..e8ed75a84 100644
--- a/src/server/api/endpoints/mute/delete.ts
+++ b/src/server/api/endpoints/mute/delete.ts
@@ -4,8 +4,8 @@ import Mute from '../../../../models/mute';
export const meta = {
desc: {
- ja: 'ユーザーのミュートを解除します。',
- en: 'Unmute a user'
+ 'ja-JP': 'ユーザーのミュートを解除します。',
+ 'en-US': 'Unmute a user'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/mute/list.ts b/src/server/api/endpoints/mute/list.ts
index e29760533..387b2396f 100644
--- a/src/server/api/endpoints/mute/list.ts
+++ b/src/server/api/endpoints/mute/list.ts
@@ -5,8 +5,8 @@ import { getFriendIds } from '../../common/get-friends';
export const meta = {
desc: {
- ja: 'ミュートしているユーザー一覧を取得します。',
- en: 'Get muted users.'
+ 'ja-JP': 'ミュートしているユーザー一覧を取得します。',
+ 'en-US': 'Get muted users.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/my/apps.ts b/src/server/api/endpoints/my/apps.ts
index 35185db41..412dff616 100644
--- a/src/server/api/endpoints/my/apps.ts
+++ b/src/server/api/endpoints/my/apps.ts
@@ -4,8 +4,8 @@ import { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: '自分のアプリケーション一覧を取得します。',
- en: 'Get my apps'
+ 'ja-JP': '自分のアプリケーション一覧を取得します。',
+ 'en-US': 'Get my apps'
},
requireCredential: true
diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts
index 9cdbec527..04f5f7562 100644
--- a/src/server/api/endpoints/notes/create.ts
+++ b/src/server/api/endpoints/notes/create.ts
@@ -9,7 +9,7 @@ import getParams from '../../get-params';
export const meta = {
desc: {
- ja: '投稿します。'
+ 'ja-JP': '投稿します。'
},
requireCredential: true,
@@ -25,33 +25,33 @@ export const meta = {
visibility: $.str.optional.or(['public', 'home', 'followers', 'specified', 'private']).note({
default: 'public',
desc: {
- ja: '投稿の公開範囲'
+ 'ja-JP': '投稿の公開範囲'
}
}),
visibleUserIds: $.arr($.type(ID)).optional.unique().min(1).note({
desc: {
- ja: '(投稿の公開範囲が specified の場合)投稿を閲覧できるユーザー'
+ 'ja-JP': '(投稿の公開範囲が specified の場合)投稿を閲覧できるユーザー'
}
}),
text: $.str.optional.nullable.pipe(isValidText).note({
default: null,
desc: {
- ja: '投稿内容'
+ 'ja-JP': '投稿内容'
}
}),
cw: $.str.optional.nullable.pipe(isValidCw).note({
desc: {
- ja: 'コンテンツの警告。このパラメータを指定すると設定したテキストで投稿のコンテンツを隠す事が出来ます。'
+ 'ja-JP': 'コンテンツの警告。このパラメータを指定すると設定したテキストで投稿のコンテンツを隠す事が出来ます。'
}
}),
viaMobile: $.bool.optional.note({
default: false,
desc: {
- ja: 'モバイルデバイスからの投稿か否か。'
+ 'ja-JP': 'モバイルデバイスからの投稿か否か。'
}
}),
@@ -66,20 +66,20 @@ export const meta = {
speed: $.num.nullable
}).optional.nullable.strict().note({
desc: {
- ja: '位置情報'
+ 'ja-JP': '位置情報'
},
ref: 'geo'
}),
mediaIds: $.arr($.type(ID)).optional.unique().range(1, 4).note({
desc: {
- ja: '添付するメディア'
+ 'ja-JP': '添付するメディア'
}
}),
renoteId: $.type(ID).optional.note({
desc: {
- ja: 'Renote対象'
+ 'ja-JP': 'Renote対象'
}
}),
@@ -90,7 +90,7 @@ export const meta = {
.each(c => c.length > 0 && c.length < 50)
}).optional.strict().note({
desc: {
- ja: 'アンケート'
+ 'ja-JP': 'アンケート'
},
ref: 'poll'
})
@@ -102,7 +102,7 @@ export const meta = {
createdNote: {
type: 'entity(Note)',
desc: {
- ja: '作成した投稿'
+ 'ja-JP': '作成した投稿'
}
}
}
diff --git a/src/server/api/endpoints/notes/delete.ts b/src/server/api/endpoints/notes/delete.ts
index 22c6101e1..6d9826cf7 100644
--- a/src/server/api/endpoints/notes/delete.ts
+++ b/src/server/api/endpoints/notes/delete.ts
@@ -5,8 +5,8 @@ import { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: '指定した投稿を削除します。',
- en: 'Delete a note.'
+ 'ja-JP': '指定した投稿を削除します。',
+ 'en-US': 'Delete a note.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/notes/favorites/create.ts b/src/server/api/endpoints/notes/favorites/create.ts
index 87f6cf1f0..daf7780ab 100644
--- a/src/server/api/endpoints/notes/favorites/create.ts
+++ b/src/server/api/endpoints/notes/favorites/create.ts
@@ -5,8 +5,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '指定した投稿をお気に入りに登録します。',
- en: 'Favorite a note.'
+ 'ja-JP': '指定した投稿をお気に入りに登録します。',
+ 'en-US': 'Favorite a note.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/notes/favorites/delete.ts b/src/server/api/endpoints/notes/favorites/delete.ts
index 3906fe99b..e42b24d32 100644
--- a/src/server/api/endpoints/notes/favorites/delete.ts
+++ b/src/server/api/endpoints/notes/favorites/delete.ts
@@ -5,8 +5,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '指定した投稿のお気に入りを解除します。',
- en: 'Unfavorite a note.'
+ 'ja-JP': '指定した投稿のお気に入りを解除します。',
+ 'en-US': 'Unfavorite a note.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts
index 3fce4fb9a..2dbb1190c 100644
--- a/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -10,65 +10,65 @@ export const meta = {
name: 'notes/hybrid-timeline',
desc: {
- ja: 'ハイブリッドタイムラインを取得します。'
+ 'ja-JP': 'ハイブリッドタイムラインを取得します。'
},
params: {
limit: $.num.optional.range(1, 100).note({
default: 10,
desc: {
- ja: '最大数'
+ 'ja-JP': '最大数'
}
}),
sinceId: $.type(ID).optional.note({
desc: {
- ja: '指定すると、この投稿を基点としてより新しい投稿を取得します'
+ 'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します'
}
}),
untilId: $.type(ID).optional.note({
desc: {
- ja: '指定すると、この投稿を基点としてより古い投稿を取得します'
+ 'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
}
}),
sinceDate: $.num.optional.note({
desc: {
- ja: '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
+ 'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
}
}),
untilDate: $.num.optional.note({
desc: {
- ja: '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
+ 'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
}
}),
includeMyRenotes: $.bool.optional.note({
default: true,
desc: {
- ja: '自分の行ったRenoteを含めるかどうか'
+ 'ja-JP': '自分の行ったRenoteを含めるかどうか'
}
}),
includeRenotedMyNotes: $.bool.optional.note({
default: true,
desc: {
- ja: 'Renoteされた自分の投稿を含めるかどうか'
+ 'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
}
}),
includeLocalRenotes: $.bool.optional.note({
default: true,
desc: {
- ja: 'Renoteされたローカルの投稿を含めるかどうか'
+ 'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
}
}),
mediaOnly: $.bool.optional.note({
desc: {
- ja: 'true にすると、メディアが添付された投稿だけ取得します'
+ 'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します'
}
}),
}
diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts
index db91230a8..a7fb14d8a 100644
--- a/src/server/api/endpoints/notes/mentions.ts
+++ b/src/server/api/endpoints/notes/mentions.ts
@@ -6,8 +6,8 @@ import { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: '自分に言及している投稿の一覧を取得します。',
- en: 'Get mentions of myself.'
+ 'ja-JP': '自分に言及している投稿の一覧を取得します。',
+ 'en-US': 'Get mentions of myself.'
},
requireCredential: true
diff --git a/src/server/api/endpoints/notes/polls/recommendation.ts b/src/server/api/endpoints/notes/polls/recommendation.ts
index a0469d187..9af223c01 100644
--- a/src/server/api/endpoints/notes/polls/recommendation.ts
+++ b/src/server/api/endpoints/notes/polls/recommendation.ts
@@ -5,8 +5,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: 'おすすめのアンケート一覧を取得します。',
- en: 'Get recommended polls.'
+ 'ja-JP': 'おすすめのアンケート一覧を取得します。',
+ 'en-US': 'Get recommended polls.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts
index 568c187f8..ab80e7f5d 100644
--- a/src/server/api/endpoints/notes/polls/vote.ts
+++ b/src/server/api/endpoints/notes/polls/vote.ts
@@ -9,8 +9,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '指定した投稿のアンケートに投票します。',
- en: 'Vote poll of a note.'
+ 'ja-JP': '指定した投稿のアンケートに投票します。',
+ 'en-US': 'Vote poll of a note.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/notes/reactions.ts b/src/server/api/endpoints/notes/reactions.ts
index 8921c5591..6e7d60e0f 100644
--- a/src/server/api/endpoints/notes/reactions.ts
+++ b/src/server/api/endpoints/notes/reactions.ts
@@ -5,8 +5,8 @@ import { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: '指定した投稿のリアクション一覧を取得します。',
- en: 'Show reactions of a note.'
+ 'ja-JP': '指定した投稿のリアクション一覧を取得します。',
+ 'en-US': 'Show reactions of a note.'
},
requireCredential: true
diff --git a/src/server/api/endpoints/notes/reactions/create.ts b/src/server/api/endpoints/notes/reactions/create.ts
index 65e24e7c0..0781db16c 100644
--- a/src/server/api/endpoints/notes/reactions/create.ts
+++ b/src/server/api/endpoints/notes/reactions/create.ts
@@ -7,8 +7,8 @@ import getParams from '../../../get-params';
export const meta = {
desc: {
- ja: '指定した投稿にリアクションします。',
- en: 'React to a note.'
+ 'ja-JP': '指定した投稿にリアクションします。',
+ 'en-US': 'React to a note.'
},
requireCredential: true,
@@ -18,13 +18,13 @@ export const meta = {
params: {
noteId: $.type(ID).note({
desc: {
- ja: '対象の投稿'
+ 'ja-JP': '対象の投稿'
}
}),
reaction: $.str.pipe(validateReaction.ok).note({
desc: {
- ja: 'リアクションの種類'
+ 'ja-JP': 'リアクションの種類'
}
})
}
diff --git a/src/server/api/endpoints/notes/reactions/delete.ts b/src/server/api/endpoints/notes/reactions/delete.ts
index 62af0407b..598eb6536 100644
--- a/src/server/api/endpoints/notes/reactions/delete.ts
+++ b/src/server/api/endpoints/notes/reactions/delete.ts
@@ -5,8 +5,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '指定した投稿へのリアクションを取り消します。',
- en: 'Unreact to a note.'
+ 'ja-JP': '指定した投稿へのリアクションを取り消します。',
+ 'en-US': 'Unreact to a note.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index 3e3fa8c4a..099bf2010 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -8,8 +8,8 @@ import getParams from '../../get-params';
export const meta = {
desc: {
- ja: 'タイムラインを取得します。',
- en: 'Get timeline of myself.'
+ 'ja-JP': 'タイムラインを取得します。',
+ 'en-US': 'Get timeline of myself.'
},
requireCredential: true,
@@ -18,58 +18,58 @@ export const meta = {
limit: $.num.optional.range(1, 100).note({
default: 10,
desc: {
- ja: '最大数'
+ 'ja-JP': '最大数'
}
}),
sinceId: $.type(ID).optional.note({
desc: {
- ja: '指定すると、この投稿を基点としてより新しい投稿を取得します'
+ 'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します'
}
}),
untilId: $.type(ID).optional.note({
desc: {
- ja: '指定すると、この投稿を基点としてより古い投稿を取得します'
+ 'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
}
}),
sinceDate: $.num.optional.note({
desc: {
- ja: '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
+ 'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
}
}),
untilDate: $.num.optional.note({
desc: {
- ja: '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
+ 'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
}
}),
includeMyRenotes: $.bool.optional.note({
default: true,
desc: {
- ja: '自分の行ったRenoteを含めるかどうか'
+ 'ja-JP': '自分の行ったRenoteを含めるかどうか'
}
}),
includeRenotedMyNotes: $.bool.optional.note({
default: true,
desc: {
- ja: 'Renoteされた自分の投稿を含めるかどうか'
+ 'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
}
}),
includeLocalRenotes: $.bool.optional.note({
default: true,
desc: {
- ja: 'Renoteされたローカルの投稿を含めるかどうか'
+ 'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
}
}),
mediaOnly: $.bool.optional.note({
desc: {
- ja: 'true にすると、メディアが添付された投稿だけ取得します'
+ 'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します'
}
}),
}
diff --git a/src/server/api/endpoints/notes/trend.ts b/src/server/api/endpoints/notes/trend.ts
index 1cbbfacad..7a0a098f2 100644
--- a/src/server/api/endpoints/notes/trend.ts
+++ b/src/server/api/endpoints/notes/trend.ts
@@ -5,8 +5,8 @@ import { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: '人気の投稿の一覧を取得します。',
- en: 'Get trend notes.'
+ 'ja-JP': '人気の投稿の一覧を取得します。',
+ 'en-US': 'Get trend notes.'
},
requireCredential: true
diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts
index dcef54866..a7b43014e 100644
--- a/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -8,8 +8,8 @@ import getParams from '../../get-params';
export const meta = {
desc: {
- ja: '指定したユーザーリストのタイムラインを取得します。',
- en: 'Get timeline of a user list.'
+ 'ja-JP': '指定したユーザーリストのタイムラインを取得します。',
+ 'en-US': 'Get timeline of a user list.'
},
requireCredential: true,
@@ -17,65 +17,65 @@ export const meta = {
params: {
listId: $.type(ID).note({
desc: {
- ja: 'リストのID'
+ 'ja-JP': 'リストのID'
}
}),
limit: $.num.optional.range(1, 100).note({
default: 10,
desc: {
- ja: '最大数'
+ 'ja-JP': '最大数'
}
}),
sinceId: $.type(ID).optional.note({
desc: {
- ja: '指定すると、この投稿を基点としてより新しい投稿を取得します'
+ 'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します'
}
}),
untilId: $.type(ID).optional.note({
desc: {
- ja: '指定すると、この投稿を基点としてより古い投稿を取得します'
+ 'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
}
}),
sinceDate: $.num.optional.note({
desc: {
- ja: '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
+ 'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
}
}),
untilDate: $.num.optional.note({
desc: {
- ja: '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
+ 'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
}
}),
includeMyRenotes: $.bool.optional.note({
default: true,
desc: {
- ja: '自分の行ったRenoteを含めるかどうか'
+ 'ja-JP': '自分の行ったRenoteを含めるかどうか'
}
}),
includeRenotedMyNotes: $.bool.optional.note({
default: true,
desc: {
- ja: 'Renoteされた自分の投稿を含めるかどうか'
+ 'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
}
}),
includeLocalRenotes: $.bool.optional.note({
default: true,
desc: {
- ja: 'Renoteされたローカルの投稿を含めるかどうか'
+ 'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
}
}),
mediaOnly: $.bool.optional.note({
desc: {
- ja: 'true にすると、メディアが添付された投稿だけ取得します'
+ 'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します'
}
}),
}
diff --git a/src/server/api/endpoints/notifications/mark_all_as_read.ts b/src/server/api/endpoints/notifications/mark_all_as_read.ts
index a9875ebb0..e2bde777b 100644
--- a/src/server/api/endpoints/notifications/mark_all_as_read.ts
+++ b/src/server/api/endpoints/notifications/mark_all_as_read.ts
@@ -4,8 +4,8 @@ import User, { ILocalUser } from '../../../../models/user';
export const meta = {
desc: {
- ja: '全ての通知を既読にします。',
- en: 'Mark all notifications as read.'
+ 'ja-JP': '全ての通知を既読にします。',
+ 'en-US': 'Mark all notifications as read.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/users/lists/create.ts b/src/server/api/endpoints/users/lists/create.ts
index d7dc2a5f7..ac4f957a0 100644
--- a/src/server/api/endpoints/users/lists/create.ts
+++ b/src/server/api/endpoints/users/lists/create.ts
@@ -4,8 +4,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: 'ユーザーリストを作成します。',
- en: 'Create a user list'
+ 'ja-JP': 'ユーザーリストを作成します。',
+ 'en-US': 'Create a user list'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/users/lists/list.ts b/src/server/api/endpoints/users/lists/list.ts
index 31fef26bd..966e1d3ad 100644
--- a/src/server/api/endpoints/users/lists/list.ts
+++ b/src/server/api/endpoints/users/lists/list.ts
@@ -3,7 +3,7 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '自分の作成したユーザーリスト一覧を取得します。'
+ 'ja-JP': '自分の作成したユーザーリスト一覧を取得します。'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/users/lists/push.ts b/src/server/api/endpoints/users/lists/push.ts
index bd4e201bd..2d68ec745 100644
--- a/src/server/api/endpoints/users/lists/push.ts
+++ b/src/server/api/endpoints/users/lists/push.ts
@@ -8,8 +8,8 @@ import { deliver } from '../../../../../queue';
export const meta = {
desc: {
- ja: '指定したユーザーリストに指定したユーザーを追加します。',
- en: 'Add a user to a user list.'
+ 'ja-JP': '指定したユーザーリストに指定したユーザーを追加します。',
+ 'en-US': 'Add a user to a user list.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/users/lists/show.ts b/src/server/api/endpoints/users/lists/show.ts
index 2fd142a60..a2dd00c6e 100644
--- a/src/server/api/endpoints/users/lists/show.ts
+++ b/src/server/api/endpoints/users/lists/show.ts
@@ -4,8 +4,8 @@ import { ILocalUser } from '../../../../../models/user';
export const meta = {
desc: {
- ja: '指定したユーザーリストの情報を取得します。',
- en: 'Show a user list.'
+ 'ja-JP': '指定したユーザーリストの情報を取得します。',
+ 'en-US': 'Show a user list.'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts
index 13377e6ff..e0a5cb9e3 100644
--- a/src/server/api/endpoints/users/recommendation.ts
+++ b/src/server/api/endpoints/users/recommendation.ts
@@ -6,7 +6,7 @@ import Mute from '../../../../models/mute';
export const meta = {
desc: {
- ja: 'おすすめのユーザー一覧を取得します。'
+ 'ja-JP': 'おすすめのユーザー一覧を取得します。'
},
requireCredential: true,
diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts
index eda3f9572..307a8f689 100644
--- a/src/server/api/endpoints/users/search.ts
+++ b/src/server/api/endpoints/users/search.ts
@@ -5,7 +5,7 @@ import getParams from '../../get-params';
export const meta = {
desc: {
- ja: 'ユーザーを検索します。'
+ 'ja-JP': 'ユーザーを検索します。'
},
requireCredential: false,
@@ -13,28 +13,28 @@ export const meta = {
params: {
query: $.str.note({
desc: {
- ja: 'クエリ'
+ 'ja-JP': 'クエリ'
}
}),
offset: $.num.optional.min(0).note({
default: 0,
desc: {
- ja: 'オフセット'
+ 'ja-JP': 'オフセット'
}
}),
limit: $.num.optional.range(1, 100).note({
default: 10,
desc: {
- ja: '取得する数'
+ 'ja-JP': '取得する数'
}
}),
localOnly: $.bool.optional.note({
default: false,
desc: {
- ja: 'ローカルユーザーのみ検索対象にするか否か'
+ 'ja-JP': 'ローカルユーザーのみ検索対象にするか否か'
}
}),
},
diff --git a/src/server/api/index.ts b/src/server/api/index.ts
index 3ec7a28df..a8f6455d9 100644
--- a/src/server/api/index.ts
+++ b/src/server/api/index.ts
@@ -46,6 +46,11 @@ router.post('/signin', require('./private/signin').default);
router.use(require('./service/github').routes());
router.use(require('./service/twitter').routes());
+// Return 404 for unknown API
+router.all('*', async ctx => {
+ ctx.status = 404;
+});
+
// Register router
app.use(router.routes());
diff --git a/src/server/api/service/twitter.ts b/src/server/api/service/twitter.ts
index 8c668e832..aad2846bb 100644
--- a/src/server/api/service/twitter.ts
+++ b/src/server/api/service/twitter.ts
@@ -14,7 +14,7 @@ function getUserToken(ctx: Koa.Context) {
function compareOrigin(ctx: Koa.Context) {
function normalizeUrl(url: string) {
- return url[url.length - 1] === '/' ? url.substr(0, url.length - 1) : url;
+ return url.endsWith('/') ? url.substr(0, url.length - 1) : url;
}
const referer = ctx.headers['referer'];
diff --git a/src/server/web/index.ts b/src/server/web/index.ts
index 7291f8a0a..452e36fe9 100644
--- a/src/server/web/index.ts
+++ b/src/server/web/index.ts
@@ -122,8 +122,7 @@ router.get('/notes/:note', async ctx => {
router.get('*', async ctx => {
await send(ctx, `app/base.html`, {
root: client,
- maxage: ms('3 days'),
- immutable: true
+ maxage: ms('5m')
});
});
diff --git a/src/server/web/url-preview.ts b/src/server/web/url-preview.ts
index e96eb309f..41ca6bad8 100644
--- a/src/server/web/url-preview.ts
+++ b/src/server/web/url-preview.ts
@@ -1,11 +1,20 @@
import * as Koa from 'koa';
+import * as request from 'request-promise-native';
import summaly from 'summaly';
+import config from '../../config';
module.exports = async (ctx: Koa.Context) => {
try {
- const summary = await summaly(ctx.query.url, {
+ const summary = config.summalyProxy ? await request.get({
+ url: config.summalyProxy,
+ qs: {
+ url: ctx.query.url
+ },
+ json: true
+ }) : await summaly(ctx.query.url, {
followRedirects: false
});
+
summary.icon = wrap(summary.icon);
summary.thumbnail = wrap(summary.thumbnail);
diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts
index b090d56ce..1da0f49a2 100644
--- a/src/services/drive/add-file.ts
+++ b/src/services/drive/add-file.ts
@@ -40,7 +40,7 @@ async function save(path: string, name: string, type: string, hash: string, size
const thumbnailKey = `${config.drive.prefix}/${uuid.v4()}/${name}.thumbnail.jpg`;
const baseUrl = config.drive.baseUrl
- || `${ config.drive.config.secure ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? ':' + config.drive.config.port : '' }/${ config.drive.bucket }`;
+ || `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? ':' + config.drive.config.port : '' }/${ config.drive.bucket }`;
await minio.putObject(config.drive.bucket, key, fs.createReadStream(path), size, {
'Content-Type': type,
@@ -116,7 +116,8 @@ async function deleteOldFile(user: IRemoteUser) {
const oldFile = await DriveFile.findOne({
_id: {
$nin: [user.avatarId, user.bannerId]
- }
+ },
+ 'metadata.userId': user._id
}, {
sort: {
_id: 1
diff --git a/src/services/following/delete.ts b/src/services/following/delete.ts
index 8a9f739bd..7c285e9ea 100644
--- a/src/services/following/delete.ts
+++ b/src/services/following/delete.ts
@@ -56,7 +56,7 @@ export default async function(follower: IUser, followee: IUser) {
}
if (isLocalUser(follower) && isRemoteUser(followee)) {
- const content = pack(renderUndo(renderFollow(follower, followee)));
+ const content = pack(renderUndo(renderFollow(follower, followee), follower));
deliver(follower, content, followee.inbox);
}
}
diff --git a/src/services/following/requests/cancel.ts b/src/services/following/requests/cancel.ts
index b0b574da5..9655a95f0 100644
--- a/src/services/following/requests/cancel.ts
+++ b/src/services/following/requests/cancel.ts
@@ -8,10 +8,19 @@ import { publishUserStream } from '../../../stream';
export default async function(followee: IUser, follower: IUser) {
if (isRemoteUser(followee)) {
- const content = pack(renderUndo(renderFollow(follower, followee)));
+ const content = pack(renderUndo(renderFollow(follower, followee), follower));
deliver(follower as ILocalUser, content, followee.inbox);
}
+ const request = await FollowRequest.findOne({
+ followeeId: followee._id,
+ followerId: follower._id
+ });
+
+ if (request == null) {
+ throw 'request not found';
+ }
+
await FollowRequest.remove({
followeeId: followee._id,
followerId: follower._id
diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index d8f0f57b6..63e355782 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -239,8 +239,8 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
async function renderActivity(data: Option, note: INote) {
const content = data.renote && data.text == null
- ? renderAnnounce(data.renote.uri ? data.renote.uri : await renderNote(data.renote), note)
- : renderCreate(await renderNote(note));
+ ? renderAnnounce(data.renote.uri ? data.renote.uri : `${config.url}/notes/${data.renote._id}`, note)
+ : renderCreate(await renderNote(note, false), note);
return packAp(content);
}
diff --git a/src/services/note/delete.ts b/src/services/note/delete.ts
index d444b13a8..d0e2b12b4 100644
--- a/src/services/note/delete.ts
+++ b/src/services/note/delete.ts
@@ -32,7 +32,7 @@ export default async function(user: IUser, note: INote) {
//#region ローカルの投稿なら削除アクティビティを配送
if (isLocalUser(user)) {
- const content = pack(renderDelete(await renderNote(note)));
+ const content = pack(renderDelete(await renderNote(note), user));
const followings = await Following.find({
followeeId: user._id,
diff --git a/src/services/update-chart.ts b/src/services/update-chart.ts
index 6b69adbdc..1f8da6be9 100644
--- a/src/services/update-chart.ts
+++ b/src/services/update-chart.ts
@@ -5,103 +5,63 @@ import { IDriveFile } from '../models/drive-file';
type Omit = Pick>;
-async function getTodayStats(): Promise {
+async function getCurrentStats(span: 'day' | 'hour'): Promise {
const now = new Date();
const y = now.getFullYear();
const m = now.getMonth();
const d = now.getDate();
- const today = new Date(y, m, d);
+ const h = now.getHours();
- // 今日の統計
- const todayStats = await Stats.findOne({
- date: today
+ const current =
+ span == 'day' ? new Date(y, m, d) :
+ span == 'hour' ? new Date(y, m, d, h) :
+ null;
+
+ // 現在(今日または今のHour)の統計
+ const currentStats = await Stats.findOne({
+ span: span,
+ date: current
});
- // 日付が変わってから、初めてのチャート更新なら
- if (todayStats == null) {
+ if (currentStats) {
+ return currentStats;
+ } else {
+ // 集計期間が変わってから、初めてのチャート更新なら
// 最も最近の統計を持ってくる
+ // * 例えば集計期間が「日」である場合で考えると、
// * 昨日何もチャートを更新するような出来事がなかった場合は、
- // 統計がそもそも作られずドキュメントが存在しないということがあり得るため、
- // 「昨日の」と決め打ちせずに「もっとも最近の」とします
- const mostRecentStats = await Stats.findOne({}, {
+ // * 統計がそもそも作られずドキュメントが存在しないということがあり得るため、
+ // * 「昨日の」と決め打ちせずに「もっとも最近の」とします
+ const mostRecentStats = await Stats.findOne({
+ span: span
+ }, {
sort: {
date: -1
}
});
- // 統計が存在しなかったら
- // * Misskeyインスタンスを建てて初めてのチャート更新時など
- if (mostRecentStats == null) {
- // 空の統計を作成
+ if (mostRecentStats) {
+ // 現在の統計を初期挿入
const data: Omit = {
- date: today,
- users: {
- local: {
- total: 0,
- diff: 0
- },
- remote: {
- total: 0,
- diff: 0
- }
- },
- notes: {
- local: {
- total: 0,
- diff: 0,
- diffs: {
- normal: 0,
- reply: 0,
- renote: 0
- }
- },
- remote: {
- total: 0,
- diff: 0,
- diffs: {
- normal: 0,
- reply: 0,
- renote: 0
- }
- }
- },
- drive: {
- local: {
- totalCount: 0,
- totalSize: 0,
- diffCount: 0,
- diffSize: 0
- },
- remote: {
- totalCount: 0,
- totalSize: 0,
- diffCount: 0,
- diffSize: 0
- }
- }
- };
-
- const stats = await Stats.insert(data);
-
- return stats;
- } else {
- // 今日の統計を初期挿入
- const data: Omit = {
- date: today,
+ span: span,
+ date: current,
users: {
local: {
total: mostRecentStats.users.local.total,
- diff: 0
+ inc: 0,
+ dec: 0
},
remote: {
total: mostRecentStats.users.remote.total,
- diff: 0
+ inc: 0,
+ dec: 0
}
},
notes: {
local: {
total: mostRecentStats.notes.local.total,
- diff: 0,
+ inc: 0,
+ dec: 0,
diffs: {
normal: 0,
reply: 0,
@@ -110,7 +70,8 @@ async function getTodayStats(): Promise {
},
remote: {
total: mostRecentStats.notes.remote.total,
- diff: 0,
+ inc: 0,
+ dec: 0,
diffs: {
normal: 0,
reply: 0,
@@ -122,78 +83,163 @@ async function getTodayStats(): Promise {
local: {
totalCount: mostRecentStats.drive.local.totalCount,
totalSize: mostRecentStats.drive.local.totalSize,
- diffCount: 0,
- diffSize: 0
+ incCount: 0,
+ incSize: 0,
+ decCount: 0,
+ decSize: 0
},
remote: {
totalCount: mostRecentStats.drive.remote.totalCount,
totalSize: mostRecentStats.drive.remote.totalSize,
- diffCount: 0,
- diffSize: 0
+ incCount: 0,
+ incSize: 0,
+ decCount: 0,
+ decSize: 0
}
}
};
const stats = await Stats.insert(data);
+ return stats;
+ } else {
+ // 統計が存在しなかったら
+ // * Misskeyインスタンスを建てて初めてのチャート更新時など
+
+ // 空の統計を作成
+ const emptyStat: Omit = {
+ span: span,
+ date: current,
+ users: {
+ local: {
+ total: 0,
+ inc: 0,
+ dec: 0
+ },
+ remote: {
+ total: 0,
+ inc: 0,
+ dec: 0
+ }
+ },
+ notes: {
+ local: {
+ total: 0,
+ inc: 0,
+ dec: 0,
+ diffs: {
+ normal: 0,
+ reply: 0,
+ renote: 0
+ }
+ },
+ remote: {
+ total: 0,
+ inc: 0,
+ dec: 0,
+ diffs: {
+ normal: 0,
+ reply: 0,
+ renote: 0
+ }
+ }
+ },
+ drive: {
+ local: {
+ totalCount: 0,
+ totalSize: 0,
+ incCount: 0,
+ incSize: 0,
+ decCount: 0,
+ decSize: 0
+ },
+ remote: {
+ totalCount: 0,
+ totalSize: 0,
+ incCount: 0,
+ incSize: 0,
+ decCount: 0,
+ decSize: 0
+ }
+ }
+ };
+
+ const stats = await Stats.insert(emptyStat);
+
return stats;
}
- } else {
- return todayStats;
}
}
-async function update(inc: any) {
- const stats = await getTodayStats();
+function update(inc: any) {
+ getCurrentStats('day').then(stats => {
+ Stats.findOneAndUpdate({
+ _id: stats._id
+ }, {
+ $inc: inc
+ });
+ });
- await Stats.findOneAndUpdate({
- _id: stats._id
- }, {
- $inc: inc
+ getCurrentStats('hour').then(stats => {
+ Stats.findOneAndUpdate({
+ _id: stats._id
+ }, {
+ $inc: inc
+ });
});
}
export async function updateUserStats(user: IUser, isAdditional: boolean) {
- const amount = isAdditional ? 1 : -1;
const origin = isLocalUser(user) ? 'local' : 'remote';
const inc = {} as any;
- inc[`users.${origin}.total`] = amount;
- inc[`users.${origin}.diff`] = amount;
+ inc[`users.${origin}.total`] = isAdditional ? 1 : -1;
+ if (isAdditional) {
+ inc[`users.${origin}.inc`] = 1;
+ } else {
+ inc[`users.${origin}.dec`] = 1;
+ }
await update(inc);
}
export async function updateNoteStats(note: INote, isAdditional: boolean) {
- const amount = isAdditional ? 1 : -1;
const origin = isLocalUser(note._user) ? 'local' : 'remote';
const inc = {} as any;
- inc[`notes.${origin}.total`] = amount;
- inc[`notes.${origin}.diff`] = amount;
+ inc[`notes.${origin}.total`] = isAdditional ? 1 : -1;
+
+ if (isAdditional) {
+ inc[`notes.${origin}.inc`] = 1;
+ } else {
+ inc[`notes.${origin}.dec`] = 1;
+ }
if (note.replyId != null) {
- inc[`notes.${origin}.diffs.reply`] = amount;
+ inc[`notes.${origin}.diffs.reply`] = isAdditional ? 1 : -1;
} else if (note.renoteId != null) {
- inc[`notes.${origin}.diffs.renote`] = amount;
+ inc[`notes.${origin}.diffs.renote`] = isAdditional ? 1 : -1;
} else {
- inc[`notes.${origin}.diffs.normal`] = amount;
+ inc[`notes.${origin}.diffs.normal`] = isAdditional ? 1 : -1;
}
await update(inc);
}
export async function updateDriveStats(file: IDriveFile, isAdditional: boolean) {
- const amount = isAdditional ? 1 : -1;
- const size = isAdditional ? file.length : -file.length;
const origin = isLocalUser(file.metadata._user) ? 'local' : 'remote';
const inc = {} as any;
- inc[`drive.${origin}.totalCount`] = amount;
- inc[`drive.${origin}.diffCount`] = amount;
- inc[`drive.${origin}.totalSize`] = size;
- inc[`drive.${origin}.diffSize`] = size;
+ inc[`drive.${origin}.totalCount`] = isAdditional ? 1 : -1;
+ inc[`drive.${origin}.totalSize`] = isAdditional ? file.length : -file.length;
+ if (isAdditional) {
+ inc[`drive.${origin}.incCount`] = 1;
+ inc[`drive.${origin}.incSize`] = file.length;
+ } else {
+ inc[`drive.${origin}.decCount`] = 1;
+ inc[`drive.${origin}.decSize`] = file.length;
+ }
await update(inc);
}
diff --git a/webpack/i18n.ts b/webpack/i18n.ts
index f73af7258..4fd439d1d 100644
--- a/webpack/i18n.ts
+++ b/webpack/i18n.ts
@@ -8,7 +8,7 @@ export const replacement = (ctx: any, _: any, key: string) => {
const client = '/src/client/app/';
let name = null;
- if (key[0] == '@') {
+ if (key.startsWith('@')) {
name = ctx.src.substr(ctx.src.indexOf(client) + client.length);
key = key.substr(1);
}
diff --git a/webpack/loaders/replace.js b/webpack/loaders/replace.js
index fd6bb3617..9f4825e18 100644
--- a/webpack/loaders/replace.js
+++ b/webpack/loaders/replace.js
@@ -7,7 +7,7 @@ function trim(text, g) {
export default function(src) {
const fn = options => {
const search = options.search;
- const g = search[search.length - 1] == 'g';
+ const g = search.endsWith('g');
const file = this.resourcePath.replace(/\\/g, '/');
const replace = options.i18n ? global[options.replace].bind(null, {
src: file,