Compare commits

..

15 Commits

Author SHA1 Message Date
Félix MARQUET
202f2c979f Merge pull request #38 from BreizhHardware/dependabot/cargo/dev/serde_json-1.0.142
build(deps): bump serde_json from 1.0.140 to 1.0.142
2025-08-12 14:42:07 +02:00
dependabot[bot]
3d8b5bd726 build(deps): bump serde_json from 1.0.140 to 1.0.142
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.140 to 1.0.142.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.140...v1.0.142)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-version: 1.0.142
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 13:54:27 +00:00
Félix MARQUET
905c352eff Merge pull request #37 from BreizhHardware/dependabot/cargo/dev/tokio-1.47.1
build(deps): bump tokio from 1.45.1 to 1.47.1
2025-08-11 15:53:23 +02:00
Félix MARQUET
1c847be019 Merge pull request #34 from BreizhHardware/dependabot/cargo/dev/rand-0.9.2
build(deps): bump rand from 0.9.1 to 0.9.2
2025-08-11 15:53:09 +02:00
Félix MARQUET
b0e8c0ff39 Merge pull request #33 from BreizhHardware/dependabot/cargo/dev/rusqlite-0.37.0
build(deps): bump rusqlite from 0.36.0 to 0.37.0
2025-08-11 15:52:58 +02:00
Félix MARQUET
2af1314e4d Merge pull request #31 from BreizhHardware/dependabot/cargo/dev/reqwest-0.12.22
build(deps): bump reqwest from 0.12.20 to 0.12.22
2025-08-11 14:52:30 +02:00
dependabot[bot]
4019137602 build(deps): bump rand from 0.9.1 to 0.9.2
Bumps [rand](https://github.com/rust-random/rand) from 0.9.1 to 0.9.2.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/rand_core-0.9.1...rand_core-0.9.2)

---
updated-dependencies:
- dependency-name: rand
  dependency-version: 0.9.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 10:37:18 +00:00
dependabot[bot]
ed066c5d5b build(deps): bump reqwest from 0.12.20 to 0.12.22
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.20 to 0.12.22.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.20...v0.12.22)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-version: 0.12.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 10:35:45 +00:00
dependabot[bot]
a9923624a1 build(deps): bump rusqlite from 0.36.0 to 0.37.0
Bumps [rusqlite](https://github.com/rusqlite/rusqlite) from 0.36.0 to 0.37.0.
- [Release notes](https://github.com/rusqlite/rusqlite/releases)
- [Changelog](https://github.com/rusqlite/rusqlite/blob/master/Changelog.md)
- [Commits](https://github.com/rusqlite/rusqlite/compare/v0.36.0...v0.37.0)

---
updated-dependencies:
- dependency-name: rusqlite
  dependency-version: 0.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 10:35:15 +00:00
dependabot[bot]
eb9fa5954f build(deps): bump tokio from 1.45.1 to 1.47.1
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.45.1 to 1.47.1.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.45.1...tokio-1.47.1)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.47.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 10:34:56 +00:00
Félix MARQUET
f7ca4622b6 Merge pull request #30 from BreizhHardware/fix/config-reset
fix(main): Update environment variable handling and API startup logic
2025-08-10 11:45:35 +02:00
Félix MARQUET
9ef9179995 Merge pull request #29 from BreizhHardware/dependabot/cargo/dev/rand-0.9.1
build(deps): bump rand from 0.8.5 to 0.9.1
2025-07-01 12:41:15 +02:00
dependabot[bot]
f510233c55 build(deps): bump rand from 0.8.5 to 0.9.1
Bumps [rand](https://github.com/rust-random/rand) from 0.8.5 to 0.9.1.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/0.8.5...rand_core-0.9.1)

---
updated-dependencies:
- dependency-name: rand
  dependency-version: 0.9.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 09:57:25 +00:00
Félix MARQUET
2f033a6460 Merge pull request #28 from BreizhHardware/dependabot/cargo/dev/bcrypt-0.17.0
build(deps): bump bcrypt from 0.15.1 to 0.17.0
2025-07-01 11:56:18 +02:00
dependabot[bot]
5bac3d5bca build(deps): bump bcrypt from 0.15.1 to 0.17.0
Bumps [bcrypt](https://github.com/Keats/rust-bcrypt) from 0.15.1 to 0.17.0.
- [Commits](https://github.com/Keats/rust-bcrypt/compare/v0.15.1...v0.17.0)

---
updated-dependencies:
- dependency-name: bcrypt
  dependency-version: 0.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-30 03:16:36 +00:00
11 changed files with 138 additions and 673 deletions

96
Cargo.lock generated
View File

@@ -132,13 +132,13 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bcrypt"
version = "0.15.1"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e65938ed058ef47d92cf8b346cc76ef48984572ade631927e9937b5ffc7662c7"
checksum = "92758ad6077e4c76a6cadbce5005f666df70d4f13b19976b1a8062eef880040f"
dependencies = [
"base64 0.22.1",
"blowfish",
"getrandom 0.2.16",
"getrandom 0.3.3",
"subtle",
"zeroize",
]
@@ -501,7 +501,7 @@ dependencies = [
"env_logger",
"log",
"openssl",
"rand",
"rand 0.9.2",
"reqwest",
"rusqlite",
"serde",
@@ -675,7 +675,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
"socket2",
"socket2 0.5.10",
"tokio",
"tower-service",
"tracing",
@@ -752,7 +752,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2",
"socket2 0.5.10",
"system-configuration",
"tokio",
"tower-service",
@@ -910,6 +910,17 @@ dependencies = [
"generic-array",
]
[[package]]
name = "io-uring"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
dependencies = [
"bitflags",
"cfg-if",
"libc",
]
[[package]]
name = "ipnet"
version = "2.11.0"
@@ -980,9 +991,9 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libsqlite3-sys"
version = "0.34.0"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91632f3b4fb6bd1d72aa3d78f41ffecfcf2b1a6648d8c241dbe7dbfaf4875e15"
checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f"
dependencies = [
"cc",
"pkg-config",
@@ -1309,8 +1320,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
]
[[package]]
@@ -1320,7 +1341,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.3",
]
[[package]]
@@ -1332,6 +1363,15 @@ dependencies = [
"getrandom 0.2.16",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom 0.3.3",
]
[[package]]
name = "redox_syscall"
version = "0.5.12"
@@ -1372,9 +1412,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "reqwest"
version = "0.12.20"
version = "0.12.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813"
checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -1428,9 +1468,9 @@ dependencies = [
[[package]]
name = "rusqlite"
version = "0.36.0"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3de23c3319433716cf134eed225fe9986bc24f63bed9be9f20c329029e672dc7"
checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f"
dependencies = [
"bitflags",
"fallible-iterator",
@@ -1570,9 +1610,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.140"
version = "1.0.142"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
dependencies = [
"itoa",
"memchr",
@@ -1643,6 +1683,16 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "socket2"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "spin"
version = "0.9.8"
@@ -1758,20 +1808,22 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.45.1"
version = "1.47.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
dependencies = [
"backtrace",
"bytes",
"io-uring",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"slab",
"socket2 0.6.0",
"tokio-macros",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -1913,7 +1965,7 @@ dependencies = [
"http 1.3.1",
"httparse",
"log",
"rand",
"rand 0.8.5",
"sha1",
"thiserror",
"url",

View File

@@ -13,7 +13,7 @@ vendored-openssl = ["openssl/vendored"]
[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.12", features = ["json", "blocking"] }
rusqlite = { version = "0.36", features = ["bundled"] }
rusqlite = { version = "0.37", features = ["bundled"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
log = "0.4"
@@ -22,5 +22,5 @@ dotenv = "0.15"
chrono = { version = "0.4", features = ["serde"] }
warp = "0.3"
openssl = { version = "0.10", features = ["vendored"] }
rand = "0.8"
bcrypt = "0.15"
rand = "0.9"
bcrypt = "0.17"

View File

@@ -12,8 +12,7 @@ use crate::database::{
get_user_by_username, verify_password, create_user, create_session,
get_session, delete_session, get_app_settings, update_app_settings
};
use crate::models::{UserLogin, UserRegistration, AuthResponse, ApiResponse, AppSettings, GithubReleaseInfo};
use crate::notifications::{ntfy, discord, slack, gotify};
use crate::models::{UserLogin, UserRegistration, AuthResponse, ApiResponse, AppSettings};
#[derive(Debug, Serialize, Deserialize)]
struct RepoRequest {
@@ -129,35 +128,6 @@ pub async fn start_api() -> Result<(), Box<dyn std::error::Error + Send + Sync>>
.and(with_db(versions_db.clone()))
.and_then(is_configured);
// Test notification routes
let test_ntfy_route = warp::path("test")
.and(warp::path("ntfy"))
.and(warp::post())
.and(with_db(versions_db.clone()))
.and(with_auth())
.and_then(test_ntfy_notification);
let test_discord_route = warp::path("test")
.and(warp::path("discord"))
.and(warp::post())
.and(with_db(versions_db.clone()))
.and(with_auth())
.and_then(test_discord_notification);
let test_slack_route = warp::path("test")
.and(warp::path("slack"))
.and(warp::post())
.and(with_db(versions_db.clone()))
.and(with_auth())
.and_then(test_slack_notification);
let test_gotify_route = warp::path("test")
.and(warp::path("gotify"))
.and(warp::post())
.and(with_db(versions_db.clone()))
.and(with_auth())
.and_then(test_gotify_notification);
// Configure CORS
let cors = warp::cors()
.allow_any_origin()
@@ -178,10 +148,6 @@ pub async fn start_api() -> Result<(), Box<dyn std::error::Error + Send + Sync>>
.or(get_settings_route)
.or(update_settings_route)
.or(is_configured_route)
.or(test_ntfy_route)
.or(test_discord_route)
.or(test_slack_route)
.or(test_gotify_route)
.with(cors);
// Start the server
@@ -906,357 +872,3 @@ async fn is_configured(db: Arc<Mutex<Connection>>) -> Result<impl Reply, Rejecti
StatusCode::OK,
))
}
async fn test_ntfy_notification(db: Arc<Mutex<Connection>>, token: String) -> Result<impl Reply, Rejection> {
let conn = db.lock().await;
// Verify authentication
if let Ok(Some(session)) = get_session(&conn, &token) {
if session.expires_at < Utc::now() {
return Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Session expired".to_string(),
data: None,
}),
StatusCode::UNAUTHORIZED,
));
}
// Retrieve settings
match get_app_settings(&conn) {
Ok(Some(settings)) => {
if let Some(ntfy_url) = &settings.ntfy_url {
// Send a test notification
let result = ntfy::send_notification(
ntfy_url,
"Test Notification",
"Ceci est une notification de test depuis l'API GitHub-NTFY.",
).await;
match result {
Ok(_) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: true,
message: "Test notification sent successfully".to_string(),
data: None,
}),
StatusCode::OK,
))
},
Err(e) => {
error!("Error sending notification: {}", e);
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: format!("Error sending notification: {}", e),
data: None,
}),
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
} else {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "NTFY URL not configured".to_string(),
data: None,
}),
StatusCode::BAD_REQUEST,
))
}
},
Ok(None) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "No settings found".to_string(),
data: None,
}),
StatusCode::NOT_FOUND,
))
},
Err(_) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Error retrieving settings".to_string(),
data: None,
}),
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
} else {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Unauthorized".to_string(),
data: None,
}),
StatusCode::UNAUTHORIZED,
))
}
}
async fn test_discord_notification(db: Arc<Mutex<Connection>>, token: String) -> Result<impl Reply, Rejection> {
let conn = db.lock().await;
// Verify authentication
if let Ok(Some(session)) = get_session(&conn, &token) {
if session.expires_at < Utc::now() {
return Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Session expired".to_string(),
data: None,
}),
StatusCode::UNAUTHORIZED,
));
}
// Retrieve settings
match get_app_settings(&conn) {
Ok(Some(settings)) => {
if let Some(webhook_url) = &settings.discord_webhook_url {
// Send a test notification
let result = discord::send_notification(
webhook_url,
"Test Notification",
"Ceci est une notification de test depuis l'API GitHub-NTFY.",
).await;
match result {
Ok(_) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: true,
message: "Test notification sent successfully".to_string(),
data: None,
}),
StatusCode::OK,
))
},
Err(e) => {
error!("Error sending notification: {}", e);
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: format!("Error sending notification: {}", e),
data: None,
}),
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
} else {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Discord webhook URL not configured".to_string(),
data: None,
}),
StatusCode::BAD_REQUEST,
))
}
},
Ok(None) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "No settings found".to_string(),
data: None,
}),
StatusCode::NOT_FOUND,
))
},
Err(_) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Error retrieving settings".to_string(),
data: None,
}),
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
} else {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Unauthorized".to_string(),
data: None,
}),
StatusCode::UNAUTHORIZED,
))
}
}
async fn test_slack_notification(db: Arc<Mutex<Connection>>, token: String) -> Result<impl Reply, Rejection> {
let conn = db.lock().await;
// Verify authentication
if let Ok(Some(session)) = get_session(&conn, &token) {
if session.expires_at < Utc::now() {
return Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Session expired".to_string(),
data: None,
}),
StatusCode::UNAUTHORIZED,
));
}
// Retrieve settings
match get_app_settings(&conn) {
Ok(Some(settings)) => {
// Send a test notification
let result = slack::send_notification(
&settings.slack_webhook_url,
"Test Notification",
"Ceci est une notification de test depuis l'API GitHub-NTFY.",
).await;
match result {
Ok(_) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: true,
message: "Test notification sent successfully".to_string(),
data: None,
}),
StatusCode::OK,
))
},
Err(e) => {
error!("Error sending notification: {}", e);
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: format!("Error sending notification: {}", e),
data: None,
}),
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
},
Ok(None) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "No settings found".to_string(),
data: None,
}),
StatusCode::NOT_FOUND,
))
},
Err(_) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Error retrieving settings".to_string(),
data: None,
}),
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
} else {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Unauthorized".to_string(),
data: None,
}),
StatusCode::UNAUTHORIZED,
))
}
}
async fn test_gotify_notification(db: Arc<Mutex<Connection>>, token: String) -> Result<impl Reply, Rejection> {
let conn = db.lock().await;
// Verify authentication
if let Ok(Some(session)) = get_session(&conn, &token) {
if session.expires_at < Utc::now() {
return Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Session expired".to_string(),
data: None,
}),
StatusCode::UNAUTHORIZED,
));
}
// Retrieve settings
match get_app_settings(&conn) {
Ok(Some(settings)) => {
// Send a test notification
let result = gotify::send_notification(
&settings.gotify_url,
"Test Notification",
"Ceci est une notification de test depuis l'API GitHub-NTFY.",
).await;
match result {
Ok(_) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: true,
message: "Test notification sent successfully".to_string(),
data: None,
}),
StatusCode::OK,
))
},
Err(e) => {
error!("Error sending notification: {}", e);
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: format!("Error sending notification: {}", e),
data: None,
}),
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
},
Ok(None) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "No settings found".to_string(),
data: None,
}),
StatusCode::NOT_FOUND,
))
},
Err(_) => {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Error retrieving settings".to_string(),
data: None,
}),
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
} else {
Ok(warp::reply::with_status(
warp::reply::json(&ApiResponse::<()> {
success: false,
message: "Unauthorized".to_string(),
data: None,
}),
StatusCode::UNAUTHORIZED,
))
}
}

