mirror of
https://git.joinsharkey.org/Sharkey/Sharkey.git
synced 2024-12-02 16:23:08 +02:00
Merge branch 'develop'
This commit is contained in:
commit
80d8af84dd
23 changed files with 180 additions and 35 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -17,6 +17,17 @@ npm i -g ts-node
|
||||||
npm run migrate
|
npm run migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
|
11.22.0 (2019/06/18)
|
||||||
|
--------------------
|
||||||
|
### ✨Improvements
|
||||||
|
* 管理画面でデータベースの各テーブルのレコード数やサイズを確認できるように
|
||||||
|
* サーバー情報にPostgreSQLのバージョンを追加
|
||||||
|
|
||||||
|
### 🐛Fixes
|
||||||
|
* リモートファイルのダウンロードに失敗することがある問題を修正
|
||||||
|
* アンケートの期間を日時指定で選択すると日時がUTCになってしまう問題を修正
|
||||||
|
* MFMのパースを修正
|
||||||
|
|
||||||
11.21.0 (2019/06/16)
|
11.21.0 (2019/06/16)
|
||||||
--------------------
|
--------------------
|
||||||
### ✨Improvements
|
### ✨Improvements
|
||||||
|
|
|
@ -1226,8 +1226,12 @@ admin/views/index.vue:
|
||||||
abuse: "スパム報告"
|
abuse: "スパム報告"
|
||||||
queue: "ジョブキュー"
|
queue: "ジョブキュー"
|
||||||
logs: "ログ"
|
logs: "ログ"
|
||||||
|
db: "データベース"
|
||||||
back-to-misskey: "Misskeyに戻る"
|
back-to-misskey: "Misskeyに戻る"
|
||||||
|
|
||||||
|
admin/views/db.vue:
|
||||||
|
tables: "テーブル"
|
||||||
|
|
||||||
admin/views/dashboard.vue:
|
admin/views/dashboard.vue:
|
||||||
dashboard: "ダッシュボード"
|
dashboard: "ダッシュボード"
|
||||||
accounts: "アカウント"
|
accounts: "アカウント"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "11.21.0",
|
"version": "11.22.0",
|
||||||
"codename": "daybreak",
|
"codename": "daybreak",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -214,7 +214,7 @@
|
||||||
"style-loader": "0.23.1",
|
"style-loader": "0.23.1",
|
||||||
"stylus": "0.54.5",
|
"stylus": "0.54.5",
|
||||||
"stylus-loader": "3.0.2",
|
"stylus-loader": "3.0.2",
|
||||||
"summaly": "2.2.0",
|
"summaly": "2.3.0",
|
||||||
"systeminformation": "4.11.1",
|
"systeminformation": "4.11.1",
|
||||||
"syuilo-password-strength": "0.0.1",
|
"syuilo-password-strength": "0.0.1",
|
||||||
"terser-webpack-plugin": "1.3.0",
|
"terser-webpack-plugin": "1.3.0",
|
||||||
|
|
|
@ -124,7 +124,7 @@ export default Vue.extend({
|
||||||
this.connection = this.$root.stream.useSharedConnection('serverStats');
|
this.connection = this.$root.stream.useSharedConnection('serverStats');
|
||||||
|
|
||||||
this.updateStats();
|
this.updateStats();
|
||||||
this.clock = setInterval(this.updateStats, 1000);
|
this.clock = setInterval(this.updateStats, 3000);
|
||||||
|
|
||||||
this.$root.getMeta().then(meta => {
|
this.$root.getMeta().then(meta => {
|
||||||
this.meta = meta;
|
this.meta = meta;
|
||||||
|
|
39
src/client/app/admin/views/db.vue
Normal file
39
src/client/app/admin/views/db.vue
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ui-card>
|
||||||
|
<template #title><fa :icon="faDatabase"/> {{ $t('tables') }}</template>
|
||||||
|
<section v-if="tables">
|
||||||
|
<div v-for="table in Object.keys(tables)"><b>{{ table }}</b> {{ tables[table].count | number }} {{ tables[table].size | bytes }}</div>
|
||||||
|
</section>
|
||||||
|
</ui-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import i18n from '../../i18n';
|
||||||
|
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
i18n: i18n('admin/views/db.vue'),
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tables: null,
|
||||||
|
faDatabase
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.fetch();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetch() {
|
||||||
|
this.$root.api('admin/get-table-stats').then(tables => {
|
||||||
|
this.tables = tables;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -22,6 +22,7 @@
|
||||||
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
|
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
|
||||||
<li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li>
|
<li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li>
|
||||||
<li @click="nav('logs')" :class="{ active: page == 'logs' }"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</li>
|
<li @click="nav('logs')" :class="{ active: page == 'logs' }"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</li>
|
||||||
|
<li @click="nav('db')" :class="{ active: page == 'db' }"><fa :icon="faDatabase" fixed-width/>{{ $t('db') }}</li>
|
||||||
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
|
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
|
||||||
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
|
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
|
||||||
<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li>
|
<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li>
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
<div v-if="page == 'instance'"><x-instance/></div>
|
<div v-if="page == 'instance'"><x-instance/></div>
|
||||||
<div v-if="page == 'queue'"><x-queue/></div>
|
<div v-if="page == 'queue'"><x-queue/></div>
|
||||||
<div v-if="page == 'logs'"><x-logs/></div>
|
<div v-if="page == 'logs'"><x-logs/></div>
|
||||||
|
<div v-if="page == 'db'"><x-db/></div>
|
||||||
<div v-if="page == 'moderators'"><x-moderators/></div>
|
<div v-if="page == 'moderators'"><x-moderators/></div>
|
||||||
<div v-if="page == 'users'"><x-users/></div>
|
<div v-if="page == 'users'"><x-users/></div>
|
||||||
<div v-if="page == 'emoji'"><x-emoji/></div>
|
<div v-if="page == 'emoji'"><x-emoji/></div>
|
||||||
|
@ -59,19 +61,20 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import { version } from '../../config';
|
import { version } from '../../config';
|
||||||
import XDashboard from "./dashboard.vue";
|
import XDashboard from './dashboard.vue';
|
||||||
import XInstance from "./instance.vue";
|
import XInstance from './instance.vue';
|
||||||
import XQueue from "./queue.vue";
|
import XQueue from './queue.vue';
|
||||||
import XLogs from "./logs.vue";
|
import XLogs from './logs.vue';
|
||||||
import XModerators from "./moderators.vue";
|
import XDb from './db.vue';
|
||||||
import XEmoji from "./emoji.vue";
|
import XModerators from './moderators.vue';
|
||||||
import XAnnouncements from "./announcements.vue";
|
import XEmoji from './emoji.vue';
|
||||||
import XUsers from "./users.vue";
|
import XAnnouncements from './announcements.vue';
|
||||||
import XDrive from "./drive.vue";
|
import XUsers from './users.vue';
|
||||||
import XAbuse from "./abuse.vue";
|
import XDrive from './drive.vue';
|
||||||
import XFederation from "./federation.vue";
|
import XAbuse from './abuse.vue';
|
||||||
|
import XFederation from './federation.vue';
|
||||||
|
|
||||||
import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks, faStream } from '@fortawesome/free-solid-svg-icons';
|
import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks, faStream, faDatabase } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faGrin } from '@fortawesome/free-regular-svg-icons';
|
import { faGrin } from '@fortawesome/free-regular-svg-icons';
|
||||||
|
|
||||||
// Detect the user agent
|
// Detect the user agent
|
||||||
|
@ -85,6 +88,7 @@ export default Vue.extend({
|
||||||
XInstance,
|
XInstance,
|
||||||
XQueue,
|
XQueue,
|
||||||
XLogs,
|
XLogs,
|
||||||
|
XDb,
|
||||||
XModerators,
|
XModerators,
|
||||||
XEmoji,
|
XEmoji,
|
||||||
XAnnouncements,
|
XAnnouncements,
|
||||||
|
@ -108,7 +112,8 @@ export default Vue.extend({
|
||||||
faGlobe,
|
faGlobe,
|
||||||
faExclamationCircle,
|
faExclamationCircle,
|
||||||
faTasks,
|
faTasks,
|
||||||
faStream
|
faStream,
|
||||||
|
faDatabase,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -89,9 +89,7 @@ export default Vue.extend({
|
||||||
|
|
||||||
get() {
|
get() {
|
||||||
const at = () => {
|
const at = () => {
|
||||||
const [date] = moment(this.atDate).toISOString().split('T');
|
return moment(`${this.atDate} ${this.atTime}`).valueOf();
|
||||||
const [hour, minute] = this.atTime.split(':');
|
|
||||||
return moment(`${date}T${hour}:${minute}Z`).valueOf();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const after = () => {
|
const after = () => {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
<p>Maintainer: <b><a :href="'mailto:' + meta.maintainerEmail" target="_blank">{{ meta.maintainerName }}</a></b></p>
|
<p>Maintainer: <b><a :href="'mailto:' + meta.maintainerEmail" target="_blank">{{ meta.maintainerName }}</a></b></p>
|
||||||
<p>Machine: {{ meta.machine }}</p>
|
<p>Machine: {{ meta.machine }}</p>
|
||||||
<p>Node: {{ meta.node }}</p>
|
<p>Node: {{ meta.node }}</p>
|
||||||
|
<p>PSQL: {{ meta.psql }}</p>
|
||||||
<p>Version: {{ meta.version }} </p>
|
<p>Version: {{ meta.version }} </p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { URL } from 'url';
|
|
||||||
import * as yaml from 'js-yaml';
|
import * as yaml from 'js-yaml';
|
||||||
import { Source, Mixin } from './types';
|
import { Source, Mixin } from './types';
|
||||||
import * as pkg from '../../package.json';
|
import * as pkg from '../../package.json';
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { parseFragment, DefaultTreeDocumentFragment } from 'parse5';
|
import { parseFragment, DefaultTreeDocumentFragment } from 'parse5';
|
||||||
import { URL } from 'url';
|
|
||||||
import { urlRegex } from './prelude';
|
import { urlRegex } from './prelude';
|
||||||
|
|
||||||
export function fromHtml(html: string): string {
|
export function fromHtml(html: string): string {
|
||||||
|
|
|
@ -98,13 +98,13 @@ export const mfmLanguage = P.createLanguage({
|
||||||
const text = input.substr(i);
|
const text = input.substr(i);
|
||||||
const match = text.match(/^(\*|_)([a-zA-Z0-9]+?[\s\S]*?)\1/);
|
const match = text.match(/^(\*|_)([a-zA-Z0-9]+?[\s\S]*?)\1/);
|
||||||
if (!match) return P.makeFailure(i, 'not a italic');
|
if (!match) return P.makeFailure(i, 'not a italic');
|
||||||
if (input[i - 1] != null && input[i - 1].match(/[a-z0-9]/i)) return P.makeFailure(i, 'not a italic');
|
if (input[i - 1] != null && input[i - 1] != ' ' && input[i - 1] != '\n') return P.makeFailure(i, 'not a italic');
|
||||||
return P.makeSuccess(i + match[0].length, match[2]);
|
return P.makeSuccess(i + match[0].length, match[2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
return P.alt(xml, underscore).map(x => createTree('italic', r.inline.atLeast(1).tryParse(x), {}));
|
return P.alt(xml, underscore).map(x => createTree('italic', r.inline.atLeast(1).tryParse(x), {}));
|
||||||
},
|
},
|
||||||
strike: r => P.regexp(/~~(.+?)~~/, 1).map(x => createTree('strike', r.inline.atLeast(1).tryParse(x), {})),
|
strike: r => P.regexp(/~~([^\n~]+?)~~/, 1).map(x => createTree('strike', r.inline.atLeast(1).tryParse(x), {})),
|
||||||
motion: r => {
|
motion: r => {
|
||||||
const paren = P.regexp(/\(\(\(([\s\S]+?)\)\)\)/, 1);
|
const paren = P.regexp(/\(\(\(([\s\S]+?)\)\)\)/, 1);
|
||||||
const xml = P.regexp(/<motion>(.+?)<\/motion>/, 1);
|
const xml = P.regexp(/<motion>(.+?)<\/motion>/, 1);
|
||||||
|
@ -164,8 +164,10 @@ export const mfmLanguage = P.createLanguage({
|
||||||
} else
|
} else
|
||||||
url = match[0];
|
url = match[0];
|
||||||
url = removeOrphanedBrackets(url);
|
url = removeOrphanedBrackets(url);
|
||||||
|
while (url.endsWith('.') || url.endsWith(',')) {
|
||||||
if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.'));
|
if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.'));
|
||||||
if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(','));
|
if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(','));
|
||||||
|
}
|
||||||
return P.makeSuccess(i + url.length, url);
|
return P.makeSuccess(i + url.length, url);
|
||||||
}).map(x => createLeaf('url', { url: x }));
|
}).map(x => createLeaf('url', { url: x }));
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import { toASCII } from 'punycode';
|
import { toASCII } from 'punycode';
|
||||||
import { URL } from 'url';
|
|
||||||
|
|
||||||
export function getFullApAccount(username: string, host: string | null) {
|
export function getFullApAccount(username: string, host: string | null) {
|
||||||
return host ? `${username}@${toPuny(host)}` : `${username}@${toPuny(config.host)}`;
|
return host ? `${username}@${toPuny(host)}` : `${username}@${toPuny(config.host)}`;
|
||||||
|
|
|
@ -25,10 +25,8 @@ export async function downloadUrl(url: string, path: string) {
|
||||||
rej(error);
|
rej(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
const requestUrl = new URL(url).pathname.match(/[^\u0021-\u00ff]/) ? encodeURI(url) : url;
|
|
||||||
|
|
||||||
const req = request({
|
const req = request({
|
||||||
url: requestUrl,
|
url: new URL(url).href, // https://github.com/syuilo/misskey/issues/2637
|
||||||
proxy: config.proxy,
|
proxy: config.proxy,
|
||||||
timeout: 10 * 1000,
|
timeout: 10 * 1000,
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
@ -3,7 +3,6 @@ import * as httpSignature from 'http-signature';
|
||||||
import { IRemoteUser } from '../../models/entities/user';
|
import { IRemoteUser } from '../../models/entities/user';
|
||||||
import perform from '../../remote/activitypub/perform';
|
import perform from '../../remote/activitypub/perform';
|
||||||
import { resolvePerson, updatePerson } from '../../remote/activitypub/models/person';
|
import { resolvePerson, updatePerson } from '../../remote/activitypub/models/person';
|
||||||
import { URL } from 'url';
|
|
||||||
import { publishApLogStream } from '../../services/stream';
|
import { publishApLogStream } from '../../services/stream';
|
||||||
import Logger from '../../services/logger';
|
import Logger from '../../services/logger';
|
||||||
import { registerOrFetchInstanceDoc } from '../../services/register-or-fetch-instance-doc';
|
import { registerOrFetchInstanceDoc } from '../../services/register-or-fetch-instance-doc';
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { resolveImage } from './image';
|
||||||
import { isCollectionOrOrderedCollection, isCollection, IPerson } from '../type';
|
import { isCollectionOrOrderedCollection, isCollection, IPerson } from '../type';
|
||||||
import { DriveFile } from '../../../models/entities/drive-file';
|
import { DriveFile } from '../../../models/entities/drive-file';
|
||||||
import { fromHtml } from '../../../mfm/fromHtml';
|
import { fromHtml } from '../../../mfm/fromHtml';
|
||||||
import { URL } from 'url';
|
|
||||||
import { resolveNote, extractEmojis } from './note';
|
import { resolveNote, extractEmojis } from './note';
|
||||||
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
||||||
import { ITag, extractHashtags } from './tag';
|
import { ITag, extractHashtags } from './tag';
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { request } from 'https';
|
import { request } from 'https';
|
||||||
import { sign } from 'http-signature';
|
import { sign } from 'http-signature';
|
||||||
import { URL } from 'url';
|
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
import { lookup, IRunOptions } from 'lookup-dns-cache';
|
import { lookup, IRunOptions } from 'lookup-dns-cache';
|
||||||
import * as promiseAny from 'promise-any';
|
import * as promiseAny from 'promise-any';
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import webFinger from './webfinger';
|
import webFinger from './webfinger';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import { createPerson, updatePerson } from './activitypub/models/person';
|
import { createPerson, updatePerson } from './activitypub/models/person';
|
||||||
import { URL } from 'url';
|
|
||||||
import { remoteLogger } from './logger';
|
import { remoteLogger } from './logger';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { User, IRemoteUser } from '../models/entities/user';
|
import { User, IRemoteUser } from '../models/entities/user';
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import * as request from 'request-promise-native';
|
import * as request from 'request-promise-native';
|
||||||
import { URL } from 'url';
|
|
||||||
import { query as urlQuery } from '../prelude/url';
|
import { query as urlQuery } from '../prelude/url';
|
||||||
|
|
||||||
type ILink = {
|
type ILink = {
|
||||||
|
|
37
src/server/api/endpoints/admin/get-table-stats.ts
Normal file
37
src/server/api/endpoints/admin/get-table-stats.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import define from '../../define';
|
||||||
|
import { getConnection } from 'typeorm';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: false,
|
||||||
|
|
||||||
|
desc: {
|
||||||
|
'en-US': 'Get table stats'
|
||||||
|
},
|
||||||
|
|
||||||
|
tags: ['meta'],
|
||||||
|
|
||||||
|
params: {
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default define(meta, async () => {
|
||||||
|
const sizes = await
|
||||||
|
getConnection().query(`
|
||||||
|
SELECT relname AS "table", reltuples as "count", pg_total_relation_size(C.oid) AS "size"
|
||||||
|
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
|
||||||
|
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
|
||||||
|
AND C.relkind <> 'i'
|
||||||
|
AND nspname !~ '^pg_toast';`)
|
||||||
|
.then(recs => {
|
||||||
|
const res = {} as Record<string, { count: number; size: number; }>;
|
||||||
|
for (const rec of recs) {
|
||||||
|
res[rec.table] = {
|
||||||
|
count: parseInt(rec.count, 10),
|
||||||
|
size: parseInt(rec.size, 10),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
|
return sizes;
|
||||||
|
});
|
|
@ -8,6 +8,7 @@ import * as bcrypt from 'bcryptjs';
|
||||||
import { Users, UserProfiles } from '../../../../models';
|
import { Users, UserProfiles } from '../../../../models';
|
||||||
import { ensure } from '../../../../prelude/ensure';
|
import { ensure } from '../../../../prelude/ensure';
|
||||||
import { sendEmail } from '../../../../services/send-email';
|
import { sendEmail } from '../../../../services/send-email';
|
||||||
|
import { ApiError } from '../../error';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
@ -27,6 +28,14 @@ export const meta = {
|
||||||
email: {
|
email: {
|
||||||
validator: $.optional.nullable.str
|
validator: $.optional.nullable.str
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
incorrectPassword: {
|
||||||
|
message: 'Incorrect password.',
|
||||||
|
code: 'INCORRECT_PASSWORD',
|
||||||
|
id: 'e54c1d7e-e7d6-4103-86b6-0a95069b4ad3'
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,7 +46,7 @@ export default define(meta, async (ps, user) => {
|
||||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||||
|
|
||||||
if (!same) {
|
if (!same) {
|
||||||
throw new Error('incorrect password');
|
throw new ApiError(meta.errors.incorrectPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
await UserProfiles.update({ userId: user.id }, {
|
await UserProfiles.update({ userId: user.id }, {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { fetchMeta } from '../../../misc/fetch-meta';
|
||||||
import * as pkg from '../../../../package.json';
|
import * as pkg from '../../../../package.json';
|
||||||
import { Emojis } from '../../../models';
|
import { Emojis } from '../../../models';
|
||||||
import { types, bool } from '../../../misc/schema';
|
import { types, bool } from '../../../misc/schema';
|
||||||
|
import { getConnection } from 'typeorm';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
stability: 'stable',
|
stability: 'stable',
|
||||||
|
@ -114,6 +115,7 @@ export default define(meta, async (ps, me) => {
|
||||||
machine: os.hostname(),
|
machine: os.hostname(),
|
||||||
os: os.platform(),
|
os: os.platform(),
|
||||||
node: process.version,
|
node: process.version,
|
||||||
|
psql: await getConnection().query('SHOW server_version').then(x => x[0].server_version),
|
||||||
|
|
||||||
cpu: {
|
cpu: {
|
||||||
model: os.cpus()[0].model,
|
model: os.cpus()[0].model,
|
||||||
|
|
|
@ -163,6 +163,19 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||||
},
|
},
|
||||||
...Chart.convertSchemaToFlatColumnDefinitions(schema)
|
...Chart.convertSchemaToFlatColumnDefinitions(schema)
|
||||||
},
|
},
|
||||||
|
indices: [{
|
||||||
|
columns: ['date']
|
||||||
|
}, {
|
||||||
|
columns: ['span']
|
||||||
|
}, {
|
||||||
|
columns: ['group']
|
||||||
|
}, {
|
||||||
|
columns: ['span', 'date']
|
||||||
|
}, {
|
||||||
|
columns: ['date', 'group']
|
||||||
|
}, {
|
||||||
|
columns: ['span', 'date', 'group']
|
||||||
|
}]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
34
test/mfm.ts
34
test/mfm.ts
|
@ -804,6 +804,14 @@ describe('MFM', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('ignore trailing periods', () => {
|
||||||
|
const tokens = parse('https://example.com...');
|
||||||
|
assert.deepStrictEqual(tokens, [
|
||||||
|
leaf('url', { url: 'https://example.com' }),
|
||||||
|
text('...')
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('with comma', () => {
|
it('with comma', () => {
|
||||||
const tokens = parse('https://example.com/foo?bar=a,b');
|
const tokens = parse('https://example.com/foo?bar=a,b');
|
||||||
assert.deepStrictEqual(tokens, [
|
assert.deepStrictEqual(tokens, [
|
||||||
|
@ -1116,6 +1124,14 @@ describe('MFM', () => {
|
||||||
], {}),
|
], {}),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://misskey.io/notes/7u1kv5dmia
|
||||||
|
it('ignore internal tilde', () => {
|
||||||
|
const tokens = parse('~~~~~');
|
||||||
|
assert.deepStrictEqual(tokens, [
|
||||||
|
text('~~~~~')
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('italic', () => {
|
describe('italic', () => {
|
||||||
|
@ -1173,6 +1189,24 @@ describe('MFM', () => {
|
||||||
text('foo_bar_baz'),
|
text('foo_bar_baz'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('require spaces', () => {
|
||||||
|
const tokens = parse('4日目_L38b a_b');
|
||||||
|
assert.deepStrictEqual(tokens, [
|
||||||
|
text('4日目_L38b a_b'),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('newline sandwich', () => {
|
||||||
|
const tokens = parse('foo\n_bar_\nbaz');
|
||||||
|
assert.deepStrictEqual(tokens, [
|
||||||
|
text('foo\n'),
|
||||||
|
tree('italic', [
|
||||||
|
text('bar')
|
||||||
|
], {}),
|
||||||
|
text('\nbaz'),
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue