Skip to content

Commit

Permalink
Introduce seeding and basic endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
lucemans committed Jul 27, 2024
1 parent c0728c9 commit ccc1120
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 10 deletions.
29 changes: 29 additions & 0 deletions engine/Cargo.lock

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

6 changes: 4 additions & 2 deletions engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@ edition = "2021"

[dependencies]
async-std = { version = "1.12.0", features = ["attributes", "tokio1"] }
bigdecimal = { version = "0.4.5", features = ["serde"] }
chrono = "0.4.38"
dotenv = "0.15.0"
dotenvy = "0.15.7"
openid = "0.14.0"
poem = "3.0.4"
poem-openapi = { version = "5.0.3", features = ["email", "email_address", "redoc", "static-files"] }
poem-openapi = { version = "5.0.3", features = ["chrono", "uuid", "email", "email_address", "redoc", "static-files"] }
reqwest = "0.12.5"
serde = "1.0.204"
serde_json = "1.0.120"
serde_with = { version = "3.9.0", features = ["json", "chrono"] }
sqlx = { version = "0.7.4", features = ["runtime-async-std", "tls-rustls", "postgres", "uuid", "chrono", "json"] }
sqlx = { version = "0.7.4", features = ["runtime-async-std", "tls-rustls", "postgres", "uuid", "chrono", "json", "ipnetwork"] }
terminal-banner = "0.4.1"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
url = { version = "2.5.2", features = ["serde"] }

