From 62d58e30cdd288cb465e6401274c2fe2f2ecf28b Mon Sep 17 00:00:00 2001 From: "MH.Dmitrii" Date: Thu, 19 Mar 2026 02:41:39 +0300 Subject: [PATCH] remake apis 0.151 --- docker/caddy/conf/Caddyfile | 4 + server/backend/endpoints/endpoints.py | 42 +++++++--- server/frontend/auth/login.js | 109 ++++++++++++-------------- server/frontend/main/api.js | 38 ++++----- 4 files changed, 101 insertions(+), 92 deletions(-) diff --git a/docker/caddy/conf/Caddyfile b/docker/caddy/conf/Caddyfile index 3fe0010..4dcfebb 100644 --- a/docker/caddy/conf/Caddyfile +++ b/docker/caddy/conf/Caddyfile @@ -9,6 +9,10 @@ https://ru.homyk.space { } handle_path /main/* { + forward_auth backend:{$PORT} { + uri /api/verify + copy_headers Authorization + } root * /srv/main file_server } diff --git a/server/backend/endpoints/endpoints.py b/server/backend/endpoints/endpoints.py index b70d79d..02f22d3 100644 --- a/server/backend/endpoints/endpoints.py +++ b/server/backend/endpoints/endpoints.py @@ -1,16 +1,21 @@ -from fastapi import FastAPI, Depends, HTTPException,status +from fastapi import FastAPI, Depends, HTTPException,status, Response, Cookie from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials import server.backend.schema.pydantic as pydantic import server.backend.database.db as db from server.backend.auth.JWT import signJWT, decodeJWT api = FastAPI(openapi_url="/api/openapi.json",docs_url="/api/docs", redoc_url="/api/redoc") -security = HTTPBearer() +security = HTTPBearer(auto_error=False) -async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)): - token = credentials.credentials +async def get_current_user( + credentials: HTTPAuthorizationCredentials = Depends(security), + token_cookie: str = Cookie(default=None) +): + token = credentials.credentials if credentials else token_cookie + if not token: + raise HTTPException(status_code=401, detail="Not authenticated") user = decodeJWT(token) if not user: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") + raise HTTPException(status_code=401, detail="Invalid token") return user async def check_roles(user=Depends(get_current_user)): user_check = await db.list_user(user["user_id"]) @@ -58,10 +63,29 @@ async def list_users(user=Depends(check_roles)): list_of_users = await db.list_users() return list_of_users -@api.post("/api/auth",response_model=pydantic.Token) -async def auth(code:pydantic.UserAccess): +@api.post("/api/auth") +async def auth(code: pydantic.UserAccess, response: Response): login = await db.login_user(code) - if login == None: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Forbidden") + if login is None: + raise HTTPException(status_code=401, detail="Forbidden") token = signJWT(login) + response.set_cookie( + key="token", + value=token, + httponly=True, + secure=True, + samesite="strict" + ) return {"access_token": token, "token_type": "bearer"} +@api.get("/api/verify") +async def verify(token: str = Cookie(default=None)): + if not token: + raise HTTPException(status_code=401, detail="No token") + user = decodeJWT(token) + if not user: + raise HTTPException(status_code=401, detail="Invalid token") + return {"status": "ok"} +@api.post("/api/logout") +async def logout(response: Response): + response.delete_cookie(key="token") + return {"status": "ok"} \ No newline at end of file diff --git a/server/frontend/auth/login.js b/server/frontend/auth/login.js index bd6d8ba..9f2ac9d 100644 --- a/server/frontend/auth/login.js +++ b/server/frontend/auth/login.js @@ -1,73 +1,64 @@ -function getToken() { - return localStorage.getItem("token") || sessionStorage.getItem("token"); -} -function tokenCheck(){ - const token = getToken(); - if (token) { - window.location.href = "https://ru.homyk.space/main/"; - } -} -document.getElementById('loginForm').addEventListener('submit', async function (e) { - e.preventDefault(); - const password = document.getElementById('password').value; - const userData = { - password - }; - try { - const response = await fetch("https://ru.homyk.space/api/auth", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - code: userData.password - }), - }); +document.addEventListener('DOMContentLoaded', () => { + fetch('/api/verify', { credentials: 'include' }) + .then(r => { if (r.ok) window.location.href = '/main/'; }) + .catch(() => {}); - const data = await response.json(); // читаем только один раз - if (response.ok) { // сохраняем только при успехе - // в sessionstorage до перезахода в браузер( - sessionStorage.setItem("token", data.access_token); - - window.location.href = 'https://ru.homyk.space/main/'; - } else { //парсинг и вывод ошибок, если есть - if (Array.isArray(data.detail)) { - const messages = data.detail.map(e => { - const field = e.loc.filter(locPart => locPart !== 'body').join(' -> '); - return `${field}: ${e.msg}`; - }); - showError(messages); - } else if (typeof data.detail === 'string') { - showError([data.detail]); + document.getElementById('loginForm').addEventListener('submit', async function(e) { + e.preventDefault(); + + const code = document.getElementById('password').value; + + try { + const response = await fetch('/api/auth', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + credentials: 'include', // ← чтобы браузер принял cookie + body: JSON.stringify({ code }) + }); + + const data = await response.json(); + + if (response.ok) { + window.location.href = '/main/'; } else { - showError(['Unknown error']); + if (Array.isArray(data.detail)) { + const messages = data.detail.map(e => { + const field = e.loc.filter(p => p !== 'body').join(' -> '); + return `${field}: ${e.msg}`; + }); + showError(messages); + } else if (typeof data.detail === 'string') { + showError([data.detail]); + } else { + showError(['Unknown error']); + } } + } catch (err) { + showError(['Connection error: ' + err.message]); } - } catch (err) { - showError(['Connection error: ' + err.message]); - } + }); }); -function showError(messages){ //Добавление их на form со стилями + +function showError(messages) { let errorElem = document.getElementById('formError'); - let container = document.getElementById('glass-container'); - if (!errorElem){ + if (!errorElem) { errorElem = document.createElement('div'); - errorElem.style.transition="3s"; errorElem.id = 'formError'; - errorElem.style.color = 'red'; - errorElem.style.marginTop = '20px'; - errorElem.style.fontSize = '14px'; - errorElem.style.fontWeight = '100'; - errorElem.style.marginBottom = '20px'; - errorElem.style.lineHeight="120%"; - errorElem.style.height = 'auto'; - const form = document.getElementById('loginForm'); - form.insertAdjacentElement('afterend', errorElem); - }; + errorElem.style.cssText = ` + color: red; + margin-top: 20px; + margin-bottom: 20px; + font-size: 14px; + font-weight: 100; + line-height: 120%; + transition: 3s; + `; + document.getElementById('loginForm').insertAdjacentElement('afterend', errorElem); + } errorElem.innerHTML = ''; messages.forEach(msg => { const li = document.createElement('li'); - li.style.listStyleType="none"; + li.style.listStyleType = 'none'; li.textContent = msg; errorElem.appendChild(li); }); diff --git a/server/frontend/main/api.js b/server/frontend/main/api.js index 98e9a4e..45b4d1e 100644 --- a/server/frontend/main/api.js +++ b/server/frontend/main/api.js @@ -1,29 +1,23 @@ -function getToken() { - return localStorage.getItem("token") || sessionStorage.getItem("token"); -} - -function tokenCheck() { - const token = getToken(); - if (!token) { - window.location.href = "https://ru.homyk.space"; - } -} - document.addEventListener("DOMContentLoaded", () => { - tokenCheck(); - document.getElementById('logoutForm').addEventListener('submit', function(e) { + // Проверка авторизации через cookie + fetch('/api/verify', { credentials: 'include' }) + .then(r => { if (!r.ok) window.location.href = '/'; }) + .catch(() => { window.location.href = '/'; }); + + // Logout — удаляем cookie на бэкенде + document.getElementById('logoutForm').addEventListener('submit', async function(e) { e.preventDefault(); - localStorage.removeItem("token"); - sessionStorage.removeItem("token"); - tokenCheck(); + await fetch('/api/logout', { + method: 'POST', + credentials: 'include' + }); + window.location.href = '/'; }); document.querySelector(".form-info").addEventListener("submit", async (e) => { e.preventDefault(); - const token = getToken(); - const guestData = { name: document.getElementById('ffname').value || "", middlename: document.getElementById('fmname').value || "", @@ -36,15 +30,11 @@ document.addEventListener("DOMContentLoaded", () => { .join(', ') }; - console.log(guestData); - try { const response = await fetch('/api/update', { method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` - }, + headers: { 'Content-Type': 'application/json' }, + credentials: 'include', // ← токен идёт через cookie body: JSON.stringify(guestData) });