feat: add cron-triggered task for purging soft-deleted ciphers
This commit is contained in:
parent
ffb8823f4c
commit
3bd68ababf
5 changed files with 153 additions and 1 deletions
29
README.md
29
README.md
|
|
@ -104,6 +104,35 @@ There are no immediate plans to implement these features. The primary goal of th
|
|||
|
||||
This project requires minimal configuration. The main configuration is done in the `wrangler.toml` file, where you specify your D1 database binding.
|
||||
|
||||
### Other Environment Variables
|
||||
|
||||
You can configure the following environment variables in `wrangler.toml` under the `[vars]` section, or set them via Cloudflare Dashboard:
|
||||
|
||||
* **`TRASH_AUTO_DELETE_DAYS`** (Optional, Default: `30`)
|
||||
|
||||
Number of days to keep soft-deleted items before automatically purging them. When a cipher is deleted, it's marked with a `deleted_at` timestamp (soft delete). After the specified number of days, the item will be permanently removed from the database.
|
||||
|
||||
* Set to `0` or a negative value to disable automatic purging
|
||||
* Defaults to `30` days if not specified
|
||||
* Example: `TRASH_AUTO_DELETE_DAYS = "7"` to keep deleted items for 7 days
|
||||
|
||||
* **`IMPORT_BATCH_SIZE`** (Optional, Default: `30`)
|
||||
|
||||
Number of records to process in each batch when importing data. This helps manage memory usage and processing time for large imports.
|
||||
|
||||
* Set to `0` to disable batching (all records imported in a single batch)
|
||||
* Defaults to `30` records per batch if not specified
|
||||
* Example: `IMPORT_BATCH_SIZE = "50"` to process 50 records per batch
|
||||
|
||||
### Scheduled Tasks (Cron)
|
||||
|
||||
The worker includes a scheduled task that runs automatically to clean up soft-deleted items. By default, this task runs daily at 03:00 UTC.
|
||||
|
||||
* **Automatic Cleanup:** The scheduled task automatically purges ciphers that have been soft-deleted for longer than the `TRASH_AUTO_DELETE_DAYS` period
|
||||
* **Schedule:** Configured in `wrangler.toml` under `[triggers]` section with cron expression `"0 3 * * *"` (daily at 03:00 UTC)
|
||||
|
||||
You can modify the cron schedule in `wrangler.toml` if you want to run the cleanup task at a different time or frequency. See [Cloudflare Cron Triggers documentation](https://developers.cloudflare.com/workers/configuration/cron-triggers/) for cron expression syntax.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! If you find a bug, have a feature request, or want to improve the code, please open an issue or submit a pull request.
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ pub mod identity;
|
|||
pub mod sync;
|
||||
pub mod folders;
|
||||
pub mod import;
|
||||
pub mod purge;
|
||||
|
|
|
|||
84
src/handlers/purge.rs
Normal file
84
src/handlers/purge.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
//! Purge handler for cleaning up soft-deleted ciphers
|
||||
//!
|
||||
//! This module handles the automatic cleanup of ciphers that have been
|
||||
//! soft-deleted (marked with deleted_at) for longer than the configured
|
||||
//! retention period.
|
||||
|
||||
use chrono::{Duration, Utc};
|
||||
use worker::{query, D1Database, Env};
|
||||
|
||||
/// Default number of days to keep soft-deleted items before purging
|
||||
const DEFAULT_PURGE_DAYS: i64 = 30;
|
||||
|
||||
/// Get the purge threshold days from environment variable or use default
|
||||
fn get_purge_days(env: &Env) -> i64 {
|
||||
env.var("TRASH_AUTO_DELETE_DAYS")
|
||||
.ok()
|
||||
.and_then(|v| v.to_string().parse::<i64>().ok())
|
||||
.unwrap_or(DEFAULT_PURGE_DAYS)
|
||||
}
|
||||
|
||||
/// Purge soft-deleted ciphers that are older than the configured threshold.
|
||||
///
|
||||
/// This function:
|
||||
/// 1. Calculates the cutoff timestamp based on TRASH_AUTO_DELETE_DAYS env var (default: 30 days)
|
||||
/// 2. Deletes all ciphers where deleted_at is not null and older than the cutoff
|
||||
/// 3. If TRASH_AUTO_DELETE_DAYS is set to 0 or negative, skips purging (disabled)
|
||||
///
|
||||
/// Returns the number of purged records on success.
|
||||
pub async fn purge_deleted_ciphers(env: &Env) -> Result<u32, worker::Error> {
|
||||
let purge_days = get_purge_days(env);
|
||||
|
||||
// If purge_days is 0 or negative, auto-purge is disabled
|
||||
if purge_days <= 0 {
|
||||
log::info!("Auto-purge is disabled (TRASH_AUTO_DELETE_DAYS <= 0)");
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let db: D1Database = env.d1("vault1")?;
|
||||
|
||||
// Calculate the cutoff timestamp
|
||||
let now = Utc::now();
|
||||
let cutoff = now - Duration::days(purge_days);
|
||||
let cutoff_str = cutoff.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
|
||||
|
||||
log::info!(
|
||||
"Purging soft-deleted ciphers older than {} days (before {})",
|
||||
purge_days,
|
||||
cutoff_str
|
||||
);
|
||||
|
||||
// First, count the records to be deleted (for logging purposes)
|
||||
let count_result = query!(
|
||||
&db,
|
||||
"SELECT COUNT(*) as count FROM ciphers WHERE deleted_at IS NOT NULL AND deleted_at < ?1",
|
||||
cutoff_str
|
||||
)?
|
||||
.first::<CountResult>(None)
|
||||
.await?;
|
||||
|
||||
let count = count_result.map(|r| r.count).unwrap_or(0);
|
||||
|
||||
if count > 0 {
|
||||
// Delete the records
|
||||
query!(
|
||||
&db,
|
||||
"DELETE FROM ciphers WHERE deleted_at IS NOT NULL AND deleted_at < ?1",
|
||||
cutoff_str
|
||||
)?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
log::info!("Successfully purged {} soft-deleted cipher(s)", count);
|
||||
} else {
|
||||
log::info!("No soft-deleted ciphers to purge");
|
||||
}
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
/// Helper struct for count query result
|
||||
#[derive(serde::Deserialize)]
|
||||
struct CountResult {
|
||||
count: u32,
|
||||
}
|
||||
23
src/lib.rs
23
src/lib.rs
|
|
@ -30,3 +30,26 @@ pub async fn main(
|
|||
|
||||
Ok(app.call(req).await?)
|
||||
}
|
||||
|
||||
/// Scheduled event handler for cron-triggered tasks.
|
||||
///
|
||||
/// This handler is triggered by Cloudflare's cron triggers configured in wrangler.toml.
|
||||
/// It performs automatic cleanup of soft-deleted ciphers that have exceeded the
|
||||
/// retention period (default: 30 days, configurable via TRASH_AUTO_DELETE_DAYS env var).
|
||||
#[event(scheduled)]
|
||||
pub async fn scheduled(_event: ScheduledEvent, env: Env, _ctx: ScheduleContext) {
|
||||
// Set up logging
|
||||
console_error_panic_hook::set_once();
|
||||
let _ = console_log::init_with_level(log::Level::Debug);
|
||||
|
||||
log::info!("Scheduled task triggered: purging soft-deleted ciphers");
|
||||
|
||||
match handlers::purge::purge_deleted_ciphers(&env).await {
|
||||
Ok(count) => {
|
||||
log::info!("Scheduled purge completed: {} cipher(s) removed", count);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Scheduled purge failed: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
name = "warden-worker"
|
||||
main = "build/index.js"
|
||||
compatibility_date = "2025-09-19"
|
||||
keep_vars = true
|
||||
|
||||
[build]
|
||||
command = "cargo install -q worker-build && worker-build --release"
|
||||
|
|
@ -8,7 +9,16 @@ command = "cargo install -q worker-build && worker-build --release"
|
|||
[vars]
|
||||
# Optional: Set the batch size for imports. Defaults to 30 if not set.
|
||||
# Set to 0 means no batching (all records imported in a single batch).
|
||||
# IMPORT_BATCH_SIZE = "30"
|
||||
# IMPORT_BATCH_SIZE = "30"
|
||||
|
||||
# Number of days to keep soft-deleted items before auto-purging.
|
||||
# Defaults to 30 days if not set. Set to 0 to disable auto-purge.
|
||||
# TRASH_AUTO_DELETE_DAYS = "30"
|
||||
|
||||
# Cron triggers for scheduled tasks
|
||||
# Runs daily at 03:00 UTC to purge soft-deleted ciphers
|
||||
[triggers]
|
||||
crons = ["0 3 * * *"]
|
||||
|
||||
[[d1_databases]]
|
||||
binding = "vault1"
|
||||
|
|
@ -17,6 +27,11 @@ database_id = "${D1_DATABASE_ID}"
|
|||
|
||||
[env.dev]
|
||||
name = "warden-worker-dev"
|
||||
keep_vars = true
|
||||
|
||||
# Dev environment also needs cron triggers
|
||||
[env.dev.triggers]
|
||||
crons = ["0 3 * * *"]
|
||||
|
||||
[[env.dev.d1_databases]]
|
||||
binding = "vault1"
|
||||
|
|
|
|||
Loading…
Reference in a new issue