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 {
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 {
@@ -20,7 +38,7 @@ https://ru.homyk.space {
roll_keep_for 72h
}
format json
}
}
}
https://music.homyk.space {
encode gzip
@@ -37,5 +55,5 @@ https://music.homyk.space {
roll_keep_for 72h
}
format json
}
}
}

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

View File

@@ -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.getElementById('loginForm').addEventListener('submit', async function (e) {
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);
@@ -46,28 +36,29 @@ document.getElementById('loginForm').addEventListener('submit', async function (
} 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);
});

View File

@@ -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";
}
}
tokenCheck()
document.getElementById('logoutForm').addEventListener('submit', async function (e) {
e.preventDefault();
localStorage.removeItem("token");
sessionStorage.removeItem("token");
tokenCheck();
});
document.addEventListener("DOMContentLoaded", () => {
const form = document.querySelector(".form-info");
form.addEventListener("submit", async (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();
await fetch('/api/logout', {
method: 'POST',
credentials: 'include'
});
window.location.href = '/';
});
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)
// });
try {
const response = await fetch('/api/update', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include', // ← токен идёт через cookie
body: JSON.stringify(guestData)
});
// if (!updateResponse.ok) {
// throw new Error('Ошибка при отправке формы');
// }
if (!response.ok) {
const err = await response.json();
throw new Error(JSON.stringify(err.detail || 'Ошибка при отправке'));
}
// const updateData = await updateResponse.json();
// console.log('Форма успешно отправлена:', updateData);
const data = await response.json();
console.log('Успешно:', data);
alert('Данные сохранены!');
// } catch (err) {
// console.error(err);
// alert('Ошибка: ' + err.message);
// }
} catch (err) {
console.error(err);
alert('Ошибка: ' + err.message);
}
});
});

View File

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