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.
|
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
|
## 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.
|
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 sync;
|
||||||
pub mod folders;
|
pub mod folders;
|
||||||
pub mod import;
|
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?)
|
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"
|
name = "warden-worker"
|
||||||
main = "build/index.js"
|
main = "build/index.js"
|
||||||
compatibility_date = "2025-09-19"
|
compatibility_date = "2025-09-19"
|
||||||
|
keep_vars = true
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
command = "cargo install -q worker-build && worker-build --release"
|
command = "cargo install -q worker-build && worker-build --release"
|
||||||
|
|
@ -8,7 +9,16 @@ command = "cargo install -q worker-build && worker-build --release"
|
||||||
[vars]
|
[vars]
|
||||||
# Optional: Set the batch size for imports. Defaults to 30 if not set.
|
# 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).
|
# 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]]
|
[[d1_databases]]
|
||||||
binding = "vault1"
|
binding = "vault1"
|
||||||
|
|
@ -17,6 +27,11 @@ database_id = "${D1_DATABASE_ID}"
|
||||||
|
|
||||||
[env.dev]
|
[env.dev]
|
||||||
name = "warden-worker-dev"
|
name = "warden-worker-dev"
|
||||||
|
keep_vars = true
|
||||||
|
|
||||||
|
# Dev environment also needs cron triggers
|
||||||
|
[env.dev.triggers]
|
||||||
|
crons = ["0 3 * * *"]
|
||||||
|
|
||||||
[[env.dev.d1_databases]]
|
[[env.dev.d1_databases]]
|
||||||
binding = "vault1"
|
binding = "vault1"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue