Compare commits
31 Commits
1055fadc48
...
renovate/e
Author | SHA1 | Date | |
---|---|---|---|
fdade90266 | |||
baf62aa2a5 | |||
c19d46a50f | |||
f001c618cd | |||
f9d46e46a5 | |||
96f1bf589c | |||
467393dad0 | |||
f619f26e93 | |||
cc4ce19af2 | |||
192dc5827b | |||
37674a6229 | |||
ef86667029 | |||
07f63a96fa | |||
fa88a3c9ed | |||
85c6a0b955 | |||
21ee97b8a4 | |||
119f026a21 | |||
d72acfac9b | |||
77c8866bb8 | |||
133f235639 | |||
7ef0499abf | |||
1383da4483 | |||
4f1a9d0865 | |||
31803feaa9 | |||
aad32f9c25 | |||
1ea2bd6acf | |||
a085116018 | |||
952a66042c | |||
6cf6ab5a37 | |||
1781318fdf | |||
2560962684 |
@ -6,7 +6,7 @@ name: default
|
|||||||
steps:
|
steps:
|
||||||
# Frontend
|
# Frontend
|
||||||
- name: web_build
|
- name: web_build
|
||||||
image: node:24
|
image: node:23
|
||||||
volumes:
|
volumes:
|
||||||
- name: web_app
|
- name: web_app
|
||||||
path: /tmp/web_build
|
path: /tmp/web_build
|
||||||
|
16
moneymgr_backend/Cargo.lock
generated
16
moneymgr_backend/Cargo.lock
generated
@ -724,9 +724,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.40"
|
version = "4.5.41"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
|
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@ -734,9 +734,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.40"
|
version = "4.5.41"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
|
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@ -746,9 +746,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.40"
|
version = "4.5.41"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
|
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -1100,9 +1100,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diesel"
|
name = "diesel"
|
||||||
version = "2.2.11"
|
version = "2.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a917a9209950404d5be011c81d081a2692a822f73c3d6af586f0cab5ff50f614"
|
checksum = "229850a212cd9b84d4f0290ad9d294afc0ae70fccaa8949dbe8b43ffafa1e20c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -6,9 +6,9 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
env_logger = "0.11.8"
|
env_logger = "0.11.8"
|
||||||
log = "0.4.27"
|
log = "0.4.27"
|
||||||
diesel = { version = "2.2.11", features = ["postgres", "r2d2"] }
|
diesel = { version = "2.2.12", features = ["postgres", "r2d2"] }
|
||||||
diesel_migrations = "2.2.0"
|
diesel_migrations = "2.2.0"
|
||||||
clap = { version = "4.5.40", features = ["env", "derive"] }
|
clap = { version = "4.5.41", features = ["env", "derive"] }
|
||||||
actix-web = "4.11.0"
|
actix-web = "4.11.0"
|
||||||
actix-cors = "0.7.1"
|
actix-cors = "0.7.1"
|
||||||
actix-multipart = "0.7.2"
|
actix-multipart = "0.7.2"
|
||||||
|
@ -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!")
|
log::warn!("The bucket does not seem to exists, trying to create it!")
|
||||||
}
|
}
|
||||||
Err(e) => {
|
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());
|
return Err(BucketServiceError::FailedFetchBucketInfo.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ impl FromRequest for AccountInPath {
|
|||||||
Self::load_account_from_path(&auth, account_id)
|
Self::load_account_from_path(&auth, account_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.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!")
|
actix_web::error::ErrorNotFound("Could not fetch account information!")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -56,7 +56,7 @@ impl FromRequest for AuthExtractor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
// Check for authentication using OpenID
|
// Check for authentication using API token
|
||||||
if let Some(token) = req.headers().get(constants::API_TOKEN_HEADER) {
|
if let Some(token) = req.headers().get(constants::API_TOKEN_HEADER) {
|
||||||
let Ok(jwt_token) = token.to_str() else {
|
let Ok(jwt_token) = token.to_str() else {
|
||||||
return Err(actix_web::error::ErrorBadRequest(
|
return Err(actix_web::error::ErrorBadRequest(
|
||||||
@ -165,13 +165,13 @@ impl FromRequest for AuthExtractor {
|
|||||||
// Update last use (if needed)
|
// Update last use (if needed)
|
||||||
if token.shall_update_time_used() {
|
if token.shall_update_time_used() {
|
||||||
if let Err(e) = tokens_service::update_time_used(&token).await {
|
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
|
// Handle tokens expiration
|
||||||
if token.is_expired() {
|
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!"));
|
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)
|
Self::load_file_from_path(&auth, file_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.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!")
|
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)
|
Self::load_inbox_entry_from_path(&auth, entry_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.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!")
|
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)
|
Self::load_movement_from_path(&auth, account_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.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!")
|
actix_web::error::ErrorNotFound("Could not fetch movement information!")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
1667
moneymgr_web/package-lock.json
generated
1667
moneymgr_web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,36 +11,36 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.0",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@fontsource/roboto": "^5.2.6",
|
"@fontsource/roboto": "^5.2.6",
|
||||||
"@jsonjoy.com/base64": "^1.1.2",
|
"@jsonjoy.com/base64": "^1.1.2",
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@mdi/react": "^1.6.1",
|
"@mdi/react": "^1.6.1",
|
||||||
"@mui/icons-material": "^7.1.2",
|
"@mui/icons-material": "^7.1.2",
|
||||||
"@mui/material": "^7.1.2",
|
"@mui/material": "^7.1.2",
|
||||||
"@mui/x-charts": "^8.5.3",
|
"@mui/x-charts": "^8.8.0",
|
||||||
"@mui/x-data-grid": "^8.5.3",
|
"@mui/x-data-grid": "^8.8.0",
|
||||||
"@mui/x-date-pickers": "^8.5.3",
|
"@mui/x-date-pickers": "^8.7.0",
|
||||||
"date-and-time": "^3.6.0",
|
"date-and-time": "^3.6.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"filesize": "^10.1.6",
|
"filesize": "^10.1.6",
|
||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-router": "^7.6.2",
|
"react-router": "^7.6.3",
|
||||||
"react-router-dom": "^7.6.2",
|
"react-router-dom": "^7.6.3",
|
||||||
"ts-pattern": "^5.7.1"
|
"ts-pattern": "^5.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.29.0",
|
"@eslint/js": "^9.31.0",
|
||||||
"@types/react": "^19.1.8",
|
"@types/react": "^19.1.8",
|
||||||
"@types/react-dom": "^19.1.6",
|
"@types/react-dom": "^19.1.6",
|
||||||
"@vitejs/plugin-react": "^4.6.0",
|
"@vitejs/plugin-react": "^4.6.0",
|
||||||
"eslint": "^9.26.0",
|
"eslint": "^9.26.0",
|
||||||
"eslint-plugin-react-dom": "^1.49.0",
|
"eslint-plugin-react-dom": "^1.52.3",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^00.4.20",
|
"eslint-plugin-react-refresh": "^00.4.20",
|
||||||
"eslint-plugin-react-x": "^1.52.2",
|
"eslint-plugin-react-x": "^1.52.3",
|
||||||
"globals": "^16.2.0",
|
"globals": "^16.2.0",
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"typescript-eslint": "^8.32.1",
|
"typescript-eslint": "^8.32.1",
|
||||||
|
@ -31,12 +31,21 @@ export class APIClient {
|
|||||||
return URL;
|
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
|
* Check out whether the backend is accessed through
|
||||||
* HTTPS or not
|
* HTTPS or not
|
||||||
*/
|
*/
|
||||||
static IsBackendSecure(): boolean {
|
static IsBackendSecure(): boolean {
|
||||||
return this.backendURL().startsWith("https");
|
return this.ActualBackendURL().startsWith("https");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,9 +2,11 @@ import DeleteIcon from "@mui/icons-material/DeleteOutlined";
|
|||||||
import DriveFileMoveOutlineIcon from "@mui/icons-material/DriveFileMoveOutline";
|
import DriveFileMoveOutlineIcon from "@mui/icons-material/DriveFileMoveOutline";
|
||||||
import LinkOffIcon from "@mui/icons-material/LinkOff";
|
import LinkOffIcon from "@mui/icons-material/LinkOff";
|
||||||
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||||
|
import ClearIcon from "@mui/icons-material/Clear";
|
||||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||||
import {
|
import {
|
||||||
IconButton,
|
IconButton,
|
||||||
|
InputAdornment,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
TextField,
|
TextField,
|
||||||
@ -392,6 +394,19 @@ function MovementsTable(p: {
|
|||||||
setFilter(e.target.value);
|
setFilter(e.target.value);
|
||||||
}}
|
}}
|
||||||
style={{ padding: "0px", flex: 1 }}
|
style={{ padding: "0px", flex: 1 }}
|
||||||
|
slotProps={{
|
||||||
|
input: {
|
||||||
|
endAdornment: filter.length > 0 && (
|
||||||
|
<InputAdornment position="end">
|
||||||
|
<Tooltip title="Clear current filter">
|
||||||
|
<IconButton size="small" onClick={() => { setFilter(""); }}>
|
||||||
|
<ClearIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<span style={{ flex: 1 }}></span>
|
<span style={{ flex: 1 }}></span>
|
||||||
<Tooltip title="Refresh table">
|
<Tooltip title="Refresh table">
|
||||||
|
@ -268,7 +268,7 @@ function CreatedToken(p: { token: TokenWithSecret }): React.ReactElement {
|
|||||||
<div style={{ padding: "15px", backgroundColor: "white" }}>
|
<div style={{ padding: "15px", backgroundColor: "white" }}>
|
||||||
<QRCodeCanvas
|
<QRCodeCanvas
|
||||||
value={`moneymgr://api=${encodeURIComponent(
|
value={`moneymgr://api=${encodeURIComponent(
|
||||||
APIClient.backendURL()
|
APIClient.ActualBackendURL()
|
||||||
)}&id=${p.token.id}&secret=${p.token.token}`}
|
)}&id=${p.token.id}&secret=${p.token.token}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user