init repo

This commit is contained in:
Lheorvine 2025-05-23 15:25:48 +02:00
commit 154dabfee1
38 changed files with 4179 additions and 0 deletions

1
.env Normal file
View file

@ -0,0 +1 @@
DATABASE_URL="postgres://postgres:secret@localhost/ksiegarnia"

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

3379
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

19
Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "ksiegarnia"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-web = "4.4.0"
actix-cors = "0.7.0"
actix-files = "0.6.2"
env_logger = "0.11"
dotenv = "0.15"
sqlx = { version = "0.7.4", features = ["postgres", "runtime-tokio-native-tls", "macros", "chrono", "bigdecimal"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
log = "0.4"
bcrypt = "0.15"
rust_decimal = { version = "1.37.1", features = ["serde", "db-postgres"] }
bigdecimal = { version = "0.3.0", features = ["serde"] }

View file

@ -0,0 +1,17 @@
-- Tabela użytkowników
CREATE TABLE uzytkownicy (
id SERIAL PRIMARY KEY,
imie VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
haslo VARCHAR(255) NOT NULL,
data_rejestracji TIMESTAMP DEFAULT NOW()
);
-- Tabela książek
CREATE TABLE ksiazki (
id SERIAL PRIMARY KEY,
tytul VARCHAR(255) NOT NULL,
autor VARCHAR(255) NOT NULL,
cena DECIMAL(10, 2) NOT NULL
);

View file

@ -0,0 +1,22 @@
ALTER TABLE ksiazki
ADD COLUMN obraz_url VARCHAR(255);
INSERT INTO ksiazki (tytul, autor, cena, obraz_url) VALUES
('Descent of Angels', 'Mitchel Scanlon', 49.99, 'descent-of-angels.jpg'),
('Horus Rising', 'Dan Abnett', 49.99, 'horus-rising.jpg'),
('False Gods', 'Graham McNeill', 49.99, 'false-gods.jpg'),
('Galaxy in Flames', 'Ben Counter', 49.99, 'galaxy-in-flames.jpg'),
('Fulgrim', 'Graham McNeill', 49.99, 'fulgrim.jpg'),
('Flight of the Eisenstein', 'James Swallow', 49.99, 'flight-of-the-eisenstein.jpg'),
('Echoes of Eternity', 'Aaron Dembski-Bowden', 59.99, 'echoes-of-eternity.jpg'),
('First and Only', 'Dan Abnett', 49.99, 'first-and-only.jpg'),
('The Devastation of Baal', 'Guy Haley', 59.99, 'devastation-of-baal.jpg'),
('Fabius Bile: Primogenitor', 'Joshua Reynolds', 39.99, 'fabius-bile-primogenitor.jpg'),
('Fabius Bile: Clonelord', 'Joshua Reynolds', 39.99, 'fabius-bile-clonelord.jpg'),
('Fabius Bile: Manflayer', 'Joshua Reynolds', 39.99, 'fabius-bile-manflayer.jpg'),
('Ahriman: Exile', 'John French', 39.99, 'ahriman-exile.jpg'),
('Ahriman: Sorcerer', 'John French', 39.99, 'ahriman-sorcerer.jpg'),
('Ahriman: Unchanged', 'John French', 39.99, 'ahriman-unchanged.jpg'),
('Mephiston: Blood of Sanguinius', 'Darius Hinks', 39.99, 'mephiston-blood-of-sanguinius.jpg'),
('Mephiston: Revenant Crusade', 'Darius Hinks', 39.99, 'mephiston-revenant-crusade.jpg'),
('Mephiston: City of Light', 'Darius Hinks', 39.99, 'mephiston-city-of-light.jpg');

View file

@ -0,0 +1,2 @@
ALTER TABLE ksiazki
ALTER COLUMN cena TYPE NUMERIC(10,2);

12
schema.sql Normal file
View file

@ -0,0 +1,12 @@
CREATE TABLE ksiazki (
id SERIAL PRIMARY KEY,
tytul VARCHAR(255) NOT NULL,
autor VARCHAR(255) NOT NULL,
cena DECIMAL(10, 2) NOT NULL
);
CREATE TABLE uzytkownicy (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
haslo VARCHAR(255) NOT NULL
);

100
src/auth.rs Normal file
View file

@ -0,0 +1,100 @@
use actix_web::{post, web, HttpResponse, Responder};
use bcrypt::{hash, verify, DEFAULT_COST};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct RegistrationData {
email: String,
haslo: String,
imie: String,
#[serde(rename = "confirmPassword")]
confirm_password: String,
}
#[post("/rejestracja")]
async fn rejestracja(
form: web::Json<RegistrationData>,
pool: web::Data<sqlx::PgPool>,
) -> impl Responder {
// Walidacja hasła
if form.haslo.len() < 8 {
return HttpResponse::BadRequest().body("Hasło musi mieć minimum 8 znaków");
}
// Sprawdzenie, czy hasła się zgadzają
if form.haslo != form.confirm_password {
return HttpResponse::BadRequest().body("Hasła nie są identyczne");
}
// Hashowanie hasła
let hashed_password = match hash(&form.haslo, DEFAULT_COST) {
Ok(h) => h,
Err(_) => return HttpResponse::InternalServerError().finish(),
};
// Zapisz do bazy danych
match sqlx::query!(
r#"
INSERT INTO uzytkownicy (email, haslo, imie)
VALUES ($1, $2, $3)
"#,
form.email,
hashed_password,
form.imie
)
.execute(pool.get_ref())
.await
{
Ok(_) => HttpResponse::Created().body("Konto utworzone pomyślnie"),
Err(e) => {
if e.to_string().contains("duplicate key value") {
HttpResponse::Conflict().body("Email jest już zarejestrowany")
} else {
HttpResponse::InternalServerError().finish()
}
}
}
}
#[derive(Deserialize)]
struct LoginData {
email: String,
haslo: String,
}
#[derive(Serialize)]
struct LoginResponse {
token: String,
imie: String,
}
#[post("/login")]
async fn login(
form: web::Json<LoginData>,
pool: web::Data<sqlx::PgPool>,
) -> impl Responder {
let user = match sqlx::query!(
"SELECT id, haslo, imie FROM uzytkownicy WHERE email = $1",
form.email
)
.fetch_optional(pool.get_ref())
.await
{
Ok(Some(u)) => u,
Ok(None) => return HttpResponse::Unauthorized().body("Nieprawidłowe dane"),
Err(_) => return HttpResponse::InternalServerError().finish(),
};
match verify(&form.haslo, &user.haslo) {
Ok(true) => {
// W praktyce użyj JWT lub innego mechanizmu autentykacji
let dummy_token = format!("user-{}-token", user.id);
HttpResponse::Ok().json(LoginResponse {
token: dummy_token,
imie: user.imie,
})
}
_ => HttpResponse::Unauthorized().body("Nieprawidłowe hasło"),
}
}

89
src/main.rs Normal file
View file

@ -0,0 +1,89 @@
mod auth;
use actix_cors::Cors;
use actix_files::Files;
use actix_web::{
get, http::header, web, App, HttpResponse, HttpServer, Responder,
};
use dotenv::dotenv;
use env_logger::{Builder, Env};
use sqlx::postgres::PgPoolOptions;
use rust_decimal::Decimal;
use bigdecimal::BigDecimal;
use serde_json::json;
#[derive(sqlx::FromRow, serde::Serialize)]
struct Book {
id: i32,
tytul: String,
autor: String,
cena: BigDecimal,
obraz_url: Option<String>
}
#[get("/api/ksiazki")]
async fn get_ksiazki(pool: web::Data<sqlx::PgPool>) -> impl Responder {
match sqlx::query_as!(
Book,
r#"SELECT id, tytul, autor, cena, obraz_url FROM ksiazki"#
)
.fetch_all(pool.get_ref())
.await
{
Ok(books) => HttpResponse::Ok().json(books),
Err(e) => {
log::error!("Błąd bazy danych: {:?}", e);
HttpResponse::InternalServerError().body(format!("Błąd: {}", e))
}
}
}
#[get("/api/check-auth")]
async fn check_auth() -> impl Responder {
HttpResponse::Ok().json(json!({
"authenticated": false,
"user": null
}))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// Inicjalizacja loggera
Builder::from_env(Env::default().default_filter_or("debug")).init();
// Ładowanie zmiennych środowiskowych
dotenv().ok();
let database_url = std::env::var("DATABASE_URL")
.expect("DATABASE_URL must be set in .env");
// Utwórz pulę połączeń
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(&database_url)
.await
.expect("Failed to create pool");
HttpServer::new(move || {
let cors = Cors::default()
.allow_any_origin()
.allowed_methods(vec!["GET", "POST"])
.allowed_headers(vec![
header::CONTENT_TYPE,
header::AUTHORIZATION,
header::ACCEPT,
header::HeaderName::from_static("content-type"),
]);
App::new()
.app_data(web::Data::new(pool.clone())) // Dodaj pulę jako globalny stan
.wrap(cors)
.wrap(actix_web::middleware::Logger::default())
.service(get_ksiazki)
.service(auth::rejestracja)
.service(auth::login)
.service(Files::new("/", "./static").index_file("index.html"))
})
.bind("0.0.0.0:7999")?
.run()
.await
}

66
static/css/styles.css Normal file
View file

@ -0,0 +1,66 @@
:root {
--bg-dark: #072C24;
--text-gold: #E3CB9A;
--accent-blue: #78DBFF;
--footer-text: #93B8B1;
}
.dark-theme {
background-color: var(--bg-dark);
color: var(--text-gold);
}
.navbar-brand {
font-family: 'Lobster', cursive;
font-size: 2.2rem;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
.nav-link {
font-family: 'Inter', sans-serif;
font-weight: 500;
transition: all 0.3s ease;
}
.auth-container a {
color: var(--accent-blue) !important;
text-decoration: none;
}
footer {
background-color: #09342b;
padding: 2rem 0;
margin-top: 4rem;
font-family: 'Inter', sans-serif;
color: var(--footer-text);
}
.card {
background: linear-gradient(145deg, #08352c 0%, #062a23 100%);
border: 1px solid #1c4d42;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
.btn-gothic {
background-color: #1c4d42;
color: var(--text-gold);
border: 1px solid #2a6b5e;
}
.btn-gothic:hover {
background-color: #2a6b5e;
color: #fff;
}
.auth-container {
background: rgba(7, 44, 36, 0.9);
border: 2px solid #2a6b5e;
backdrop-filter: blur(5px);
}
.auth-container h2 {
font-family: 'Lobster', cursive;
text-align: center;
font-size: 2.5rem;
margin-bottom: 2rem;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
static/images/fulgrim.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

49
static/index.html Normal file
View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dark Athenaeum</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/styles.css" rel="stylesheet">
<link href="https://fonts.cdnfonts.com/css/old-english-text-mt" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Lobster&family=Inter:wght@400;500;700&display=swap" rel="stylesheet">
</head>
<body class="dark-theme">
<nav class="navbar navbar-expand-lg navbar-dark bg-black">
<div class="navbar-nav">
<!-- Dla niezalogowanych -->
<div class="anonymous-links">
<a class="nav-link" href="/login.html">Logowanie</a>
<a class="nav-link" href="/register.html">Rejestracja</a>
</div>
<!-- Dla zalogowanych -->
<div class="user-links" style="display: none;">
<a class="nav-link" href="/profile.html">Profil</a>
<a class="nav-link" href="#" id="logoutLink">Wyloguj</a>
</div>
<a class="nav-link" href="/cart.html">
<i class="bi bi-basket"></i> Koszyk
</a>
</div>
</nav>
<main class="container py-5">
<h2 class="text-center mb-5 neon-title">NOWOŚCI</h2>
<div class="row g-4" id="books-container">
<!-- Dynamicznie ładowane książki -->
<div class="col-12 text-center">
<div class="spinner-border text-danger" role="status">
<span class="visually-hidden">Ładowanie...</span>
</div>
</div>
</div>
</main>
<script src="/js/main.js"></script>
</body>
</html>

135
static/js/main.js Normal file
View file

@ -0,0 +1,135 @@
function updateNavVisibility() {
const token = localStorage.getItem('token');
document.querySelectorAll('.anonymous-links, .user-links').forEach(el => {
el.style.display = token ? 'none' : 'block';
});
document.querySelector('.user-links').style.display = token ? 'block' : 'none';
}
document.getElementById('logoutLink')?.addEventListener('click', (e) => {
e.preventDefault();
localStorage.removeItem('token');
localStorage.removeItem('imie');
window.location.href = '/';
});
document.addEventListener('DOMContentLoaded', () => {
updateNavVisibility();
// Aktualizuj awatar/imię w nagłówku
const userName = localStorage.getItem('imie');
if(userName) {
const profileLink = document.querySelector('a[href="/profile.html"]');
if(profileLink) profileLink.innerHTML = `👤 ${userName}`;
}
});
async function loadUserData() {
const response = await fetch('/api/check-auth');
const data = await response.json();
updateNavVisibility(data.authenticated);
}
document.getElementById('loginForm')?.addEventListener('submit', async (e) => {
e.preventDefault();
const email = document.getElementById('loginEmail').value;
const password = document.getElementById('loginPassword').value;
const response = await fetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, haslo: password }),
});
const data = await response.json();
if (response.ok) {
localStorage.setItem('token', data.token);
localStorage.setItem('imie', data.imie);
window.location.href = '/';
} else {
alert(data.message || 'Logowanie nieudane');
}
});
document.getElementById('registerForm')?.addEventListener('submit', async (e) => {
e.preventDefault();
const name = document.getElementById('registerName').value;
const email = document.getElementById('registerEmail').value;
const password = document.getElementById('registerPassword').value;
const confirmPassword = document.getElementById('registerConfirmPassword').value;
if (password !== confirmPassword) {
alert('Hasła nie są identyczne');
return;
}
const response = await fetch('/rejestracja', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ imie: name, email, haslo: password, confirmPassword }),
});
if (response.ok) {
alert('Rejestracja udana');
window.location.href = '/login.html';
} else {
const data = await response.text();
alert(data || 'Rejestracja nieudana');
}
});
async function loadBooks() {
try {
const response = await fetch('/api/ksiazki');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const books = await response.json();
const booksContainer = document.getElementById('books-container');
booksContainer.innerHTML = '';
books.forEach(book => {
const bookElement = document.createElement('div');
bookElement.className = 'col-md-4';
bookElement.innerHTML = `
<div class="card mb-4">
<img src="${book.obraz_url || 'https://via.placeholder.com/150'}" class="card-img-top" alt="${book.tytul}">
<div class="card-body">
<h5 class="card-title">${book.tytul}</h5>
<p class="card-text">${book.autor}</p>
<p class="card-text">${book.cena} PLN</p>
<button class="btn btn-primary">Dodaj do koszyka</button>
</div>
</div>
`;
booksContainer.appendChild(bookElement);
});
} catch (error) {
console.error('Error loading books:', error);
const booksContainer = document.getElementById('books-container');
booksContainer.innerHTML = '<div class="col-12 text-center"><p>Wystąpił błąd podczas ładowania książek.</p></div>';
}
}
document.addEventListener('DOMContentLoaded', () => {
if (document.getElementById('books-container')) {
loadBooks();
}
});
document.addEventListener('DOMContentLoaded', () => {
if (document.getElementById('books-container')) {
loadBooks();
}
});
document.addEventListener('DOMContentLoaded', () => {
if (document.getElementById('books-container')) {
loadBooks();
}
});