View File

@@ -82,28 +82,4 @@ pub async fn send_docker_notification(release: &DockerReleaseInfo, webhook_url:
error!("Error sending to Discord: {}", e);
}
}
}
pub async fn send_notification(webhook_url: &str, title: &str, message: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = reqwest::Client::new();
let data = json!({
"content": format!("**{}**\n{}", title, message),
"username": "GitHub Ntfy Test"
});
let response = client.post(webhook_url)
.header("Content-Type", "application/json")
.json(&data)
.send()
.await?;
if response.status().is_success() {
info!("Test notification sent to Discord successfully");
Ok(())
} else {
let error_msg = format!("Failed to send test notification to Discord. Status code: {}", response.status());
error!("{}", error_msg);
Err(error_msg.into())
}
}
}

View File

@@ -75,32 +75,4 @@ pub async fn send_docker_notification(release: &DockerReleaseInfo, token: &str,
error!("Error sending to Gotify: {}", e);
}
}
}
pub async fn send_notification(gotify_url: &str, title: &str, message: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = reqwest::Client::new();
// Note: For test notifications, we'll need to get the token from settings
// This function will be called from the API where we have access to settings
let url = format!("{}/message", gotify_url);
let content = json!({
"title": title,
"message": message,
"priority": 5
});
let response = client.post(&url)
.json(&content)
.send()
.await?;
if response.status().is_success() {
info!("Test notification sent to Gotify successfully");
Ok(())
} else {
let error_msg = format!("Failed to send test notification to Gotify. Status code: {}", response.status());
error!("{}", error_msg);
Err(error_msg.into())
}
}
}

