Production Ready
This commit is contained in:
parent
b621b71494
commit
4ebb1d8e88
|
@ -5,8 +5,9 @@ import { PageLogin } from './pages/login'
|
|||
import { BrowserRouter, Route, Routes } from 'react-router-dom'
|
||||
import { PageNotFound } from './pages/notfound'
|
||||
import { PageRegister } from './pages/register'
|
||||
import { PageInfo } from './pages/info'
|
||||
|
||||
const API_BASE_ADDRESS = "http://127.0.0.1:30"
|
||||
const API_BASE_ADDRESS = "https://api.charmless.today"
|
||||
|
||||
export const api = (path: string) => API_BASE_ADDRESS + path
|
||||
|
||||
|
@ -41,6 +42,7 @@ const LSPYggdrasilWebApp = () => {
|
|||
<Routes>
|
||||
<Route path="/" element={ <PageLogin /> } />
|
||||
<Route path="/register" element={ <PageRegister /> } />
|
||||
<Route path="/info" element={ <PageInfo /> } />
|
||||
<Route path="*" element={ <PageNotFound /> } />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import { useRef, useState } from "react"
|
||||
import axios from "axios"
|
||||
import { api } from ".."
|
||||
|
||||
export const PageInfo = () => {
|
||||
const fileRef = useRef<HTMLInputElement>(null)
|
||||
const [error, setError] = useState("没上传/没报错")
|
||||
const [disableBtn, setDisableBtn] = useState(false)
|
||||
|
||||
return <div>
|
||||
<h1>
|
||||
总之如果你能看到这里说明你登陆成功了,下面是你的账号信息: Key(别发给别人)={window.sessionStorage.getItem('identifier')}, Textures={window.sessionStorage.getItem('textures')}, Username={window.sessionStorage.getItem('username')}, UUID={window.sessionStorage.getItem('uuid')}
|
||||
</h1>
|
||||
|
||||
<h2>
|
||||
神马?你问我改密码?我懒得做,真要改找lama
|
||||
</h2>
|
||||
|
||||
<h3>
|
||||
为什么这个页面成这个B样子了?因为我没时间做了,你就说能不能用吧
|
||||
</h3>
|
||||
|
||||
<h4>
|
||||
登出?自己关了这个页面就登出了
|
||||
</h4>
|
||||
|
||||
<input type="file" accept=".png" ref={fileRef}></input>
|
||||
<button disabled={disableBtn} onClick={() => {
|
||||
setDisableBtn(true)
|
||||
setError("正传着呢")
|
||||
const file = fileRef.current?.files?.[0]
|
||||
if(!file) {
|
||||
return setError("你选了个寄吧,服务端不接受你不选文件谢谢")
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const r = await axios({
|
||||
method: 'put',
|
||||
url: api('/api/textures/skin'),
|
||||
data: file,
|
||||
headers: {
|
||||
'content-type': 'image/png',
|
||||
'x-lsp-identifier': window.sessionStorage.getItem('identifier') as string
|
||||
}
|
||||
})
|
||||
if(r.data.err !== 1.048596) {
|
||||
return setError(`上传失败! 错误代码 ${r.data.err} 原因: ${r.data.msg}`)
|
||||
} else {
|
||||
return setError("上传成功 msg=" + r.data.msg)
|
||||
}
|
||||
})().finally(() => {
|
||||
setDisableBtn(false)
|
||||
})
|
||||
}}>上传(皮肤)</button>
|
||||
<button disabled={disableBtn}>上传(披风)</button>
|
||||
|
||||
<p>皮肤上传报错:{error}</p>
|
||||
</div>
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { Box, Button, CssBaseline, Grid, Link, Paper, TextField, ThemeProvider, Typography, Alert, Collapse } from "@mui/material"
|
||||
import { Box, Button, CssBaseline, Grid, Paper, TextField, ThemeProvider, Typography, Alert, Collapse, Dialog, DialogTitle, DialogContentText, DialogActions, DialogContent, Link } from "@mui/material"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { api, theme } from '../index'
|
||||
import axios from "axios"
|
||||
|
@ -9,24 +9,56 @@ export const PageLogin = () => {
|
|||
const navigate = useNavigate()
|
||||
const usernameInput = useRef<HTMLInputElement>(null)
|
||||
const passwordInput = useRef<HTMLInputElement>(null)
|
||||
const [disableBtn, setDisableBtn] = useState(false)
|
||||
const [cors, setCors] = useState(false)
|
||||
const [illegal, setIllegal] = useState(false)
|
||||
const [illegalMessgae, setIllegalMessgae] = useState("")
|
||||
|
||||
const handleLogin = () => {
|
||||
const username = usernameInput.current?.value
|
||||
const password = passwordInput.current?.value
|
||||
if(!username || !password) {
|
||||
return setIllegal(true)
|
||||
}
|
||||
setDisableBtn(true);
|
||||
(async () => {
|
||||
const username = usernameInput.current?.value
|
||||
const password = passwordInput.current?.value
|
||||
if(!username || !password) {
|
||||
setIllegalMessgae("请输入用户名或密码")
|
||||
return setIllegal(true)
|
||||
}
|
||||
|
||||
try {
|
||||
let response = await axios.post(api("/api/login"), {
|
||||
username,
|
||||
password,
|
||||
})
|
||||
|
||||
if(response.data.err !== 1.048596) {
|
||||
setIllegalMessgae(`登录失败! 错误代码 ${response.data.err} 原因: ${response.data.msg}`)
|
||||
setIllegal(true)
|
||||
}
|
||||
|
||||
axios.post(api("/api/login"), {
|
||||
username,
|
||||
password,
|
||||
createToken: false
|
||||
}).then((response) => {
|
||||
window.sessionStorage.setItem('identifier', response.data.identifier)
|
||||
window.sessionStorage.setItem('textures', response.data.texturs)
|
||||
window.sessionStorage.setItem('username', response.data.username)
|
||||
window.sessionStorage.setItem('uuid', response.data.uuid)
|
||||
|
||||
if(response.data.err !== 1.048596) {
|
||||
setIllegalMessgae(`登录失败! 错误代码 ${response.data.err} 原因: ${response.data.msg}`)
|
||||
return setIllegal(true)
|
||||
}
|
||||
|
||||
console.log(response.data)
|
||||
|
||||
window.sessionStorage.setItem('identifier', response.data.extra.identifier)
|
||||
window.sessionStorage.setItem('textures', JSON.stringify(response.data.extra.textures))
|
||||
window.sessionStorage.setItem('username', response.data.extra.username)
|
||||
window.sessionStorage.setItem('uuid', response.data.extra.uuid)
|
||||
|
||||
navigate("/info")
|
||||
} catch (err: any) {
|
||||
if(err.code === "ERR_NETWORK") {
|
||||
return setCors(true)
|
||||
}
|
||||
setIllegalMessgae(`登录失败! 错误代码 ${err.code} 原因: ${err.message}`)
|
||||
setIllegal(true)
|
||||
}
|
||||
|
||||
})().finally(() => {
|
||||
setDisableBtn(false);
|
||||
})
|
||||
|
||||
}
|
||||
|
@ -34,6 +66,20 @@ export const PageLogin = () => {
|
|||
return <ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
|
||||
<Dialog open={cors} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description">
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{"无法发送请求!"}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
由于您的浏览器有 CORS 限制导致页面无法向 Mojang API/LSP-Yggdrasil API 发送请求。您可以选择 <Link href="https://chrome.google.com/webstore/detail/cors-unblock/lfhmikememgdcahcdlaciloancbhjino">点我安装绕过CORS Chrome浏览器插件</Link>。或者添加 Chrome 启动参数: --disable-web-security 来解决。
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => {setCors(false)}}>我知道了</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<div className="rootPanel">
|
||||
|
||||
<Box>
|
||||
|
@ -49,7 +95,7 @@ export const PageLogin = () => {
|
|||
<Grid item>
|
||||
<Collapse in={illegal}>
|
||||
<Alert severity="error">
|
||||
无效的用户名或密码
|
||||
{illegalMessgae}
|
||||
</Alert>
|
||||
</Collapse>
|
||||
</Grid>
|
||||
|
@ -63,7 +109,7 @@ export const PageLogin = () => {
|
|||
</Grid>
|
||||
|
||||
<Grid item>
|
||||
<Button onClick={handleLogin} variant="contained" color="primary" >
|
||||
<Button onClick={handleLogin} variant="contained" color="primary" disabled={disableBtn} >
|
||||
登录
|
||||
</Button>
|
||||
</Grid>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Box, Button, CssBaseline, Grid, Paper, TextField, ThemeProvider, Typography, Alert, Collapse } from "@mui/material"
|
||||
import { Box, Button, CssBaseline, Grid, Paper, TextField, Link, ThemeProvider, Typography, Alert, Collapse, Checkbox, FormGroup, FormControlLabel, Dialog, DialogTitle, DialogContentText, DialogActions, DialogContent } from "@mui/material"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { api, theme } from '../index'
|
||||
import axios from "axios"
|
||||
|
@ -7,15 +7,109 @@ import "./custom.css"
|
|||
|
||||
export const PageRegister = () => {
|
||||
const navigate = useNavigate()
|
||||
const [illegal1, setIllegal1] = useState(false)
|
||||
const [illegal2, setIllegal2] = useState(false)
|
||||
const [illegal3, setIllegal3] = useState(false)
|
||||
const usernameInput = useRef<HTMLInputElement>(null)
|
||||
const passwordInput = useRef<HTMLInputElement>(null)
|
||||
const emailInput = useRef<HTMLInputElement>(null)
|
||||
const invitationCodeInput = useRef<HTMLInputElement>(null)
|
||||
const validationCodeInput = useRef<HTMLInputElement>(null)
|
||||
const [useMojangSkin, setUseMojangSkin] = useState(false)
|
||||
const [disableBtn, setDisableBtn] = useState(false)
|
||||
const [cors, setCors] = useState(false)
|
||||
const [illegal, setIllegal] = useState(false)
|
||||
const [illegalMessgae, setIllegalMessgae] = useState("")
|
||||
|
||||
|
||||
const handleRegister = () => {
|
||||
setDisableBtn(true);
|
||||
(async () => {
|
||||
const username = usernameInput.current?.value
|
||||
const password = passwordInput.current?.value
|
||||
const email = emailInput.current?.value
|
||||
const invitationCode = passwordInput.current?.value
|
||||
const validationCode = invitationCodeInput.current?.value
|
||||
if(!username || !password) {
|
||||
setIllegalMessgae("请将以下内容全部输入")
|
||||
setIllegal(true)
|
||||
}
|
||||
|
||||
let textureMigrations = undefined
|
||||
|
||||
if(useMojangSkin) {
|
||||
try {
|
||||
const response = await axios.get("https://api.mojang.com/users/profiles/minecraft/" + username)
|
||||
const profile = await axios.get("https://sessionserver.mojang.com/session/minecraft/profile/" + response.data.id)
|
||||
profile.data.properties.forEach((prop: any) => {
|
||||
if(prop.name === "textures") {
|
||||
const texture = JSON.parse(atob(prop.value)).textures
|
||||
|
||||
if(texture.SKIN || texture.CAPE) {
|
||||
textureMigrations = {
|
||||
skin: texture.SKIN?.url ?? undefined,
|
||||
cape: texture.SKIN?.url ?? undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch(err: any) {
|
||||
if(err.code === "ERR_NETWORK") {
|
||||
setCors(true)
|
||||
return
|
||||
}
|
||||
setIllegalMessgae(`无法同步正版皮肤,可能是用户不存在`)
|
||||
setIllegal(true)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(textureMigrations)
|
||||
|
||||
try {
|
||||
const response = await axios.post(api("/api/register"), {
|
||||
username,
|
||||
password,
|
||||
email,
|
||||
invitationCode,
|
||||
validationCode,
|
||||
textureMigrations
|
||||
})
|
||||
|
||||
if(response.data.err !== 1.048596) {
|
||||
setIllegalMessgae(`注册失败! 错误代码 ${response.data.err} 原因: ${response.data.msg}`)
|
||||
setIllegal(true)
|
||||
} else {
|
||||
navigate("/")
|
||||
}
|
||||
} catch (err: any) {
|
||||
if(err.code === "ERR_NETWORK") {
|
||||
setCors(true)
|
||||
return
|
||||
}
|
||||
setIllegalMessgae(`注册失败! 错误代码 ${err.code} 原因: ${err.message}`)
|
||||
setIllegal(true)
|
||||
}
|
||||
})().finally(() => {
|
||||
setDisableBtn(false);
|
||||
})
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
||||
return <ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
|
||||
<Dialog open={cors} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description">
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{"无法发送请求!"}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
由于您的浏览器有 CORS 限制导致页面无法向 Mojang API/LSP-Yggdrasil API 发送请求。您可以选择 <Link href="https://chrome.google.com/webstore/detail/cors-unblock/lfhmikememgdcahcdlaciloancbhjino">点我安装绕过CORS Chrome浏览器插件</Link>。或者添加 Chrome 启动参数: --disable-web-security 来解决。
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => {setCors(false)}}>我知道了</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<div className="rootPanel">
|
||||
|
||||
<Box>
|
||||
|
@ -28,48 +122,45 @@ export const PageRegister = () => {
|
|||
</Typography>
|
||||
|
||||
</Grid>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item><Collapse in={illegal1}>
|
||||
<Alert severity="error">
|
||||
无效的用户名、密码或邮箱
|
||||
</Alert>
|
||||
</Collapse></Grid>
|
||||
|
||||
<Grid item><Collapse in={illegal2}>
|
||||
<Alert severity="error">
|
||||
无效的邀请码
|
||||
</Alert>
|
||||
</Collapse></Grid>
|
||||
|
||||
<Grid item><Collapse in={illegal3}>
|
||||
<Alert severity="error">
|
||||
无效的邀请码
|
||||
</Alert>
|
||||
</Collapse></Grid>
|
||||
<Grid container style={{maxWidth: '100%'}} spacing={2}>
|
||||
<Grid style={{maxWidth: '100%'}} item>
|
||||
<Collapse style={{maxWidth: '100%'}} in={illegal}>
|
||||
<Alert style={{paddingRight: '1%', marginTop: '2px', maxWidth: '100%'}} severity="error">
|
||||
{illegalMessgae}
|
||||
</Alert>
|
||||
</Collapse>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
<Grid item>
|
||||
<TextField fullWidth required label="用户名" type="text" /* inputRef={usernameInput} */ onInput={() => setIllegal1(false)} />
|
||||
<TextField fullWidth required label="用户名" type="text" inputRef={usernameInput} onInput={() => setIllegal(false)} />
|
||||
</Grid>
|
||||
|
||||
<Grid item>
|
||||
<TextField fullWidth required label="密码" type="password" /* inputRef={passwordInput} */ onInput={() => setIllegal1(false)} />
|
||||
<TextField fullWidth required label="密码" type="password" inputRef={passwordInput} onInput={() => setIllegal(false)} />
|
||||
</Grid>
|
||||
|
||||
<Grid item>
|
||||
<TextField fullWidth required label="邮箱" type="text" /* inputRef={emailInput} */ onInput={() => setIllegal1(false)} />
|
||||
<TextField fullWidth required label="邮箱" type="text" inputRef={emailInput} onInput={() => setIllegal(false)} />
|
||||
</Grid>
|
||||
|
||||
<Grid item>
|
||||
<TextField fullWidth required label="邀请码" type="text" inputRef={invitationCodeInput} onInput={() => setIllegal(false)} />
|
||||
</Grid>
|
||||
|
||||
<Grid item>
|
||||
<TextField fullWidth required label="校验码" type="text" /* inputRef={validationCodeInput} */ onInput={() => setIllegal2(false)} />
|
||||
<TextField fullWidth required label="校验码" type="text" inputRef={validationCodeInput} onInput={() => setIllegal(false)} />
|
||||
</Grid>
|
||||
|
||||
<Grid item>
|
||||
<TextField fullWidth label="邀请码" type="text" /* inputRef={invitationCodeInput} */ onInput={() => setIllegal3(false)} />
|
||||
<FormGroup>
|
||||
<FormControlLabel label="使用同名正版用户皮肤" control={<Checkbox required checked={useMojangSkin} onClick={() => {setUseMojangSkin(!useMojangSkin)}} />}/>
|
||||
</FormGroup>
|
||||
</Grid>
|
||||
|
||||
<Grid item>
|
||||
<Button /* onClick={handleRegister} */ variant="contained" color="primary" >
|
||||
<Button onClick={handleRegister} variant="contained" color="primary" disabled={disableBtn} >
|
||||
注册
|
||||
</Button>
|
||||
</Grid>
|
||||
|
|
Loading…
Reference in New Issue