mirror of
https://codeberg.org/demostf/backup.git
synced 2026-06-03 09:54:18 +02:00
allow specifying access key for backing up private demos
This commit is contained in:
parent
24f9594223
commit
a1b3b598e6
6 changed files with 50 additions and 15 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -78,6 +78,7 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"main_error",
|
"main_error",
|
||||||
"md5 0.8.0",
|
"md5 0.8.0",
|
||||||
|
"secretfile",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
@ -940,6 +941,12 @@ version = "1.0.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "secretfile"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e1f99fdbcc14f9d8292bd680cb6ec6c265d141e5073f61024fcddc891e405c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.228"
|
version = "1.0.228"
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ tracing = "0.1.44"
|
||||||
tracing-subscriber = "0.3.23"
|
tracing-subscriber = "0.3.23"
|
||||||
futures-util = "0.3.32"
|
futures-util = "0.3.32"
|
||||||
md5 = "0.8.0"
|
md5 = "0.8.0"
|
||||||
|
secretfile = "0.1.1"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|
|
||||||
12
README.md
12
README.md
|
|
@ -2,13 +2,19 @@
|
||||||
|
|
||||||
Backup program for demos.tf demos.
|
Backup program for demos.tf demos.
|
||||||
|
|
||||||
A simple program that incrementally backs up every demo file from demos.tf to a local directory.
|
A simple program that incrementally backs up every demo file from demos.tf to a
|
||||||
|
local directory.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
The following environment variables are required for the program
|
The following environment variables are required for the program:
|
||||||
|
|
||||||
STORAGE_ROOT: The directory to store the demos in
|
STORAGE_ROOT: The directory to store the demos in
|
||||||
STATE_FILE: The textfile to store the backup progress in between runs
|
STATE_FILE: The textfile to store the backup progress in between runs
|
||||||
|
|
||||||
The program will look in a .env file if the variables aren't set in the environment
|
The following optional environment variables can additionaly be supplied:
|
||||||
|
|
||||||
|
ACCESS_KEY_FILE: File containig the api access key, required to backup private demos
|
||||||
|
|
||||||
|
The program will look in a .env file if the variables aren't set in the
|
||||||
|
environment
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,10 @@ in {
|
||||||
default = "*:0/10";
|
default = "*:0/10";
|
||||||
description = "Interval to run the service";
|
description = "Interval to run the service";
|
||||||
};
|
};
|
||||||
|
keyFile = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
description = "access key file path";
|
||||||
|
};
|
||||||
|
|
||||||
package = mkOption {
|
package = mkOption {
|
||||||
type = types.package;
|
type = types.package;
|
||||||
|
|
@ -48,15 +52,24 @@ in {
|
||||||
systemd.services.demostf-backup = {
|
systemd.services.demostf-backup = {
|
||||||
description = "Backup demos for demos.tf";
|
description = "Backup demos for demos.tf";
|
||||||
|
|
||||||
environment = {
|
environment =
|
||||||
|
{
|
||||||
STORAGE_ROOT = cfg.target;
|
STORAGE_ROOT = cfg.target;
|
||||||
SOURCE = cfg.api;
|
SOURCE = cfg.api;
|
||||||
STATE_FILE = cfg.stateFile;
|
STATE_FILE = cfg.stateFile;
|
||||||
RUST_LOG = cfg.logLevel;
|
RUST_LOG = cfg.logLevel;
|
||||||
|
}
|
||||||
|
// optionalAttrs (cfg.keyFile != null) {
|
||||||
|
ACCESS_KEY_FILE = "$CREDENTIALS_DIRECTORY/api_key";
|
||||||
};
|
};
|
||||||
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = "${cfg.package}/bin/demostf-backup";
|
ExecStart = "${cfg.package}/bin/demostf-backup";
|
||||||
|
|
||||||
|
LoadCredential = optionals (cfg.keyFile != null) [
|
||||||
|
"api_key:${cfg.keyFile}"
|
||||||
|
];
|
||||||
|
|
||||||
ReadWritePaths = [cfg.target cfg.stateFile];
|
ReadWritePaths = [cfg.target cfg.stateFile];
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
User = cfg.user;
|
User = cfg.user;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::store::Store;
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
use crate::store::Store;
|
||||||
use demostf_client::{ApiClient, Demo, ListOrder, ListParams};
|
use demostf_client::{ApiClient, Demo, ListOrder, ListParams};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
|
|
@ -11,11 +11,13 @@ pub struct Backup {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Backup {
|
impl Backup {
|
||||||
pub fn new(store: Store) -> Self {
|
pub fn new(store: Store, access_key: Option<String>) -> Self {
|
||||||
Backup {
|
let mut client = ApiClient::new();
|
||||||
store,
|
if let Some(access_key) = access_key {
|
||||||
client: ApiClient::new(),
|
info!("using access key");
|
||||||
|
client.set_access_key(access_key);
|
||||||
}
|
}
|
||||||
|
Backup { store, client }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all, fields(demo.id = demo.id, demo.name = name))]
|
#[instrument(skip_all, fields(demo.id = demo.id, demo.name = name))]
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,13 @@ async fn main() -> Result<(), MainError> {
|
||||||
let mut args: HashMap<_, _> = dotenvy::vars().collect();
|
let mut args: HashMap<_, _> = dotenvy::vars().collect();
|
||||||
let store = Store::new(args.get("STORAGE_ROOT").expect("no STORAGE_ROOT set"));
|
let store = Store::new(args.get("STORAGE_ROOT").expect("no STORAGE_ROOT set"));
|
||||||
let state_path = PathBuf::from(args.remove("STATE_FILE").expect("no STATE_FILE set"));
|
let state_path = PathBuf::from(args.remove("STATE_FILE").expect("no STATE_FILE set"));
|
||||||
let backup = Backup::new(store);
|
let key_file = args.remove("ACCESS_KEY_FILE");
|
||||||
|
let access_key = key_file
|
||||||
|
.as_deref()
|
||||||
|
.map(secretfile::load)
|
||||||
|
.transpose()?
|
||||||
|
.filter(|key| !key.is_empty());
|
||||||
|
let backup = Backup::new(store, access_key);
|
||||||
|
|
||||||
let last_page = if state_path.is_file() {
|
let last_page = if state_path.is_file() {
|
||||||
max(
|
max(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue