Compare commits
	
		
			54 Commits
		
	
	
		
			1.0.2
			...
			47304ec5cc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 47304ec5cc | |||
| 77c8866bb8 | |||
| 133f235639 | |||
| 7ef0499abf | |||
| 1383da4483 | |||
| 4f1a9d0865 | |||
| 31803feaa9 | |||
| aad32f9c25 | |||
| 1ea2bd6acf | |||
| a085116018 | |||
| 952a66042c | |||
| 6cf6ab5a37 | |||
| 1781318fdf | |||
| 2560962684 | |||
| dc3704f13b | |||
| af4c3c4a8f | |||
| a268e1bc4d | |||
| 77fc3699d6 | |||
| 96b3f6d4dc | |||
| 9a74c6c951 | |||
| c64fa838c9 | |||
| 8f45109e87 | |||
| d0c20aa68b | |||
| b84305428c | |||
| 560e415cb1 | |||
| 4863b1f4af | |||
| 6fa87a67d6 | |||
| e7219d2e44 | |||
| c9af4e4fc9 | |||
| 6cb4bcf4d4 | |||
| 447b6d89fb | |||
| a2a7d3d94a | |||
| 05a5da7527 | |||
| 43f62802dc | |||
| 60f6b94b41 | |||
| 5aaefafb48 | |||
| 63af408224 | |||
| a5966c8d53 | |||
| 196cb0b5c9 | |||
| ba06cd04ff | |||
| dc4f41f31c | |||
| 9ba928102c | |||
| 7d7ad0ce89 | |||
| d0ff90701f | |||
| f38482757e | |||
| 7e976d903d | |||
| 737d7f76d4 | |||
| cf0e42ff0e | |||
| d2fa9bf9ee | |||
| fb5a85311c | |||
| 288d334615 | |||
| 87f017fc42 | |||
| 43fb8dcda6 | |||
| a3b9c7cdb1 | 
							
								
								
									
										38
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								README.md
									
									
									
									
									
								
							@@ -3,10 +3,46 @@
 | 
			
		||||
 | 
			
		||||
Open Source web-based personal expenses tool.
 | 
			
		||||
 | 
			
		||||
**Note :** This project does not handle authentication itself. Instead, it relies on OpenID to achieve users authentication.
 | 
			
		||||
 | 
			
		||||
## Setup prod env
 | 
			
		||||
1. Install prerequisites:
 | 
			
		||||
   1. docker
 | 
			
		||||
   2. docker compose
 | 
			
		||||
   3. git
 | 
			
		||||
 | 
			
		||||
2. Clone this git repository:
 | 
			
		||||
```bash
 | 
			
		||||
git clone https://gitea.communiquons.org/pierre/MoneyMgr
 | 
			
		||||
cd MoneyMgr/docker_prod
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
3. Copy and adapt env values
 | 
			
		||||
```bash
 | 
			
		||||
cp .env.sample .env
 | 
			
		||||
nano .env
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
4. Create required directories:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
mkdir -p storage/{db,redis-data,redis-conf,minio}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
5. Start containers
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
docker compose up
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
6. Checkout http://localhost:8000/
 | 
			
		||||
 | 
			
		||||
> The default credentials are `admin` / `admin`
 | 
			
		||||
 | 
			
		||||
## Setup dev env
 | 
			
		||||
1. Install prerequisites:
 | 
			
		||||
   1. docker
 | 
			
		||||
   2. docker-compose
 | 
			
		||||
   2. docker compose
 | 
			
		||||
   3. rust
 | 
			
		||||
   4. node
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								docker_prod/.env.sample
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								docker_prod/.env.sample
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
MINIO_ROOT_USER=rootuser
 | 
			
		||||
MINIO_ROOT_PASSWORD=rootpassword
 | 
			
		||||
DB_USER=db_user
 | 
			
		||||
DB_PASSWORD=db_password
 | 
			
		||||
REDIS_PASS=redis_password
 | 
			
		||||
WEBSITE_ORIGIN=http://localhost:8000
 | 
			
		||||
APP_SECRET=secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret
 | 
			
		||||
AUTH_SECRET_KEY=secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret
 | 
			
		||||
OIDC_CLIENT_ID=bar
 | 
			
		||||
