refactor: reorganize project into monorepo with frontend scaffolding

Convert project from single backend to monorepo structure with separate
frontend (Vue 3 + TypeScript + Vite) and backend directories. Updates
all configuration files and build system to support both workspaces.

Ref: T007 (specs/001-modbus-relay-control)
This commit is contained in:
2026-01-01 17:35:58 +01:00
parent 6903d76682
commit 837a49fc58
48 changed files with 1243 additions and 51 deletions

View File

@@ -0,0 +1,7 @@
//! Persistence layer implementations.
//!
//! This module contains the concrete implementations of repository traits
//! for data persistence, including SQLite-based storage for relay labels.
/// `SQLite` repository implementation for relay labels.
pub mod sqlite_repository;

View File

@@ -0,0 +1,64 @@
use sqlx::SqlitePool;
use crate::domain::relay::repository::RepositoryError;
/// `SQLite` implementation of the relay label repository.
///
/// This repository manages persistent storage of relay labels using `SQLite`,
/// with automatic schema migrations via `SQLx`.
pub struct SqliteRelayLabelRepository {
/// The `SQLite` connection pool for database operations.
pool: SqlitePool,
}
impl SqliteRelayLabelRepository {
/// Creates a new `SQLite` relay label repository.
///
/// # Arguments
///
/// * `db_path` - The `SQLite` database path or connection string (e.g., `"sqlite://data.db"`)
///
/// # Errors
///
/// Returns `RepositoryError::DatabaseError` if the connection fails or migrations cannot be applied.
pub async fn new(db_path: &str) -> Result<Self, RepositoryError> {
let pool = SqlitePool::connect(db_path)
.await
.map_err(|e| RepositoryError::DatabaseError(e.to_string()))?;
let repo = Self { pool };
repo.run_migrations().await?;
Ok(repo)
}
/// Returns a reference to the underlying connection pool.
///
/// This is primarily used for testing to verify schema and constraints.
#[must_use]
pub const fn pool(&self) -> &SqlitePool {
&self.pool
}
/// Creates a new in-memory `SQLite` relay label repository.
///
/// This is useful for testing and ephemeral data storage.
///
/// # Errors
///
/// Returns `RepositoryError::DatabaseError` if the in-memory database cannot be created.
pub async fn in_memory() -> Result<Self, RepositoryError> {
Self::new("sqlite::memory:").await
}
/// Runs all pending database migrations.
///
/// # Errors
///
/// Returns `RepositoryError::DatabaseError` if migrations fail to apply.
async fn run_migrations(&self) -> Result<(), RepositoryError> {
sqlx::migrate!("../migrations/")
.run(&self.pool)
.await
.map_err(|e| RepositoryError::DatabaseError(e.to_string()))?;
Ok(())
}
}