18 Commits
front ... main

Author SHA1 Message Date
66b6878e50 cache images
All checks were successful
Build Docker / deploy (push) Successful in 40s
Build Docker / build (push) Successful in 35s
2026-03-19 03:06:41 +03:00
7bdf422c32 fix js
All checks were successful
Build Docker / deploy (push) Successful in 51s
Build Docker / build (push) Successful in 34s
2026-03-19 03:00:41 +03:00
9a259cb637 fix logout button
All checks were successful
Build Docker / deploy (push) Successful in 40s
Build Docker / build (push) Successful in 35s
2026-03-19 02:57:02 +03:00
54072eb87d logout button fix
All checks were successful
Build Docker / deploy (push) Successful in 40s
Build Docker / build (push) Successful in 35s
2026-03-19 02:53:17 +03:00
ecf374685f error handle 0.152
All checks were successful
Build Docker / deploy (push) Successful in 38s
Build Docker / build (push) Successful in 34s
2026-03-19 02:48:16 +03:00
62d58e30cd remake apis 0.151
All checks were successful
Build Docker / deploy (push) Successful in 44s
Build Docker / build (push) Successful in 34s
2026-03-19 02:41:39 +03:00
a13b88bd32 fix font types 0.150
All checks were successful
Build Docker / deploy (push) Successful in 49s
Build Docker / build (push) Successful in 36s
2026-03-19 02:25:51 +03:00
ea7bfc64f2 fix jss 0.149
All checks were successful
Build Docker / deploy (push) Successful in 55s
Build Docker / build (push) Successful in 35s
2026-03-19 02:09:25 +03:00
0e2a17a3e3 fix login.js 0.148
All checks were successful
Build Docker / deploy (push) Successful in 38s
Build Docker / build (push) Successful in 39s
2026-03-19 02:00:48 +03:00
185cefe250 fix login.js 0.147
All checks were successful
Build Docker / deploy (push) Successful in 45s
Build Docker / build (push) Successful in 44s
2026-03-19 01:58:13 +03:00
25ec2c7bcb fix jss 0.146
All checks were successful
Build Docker / deploy (push) Successful in 41s
Build Docker / build (push) Successful in 38s
2026-03-19 01:52:57 +03:00
b667cde3d7 fix caddyfile 0.145
All checks were successful
Build Docker / deploy (push) Successful in 1m40s
Build Docker / build (push) Successful in 36s
2026-03-19 01:47:41 +03:00
d30af82765 fix caddy file 0.134
All checks were successful
Build Docker / deploy (push) Successful in 48s
Build Docker / build (push) Successful in 42s
2026-03-19 01:43:57 +03:00
efd8510853 fix caddy file 0.133
All checks were successful
Build Docker / deploy (push) Successful in 38s
Build Docker / build (push) Successful in 34s
2026-03-19 01:32:00 +03:00
e558aa6bdd fix caddy 0.132
All checks were successful
Build Docker / deploy (push) Successful in 50s
Build Docker / build (push) Successful in 38s
2026-03-19 01:29:09 +03:00
bd9210a3b5 fix caddy 0.131
All checks were successful
Build Docker / deploy (push) Successful in 39s
Build Docker / build (push) Successful in 34s
2026-03-19 01:22:49 +03:00
1eba5623a3 fix caddyfile 0.130
All checks were successful
Build Docker / deploy (push) Successful in 50s
Build Docker / build (push) Successful in 32s
2026-03-19 01:16:07 +03:00
c762662231 fix caddyfile
All checks were successful
Build Docker / deploy (push) Successful in 1m6s
Build Docker / build (push) Successful in 36s
2026-03-19 01:12:24 +03:00
8 changed files with 162 additions and 141 deletions

View File