OIDC_CLIENT_SECRET=foo
 | 
			
		||||
							
								
								
									
										3
									
								
								docker_prod/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docker_prod/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
.env
 | 
			
		||||
storage
 | 
			
		||||
auth/users.json
 | 
			
		||||
							
								
								
									
										5
									
								
								docker_prod/auth/clients.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								docker_prod/auth/clients.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
- id: ${OIDC_CLIENT_ID}
 | 
			
		||||
  name: MoneyMgr
 | 
			
		||||
  description: Money management tool
 | 
			
		||||
  secret: ${OIDC_CLIENT_SECRET}
 | 
			
		||||
  redirect_uri: ${APP_ORIGIN}/oidc_cb
 | 
			
		||||
							
								
								
									
										79
									
								
								docker_prod/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								docker_prod/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
services:
 | 
			
		||||
  minio:
 | 
			
		||||
    image: minio/minio
 | 
			
		||||
    user: "1000"
 | 
			
		||||
    environment:
 | 
			
		||||
      - MINIO_ROOT_USER=$MINIO_ROOT_USER
 | 
			
		||||
      - MINIO_ROOT_PASSWORD=$MINIO_ROOT_PASSWORD
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./storage/minio:/data
 | 
			
		||||
    command: [ "minio", "server", "/data", "--console-address", ":9090" ]
 | 
			
		||||
    ports:
 | 
			
		||||
      - 9000:9000
 | 
			
		||||
      - 9090:9090
 | 
			
		||||
    expose:
 | 
			
		||||
      - 9000
 | 
			
		||||
 | 
			
		||||
  db:
 | 
			
		||||
    image: postgres
 | 
			
		||||
    user: "1000"
 | 
			
		||||
    ports:
 | 
			
		||||
      - "5432:5432"
 | 
			
		||||
    expose:
 | 
			
		||||
      - 5432
 | 
			
		||||
    environment:
 | 
			
		||||
      - POSTGRES_USER=$DB_USER
 | 
			
		||||
      - POSTGRES_PASSWORD=$DB_PASSWORD
 | 
			
		||||
      - POSTGRES_DB=moneymgr
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./storage/db:/var/lib/postgresql/data
 | 
			
		||||
 | 
			
		||||
  oidc:
 | 
			
		||||
    image: pierre42100/basic_oidc
 | 
			
		||||
    user: "1000"
 | 
			
		||||
    environment:
 | 
			
		||||
      - LISTEN_ADDRESS=0.0.0.0:9001
 | 
			
		||||
      - STORAGE_PATH=/storage
 | 
			
		||||
      - TOKEN_KEY=$AUTH_SECRET_KEY
 | 
			
		||||
      - WEBSITE_ORIGIN=http://localhost:9001
 | 
			
		||||
      - OIDC_CLIENT_ID=$OIDC_CLIENT_ID
 | 
			
		||||
      - OIDC_CLIENT_SECRET=$OIDC_CLIENT_SECRET
 | 
			
		||||
      - APP_ORIGIN=$WEBSITE_ORIGIN
 | 
			
		||||
    expose:
 | 
			
		||||
      - 9001
 | 
			
		||||
    ports:
 | 
			
		||||
      - 9001:9001
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./auth:/storage
 | 
			
		||||
 | 
			
		||||
  redis:
 | 
			
		||||
    image: redis:alpine
 | 
			
		||||
    user: "1000"
 | 
			
		||||
    command: redis-server --requirepass ${REDIS_PASS:-secretredis}
 | 
			
		||||
    expose:
 | 
			
		||||
      - 6379
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./storage/redis-data:/data
 | 
			
		||||
      - ./storage/redis-conf:/usr/local/etc/redis/redis.conf
 | 
			
		||||
 | 
			
		||||
  moneymgr:
 | 
			
		||||
    image: pierre42100/moneymgr_backend
 | 
			
		||||
    user: "1000"
 | 
			
		||||
    ports:
 | 
			
		||||
      - 8000:8000
 | 
			
		||||
    environment:
 | 
			
		||||
      - WEBSITE_ORIGIN=${WEBSITE_ORIGIN}
 | 
			
		||||
      - SECRET=${APP_SECRET}
 | 
			
		||||
      - DB_HOST=db
 | 
			
		||||
      - DB_USERNAME=$DB_USER
 | 
			
		||||
      - DB_PASSWORD=$DB_PASSWORD
 | 
			
		||||
      - DB_NAME=moneymgr
 | 
			
		||||
      - OIDC_CONFIGURATION_URL=http://oidc:9001/.well-known/openid-configuration
 | 
			
		||||
      - OIDC_PROVIDER_NAME=OIDC
 | 
			
		||||
      - OIDC_CLIENT_ID=$OIDC_CLIENT_ID
 | 
			
		||||
      - OIDC_CLIENT_SECRET=$OIDC_CLIENT_SECRET
 | 
			
		||||
      - S3_ENDPOINT=http://minio:9000
 | 
			
		||||
      - S3_ACCESS_KEY=$MINIO_ROOT_USER
 | 
			
		||||
      - S3_SECRET_KEY=$MINIO_ROOT_PASSWORD
 | 
			
		||||
      - REDIS_HOSTNAME=redis
 | 
			
		||||
      - REDIS_PASSWORD=${REDIS_PASS:-secretredis}
 | 
			
		||||
							
								
								
									
										38
									
								
								moneymgr_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										38
									
								
								moneymgr_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -724,9 +724,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "clap"
 | 
			
		||||
