Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66b6878e50 | |||
| 7bdf422c32 | |||
| 9a259cb637 | |||
| 54072eb87d | |||
| ecf374685f | |||
| 62d58e30cd | |||
| a13b88bd32 | |||
| ea7bfc64f2 | |||
| 0e2a17a3e3 | |||
| 185cefe250 | |||
| 25ec2c7bcb | |||
| b667cde3d7 | |||
| d30af82765 | |||
| efd8510853 | |||
| e558aa6bdd | |||
| bd9210a3b5 | |||
| 1eba5623a3 | |||
| c762662231 |
@@ -4,14 +4,32 @@ http://ru.homyk.space {
|
||||
https://ru.homyk.space {
|
||||
encode gzip
|
||||
|
||||
# --- API ---
|
||||
handle /api/* {
|
||||
reverse_proxy backend:{$PORT}
|
||||
}
|
||||
|
||||
# --- Frontend ---
|
||||
root * /srv
|
||||
handle_path /main/* {
|
||||
@images path_regexp \.(jpg|jpeg|png|webp|gif|svg)$
|
||||
header @images Cache-Control "public, max-age=604800"
|
||||
|
||||
forward_auth backend:{$PORT} {
|
||||
uri /api/verify
|
||||
copy_headers Authorization
|
||||
}
|
||||
root * /srv/main
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_errors {
|
||||
@unauth expression {http.error.status_code} == 401
|
||||
redir @unauth / 302
|
||||
}
|
||||
handle {
|
||||
root * /srv/auth
|
||||
file_server {
|
||||
index login.html
|
||||
}
|
||||
}
|
||||
|
||||
log {
|
||||
output file /data/logs/caddy.log {
|
||||
|
||||
@@ -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"}
|
||||
@@ -1,39 +1,29 @@
|
||||
function getToken() {
|
||||
return localStorage.getItem("token") || sessionStorage.getItem("token");
|
||||
}
|
||||
function tokenCheck(){
|
||||
const token = getToken();
|
||||
if (token) {
|
||||
window.location.href = "http://localhost:5500/server/frontend/main/index.html";
|
||||
}
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
fetch('/api/verify', { credentials: 'include' })
|
||||
.then(r => { if (r.ok) window.location.href = '/main/'; })
|
||||
.catch(() => {});
|
||||
|
||||
document.getElementById('loginForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
const password = document.getElementById('password').value;
|
||||
const userData = {
|
||||
password
|
||||
};
|
||||
|
||||
const code = document.getElementById('password').value;
|
||||
|
||||
try {
|
||||
const response = await fetch("http://localhost:8000/api/auth", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
password: userData.password
|
||||
}),
|
||||
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) { // сохраняем только при успехе
|
||||
// в sessionstorage до перезахода в браузер(
|
||||
sessionStorage.setItem("token", data.access_token);
|
||||
const data = await response.json();
|
||||
|
||||
window.location.href = 'http://localhost:5500/server/frontend/main/index.html';
|
||||
} else { //парсинг и вывод ошибок, если есть
|
||||
if (response.ok) {
|
||||
window.location.href = '/main/';
|
||||
} else {
|
||||
if (Array.isArray(data.detail)) {
|
||||
const messages = data.detail.map(e => {
|
||||
const field = e.loc.filter(locPart => locPart !== 'body').join(' -> ');
|
||||
const field = e.loc.filter(p => p !== 'body').join(' -> ');
|
||||
return `${field}: ${e.msg}`;
|
||||
});
|
||||
showError(messages);
|
||||
@@ -47,27 +37,28 @@ document.getElementById('loginForm').addEventListener('submit', async function (
|
||||
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) {
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -1,69 +1,55 @@
|
||||
function getToken() {
|
||||
return localStorage.getItem("token") || sessionStorage.getItem("token");
|
||||
}
|
||||
function tokenCheck(){
|
||||
const token = getToken();
|
||||
if (!token) {
|
||||
window.location.href = "http://localhost:5500/server/frontend/auth/login.html";
|
||||
}
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
tokenCheck()
|
||||
document.getElementById('logoutForm').addEventListener('submit', async 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('click', async function(e) {
|
||||
e.preventDefault();
|
||||
localStorage.removeItem("token");
|
||||
sessionStorage.removeItem("token");
|
||||
tokenCheck();
|
||||
await fetch('/api/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include'
|
||||
});
|
||||
window.location.href = '/';
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const form = document.querySelector(".form-info");
|
||||
|
||||
form.addEventListener("submit", async (e) => {
|
||||
document.querySelector(".form-info").addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
// try {
|
||||
// собираем данные из формы
|
||||
const name = document.getElementById('ffname').value || "";
|
||||
const middlename = document.getElementById('fmname').value || "";
|
||||
const surname = document.getElementById('flname').value || "";
|
||||
const text_field = document.getElementById('text_field')?.value || "";
|
||||
const food = document.querySelector('input[name="food"]:checked')?.value || "";
|
||||
const types_of_alco = Array.from(document.querySelectorAll('input[name="drink"]:checked'))
|
||||
.map(el => el.value)
|
||||
.join(', ');
|
||||
|
||||
const guestData = {
|
||||
name: name,
|
||||
middlename: middlename,
|
||||
surname: surname,
|
||||
text_field: text_field,
|
||||
name: document.getElementById('ffname').value || "",
|
||||
middlename: document.getElementById('fmname').value || "",
|
||||
surname: document.getElementById('flname').value || "",
|
||||
text_field: document.getElementById('text_field')?.value || "",
|
||||
activated: true,
|
||||
types_of_food: food,
|
||||
types_of_alco: types_of_alco
|
||||
types_of_food: document.querySelector('input[name="food"]:checked')?.value || "",
|
||||
types_of_alco: Array.from(document.querySelectorAll('input[name="drink"]:checked'))
|
||||
.map(el => el.value)
|
||||
.join(', ')
|
||||
};
|
||||
console.log(guestData)
|
||||
|
||||
// отправка на /api/update с Authorization
|
||||
// const updateResponse = await fetch('/api/update', {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// 'Authorization': `Bearer ${token}`
|
||||
// },
|
||||
// body: JSON.stringify(guestData)
|
||||
// });
|
||||
|
||||
// if (!updateResponse.ok) {
|
||||
// throw new Error('Ошибка при отправке формы');
|
||||
// }
|
||||
|
||||
// const updateData = await updateResponse.json();
|
||||
// console.log('Форма успешно отправлена:', updateData);
|
||||
|
||||
// } catch (err) {
|
||||
// console.error(err);
|
||||
// alert('Ошибка: ' + err.message);
|
||||
// }
|
||||
});
|
||||
try {
|
||||
const response = await fetch('/api/update', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include', // ← токен идёт через cookie
|
||||
body: JSON.stringify(guestData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const err = await response.json();
|
||||
throw new Error(JSON.stringify(err.detail || 'Ошибка при отправке'));
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Успешно:', data);
|
||||
alert('Данные сохранены!');
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('Ошибка: ' + err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -469,8 +469,10 @@
|
||||
<button type="submit" class="answer_btn" id="bsubmit">
|
||||
Подтвердить участие
|
||||
</button>
|
||||
|
||||
</form>
|
||||
<button type="logout" class="answer_btn" id="logoutForm">
|
||||
Выйти
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user