0
0
Fork 0

インスタンスURLのバリデーション

This commit is contained in:
Xeltica 2022-06-21 22:23:53 +09:00
parent 9d7f78856d
commit 9cc696f87a
6 changed files with 43 additions and 11 deletions

View file

@ -29,7 +29,17 @@ router.get('/login', async ctx => {
return;
}
const meta = await api<{ name: string, uri: string, version: string, features: Record<string, boolean | undefined> }>(host, 'meta', {});
// http://, https://を潰す
host = host.trim().replace(/^https?:\/\//g, '').replace(/\/+/g, '');
const meta = await api<{ name: string, uri: string, version: string, features: Record<string, boolean | undefined> }>(host, 'meta', {}).catch(async e => {
if (!(e instanceof Error && e.name === 'Error')) throw e;
await die(ctx, 'hostNotFound');
});
// NOTE: catchが呼ばれた場合はvoidとなるためundefinedのはず
if (typeof meta === 'undefined') return;
if (typeof meta !== 'object') {
await die(ctx, 'other');
return;
@ -40,9 +50,6 @@ router.get('/login', async ctx => {
return;
}
// ホスト名の正規化
host = meta.uri.replace(/^https?:\/\//, '');
const { name, permission, description } = misskeyAppInfo;
if (meta.features.miauth) {

View file

@ -5,6 +5,8 @@ export const errorCodes = [
'tokenRequired',
'invalidParamater',
'notAuthorized',
'hostNotFound',
'invalidHostFormat',
'other',
] as const;

View file

@ -1,5 +1,5 @@
import React, { useEffect } from 'react';
import { BrowserRouter, useLocation } from 'react-router-dom';
import React, { useEffect, useState } from 'react';
import { BrowserRouter, Link, useLocation } from 'react-router-dom';
import { Provider, useDispatch } from 'react-redux';
import { useTranslation } from 'react-i18next';
@ -22,7 +22,18 @@ const AppInner : React.VFC = () => {
const {t} = useTranslation();
const error = (window as any).__misshaialert?.error;
const [error, setError] = useState<any>((window as any).__misshaialert?.error);
// ページ遷移がまだされていないかどうか
const [isFirstView, setFirstView] = useState(true);
useEffect(() => {
if (isFirstView) {
setFirstView(false);
} else if (!isFirstView && error) {
setError(null);
}
}, [$location]);
useEffect(() => {
const qMobile = window.matchMedia(`(max-width: ${BREAKPOINT_SM})`);
@ -47,6 +58,7 @@ const AppInner : React.VFC = () => {
{t('_error.additionalInfo')}
{t(`_error.${error}`)}
</p>
<Link to="/" className="btn primary">{t('retry')}</Link>
</div>
) : <Router />}
<footer className="text-center pa-5">

View file

@ -11,6 +11,10 @@ export const LoginForm: React.VFC = () => {
const [host, setHost] = useState('');
const {t} = useTranslation();
const login = () => {
location.href = `//${location.host}/login?host=${encodeURIComponent(host)}`;
};
return (
<nav>
<div>
@ -21,14 +25,18 @@ export const LoginForm: React.VFC = () => {
className="input-field"
type="text"
value={host}
placeholder={t('instanceUrlPlaceholder')}
onChange={(e) => setHost(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') login();
}}
required
/>
<button
className={!host ? 'btn' : 'btn primary'}
style={{ width: 128 }}
disabled={!host}
onClick={() => location.href = `//${location.host}/login?host=${encodeURIComponent(host)}`}
onClick={login}
>
{t('login')}
</button>

View file

@ -49,6 +49,7 @@
"update": "更新する",
"shareMisskeyTools": "#MisskeyTools をシェアする",
"shareMisskeyToolsNote": "#MisskeyTools はいいぞ\n\nhttps://misskey.tools",
"instanceUrlPlaceholder": "例misskey.io",
"_sidebar": {
"dashboard": "ダッシュボード",
"tools": "ツール",
@ -158,7 +159,8 @@
"tokenRequired": "トークンがありません。",
"invalidParameter": "パラメータが不正です。",
"notAuthorized": "権限がありません。",
"other": "なし"
"hostNotFound": "インスタンスに接続できませんでした。ホスト名が正しく入力されているか、接続先のインスタンスが正常に動作しているかご確認ください。",
"other": "不明なエラーです。"
},
"_sendTest": {
"title": "アラートをテスト送信しますか?",

View file

@ -14,6 +14,7 @@ import { Skeleton } from '../../components/Skeleton';
import './misshai.scss';
import { Ranking } from '../../components/Ranking';
import { useTitle } from '../../hooks/useTitle';
import { Link } from 'react-router-dom';
const variables = [
'notesCount',
@ -236,8 +237,8 @@ export const MisshaiPage: React.VFC = () => {
<div className="card misshaiRanking">
<div className="body">
<h1><i className="bi bi-bar-chart"></i> {t('_missHai.ranking')}</h1>
<Ranking limit={limit} />
{limit && <button className="btn primary" onClick={() => setLimit(undefined)}>{t('_missHai.showAll')}</button>}
<Ranking limit={10} />
<Link to="/apps/miss-hai/ranking" className="btn primary" onClick={() => setLimit(undefined)}>{t('_missHai.showAll')}</Link>
<label className="input-check mt-2">
<input type="checkbox" checked={draft.useRanking} onChange={(e) => {
updateSetting({ useRanking: e.target.checked });