Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Rooms #83

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions fixtures/products.sql
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ VALUES
(4, 'Deactivated by Timestamp', 30000, true, '2024-09-01'),
(5, 'Expensive', 100000, true, NULL),
(6, 'Overflow trigger', 100000000000, true, NULL);

INSERT INTO room_products(room_id, product_id)
VALUES
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(1, 6);

5 changes: 5 additions & 0 deletions fixtures/rooms.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
INSERT INTO rooms(id, name, active, deactivate_after_timestamp)
VALUES
(1, 'Enabled', true, NULL),
(2, 'Inactive', false, NULL),
(3, 'Deactivated by Timestamp', true, '2024-09-01');
26 changes: 26 additions & 0 deletions migrations/0001_initial.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ CREATE TABLE users (
CREATE UNIQUE INDEX users_username_key ON users(LOWER(username));
CREATE UNIQUE INDEX users_email_key ON users(LOWER(email));

CREATE TABLE rooms (
id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
name VARCHAR(128) NOT NULL,
active BOOLEAN NOT NULL,
deactivate_after_timestamp TIMESTAMPTZ
);

CREATE UNIQUE INDEX rooms_name_key ON rooms(LOWER(name));

CREATE TABLE products (
id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
name VARCHAR(128) NOT NULL,
Expand All @@ -17,6 +26,23 @@ CREATE TABLE products (
deactivate_after_timestamp TIMESTAMPTZ
);

CREATE TABLE room_products (
room_id INT NOT NULL,
product_id INT NOT NULL,

PRIMARY KEY(room_id, product_id),

CONSTRAINT fk_room
FOREIGN KEY(room_id)
REFERENCES rooms(id)
ON DELETE CASCADE,

CONSTRAINT fk_product
FOREIGN KEY(product_id)
REFERENCES products(id)
ON DELETE CASCADE
);

CREATE TABLE product_aliases (
alias_name VARCHAR(128) PRIMARY KEY NOT NULL CONSTRAINT lower_case_and_no_whitespace_or_colon CHECK(LENGTH(alias_name) != 0 AND alias_name NOT LIKE '% %' AND alias_name NOT LIKE '%:%' AND alias_name = LOWER(alias_name)),
product_id INT NOT NULL,
Expand Down
15 changes: 14 additions & 1 deletion scripts/sample_data.sql
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
INSERT INTO rooms(id, name, active, deactivate_after_timestamp)
VALUES
(1, 'Alrum', true, NULL),
(2, 'Ølrum', true, NULL),
(3, 'Deactivated', false, NULL),
(4, 'Expired', true, '2024-09-01');

INSERT INTO products(id, name, price, active, deactivate_after_timestamp)
VALUES
(1, 'Øl', 700 , true, NULL),
(2, 'Sodavand', 1200, true, NULL),
(3, 'Søm', 200, false, NULL),
(4, 'Fytteturs Billet', 30000, true, '2024-09-01');

INSERT INTO room_products(room_id, product_id)
VALUES
(1, 1),
(1, 2),
(2, 1);

INSERT INTO product_aliases(alias_name, product_id)
VALUES
('øl', 1),
Expand All @@ -24,4 +37,4 @@ VALUES
(1, 'This is a sample news item', true, NULL),
(2, 'Another sample news item', true, NULL),
(3, 'Deactivated news', false, NULL),
(4, 'Deactivated by timestamp', true, '2024-09-01');
(4, 'Expired', true, '2024-09-01');
67 changes: 53 additions & 14 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use axum::{
body::{Body, Bytes},
debug_handler,
error_handling::HandleErrorLayer,
extract::{Query, Request, State},
extract::{Path, Query, Request, State},
http::{header, response::Parts, HeaderName, HeaderValue, Method, StatusCode, Uri},
middleware::{self, Next},
routing::{get, post},
Expand All @@ -28,6 +28,7 @@ use lru::LruCache;
use protocol::{
buy_request::{BuyError, BuyRequest, BuyResponse},
products::active_products_response::DatabaseError,
rooms::{RoomIdRequest, RoomInfoError, RoomInfoResponse},
users::{UserInfoError, UserInfoResponse, UsernameRequest},
};
use protocol::{
Expand Down Expand Up @@ -95,12 +96,13 @@ fn app(pool: PgPool) -> Router {
};

let router = Router::new()
.route("/", get(index_handler))
.route("/menu/", get(menu_handler))
.route("/:room_id/", get(index_handler))
.route("/:room_id/menu/", get(menu_handler))
.route("/api/products/active", get(get_active_products))
.route("/api/purchase/quickbuy", post(quickbuy_handler))
.route("/api/news/active", get(get_active_news_handler))
.route("/api/users/info", get(get_users_info_handler))
.route("/api/rooms/info", get(get_rooms_info_handler))
.nest_service(
"/static",
ServiceBuilder::new()
Expand Down Expand Up @@ -266,6 +268,7 @@ async fn disable_browser_cache(request: Request, next: Next) -> Response {
#[debug_handler]
async fn get_active_products(
State(state): State<MyState>,
Query(room_id_request): Query<RoomIdRequest>,
) -> ResultJson<ActiveProductsResponse, DatabaseError> {
async {
let products = sqlx::query!(
Expand All @@ -274,11 +277,20 @@ async fn get_active_products(
-- ' ' is an illegal character in aliases so it can be used as a separator
FROM products
LEFT JOIN product_aliases
ON products.id=product_aliases.product_id
WHERE products.active=true AND (products.deactivate_after_timestamp IS NULL OR products.deactivate_after_timestamp > now())
ON products.id = product_aliases.product_id
JOIN room_products
ON products.id = room_products.product_id
JOIN rooms
ON rooms.id = room_products.room_id
WHERE products.active = true
AND (products.deactivate_after_timestamp IS NULL OR products.deactivate_after_timestamp > NOW())
AND rooms.id = $1 -- Replace <ROOM_ID> with the actual room ID
AND rooms.active = true
AND (rooms.deactivate_after_timestamp IS NULL OR rooms.deactivate_after_timestamp > NOW())
GROUP BY products.id, products.name, products.price
ORDER BY products.id
"#)
ORDER BY products.id;
"#,
room_id_request.room_id)
.fetch_all(&state.pool)
.await?;

Expand Down Expand Up @@ -312,7 +324,8 @@ async fn quickbuy_handler(
}
QuickBuyType::MultiBuy { username, products } => {
let (bought_products, product_price_sum, new_user_balance) =
execute_multi_buy_query(&username, &products, &state.pool).await?;
execute_multi_buy_query(&username, &products, buy_request.room_id, &state.pool)
.await?;
Ok(BuyResponse::MultiBuy {
username,
bought_products,
Expand Down Expand Up @@ -371,22 +384,48 @@ async fn get_users_info_handler(
}.await.into()
}

#[debug_handler]
async fn get_rooms_info_handler(
State(state): State<MyState>,
Query(room_request): Query<RoomIdRequest>,
) -> ResultJson<RoomInfoResponse, RoomInfoError> {
async {
let room = sqlx::query!(
r#"
SELECT id, name
FROM rooms
WHERE id = $1 AND active=true AND (deactivate_after_timestamp IS NULL OR deactivate_after_timestamp > now())
"#,
room_request.room_id)
.fetch_optional(&state.pool)
.await?;

let room = room.ok_or(RoomInfoError::InvalidRoom(room_request.room_id))?;
let room_info_response = RoomInfoResponse { room_id: room.id, name: room.name };
Ok(room_info_response)
}.await.into()
}

#[derive(Template)]
#[template(path = "index.html")]
struct IndexTemplate {}
struct IndexTemplate {
pub room_id: i64,
}

#[debug_handler]
async fn index_handler() -> IndexTemplate {
IndexTemplate {}
async fn index_handler(Path(room_id): Path<i64>) -> IndexTemplate {
IndexTemplate { room_id }
}

#[derive(Template)]
#[template(path = "menu.html")]
struct MenuTemplate {}
struct MenuTemplate {
pub room_id: i64,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i64? in src/protocol/buy_request.rs it is a i32?

}

#[debug_handler]
async fn menu_handler() -> MenuTemplate {
MenuTemplate {}
async fn menu_handler(Path(room_id): Path<i64>) -> MenuTemplate {
MenuTemplate { room_id }
}

#[derive(Template)]
Expand Down
1 change: 1 addition & 0 deletions src/protocol.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod buy_request;
pub mod news;
pub mod products;
pub mod rooms;
pub mod users;
1 change: 1 addition & 0 deletions src/protocol/buy_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
#[derive(Deserialize, Serialize)]
pub struct BuyRequest {
pub quickbuy: String,
pub room_id: i32,
}

#[derive(Deserialize, Serialize)]
Expand Down
41 changes: 41 additions & 0 deletions src/protocol/rooms.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use axum::http::StatusCode;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use serde_with::DisplayFromStr;
use thiserror::Error;

use crate::responses::result_json::HttpStatusCode;

#[derive(Debug, Serialize, Deserialize)]
pub struct RoomIdRequest {
pub room_id: i32,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RoomInfoResponse {
pub room_id: i32,
pub name: String,
}

#[serde_as]
#[derive(Error, Debug, Serialize)]
pub enum RoomInfoError {
#[error("database error: {0}")]
DbError(
#[serde_as(as = "DisplayFromStr")]
#[from]
sqlx::Error,
),

#[error("invalid room id: {0}")]
InvalidRoom(i32),
}

impl HttpStatusCode for RoomInfoError {
fn status_code(&self) -> axum::http::StatusCode {
match self {
RoomInfoError::DbError(_) => StatusCode::INTERNAL_SERVER_ERROR,
RoomInfoError::InvalidRoom(_) => StatusCode::BAD_REQUEST,
}
}
}
Loading
Loading