リファクタとデザイン調整とresolve #1
This commit is contained in:
parent
41f40dd2c2
commit
219b15d9e6
17 changed files with 404 additions and 64 deletions
16
migration/1599577510614-mode.ts
Normal file
16
migration/1599577510614-mode.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import {MigrationInterface, QueryRunner} from 'typeorm';
|
||||
|
||||
export class mode1599577510614 implements MigrationInterface {
|
||||
name = 'mode1599577510614'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('CREATE TYPE "user_alertmode_enum" AS ENUM(\'note\', \'notification\', \'nothing\')');
|
||||
await queryRunner.query('ALTER TABLE "user" ADD "alertMode" "user_alertmode_enum" NOT NULL DEFAULT \'note\'');
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE "user" DROP COLUMN "alertMode"');
|
||||
await queryRunner.query('DROP TYPE "user_alertmode_enum"');
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
"dependencies": {
|
||||
"@types/koa-bodyparser": "^4.3.0",
|
||||
"@types/koa-mount": "^4.0.0",
|
||||
"@types/koa-multer": "^1.0.0",
|
||||
"@types/koa-static": "^4.0.1",
|
||||
"@types/node-cron": "^2.0.3",
|
||||
"@types/uuid": "^8.0.0",
|
||||
|
@ -29,6 +30,7 @@
|
|||
"koa": "^2.13.0",
|
||||
"koa-bodyparser": "^4.3.0",
|
||||
"koa-mount": "^4.0.0",
|
||||
"koa-multer": "^1.0.2",
|
||||
"koa-router": "^9.1.0",
|
||||
"koa-session": "^6.0.0",
|
||||
"koa-static": "^5.0.0",
|
||||
|
|
|
@ -2,32 +2,15 @@ import { api } from '../services/misskey';
|
|||
import { config } from '../config';
|
||||
import { User } from '../models/entities/user';
|
||||
import { updateUser } from './users';
|
||||
import { Score } from '../types/Score';
|
||||
|
||||
export const format = async (user: User): Promise<string> => {
|
||||
const miUser = await api<Record<string, number>>(user.host, 'users/show', { username: user.username }, user.token);
|
||||
if (miUser.error) {
|
||||
throw miUser.error;
|
||||
}
|
||||
const notesDelta = toSignedString(miUser.notesCount - user.prevNotesCount);
|
||||
const followingDelta = toSignedString(miUser.followingCount - user.prevFollowingCount);
|
||||
const followersDelta = toSignedString(miUser.followersCount - user.prevFollowersCount);
|
||||
export const format = (score: Score): string => `昨日のMisskeyの活動は
|
||||
|
||||
await updateUser(user.username, user.host, {
|
||||
prevNotesCount: miUser.notesCount,
|
||||
prevFollowingCount: miUser.followingCount,
|
||||
prevFollowersCount: miUser.followersCount,
|
||||
});
|
||||
|
||||
return `昨日のMisskeyの活動は
|
||||
|
||||
ノート: ${miUser.notesCount}(${notesDelta})
|
||||
フォロー : ${miUser.followingCount}(${followingDelta})
|
||||
フォロワー :${miUser.followersCount}(${followersDelta})
|
||||
ノート: ${score.notesCount}(${score.notesDelta})
|
||||
フォロー : ${score.followingCount}(${score.followingDelta})
|
||||
フォロワー :${score.followersCount}(${score.followersDelta})
|
||||
|
||||
でした。
|
||||
${config.url}
|
||||
|
||||
#misshaialert`;
|
||||
};
|
||||
|
||||
export const toSignedString = (num: number): string => num < 0 ? num.toString() : '+' + num;
|
19
src/functions/get-scores.ts
Normal file
19
src/functions/get-scores.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { User } from '../models/entities/user';
|
||||
import { Score } from '../types/Score';
|
||||
import { api } from '../services/misskey';
|
||||
import { toSignedString } from './to-signed-string';
|
||||
|
||||
export const getScores = async (user: User): Promise<Score> => {
|
||||
const miUser = await api<Record<string, number>>(user.host, 'users/show', { username: user.username }, user.token);
|
||||
if (miUser.error) {
|
||||
throw miUser.error;
|
||||
}
|
||||
return {
|
||||
notesCount: miUser.notesCount,
|
||||
followingCount: miUser.followingCount,
|
||||
followersCount: miUser.followersCount,
|
||||
notesDelta: toSignedString(miUser.notesCount - user.prevNotesCount),
|
||||
followingDelta: toSignedString(miUser.followingCount - user.prevFollowingCount),
|
||||
followersDelta: toSignedString(miUser.followersCount - user.prevFollowersCount),
|
||||
};
|
||||
};
|
1
src/functions/to-signed-string.ts
Normal file
1
src/functions/to-signed-string.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export const toSignedString = (num: number): string => num < 0 ? num.toString() : '+' + num;
|
16
src/functions/update-score.ts
Normal file
16
src/functions/update-score.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { User } from '../models/entities/user';
|
||||
import { api } from '../services/misskey';
|
||||
import { updateUser } from './users';
|
||||
|
||||
export const updateScore = async (user: User): Promise<void> => {
|
||||
const miUser = await api<Record<string, number>>(user.host, 'users/show', { username: user.username }, user.token);
|
||||
if (miUser.error) {
|
||||
throw miUser.error;
|
||||
}
|
||||
|
||||
await updateUser(user.username, user.host, {
|
||||
prevNotesCount: miUser.notesCount,
|
||||
prevFollowingCount: miUser.followingCount,
|
||||
prevFollowersCount: miUser.followersCount,
|
||||
});
|
||||
};
|
|
@ -1,4 +1,5 @@
|
|||
import { Entity, Column, PrimaryGeneratedColumn, Index } from 'typeorm';
|
||||
import { AlertMode, alertModes } from '../../types/AlertMode';
|
||||
|
||||
@Entity()
|
||||
@Index([ 'username', 'host' ], { unique: true })
|
||||
|
@ -44,4 +45,11 @@ export class User {
|
|||
default: 0,
|
||||
})
|
||||
public prevFollowersCount: number;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: alertModes,
|
||||
default: 'note'
|
||||
})
|
||||
public alertMode: AlertMode;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { Context } from 'koa';
|
||||
|
||||
export const die = (ctx: Context, error: string, status = 400): Promise<void> => {
|
||||
export const die = (ctx: Context, error = '問題が発生しました。お手数ですが、最初からやり直してください。', status = 400): Promise<void> => {
|
||||
ctx.status = status;
|
||||
return ctx.render('error', { error });
|
||||
};
|
||||
|
|
|
@ -6,8 +6,11 @@ import crypto from 'crypto';
|
|||
import { die } from './die';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { config } from '../config';
|
||||
import { upsertUser, getUser, getUserCount, updateUser, updateUsersMisshaiToken, getUserByMisshaiToken } from '../functions/users';
|
||||
import { upsertUser, getUser, getUserCount, updateUser, updateUsersMisshaiToken, getUserByMisshaiToken, deleteUser } from '../functions/users';
|
||||
import { api } from '../services/misskey';
|
||||
import { getScores } from '../functions/get-scores';
|
||||
import { AlertMode, alertModes } from '../types/AlertMode';
|
||||
import { Users } from '../models';
|
||||
|
||||
export const router = new Router<DefaultState, Context>();
|
||||
|
||||
|
@ -29,7 +32,7 @@ const login = async (ctx: Context, user: Record<string, unknown>, host: string,
|
|||
const u = await getUser(user.username as string, host);
|
||||
|
||||
if (!u) {
|
||||
await die(ctx, '問題が発生しました。お手数ですが、最初からやり直してください。');
|
||||
await die(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -58,7 +61,9 @@ router.get('/', async ctx => {
|
|||
});
|
||||
} else {
|
||||
await ctx.render('mypage', {
|
||||
user
|
||||
user,
|
||||
usersCount: await getUserCount(),
|
||||
score: await getScores(user),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -76,7 +81,7 @@ router.get('/login', async ctx => {
|
|||
host = meta.uri.replace(/^https?:\/\//, '');
|
||||
const name = 'みす廃あらーと';
|
||||
const description = 'ついついノートしすぎていませんか?';
|
||||
const permission = [ 'write:notes' ];
|
||||
const permission = [ 'write:notes', 'write:notifications' ];
|
||||
|
||||
if (meta.features.miauth) {
|
||||
// Use MiAuth
|
||||
|
@ -106,6 +111,42 @@ router.get('/login', async ctx => {
|
|||
}
|
||||
});
|
||||
|
||||
router.get('/logout', async ctx => {
|
||||
const token = ctx.cookies.get('token');
|
||||
if (!token) {
|
||||
await die(ctx, 'ログインしていません');
|
||||
return;
|
||||
}
|
||||
ctx.cookies.set('token', '');
|
||||
await ctx.render('welcome', {
|
||||
usersCount: await getUserCount(),
|
||||
welcomeMessage: 'ログアウトしました。',
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/optout', async ctx => {
|
||||
const token = ctx.cookies.get('token');
|
||||
if (!token) {
|
||||
await die(ctx, 'ログインしていません');
|
||||
return;
|
||||
}
|
||||
ctx.cookies.set('token', '');
|
||||
|
||||
const u = await getUserByMisshaiToken(token);
|
||||
|
||||
if (!u) {
|
||||
await die(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
await deleteUser(u.username, u.host);
|
||||
|
||||
await ctx.render('welcome', {
|
||||
usersCount: await getUserCount(),
|
||||
welcomeMessage: '連携を解除しました。',
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/terms', async ctx => {
|
||||
await ctx.render('term');
|
||||
});
|
||||
|
@ -127,7 +168,7 @@ router.get('/miauth', async ctx => {
|
|||
const host = sessionHostCache[session];
|
||||
delete sessionHostCache[session];
|
||||
if (!host) {
|
||||
await die(ctx, '問題が発生しました。お手数ですが、最初からやり直してください。');
|
||||
await die(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -135,7 +176,7 @@ router.get('/miauth', async ctx => {
|
|||
const { token, user } = (await axios.post(url)).data;
|
||||
|
||||
if (!token || !user) {
|
||||
await die(ctx, '問題が発生しました。お手数ですが、最初からやり直してください。');
|
||||
await die(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -152,13 +193,13 @@ router.get('/legacy-auth', async ctx => {
|
|||
const host = sessionHostCache[token];
|
||||
delete sessionHostCache[token];
|
||||
if (!host) {
|
||||
await die(ctx, '問題が発生しました。お手数ですが、最初からやり直してください。');
|
||||
await die(ctx);
|
||||
return;
|
||||
}
|
||||
const appSecret = tokenSecretCache[token];
|
||||
delete tokenSecretCache[token];
|
||||
if (!appSecret) {
|
||||
await die(ctx, '問題が発生しました。お手数ですが、最初からやり直してください。');
|
||||
await die(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -172,6 +213,32 @@ router.get('/legacy-auth', async ctx => {
|
|||
await login(ctx, user, host, i);
|
||||
});
|
||||
|
||||
router.post('/update-settings', async ctx => {
|
||||
const mode = ctx.request.body.alertMode as AlertMode;
|
||||
// 一応型チェック
|
||||
if (!alertModes.includes(mode)) {
|
||||
await die(ctx, `${mode} is an invalid value`);
|
||||
return;
|
||||
}
|
||||
|
||||
const token = ctx.cookies.get('token');
|
||||
if (!token) {
|
||||
await die(ctx, 'ログインしていません');
|
||||
return;
|
||||
}
|
||||
|
||||
const u = await getUserByMisshaiToken(token);
|
||||
|
||||
if (!u) {
|
||||
await die(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
await Users.update(u.id, { alertMode: mode });
|
||||
|
||||
ctx.redirect('/');
|
||||
});
|
||||
|
||||
|
||||
// Return 404 for other pages
|
||||
router.all('(.*)', async ctx => {
|
||||
|
|
|
@ -5,24 +5,38 @@ import { Users } from '../models';
|
|||
import { api } from './misskey';
|
||||
import { format } from '../functions/format';
|
||||
import { deleteUser } from '../functions/users';
|
||||
import { updateScore } from '../functions/update-score';
|
||||
import { getScores } from '../functions/get-scores';
|
||||
|
||||
export default (): void => {
|
||||
cron.schedule('0 0 0 * * *', async () => {
|
||||
cron.schedule('50 45 0 * * *', async () => {
|
||||
const users = await Users.createQueryBuilder()
|
||||
.select()
|
||||
.getMany();
|
||||
for (const user of users) {
|
||||
try {
|
||||
const text = await format(user);
|
||||
await updateScore(user);
|
||||
const text = format(await getScores(user));
|
||||
|
||||
const res = await api<Record<string, unknown>>(user.host, 'notes/create', {
|
||||
text,
|
||||
visibility: 'home'
|
||||
}, user.token);
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
if (user.alertMode === 'note') {
|
||||
const res = await api<Record<string, unknown>>(user.host, 'notes/create', {
|
||||
text,
|
||||
visibility: 'specified'
|
||||
}, user.token);
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
break;
|
||||
} else if (user.alertMode === 'notification') {
|
||||
const res = await api(user.host, 'notifications/create', {
|
||||
header: 'みす廃あらーと',
|
||||
icon: 'https://i.imgur.com/B991yTl.png',
|
||||
body: text,
|
||||
}, user.token);
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
if (e.code === 'NO_SUCH_USER' || e.code === 'AUTHENTICATION_FAILED') {
|
||||
// ユーザーが削除されている場合、レコードからも消してとりやめ
|
||||
|
@ -32,7 +46,8 @@ export default (): void => {
|
|||
console.error(e);
|
||||
}
|
||||
} finally {
|
||||
await delay(3000);
|
||||
if (user.alertMode === 'note')
|
||||
await delay(3000);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
7
src/types/AlertMode.ts
Normal file
7
src/types/AlertMode.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export const alertModes = [
|
||||
'note',
|
||||
'notification',
|
||||
'nothing'
|
||||
] as const;
|
||||
|
||||
export type AlertMode = typeof alertModes[number];
|
9
src/types/Score.ts
Normal file
9
src/types/Score.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
export type Score = {
|
||||
notesCount: number;
|
||||
followingCount: number;
|
||||
followersCount: number;
|
||||
notesDelta: string;
|
||||
followingDelta: string;
|
||||
followersDelta: string;
|
||||
};
|
|
@ -1,3 +1,25 @@
|
|||
mixin exta()
|
||||
a(href=attributes.href target="_blank" rel="noopener noreferrer")
|
||||
block
|
||||
|
||||
mixin serverInfo()
|
||||
.xd-card
|
||||
.header
|
||||
h1.title
|
||||
i.fas.fa-info-circle
|
||||
| サービスの情報
|
||||
.body
|
||||
dl
|
||||
dt
|
||||
i.fas.fa-users
|
||||
| 登録者数
|
||||
dd !{usersCount} 人
|
||||
dt
|
||||
| 総ノート数
|
||||
dd (coming soon)
|
||||
dt
|
||||
| 総フォロー数
|
||||
dd (coming soon)
|
||||
dt
|
||||
| 総フォロワー数
|
||||
dd (coming soon)
|
|
@ -1,6 +1,63 @@
|
|||
extends _base
|
||||
|
||||
block content
|
||||
section
|
||||
h2 マイページ
|
||||
p おかえりなさい、@!{ user.username }@!{ user.host } さん。
|
||||
h2 マイページ
|
||||
p おかえりなさい、@!{ user.username }@!{ user.host } さん。
|
||||
|
||||
section#scores
|
||||
.xd-cards.center
|
||||
+serverInfo()
|
||||
.xd-card
|
||||
.header
|
||||
h1.title
|
||||
i.fas.fa-chart-area
|
||||
| あなたの廃人度は…
|
||||
.body
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th 内容
|
||||
th スコア
|
||||
th 前日比
|
||||
tbody
|
||||
tr
|
||||
td ノート
|
||||
td !{score.notesCount}
|
||||
td !{score.notesDelta}
|
||||
tr
|
||||
td フォロー
|
||||
td !{score.followingCount}
|
||||
td !{score.followingDelta}
|
||||
tr
|
||||
td フォロワー
|
||||
td !{score.followersCount}
|
||||
td !{score.followersDelta}
|
||||
|
||||
section.xd-card#settings
|
||||
.header
|
||||
h1.title
|
||||
i.fas.fa-cog
|
||||
| 設定
|
||||
.body
|
||||
form(method="post", action="/update-settings")
|
||||
p: label スコア通知方法:
|
||||
select(name="alertMode")
|
||||
option(value="note", selected=user.alertMode === 'note') 自動的にノートを投稿 (標準)
|
||||
option(value="notification", selected=user.alertMode === 'notification') Misskey に通知
|
||||
option(value="nothing", selected=user.alertMode === 'nothing') 通知しない
|
||||
p: label タイムゾーン:(coming soon)
|
||||
button.primary(type="submit") 保存
|
||||
.body
|
||||
div.mb-2: a.xd-button.danger#logout(href="/logout") ログアウト
|
||||
div: a.xd-button.danger#optout(href="/optout") アカウント連携を解除する
|
||||
|
||||
block script
|
||||
script.
|
||||
document.getElementById("optout").addEventListener("click", (e) => {
|
||||
if (!confirm('連携を解除すると、統計情報などのデータが削除されてしまい、以後アラート機能をご利用いただけなくなります。この操作は変更できません。\n\nそれでもなお、連携を解除しますか?'))
|
||||
e.preventDefault();
|
||||
});
|
||||
document.getElementById("logout").addEventListener("click", (e) => {
|
||||
if (!confirm('ログアウトしますか?'))
|
||||
e.preventDefault();
|
||||
});
|
|
@ -20,17 +20,7 @@ block content
|
|||
|
||||
section
|
||||
.xd-cards.center
|
||||
.xd-card
|
||||
.header
|
||||
h1.title
|
||||
i.fas.fa-info-circle
|
||||
| 情報
|
||||
.body
|
||||
dl
|
||||
dt
|
||||
i.fas.fa-users
|
||||
| 登録者数
|
||||
dd !{usersCount} 人
|
||||
+serverInfo()
|
||||
.xd-card
|
||||
.header
|
||||
h1.title
|
||||
|
|
|
@ -18,6 +18,9 @@ $card-header: $bg-pale-1;
|
|||
$card-footer: $bg-pale-1;
|
||||
$card-fg: $fg;
|
||||
$divider: rgba($fg, 0.25);
|
||||
$table-bg-header: $md-grey-300;
|
||||
$table-bg-odd: $md-grey-100;
|
||||
$table-bg-even: white;
|
||||
$barSize: 64px;
|
||||
|
||||
* {
|
||||
|
@ -144,6 +147,18 @@ button, .xd-button {
|
|||
background: $primary-dark;
|
||||
}
|
||||
}
|
||||
|
||||
&.danger {
|
||||
background: $md-red-700;
|
||||
color: $md-white;
|
||||
border-color: $md-red-900;
|
||||
&:hover {
|
||||
background: $md-red-500;
|
||||
}
|
||||
&:active {
|
||||
background: $md-red-800;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ul, ol {
|
||||
|
@ -161,6 +176,27 @@ a, .link {
|
|||
}
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid $divider;
|
||||
border-radius: 2px;
|
||||
width: 100%;
|
||||
|
||||
> thead {
|
||||
background: $table-bg-header;
|
||||
margin-bottom: 2px;
|
||||
text-align: left;
|
||||
}
|
||||
> tbody > tr {
|
||||
background: $table-bg-even;
|
||||
&:nth-child(odd) {
|
||||
background: $table-bg-odd;
|
||||
}
|
||||
}
|
||||
th, td {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
border-radius: 2px;
|
||||
color: #0f0;
|
||||
|
@ -233,6 +269,9 @@ code {
|
|||
}
|
||||
@media screen and (max-width: 640px) {
|
||||
flex-wrap: wrap;
|
||||
> .xd-card {
|
||||
margin: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.wrap {
|
||||
|
|
103
yarn.lock
103
yarn.lock
|
@ -158,6 +158,13 @@
|
|||
dependencies:
|
||||
"@types/koa" "*"
|
||||
|
||||
"@types/koa-multer@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/koa-multer/-/koa-multer-1.0.0.tgz#f449f399ac3f80c05753452f000e8694ceec4249"
|
||||
integrity sha512-1Fh/tu7nj6/QefLcTuHUUeeZ5J9zqOQbDfTLPG8sh9ni7eioDJ1jOYuP95k3/uo0ApSiUOZ5c2fEXTqiEnPZ+w==
|
||||
dependencies:
|
||||
"@types/koa" "*"
|
||||
|
||||
"@types/koa-router@^7.4.1":
|
||||
version "7.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/koa-router/-/koa-router-7.4.1.tgz#3702a4cabe4558cc4eec70d5574acc04beecff7c"
|
||||
|
@ -416,6 +423,11 @@ app-root-path@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad"
|
||||
integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==
|
||||
|
||||
append-field@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/append-field/-/append-field-0.1.0.tgz#6ddc58fa083c7bc545d3c5995b2830cc2366d44a"
|
||||
integrity sha1-bdxY+gg8e8VF08WZWygwzCNm1Eo=
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
|
@ -506,6 +518,11 @@ braces@~3.0.2:
|
|||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
buffer-writer@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04"
|
||||
|
@ -519,6 +536,14 @@ buffer@^5.1.0:
|
|||
base64-js "^1.0.2"
|
||||
ieee754 "^1.1.4"
|
||||
|
||||
busboy@^0.2.11:
|
||||
version "0.2.14"
|
||||
resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453"
|
||||
integrity sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=
|
||||
dependencies:
|
||||
dicer "0.2.5"
|
||||
readable-stream "1.1.x"
|
||||
|
||||
bytes@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
|
||||
|
@ -709,6 +734,16 @@ concat-map@0.0.1:
|
|||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
concat-stream@^1.5.0:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
|
||||
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^2.2.2"
|
||||
typedarray "^0.0.6"
|
||||
|
||||
condense-newlines@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/condense-newlines/-/condense-newlines-0.2.1.tgz#3de985553139475d32502c83b02f60684d24c55f"
|
||||
|
@ -919,6 +954,14 @@ destroy@^1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
||||
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
|
||||
|
||||
dicer@0.2.5:
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f"
|
||||
integrity sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=
|
||||
dependencies:
|
||||
readable-stream "1.1.x"
|
||||
streamsearch "0.1.2"
|
||||
|
||||
diff@^3.1.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
||||
|
@ -1506,7 +1549,7 @@ inflight@^1.0.4:
|
|||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@~2.0.1, inherits@~2.0.3:
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
@ -1805,6 +1848,13 @@ koa-mount@^4.0.0:
|
|||
debug "^4.0.1"
|
||||
koa-compose "^4.1.0"
|
||||
|
||||
koa-multer@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/koa-multer/-/koa-multer-1.0.2.tgz#d38f7ffd1db97b1aad33e7774732f000ebd67259"
|
||||
integrity sha512-0kFzN4atVd+9oiG+4fYxQ9S2T3dPhKNvmhITIY606Qn9wLEmfhW0DhSpOzRYhddN//4rh/TCK95TMtflmFa5lA==
|
||||
dependencies:
|
||||
multer "1.3.0"
|
||||
|
||||
koa-router@^9.1.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/koa-router/-/koa-router-9.1.0.tgz#47d1ce2109fd62b1d76eb42df90b635ff93b6831"
|
||||
|
@ -2025,6 +2075,20 @@ ms@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
multer@1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/multer/-/multer-1.3.0.tgz#092b2670f6846fa4914965efc8cf94c20fec6cd2"
|
||||
integrity sha1-CSsmcPaEb6SRSWXvyM+Uwg/sbNI=
|
||||
dependencies:
|
||||
append-field "^0.1.0"
|
||||
busboy "^0.2.11"
|
||||
concat-stream "^1.5.0"
|
||||
mkdirp "^0.5.1"
|
||||
object-assign "^3.0.0"
|
||||
on-finished "^2.3.0"
|
||||
type-is "^1.6.4"
|
||||
xtend "^4.0.0"
|
||||
|
||||
mz@^2.4.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
|
||||
|
@ -2131,6 +2195,11 @@ npm-run-all@^4.1.5:
|
|||
shell-quote "^1.6.1"
|
||||
string.prototype.padend "^3.0.0"
|
||||
|
||||
object-assign@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
|
||||
integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=
|
||||
|
||||
object-assign@^4.0.1, object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
|
@ -2659,17 +2728,17 @@ read-pkg@^3.0.0:
|
|||
normalize-package-data "^2.3.2"
|
||||
path-type "^3.0.0"
|
||||
|
||||
readable-stream@~1.0.31:
|
||||
version "1.0.34"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
|
||||
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
|
||||
readable-stream@1.1.x:
|
||||
version "1.1.14"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
|
||||
integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk=
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.1"
|
||||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readable-stream@~2.3.6:
|
||||
readable-stream@^2.2.2, readable-stream@~2.3.6:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
|
@ -2682,6 +2751,16 @@ readable-stream@~2.3.6:
|
|||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@~1.0.31:
|
||||
version "1.0.34"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
|
||||
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.1"
|
||||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readdirp@~3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada"
|
||||
|
@ -2962,6 +3041,11 @@ sprintf-js@~1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
|
||||
|
||||
streamsearch@0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
|
||||
integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=
|
||||
|
||||
string-width@^3.0.0, string-width@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
|
||||
|
@ -3205,7 +3289,7 @@ type-fest@^0.8.1:
|
|||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
|
||||
|
||||
type-is@^1.6.16:
|
||||
type-is@^1.6.16, type-is@^1.6.4:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
|
||||
|
@ -3220,6 +3304,11 @@ typedarray-to-buffer@^3.1.5:
|
|||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typedarray@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
typeorm@0.2.25:
|
||||
version "0.2.25"
|
||||
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.25.tgz#1a33513b375b78cc7740d2405202208b918d7dde"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue