summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgutfuckllc <40531868+gutfuckllc@users.noreply.github.com>2018-08-06 15:45:52 -0400
committergutfuckllc <40531868+gutfuckllc@users.noreply.github.com>2018-08-06 15:45:52 -0400
commita256393b81f2fda62641961ce515a85c8b9f51c3 (patch)
tree9a205811a11b675b853aca3e463152a0d816ff69 /src
parentTranslated mute. (diff)
parent5.19.0 (diff)
downloadmisskey-a256393b81f2fda62641961ce515a85c8b9f51c3.tar.gz
misskey-a256393b81f2fda62641961ce515a85c8b9f51c3.tar.bz2
misskey-a256393b81f2fda62641961ce515a85c8b9f51c3.zip
Merge remote-tracking branch 'upstream/master' into devel
Diffstat (limited to 'src')
-rw-r--r--src/client/app/common/scripts/date-stringify.ts13
-rw-r--r--src/client/app/common/views/components/autocomplete.vue2
-rw-r--r--src/client/app/common/views/components/signin.vue2
-rw-r--r--src/client/app/desktop/api/update-avatar.ts16
-rw-r--r--src/client/app/desktop/api/update-banner.ts16
-rw-r--r--src/client/app/desktop/views/components/note-detail.vue3
-rw-r--r--src/client/app/desktop/views/components/note-preview.vue3
-rw-r--r--src/client/app/desktop/views/components/notes.note.sub.vue3
-rw-r--r--src/client/app/desktop/views/components/notes.note.vue3
-rw-r--r--src/client/app/desktop/views/components/user-lists-window.vue2
-rw-r--r--src/client/app/desktop/views/pages/user/user.profile.vue4
-rw-r--r--src/client/app/desktop/views/widgets/post-form.vue2
-rw-r--r--src/index.ts2
-rw-r--r--src/queue/processors/http/deliver.ts5
-rw-r--r--src/server/api/endpoints/users/search.ts161
-rw-r--r--src/server/api/endpoints/users/search_by_username.ts70
16 files changed, 175 insertions, 132 deletions
diff --git a/src/client/app/common/scripts/date-stringify.ts b/src/client/app/common/scripts/date-stringify.ts
deleted file mode 100644
index 2b8e525567..0000000000
--- a/src/client/app/common/scripts/date-stringify.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-export default date => {
- if (typeof date == 'string') date = new Date(date);
- return (
- date.getFullYear() + '%i18n:common.date.full-year%' +
- (date.getMonth() + 1) + '%i18n:common.date.month%' +
- date.getDate() + '%i18n:common.date.day%' +
- ' ' +
- date.getHours() + '%i18n:common.date.hours%' +
- date.getMinutes() + '%i18n:common.date.minutes%' +
- ' ' +
- `(${['日', '月', '火', '水', '木', '金', '土'][date.getDay()]})`
- );
-};
diff --git a/src/client/app/common/views/components/autocomplete.vue b/src/client/app/common/views/components/autocomplete.vue
index cd6066877c..b274eaa0a0 100644
--- a/src/client/app/common/views/components/autocomplete.vue
+++ b/src/client/app/common/views/components/autocomplete.vue
@@ -132,7 +132,7 @@ export default Vue.extend({
this.users = users;
this.fetching = false;
} else {
- (this as any).api('users/search_by_username', {
+ (this as any).api('users/search', {
query: this.q,
limit: 30
}).then(users => {
diff --git a/src/client/app/common/views/components/signin.vue b/src/client/app/common/views/components/signin.vue
index 58241cef09..deaeeca6a7 100644
--- a/src/client/app/common/views/components/signin.vue
+++ b/src/client/app/common/views/components/signin.vue
@@ -12,7 +12,7 @@
</ui-input>
<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required/>
<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button>
- <p style="margin: 8px 0;">または<a :href="`${apiUrl}/signin/twitter`">Twitterでログイン</a></p>
+ <p style="margin: 8px 0;">%i18n:@or%<a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p>
</form>
</template>
diff --git a/src/client/app/desktop/api/update-avatar.ts b/src/client/app/desktop/api/update-avatar.ts
index 887367a24e..83820f92bd 100644
--- a/src/client/app/desktop/api/update-avatar.ts
+++ b/src/client/app/desktop/api/update-avatar.ts
@@ -8,7 +8,7 @@ export default (os: OS) => (cb, file = null) => {
const w = os.new(CropWindow, {
image: file,
- title: 'アバターとして表示する部分を選択',
+ title: '%i18n:desktop.avatar-crop-title%',
aspectRatio: 1 / 1
});
@@ -18,11 +18,11 @@ export default (os: OS) => (cb, file = null) => {
data.append('file', blob, file.name + '.cropped.png');
os.api('drive/folders/find', {
- name: 'アイコン'
+ name: '%i18n:desktop.avatar%'
}).then(iconFolder => {
if (iconFolder.length === 0) {
os.api('drive/folders/create', {
- name: 'アイコン'
+ name: '%i18n:desktop.avatar%'
}).then(iconFolder => {
upload(data, iconFolder);
});
@@ -41,7 +41,7 @@ export default (os: OS) => (cb, file = null) => {
const upload = (data, folder) => {
const dialog = os.new(ProgressDialog, {
- title: '新しいアバターをアップロードしています'
+ title: '%i18n:desktop.uploading-avatar%'
});
document.body.appendChild(dialog.$el);
@@ -76,10 +76,10 @@ export default (os: OS) => (cb, file = null) => {
});
os.apis.dialog({
- title: '%fa:info-circle%アバターを更新しました',
- text: '新しいアバターが反映されるまで時間がかかる場合があります。',
+ title: '%fa:info-circle% %i18n:desktop.avatar-updated%',
+ text: null,
actions: [{
- text: 'わかった'
+ text: '%i18n:common.got-it%'
}]
});
@@ -92,7 +92,7 @@ export default (os: OS) => (cb, file = null) => {
} else {
os.apis.chooseDriveFile({
multiple: false,
- title: '%fa:image%アバターにする画像を選択'
+ title: '%fa:image% %i18n:desktop.choose-avatar%'
}).then(file => {
fileSelected(file);
});
diff --git a/src/client/app/desktop/api/update-banner.ts b/src/client/app/desktop/api/update-banner.ts
index 4e6dd4e2c7..33c4e306a2 100644
--- a/src/client/app/desktop/api/update-banner.ts
+++ b/src/client/app/desktop/api/update-banner.ts
@@ -8,7 +8,7 @@ export default (os: OS) => {
const cropImage = file => new Promise((resolve, reject) => {
const w = os.new(CropWindow, {
image: file,
- title: 'バナーとして表示する部分を選択',
+ title: '%i18n:desktop.banner-crop-title%',
aspectRatio: 16 / 9
});
@@ -18,11 +18,11 @@ export default (os: OS) => {
data.append('file', blob, file.name + '.cropped.png');
os.api('drive/folders/find', {
- name: 'バナー'
+ name: '%i18n:desktop.banner%'
}).then(bannerFolder => {
if (bannerFolder.length === 0) {
os.api('drive/folders/create', {
- name: 'バナー'
+ name: '%i18n:desktop.banner%'
}).then(iconFolder => {
resolve(upload(data, iconFolder));
});
@@ -43,7 +43,7 @@ export default (os: OS) => {
const upload = (data, folder) => new Promise((resolve, reject) => {
const dialog = os.new(ProgressDialog, {
- title: '新しいバナーをアップロードしています'
+ title: '%i18n:desktop.uploading-banner%'
});
document.body.appendChild(dialog.$el);
@@ -79,10 +79,10 @@ export default (os: OS) => {
});
os.apis.dialog({
- title: '%fa:info-circle%バナーを更新しました',
- text: '新しいバナーが反映されるまで時間がかかる場合があります。',
+ title: '%fa:info-circle% %i18n:desktop.banner-updated%',
+ text: null,
actions: [{
- text: 'わかった'
+ text: '%i18n:common.got-it%'
}]
});
@@ -95,7 +95,7 @@ export default (os: OS) => {
? Promise.resolve(file)
: os.apis.chooseDriveFile({
multiple: false,
- title: '%fa:image%バナーにする画像を選択'
+ title: '%fa:image% %i18n:desktop.choose-banner%'
});
return selectedFile
diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue
index 36a5889220..b6980fae72 100644
--- a/src/client/app/desktop/views/components/note-detail.vue
+++ b/src/client/app/desktop/views/components/note-detail.vue
@@ -79,7 +79,6 @@
<script lang="ts">
import Vue from 'vue';
-import dateStringify from '../../../common/scripts/date-stringify';
import parse from '../../../../../mfm/parse';
import MkPostFormWindow from './post-form-window.vue';
@@ -129,7 +128,7 @@ export default Vue.extend({
: 0;
},
title(): string {
- return dateStringify(this.p.createdAt);
+ return new Date(this.p.createdAt).toLocaleString();
},
urls(): string[] {
if (this.p.text) {
diff --git a/src/client/app/desktop/views/components/note-preview.vue b/src/client/app/desktop/views/components/note-preview.vue
index 2a49557247..c723db98c0 100644
--- a/src/client/app/desktop/views/components/note-preview.vue
+++ b/src/client/app/desktop/views/components/note-preview.vue
@@ -12,7 +12,6 @@
<script lang="ts">
import Vue from 'vue';
-import dateStringify from '../../../common/scripts/date-stringify';
export default Vue.extend({
props: {
@@ -28,7 +27,7 @@ export default Vue.extend({
},
computed: {
title(): string {
- return dateStringify(this.note.createdAt);
+ return new Date(this.note.createdAt).toLocaleString();
}
}
});
diff --git a/src/client/app/desktop/views/components/notes.note.sub.vue b/src/client/app/desktop/views/components/notes.note.sub.vue
index a8186fb7e4..fc851e83e9 100644
--- a/src/client/app/desktop/views/components/notes.note.sub.vue
+++ b/src/client/app/desktop/views/components/notes.note.sub.vue
@@ -12,13 +12,12 @@
<script lang="ts">
import Vue from 'vue';
-import dateStringify from '../../../common/scripts/date-stringify';
export default Vue.extend({
props: ['note'],
computed: {
title(): string {
- return dateStringify(this.note.createdAt);
+ return new Date(this.note.createdAt).toLocaleString();
}
}
});
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index 3aa52f75f0..fbbd524070 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -71,7 +71,6 @@
<script lang="ts">
import Vue from 'vue';
-import dateStringify from '../../../common/scripts/date-stringify';
import parse from '../../../../../mfm/parse';
import MkPostFormWindow from './post-form-window.vue';
@@ -128,7 +127,7 @@ export default Vue.extend({
},
title(): string {
- return dateStringify(this.p.createdAt);
+ return new Date(this.p.createdAt).toLocaleString();
},
urls(): string[] {
diff --git a/src/client/app/desktop/views/components/user-lists-window.vue b/src/client/app/desktop/views/components/user-lists-window.vue
index 47648c287d..72ae9cf4e4 100644
--- a/src/client/app/desktop/views/components/user-lists-window.vue
+++ b/src/client/app/desktop/views/components/user-lists-window.vue
@@ -27,7 +27,7 @@ export default Vue.extend({
methods: {
add() {
(this as any).apis.input({
- title: 'リスト名',
+ title: '%i18n:@list-name%',
}).then(async title => {
const list = await (this as any).api('users/lists/create', {
title
diff --git a/src/client/app/desktop/views/pages/user/user.profile.vue b/src/client/app/desktop/views/pages/user/user.profile.vue
index 35993f19a5..efd5be4672 100644
--- a/src/client/app/desktop/views/pages/user/user.profile.vue
+++ b/src/client/app/desktop/views/pages/user/user.profile.vue
@@ -13,7 +13,7 @@
<span v-if="user.isMuted">%fa:eye% %i18n:@unmute%</span>
<span v-if="!user.isMuted">%fa:eye-slash% %i18n:@mute%</span>
</button>
- <button class="mute ui" @click="list">%fa:list% %i18n:@add-to-list%</button>
+ <button class="mute ui" @click="list">%fa:list% %i18n:@push-to-a-list%</button>
</div>
</div>
</template>
@@ -76,7 +76,7 @@ export default Vue.extend({
});
(this as any).apis.dialog({
title: 'Done!',
- text: `${this.user.name}を${list.title}に追加しました。`
+ text: '%i18n:@list-pushed%'.replace('{user}', this.user.name).replace('{list}', list.title)
});
});
}
diff --git a/src/client/app/desktop/views/widgets/post-form.vue b/src/client/app/desktop/views/widgets/post-form.vue
index 618d19efc4..19a2790d95 100644
--- a/src/client/app/desktop/views/widgets/post-form.vue
+++ b/src/client/app/desktop/views/widgets/post-form.vue
@@ -55,7 +55,7 @@ export default define({
}).then(data => {
this.clear();
}).catch(err => {
- alert('失敗した');
+ alert('Something happened');
}).then(() => {
this.posting = false;
});
diff --git a/src/index.ts b/src/index.ts
index 18eff8176c..0dda8b05b7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,6 +4,8 @@
Error.stackTraceLimit = Infinity;
+require('events').EventEmitter.defaultMaxListeners = 128;
+
import * as os from 'os';
import * as cluster from 'cluster';
import * as debug from 'debug';
diff --git a/src/queue/processors/http/deliver.ts b/src/queue/processors/http/deliver.ts
index e06866da4e..75e46559dd 100644
--- a/src/queue/processors/http/deliver.ts
+++ b/src/queue/processors/http/deliver.ts
@@ -7,6 +7,11 @@ export default async (job: bq.Job, done: any): Promise<void> => {
await request(job.data.user, job.data.to, job.data.content);
done();
} catch (res) {
+ if (!res.hasOwnProperty('statusCode')) {
+ console.warn(`deliver failed (unknown): ${res}`);
+ return done();
+ }
+
if (res.statusCode == null) return done();
if (res.statusCode >= 400 && res.statusCode < 500) {
// HTTPステータスコード4xxはクライアントエラーであり、それはつまり
diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts
index d443d35b47..eda3f95728 100644
--- a/src/server/api/endpoints/users/search.ts
+++ b/src/server/api/endpoints/users/search.ts
@@ -1,33 +1,156 @@
import $ from 'cafy';
-import User, { pack, ILocalUser } from '../../../../models/user';
const escapeRegexp = require('escape-regexp');
+import User, { pack, ILocalUser, validateUsername, IUser } from '../../../../models/user';
+import getParams from '../../get-params';
+
+export const meta = {
+ desc: {
+ ja: 'ユーザーを検索します。'
+ },
+
+ requireCredential: false,
+
+ params: {
+ query: $.str.note({
+ desc: {
+ ja: 'クエリ'
+ }
+ }),
+
+ offset: $.num.optional.min(0).note({
+ default: 0,
+ desc: {
+ ja: 'オフセット'
+ }
+ }),
+
+ limit: $.num.optional.range(1, 100).note({
+ default: 10,
+ desc: {
+ ja: '取得する数'
+ }
+ }),
+
+ localOnly: $.bool.optional.note({
+ default: false,
+ desc: {
+ ja: 'ローカルユーザーのみ検索対象にするか否か'
+ }
+ }),
+ },
+};
/**
* Search a user
*/
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
- // Get 'query' parameter
- const [query, queryError] = $.str.pipe(x => x != '').get(params.query);
- if (queryError) return rej('invalid query param');
+ const [ps, psErr] = getParams(meta, params);
+ if (psErr) return rej(psErr);
+
+ const isUsername = validateUsername(ps.query.replace('@', ''));
+
+ let users: IUser[] = [];
+
+ if (isUsername) {
+ users = await User
+ .find({
+ host: null,
+ usernameLower: new RegExp('^' + escapeRegexp(ps.query.replace('@', '').toLowerCase()))
+ }, {
+ limit: ps.limit,
+ skip: ps.offset
+ });
+
+ if (users.length < ps.limit && !ps.localOnly) {
+ const otherUsers = await User
+ .find({
+ host: { $ne: null },
+ usernameLower: new RegExp('^' + escapeRegexp(ps.query.replace('@', '').toLowerCase()))
+ }, {
+ limit: ps.limit - users.length
+ });
+
+ users = users.concat(otherUsers);
+ }
- // Get 'max' parameter
- const [max = 10, maxErr] = $.num.optional.range(1, 30).get(params.max);
- if (maxErr) return rej('invalid max param');
+ if (users.length < ps.limit) {
+ const otherUsers = await User
+ .find({
+ _id: { $nin: users.map(u => u._id) },
+ host: null,
+ usernameLower: new RegExp(escapeRegexp(ps.query.replace('@', '').toLowerCase()))
+ }, {
+ limit: ps.limit - users.length
+ });
- const escapedQuery = escapeRegexp(query);
+ users = users.concat(otherUsers);
+ }
- // Search users
- const users = await User
- .find({
- host: null,
- $or: [{
- usernameLower: new RegExp(escapedQuery.replace('@', '').toLowerCase())
+ if (users.length < ps.limit && !ps.localOnly) {
+ const otherUsers = await User
+ .find({
+ _id: { $nin: users.map(u => u._id) },
+ host: { $ne: null },
+ usernameLower: new RegExp(escapeRegexp(ps.query.replace('@', '').toLowerCase()))
+ }, {
+ limit: ps.limit - users.length
+ });
+
+ users = users.concat(otherUsers);
+ }
+ }
+
+ if (users.length < ps.limit) {
+ const otherUsers = await User
+ .find({
+ _id: { $nin: users.map(u => u._id) },
+ host: null,
+ name: new RegExp('^' + escapeRegexp(ps.query.toLowerCase()))
+ }, {
+ limit: ps.limit - users.length
+ });
+
+ users = users.concat(otherUsers);
+ }
+
+ if (users.length < ps.limit && !ps.localOnly) {
+ const otherUsers = await User
+ .find({
+ _id: { $nin: users.map(u => u._id) },
+ host: { $ne: null },
+ name: new RegExp('^' + escapeRegexp(ps.query.toLowerCase()))
+ }, {
+ limit: ps.limit - users.length
+ });
+
+ users = users.concat(otherUsers);
+ }
+
+ if (users.length < ps.limit) {
+ const otherUsers = await User
+ .find({
+ _id: { $nin: users.map(u => u._id) },
+ host: null,
+ name: new RegExp(escapeRegexp(ps.query.toLowerCase()))
+ }, {
+ limit: ps.limit - users.length
+ });
+
+ users = users.concat(otherUsers);
+ }
+
+ if (users.length < ps.limit && !ps.localOnly) {
+ const otherUsers = await User
+ .find({
+ _id: { $nin: users.map(u => u._id) },
+ host: { $ne: null },
+ name: new RegExp(escapeRegexp(ps.query.toLowerCase()))
}, {
- name: new RegExp(escapedQuery)
- }]
- }, {
- limit: max
- });
+ limit: ps.limit - users.length
+ });
+
+ users = users.concat(otherUsers);
+ }
// Serialize
res(await Promise.all(users.map(user => pack(user, me, { detail: true }))));
diff --git a/src/server/api/endpoints/users/search_by_username.ts b/src/server/api/endpoints/users/search_by_username.ts
deleted file mode 100644
index bfab378389..0000000000
--- a/src/server/api/endpoints/users/search_by_username.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-import $ from 'cafy';
-import User, { pack, ILocalUser } from '../../../../models/user';
-const escapeRegexp = require('escape-regexp');
-
-/**
- * Search a user by username
- */
-export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
- // Get 'query' parameter
- const [query, queryError] = $.str.get(params.query);
- if (queryError) return rej('invalid query param');
-
- // Get 'offset' parameter
- const [offset = 0, offsetErr] = $.num.optional.min(0).get(params.offset);
- if (offsetErr) return rej('invalid offset param');
-
- // Get 'limit' parameter
- const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
- if (limitErr) return rej('invalid limit param');
-
- let users = await User
- .find({
- host: null,
- usernameLower: new RegExp('^' + escapeRegexp(query.toLowerCase()))
- }, {
- limit: limit,
- skip: offset
- });
-
- if (users.length < limit) {
- const otherUsers = await User
- .find({
- host: { $ne: null },
- usernameLower: new RegExp('^' + escapeRegexp(query.toLowerCase()))
- }, {
- limit: limit - users.length
- });
-
- users = users.concat(otherUsers);
- }
-
- if (users.length < limit) {
- const otherUsers = await User
- .find({
- _id: { $nin: users.map(u => u._id) },
- host: null,
- usernameLower: new RegExp(escapeRegexp(query.toLowerCase()))
- }, {
- limit: limit - users.length
- });
-
- users = users.concat(otherUsers);
- }
-
- if (users.length < limit) {
- const otherUsers = await User
- .find({
- _id: { $nin: users.map(u => u._id) },
- host: { $ne: null },
- usernameLower: new RegExp(escapeRegexp(query.toLowerCase()))
- }, {
- limit: limit - users.length
- });
-
- users = users.concat(otherUsers);
- }
-
- // Serialize
- res(await Promise.all(users.map(user => pack(user, me, { detail: true }))));
-});