Skip to content

Latest commit

 

History

23 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Gestion

Personal finance management application to track expenses and incomes. Built as a monorepo with three components:

  • front/ — SvelteKit SPA (SSR) written in TypeScript.
  • server/ — Rust backend API with a layered architecture.
  • database/ — SurrealDB schema and data model.

Tech Stack

Layer Technology
Frontend Svelte 5 (runes), SvelteKit 2, TypeScript, Vite 7
Icons @iconify/svelte
Package manager Bun
Backend Rust (edition 2024), Actix-web 4
Auth jsonwebtoken 10 (JWT)
Database SurrealDB 3 (surrealdb + surrealdb-types crates)
Env config dotenvy
Testing Vitest + Playwright (frontend)
Linting / Format ESLint, Prettier (frontend)

Architecture

High-level overview

The frontend runs SvelteKit server-side load functions (+page.server.ts) that call the Rust API using a JWT stored in a token cookie. The API talks to SurrealDB over WebSocket.

                     +---------------------------------------------+
                     |                 BROWSER (SPA)                |
                     |  /signup  /signin  /dashboard                |
                     +---------------------+-----------------------+
                                           |
                      HTTP (form-urlencoded) | JWT in cookie / Authorization header
                                           v
                     +---------------------------------------------+
                     |                 SVELTEKIT                   |
                     |  SSR load functions  (+page.server.ts)      |
                     |  actions / stores / interfaces              |
                     +---------------------+-----------------------+
                                           |
                          REST (fetch)     | Authorization: <token>
                                           v
                     +---------------------------------------------+
                     |              RUST API (Actix-web)           |
                     |  routes -> services -> repositories         |
                     |  UserJwt middleware (JWT validation)        |
                     +---------------------+-----------------------+
                                           |
                             SurrealQL     | WebSocket (Ws)
                                           v
                     +---------------------------------------------+
                     |                SurrealDB 3                  |
                     |  user  expense  income  (had relation)     |
                     +---------------------------------------------+

Backend layered architecture

The server follows the layered pattern from Black Hat Rust by Sylvain Kerkour: controllers, services and repositories are separated, keeping HTTP concerns, business logic and data access independent.

 HTTP request
     |
     v
 +-------------------------+      +-------------------------+
 |  ROUTES / CONTROLLERS   | ---> |  SERVICES               |
 |  Actix-web handlers     |      |  Business logic,        |
 |  (mod.rs)               |      |  validation,            |
 +-------------------------+      |  error handling (code)  |
     ^                            +-------------------------+
     |                                     |
     |  UserJwt middleware                v
     |  (validates JWT, injects    +-------------------------+
     |   user id into request)     |  REPOSITORIES           |
     |                             |  SurrealQL queries,     |
     +-----------------------------|  DB <-> domain mapping  |
                                   +-------------------------+
                                            |
                                            v
                                     +---------------+
                                     |  SurrealDB    |
                                     +---------------+

Modules: auth, user, expenses, incomes, plus a shared layer (config, shared) for DB connection, JWT middleware and common entities.

Authentication flow

 POST /auth/signin  (username + password)
     |
     v
 SigninRepository: SELECT ... WHERE username = $u AND crypto::bcrypt::compare(password, $p)
     |
     v
 Generate JWT (jsonwebtoken, secret from TOKEN_SEED)
     |
     v
 Frontend stores token in `token` cookie
     |
     v
 Subsequent requests -> UserJwt middleware decodes JWT -> req.extensions user id

Passwords are never stored in plain text: SurrealDB hashes them with crypto::bcrypt::generate on user.password (see database/model.surql).

Project Structure

gestion/
├── database/
│   └── model.surql              # SurrealDB schema (tables, fields, permissions)
├── front/                       # SvelteKit application
│   ├── src/
│   │   ├── lib/
│   │   │   ├── components/      # Reusable Svelte components
│   │   │   ├── interfaces/      # TS types (user, expenses, incomes)
│   │   │   ├── store/           # Svelte 5 runes store (user state)
│   │   │   └── config.ts        # API URL config
│   │   └── routes/              # /signin /signup /dashboard/{expenses,incomes}
│   └── package.json
└── server/                      # Rust API
    ├── src/
    │   ├── main.rs              # App bootstrap + routes
    │   ├── auth/                # signup / signin (controller, service, repository)
    │   ├── user/                # user profile endpoints
    │   ├── expenses/            # expenses CRUD
    │   ├── incomes/             # incomes CRUD
    │   ├── config/              # env vars, DB + JWT config
    │   └── shared/              # DB connection, JWT middleware, entities
    ├── .env.example
    └── Cargo.toml

Data Model

Entity-Relationship diagram

+--------+        +-------+        +-----------+
|  user  | 1 --- * |  had  | * --- 1 |  expense  |
+--------+        +-------+        +-----------+
     |                  |        +-----------+
     |                  *--------|  income   |
     |                           +-----------+
     |
     |  username UNIQUE (index_username)

A user has (had) zero or more expense and income records. The had relation is ENFORCED SCHEMALESS and points IN to user and OUT to expense | income.

Tables

Table Type Fields
user SCHEMAFULL name, lastname, username, password (bcrypt)
expense SCHEMAFULL amount, description, processed, date
income SCHEMAFULL amount, description, processed, date
had RELATION in: user, out: expense | income

Table-level PERMISSIONS NONE and field-level permissions restrict access; the user table only allows select/update/delete for WHERE id = $auth.id.

API Endpoints

Method Path Description
GET / Health check
POST /auth/signup Create a user account
POST /auth/signin Log in, returns user + JWT
GET /user/refresh Refresh the JWT and return current user
GET /user/username/{username} Check if a username exists
PATCH /user/names Update name / lastname
PATCH /user/username Update username
GET /expense List expenses of the authenticated user
POST /expense Create an expense
PATCH /expense/{id} Update all expense fields
PATCH /expense/{id}/amount Update expense amount
PATCH /expense/{id}/description Update expense description
PATCH /expense/{id}/date Update expense date
PATCH /expense/{id}/processed Update expense processed flag
DELETE /expense/{id} Delete an expense
GET /income List incomes of the authenticated user
POST /income Create an income
PATCH /income/{id} Update all income fields
PATCH /income/{id}/amount Update income amount
PATCH /income/{id}/description Update income description
PATCH /income/{id}/date Update income date
PATCH /income/{id}/processed Update income processed flag
DELETE /income/{id} Delete an income

Expense and income endpoints are protected by the UserJwt middleware and require an Authorization: <token> header. Bodies are sent as application/x-www-form-urlencoded.

Getting Started

Prerequisites

  • Rust (edition 2024)
  • Bun
  • A running SurrealDB instance
  • The schema from database/model.surql applied to the target database

1. Backend

cd server
cp .env.example .env   # then set the correct values
cargo run

Configuration (.env):

HOST=0.0.0.0
PORT=8080

DB_WSS=ws://localhost:8000
DB_USER=root
DB_PASS=your_root_password
DB_NS=management
DB_NAME=mndb
TOKEN_SEED=your_jwt_secret

2. Frontend

cd front
bun install
bun run dev

The frontend points to the API via src/lib/config.ts (apiUrl). Available scripts:

Command Description
bun run dev Start the development server
bun run build Production build
bun run check Type-check with svelte-check
bun run lint Prettier + ESLint
bun run test Run Vitest unit tests

About

App of mony with svelte + rust + surrealDB

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages