From e02f7ee46876d32ca094c2b5cf3647248baf7645 Mon Sep 17 00:00:00 2001 From: physcik Date: Sun, 3 May 2026 21:26:49 +0500 Subject: Register page + logout --- backend/src/API/AuthenticationAPI.zig | 54 ++++++++++++++++++++++++++++ front/src/App.tsx | 12 +++++++ front/src/Authentication/LoginPage.tsx | 4 ++- front/src/Authentication/LogoutPage.tsx | 11 ++++++ front/src/Authentication/RegisterPage.tsx | 59 +++++++++++++++++++++++++++++++ front/src/Emelents/Sidebar.tsx | 16 ++++++++- front/src/Locales/ru_RU.ts | 2 ++ 7 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 front/src/Authentication/LogoutPage.tsx create mode 100644 front/src/Authentication/RegisterPage.tsx diff --git a/backend/src/API/AuthenticationAPI.zig b/backend/src/API/AuthenticationAPI.zig index 0c2a1e9..82cdb1c 100644 --- a/backend/src/API/AuthenticationAPI.zig +++ b/backend/src/API/AuthenticationAPI.zig @@ -23,6 +23,14 @@ fn getUser(data: *Handler.RequestData, _: *httpz.Request, res: *httpz.Response) }, .{}); } +const authResult = struct { + Token: []const u8, + User: struct { + Username: []const u8, + Role: []const u8 + }, +}; + fn register(_: *Handler.RequestData, req: *httpz.Request, res: *httpz.Response) !void { var body = try req.json(model.RequestBody) orelse { res.setStatus(.bad_request); @@ -40,6 +48,19 @@ fn register(_: *Handler.RequestData, req: *httpz.Request, res: *httpz.Response) res.setStatus(.internal_server_error); return; }; + + const loginResult = loginAction(req.arena, body) catch |err| { + if (err == db.ResultErrors.NotFound) { + res.setStatus(.unauthorized); + try res.json(errDesc.ErrorDescriptor { + .Message = "Login or password is incorrect" + }, .{}); + return; + } + return err; + }; + + try res.json(loginResult, .{}); res.setStatus(.created); } @@ -72,3 +93,36 @@ fn login(_: *Handler.RequestData, req: *httpz.Request, res: *httpz.Response) !vo }, } , .{}); } + +/// Logs in with the given credentials +fn loginAction(allocator: std.mem.Allocator, requetsed: model.RequestBody) !authResult { + const username = requetsed.Username; + const pwd = requetsed.Password; + const user = try db.Users.GetByCredentials(allocator, username, pwd); // catch |err| { + // if (err == db.ResultErrors.NotFound) { + // res.setStatus(.unauthorized); + // try res.json(errDesc.ErrorDescriptor { + // .Message = "Login or password is incorrect" + // }, .{}); + // return; + // } + // return err; + // }; + + const token = try Tokens.GenerateNewSession(allocator, user); + + return authResult { + .Token = token, + .User = .{ + .Username = user.Username, + .Role = user.Role.ToString(), + } + }; + // try res.json(.{ + // .Token = token, + // .User = .{ + // .Username = user.Username, + // .Role = user.Role.ToString(), + // }, + // } , .{}); +} diff --git a/front/src/App.tsx b/front/src/App.tsx index c106139..7b68f3e 100644 --- a/front/src/App.tsx +++ b/front/src/App.tsx @@ -14,7 +14,9 @@ import RangedWeaponPage from './Pages/RangedWeapon'; import { AuthContext, Authentication, GetUserInfo } from './Authentication/ContextProvider'; import { useCookies } from 'react-cookie'; import LoginPage from './Authentication/LoginPage'; +import LogoutPage from './Authentication/LogoutPage'; import { User } from './Authentication/Models'; +import RegisterPage from './Authentication/RegisterPage'; const router = createBrowserRouter([ { @@ -33,10 +35,20 @@ const router = createBrowserRouter([ path: "/weapons/:id", element: (), }, + + // auth { path: "/login", element: (), }, + { + path: "/register", + element: (), + }, + { + path: "/logout", + element: (), + }, ]); function App() { diff --git a/front/src/Authentication/LoginPage.tsx b/front/src/Authentication/LoginPage.tsx index e98c506..e673418 100644 --- a/front/src/Authentication/LoginPage.tsx +++ b/front/src/Authentication/LoginPage.tsx @@ -3,7 +3,6 @@ import { useContext, useState } from "react"; import { useNavigate } from "react-router"; import { BackendURL } from "../Config"; import { Authentication, SaveState } from "./ContextProvider"; -import { useCookies } from 'react-cookie'; import { GetLocalizedString } from "../Locales/Locales"; import { LanguageContext } from "../Locales/Context"; @@ -40,6 +39,9 @@ function LoginPage() { window.location.reload(); }); }}> { GetLocalizedString("Log in", lang) } + + +

