This commit is contained in:
Xeltica 2020-07-24 01:53:00 +09:00
parent b958350d0f
commit dbd58fc39d
8 changed files with 201 additions and 106 deletions

View File

@ -1,8 +1,8 @@
import Koa from 'koa';
import { router, render } from '.';
import { router } from './router';
import config from './config';
import session from 'koa-session';
import bodyParser from 'koa-bodyparser';
import { render } from './render';
const app = new Koa();
@ -12,7 +12,6 @@ console.log('Simpkey v' + config.version);
app.use(bodyParser());
app.use(render);
app.use(router.routes());
app.use(router.allowedMethods());
console.log('App launched!');

6
src/die.ts Normal file
View File

@ -0,0 +1,6 @@
import { Context } from 'koa';
export const die = (ctx: Context, error: string): Promise<void> => {
ctx.status = 400;
return ctx.render('error', { error });
};

View File

@ -1,100 +0,0 @@
import { Context, DefaultState } from 'koa';
import views from 'koa-views';
import Router from 'koa-router';
import config from './config';
import { signIn, api, i } from './misskey';
import { Note } from './models/Note';
import { User } from './models/User';
export const die = (ctx: Context, error: string): Promise<void> => {
ctx.status = 400;
return ctx.render('error', { error });
};
export const render = views(__dirname + '/views', {
extension: 'pug', options: {
...config,
getAcct: (user: User) => user.host ? `@${user.username}@${user.host}` : `@${user.username}`,
getUserName: (user: User) => user.name || user.username,
}
});
export const router = new Router<DefaultState, Context>();
const staticRouting = [
[ 'about', 'Simpkey について' ],
[ 'terms', '利用規約' ],
[ 'privacy-policy', 'プライバシーポリシー' ],
];
for (const [ name, title ] of staticRouting) {
router.get('/' + name, async (ctx, next) => {
await ctx.render(name, { title });
await next();
});
}
async function timeline(ctx: Context, host: string, endpoint: string, timelineName: string, token: string) {
const user = await i(host, token);
const timeline = await api<Note[]>(host, endpoint, { i: token });
await ctx.render('timeline', {
title: timelineName + ' - Simpkey',
user, timeline, timelineName
});
}
router.get('/ltl', async (ctx, next) => {
const token = ctx.cookies.get('i');
const host = ctx.cookies.get('host');
if (!token || !host) {
await die(ctx, 'ログインしてください');
} else {
const meta = await api<any>(host, 'meta', { i: token });
if (meta.disableLocalTimeline) {
await die(ctx, 'ローカルタイムラインは無効化されています');
} else {
await timeline(ctx, host, 'notes/local-timeline', 'ローカルタイムライン', token);
}
}
await next();
});
router.get('/', async (ctx, next) => {
const token = ctx.cookies.get('i');
const host = ctx.cookies.get('host');
if (!token || !host) {
console.log('no session so show top page');
await ctx.render('index', {
title: 'Simpkey'
});
} else {
console.log('show timeline with the session');
await timeline(ctx, host, 'notes/timeline', 'ホームタイムライン', token);
}
await next();
});
router.post('/', async (ctx) => {
const {
host,
username,
password,
token
} = ctx.request.body;
if (!host || !username || !password) {
await die(ctx, 'パラメータが足りません');
return;
}
try {
const { id, i } = await signIn(host, username, password, token);
ctx.cookies.set('id', id);
ctx.cookies.set('host', host);
ctx.cookies.set('i', i);
console.log('login as ' + username);
ctx.redirect('/');
} catch (err) {
await die(ctx, err.message);
console.error(err);
}
});

34
src/render.ts Normal file
View File

@ -0,0 +1,34 @@
import config from './config';
import views from 'koa-views';
import { User } from './models/User';
import { Note } from './models/Note';
export const render = views(__dirname + '/views', {
extension: 'pug', options: {
...config,
getAcct: (user: User) => user.host ? `@${user.username}@${user.host}` : `@${user.username}`,
getUserName: (user: User) => user.name || user.username,
getVisibility: (note: Note) => {
let icon: string;
switch (note.visibility) {
case 'public':
icon = '🌐';
break;
case 'home':
icon = '🏠';
break;
case 'followers':
icon = '🔒';
break;
case 'specified':
icon = '✉️';
break;
default:
icon = '❓';
break;
}
if (note.localOnly) icon += '☣';
return icon;
},
}
});

146
src/router.ts Normal file
View File