[dependencies.uuid]
version = "1.10.0"
Expand Down
2 changes: 1 addition & 1 deletion engine/migrations/0002_sessions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS sessions
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id INT NOT NULL,
user_agent VARCHAR(255) NOT NULL,
user_ip VARCHAR(255) NOT NULL,
user_ip INET NOT NULL,
last_access TIMESTAMPTZ NOT NULL DEFAULT NOW(),
valid BOOLEAN NOT NULL DEFAULT TRUE
);
27 changes: 27 additions & 0 deletions engine/migrations/0003_property.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-- Introduce basic property data
CREATE TABLE IF NOT EXISTS properties
(
id INT PRIMARY KEY,
owner_id INT NOT NULL,
product_id INT NOT NULL,
name VARCHAR(255),
media INT[],
created TIMESTAMPTZ NOT NULL DEFAULT NOW(),
modified TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS products
(
id INT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
media INT[],
tweakers_id INT[],
ean VARCHAR(255)[],
upc VARCHAR(255)[],
sku VARCHAR(255)[],
created TIMESTAMPTZ NOT NULL DEFAULT NOW(),
modified TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

INSERT INTO properties (id, owner_id, product_id) VALUES (4, 1, 1) ON CONFLICT DO NOTHING;
INSERT INTO products (id, name, media, tweakers_id, ean, sku) VALUES (1, 'Anker 737 (PowerCore 24k)', ARRAY[1], ARRAY[1855004], ARRAY['0194644098728'], ARRAY['a1289', 'A1289011']) ON CONFLICT DO NOTHING;
9 changes: 9 additions & 0 deletions engine/migrations/0004_media.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS media
(
id INT PRIMARY KEY,
description VARCHAR(255),
url VARCHAR(255) NOT NULL,
created TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

INSERT INTO media (id, description, url, created) VALUES (1, 'My Cool Property', 'https://media.s-bol.com/VZVXoqVokWkv/z700v2/960x1200.jpg', NOW());
8 changes: 6 additions & 2 deletions engine/src/auth/session.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use std::net::IpAddr;

use poem_openapi::Object;
use serde::{Deserialize, Serialize};
use sqlx::types::chrono;
use uuid::Uuid;

use crate::database::Database;

#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize)]
#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Object)]
pub struct SessionState {
pub id: Uuid,
pub user_id: i32,
pub user_agent: String,
pub user_ip: IpAddr,
pub last_access: chrono::DateTime<chrono::Utc>,
pub valid: bool,
}
Expand All @@ -17,7 +21,7 @@ impl SessionState {
pub async fn new(
user_id: i32,
user_agent: &str,
user_ip: &str,
user_ip: &IpAddr,
database: &Database,
) -> Result<Self, sqlx::Error> {
let session = sqlx::query_as::<_, SessionState>(
Expand Down
25 changes: 25 additions & 0 deletions engine/src/models/media.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use poem_openapi::Object;
use serde::{Deserialize, Serialize};

use crate::database::Database;

#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Object)]
pub struct Media {
/// Randomly generated ID
pub id: i32,
/// Alt text for the image
pub description: String,
/// URL of the image
pub url: String,
}

impl Media {
pub async fn get_by_id(id: i32, database: &Database) -> Result<Self, sqlx::Error> {
let media = sqlx::query_as::<_, Self>("SELECT * FROM media WHERE id = $1")
.bind(id)
.fetch_one(&database.pool)
.await?;

Ok(media)
}
}
3 changes: 3 additions & 0 deletions engine/src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
pub mod user_data;
pub mod property;
pub mod product;
pub mod media;
33 changes: 33 additions & 0 deletions engine/src/models/product.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use poem_openapi::Object;
use serde::{Deserialize, Serialize};

use crate::database::Database;

#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Object, Default)]
pub struct Product {
/// Cherry Picked Identifier
pub id: i32,
/// Name of the product
pub name: String,
/// Media associated with the product
pub media: Vec<i32>,
/// Tweakers ID
pub tweakers_id: Option<Vec<i32>>,
/// EANs
pub ean: Option<Vec<String>>,
/// UPCs
pub upc: Option<Vec<String>>,
/// SKUs
pub sku: Option<Vec<String>>,
}

impl Product {
pub async fn get_by_id(id: i32, database: &Database) -> Result<Self, sqlx::Error> {
let product = sqlx::query_as::<_, Self>("SELECT * FROM products WHERE id = $1")
.bind(id)
.fetch_one(&database.pool)
.await?;

Ok(product)
}
}
39 changes: 39 additions & 0 deletions engine/src/models/property.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use poem_openapi::Object;
use serde::{Deserialize, Serialize};
use sqlx::types::chrono;

use crate::database::Database;

#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Object, Default)]
pub struct Property {
pub id: i32,
pub owner_id: i32,
pub product_id: i32,
pub name: Option<String>,
pub media: Option<Vec<i32>>,
pub created: chrono::DateTime<chrono::Utc>,
pub modified: chrono::DateTime<chrono::Utc>,
}

impl Property {
pub async fn get_by_owner_id(
owner_id: i32,
database: &Database,
) -> Result<Vec<Self>, sqlx::Error> {
let properties = sqlx::query_as::<_, Self>("SELECT * FROM properties WHERE owner_id = $1")
.bind(owner_id)
.fetch_all(&database.pool)
.await?;

Ok(properties)
}

pub async fn get_by_id(id: i32, database: &Database) -> Result<Self, sqlx::Error> {
let property = sqlx::query_as::<_, Self>("SELECT * FROM properties WHERE id = $1")
.bind(id)
.fetch_one(&database.pool)
.await?;

Ok(property)
}
}
2 changes: 1 addition & 1 deletion engine/src/routes/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ pub async fn callback(
.unwrap();

let user_agent = headers.get("user-agent").unwrap().to_str().unwrap();
let user_ip = ip.0.unwrap().to_string();
let user_ip = ip.0.unwrap();

let session = SessionState::new(user.id, user_agent, &user_ip, &state.database)
.await
Expand Down
56 changes: 52 additions & 4 deletions engine/src/routes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use std::sync::Arc;

use poem::{
get, handler, listener::TcpListener, middleware::CookieJarManager, web::Html, EndpointExt,
Route, Server,
get, handler,
listener::TcpListener,
middleware::CookieJarManager,
web::{Data, Html, Path},
EndpointExt, Route, Server,
};
use poem_openapi::{param::Query, payload::PlainText, OpenApi, OpenApiService};

use crate::state::AppState;
use crate::{models::{media::Media, product::Product, property::Property}, state::AppState};

pub mod auth;

Expand All @@ -22,6 +25,48 @@ impl Api {
None => PlainText("Hello, World!".to_string()),
}
}

#[oai(path = "/properties", method = "get")]
async fn get_properties(
&self,
state: Data<&Arc<AppState>>,
owner_id: Query<Option<i32>>,
) -> poem_openapi::payload::Json<Vec<Property>> {
let owner_id = owner_id.0.unwrap_or(0);

let properties = Property::get_by_owner_id(owner_id, &state.database)
.await
.unwrap();

poem_openapi::payload::Json(properties)
}

#[oai(path = "/media/:media_id", method = "get")]
async fn get_media(&self, state: Data<&Arc<AppState>>, media_id: Path<i32>) -> poem_openapi::payload::Json<Media> {
let media = Media::get_by_id(media_id.0, &state.database)
.await
.unwrap();

poem_openapi::payload::Json(media)
}

#[oai(path = "/product/:product_id", method = "get")]
async fn get_product(&self, state: Data<&Arc<AppState>>, product_id: Path<i32>) -> poem_openapi::payload::Json<Product> {
let product = Product::get_by_id(product_id.0, &state.database)
.await
.unwrap();

poem_openapi::payload::Json(product)
}

#[oai(path = "/property/:property_id", method = "get")]
async fn get_property(&self, state: Data<&Arc<AppState>>, property_id: Path<i32>) -> poem_openapi::payload::Json<Property> {
let property = Property::get_by_id(property_id.0, &state.database)
.await
.unwrap();

poem_openapi::payload::Json(property)
}
}

// returns the html from the index.html file
Expand All @@ -41,7 +86,10 @@ pub async fn serve(state: AppState) -> Result<(), poem::Error> {
let app = Route::new()
.at("/login", get(auth::login))
.at("/me", get(auth::me))
.at("/sessions", get(auth::get_sessions).delete(auth::delete_sessions))
.at(
"/sessions",
get(auth::get_sessions).delete(auth::delete_sessions),
)
.at("/callback", get(auth::callback))
.nest("/api", api_service)
.nest("/openapi.json", spec)
Expand Down

0 comments on commit ccc1120

Please sign in to comment.