View File

@@ -81,27 +81,4 @@ pub async fn send_docker_notification(release: &DockerReleaseInfo, auth: &str, n
error!("Error sending to Ntfy: {}", e);
}
}
}
pub async fn send_notification(ntfy_url: &str, title: &str, message: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = reqwest::Client::new();
let mut headers = HeaderMap::new();
headers.insert("Title", HeaderValue::from_str(title)?);
headers.insert("Priority", HeaderValue::from_static("default"));
let response = client.post(ntfy_url)
.headers(headers)
.body(message.to_string())
.send()
.await?;
if response.status().is_success() {
info!("Test notification sent to NTFY successfully");
Ok(())
} else {
let error_msg = format!("Failed to send test notification to NTFY. Status code: {}", response.status());
error!("{}", error_msg);
Err(error_msg.into())
}
}
}

View File

@@ -128,27 +128,4 @@ pub async fn send_docker_notification(release: &DockerReleaseInfo, webhook_url:
error!("Error sending to Slack: {}", e);
}
}
}
pub async fn send_notification(webhook_url: &str, title: &str, message: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = reqwest::Client::new();
let data = json!({
"text": format!("*{}*\n{}", title, message)
});
let response = client.post(webhook_url)
.header("Content-Type", "application/json")
.json(&data)
.send()
.await?;
if response.status().is_success() {
info!("Test notification sent to Slack successfully");
Ok(())
} else {
let error_msg = format!("Failed to send test notification to Slack. Status code: {}", response.status());
error!("{}", error_msg);
Err(error_msg.into())
}
}
}