53
static/login.html Normal file
View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Logowanie</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/styles.css" rel="stylesheet">
<link href="https://fonts.cdnfonts.com/css/old-english-text-mt" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Lobster&family=Inter:wght@400;500;700&display=swap" rel="stylesheet">
</head>
<body class="dark-theme">
<nav class="navbar navbar-expand-lg navbar-dark bg-black">
<div class="navbar-nav">
<!-- Dla niezalogowanych -->
<div class="anonymous-links">
<a class="nav-link" href="/login.html">Logowanie</a>
<a class="nav-link" href="/register.html">Rejestracja</a>
</div>
<!-- Dla zalogowanych -->
<div class="user-links" style="display: none;">
<a class="nav-link" href="/profile.html">Profil</a>
<a class="nav-link" href="#" id="logoutLink">Wyloguj</a>
</div>
<a class="nav-link" href="/cart.html">
<i class="bi bi-basket"></i> Koszyk
</a>
</div>
</nav>
<div class="auth-container">
<h2 class="neon-title mb-4">LOGOWANIE</h2>
<form id="loginForm">
<div class="mb-3">
<input type="email" class="form-control dark-input"
placeholder="Email" required id="loginEmail">
</div>
<div class="mb-3">
<input type="password" class="form-control dark-input"
placeholder="Hasło" required id="loginPassword">
</div>
<button type="submit" class="btn btn-gothic w-100">ZALOGUJ SIĘ</button>
</form>
<div class="text-center mt-3">
<a href="/register.html" class="text-danger">Nie masz konta? Zarejestruj się</a>
</div>
</div>
<script src="/js/main.js"></script>
</body>
</html>