@ -0,0 +1,146 @@
import { Context, DefaultState } from 'koa';
import Router from 'koa-router';
import { signIn, api, i } from './misskey';
import { Note } from './models/Note';
import { die } from './die';
export const router = new Router<DefaultState, Context>();
const staticRouting = [
[ 'about', 'Simpkey について' ],
[ 'terms', '利用規約' ],
[ 'privacy-policy', 'プライバシーポリシー' ],
[ 'settings', '設定' ],
];
for (const [ name, title ] of staticRouting) {
router.get('/' + name, async ctx => {
await ctx.render(name, { title });
});
}
async function timeline(ctx: Context, host: string, endpoint: string, timelineName: string, token: string) {
const user = await i(host, token);
const timeline = await api<Note[]>(host, endpoint, { i: token });
await ctx.render('timeline', {
title: timelineName + ' - Simpkey',
user, timeline, timelineName
});
}
router.get('/', async ctx => {
const token = ctx.cookies.get('i');
const host = ctx.cookies.get('host');
if (!token || !host) {
console.log('no session so show top page');
await ctx.render('index', {
title: 'Simpkey'
});
return;
}
await timeline(ctx, host, 'notes/timeline', 'ホームタイムライン', token);
});
router.get('/ltl', async ctx => {
const token = ctx.cookies.get('i');
const host = ctx.cookies.get('host');
if (!token || !host) {
await die(ctx, 'ログインしてください');
return;
}
const meta = await api<any>(host, 'meta', { i: token });
if (meta.disableLocalTimeline) {
await die(ctx, 'ローカルタイムラインは無効化されています');
} else {
await timeline(ctx, host, 'notes/local-timeline', 'ローカルタイムライン', token);
}
});
router.get('/stl', async ctx => {
const token = ctx.cookies.get('i');
const host = ctx.cookies.get('host');
if (!token || !host) {
await die(ctx, 'ログインしてください');
return;
}
const meta = await api<any>(host, 'meta', { i: token });
if (meta.disableLocalTimeline) {
await die(ctx, 'ソーシャルタイムラインは無効化されています');
} else {
await timeline(ctx, host, 'notes/hybrid-timeline', 'ソーシャルタイムライン', token);
}
});
router.get('/gtl', async ctx => {
const token = ctx.cookies.get('i');
const host = ctx.cookies.get('host');
if (!token || !host) {
await die(ctx, 'ログインしてください');
return;
}
const meta = await api<any>(host, 'meta', { i: token });
if (meta.disableGlobalTimeline) {
await die(ctx, 'グローバルタイムラインは無効化されています');
} else {
await timeline(ctx, host, 'notes/global-timeline', 'グローバルタイムライン', token);
}
});
router.post('/', async ctx => {
const {
host,
username,
password,
token
} = ctx.request.body;
if (!host || !username || !password) {
await die(ctx, 'パラメータが足りません');
return;
}
try {
const { id, i } = await signIn(host, username, password, token);
ctx.cookies.set('id', id);
ctx.cookies.set('host', host);
ctx.cookies.set('i', i);
console.log('login as ' + username);
ctx.redirect('/');
} catch (err) {
await die(ctx, err.message);
console.error(err);
}
});
router.post('/action/:action', async ctx => {
const i = ctx.cookies.get('i');
const host = ctx.cookies.get('host');
if (!i || !host) {
await die(ctx, 'ログインしてください');
return;
}
const action = ctx.params.action as string;
switch (action) {
case 'create-note': {
const { text } = ctx.request.body;
if (!text) await die(ctx, 'テキストがありません');
await api(host, 'notes/create', { text, i });
break;
}
}
ctx.redirect('back', '/');
});
router.post('/logout', ctx => {
ctx.cookies.set('id');
ctx.cookies.set('host');
ctx.cookies.set('i');
ctx.redirect('/');
});
// Return 404 for other pages
router.all('(.*)', async ctx => {
ctx.status = 404;
await die(ctx, 'ページが見つかりませんでした');
});

View File

@ -8,8 +8,9 @@ mixin note(note)
| &nbsp;
span(style="color: gray")= getAcct(note.user)
p= note.text
aside= new Date(note.createdAt).toLocaleString()
aside= note.visibility
aside
!=getVisibility(note)
!=new Date(note.createdAt).toLocaleString()
html
head

7
src/views/settings.pug Normal file
View File

@ -0,0 +1,7 @@
extends _base
block content
h2 設定
h3 ユーザー
form(action="/logout", method="post")
button(type="submit") ログアウト

View File

@ -5,7 +5,7 @@ block content
p: b=getUserName(user)
| &nbsp;
span(style="color: gray")= getAcct(user)
form(action="/note", method="post")
form(action="/action/create-note", method="post")
textarea(name="text", placeholder="今何してる?" style="max-width: 100%; min-width: 100%; height: 6em; margin-bottom: 8px")
button(type="submit") ノート
hr
@ -17,6 +17,8 @@ block content
a(href="/stl") ソーシャル
| ・
a(href="/gtl") グローバル
| ・
a(href="/settings") 設定
h2= timelineName
each note in timeline
if (note.renote)