0
0
Fork 0

リファクタとデザイン調整とresolve #1

This commit is contained in:
Xeltica 2020-09-09 00:48:02 +09:00
parent 41f40dd2c2
commit 219b15d9e6
17 changed files with 404 additions and 64 deletions

View 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"');
}
}

View file

@ -21,6 +21,7 @@
"dependencies": { "dependencies": {
"@types/koa-bodyparser": "^4.3.0", "@types/koa-bodyparser": "^4.3.0",
"@types/koa-mount": "^4.0.0", "@types/koa-mount": "^4.0.0",
"@types/koa-multer": "^1.0.0",
"@types/koa-static": "^4.0.1", "@types/koa-static": "^4.0.1",
"@types/node-cron": "^2.0.3", "@types/node-cron": "^2.0.3",
"@types/uuid": "^8.0.0", "@types/uuid": "^8.0.0",
@ -29,6 +30,7 @@
"koa": "^2.13.0", "koa": "^2.13.0",
"koa-bodyparser": "^4.3.0", "koa-bodyparser": "^4.3.0",
"koa-mount": "^4.0.0", "koa-mount": "^4.0.0",
"koa-multer": "^1.0.2",
"koa-router": "^9.1.0", "koa-router": "^9.1.0",
"koa-session": "^6.0.0", "koa-session": "^6.0.0",
"koa-static": "^5.0.0", "koa-static": "^5.0.0",

View file

@ -2,32 +2,15 @@ import { api } from '../services/misskey';
import { config } from '../config'; import { config } from '../config';
import { User } from '../models/entities/user'; import { User } from '../models/entities/user';
import { updateUser } from './users'; import { updateUser } from './users';
import { Score } from '../types/Score';
export const format = async (user: User): Promise<string> => { export const format = (score: Score): string => `昨日のMisskeyの活動は
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);
await updateUser(user.username, user.host, { ノート: ${score.notesCount}(${score.notesDelta})
prevNotesCount: miUser.notesCount, フォロー : ${score.followingCount}(${score.followingDelta})
prevFollowingCount: miUser.followingCount, フォロワー :${score.followersCount}(${score.followersDelta})
prevFollowersCount: miUser.followersCount,
});
return `昨日のMisskeyの活動は
ノート: ${miUser.notesCount}(${notesDelta})
フォロー : ${miUser.followingCount}(${followingDelta})
フォロワー :${miUser.followersCount}(${followersDelta})
${config.url} ${config.url}
#misshaialert`; #misshaialert`;
};
export const toSignedString = (num: number): string => num < 0 ? num.toString() : '+' + num;

View 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),
};
};

View file

@ -0,0 +1 @@
export const toSignedString = (num: number): string => num < 0 ? num.toString() : '+' + num;

View 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,
});
};

View file

@ -1,4 +1,5 @@
import { Entity, Column, PrimaryGeneratedColumn, Index } from 'typeorm'; import { Entity, Column, PrimaryGeneratedColumn, Index } from 'typeorm';
import { AlertMode, alertModes } from '../../types/AlertMode';
@Entity() @Entity()
@Index([ 'username', 'host' ], { unique: true }) @Index([ 'username', 'host' ], { unique: true })
@ -44,4 +45,11 @@ export class User {
default: 0, default: 0,
}) })
public prevFollowersCount: number; public prevFollowersCount: number;
@Column({
type: 'enum',
enum: alertModes,
default: 'note'
})
public alertMode: AlertMode;
} }

View file

@ -1,6 +1,6 @@
import { Context } from 'koa'; 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; ctx.status = status;
return ctx.render('error', { error }); return ctx.render('error', { error });
}; };

View file