View File

@@ -1,5 +0,0 @@
<template>
<div class="min-h-screen bg-gray-900 text-gray-200 flex items-center justify-center">
<slot />
</div>
</template>

View File

@@ -1,64 +1,63 @@
<template>
<div class="w-full max-w-md p-8 space-y-8 bg-gray-800 rounded-lg shadow-lg">
<div class="text-center">
<h1 class="text-2xl font-bold text-white">Login</h1>
<p class="mt-2 text-sm text-gray-400">Sign in to manage your notifications</p>
</div>
<form @submit.prevent="handleLogin" class="mt-8 space-y-6">
<div>
<input
id="username"
v-model="form.username"
type="text"
required
class="block w-full px-3 py-2 mt-1 text-white placeholder-gray-500 bg-gray-700 border border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
/>
<div class="flex items-center justify-center min-h-screen bg-gray-900">
<div class="w-full max-w-md p-8 space-y-8 bg-gray-800 rounded-lg shadow-lg">
<div class="text-center">
<h1 class="text-2xl font-bold text-white">Login</h1>
<p class="mt-2 text-sm text-gray-400">Sign in to manage your notifications</p>
</div>
<form @submit.prevent="handleLogin" class="mt-8 space-y-6">
<div>
<div>
<label for="password" class="block text-sm font-medium text-gray-400">Password</label>
<label for="username" class="block text-sm font-medium text-gray-400">Username</label>
<input
id="username"
v-model="form.username"
type="text"
required
class="block w-full px-3 py-2 mt-1 text-white placeholder-gray-500 bg-gray-700 border border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
/>
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-400">Password</label>
<input
id="password"
v-model="form.password"
type="password"
required
class="block w-full px-3 py-2 mt-1 text-white placeholder-gray-500 bg-gray-700 border border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
/>
</div>
<div v-if="error" class="p-3 text-sm text-red-500 bg-red-100 rounded-md">
id="password"
v-model="form.password"
type="password"
required
class="block w-full px-3 py-2 mt-1 text-white placeholder-gray-500 bg-gray-700 border border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
/>
</div>
{{ error }}
<div v-if="error" class="p-3 text-sm text-red-500 bg-red-100 rounded-md">
{{ error }}
</div>
</div>
<div>
<UButton
<div>
<UButton
type="submit"
color="primary"
block
:loading="loading"
>
Login
</UButton>
</div>
</form>
type="submit"
color="primary"
block
:loading="loading"
>
Login
</UButton>
</div>
</form>
<div class="text-center mt-4">
<p class="text-sm text-gray-400">
<div class="text-center mt-4">
<p class="text-sm text-gray-400">
First time?
<NuxtLink to="/onboarding" class="font-medium text-indigo-400 hover:text-indigo-300">
Setup your application
</NuxtLink>
</p>
First time?
<NuxtLink to="/onboarding" class="font-medium text-indigo-400 hover:text-indigo-300">
Setup your application
</NuxtLink>
</p>
</div>
</div>
</div>
</template>
<script setup>
// Utiliser le layout d'authentification
definePageMeta({
layout: 'auth'
})
const auth = useAuth();
const router = useRouter();