57
static/register.html Normal file
View file

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Rejestracja</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/styles.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Lobster&family=Inter:wght@400;500;700&display=swap" rel="stylesheet">
</head>
<body class="dark-theme">
<nav class="navbar navbar-expand-lg navbar-dark bg-black">
<div class="navbar-nav">
<!-- Dla niezalogowanych -->
<div class="anonymous-links">
<a class="nav-link" href="/login.html">Logowanie</a>
<a class="nav-link" href="/register.html">Rejestracja</a>
</div>
<!-- Dla zalogowanych -->
<div class="user-links" style="display: none;">
<a class="nav-link" href="/profile.html">Profil</a>
<a class="nav-link" href="#" id="logoutLink">Wyloguj</a>
</div>
<a class="nav-link" href="/cart.html">
<i class="bi bi-basket"></i> Koszyk
</a>
</div>
</nav>
<div class="auth-container">
<h2 class="neon-title mb-4">REJESTRACJA</h2>
<form id="registerForm">
<div class="mb-3">
<input type="text" class="form-control dark-input"
placeholder="Imię" required id="registerName">
</div>
<div class="mb-3">
<input type="email" class="form-control dark-input"
placeholder="Email" required id="registerEmail">
</div>
<div class="mb-3">
<input type="password" class="form-control dark-input"
placeholder="Hasło (min. 8 znaków)" required minlength="8" id="registerPassword">
</div>
<div class="mb-3">
<input type="password" class="form-control dark-input"
placeholder="Powtórz hasło" required id="registerConfirmPassword">
</div>
<button type="submit" class="btn btn-gothic w-100">ZAREJESTRUJ SIĘ</button>
</form>
</div>
<script src="/js/main.js"></script>
</body>
</html>

