initial commit
This commit is contained in:
3
apps/backend/.env.example
Normal file
3
apps/backend/.env.example
Normal file
@@ -0,0 +1,3 @@
|
||||
DATABASE_URL=postgres://postgres:postgres@localhost:5432/bacchus
|
||||
JWT_SECRET=<secret here>
|
||||
RUST_LOG=bacchus=debug,axum=info,tower_http=info
|
2497
apps/backend/Cargo.lock
generated
Normal file
2497
apps/backend/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
apps/backend/Cargo.toml
Normal file
17
apps/backend/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "bacchus"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
axum = { version = "0.8.4", features = ["macros","json"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls","postgres","macros","uuid","chrono","migrate"] }
|
||||
uuid = { version = "1", features = ["serde","v4"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
tower-http = { version = "0.6.6", features = ["cors","trace"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
anyhow = "1"
|
63
apps/backend/migrations/0001_init.sql
Normal file
63
apps/backend/migrations/0001_init.sql
Normal file
@@ -0,0 +1,63 @@
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
display_name TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL DEFAULT 'user' CHECK (role IN ('user','admin')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE products (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
price_cents INT NOT NULL CHECK (price_cents >= 0),
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE ledger (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
amount_cents INT NOT NULL,
|
||||
kind TEXT NOT NULL CHECK (kind IN ('topup','purchase','adjustment')),
|
||||
ref_id UUID,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE orders (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
total_cents INT NOT NULL CHECK (total_cents >= 0),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE order_items (
|
||||
order_id UUID NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
|
||||
product_id UUID NOT NULL REFERENCES products(id),
|
||||
qty INT NOT NULL CHECK (qty > 0),
|
||||
price_cents INT NOT NULL CHECK (price_cents >= 0),
|
||||
PRIMARY KEY (order_id, product_id)
|
||||
);
|
||||
|
||||
CREATE TABLE inventory_movements (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
product_id UUID NOT NULL REFERENCES products(id),
|
||||
qty INT NOT NULL,
|
||||
reason TEXT NOT NULL CHECK (reason IN ('purchase','consumption','correction')),
|
||||
note TEXT,
|
||||
created_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE VIEW user_balances AS
|
||||
SELECT user_id, COALESCE(SUM(amount_cents),0) AS balance_cents
|
||||
FROM ledger GROUP BY user_id;
|
||||
|
||||
CREATE VIEW product_stock AS
|
||||
SELECT p.id AS product_id, COALESCE(SUM(m.qty),0) AS stock
|
||||
FROM products p
|
||||
LEFT JOIN inventory_movements m ON m.product_id = p.id
|
||||
GROUP BY p.id;
|
14
apps/backend/src/main.rs
Normal file
14
apps/backend/src/main.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use axum::{
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// build our application with a single route
|
||||
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
|
||||
|
||||
// run our app with hyper, listening globally on port 3000
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
Reference in New Issue
Block a user