View File

@@ -1,5 +1,5 @@
<template>
<div class="min-h-screen p-6">
<div class="min-h-screen bg-gray-900 p-6">
<div class="max-w-3xl mx-auto bg-gray-800 rounded-lg shadow-lg overflow-hidden">
<div class="p-6 border-b border-gray-700">
<h1 class="text-2xl font-bold text-white">Application Setup</h1>
@@ -247,11 +247,6 @@
</template>
<script setup>
// Utiliser le layout d'authentification
definePageMeta({
layout: 'auth'
})
const auth = useAuth();
const router = useRouter();
const route = useRoute();

View File

@@ -1,5 +1,6 @@
<template>
<div>
<AppHeader />
<div class="container mx-auto px-4 py-8">
<h1 class="text-2xl font-bold text-white mb-8">Settings</h1>
@@ -14,19 +15,7 @@
<div class="space-y-6">
<!-- NTFY -->
<div>
<div class="flex justify-between items-center mb-2">
<h3 class="text-lg font-medium">NTFY</h3>
<UButton
@click="testNotification('ntfy')"
size="sm"
color="gray"
variant="outline"
:loading="testingNotifications.ntfy"
:disabled="!settings.ntfy_url"
>
Tester
</UButton>
</div>
<h3 class="text-lg font-medium mb-2">NTFY</h3>
<div class="space-y-2">
<UInput
v-model="settings.ntfy_url"
@@ -55,19 +44,7 @@
<!-- Discord -->
<div>
<div class="flex justify-between items-center mb-2">
<h3 class="text-lg font-medium">Discord</h3>
<UButton
@click="testNotification('discord')"
size="sm"
color="gray"
variant="outline"
:loading="testingNotifications.discord"
:disabled="!settings.discord_webhook_url"
>
Tester
</UButton>
</div>
<h3 class="text-lg font-medium mb-2">Discord</h3>
<UInput
v-model="settings.discord_webhook_url"
label="Discord Webhook URL"
@@ -78,19 +55,7 @@
<!-- Slack -->
<div>
<div class="flex justify-between items-center mb-2">
<h3 class="text-lg font-medium">Slack</h3>
<UButton
@click="testNotification('slack')"
size="sm"
color="gray"
variant="outline"
:loading="testingNotifications.slack"
:disabled="!settings.slack_webhook_url"
>
Tester
</UButton>
</div>
<h3 class="text-lg font-medium mb-2">Slack</h3>
<UInput
v-model="settings.slack_webhook_url"
label="Slack Webhook URL"
@@ -101,19 +66,7 @@
<!-- Gotify -->
<div>
<div class="flex justify-between items-center mb-2">
<h3 class="text-lg font-medium">Gotify</h3>
<UButton
@click="testNotification('gotify')"
size="sm"
color="gray"
variant="outline"
:loading="testingNotifications.gotify"
:disabled="!settings.gotify_url || !settings.gotify_token"
>
Tester
</UButton>
</div>
<h3 class="text-lg font-medium mb-2">Gotify</h3>
<div class="space-y-2">
<UInput
v-model="settings.gotify_url"
@@ -255,12 +208,6 @@ const settings = reactive({
const error = ref('');
const success = ref('');
const loading = ref(false);
const testingNotifications = reactive({
ntfy: false,
discord: false,
slack: false,
gotify: false
});
// Load current settings
async function loadSettings() {
@@ -344,41 +291,4 @@ async function saveSettings() {
loading.value = false;
}
}
// Function to test notifications
async function testNotification(type) {
try {
// Set loading state for the specific notification type
testingNotifications[type] = true;
error.value = '';
success.value = '';
// Send test notification via our API endpoint
const response = await fetch(`/test/${type}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': auth.token.value
}
});
if (!response.ok) {
const data = await response.json();
throw new Error(data.message || 'Error sending test notification');
}
const data = await response.json();
if (data.success) {
success.value = `Notification de test envoyée avec succès via ${type.toUpperCase()}`;
} else {
throw new Error(data.message || 'Error sending test notification');
}
} catch (err) {
error.value = err.message || 'Une erreur est survenue lors de l\'envoi de la notification de test';
} finally {
// Reset loading state for the specific notification type
testingNotifications[type] = false;
}
}
</script>