Don't have an account? Click here.

); } diff --git a/front/src/Authentication/LogoutPage.tsx b/front/src/Authentication/LogoutPage.tsx new file mode 100644 index 0000000..a234985 --- /dev/null +++ b/front/src/Authentication/LogoutPage.tsx @@ -0,0 +1,11 @@ +import { useNavigate } from "react-router"; + +function LogoutPage() { + const navigate = useNavigate(); + + document.cookie = "X-AUTH-TOKEN=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; + navigate("/"); + return (

Click here if you were not redirected already

); +} + +export default LogoutPage; diff --git a/front/src/Authentication/RegisterPage.tsx b/front/src/Authentication/RegisterPage.tsx new file mode 100644 index 0000000..b30b182 --- /dev/null +++ b/front/src/Authentication/RegisterPage.tsx @@ -0,0 +1,59 @@ +import axios from "axios"; +import { useContext, useState } from "react"; +import { useNavigate } from "react-router"; +import { BackendURL } from "../Config"; +import { Authentication, SaveState } from "./ContextProvider"; +import { GetLocalizedString } from "../Locales/Locales"; +import { LanguageContext } from "../Locales/Context"; + +const RegisterURL = `${BackendURL}/auth/register`; + +function RegisterPage() { + const lang = useContext(LanguageContext); + + const [username, setUsername] = useState(""); + const [passw, setPassw] = useState(""); + const navigate = useNavigate(); + + function SetAuthState(newAuthState: Authentication | null) { + if (newAuthState) { + console.log(`Logging in as ${newAuthState.User}...`); + } else { + console.log(`Logging out...`); + } + SaveState(newAuthState, (cookie: string) => { + document.cookie = `X-AUTH-TOKEN=${cookie}; path=/;`; + }) + } + + return ( +
+

{ GetLocalizedString("Username", lang) }

+ setUsername(ev.target.value)} /> +

{ GetLocalizedString("Password", lang) }

+ setPassw(ev.target.value)} /> + +
+ ); +} + +async function Register(username: string, passw: string, onSuccess: (data: Authentication) => void) { + await axios.post( + RegisterURL, { + Username: username, + Password: passw + } + ).then(resp => { + onSuccess(resp.data); + }).catch(err => { + console.log(`Failed to send a login responce: ${err}`); + }); +} + +export default RegisterPage; diff --git a/front/src/Emelents/Sidebar.tsx b/front/src/Emelents/Sidebar.tsx index ad565fa..34766c3 100644 --- a/front/src/Emelents/Sidebar.tsx +++ b/front/src/Emelents/Sidebar.tsx @@ -1,4 +1,6 @@ import { useContext } from 'react'; +import { AuthContext } from '../Authentication/ContextProvider'; +import { User } from '../Authentication/Models'; import { AllowedLanguages, LanguageContext } from '../Locales/Context'; import { GetLocalizedString } from '../Locales/Locales'; import './Elements.css'; @@ -9,6 +11,7 @@ type SidebarProps = { }; function Sidebar({setLang}: SidebarProps) { + var user = useContext(AuthContext); var lang = useContext(LanguageContext); return ( @@ -19,7 +22,7 @@ function Sidebar({setLang}: SidebarProps) { - + { AuthElement(user) }