diff --git a/.env.template b/.env.template index 9d7e56f..abde7ce 100644 --- a/.env.template +++ b/.env.template @@ -1,9 +1,10 @@ DB_USER=postgres DB_PASSWORD=supersecurepassword123987 DB_NAME=advent -DATABASE_URL="postgresql://${DB_USER}:${DB_PASSWORD}@localhost:5432/${DB_NAME}" -DOCKER_DB_URL="postgresql://${DB_USER}:${DB_PASSWORD}@host.docker.internal:5432/${DB_NAME}" -REDIS_URL=redis://127.0.0.1:6379/ +DATABASE_URL="postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}" +#DATABASE_URL="postgresql://${DB_USER}:${DB_PASSWORD}@localhost:5432/${DB_NAME}" +REDIS_URL=redis://redis:6379/ +#REDIS_URL=redis://127.0.0.1:6379/ PROD=true # Set's the hostname for oauth #OAUTH_HOST=advent.codes \ No newline at end of file diff --git a/.gitignore b/.gitignore index 88c43a7..6397790 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /target .idea .env +.env.docker at-advent.db at-advent.db-shm at-advent.db-wal diff --git a/.sqlx/query-5aaab1a1aee88cf73cb3cc52750ea2cd5b7a731a754608cfad7b1c60ebf55e12.json b/.sqlx/query-5aaab1a1aee88cf73cb3cc52750ea2cd5b7a731a754608cfad7b1c60ebf55e12.json new file mode 100644 index 0000000..7958533 --- /dev/null +++ b/.sqlx/query-5aaab1a1aee88cf73cb3cc52750ea2cd5b7a731a754608cfad7b1c60ebf55e12.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT time_challenge_one_completed, time_challenge_two_completed\n FROM challenges\n WHERE user_did = $1 AND day = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "time_challenge_one_completed", + "type_info": "Timestamptz" + }, + { + "ordinal": 1, + "name": "time_challenge_two_completed", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + "Int4" + ] + }, + "nullable": [ + true, + true + ] + }, + "hash": "5aaab1a1aee88cf73cb3cc52750ea2cd5b7a731a754608cfad7b1c60ebf55e12" +} diff --git a/.sqlx/query-c1bdff20378acbf869de74c8a5e60c7c4f13b87d64c84d78436576657319170c.json b/.sqlx/query-c1bdff20378acbf869de74c8a5e60c7c4f13b87d64c84d78436576657319170c.json new file mode 100644 index 0000000..c867bbf --- /dev/null +++ b/.sqlx/query-c1bdff20378acbf869de74c8a5e60c7c4f13b87d64c84d78436576657319170c.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT day, time_challenge_one_completed, time_challenge_two_completed\n FROM challenges\n WHERE user_did = $1 AND day BETWEEN 1 AND 25\n ORDER BY day", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "day", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "time_challenge_one_completed", + "type_info": "Timestamptz" + }, + { + "ordinal": 2, + "name": "time_challenge_two_completed", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + true, + true + ] + }, + "hash": "c1bdff20378acbf869de74c8a5e60c7c4f13b87d64c84d78436576657319170c" +} diff --git a/Justfile b/Justfile new file mode 100755 index 0000000..23e8a88 --- /dev/null +++ b/Justfile @@ -0,0 +1,10 @@ +#!/usr/bin/env just --justfile + +release: + docker buildx build \ + --platform linux/arm64,linux/amd64 \ + --tag fatfingers23/at_advent_web:latest \ + --tag fatfingers23/at_advent:0.0.1 \ + --file web/Dockerfile \ + --builder desktop-linux \ + --push . diff --git a/compose.dev.yml b/compose.dev.yml index a0e3d2e..d0b3e4e 100644 --- a/compose.dev.yml +++ b/compose.dev.yml @@ -1,4 +1,17 @@ services: + # app: + # build: + # context: . + # dockerfile: ./web/Dockerfile + # env_file: + # - .env.docker + # ports: + # - '${FORWARD_APP_PORT:-3000}:3000' + # depends_on: + # - postgres + # - redis + # networks: + # - advent-network postgres: image: postgres:17.6 restart: unless-stopped diff --git a/web/Dockerfile b/web/Dockerfile index aaae935..913b085 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,4 +1,22 @@ -FROM ubuntu:latest -LABEL authors="baileytownsend" +# Build stage +FROM rust:1.87-bookworm AS builder -ENTRYPOINT ["top", "-b"] \ No newline at end of file +WORKDIR /app +COPY . /app + +# Build the web binary +ENV SQLX_OFFLINE=true +RUN cargo build --release --package web + +# Runtime stage +FROM debian:bookworm-slim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=builder /app/target/release/web /usr/local/bin/web + +EXPOSE 3000 + +CMD ["web"] diff --git a/web/build.rs b/web/build.rs deleted file mode 100644 index f640a01..0000000 --- a/web/build.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::process::Command; - -fn main() { - std::fs::create_dir_all("build").expect("failed to create build directory"); - - Command::new("npx") - .args([ - "@tailwindcss/cli", - "-m", - "-i", - "assets/css/base.css", - "-o", - "public/css/style.css", - ]) - .status() - .expect("failed to run tailwindcss"); - - // Command::new("bun") - // .args([ - // "build", - // "--minify", - // "--outdir=build", - // "--entry-naming", - // "[name].[hash].[ext]", - // "--asset-naming", - // "[name].[hash].[ext]", - // "./assets/scripts/index.ts", - // ]) - // .status() - // .expect("failed to run bun"); - - copy_files("public"); -} - -fn copy_files(dir: &str) { - for entry in std::fs::read_dir(dir).expect("failed to read dir `public`") { - let entry = entry.expect("failed to read entry"); - - if entry.file_type().unwrap().is_dir() { - copy_files(entry.path().to_str().unwrap()); - } else { - let path = entry.path(); - let filename = path.file_name().unwrap().to_str().unwrap(); - let dest = format!("build/{}", filename); - - std::fs::copy(path, dest).expect("failed to copy file"); - } - } -} diff --git a/web/src/main.rs b/web/src/main.rs index 7332edd..445859f 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -34,7 +34,7 @@ use shared::{ atrium::stores::AtriumSessionStore, atrium::stores::AtriumStateStore, }; -use sqlx::{PgPool, postgres::PgPoolOptions}; +use sqlx::{PgPool, migrate::Migrator, postgres::PgPoolOptions}; use std::{ env, net::{IpAddr, Ipv4Addr, SocketAddr}, @@ -112,7 +112,6 @@ fn build_oauth_client( }; if let Ok(oauth_host) = env::var("OAUTH_HOST") { - println!("https://{oauth_host}/oauth-client-metadata.json"); let config = OAuthClientConfig { client_metadata: AtprotoClientMetadata { client_id: format!("https://{oauth_host}/oauth-client-metadata.json"), @@ -158,7 +157,13 @@ async fn main() -> Result<(), Box> { ) .init(); - let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7878); + let host = env::var("HOST").unwrap_or_else(|_| "127.0.0.1".to_string()); + let port: u16 = env::var("PORT") + .unwrap_or_else(|_| "7878".to_string()) + .parse() + .expect("PORT must be a number"); + + let addr = SocketAddr::new(host.parse().expect("Invalid HOST address"), port); let host = addr.ip(); let port = addr.port(); let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); @@ -175,6 +180,14 @@ async fn main() -> Result<(), Box> { .await .expect("can't connect to database"); + // Run database migrations + static MIGRATOR: Migrator = sqlx::migrate!("../migrations"); + MIGRATOR + .run(&postgres_pool) + .await + .expect("failed to run database migrations"); + log::info!("database migrations applied successfully"); + // redis pool setup let redis_url = env::var("REDIS_URL").expect("REDIS_URL must be set in the environment or .env");