init: 0.1.2
Some checks failed
/ build and run u-api (push) Failing after 8s
/ clean (push) Successful in 0s

feat: login page && auth required
todo: file dir cleanup
This commit is contained in:
loveuer
2025-05-09 17:49:12 +08:00
parent e5cf2c1367
commit 93c4339039
30 changed files with 1166 additions and 35 deletions

32
frontend/src/api/auth.ts Normal file
View File

@ -0,0 +1,32 @@
import {useState} from "react";
import {message} from "../component/message/u-message.tsx";
export interface User {
id: number;
username: string;
login_at: number;
token: string;
}
export const useAuth = () => {
const [user, setUser] = useState<User | null>(null)
const login = async (username: string, password: string) => {
let res = await fetch("/api/uauth/login", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({username: username, password: password}),
})
if(res.status !== 200) {
message.error("账号或密码错误")
throw new Error(await res.text())
}
let jes = await res.json() as User;
setUser(jes)
}
return {user, login}
}

View File

@ -17,7 +17,7 @@ export const useFileUpload = () => {
try {
console.log(`[D] api.Upload: upload file = ${file.name}, size = ${file.size}`, file);
const url = `/api/share/${file.name}`;
const url = `/api/ushare/${file.name}`;
// 1. 初始化上传
const res1 = await fetch(url, {
@ -51,7 +51,7 @@ export const useFileUpload = () => {
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
const res = await fetch(`/api/share/${code}`, {
const res = await fetch(`/api/ushare/${code}`, {
method: "POST",
headers: {
"Range": `bytes=${start}-${end - 1}`,

View File

@ -41,7 +41,7 @@ export const PanelRight = () => {
}
async function onFetchFile() {
const url = `/api/share/${code}`
const url = `/ushare/${code}`
console.log('[D] onFetchFile: url =', url)
const link = document.createElement('a');
link.href = url;

View File

@ -1,6 +1,7 @@
import React from "react";
import {createUseStyles} from "react-jss";
import {CloudBackground} from "../component/fluid/cloud.tsx";
import React, { useState } from "react";
import { createUseStyles } from "react-jss";
import { CloudBackground } from "../component/fluid/cloud.tsx";
import {useAuth} from "../api/auth.ts";
const useClass = createUseStyles({
container: {
@ -75,24 +76,106 @@ const useClass = createUseStyles({
},
},
},
inputContainer: {
position: 'relative',
width: '100%',
marginTop: '20px',
},
inputField: {
width: "calc(100% - 52px)",
padding: "12px 35px 12px 15px",
border: "1px solid #ddd",
borderRadius: "6px",
fontSize: "16px",
transition: "border-color 0.3s",
"&:focus": {
outline: "none",
borderColor: "#1a73e8",
boxShadow: "0 0 0 2px rgba(26, 115, 232, 0.2)",
},
"&:hover": {
borderColor: "#1a73e8",
}
},
iconButton: {
position: 'absolute',
right: '10px',
top: '50%',
transform: 'translateY(-50%)',
background: 'transparent',
border: 'none',
cursor: 'pointer',
color: '#666',
"&:hover": {
color: '#333',
}
}
})
export const Login: React.FC = () => {
const classes = useClass()
const {login} = useAuth()
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const [showPassword, setShowPassword] = useState(false)
const onLogin = async () => {
try {
await login(username, password)
window.location.href = "/"
} catch (_e) {
}
}
return <div className={classes.container}>
<CloudBackground/>
<div className={classes.login_container}>
<div className={classes.form}>
<h2>UShare</h2>
<div className={classes.input}>
<input placeholder={"请输入账号"}/>
{/* 用户名输入框 */}
<div className={classes.inputContainer}>
<input
className={classes.inputField}
placeholder="请输入账号"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
{username && (
<button
className={classes.iconButton}
onClick={() => setUsername("")}
style={{ right: '10px', fontSize: '16px' }}
>
×
</button>
)}
</div>
<div className={classes.input}>
<input placeholder={"请输入密码"} type={"password"} />
{/* 密码输入框 */}
<div className={classes.inputContainer}>
<input
className={classes.inputField}
placeholder="请输入密码"
type={showPassword ? "text" : "password"}
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button
className={classes.iconButton}
onMouseDown={() => setShowPassword(true)}
onMouseUp={() => setShowPassword(false)}
onMouseLeave={() => setShowPassword(false)}
style={{ right: '10px', fontSize: '12px' }}
>
{showPassword ? "👁" : "👁"}
</button>
</div>
<div className={classes.button}>
<button></button>
<button onClick={onLogin}></button>
</div>
</div>
</div>

View File

@ -9,7 +9,11 @@ export default defineConfig({
'/api': {
target: 'http://127.0.0.1:9119',
changeOrigin: true
}
},
'/ushare': {
target: 'http://127.0.0.1:9119',
changeOrigin: true
},
}
}
})