version = "4.5.38"
 | 
			
		||||
version = "4.5.40"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
 | 
			
		||||
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "clap_builder",
 | 
			
		||||
 "clap_derive",
 | 
			
		||||
@@ -734,9 +734,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "clap_builder"
 | 
			
		||||
version = "4.5.38"
 | 
			
		||||
version = "4.5.40"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
 | 
			
		||||
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "anstream",
 | 
			
		||||
 "anstyle",
 | 
			
		||||
@@ -746,9 +746,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "clap_derive"
 | 
			
		||||
version = "4.5.32"
 | 
			
		||||
version = "4.5.40"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
 | 
			
		||||
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "heck",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
@@ -948,9 +948,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "crypto-common"
 | 
			
		||||
version = "0.2.0-rc.2"
 | 
			
		||||
version = "0.2.0-rc.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "170d71b5b14dec99db7739f6fc7d6ec2db80b78c3acb77db48392ccc3d8a9ea0"
 | 
			
		||||
checksum = "8a23fa214dea9efd4dacee5a5614646b30216ae0f05d4bb51bafb50e9da1c5be"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "hybrid-array",
 | 
			
		||||
]
 | 
			
		||||
@@ -1100,9 +1100,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "diesel"
 | 
			
		||||
version = "2.2.10"
 | 
			
		||||
version = "2.2.11"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ff3e1edb1f37b4953dd5176916347289ed43d7119cc2e6c7c3f7849ff44ea506"
 | 
			
		||||
checksum = "a917a9209950404d5be011c81d081a2692a822f73c3d6af586f0cab5ff50f614"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "bitflags",
 | 
			
		||||
 "byteorder",
 | 
			
		||||
@@ -1159,13 +1159,13 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "digest"
 | 
			
		||||
version = "0.11.0-pre.10"
 | 
			
		||||
version = "0.11.0-rc.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "6c478574b20020306f98d61c8ca3322d762e1ff08117422ac6106438605ea516"
 | 
			
		||||
checksum = "460dd7f37e4950526b54a5a6b1f41b6c8e763c58eb9a8fc8fc05ba5c2f44ca7b"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "block-buffer 0.11.0-rc.4",
 | 
			
		||||
 "const-oid 0.10.1",
 | 
			
		||||
 "crypto-common 0.2.0-rc.2",
 | 
			
		||||
 "crypto-common 0.2.0-rc.3",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
