feat(api): implement user authentication and registration endpoints
- Add main application logic in `api/src/main.rs` to initialize server, database, and services. - Create authentication routes in `api/src/routes/auth.rs` for login, register, logout, and user info retrieval. - Implement configuration route in `api/src/routes/config.rs` to expose application settings. - Define application state management in `api/src/state.rs` to share user service and configuration. - Set up Docker Compose configuration in `compose.yml` for backend, worker, and database services. - Establish domain logic in `domain` crate with user entities, repositories, and services. - Implement SQLite user repository in `infra/src/user_repository.rs` for user data persistence. - Create database migration handling in `infra/src/db.rs` and session store in `infra/src/session_store.rs`. - Add necessary dependencies and features in `Cargo.toml` files for both `domain` and `infra` crates.
This commit is contained in:
138
Cargo.lock
generated
138
Cargo.lock
generated
@@ -32,6 +32,37 @@ version = "1.0.100"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "api"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"async-trait",
|
||||||
|
"axum",
|
||||||
|
"axum-login",
|
||||||
|
"chrono",
|
||||||
|
"config",
|
||||||
|
"domain",
|
||||||
|
"dotenvy",
|
||||||
|
"infra",
|
||||||
|
"k-core",
|
||||||
|
"password-auth",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sqlx",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
"time",
|
||||||
|
"tokio",
|
||||||
|
"tower",
|
||||||
|
"tower-http",
|
||||||
|
"tower-sessions",
|
||||||
|
"tower-sessions-sqlx-store",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"uuid",
|
||||||
|
"validator",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "argon2"
|
name = "argon2"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -558,6 +589,22 @@ dependencies = [
|
|||||||
"const-random",
|
"const-random",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "domain"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"async-trait",
|
||||||
|
"chrono",
|
||||||
|
"futures-core",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dotenvy"
|
name = "dotenvy"
|
||||||
version = "0.15.7"
|
version = "0.15.7"
|
||||||
@@ -1125,6 +1172,28 @@ dependencies = [
|
|||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "infra"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-nats",
|
||||||
|
"async-trait",
|
||||||
|
"chrono",
|
||||||
|
"domain",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"k-core",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sqlx",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
"tokio",
|
||||||
|
"tower-sessions",
|
||||||
|
"tower-sessions-sqlx-store",
|
||||||
|
"tracing",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
@@ -2401,75 +2470,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "template-api"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"async-trait",
|
|
||||||
"axum",
|
|
||||||
"axum-login",
|
|
||||||
"chrono",
|
|
||||||
"config",
|
|
||||||
"dotenvy",
|
|
||||||
"k-core",
|
|
||||||
"password-auth",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sqlx",
|
|
||||||
"template-domain",
|
|
||||||
"template-infra",
|
|
||||||
"thiserror 2.0.17",
|
|
||||||
"time",
|
|
||||||
"tokio",
|
|
||||||
"tower",
|
|
||||||
"tower-http",
|
|
||||||
"tower-sessions",
|
|
||||||
"tower-sessions-sqlx-store",
|
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
"uuid",
|
|
||||||
"validator",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "template-domain"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"async-trait",
|
|
||||||
"chrono",
|
|
||||||
"futures-core",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror 2.0.17",
|
|
||||||
"tokio",
|
|
||||||
"tracing",
|
|
||||||
"uuid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "template-infra"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"async-nats",
|
|
||||||
"async-trait",
|
|
||||||
"chrono",
|
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
|
||||||
"k-core",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sqlx",
|
|
||||||
"template-domain",
|
|
||||||
"thiserror 2.0.17",
|
|
||||||
"tokio",
|
|
||||||
"tower-sessions",
|
|
||||||
"tower-sessions-sqlx-store",
|
|
||||||
"tracing",
|
|
||||||
"uuid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["template-domain", "template-infra", "template-api"]
|
members = ["domain", "infra", "api"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ WORKDIR /app
|
|||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Build the release binary
|
# Build the release binary
|
||||||
RUN cargo build --release -p template-api
|
RUN cargo build --release -p api
|
||||||
|
|
||||||
FROM debian:bookworm-slim
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ WORKDIR /app
|
|||||||
# Install OpenSSL (required for many Rust networking crates) and CA certificates
|
# Install OpenSSL (required for many Rust networking crates) and CA certificates
|
||||||
RUN apt-get update && apt-get install -y libssl3 ca-certificates && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y libssl3 ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY --from=builder /app/target/release/template-api .
|
COPY --from=builder /app/target/release/api .
|
||||||
|
|
||||||
|
|
||||||
# Create data directory for SQLite
|
# Create data directory for SQLite
|
||||||
@@ -24,4 +24,4 @@ ENV SESSION_SECRET=supersecretchangeinproduction
|
|||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
CMD ["./template-api"]
|
CMD ["./api"]
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "template-api"
|
name = "api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
default-run = "template-api"
|
default-run = "api"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["sqlite"]
|
default = ["sqlite"]
|
||||||
sqlite = [
|
sqlite = [
|
||||||
"template-infra/sqlite",
|
"infra/sqlite",
|
||||||
"tower-sessions-sqlx-store/sqlite",
|
"tower-sessions-sqlx-store/sqlite",
|
||||||
"sqlx/sqlite",
|
"sqlx/sqlite",
|
||||||
]
|
]
|
||||||
postgres = [
|
postgres = [
|
||||||
"template-infra/postgres",
|
"infra/postgres",
|
||||||
"tower-sessions-sqlx-store/postgres",
|
"tower-sessions-sqlx-store/postgres",
|
||||||
"sqlx/postgres",
|
"sqlx/postgres",
|
||||||
"k-core/postgres",
|
"k-core/postgres",
|
||||||
]
|
]
|
||||||
broker-nats = ["template-infra/broker-nats"]
|
broker-nats = ["infra/broker-nats"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
k-core = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-core", features = [
|
k-core = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-core", features = [
|
||||||
"logging",
|
"logging",
|
||||||
"db-sqlx",
|
"db-sqlx",
|
||||||
] }
|
] }
|
||||||
template-domain = { path = "../template-domain" }
|
domain = { path = "../domain" }
|
||||||
template-infra = { path = "../template-infra", default-features = false, features = [
|
infra = { path = "../infra", default-features = false, features = [
|
||||||
"sqlite",
|
"sqlite",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
@@ -3,14 +3,14 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use axum_login::{AuthnBackend, UserId};
|
use axum_login::{AuthnBackend, UserId};
|
||||||
|
use infra::session_store::InfraSessionStore;
|
||||||
use password_auth::verify_password;
|
use password_auth::verify_password;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
|
||||||
use template_infra::session_store::InfraSessionStore;
|
|
||||||
use tower_sessions::SessionManagerLayer;
|
use tower_sessions::SessionManagerLayer;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::error::ApiError;
|
use crate::error::ApiError;
|
||||||
use template_domain::{User, UserRepository};
|
use domain::{User, UserRepository};
|
||||||
|
|
||||||
/// Wrapper around domain User to implement AuthUser
|
/// Wrapper around domain User to implement AuthUser
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@@ -3,14 +3,14 @@
|
|||||||
//! Maps domain errors to HTTP responses
|
//! Maps domain errors to HTTP responses
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
|
Json,
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
Json,
|
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use template_domain::DomainError;
|
use domain::DomainError;
|
||||||
|
|
||||||
/// API-level errors
|
/// API-level errors
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::time::Duration as StdDuration;
|
use std::time::Duration as StdDuration;
|
||||||
|
|
||||||
|
use domain::UserService;
|
||||||
|
use infra::factory::build_session_store;
|
||||||
|
use infra::factory::build_user_repository;
|
||||||
use k_core::logging;
|
use k_core::logging;
|
||||||
use template_domain::UserService;
|
|
||||||
use template_infra::factory::build_session_store;
|
|
||||||
use template_infra::factory::build_user_repository;
|
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tower_sessions::{Expiry, SessionManagerLayer};
|
use tower_sessions::{Expiry, SessionManagerLayer};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
@@ -44,7 +44,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
let db_pool = k_core::db::connect(&db_config).await?;
|
let db_pool = k_core::db::connect(&db_config).await?;
|
||||||
|
|
||||||
// 4. Run migrations (using the re-export if you kept it, or direct k_core)
|
// 4. Run migrations (using the re-export if you kept it, or direct k_core)
|
||||||
template_infra::db::run_migrations(&db_pool).await?;
|
infra::db::run_migrations(&db_pool).await?;
|
||||||
|
|
||||||
// 5. Initialize Services
|
// 5. Initialize Services
|
||||||
let user_repo = build_user_repository(&db_pool).await?;
|
let user_repo = build_user_repository(&db_pool).await?;
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
use axum::{
|
|
||||||
extract::{State, Json},
|
|
||||||
response::IntoResponse,
|
|
||||||
Router, routing::post,
|
|
||||||
};
|
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
|
use axum::{
|
||||||
|
Router,
|
||||||
|
extract::{Json, State},
|
||||||
|
response::IntoResponse,
|
||||||
|
routing::post,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dto::{LoginRequest, RegisterRequest, UserResponse},
|
dto::{LoginRequest, RegisterRequest, UserResponse},
|
||||||
error::ApiError,
|
error::ApiError,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
};
|
};
|
||||||
use template_domain::{DomainError, Email};
|
use domain::{DomainError, Email};
|
||||||
|
|
||||||
pub fn router() -> Router<AppState> {
|
pub fn router() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
@@ -24,22 +25,31 @@ async fn login(
|
|||||||
mut auth_session: crate::auth::AuthSession,
|
mut auth_session: crate::auth::AuthSession,
|
||||||
Json(payload): Json<LoginRequest>,
|
Json(payload): Json<LoginRequest>,
|
||||||
) -> Result<impl IntoResponse, ApiError> {
|
) -> Result<impl IntoResponse, ApiError> {
|
||||||
let user = match auth_session.authenticate(crate::auth::Credentials {
|
let user = match auth_session
|
||||||
email: payload.email,
|
.authenticate(crate::auth::Credentials {
|
||||||
password: payload.password,
|
email: payload.email,
|
||||||
}).await {
|
password: payload.password,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(Some(user)) => user,
|
Ok(Some(user)) => user,
|
||||||
Ok(None) => return Err(ApiError::Validation("Invalid credentials".to_string())),
|
Ok(None) => return Err(ApiError::Validation("Invalid credentials".to_string())),
|
||||||
Err(_) => return Err(ApiError::Internal("Authentication failed".to_string())),
|
Err(_) => return Err(ApiError::Internal("Authentication failed".to_string())),
|
||||||
};
|
};
|
||||||
|
|
||||||
auth_session.login(&user).await.map_err(|_| ApiError::Internal("Login failed".to_string()))?;
|
auth_session
|
||||||
|
.login(&user)
|
||||||
|
.await
|
||||||
|
.map_err(|_| ApiError::Internal("Login failed".to_string()))?;
|
||||||
|
|
||||||
Ok((StatusCode::OK, Json(UserResponse {
|
Ok((
|
||||||
id: user.0.id,
|
StatusCode::OK,
|
||||||
email: user.0.email.into_inner(),
|
Json(UserResponse {
|
||||||
created_at: user.0.created_at,
|
id: user.0.id,
|
||||||
})))
|
email: user.0.email.into_inner(),
|
||||||
|
created_at: user.0.created_at,
|
||||||
|
}),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn register(
|
async fn register(
|
||||||
@@ -47,8 +57,15 @@ async fn register(
|
|||||||
mut auth_session: crate::auth::AuthSession,
|
mut auth_session: crate::auth::AuthSession,
|
||||||
Json(payload): Json<RegisterRequest>,
|
Json(payload): Json<RegisterRequest>,
|
||||||
) -> Result<impl IntoResponse, ApiError> {
|
) -> Result<impl IntoResponse, ApiError> {
|
||||||
if state.user_service.find_by_email(&payload.email).await?.is_some() {
|
if state
|
||||||
return Err(ApiError::Domain(DomainError::UserAlreadyExists(payload.email)));
|
.user_service
|
||||||
|
.find_by_email(&payload.email)
|
||||||
|
.await?
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
return Err(ApiError::Domain(DomainError::UserAlreadyExists(
|
||||||
|
payload.email,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: In a real app, you would hash the password here.
|
// Note: In a real app, you would hash the password here.
|
||||||
@@ -57,18 +74,27 @@ async fn register(
|
|||||||
let email = Email::try_from(payload.email).map_err(|e| ApiError::Validation(e.to_string()))?;
|
let email = Email::try_from(payload.email).map_err(|e| ApiError::Validation(e.to_string()))?;
|
||||||
|
|
||||||
// Using email as subject for local auth for now
|
// Using email as subject for local auth for now
|
||||||
let user = state.user_service.find_or_create(&email.as_ref().to_string(), email.as_ref()).await?;
|
let user = state
|
||||||
|
.user_service
|
||||||
|
.find_or_create(&email.as_ref().to_string(), email.as_ref())
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Log the user in
|
// Log the user in
|
||||||
let auth_user = crate::auth::AuthUser(user.clone());
|
let auth_user = crate::auth::AuthUser(user.clone());
|
||||||
|
|
||||||
auth_session.login(&auth_user).await.map_err(|_| ApiError::Internal("Login failed".to_string()))?;
|
auth_session
|
||||||
|
.login(&auth_user)
|
||||||
|
.await
|
||||||
|
.map_err(|_| ApiError::Internal("Login failed".to_string()))?;
|
||||||
|
|
||||||
Ok((StatusCode::CREATED, Json(UserResponse {
|
Ok((
|
||||||
id: user.id,
|
StatusCode::CREATED,
|
||||||
email: user.email.into_inner(),
|
Json(UserResponse {
|
||||||
created_at: user.created_at,
|
id: user.id,
|
||||||
})))
|
email: user.email.into_inner(),
|
||||||
|
created_at: user.created_at,
|
||||||
|
}),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn logout(mut auth_session: crate::auth::AuthSession) -> impl IntoResponse {
|
async fn logout(mut auth_session: crate::auth::AuthSession) -> impl IntoResponse {
|
||||||
@@ -79,7 +105,9 @@ async fn logout(mut auth_session: crate::auth::AuthSession) -> impl IntoResponse
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn me(auth_session: crate::auth::AuthSession) -> Result<impl IntoResponse, ApiError> {
|
async fn me(auth_session: crate::auth::AuthSession) -> Result<impl IntoResponse, ApiError> {
|
||||||
let user = auth_session.user.ok_or(ApiError::Unauthorized("Not logged in".to_string()))?;
|
let user = auth_session
|
||||||
|
.user
|
||||||
|
.ok_or(ApiError::Unauthorized("Not logged in".to_string()))?;
|
||||||
|
|
||||||
Ok(Json(UserResponse {
|
Ok(Json(UserResponse {
|
||||||
id: user.0.id,
|
id: user.0.id,
|
||||||
@@ -6,7 +6,7 @@ use axum::extract::FromRef;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use template_domain::UserService;
|
use domain::UserService;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
50
compose.yml
Normal file
50
compose.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
services:
|
||||||
|
backend:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
# In production, use a secure secret
|
||||||
|
- SESSION_SECRET=dev_secret_key_12345
|
||||||
|
- DATABASE_URL=sqlite:///app/data/notes.db
|
||||||
|
- CORS_ALLOWED_ORIGINS=http://localhost:8080,http://localhost:5173
|
||||||
|
- HOST=0.0.0.0
|
||||||
|
- PORT=3000
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
|
||||||
|
worker:
|
||||||
|
build: .
|
||||||
|
command: ["./notes-worker"]
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=sqlite:///app/data/notes.db
|
||||||
|
- BROKER_URL=nats://nats:4222
|
||||||
|
- QDRANT_URL=http://qdrant:6334
|
||||||
|
- EMBEDDING_PROVIDER=fastembed
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
- nats
|
||||||
|
- qdrant
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
|
||||||
|
nats:
|
||||||
|
image: nats:alpine
|
||||||
|
ports:
|
||||||
|
- "4222:4222"
|
||||||
|
- "6222:6222"
|
||||||
|
- "8222:8222"
|
||||||
|
restart: unless-stopped
|
||||||
|
db:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: user
|
||||||
|
POSTGRES_PASSWORD: password
|
||||||
|
POSTGRES_DB: k_template_db
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
volumes:
|
||||||
|
- db_data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db_data:
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "template-domain"
|
name = "domain"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "template-infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ broker-nats = ["dep:async-nats", "dep:futures-util"]
|
|||||||
k-core = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-core", features = [
|
k-core = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-core", features = [
|
||||||
"db-sqlx",
|
"db-sqlx",
|
||||||
] }
|
] }
|
||||||
template-domain = { path = "../template-domain" }
|
domain = { path = "../domain" }
|
||||||
async-trait = "0.1.89"
|
async-trait = "0.1.89"
|
||||||
chrono = { version = "0.4.42", features = ["serde"] }
|
chrono = { version = "0.4.42", features = ["serde"] }
|
||||||
sqlx = { version = "0.8.6", features = ["runtime-tokio", "chrono", "migrate"] }
|
sqlx = { version = "0.8.6", features = ["runtime-tokio", "chrono", "migrate"] }
|
||||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
use crate::SqliteUserRepository;
|
use crate::SqliteUserRepository;
|
||||||
use crate::db::DatabasePool;
|
use crate::db::DatabasePool;
|
||||||
use template_domain::UserRepository;
|
use domain::UserRepository;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum FactoryError {
|
pub enum FactoryError {
|
||||||
@@ -12,7 +12,7 @@ pub enum FactoryError {
|
|||||||
#[error("Not implemented: {0}")]
|
#[error("Not implemented: {0}")]
|
||||||
NotImplemented(String),
|
NotImplemented(String),
|
||||||
#[error("Infrastructure error: {0}")]
|
#[error("Infrastructure error: {0}")]
|
||||||
Infrastructure(#[from] template_domain::DomainError),
|
Infrastructure(#[from] domain::DomainError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FactoryResult<T> = Result<T, FactoryError>;
|
pub type FactoryResult<T> = Result<T, FactoryError>;
|
||||||
@@ -5,7 +5,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use sqlx::{FromRow, SqlitePool};
|
use sqlx::{FromRow, SqlitePool};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use template_domain::{DomainError, DomainResult, Email, User, UserRepository};
|
use domain::{DomainError, DomainResult, Email, User, UserRepository};
|
||||||
|
|
||||||
/// SQLite adapter for UserRepository
|
/// SQLite adapter for UserRepository
|
||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
@@ -145,7 +145,7 @@ mod tests {
|
|||||||
use k_core::db::connect; // Import k_core::db::connect
|
use k_core::db::connect; // Import k_core::db::connect
|
||||||
|
|
||||||
async fn setup_test_db() -> SqlitePool {
|
async fn setup_test_db() -> SqlitePool {
|
||||||
let config = DatabaseConfig::in_memory();
|
let config = DatabaseConfig::default();
|
||||||
// connect returns DatabasePool directly now
|
// connect returns DatabasePool directly now
|
||||||
let db_pool = connect(&config).await.expect("Failed to create pool");
|
let db_pool = connect(&config).await.expect("Failed to create pool");
|
||||||
run_migrations(&db_pool).await.unwrap();
|
run_migrations(&db_pool).await.unwrap();
|
||||||
Reference in New Issue
Block a user