142
struktura_bazy.sql Normal file
View file

@ -0,0 +1,142 @@
--
-- PostgreSQL database dump
--
-- Dumped from database version 16.8
-- Dumped by pg_dump version 16.8
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- Name: ksiazki; Type: TABLE; Schema: public; Owner: postgres
--
CREATE TABLE public.ksiazki (
id integer NOT NULL,
tytul character varying(255) NOT NULL,
autor character varying(255) NOT NULL,
cena numeric(10,2) NOT NULL
);
ALTER TABLE public.ksiazki OWNER TO postgres;
--
-- Name: ksiazki_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
--
CREATE SEQUENCE public.ksiazki_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.ksiazki_id_seq OWNER TO postgres;
--
-- Name: ksiazki_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
--
ALTER SEQUENCE public.ksiazki_id_seq OWNED BY public.ksiazki.id;
--
-- Name: uzytkownicy; Type: TABLE; Schema: public; Owner: postgres
--
CREATE TABLE public.uzytkownicy (
id integer NOT NULL,
email character varying(255) NOT NULL,
haslo character varying(255) NOT NULL
);
ALTER TABLE public.uzytkownicy OWNER TO postgres;
--
-- Name: uzytkownicy_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
--
CREATE SEQUENCE public.uzytkownicy_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.uzytkownicy_id_seq OWNER TO postgres;
--
-- Name: uzytkownicy_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
--
ALTER SEQUENCE public.uzytkownicy_id_seq OWNED BY public.uzytkownicy.id;
--
-- Name: ksiazki id; Type: DEFAULT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public.ksiazki ALTER COLUMN id SET DEFAULT nextval('public.ksiazki_id_seq'::regclass);
--
-- Name: uzytkownicy id; Type: DEFAULT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public.uzytkownicy ALTER COLUMN id SET DEFAULT nextval('public.uzytkownicy_id_seq'::regclass);
--
-- Name: ksiazki ksiazki_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public.ksiazki
ADD CONSTRAINT ksiazki_pkey PRIMARY KEY (id);
--
-- Name: uzytkownicy uzytkownicy_email_key; Type: CONSTRAINT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public.uzytkownicy
ADD CONSTRAINT uzytkownicy_email_key UNIQUE (email);
--
-- Name: uzytkownicy uzytkownicy_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public.uzytkownicy
ADD CONSTRAINT uzytkownicy_pkey PRIMARY KEY (id);
--
-- Name: SCHEMA public; Type: ACL; Schema: -; Owner: pg_database_owner
--
GRANT USAGE ON SCHEMA public TO ksiegarnia_user;
--
-- PostgreSQL database dump complete
--

