diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2021-11-12 02:02:25 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2021-11-12 02:02:25 +0900 |
| commit | 0e4a111f81cceed275d9bec2695f6e401fb654d8 (patch) | |
| tree | 40874799472fa07416f17b50a398ac33b7771905 /src/server/web | |
| parent | update deps (diff) | |
| download | sharkey-0e4a111f81cceed275d9bec2695f6e401fb654d8.tar.gz sharkey-0e4a111f81cceed275d9bec2695f6e401fb654d8.tar.bz2 sharkey-0e4a111f81cceed275d9bec2695f6e401fb654d8.zip | |
refactoring
Resolve #7779
Diffstat (limited to 'src/server/web')
| -rw-r--r-- | src/server/web/bios.css | 40 | ||||
| -rw-r--r-- | src/server/web/bios.js | 87 | ||||
| -rw-r--r-- | src/server/web/boot.js | 166 | ||||
| -rw-r--r-- | src/server/web/cli.css | 19 | ||||
| -rw-r--r-- | src/server/web/cli.js | 55 | ||||
| -rw-r--r-- | src/server/web/feed.ts | 58 | ||||
| -rw-r--r-- | src/server/web/index.ts | 419 | ||||
| -rw-r--r-- | src/server/web/manifest.json | 28 | ||||
| -rw-r--r-- | src/server/web/manifest.ts | 15 | ||||
| -rw-r--r-- | src/server/web/style.css | 29 | ||||
| -rw-r--r-- | src/server/web/url-preview.ts | 53 | ||||
| -rw-r--r-- | src/server/web/views/base.pug | 60 | ||||
| -rw-r--r-- | src/server/web/views/bios.pug | 20 | ||||
| -rw-r--r-- | src/server/web/views/channel.pug | 21 | ||||
| -rw-r--r-- | src/server/web/views/cli.pug | 21 | ||||
| -rw-r--r-- | src/server/web/views/clip.pug | 33 | ||||
| -rw-r--r-- | src/server/web/views/flush.pug | 47 | ||||
| -rw-r--r-- | src/server/web/views/gallery-post.pug | 35 | ||||
| -rw-r--r-- | src/server/web/views/info-card.pug | 50 | ||||
| -rw-r--r-- | src/server/web/views/note.pug | 43 | ||||
| -rw-r--r-- | src/server/web/views/page.pug | 33 | ||||
| -rw-r--r-- | src/server/web/views/user.pug | 42 |
22 files changed, 0 insertions, 1374 deletions
diff --git a/src/server/web/bios.css b/src/server/web/bios.css deleted file mode 100644 index b0da3ee39b..0000000000 --- a/src/server/web/bios.css +++ /dev/null @@ -1,40 +0,0 @@ -* { - font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace; -} - -html { - background: #ffb4e1; -} - -main { - background: #dedede; -} -main > .tabs { - padding: 16px; - border-bottom: solid 4px #c3c3c3; -} - -#lsEditor > .adder { - margin: 16px; - padding: 16px; - border: solid 2px #c3c3c3; -} -#lsEditor > .adder > textarea { - display: block; - width: 100%; - min-height: 5em; - box-sizing: border-box; -} -#lsEditor > .record { - padding: 16px; - border-bottom: solid 1px #c3c3c3; -} -#lsEditor > .record > header { - font-weight: bold; -} -#lsEditor > .record > textarea { - display: block; - width: 100%; - min-height: 5em; - box-sizing: border-box; -} diff --git a/src/server/web/bios.js b/src/server/web/bios.js deleted file mode 100644 index d06dee801a..0000000000 --- a/src/server/web/bios.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -window.onload = async () => { - const account = JSON.parse(localStorage.getItem('account')); - const i = account.token; - - const api = (endpoint, data = {}) => { - const promise = new Promise((resolve, reject) => { - // Append a credential - if (i) data.i = i; - - // Send request - fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, { - method: 'POST', - body: JSON.stringify(data), - credentials: 'omit', - cache: 'no-cache' - }).then(async (res) => { - const body = res.status === 204 ? null : await res.json(); - - if (res.status === 200) { - resolve(body); - } else if (res.status === 204) { - resolve(); - } else { - reject(body.error); - } - }).catch(reject); - }); - - return promise; - }; - - const content = document.getElementById('content'); - - document.getElementById('ls').addEventListener('click', () => { - content.innerHTML = ''; - - const lsEditor = document.createElement('div'); - lsEditor.id = 'lsEditor'; - - const adder = document.createElement('div'); - adder.classList.add('adder'); - const addKeyInput = document.createElement('input'); - const addValueTextarea = document.createElement('textarea'); - const addButton = document.createElement('button'); - addButton.textContent = 'add'; - addButton.addEventListener('click', () => { - localStorage.setItem(addKeyInput.value, addValueTextarea.value); - location.reload(); - }); - - adder.appendChild(addKeyInput); - adder.appendChild(addValueTextarea); - adder.appendChild(addButton); - lsEditor.appendChild(adder); - - for (let i = 0; i < localStorage.length; i++) { - const k = localStorage.key(i); - const record = document.createElement('div'); - record.classList.add('record'); - const header = document.createElement('header'); - header.textContent = k; - const textarea = document.createElement('textarea'); - textarea.textContent = localStorage.getItem(k); - const saveButton = document.createElement('button'); - saveButton.textContent = 'save'; - saveButton.addEventListener('click', () => { - localStorage.setItem(k, textarea.value); - location.reload(); - }); - const removeButton = document.createElement('button'); - removeButton.textContent = 'remove'; - removeButton.addEventListener('click', () => { - localStorage.removeItem(k); - location.reload(); - }); - record.appendChild(header); - record.appendChild(textarea); - record.appendChild(saveButton); - record.appendChild(removeButton); - lsEditor.appendChild(record); - } - - content.appendChild(lsEditor); - }); -}; diff --git a/src/server/web/boot.js b/src/server/web/boot.js deleted file mode 100644 index d4a2529e63..0000000000 --- a/src/server/web/boot.js +++ /dev/null @@ -1,166 +0,0 @@ -/** - * BOOT LOADER - * サーバーからレスポンスされるHTMLに埋め込まれるスクリプトで、以下の役割を持ちます。 - * - 翻訳ファイルをフェッチする。 - * - バージョンに基づいて適切なメインスクリプトを読み込む。 - * - キャッシュされたコンパイル済みテーマを適用する。 - * - クライアントの設定値に基づいて対応するHTMLクラス等を設定する。 - * テーマをこの段階で設定するのは、メインスクリプトが読み込まれる間もテーマを適用したいためです。 - * 注: webpackは介さないため、このファイルではrequireやimportは使えません。 - */ - -'use strict'; - -// ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので -(async () => { - window.onerror = (e) => { - renderError('SOMETHING_HAPPENED', e.toString()); - }; - window.onunhandledrejection = (e) => { - renderError('SOMETHING_HAPPENED_IN_PROMISE', e.toString()); - }; - - const v = localStorage.getItem('v') || VERSION; - - //#region Detect language & fetch translations - const localeVersion = localStorage.getItem('localeVersion'); - const localeOutdated = (localeVersion == null || localeVersion !== v); - - if (!localStorage.hasOwnProperty('locale') || localeOutdated) { - const supportedLangs = LANGS; - let lang = localStorage.getItem('lang'); - if (lang == null || !supportedLangs.includes(lang)) { - if (supportedLangs.includes(navigator.language)) { - lang = navigator.language; - } else { - lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); - - // Fallback - if (lang == null) lang = 'en-US'; - } - } - - const res = await fetch(`/assets/locales/${lang}.${v}.json`); - if (res.status === 200) { - localStorage.setItem('lang', lang); - localStorage.setItem('locale', await res.text()); - localStorage.setItem('localeVersion', v); - } else if (localeOutdated) { - // nop - } else { - await checkUpdate(); - renderError('LOCALE_FETCH_FAILED'); - return; - } - } - //#endregion - - //#region Script - const salt = localStorage.getItem('salt') - ? `?salt=${localStorage.getItem('salt')}` - : ''; - - const script = document.createElement('script'); - script.setAttribute('src', `/assets/app.${v}.js${salt}`); - script.setAttribute('async', 'true'); - script.setAttribute('defer', 'true'); - script.addEventListener('error', async () => { - await checkUpdate(); - renderError('APP_FETCH_FAILED'); - }); - document.head.appendChild(script); - //#endregion - - //#region Theme - const theme = localStorage.getItem('theme'); - if (theme) { - for (const [k, v] of Object.entries(JSON.parse(theme))) { - document.documentElement.style.setProperty(`--${k}`, v.toString()); - - // HTMLの theme-color 適用 - if (k === 'htmlThemeColor') { - for (const tag of document.head.children) { - if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') { - tag.setAttribute('content', v); - break; - } - } - } - } - } - //#endregion - - const fontSize = localStorage.getItem('fontSize'); - if (fontSize) { - document.documentElement.classList.add('f-' + fontSize); - } - - const useSystemFont = localStorage.getItem('useSystemFont'); - if (useSystemFont) { - document.documentElement.classList.add('useSystemFont'); - } - - const wallpaper = localStorage.getItem('wallpaper'); - if (wallpaper) { - document.documentElement.style.backgroundImage = `url(${wallpaper})`; - } - - const customCss = localStorage.getItem('customCss'); - if (customCss && customCss.length > 0) { - const style = document.createElement('style'); - style.innerHTML = customCss; - document.head.appendChild(style); - } - - // eslint-disable-next-line no-inner-declarations - function renderError(code, details) { - document.documentElement.innerHTML = ` - <h1>⚠エラーが発生しました</h1> - <p>問題が解決しない場合は管理者までお問い合わせください。以下のオプションを試すこともできます:</p> - <ul> - <li><a href="/cli">簡易クライアント</a>を起動</li> - <li><a href="/bios">BIOS</a>で修復を試みる</li> - <li><a href="/flush">キャッシュをクリア</a>する</li> - </ul> - <hr> - <code>ERROR CODE: ${code}</code> - <details> - ${details} - </details> - `; - } - - // eslint-disable-next-line no-inner-declarations - async function checkUpdate() { - // TODO: サーバーが落ちている場合などのエラーハンドリング - const res = await fetch('/api/meta', { - method: 'POST', - cache: 'no-cache' - }); - - const meta = await res.json(); - - if (meta.version != v) { - localStorage.setItem('v', meta.version); - refresh(); - } - } - - // eslint-disable-next-line no-inner-declarations - function refresh() { - // Random - localStorage.setItem('salt', Math.random().toString().substr(2, 8)); - - // Clear cache (service worker) - try { - navigator.serviceWorker.controller.postMessage('clear'); - navigator.serviceWorker.getRegistrations().then(registrations => { - registrations.forEach(registration => registration.unregister()); - }); - } catch (e) { - console.error(e); - } - - location.reload(); - } -})(); diff --git a/src/server/web/cli.css b/src/server/web/cli.css deleted file mode 100644 index 07cd27830b..0000000000 --- a/src/server/web/cli.css +++ /dev/null @@ -1,19 +0,0 @@ -* { - font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace; -} - -html { - background: #ffb4e1; -} - -main { - background: #dedede; -} - -#tl > div { - padding: 16px; - border-bottom: solid 1px #c3c3c3; -} -#tl > div > header { - font-weight: bold; -} diff --git a/src/server/web/cli.js b/src/server/web/cli.js deleted file mode 100644 index 3dff1d4860..0000000000 --- a/src/server/web/cli.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict'; - -window.onload = async () => { - const account = JSON.parse(localStorage.getItem('account')); - const i = account.token; - - const api = (endpoint, data = {}) => { - const promise = new Promise((resolve, reject) => { - // Append a credential - if (i) data.i = i; - - // Send request - fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, { - method: 'POST', - body: JSON.stringify(data), - credentials: 'omit', - cache: 'no-cache' - }).then(async (res) => { - const body = res.status === 204 ? null : await res.json(); - - if (res.status === 200) { - resolve(body); - } else if (res.status === 204) { - resolve(); - } else { - reject(body.error); - } - }).catch(reject); - }); - - return promise; - }; - - document.getElementById('submit').addEventListener('click', () => { - api('notes/create', { - text: document.getElementById('text').value - }).then(() => { - location.reload(); - }); - }); - - api('notes/timeline').then(notes => { - const tl = document.getElementById('tl'); - for (const note of notes) { - const el = document.createElement('div'); - const name = document.createElement('header'); - name.textContent = `${note.user.name} @${note.user.username}`; - const text = document.createElement('div'); - text.textContent = `${note.text}`; - el.appendChild(name); - el.appendChild(text); - tl.appendChild(el); - } - }); -}; diff --git a/src/server/web/feed.ts b/src/server/web/feed.ts deleted file mode 100644 index 1d4c47dafb..0000000000 --- a/src/server/web/feed.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Feed } from 'feed'; -import config from '@/config/index'; -import { User } from '@/models/entities/user'; -import { Notes, DriveFiles, UserProfiles } from '@/models/index'; -import { In } from 'typeorm'; - -export default async function(user: User) { - const author = { - link: `${config.url}/@${user.username}`, - name: user.name || user.username - }; - - const profile = await UserProfiles.findOneOrFail(user.id); - - const notes = await Notes.find({ - where: { - userId: user.id, - renoteId: null, - visibility: In(['public', 'home']) - }, - order: { createdAt: -1 }, - take: 20 - }); - - const feed = new Feed({ - id: author.link, - title: `${author.name} (@${user.username}@${config.host})`, - updated: notes[0].createdAt, - generator: 'Misskey', - description: `${user.notesCount} Notes, ${profile.ffVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.ffVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`, - link: author.link, - image: user.avatarUrl ? user.avatarUrl : undefined, - feedLinks: { - json: `${author.link}.json`, - atom: `${author.link}.atom`, - }, - author, - copyright: user.name || user.username - }); - - for (const note of notes) { - const files = note.fileIds.length > 0 ? await DriveFiles.find({ - id: In(note.fileIds) - }) : []; - const file = files.find(file => file.type.startsWith('image/')); - - feed.addItem({ - title: `New note by ${author.name}`, - link: `${config.url}/notes/${note.id}`, - date: note.createdAt, - description: note.cw || undefined, - content: note.text || undefined, - image: file ? DriveFiles.getPublicUrl(file) || undefined : undefined - }); - } - - return feed; -} diff --git a/src/server/web/index.ts b/src/server/web/index.ts deleted file mode 100644 index 70d5b696ff..0000000000 --- a/src/server/web/index.ts +++ /dev/null @@ -1,419 +0,0 @@ -/** - * Web Client Server - */ - -import * as os from 'os'; -import * as fs from 'fs'; -import { dirname } from 'path'; -import * as ms from 'ms'; -import * as Koa from 'koa'; -import * as Router from '@koa/router'; -import * as send from 'koa-send'; -import * as favicon from 'koa-favicon'; -import * as views from 'koa-views'; - -import packFeed from './feed'; -import { fetchMeta } from '@/misc/fetch-meta'; -import { genOpenapiSpec } from '../api/openapi/gen-spec'; -import config from '@/config/index'; -import { Users, Notes, Emojis, UserProfiles, Pages, Channels, Clips, GalleryPosts } from '@/models/index'; -import { parseAcct } from '@/misc/acct'; -import { getNoteSummary } from '@/misc/get-note-summary'; -import { getConnection } from 'typeorm'; -import { redisClient } from '../../db/redis'; -import * as locales from '../../../locales/index'; - -//const _filename = fileURLToPath(import.meta.url); -const _filename = __filename; -const _dirname = dirname(_filename); - -const staticAssets = `${_dirname}/../../../assets/`; -const assets = `${_dirname}/../../assets/`; - -// Init app -const app = new Koa(); - -// Init renderer -app.use(views(_dirname + '/views', { - extension: 'pug', - options: { - version: config.version, - config - } -})); - -// Serve favicon -app.use(favicon(`${_dirname}/../../../assets/favicon.ico`)); - -// Common request handler -app.use(async (ctx, next) => { - // IFrameの中に入れられないようにする - ctx.set('X-Frame-Options', 'DENY'); - await next(); -}); - -// Init router -const router = new Router(); - -//#region static assets - -router.get('/static-assets/(.*)', async ctx => { - await send(ctx as any, ctx.path.replace('/static-assets/', ''), { - root: staticAssets, - maxage: ms('7 days'), - }); -}); - -router.get('/assets/(.*)', async ctx => { - await send(ctx as any, ctx.path.replace('/assets/', ''), { - root: assets, - maxage: ms('7 days'), - }); -}); - -// Apple touch icon -router.get('/apple-touch-icon.png', async ctx => { - await send(ctx as any, '/apple-touch-icon.png', { - root: staticAssets - }); -}); - -router.get('/twemoji/(.*)', async ctx => { - const path = ctx.path.replace('/twemoji/', ''); - - if (!path.match(/^[0-9a-f-]+\.svg$/)) { - ctx.status = 404; - return; - } - - ctx.set('Content-Security-Policy', `default-src 'none'; style-src 'unsafe-inline'`); - - await send(ctx as any, path, { - root: `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/`, - maxage: ms('30 days'), - }); -}); - -// ServiceWorker -router.get('/sw.js', async ctx => { - await send(ctx as any, `/sw.${config.version}.js`, { - root: assets - }); -}); - -// Manifest -router.get('/manifest.json', require('./manifest')); - -router.get('/robots.txt', async ctx => { - await send(ctx as any, '/robots.txt', { - root: staticAssets - }); -}); - -//#endregion - -// Docs -router.get('/api-doc', async ctx => { - await send(ctx as any, '/redoc.html', { - root: staticAssets - }); -}); - -// URL preview endpoint -router.get('/url', require('./url-preview')); - -router.get('/api.json', async ctx => { - ctx.body = genOpenapiSpec(); -}); - -const getFeed = async (acct: string) => { - const { username, host } = parseAcct(acct); - const user = await Users.findOne({ - usernameLower: username.toLowerCase(), - host, - isSuspended: false - }); - - return user && await packFeed(user); -}; - -// Atom -router.get('/@:user.atom', async ctx => { - const feed = await getFeed(ctx.params.user); - - if (feed) { - ctx.set('Content-Type', 'application/atom+xml; charset=utf-8'); - ctx.body = feed.atom1(); - } else { - ctx.status = 404; - } -}); - -// RSS -router.get('/@:user.rss', async ctx => { - const feed = await getFeed(ctx.params.user); - - if (feed) { - ctx.set('Content-Type', 'application/rss+xml; charset=utf-8'); - ctx.body = feed.rss2(); - } else { - ctx.status = 404; - } -}); - -// JSON -router.get('/@:user.json', async ctx => { - const feed = await getFeed(ctx.params.user); - - if (feed) { - ctx.set('Content-Type', 'application/json; charset=utf-8'); - ctx.body = feed.json1(); - } else { - ctx.status = 404; - } -}); - -//#region SSR (for crawlers) -// User -router.get(['/@:user', '/@:user/:sub'], async (ctx, next) => { - const { username, host } = parseAcct(ctx.params.user); - const user = await Users.findOne({ - usernameLower: username.toLowerCase(), - host, - isSuspended: false - }); - - if (user != null) { - const profile = await UserProfiles.findOneOrFail(user.id); - const meta = await fetchMeta(); - const me = profile.fields - ? profile.fields - .filter(filed => filed.value != null && filed.value.match(/^https?:/)) - .map(field => field.value) - : []; - - await ctx.render('user', { - user, profile, me, - sub: ctx.params.sub, - instanceName: meta.name || 'Misskey', - icon: meta.iconUrl - }); - ctx.set('Cache-Control', 'public, max-age=30'); - } else { - // リモートユーザーなので - // モデレータがAPI経由で参照可能にするために404にはしない - await next(); - } -}); - -router.get('/users/:user', async ctx => { - const user = await Users.findOne({ - id: ctx.params.user, - host: null, - isSuspended: false - }); - - if (user == null) { - ctx.status = 404; - return; - } - - ctx.redirect(`/@${user.username}${ user.host == null ? '' : '@' + user.host}`); -}); - -// Note -router.get('/notes/:note', async (ctx, next) => { - const note = await Notes.findOne(ctx.params.note); - - if (note) { - const _note = await Notes.pack(note); - const profile = await UserProfiles.findOneOrFail(note.userId); - const meta = await fetchMeta(); - await ctx.render('note', { - note: _note, - profile, - // TODO: Let locale changeable by instance setting - summary: getNoteSummary(_note, locales['ja-JP']), - instanceName: meta.name || 'Misskey', - icon: meta.iconUrl - }); - - if (['public', 'home'].includes(note.visibility)) { - ctx.set('Cache-Control', 'public, max-age=180'); - } else { - ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); - } - - return; - } - - await next(); -}); - -// Page -router.get('/@:user/pages/:page', async (ctx, next) => { - const { username, host } = parseAcct(ctx.params.user); - const user = await Users.findOne({ - usernameLower: username.toLowerCase(), - host - }); - - if (user == null) return; - - const page = await Pages.findOne({ - name: ctx.params.page, - userId: user.id - }); - - if (page) { - const _page = await Pages.pack(page); - const profile = await UserProfiles.findOneOrFail(page.userId); - const meta = await fetchMeta(); - await ctx.render('page', { - page: _page, - profile, - instanceName: meta.name || 'Misskey' - }); - - if (['public'].includes(page.visibility)) { - ctx.set('Cache-Control', 'public, max-age=180'); - } else { - ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); - } - - return; - } - - await next(); -}); - -// Clip -// TODO: 非publicなclipのハンドリング -router.get('/clips/:clip', async (ctx, next) => { - const clip = await Clips.findOne({ - id: ctx.params.clip, - }); - - if (clip) { - const _clip = await Clips.pack(clip); - const profile = await UserProfiles.findOneOrFail(clip.userId); - const meta = await fetchMeta(); - await ctx.render('clip', { - clip: _clip, - profile, - instanceName: meta.name || 'Misskey' - }); - - ctx.set('Cache-Control', 'public, max-age=180'); - - return; - } - - await next(); -}); - -// Gallery post -router.get('/gallery/:post', async (ctx, next) => { - const post = await GalleryPosts.findOne(ctx.params.post); - - if (post) { - const _post = await GalleryPosts.pack(post); - const profile = await UserProfiles.findOneOrFail(post.userId); - const meta = await fetchMeta(); - await ctx.render('gallery-post', { - post: _post, - profile, - instanceName: meta.name || 'Misskey', - icon: meta.iconUrl - }); - - ctx.set('Cache-Control', 'public, max-age=180'); - - return; - } - - await next(); -}); - -// Channel -router.get('/channels/:channel', async (ctx, next) => { - const channel = await Channels.findOne({ - id: ctx.params.channel, - }); - - if (channel) { - const _channel = await Channels.pack(channel); - const meta = await fetchMeta(); - await ctx.render('channel', { - channel: _channel, - instanceName: meta.name || 'Misskey' - }); - - ctx.set('Cache-Control', 'public, max-age=180'); - - return; - } - - await next(); -}); -//#endregion - -router.get('/_info_card_', async ctx => { - const meta = await fetchMeta(true); - - ctx.remove('X-Frame-Options'); - - await ctx.render('info-card', { - version: config.version, - host: config.host, - meta: meta, - originalUsersCount: await Users.count({ host: null }), - originalNotesCount: await Notes.count({ userHost: null }) - }); -}); - -router.get('/bios', async ctx => { - await ctx.render('bios', { - version: config.version, - }); -}); - -router.get('/cli', async ctx => { - await ctx.render('cli', { - version: config.version, - }); -}); - -const override = (source: string, target: string, depth: number = 0) => - [, ...target.split('/').filter(x => x), ...source.split('/').filter(x => x).splice(depth)].join('/'); - -router.get('/othello', async ctx => ctx.redirect(override(ctx.URL.pathname, 'games/reversi', 1))); -router.get('/reversi', async ctx => ctx.redirect(override(ctx.URL.pathname, 'games'))); - -router.get('/flush', async ctx => { - await ctx.render('flush'); -}); - -// streamingに非WebSocketリクエストが来た場合にbase htmlをキャシュ付きで返すと、Proxy等でそのパスがキャッシュされておかしくなる -router.get('/streaming', async ctx => { - ctx.status = 503; - ctx.set('Cache-Control', 'private, max-age=0'); -}); - -// Render base html for all requests -router.get('(.*)', async ctx => { - const meta = await fetchMeta(); - await ctx.render('base', { - img: meta.bannerUrl, - title: meta.name || 'Misskey', - instanceName: meta.name || 'Misskey', - desc: meta.description, - icon: meta.iconUrl - }); - ctx.set('Cache-Control', 'public, max-age=300'); -}); - -// Register router -app.use(router.routes()); - -module.exports = app; diff --git a/src/server/web/manifest.json b/src/server/web/manifest.json deleted file mode 100644 index 48030a2980..0000000000 --- a/src/server/web/manifest.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "short_name": "Misskey", - "name": "Misskey", - "start_url": "/", - "display": "standalone", - "background_color": "#313a42", - "theme_color": "#86b300", - "icons": [ - { - "src": "/static-assets/icons/192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/static-assets/icons/512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "share_target": { - "action": "/share/", - "params": { - "title": "title", - "text": "text", - "url": "url" - } - } -} diff --git a/src/server/web/manifest.ts b/src/server/web/manifest.ts deleted file mode 100644 index 918fe27c03..0000000000 --- a/src/server/web/manifest.ts +++ /dev/null @@ -1,15 +0,0 @@ -import * as Koa from 'koa'; -import * as manifest from './manifest.json'; -import { fetchMeta } from '@/misc/fetch-meta'; - -module.exports = async (ctx: Koa.Context) => { - const json = JSON.parse(JSON.stringify(manifest)); - - const instance = await fetchMeta(true); - - json.short_name = instance.name || 'Misskey'; - json.name = instance.name || 'Misskey'; - - ctx.set('Cache-Control', 'max-age=300'); - ctx.body = json; -}; diff --git a/src/server/web/style.css b/src/server/web/style.css deleted file mode 100644 index 43fbe1ab06..0000000000 --- a/src/server/web/style.css +++ /dev/null @@ -1,29 +0,0 @@ -html { - background-color: var(--bg); - color: var(--fg); -} - -#splash { - position: fixed; - z-index: 10000; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - cursor: wait; - background-color: var(--bg); - opacity: 1; - transition: opacity 0.5s ease; -} - -#splash > img { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - margin: auto; - width: 64px; - height: 64px; - pointer-events: none; -} diff --git a/src/server/web/url-preview.ts b/src/server/web/url-preview.ts deleted file mode 100644 index 1375420c0a..0000000000 --- a/src/server/web/url-preview.ts +++ /dev/null @@ -1,53 +0,0 @@ -import * as Koa from 'koa'; -import summaly from 'summaly'; -import { fetchMeta } from '@/misc/fetch-meta'; -import Logger from '@/services/logger'; -import config from '@/config/index'; -import { query } from '@/prelude/url'; -import { getJson } from '@/misc/fetch'; - -const logger = new Logger('url-preview'); - -module.exports = async (ctx: Koa.Context) => { - const meta = await fetchMeta(); - - logger.info(meta.summalyProxy - ? `(Proxy) Getting preview of ${ctx.query.url}@${ctx.query.lang} ...` - : `Getting preview of ${ctx.query.url}@${ctx.query.lang} ...`); - - try { - const summary = meta.summalyProxy ? await getJson(`${meta.summalyProxy}?${query({ - url: ctx.query.url, - lang: ctx.query.lang || 'ja-JP' - })}`) : await summaly(ctx.query.url, { - followRedirects: false, - lang: ctx.query.lang || 'ja-JP' - }); - - logger.succ(`Got preview of ${ctx.query.url}: ${summary.title}`); - - summary.icon = wrap(summary.icon); - summary.thumbnail = wrap(summary.thumbnail); - - // Cache 7days - ctx.set('Cache-Control', 'max-age=604800, immutable'); - - ctx.body = summary; - } catch (e) { - logger.warn(`Failed to get preview of ${ctx.query.url}: ${e}`); - ctx.status = 200; - ctx.set('Cache-Control', 'max-age=86400, immutable'); - ctx.body = '{}'; - } -}; - -function wrap(url?: string): string | null { - return url != null - ? url.match(/^https?:\/\//) - ? `${config.url}/proxy/preview.jpg?${query({ - url, - preview: '1' - })}` - : url - : null; -} diff --git a/src/server/web/views/base.pug b/src/server/web/views/base.pug deleted file mode 100644 index 42c068c403..0000000000 --- a/src/server/web/views/base.pug +++ /dev/null @@ -1,60 +0,0 @@ -block vars - -doctype html - -!= '<!--\n' -!= ' _____ _ _ \n' -!= ' | |_|___ ___| |_ ___ _ _ \n' -!= ' | | | | |_ -|_ -| \'_| -_| | |\n' -!= ' |_|_|_|_|___|___|_,_|___|_ |\n' -!= ' |___|\n' -!= ' Thank you for using Misskey!\n' -!= ' If you are reading this message... how about joining the development?\n' -!= ' https://github.com/misskey-dev/misskey' -!= '\n-->\n' - -html - - head - meta(charset='utf-8') - meta(name='application-name' content='Misskey') - meta(name='referrer' content='origin') - meta(name='theme-color' content='#86b300') - meta(name='theme-color-orig' content='#86b300') - meta(property='og:site_name' content= instanceName || 'Misskey') - meta(name='viewport' content='width=device-width, initial-scale=1') - link(rel='icon' href= icon || '/favicon.ico') - link(rel='apple-touch-icon' href= icon || '/apple-touch-icon.png') - link(rel='manifest' href='/manifest.json') - link(rel='prefetch' href='https://xn--931a.moe/assets/info.jpg') - link(rel='prefetch' href='https://xn--931a.moe/assets/not-found.jpg') - link(rel='prefetch' href='https://xn--931a.moe/assets/error.jpg') - link(rel='preload' href='https://use.fontawesome.com/releases/v5.15.3/css/all.css' as='style') - link(rel='stylesheet' href='https://use.fontawesome.com/releases/v5.15.3/css/all.css') - - title - block title - = title || 'Misskey' - - block desc - meta(name='description' content= desc || '✨🌎✨ A interplanetary communication platform ✨🚀✨') - - block meta - - block og - meta(property='og:image' content=img) - - style - include ../style.css - - script - include ../boot.js - - body - noscript: p - | JavaScriptを有効にしてください - br - | Please turn on your JavaScript - div#splash - img(src='/favicon.ico') - block content diff --git a/src/server/web/views/bios.pug b/src/server/web/views/bios.pug deleted file mode 100644 index d81a3ee67f..0000000000 --- a/src/server/web/views/bios.pug +++ /dev/null @@ -1,20 +0,0 @@ -doctype html - -html - - head - meta(charset='utf-8') - meta(name='application-name' content='Misskey') - title Misskey BIOS - style - include ../bios.css - script - include ../bios.js - - body - header - h1 Misskey BIOS #{version} - main - div.tabs - button#ls edit local storage - div#content diff --git a/src/server/web/views/channel.pug b/src/server/web/views/channel.pug deleted file mode 100644 index 273632f0e0..0000000000 --- a/src/server/web/views/channel.pug +++ /dev/null @@ -1,21 +0,0 @@ -extends ./base - -block vars - - const title = channel.name; - - const url = `${config.url}/channels/${channel.id}`; - -block title - = `${title} | ${instanceName}` - -block desc - meta(name='description' content= channel.description) - -block og - meta(property='og:type' content='article') - meta(property='og:title' content= title) - meta(property='og:description' content= channel.description) - meta(property='og:url' content= url) - meta(property='og:image' content= channel.bannerUrl) - -block meta - meta(name='twitter:card' content='summary') diff --git a/src/server/web/views/cli.pug b/src/server/web/views/cli.pug deleted file mode 100644 index d2cf7c4335..0000000000 --- a/src/server/web/views/cli.pug +++ /dev/null @@ -1,21 +0,0 @@ -doctype html - -html - - head - meta(charset='utf-8') - meta(name='application-name' content='Misskey') - title Misskey Cli - style - include ../cli.css - script - include ../cli.js - - body - header - h1 Misskey Cli #{version} - main - div#form - textarea#text - button#submit submit - div#tl diff --git a/src/server/web/views/clip.pug b/src/server/web/views/clip.pug deleted file mode 100644 index 8de53f19d6..0000000000 --- a/src/server/web/views/clip.pug +++ /dev/null @@ -1,33 +0,0 @@ -extends ./base - -block vars - - const user = clip.user; - - const title = clip.name; - - const url = `${config.url}/clips/${clip.id}`; - -block title - = `${title} | ${instanceName}` - -block desc - meta(name='description' content= clip.description) - -block og - meta(property='og:type' content='article') - meta(property='og:title' content= title) - meta(property='og:description' content= clip.description) - meta(property='og:url' content= url) - meta(property='og:image' content= user.avatarUrl) - -block meta - if profile.noCrawle - meta(name='robots' content='noindex') - - meta(name='misskey:user-username' content=user.username) - meta(name='misskey:user-id' content=user.id) - meta(name='misskey:clip-id' content=clip.id) - - meta(name='twitter:card' content='summary') - - // todo - if user.twitter - meta(name='twitter:creator' content=`@${user.twitter.screenName}`) diff --git a/src/server/web/views/flush.pug b/src/server/web/views/flush.pug deleted file mode 100644 index ec585a34db..0000000000 --- a/src/server/web/views/flush.pug +++ /dev/null @@ -1,47 +0,0 @@ -doctype html - -html - #msg - script. - const msg = document.getElementById('msg'); - const successText = `\nSuccess Flush! <a href="/">Back to Misskey</a>\n成功しました。<a href="/">Misskeyを開き直してください。</a>`; - - message('Start flushing.'); - - (async function() { - try { - localStorage.clear(); - message('localStorage cleared.'); - - const idbPromises = ['MisskeyClient', 'keyval-store'].map((name, i, arr) => new Promise((res, rej) => { - const delidb = indexedDB.deleteDatabase(name); - delidb.onsuccess = () => res(message(`indexedDB "${name}" cleared. (${i + 1}/${arr.length})`)); - delidb.onerror = e => rej(e) - })); - - await Promise.all(idbPromises); - - if (navigator.serviceWorker.controller) { - navigator.serviceWorker.controller.postMessage('clear'); - await navigator.serviceWorker.getRegistrations() - .then(registrations => { - return Promise.all(registrations.map(registration => registration.unregister())); - }) - .catch(e => { throw Error(e) }); - } - - message(successText); - } catch (e) { - message(`\n${e}\n\nFlush Failed. <a href="/flush">Please retry.</a>\n失敗しました。<a href="/flush">もう一度試してみてください。</a>`); - message(`\nIf you retry more than 3 times, clear the browser cache or contact to instance admin.\n3回以上試しても失敗する場合、ブラウザのキャッシュを消去し、それでもだめならインスタンス管理者に連絡してみてください。\n`) - - console.error(e); - setTimeout(() => { - location = '/'; - }, 10000) - } - })(); - - function message(text) { - msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/\n/g,'<br>')}</p>`) - } diff --git a/src/server/web/views/gallery-post.pug b/src/server/web/views/gallery-post.pug deleted file mode 100644 index 95bbb2437c..0000000000 --- a/src/server/web/views/gallery-post.pug +++ /dev/null @@ -1,35 +0,0 @@ -extends ./base - -block vars - - const user = post.user; - - const title = post.title; - - const url = `${config.url}/gallery/${post.id}`; - -block title - = `${title} | ${instanceName}` - -block desc - meta(name='description' content= post.description) - -block og - meta(property='og:type' content='article') - meta(property='og:title' content= title) - meta(property='og:description' content= post.description) - meta(property='og:url' content= url) - meta(property='og:image' content= post.files[0].thumbnailUrl) - -block meta - if user.host || profile.noCrawle - meta(name='robots' content='noindex') - - meta(name='misskey:user-username' content=user.username) - meta(name='misskey:user-id' content=user.id) - - meta(name='twitter:card' content='summary') - - // todo - if user.twitter - meta(name='twitter:creator' content=`@${user.twitter.screenName}`) - - if !user.host - link(rel='alternate' href=url type='application/activity+json') diff --git a/src/server/web/views/info-card.pug b/src/server/web/views/info-card.pug deleted file mode 100644 index 1d62778ce1..0000000000 --- a/src/server/web/views/info-card.pug +++ /dev/null @@ -1,50 +0,0 @@ -doctype html - -html - - head - meta(charset='utf-8') - meta(name='application-name' content='Misskey') - title= meta.name || host - style. - html, body { - margin: 0; - padding: 0; - min-height: 100vh; - background: #fff; - } - - #a { - display: block; - } - - #banner { - background-size: cover; - background-position: center center; - } - - #title { - display: inline-block; - margin: 24px; - padding: 0.5em 0.8em; - color: #fff; - background: rgba(0, 0, 0, 0.5); - font-weight: bold; - font-size: 1.3em; - } - - #content { - overflow: auto; - color: #353c3e; - } - - #description { - margin: 24px; - } - - body - a#a(href=`https://${host}` target="_blank") - header#banner(style=`background-image: url(${meta.bannerUrl})`) - div#title= meta.name || host - div#content - div#description= meta.description diff --git a/src/server/web/views/note.pug b/src/server/web/views/note.pug deleted file mode 100644 index 7030936975..0000000000 --- a/src/server/web/views/note.pug +++ /dev/null @@ -1,43 +0,0 @@ -extends ./base - -block vars - - const user = note.user; - - const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`; - - const url = `${config.url}/notes/${note.id}`; - -block title - = `${title} | ${instanceName}` - -block desc - meta(name='description' content= summary) - -block og - meta(property='og:type' content='article') - meta(property='og:title' content= title) - meta(property='og:description' content= summary) - meta(property='og:url' content= url) - meta(property='og:image' content= user.avatarUrl) - -block meta - if user.host || profile.noCrawle - meta(name='robots' content='noindex') - - meta(name='misskey:user-username' content=user.username) - meta(name='misskey:user-id' content=user.id) - meta(name='misskey:note-id' content=note.id) - - meta(name='twitter:card' content='summary') - - // todo - if user.twitter - meta(name='twitter:creator' content=`@${user.twitter.screenName}`) - - if note.prev - link(rel='prev' href=`${config.url}/notes/${note.prev}`) - if note.next - link(rel='next' href=`${config.url}/notes/${note.next}`) - - if !user.host - link(rel='alternate' href=url type='application/activity+json') - if note.uri - link(rel='alternate' href=note.uri type='application/activity+json') diff --git a/src/server/web/views/page.pug b/src/server/web/views/page.pug deleted file mode 100644 index cb9e1039e1..0000000000 --- a/src/server/web/views/page.pug +++ /dev/null @@ -1,33 +0,0 @@ -extends ./base - -block vars - - const user = page.user; - - const title = page.title; - - const url = `${config.url}/@${user.username}/${page.name}`; - -block title - = `${title} | ${instanceName}` - -block desc - meta(name='description' content= page.summary) - -block og - meta(property='og:type' content='article') - meta(property='og:title' content= title) - meta(property='og:description' content= page.summary) - meta(property='og:url' content= url) - meta(property='og:image' content= page.eyeCatchingImage ? page.eyeCatchingImage.thumbnailUrl : user.avatarUrl) - -block meta - if profile.noCrawle - meta(name='robots' content='noindex') - - meta(name='misskey:user-username' content=user.username) - meta(name='misskey:user-id' content=user.id) - meta(name='misskey:page-id' content=page.id) - - meta(name='twitter:card' content='summary') - - // todo - if user.twitter - meta(name='twitter:creator' content=`@${user.twitter.screenName}`) diff --git a/src/server/web/views/user.pug b/src/server/web/views/user.pug deleted file mode 100644 index 1a8a6b4413..0000000000 --- a/src/server/web/views/user.pug +++ /dev/null @@ -1,42 +0,0 @@ -extends ./base - -block vars - - const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`; - - const url = `${config.url}/@${(user.host ? `${user.username}@${user.host}` : user.username)}`; - - const img = user.avatarUrl || null; - -block title - = `${title} | ${instanceName}` - -block desc - meta(name='description' content= profile.description) - -block og - meta(property='og:type' content='blog') - meta(property='og:title' content= title) - meta(property='og:description' content= profile.description) - meta(property='og:url' content= url) - meta(property='og:image' content= img) - -block meta - if user.host || profile.noCrawle - meta(name='robots' content='noindex') - - meta(name='misskey:user-username' content=user.username) - meta(name='misskey:user-id' content=user.id) - - meta(name='twitter:card' content='summary') - - if profile.twitter - meta(name='twitter:creator' content=`@${profile.twitter.screenName}`) - - if !sub - if !user.host - link(rel='alternate' href=`${config.url}/users/${user.id}` type='application/activity+json') - if user.uri - link(rel='alternate' href=user.uri type='application/activity+json') - if profile.url - link(rel='alternate' href=profile.url type='text/html') - - each m in me - link(rel='me' href=`${m}`) |