mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 15:04:52 +09:00
Clean up the configuration for compiling TypeScript for the Web interface to us its own tsconfig.web.json config files and reference the TypeScript source files directly in the configuration and imports instead of files compiled to dist. To properly support recompliation with TypeScript's projects feature each package's tsconfig.web.json file is set to output the build to dist/web_build which is then ignored in .npmignore. Webpack is still used to create the proper build in dist/web, so this is mainly to make TypeScipt's compiler happy but it also allows the root tsconfig.json file to include the web interface code, meaning running the build script now also checks the typing of the web interface. Final part of #491.
161 lines
4.3 KiB
TypeScript
161 lines
4.3 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
|
|
import { BaseWebPlugin, notifyErrorHandler } from "@clusterio/web_ui";
|
|
import { Button, Form, Input, Spin, Typography } from "antd";
|
|
|
|
import "./index.css";
|
|
|
|
const { Paragraph, Text } = Typography;
|
|
|
|
interface LoginFormProps {
|
|
setToken(token: string): void,
|
|
}
|
|
|
|
function LoginForm(props: LoginFormProps) {
|
|
let [servers, setServers] = useState<string[] | null>(null);
|
|
let [playerCode, setPlayerCode] = useState<string | null>(null);
|
|
let [playerCodeError, setPlayerCodeError] = useState<string | null>(null);
|
|
let [verifyCode, setVerifyCode] = useState<string | undefined>();
|
|
let [verifyToken, setVerifyToken] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
let response = await fetch(`${webRoot}api/player_auth/servers`);
|
|
if (response.ok) {
|
|
setServers(await response.json());
|
|
} else {
|
|
setServers([]);
|
|
}
|
|
})();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (verifyToken) {
|
|
let checkLoop = setInterval(() => {
|
|
(async () => {
|
|
let response = await fetch(`${webRoot}api/player_auth/verify`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
verify_token: verifyToken,
|
|
verify_code: verifyCode,
|
|
player_code: playerCode,
|
|
}),
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`Bad server response: ${response.status}`);
|
|
}
|
|
let json = await response.json();
|
|
if (json.error) {
|
|
// While there are other possibilites the most likely
|
|
// cause of an error response here is that the code expired.
|
|
setPlayerCodeError("Code expired");
|
|
setVerifyToken(null);
|
|
clearInterval(checkLoop);
|
|
return;
|
|
}
|
|
if (json.verified) {
|
|
props.setToken(json.token);
|
|
}
|
|
})().catch(err => {
|
|
clearInterval(checkLoop);
|
|
notifyErrorHandler("Error verifying code")(err);
|
|
});
|
|
}, 2000);
|
|
return () => { clearInterval(checkLoop); };
|
|
}
|
|
|
|
return undefined;
|
|
}, [verifyToken]);
|
|
|
|
if (!servers) {
|
|
return <Spin size="large" />;
|
|
}
|
|
|
|
if (servers.length === 0) {
|
|
return <Paragraph>
|
|
There are no servers in the cluster currently running that can complete
|
|
the login via Factorio. Please try again later.
|
|
</Paragraph>;
|
|
}
|
|
|
|
return <>
|
|
<Paragraph>Login using your Factorio account is a 3 step process:</Paragraph>
|
|
<style>
|
|
</style>
|
|
<ol className="factorio-login-steps">
|
|
<li>
|
|
<Paragraph>
|
|
Start Factorio, join one of the following multiplayer servers
|
|
and type <Text keyboard>/web-login</Text> into the chat:
|
|
</Paragraph>
|
|
<ul>
|
|
{servers.map(text => <li key={text}>{text}</li>)}
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<Paragraph>Enter the code displayed in the in-game dialog here:</Paragraph>
|
|
<Paragraph>
|
|
<Form
|
|
layout="inline"
|
|
onFinish={values => {
|
|
(async () => {
|
|
let response = await fetch(`${webRoot}api/player_auth/player_code`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
player_code: values.player_code,
|
|
}),
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`Bad server response: ${response.status}`);
|
|
}
|
|
|
|
let json = await response.json();
|
|
if (json.error) {
|
|
setPlayerCodeError("Invalid Code");
|
|
setVerifyToken(null);
|
|
return;
|
|
}
|
|
|
|
setPlayerCodeError(null);
|
|
setPlayerCode(values.player_code);
|
|
setVerifyCode(json.verify_code);
|
|
setVerifyToken(json.verify_token);
|
|
})().catch(notifyErrorHandler("Error verifying code"));
|
|
}}
|
|
>
|
|
<Form.Item
|
|
name="player_code"
|
|
rules={[{ required: true, message: "Field is required" }]}
|
|
help={playerCodeError}
|
|
validateStatus={playerCodeError ? "error" : undefined}
|
|
>
|
|
<Input style={{ width: "10em" }} />
|
|
</Form.Item>
|
|
<Button type="primary" htmlType="submit">Verify</Button>
|
|
</Form>
|
|
</Paragraph>
|
|
</li>
|
|
<li>
|
|
<Paragraph>Enter this code into the in-game dialog:</Paragraph>
|
|
<Paragraph>
|
|
<Input disabled={verifyCode === undefined} style={{ width: "10em" }} value={verifyCode} />
|
|
</Paragraph>
|
|
</li>
|
|
</ol>
|
|
</>;
|
|
}
|
|
|
|
|
|
export class WebPlugin extends BaseWebPlugin {
|
|
async init() {
|
|
this.logger.info("Player Auth init");
|
|
this.loginForms = [{
|
|
name: "player_auth.factorio",
|
|
title: "Factorio",
|
|
Component: LoginForm,
|
|
}];
|
|
}
|
|
}
|