remake apis 0.151
This commit is contained in:
@@ -9,6 +9,10 @@ https://ru.homyk.space {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handle_path /main/* {
|
handle_path /main/* {
|
||||||
|
forward_auth backend:{$PORT} {
|
||||||
|
uri /api/verify
|
||||||
|
copy_headers Authorization
|
||||||
|
}
|
||||||
root * /srv/main
|
root * /srv/main
|
||||||
file_server
|
file_server
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||||
import server.backend.schema.pydantic as pydantic
|
import server.backend.schema.pydantic as pydantic
|
||||||
import server.backend.database.db as db
|
import server.backend.database.db as db
|
||||||
from server.backend.auth.JWT import signJWT, decodeJWT
|
from server.backend.auth.JWT import signJWT, decodeJWT
|
||||||
api = FastAPI(openapi_url="/api/openapi.json",docs_url="/api/docs", redoc_url="/api/redoc")
|
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)):
|
async def get_current_user(
|
||||||
token = credentials.credentials
|
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)
|
user = decodeJWT(token)
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
|
raise HTTPException(status_code=401, detail="Invalid token")
|
||||||
return user
|
return user
|
||||||
async def check_roles(user=Depends(get_current_user)):
|
async def check_roles(user=Depends(get_current_user)):
|
||||||
user_check = await db.list_user(user["user_id"])
|
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()
|
list_of_users = await db.list_users()
|
||||||
return list_of_users
|
return list_of_users
|
||||||
|
|
||||||
@api.post("/api/auth",response_model=pydantic.Token)
|
@api.post("/api/auth")
|
||||||
async def auth(code:pydantic.UserAccess):
|
async def auth(code: pydantic.UserAccess, response: Response):
|
||||||
login = await db.login_user(code)
|
login = await db.login_user(code)
|
||||||
if login == None:
|
if login is None:
|
||||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Forbidden")
|
raise HTTPException(status_code=401, detail="Forbidden")
|
||||||
token = signJWT(login)
|
token = signJWT(login)
|
||||||
|
response.set_cookie(
|
||||||
|
key="token",
|
||||||
|
value=token,
|
||||||
|
httponly=True,
|
||||||
|
secure=True,
|
||||||
|
samesite="strict"
|
||||||
|
)
|
||||||
return {"access_token": token, "token_type": "bearer"}
|
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"}
|
||||||
@@ -1,73 +1,64 @@
|
|||||||
function getToken() {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
return localStorage.getItem("token") || sessionStorage.getItem("token");
|
fetch('/api/verify', { credentials: 'include' })
|
||||||
}
|
.then(r => { if (r.ok) window.location.href = '/main/'; })
|
||||||
function tokenCheck(){
|
.catch(() => {});
|
||||||
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
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json(); // читаем только один раз
|
document.getElementById('loginForm').addEventListener('submit', async function(e) {
|
||||||
if (response.ok) { // сохраняем только при успехе
|
e.preventDefault();
|
||||||
// в sessionstorage до перезахода в браузер(
|
|
||||||
sessionStorage.setItem("token", data.access_token);
|
const code = document.getElementById('password').value;
|
||||||
|
|
||||||
window.location.href = 'https://ru.homyk.space/main/';
|
try {
|
||||||
} else { //парсинг и вывод ошибок, если есть
|
const response = await fetch('/api/auth', {
|
||||||
if (Array.isArray(data.detail)) {
|
method: 'POST',
|
||||||
const messages = data.detail.map(e => {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
const field = e.loc.filter(locPart => locPart !== 'body').join(' -> ');
|
credentials: 'include', // ← чтобы браузер принял cookie
|
||||||
return `${field}: ${e.msg}`;
|
body: JSON.stringify({ code })
|
||||||
});
|
});
|
||||||
showError(messages);
|
|
||||||
} else if (typeof data.detail === 'string') {
|
const data = await response.json();
|
||||||
showError([data.detail]);
|
|
||||||
|
if (response.ok) {
|
||||||
|
window.location.href = '/main/';
|
||||||
} else {
|
} 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 errorElem = document.getElementById('formError');
|
||||||
let container = document.getElementById('glass-container');
|
if (!errorElem) {
|
||||||
if (!errorElem){
|
|
||||||
errorElem = document.createElement('div');
|
errorElem = document.createElement('div');
|
||||||
errorElem.style.transition="3s";
|
|
||||||
errorElem.id = 'formError';
|
errorElem.id = 'formError';
|
||||||
errorElem.style.color = 'red';
|
errorElem.style.cssText = `
|
||||||
errorElem.style.marginTop = '20px';
|
color: red;
|
||||||
errorElem.style.fontSize = '14px';
|
margin-top: 20px;
|
||||||
errorElem.style.fontWeight = '100';
|
margin-bottom: 20px;
|
||||||
errorElem.style.marginBottom = '20px';
|
font-size: 14px;
|
||||||
errorElem.style.lineHeight="120%";
|
font-weight: 100;
|
||||||
errorElem.style.height = 'auto';
|
line-height: 120%;
|
||||||
const form = document.getElementById('loginForm');
|
transition: 3s;
|
||||||
form.insertAdjacentElement('afterend', errorElem);
|
`;
|
||||||
};
|
document.getElementById('loginForm').insertAdjacentElement('afterend', errorElem);
|
||||||
|
}
|
||||||
errorElem.innerHTML = '';
|
errorElem.innerHTML = '';
|
||||||
messages.forEach(msg => {
|
messages.forEach(msg => {
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.style.listStyleType="none";
|
li.style.listStyleType = 'none';
|
||||||
li.textContent = msg;
|
li.textContent = msg;
|
||||||
errorElem.appendChild(li);
|
errorElem.appendChild(li);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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", () => {
|
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();
|
e.preventDefault();
|
||||||
localStorage.removeItem("token");
|
await fetch('/api/logout', {
|
||||||
sessionStorage.removeItem("token");
|
method: 'POST',
|
||||||
tokenCheck();
|
credentials: 'include'
|
||||||
|
});
|
||||||
|
window.location.href = '/';
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector(".form-info").addEventListener("submit", async (e) => {
|
document.querySelector(".form-info").addEventListener("submit", async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const token = getToken();
|
|
||||||
|
|
||||||
const guestData = {
|
const guestData = {
|
||||||
name: document.getElementById('ffname').value || "",
|
name: document.getElementById('ffname').value || "",
|
||||||
middlename: document.getElementById('fmname').value || "",
|
middlename: document.getElementById('fmname').value || "",
|
||||||
@@ -36,15 +30,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
.join(', ')
|
.join(', ')
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(guestData);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/update', {
|
const response = await fetch('/api/update', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
'Content-Type': 'application/json',
|
credentials: 'include', // ← токен идёт через cookie
|
||||||
'Authorization': `Bearer ${token}`
|
|
||||||
},
|
|
||||||
body: JSON.stringify(guestData)
|
body: JSON.stringify(guestData)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user