@ -6,8 +6,11 @@ import crypto from 'crypto';
import { die } from './die'; import { die } from './die';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { config } from '../config'; 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 { 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>(); 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); const u = await getUser(user.username as string, host);
if (!u) { if (!u) {
await die(ctx, '問題が発生しました。お手数ですが、最初からやり直してください。'); await die(ctx);
return; return;
} }
@ -58,7 +61,9 @@ router.get('/', async ctx => {
}); });
} else { } else {
await ctx.render('mypage', { 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?:\/\//, ''); host = meta.uri.replace(/^https?:\/\//, '');
const name = 'みす廃あらーと'; const name = 'みす廃あらーと';
const description = 'ついついノートしすぎていませんか?'; const description = 'ついついノートしすぎていませんか?';
const permission = [ 'write:notes' ]; const permission = [ 'write:notes', 'write:notifications' ];
if (meta.features.miauth) { if (meta.features.miauth) {
// Use 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 => { router.get('/terms', async ctx => {
await ctx.render('term'); await ctx.render('term');
}); });
@ -127,7 +168,7 @@ router.get('/miauth', async ctx => {
const host = sessionHostCache[session]; const host = sessionHostCache[session];
delete sessionHostCache[session]; delete sessionHostCache[session];
if (!host) { if (!host) {
await die(ctx, '問題が発生しました。お手数ですが、最初からやり直してください。'); await die(ctx);
return; return;
} }
@ -135,7 +176,7 @@ router.get('/miauth', async ctx => {
const { token, user } = (await axios.post(url)).data; const { token, user } = (await axios.post(url)).data;
if (!token || !user) { if (!token || !user) {
await die(ctx, '問題が発生しました。お手数ですが、最初からやり直してください。'); await die(ctx);
return; return;
} }
@ -152,13 +193,13 @@ router.get('/legacy-auth', async ctx => {
const host = sessionHostCache[token]; const host = sessionHostCache[token];
delete sessionHostCache[token]; delete sessionHostCache[token];
if (!host) { if (!host) {
await die(ctx, '問題が発生しました。お手数ですが、最初からやり直してください。'); await die(ctx);
return; return;
} }
const appSecret = tokenSecretCache[token]; const appSecret = tokenSecretCache[token];
delete tokenSecretCache[token]; delete tokenSecretCache[token];
if (!appSecret) { if (!appSecret) {
await die(ctx, '問題が発生しました。お手数ですが、最初からやり直してください。'); await die(ctx);
return; return;
} }
@ -172,6 +213,32 @@ router.get('/legacy-auth', async ctx => {
await login(ctx, user, host, i); 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 // Return 404 for other pages
router.all('(.*)', async ctx => { router.all('(.*)', async ctx => {

View file

@ -5,24 +5,38 @@ import { Users } from '../models';
import { api } from './misskey'; import { api } from './misskey';
import { format } from '../functions/format'; import { format } from '../functions/format';
import { deleteUser } from '../functions/users'; import { deleteUser } from '../functions/users';
import { updateScore } from '../functions/update-score';
import { getScores } from '../functions/get-scores';
export default (): void => { export default (): void => {
cron.schedule('0 0 0 * * *', async () => { cron.schedule('50 45 0 * * *', async () => {
const users = await Users.createQueryBuilder() const users = await Users.createQueryBuilder()
.select() .select()
.getMany(); .getMany();
for (const user of users) { for (const user of users) {
try { try {
const text = await format(user); await updateScore(user);
const text = format(await getScores(user));
if (user.alertMode === 'note') {
const res = await api<Record<string, unknown>>(user.host, 'notes/create', { const res = await api<Record<string, unknown>>(user.host, 'notes/create', {
text, text,
visibility: 'home' visibility: 'specified'
}, user.token); }, user.token);
if (res.error) { if (res.error) {
throw 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) { } catch (e) {
if (e.code === 'NO_SUCH_USER' || e.code === 'AUTHENTICATION_FAILED') { if (e.code === 'NO_SUCH_USER' || e.code === 'AUTHENTICATION_FAILED') {
// ユーザーが削除されている場合、レコードからも消してとりやめ // ユーザーが削除されている場合、レコードからも消してとりやめ
@ -32,6 +46,7 @@ export default (): void => {
console.error(e); console.error(e);
} }
} finally { } finally {
if (user.alertMode === 'note')
await delay(3000); await delay(3000);
} }
} }

7
src/types/AlertMode.ts Normal file
View 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
View file

@ -0,0 +1,9 @@
export type Score = {
notesCount: number;
followingCount: number;
followersCount: number;
notesDelta: string;
followingDelta: string;
followersDelta: string;
};

View file

@ -1,3 +1,25 @@
mixin exta() mixin exta()
a(href=attributes.href target="_blank" rel="noopener noreferrer") a(href=attributes.href target="_blank" rel="noopener noreferrer")
block 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)

View file

@ -1,6 +1,63 @@
extends _base extends _base
block content block content
section
h2 マイページ h2 マイページ
p おかえりなさい、@!{ user.username }@!{ user.host } さん。 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();
});

View file

@ -20,17 +20,7 @@ block content
section section
.xd-cards.center .xd-cards.center
.xd-card +serverInfo()
.header
h1.title
i.fas.fa-info-circle
| 情報
.body
dl
dt
i.fas.fa-users
| 登録者数
dd !{usersCount} 人
.xd-card .xd-card
.header .header
h1.title h1.title

View file

@ -18,6 +18,9 @@ $card-header: $bg-pale-1;
$card-footer: $bg-pale-1; $card-footer: $bg-pale-1;
$card-fg: $fg; $card-fg: $fg;
$divider: rgba($fg, 0.25); $divider: rgba($fg, 0.25);
$table-bg-header: $md-grey-300;
$table-bg-odd: $md-grey-100;
$table-bg-even: white;
$barSize: 64px; $barSize: 64px;
* { * {
@ -144,6 +147,18 @@ button, .xd-button {
background: $primary-dark; 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 { // 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 { code {
border-radius: 2px; border-radius: 2px;
color: #0f0; color: #0f0;
@ -233,6 +269,9 @@ code {
} }
@media screen and (max-width: 640px) { @media screen and (max-width: 640px) {
flex-wrap: wrap; flex-wrap: wrap;
> .xd-card {
margin: 8px 0;
}
} }
&.wrap { &.wrap {

103
yarn.lock
View file

@ -158,6 +158,13 @@
dependencies: dependencies:
"@types/koa" "*" "@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": "@types/koa-router@^7.4.1":
version "7.4.1" version "7.4.1"
resolved "https://registry.yarnpkg.com/@types/koa-router/-/koa-router-7.4.1.tgz#3702a4cabe4558cc4eec70d5574acc04beecff7c" 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" resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad"
integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== 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: argparse@^1.0.7:
version "1.0.10" version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@ -506,6 +518,11 @@ braces@~3.0.2:
dependencies: dependencies:
fill-range "^7.0.1" 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: buffer-writer@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" 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" base64-js "^1.0.2"
ieee754 "^1.1.4" 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: bytes@3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" 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" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 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: condense-newlines@^0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/condense-newlines/-/condense-newlines-0.2.1.tgz#3de985553139475d32502c83b02f60684d24c55f" 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" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= 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: diff@^3.1.0:
version "3.5.0" version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
@ -1506,7 +1549,7 @@ inflight@^1.0.4:
once "^1.3.0" once "^1.3.0"
wrappy "1" 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" version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -1805,6 +1848,13 @@ koa-mount@^4.0.0:
debug "^4.0.1" debug "^4.0.1"
koa-compose "^4.1.0" 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: koa-router@^9.1.0:
version "9.1.0" version "9.1.0"
resolved "https://registry.yarnpkg.com/koa-router/-/koa-router-9.1.0.tgz#47d1ce2109fd62b1d76eb42df90b635ff93b6831" 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" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 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: mz@^2.4.0:
version "2.7.0" version "2.7.0"
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" 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" shell-quote "^1.6.1"
string.prototype.padend "^3.0.0" 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: object-assign@^4.0.1, object-assign@^4.1.1:
version "4.1.1" version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 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" normalize-package-data "^2.3.2"
path-type "^3.0.0" path-type "^3.0.0"
readable-stream@~1.0.31: readable-stream@1.1.x:
version "1.0.34" version "1.1.14"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk=
dependencies: dependencies:
core-util-is "~1.0.0" core-util-is "~1.0.0"
inherits "~2.0.1" inherits "~2.0.1"
isarray "0.0.1" isarray "0.0.1"
string_decoder "~0.10.x" string_decoder "~0.10.x"
readable-stream@~2.3.6: readable-stream@^2.2.2, readable-stream@~2.3.6:
version "2.3.7" version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@ -2682,6 +2751,16 @@ readable-stream@~2.3.6:
string_decoder "~1.1.1" string_decoder "~1.1.1"
util-deprecate "~1.0.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: readdirp@~3.4.0:
version "3.4.0" version "3.4.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" 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" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= 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: string-width@^3.0.0, string-width@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" 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" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
type-is@^1.6.16: type-is@^1.6.16, type-is@^1.6.4:
version "1.6.18" version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
@ -3220,6 +3304,11 @@ typedarray-to-buffer@^3.1.5:
dependencies: dependencies:
is-typedarray "^1.0.0" 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: typeorm@0.2.25:
version "0.2.25" version "0.2.25"
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.25.tgz#1a33513b375b78cc7740d2405202208b918d7dde" resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.25.tgz#1a33513b375b78cc7740d2405202208b918d7dde"