@@ -2304,7 +2304,7 @@ dependencies = [
 | 
			
		||||
 "rust_xlsxwriter",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "sha2 0.11.0-pre.5",
 | 
			
		||||
 "sha2 0.11.0-rc.0",
 | 
			
		||||
 "tempfile",
 | 
			
		||||
 "thiserror 2.0.12",
 | 
			
		||||
 "tokio",
 | 
			
		||||
@@ -3296,13 +3296,13 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "sha2"
 | 
			
		||||
version = "0.11.0-pre.5"
 | 
			
		||||
version = "0.11.0-rc.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "19b4241d1a56954dce82cecda5c8e9c794eef6f53abe5e5216bac0a0ea71ffa7"
 | 
			
		||||
checksum = "aa1d2e6b3cc4e43a8258a9a3b17aa5dfd2cc5186c7024bba8a64aa65b2c71a59"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "cpufeatures",
 | 
			
		||||
 "digest 0.11.0-pre.10",
 | 
			
		||||
 "digest 0.11.0-rc.0",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
@@ -3571,9 +3571,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "tokio"
 | 
			
		||||
version = "1.45.0"
 | 
			
		||||
version = "1.45.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
 | 
			
		||||
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "backtrace",
 | 
			
		||||
 "bytes",
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,9 @@ edition = "2024"
 | 
			
		||||
[dependencies]
 | 
			
		||||
env_logger = "0.11.8"
 | 
			
		||||
log = "0.4.27"
 | 
			
		||||
diesel = { version = "2.2.10", features = ["postgres", "r2d2"] }
 | 
			
		||||
diesel = { version = "2.2.11", features = ["postgres", "r2d2"] }
 | 
			
		||||
diesel_migrations = "2.2.0"
 | 
			
		||||
clap = { version = "4.5.38", features = ["env", "derive"] }
 | 
			
		||||
clap = { version = "4.5.40", features = ["env", "derive"] }
 | 
			
		||||
actix-web = "4.11.0"
 | 
			
		||||
actix-cors = "0.7.1"
 | 
			
		||||
actix-multipart = "0.7.2"
 | 
			
		||||
@@ -20,17 +20,17 @@ anyhow = "1.0.98"
 | 
			
		||||
serde = { version = "1.0.219", features = ["derive"] }
 | 
			
		||||
rust-s3 = "0.36.0-beta.2"
 | 
			
		||||
thiserror = "2.0.12"
 | 
			
		||||
tokio = "1.45.0"
 | 
			
		||||
tokio = "1.45.1"
 | 
			
		||||
futures-util = "0.3.31"
 | 
			
		||||
serde_json = "1.0.140"
 | 
			
		||||
light-openid = "1.0.4"
 | 
			
		||||
rand = "0.9.1"
 | 
			
		||||
ipnet = { version = "2.11.0", features = ["serde"] }
 | 
			
		||||
lazy-regex = "3.4.1"
 | 
			
		||||
jwt-simple = { version = "0.12.11", default-features = false, features = ["pure-rust"] }
 | 
			
		||||
jwt-simple = { version = "0.12.12", default-features = false, features = ["pure-rust"] }
 | 
			
		||||
mime_guess = "2.0.5"
 | 
			
		||||
rust-embed = { version = "8.7.2" }
 | 
			
		||||
sha2 = "0.11.0-pre.5"
 | 
			
		||||
sha2 = "0.11.0-rc.0"
 | 
			
		||||
base16ct = "0.2.0"
 | 
			
		||||
httpdate = "1.0.3"
 | 
			
		||||
chrono = "0.4.41"
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ use std::process::Command;
 | 
			
		||||
struct Args {
 | 
			
		||||
    /// URL to Money manager API
 | 
			
		||||
    #[arg(short('U'), long, env, default_value = "http://localhost:8000/api")]
 | 
			
		||||
    matrix_gw_url: String,
 | 
			
		||||
    moneymgr_url: String,
 | 
			
		||||
 | 
			
		||||
    /// Token ID
 | 
			
		||||
    #[arg(short('i'), long, env)]
 | 
			
		||||
@@ -39,7 +39,8 @@ struct Args {
 | 
			
		||||
fn main() {
 | 
			
		||||
    let args: Args = Args::parse();
 | 
			
		||||
 | 
			
		||||
    let full_url = format!("{}{}", args.matrix_gw_url, args.uri);
 | 
			
		||||
    let full_url = format!("{}{}", args.moneymgr_url, args.uri);
 | 
			
		||||
 | 
			
		||||
    log::debug!("Full URL: {full_url}");
 | 
			
		||||
 | 
			
		||||
    let key = HS256Key::from_bytes(args.token_secret.as_bytes());
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ pub async fn create_bucket_if_required() -> anyhow::Result<()> {
 | 
			
		||||
            log::warn!("The bucket does not seem to exists, trying to create it!")
 | 
			
		||||
        }
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            log::error!("Got unexpected error when querying bucket info: {}", e);
 | 
			
		||||
            log::error!("Got unexpected error when querying bucket info: {e}");
 | 
			
		||||
            return Err(BucketServiceError::FailedFetchBucketInfo.into());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ impl FromRequest for AccountInPath {
 | 
			
		||||
            Self::load_account_from_path(&auth, account_id)
 | 
			
		||||
                .await
 | 
			
		||||
                .map_err(|e| {
 | 
			
		||||
                    log::error!("Failed to extract account ID from URL! {}", e);
 | 
			
		||||
                    log::error!("Failed to extract account ID from URL! {e}");
 | 
			
		||||
                    actix_web::error::ErrorNotFound("Could not fetch account information!")
 | 
			
		||||
                })
 | 
			
		||||
        })
 | 
			
		||||
 
 | 
			
		||||
@@ -165,13 +165,13 @@ impl FromRequest for AuthExtractor {
 | 
			
		||||
                // Update last use (if needed)
 | 
			
		||||
                if token.shall_update_time_used() {
 | 
			
		||||
                    if let Err(e) = tokens_service::update_time_used(&token).await {
 | 
			
		||||
                        log::error!("Failed to refresh last usage of token! {}", e);
 | 
			
		||||
                        log::error!("Failed to refresh last usage of token! {e}");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Handle tokens expiration
 | 
			
		||||
                if token.is_expired() {
 | 
			
		||||
                    log::error!("Attempted to use expired token! {:?}", token);
 | 
			
		||||
                    log::error!("Attempted to use expired token! {token:?}");
 | 
			
		||||
                    return Err(actix_web::error::ErrorBadRequest("Token has expired!"));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ impl FromRequest for FileIdExtractor {
 | 
			
		||||
            Self::load_file_from_path(&auth, file_id)
 | 
			
		||||
                .await
 | 
			
		||||
                .map_err(|e| {
 | 
			
		||||
                    log::error!("Failed to extract file ID from URL! {}", e);
 | 
			
		||||
                    log::error!("Failed to extract file ID from URL! {e}");
 | 
			
		||||
                    actix_web::error::ErrorNotFound("Could not fetch file information!")
 | 
			
		||||
                })
 | 
			
		||||
        })
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ impl FromRequest for InboxEntryInPath {
 | 
			
		||||
            Self::load_inbox_entry_from_path(&auth, entry_id)
 | 
			
		||||
                .await
 | 
			
		||||
                .map_err(|e| {
 | 
			
		||||
                    log::error!("Failed to extract inbox entry ID from URL! {}", e);
 | 
			
		||||
                    log::error!("Failed to extract inbox entry ID from URL! {e}");
 | 
			
		||||
                    actix_web::error::ErrorNotFound("Could not fetch inbox entry information!")
 | 
			
		||||
                })
 | 
			
		||||
        })
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ impl FromRequest for MovementInPath {
 | 
			
		||||
            Self::load_movement_from_path(&auth, account_id)
 | 
			
		||||
                .await
 | 
			
		||||
                .map_err(|e| {
 | 
			
		||||
                    log::error!("Failed to extract movement ID from URL! {}", e);
 | 
			
		||||
                    log::error!("Failed to extract movement ID from URL! {e}");
 | 
			
		||||
                    actix_web::error::ErrorNotFound("Could not fetch movement information!")
 | 
			
		||||
                })
 | 
			
		||||
        })
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1916
									
								
								moneymgr_web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1916
									
								
								moneymgr_web/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -11,37 +11,37 @@
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@emotion/react": "^11.14.0",
 | 
			
		||||
    "@emotion/styled": "^11.14.0",
 | 
			
		||||
    "@fontsource/roboto": "^5.2.5",
 | 
			
		||||
    "@emotion/styled": "^11.14.1",
 | 
			
		||||
    "@fontsource/roboto": "^5.2.6",
 | 
			
		||||
    "@jsonjoy.com/base64": "^1.1.2",
 | 
			
		||||
    "@mdi/js": "^7.4.47",
 | 
			
		||||
    "@mdi/react": "^1.6.1",
 | 
			
		||||
    "@mui/icons-material": "^7.1.0",
 | 
			
		||||
    "@mui/material": "^7.1.0",
 | 
			
		||||
    "@mui/x-charts": "^8.3.1",
 | 
			
		||||
    "@mui/x-data-grid": "^8.3.1",
 | 
			
		||||
    "@mui/x-date-pickers": "^8.3.1",
 | 
			
		||||
    "@mui/icons-material": "^7.1.2",
 | 
			
		||||
    "@mui/material": "^7.1.2",
 | 
			
		||||
    "@mui/x-charts": "^8.7.0",
 | 
			
		||||
    "@mui/x-data-grid": "^8.7.0",
 | 
			
		||||
    "@mui/x-date-pickers": "^8.5.3",
 | 
			
		||||
    "date-and-time": "^3.6.0",
 | 
			
		||||
    "dayjs": "^1.11.13",
 | 
			
		||||
    "filesize": "^10.1.6",
 | 
			
		||||
    "qrcode.react": "^4.2.0",
 | 
			
		||||
    "react": "^19.1.0",
 | 
			
		||||
    "react-dom": "^19.1.0",
 | 
			
		||||
    "react-router": "^7.6.0",
 | 
			
		||||
    "react-router-dom": "^7.6.0",
 | 
			
		||||
    "ts-pattern": "^5.7.0"
 | 
			
		||||
    "react-router": "^7.6.3",
 | 
			
		||||
    "react-router-dom": "^7.6.3",
 | 
			
		||||
    "ts-pattern": "^5.7.1"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@eslint/js": "^9.26.0",
 | 
			
		||||
    "@types/react": "^19.1.4",
 | 
			
		||||
    "@types/react-dom": "^19.1.5",
 | 
			
		||||
    "@vitejs/plugin-react": "^4.4.1",
 | 
			
		||||
    "@eslint/js": "^9.30.1",
 | 
			
		||||
    "@types/react": "^19.1.8",
 | 
			
		||||
    "@types/react-dom": "^19.1.6",
 | 
			
		||||
    "@vitejs/plugin-react": "^4.6.0",
 | 
			
		||||
    "eslint": "^9.26.0",
 | 
			
		||||
    "eslint-plugin-react-dom": "^1.49.0",
 | 
			
		||||
    "eslint-plugin-react-dom": "^1.52.2",
 | 
			
		||||
    "eslint-plugin-react-hooks": "^5.2.0",
 | 
			
		||||
    "eslint-plugin-react-refresh": "^00.4.20",
 | 
			
		||||
    "eslint-plugin-react-x": "^1.49.0",
 | 
			
		||||
    "globals": "^16.1.0",
 | 
			
		||||
    "eslint-plugin-react-x": "^1.52.2",
 | 
			
		||||
    "globals": "^16.2.0",
 | 
			
		||||
    "typescript": "~5.8.3",
 | 
			
		||||
    "typescript-eslint": "^8.32.1",
 | 
			
		||||
    "vite": "^6.3.5"
 | 
			
		||||
 
 | 
			
		||||
@@ -31,12 +31,21 @@ export class APIClient {
 | 
			
		||||
    return URL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get the full URL at which the backend can be contacted
 | 
			
		||||
   */
 | 
			
		||||
  static ActualBackendURL(): string {
 | 
			
		||||
    const backendURL = this.backendURL();
 | 
			
		||||
    if (backendURL.startsWith("/")) return `${location.origin}${backendURL}`;
 | 
			
		||||
    else return backendURL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Check out whether the backend is accessed through
 | 
			
		||||
   * HTTPS or not
 | 
			
		||||
   */
 | 
			
		||||
  static IsBackendSecure(): boolean {
 | 
			
		||||
    return this.backendURL().startsWith("https");
 | 
			
		||||
    return this.ActualBackendURL().startsWith("https");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
 
 | 
			
		||||
@@ -268,7 +268,7 @@ function CreatedToken(p: { token: TokenWithSecret }): React.ReactElement {
 | 
			
		||||
          <div style={{ padding: "15px", backgroundColor: "white" }}>
 | 
			
		||||
            <QRCodeCanvas
 | 
			
		||||
              value={`moneymgr://api=${encodeURIComponent(
 | 
			
		||||
                APIClient.backendURL()
 | 
			
		||||
                APIClient.ActualBackendURL()
 | 
			
		||||
              )}&id=${p.token.id}&secret=${p.token.token}`}
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user