0
struktura_bazy.txt Normal file
View file

8
templates/dashboard.html Normal file
View file

@ -0,0 +1,8 @@
<!DOCTYPE html>
<html lang="pl">
<head><meta charset="UTF-8"><title>Dashboard</title></head>
<body>
<h1>Witaj, użytkowniku {{ user_id }}!</h1>
<a href="/logout">Wyloguj się</a>
</body>
</html>

13
templates/login.html Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="pl">
<head><meta charset="UTF-8"><title>Logowanie</title></head>
<body>
<h1>Logowanie</h1>
<form action="/login" method="post">
<label>Email:<input type="email" name="email" required></label><br>
<label>Password:<input type="password" name="password" required></label><br>
<button type="submit">Zaloguj się</button>
</form>
<a href="/register">Nie masz konta? Zarejestruj się</a>
</body>
</html>

14
templates/register.html Normal file
View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="pl">
<head><meta charset="UTF-8"><title>Rejestracja</title></head>
<body>
<h1>Rejestracja</h1>
<form action="/register" method="post">
<label>Username:<input type="text" name="username" required></label><br>
<label>Email:<input type="email" name="email" required></label><br>
<label>Password:<input type="password" name="password" required></label><br>
<button type="submit">Zarejestruj się</button>
</form>
<a href="/login">Mam już konto. Zaloguj się</a>
</body>
</html>