feat(infra): pass Blossom S3 config to Zooid with schema key prefix #69
@@ -26,6 +26,13 @@ LIVEKIT_URL=
|
||||
LIVEKIT_API_KEY=
|
||||
LIVEKIT_API_SECRET=
|
||||
|
||||
# Blossom S3 (optional; when region, bucket, access key, and secret are all set, relays with blossom enabled sync with adapter s3 and key_prefix = relay schema)
|
||||
BLOSSOM_S3_ENDPOINT=
|
||||
BLOSSOM_S3_REGION=
|
||||
BLOSSOM_S3_BUCKET=
|
||||
BLOSSOM_S3_ACCESS_KEY=
|
||||
BLOSSOM_S3_SECRET_KEY=
|
||||
|
||||
# Billing
|
||||
NWC_URL= # Nostr Wallet Connect URL for generating Lightning invoices
|
||||
ENCRYPTION_SECRET= # Nostr secret key (hex or nsec) used to encrypt tenant NWC URLs at rest
|
||||
|
||||
@@ -43,6 +43,11 @@ Environment variables:
|
||||
| `LIVEKIT_URL` | LiveKit URL sent to zooid when relay livekit is enabled | _optional_ |
|
||||
| `LIVEKIT_API_KEY` | LiveKit API key sent to zooid | _optional_ |
|
||||
| `LIVEKIT_API_SECRET` | LiveKit API secret sent to zooid | _optional_ |
|
||||
| `BLOSSOM_S3_ENDPOINT` | S3-compatible endpoint URL for Blossom; omit for AWS S3 | _optional_ |
|
||||
| `BLOSSOM_S3_REGION` | S3 region; with bucket, access key, and secret enables S3 for Blossom | _optional_ |
|
||||
| `BLOSSOM_S3_BUCKET` | S3 bucket name | _optional_ |
|
||||
| `BLOSSOM_S3_ACCESS_KEY` | S3 access key ID | _optional_ |
|
||||
| `BLOSSOM_S3_SECRET_KEY` | S3 secret access key | _optional_ |
|
||||
| `NWC_URL` | Platform NWC URL used to generate BOLT11 invoices | _required for invoice generation_ |
|
||||
| `ENCRYPTION_SECRET` | Nostr secret key (hex or nsec) used to encrypt tenant NWC URLs at rest | _required_ |
|
||||
| `STRIPE_SECRET_KEY` | Stripe API secret key used for billing API operations | _required_ |
|
||||
|
||||
@@ -5,6 +5,7 @@ Infra is a service which listens for activity and synchronizes relay updates to
|
||||
Members:
|
||||
|
||||
- `api_url: String` - the URL of the zooid instance to be managed, from `ZOOID_API_URL`
|
||||
- `blossom_s3: Option<BlossomS3Sync>` - shared Blossom S3 settings from `BLOSSOM_S3_*` when region, bucket, access key, and secret are all non-empty after trim
|
||||
- `query: Query`
|
||||
- `command: Command`
|
||||
|
||||
@@ -36,3 +37,4 @@ Members:
|
||||
- Otherwise, sends `PATCH /relay/:id` to update it.
|
||||
- Includes `secret` only for relay creation (`POST`) so updates do not rotate relay identity.
|
||||
- Passes relay configuration in the body including host, schema, inactive flag, info, policy, groups, management, blossom, livekit, push, and roles.
|
||||
- When `blossom_s3` is configured and the relay has blossom enabled, the blossom section includes `adapter: "s3"`, S3 fields from the environment, and `s3.key_prefix` set to the relay's `schema`. Otherwise blossom omits S3 (zooid defaults to local storage) or sends `{ "enabled": false }` when blossom is disabled.
|
||||
|
||||
+94
-1
@@ -10,6 +10,47 @@ const RELAY_SYNC_RETRY_BASE_DELAY_SECS: u64 = 30;
|
||||
const RELAY_SYNC_RETRY_MAX_DELAY_SECS: u64 = 15 * 60;
|
||||
const RELAY_SYNC_RETRY_MAX_ATTEMPTS: usize = 6;
|
||||
|
||||
/// Blossom S3 settings from env; relay sync sets `key_prefix` to the relay schema.
|
||||
#[derive(Clone)]
|
||||
struct BlossomS3Sync {
|
||||
endpoint: String,
|
||||
region: String,
|
||||
bucket: String,
|
||||
access_key: String,
|
||||
secret_key: String,
|
||||
}
|
||||
|
||||
impl BlossomS3Sync {
|
||||
fn from_env() -> Option<Self> {
|
||||
let region = std::env::var("BLOSSOM_S3_REGION").unwrap_or_default();
|
||||
let bucket = std::env::var("BLOSSOM_S3_BUCKET").unwrap_or_default();
|
||||
let access_key = std::env::var("BLOSSOM_S3_ACCESS_KEY").unwrap_or_default();
|
||||
let secret_key = std::env::var("BLOSSOM_S3_SECRET_KEY").unwrap_or_default();
|
||||
|
||||
let region = region.trim().to_string();
|
||||
let bucket = bucket.trim().to_string();
|
||||
let access_key = access_key.trim().to_string();
|
||||
let secret_key = secret_key.trim().to_string();
|
||||
|
||||
if region.is_empty() || bucket.is_empty() || access_key.is_empty() || secret_key.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let endpoint = std::env::var("BLOSSOM_S3_ENDPOINT")
|
||||
.unwrap_or_default()
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
Some(Self {
|
||||
endpoint,
|
||||
region,
|
||||
bucket,
|
||||
access_key,
|
||||
secret_key,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Infra {
|
||||
api_url: String,
|
||||
@@ -18,6 +59,7 @@ pub struct Infra {
|
||||
livekit_api_key: String,
|
||||
livekit_api_secret: String,
|
||||
api_secret: String,
|
||||
blossom_s3: Option<BlossomS3Sync>,
|
||||
query: Query,
|
||||
command: Command,
|
||||
}
|
||||
@@ -30,6 +72,7 @@ impl Infra {
|
||||
let livekit_api_key = std::env::var("LIVEKIT_API_KEY").unwrap_or_default();
|
||||
let livekit_api_secret = std::env::var("LIVEKIT_API_SECRET").unwrap_or_default();
|
||||
let api_secret = std::env::var("ZOOID_API_SECRET").unwrap_or_default();
|
||||
let blossom_s3 = BlossomS3Sync::from_env();
|
||||
|
||||
if api_url.trim().is_empty() {
|
||||
anyhow::bail!("missing ZOOID_API_URL");
|
||||
@@ -45,6 +88,7 @@ impl Infra {
|
||||
livekit_api_key,
|
||||
livekit_api_secret,
|
||||
api_secret,
|
||||
blossom_s3,
|
||||
query,
|
||||
command,
|
||||
})
|
||||
@@ -254,6 +298,7 @@ impl Infra {
|
||||
host,
|
||||
livekit,
|
||||
is_new.then(|| Keys::generate().secret_key().to_secret_hex()),
|
||||
self.blossom_s3.as_ref(),
|
||||
);
|
||||
|
||||
let url = format!("{}/relay/{}", base, relay.id);
|
||||
@@ -323,7 +368,10 @@ fn relay_sync_body(
|
||||
host: String,
|
||||
livekit: serde_json::Value,
|
||||
secret: Option<String>,
|
||||
blossom_s3: Option<&BlossomS3Sync>,
|
||||
) -> serde_json::Value {
|
||||
let blossom = blossom_sync_json(relay, blossom_s3);
|
||||
|
||||
let mut body = serde_json::json!({
|
||||
"host": host,
|
||||
"schema": relay.schema,
|
||||
@@ -341,7 +389,7 @@ fn relay_sync_body(
|
||||
},
|
||||
"groups": { "enabled": relay.groups_enabled == 1 },
|
||||
"management": { "enabled": relay.management_enabled == 1 },
|
||||
"blossom": { "enabled": relay.blossom_enabled == 1 },
|
||||
"blossom": blossom,
|
||||
"livekit": livekit,
|
||||
"push": { "enabled": relay.push_enabled == 1 },
|
||||
"roles": {
|
||||
@@ -357,6 +405,51 @@ fn relay_sync_body(
|
||||
body
|
||||
}
|
||||
|
||||
fn blossom_sync_json(relay: &Relay, blossom_s3: Option<&BlossomS3Sync>) -> serde_json::Value {
|
||||
let enabled = relay.blossom_enabled == 1;
|
||||
if !enabled {
|
||||
return serde_json::json!({ "enabled": false });
|
||||
}
|
||||
|
||||
let Some(s3) = blossom_s3 else {
|
||||
return serde_json::json!({ "enabled": true });
|
||||
};
|
||||
|
||||
let mut s3_obj = serde_json::Map::new();
|
||||
if !s3.endpoint.trim().is_empty() {
|
||||
s3_obj.insert(
|
||||
"endpoint".to_string(),
|
||||
serde_json::Value::String(s3.endpoint.clone()),
|
||||
);
|
||||
}
|
||||
s3_obj.insert(
|
||||
"region".to_string(),
|
||||
serde_json::Value::String(s3.region.clone()),
|
||||
);
|
||||
s3_obj.insert(
|
||||
"bucket".to_string(),
|
||||
serde_json::Value::String(s3.bucket.clone()),
|
||||
);
|
||||
s3_obj.insert(
|
||||
"access_key".to_string(),
|
||||
serde_json::Value::String(s3.access_key.clone()),
|
||||
);
|
||||
s3_obj.insert(
|
||||
"secret_key".to_string(),
|
||||
serde_json::Value::String(s3.secret_key.clone()),
|
||||
);
|
||||
s3_obj.insert(
|
||||
"key_prefix".to_string(),
|
||||
serde_json::Value::String(relay.schema.clone()),
|
||||
);
|
||||
|
||||
serde_json::json!({
|
||||
"enabled": true,
|
||||
"adapter": "s3",
|
||||
"s3": serde_json::Value::Object(s3_obj),
|
||||
})
|
||||
}
|
||||
|
||||
fn should_sync_relay_activity(activity_type: &str) -> bool {
|
||||
matches!(
|
||||
activity_type,
|
||||
|
||||
Reference in New Issue
Block a user