@@ -4,14 +4,32 @@ http://ru.homyk.space {
https://ru.homyk.space { https://ru.homyk.space {
encode gzip encode gzip
# --- API ---
handle /api/* { handle /api/* {
reverse_proxy backend:{$PORT} reverse_proxy backend:{$PORT}
} }
# --- Frontend --- handle_path /main/* {
root * /srv @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 file_server
}
handle_errors {
@unauth expression {http.error.status_code} == 401
redir @unauth / 302
}
handle {
root * /srv/auth
file_server {
index login.html
}
}
log { log {
output file /data/logs/caddy.log { output file /data/logs/caddy.log {

View File

@@ -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"}

View File

@@ -1,39 +1,29 @@
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 = "http://localhost:5500/server/frontend/main/index.html";
}
}
document.getElementById('loginForm').addEventListener('submit', async function(e) { document.getElementById('loginForm').addEventListener('submit', async function(e) {
e.preventDefault(); e.preventDefault();
const password = document.getElementById('password').value;
const userData = { const code = document.getElementById('password').value;
password
};
try { try {
const response = await fetch("http://localhost:8000/api/auth", { const response = await fetch('/api/auth', {
method: "POST", method: 'POST',
headers: { headers: { 'Content-Type': 'application/json' },
"Content-Type": "application/x-www-form-urlencoded", credentials: 'include', // ← чтобы браузер принял cookie
}, body: JSON.stringify({ code })
body: new URLSearchParams({
password: userData.password
}),
}); });
const data = await response.json(); // читаем только один раз const data = await response.json();
if (response.ok) { // сохраняем только при успехе
// в sessionstorage до перезахода в браузер(
sessionStorage.setItem("token", data.access_token);
window.location.href = 'http://localhost:5500/server/frontend/main/index.html'; if (response.ok) {
} else { //парсинг и вывод ошибок, если есть window.location.href = '/main/';
} else {
if (Array.isArray(data.detail)) { if (Array.isArray(data.detail)) {
const messages = data.detail.map(e => { 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}`; return `${field}: ${e.msg}`;
}); });
showError(messages); showError(messages);
@@ -47,27 +37,28 @@ document.getElementById('loginForm').addEventListener('submit', async function (
showError(['Connection error: ' + err.message]); 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);
}); });

View File

@@ -1,69 +1,55 @@
function getToken() { document.addEventListener("DOMContentLoaded", () => {
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";
}
}
tokenCheck() // Проверка авторизации через cookie
document.getElementById('logoutForm').addEventListener('submit', async function (e) { 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(); e.preventDefault();
localStorage.removeItem("token"); await fetch('/api/logout', {
sessionStorage.removeItem("token"); method: 'POST',
tokenCheck(); credentials: 'include'
});
window.location.href = '/';
}); });
document.addEventListener("DOMContentLoaded", () => { document.querySelector(".form-info").addEventListener("submit", async (e) => {
const form = document.querySelector(".form-info");
form.addEventListener("submit", async (e) => {
e.preventDefault(); 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 = { const guestData = {
name: name, name: document.getElementById('ffname').value || "",
middlename: middlename, middlename: document.getElementById('fmname').value || "",
surname: surname, surname: document.getElementById('flname').value || "",
text_field: text_field, text_field: document.getElementById('text_field')?.value || "",
activated: true, activated: true,
types_of_food: food, types_of_food: document.querySelector('input[name="food"]:checked')?.value || "",
types_of_alco: types_of_alco types_of_alco: Array.from(document.querySelectorAll('input[name="drink"]:checked'))
.map(el => el.value)
.join(', ')
}; };
console.log(guestData)
// отправка на /api/update с Authorization try {
// const updateResponse = 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)
// });
// if (!updateResponse.ok) {
// throw new Error('Ошибка при отправке формы');
// }
// const updateData = await updateResponse.json();
// console.log('Форма успешно отправлена:', updateData);
// } catch (err) {
// console.error(err);
// alert('Ошибка: ' + err.message);
// }
});
}); });
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);
}
});
});

View File

@@ -469,8 +469,10 @@
<button type="submit" class="answer_btn" id="bsubmit"> <button type="submit" class="answer_btn" id="bsubmit">
Подтвердить участие Подтвердить участие
</button> </button>
</form> </form>
<button type="logout" class="answer_btn" id="logoutForm">
Выйти
</button>
</div> </div>
</section> </section>
</main> </main>