Improve environment variable handling and reading (#5389)

* wip: better env var reading

* move most env vars to env.rs

* migrate more env vars

* more migration

* more migrations

* More migration

* 🦀 dotenvy is gone (almost)

* 🦀 dotenvy is gone 🦀

* Fix mural source account env var handling

* Remove defaults from admin key vars

* dummy commit to update github pr

* fix ci
This commit is contained in:
aecsocket
2026-02-19 17:33:41 +00:00
committed by GitHub
parent b6b4bc21f1
commit ec81bcb13c
49 changed files with 636 additions and 661 deletions

View File

@@ -1,13 +1,13 @@
use crate::auth::get_user_from_headers;
use crate::database::PgPool;
use crate::database::redis::RedisPool;
use crate::env::ENV;
use crate::models::analytics::{PageView, Playtime};
use crate::models::pats::Scopes;
use crate::queue::analytics::AnalyticsQueue;
use crate::queue::session::AuthQueue;
use crate::routes::ApiError;
use crate::util::date::get_current_tenths_of_ms;
use crate::util::env::parse_strings_from_var;
use actix_web::{HttpRequest, HttpResponse};
use actix_web::{post, web};
use serde::Deserialize;
@@ -73,11 +73,10 @@ pub async fn page_view_ingest(
})?;
let url_origin = url.origin().ascii_serialization();
let is_valid_url_origin =
parse_strings_from_var("ANALYTICS_ALLOWED_ORIGINS")
.unwrap_or_default()
.iter()
.any(|origin| origin == "*" || url_origin == *origin);
let is_valid_url_origin = ENV
.ANALYTICS_ALLOWED_ORIGINS
.iter()
.any(|origin| origin == "*" || url_origin == *origin);
if !is_valid_url_origin {
return Err(ApiError::InvalidInput(

View File

@@ -1,6 +1,7 @@
use std::{collections::HashMap, net::Ipv4Addr, sync::Arc};
use crate::database::PgPool;
use crate::env::ENV;
use crate::{
auth::get_user_from_headers,
database::{
@@ -13,10 +14,7 @@ use crate::{
},
queue::{analytics::AnalyticsQueue, session::AuthQueue},
routes::analytics::FILTERED_HEADERS,
util::{
date::get_current_tenths_of_ms, env::parse_strings_from_var,
error::Context,
},
util::{date::get_current_tenths_of_ms, error::Context},
};
use actix_web::{HttpRequest, delete, get, patch, post, put, web};
use ariadne::ids::UserId;
@@ -70,11 +68,10 @@ async fn ingest_click(
})?;
let url_origin = url.origin().ascii_serialization();
let is_valid_url_origin =
parse_strings_from_var("ANALYTICS_ALLOWED_ORIGINS")
.unwrap_or_default()
.iter()
.any(|origin| origin == "*" || url_origin == *origin);
let is_valid_url_origin = ENV
.ANALYTICS_ALLOWED_ORIGINS
.iter()
.any(|origin| origin == "*" || url_origin == *origin);
if !is_valid_url_origin {
return Err(ApiError::InvalidInput(

View File

@@ -12,6 +12,7 @@ use crate::database::models::{
};
use crate::database::redis::RedisPool;
use crate::database::{PgPool, PgTransaction};
use crate::env::ENV;
use crate::models::billing::{
Charge, ChargeStatus, ChargeType, PaymentPlatform, Price, PriceDuration,
Product, ProductMetadata, ProductPrice, SubscriptionMetadata,
@@ -1437,7 +1438,7 @@ pub async fn active_servers(
pool: web::Data<PgPool>,
query: web::Query<ActiveServersQuery>,
) -> Result<HttpResponse, ApiError> {
let master_key = dotenvy::var("PYRO_API_KEY")?;
let master_key = &ENV.PYRO_API_KEY;
if req
.head()
@@ -1626,7 +1627,7 @@ pub async fn stripe_webhook(
if let Ok(event) = Webhook::construct_event(
&payload,
stripe_signature,
&dotenvy::var("STRIPE_WEBHOOK_SECRET")?,
&ENV.STRIPE_WEBHOOK_SECRET,
) {
struct PaymentIntentMetadata {
pub user_item: crate::database::models::user_item::DBUser,
@@ -2036,23 +2037,23 @@ pub async fn stripe_webhook(
client
.post(format!(
"{}/modrinth/v0/servers/{}/unsuspend",
dotenvy::var("ARCHON_URL")?,
ENV.ARCHON_URL,
id
))
.header("X-Master-Key", dotenvy::var("PYRO_API_KEY")?)
.header("X-Master-Key", &ENV.PYRO_API_KEY)
.send()
.await?
.error_for_status()?;
client
.post(format!(
"{}/modrinth/v0/servers/{}/reallocate",
dotenvy::var("ARCHON_URL")?,
id
))
"{}/modrinth/v0/servers/{}/reallocate",
ENV.ARCHON_URL,
id
))
.header(
"X-Master-Key",
dotenvy::var("PYRO_API_KEY")?,
&ENV.PYRO_API_KEY,
)
.json(&body)
.send()
@@ -2114,9 +2115,9 @@ pub async fn stripe_webhook(
let res = client
.post(format!(
"{}/modrinth/v0/servers/create",
dotenvy::var("ARCHON_URL")?,
ENV.ARCHON_URL,
))
.header("X-Master-Key", dotenvy::var("PYRO_API_KEY")?)
.header("X-Master-Key", &ENV.PYRO_API_KEY)
.json(&serde_json::json!({
"user_id": to_base62(metadata.user_item.id.0 as u64),
"name": server_name,

View File

@@ -1,6 +1,7 @@
use std::{collections::HashMap, fmt::Write, sync::LazyLock, time::Instant};
use crate::database::PgPool;
use crate::env::ENV;
use actix_web::{HttpRequest, HttpResponse, get, post, web};
use chrono::{DateTime, Utc};
use eyre::eyre;
@@ -89,7 +90,7 @@ impl DelphiReport {
pool: &PgPool,
redis: &RedisPool,
) -> Result<(), ApiError> {
let webhook_url = dotenvy::var("DELPHI_SLACK_WEBHOOK")?;
let webhook_url = ENV.DELPHI_SLACK_WEBHOOK.clone();
let mut message_header =
format!("⚠️ Suspicious traces found at {}", self.url);
@@ -115,7 +116,7 @@ impl DelphiReport {
self.project_id,
pool,
redis,
webhook_url,
&webhook_url,
Some(message_header),
)
.await
@@ -317,7 +318,7 @@ pub async fn run(
);
DELPHI_CLIENT
.post(dotenvy::var("DELPHI_URL")?)
.post(&ENV.DELPHI_URL)
.json(&serde_json::json!({
"url": file_data.url,
"project_id": ProjectId(file_data.project_id.0 as u64),
@@ -407,7 +408,7 @@ async fn issue_type_schema(
&cache_entry
.insert((
DELPHI_CLIENT
.get(format!("{}/schema", dotenvy::var("DELPHI_URL")?))
.get(format!("{}/schema", ENV.DELPHI_URL))
.send()
.await
.and_then(|res| res.error_for_status())

View File

@@ -8,6 +8,7 @@ use crate::database::models::flow_item::DBFlow;
use crate::database::models::notification_item::NotificationBuilder;
use crate::database::models::{DBUser, DBUserId};
use crate::database::redis::RedisPool;
use crate::env::ENV;
use crate::file_hosting::{FileHost, FileHostPublicity};
use crate::models::notifications::NotificationBody;
use crate::models::pats::Scopes;
@@ -17,7 +18,6 @@ use crate::queue::session::AuthQueue;
use crate::routes::ApiError;
use crate::routes::internal::session::issue_session;
use crate::util::captcha::check_hcaptcha;
use crate::util::env::parse_strings_from_var;
use crate::util::error::Context;
use crate::util::ext::get_image_ext;
use crate::util::img::upload_image_optimized;
@@ -257,41 +257,41 @@ impl AuthProvider {
&self,
state: String,
) -> Result<String, AuthenticationError> {
let self_addr = dotenvy::var("SELF_ADDR")?;
let self_addr = &ENV.SELF_ADDR;
let raw_redirect_uri = format!("{self_addr}/v2/auth/callback");
let redirect_uri = urlencoding::encode(&raw_redirect_uri);
Ok(match self {
AuthProvider::GitHub => {
let client_id = dotenvy::var("GITHUB_CLIENT_ID")?;
let client_id = &ENV.GITHUB_CLIENT_ID;
format!(
"https://github.com/login/oauth/authorize?client_id={client_id}&prompt=select_account&state={state}&scope=read%3Auser%20user%3Aemail&redirect_uri={redirect_uri}",
)
}
AuthProvider::Discord => {
let client_id = dotenvy::var("DISCORD_CLIENT_ID")?;
let client_id = &ENV.DISCORD_CLIENT_ID;
format!(
"https://discord.com/api/oauth2/authorize?client_id={client_id}&state={state}&response_type=code&scope=identify%20email&redirect_uri={redirect_uri}"
)
}
AuthProvider::Microsoft => {
let client_id = dotenvy::var("MICROSOFT_CLIENT_ID")?;
let client_id = &ENV.MICROSOFT_CLIENT_ID;
format!(
"https://login.live.com/oauth20_authorize.srf?client_id={client_id}&response_type=code&scope=user.read&state={state}&prompt=select_account&redirect_uri={redirect_uri}"
)
}
AuthProvider::GitLab => {
let client_id = dotenvy::var("GITLAB_CLIENT_ID")?;
let client_id = &ENV.GITLAB_CLIENT_ID;
format!(
"https://gitlab.com/oauth/authorize?client_id={client_id}&state={state}&scope=read_user+profile+email&response_type=code&redirect_uri={redirect_uri}",
)
}
AuthProvider::Google => {
let client_id = dotenvy::var("GOOGLE_CLIENT_ID")?;
let client_id = &ENV.GOOGLE_CLIENT_ID;
format!(
"https://accounts.google.com/o/oauth2/v2/auth?client_id={}&state={}&scope={}&response_type=code&redirect_uri={}",
@@ -317,8 +317,8 @@ impl AuthProvider {
)
}
AuthProvider::PayPal => {
let api_url = dotenvy::var("PAYPAL_API_URL")?;
let client_id = dotenvy::var("PAYPAL_CLIENT_ID")?;
let api_url = &ENV.PAYPAL_API_URL;
let client_id = &ENV.PAYPAL_CLIENT_ID;
let auth_url = if api_url.contains("sandbox") {
"sandbox.paypal.com"
@@ -340,8 +340,7 @@ impl AuthProvider {
&self,
query: HashMap<String, String>,
) -> Result<String, AuthenticationError> {
let redirect_uri =
format!("{}/v2/auth/callback", dotenvy::var("SELF_ADDR")?);
let redirect_uri = format!("{}/v2/auth/callback", &ENV.SELF_ADDR);
#[derive(Deserialize)]
struct AccessToken {
@@ -353,8 +352,8 @@ impl AuthProvider {
let code = query
.get("code")
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
let client_id = dotenvy::var("GITHUB_CLIENT_ID")?;
let client_secret = dotenvy::var("GITHUB_CLIENT_SECRET")?;
let client_id = ENV.GITHUB_CLIENT_ID.as_str();
let client_secret = ENV.GITHUB_CLIENT_SECRET.as_str();
let url = format!(
"https://github.com/login/oauth/access_token?client_id={client_id}&client_secret={client_secret}&code={code}&redirect_uri={redirect_uri}"
@@ -374,12 +373,12 @@ impl AuthProvider {
let code = query
.get("code")
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
let client_id = dotenvy::var("DISCORD_CLIENT_ID")?;
let client_secret = dotenvy::var("DISCORD_CLIENT_SECRET")?;
let client_id = ENV.DISCORD_CLIENT_ID.as_str();
let client_secret = ENV.DISCORD_CLIENT_SECRET.as_str();
let mut map = HashMap::new();
map.insert("client_id", &*client_id);
map.insert("client_secret", &*client_secret);
map.insert("client_id", client_id);
map.insert("client_secret", client_secret);
map.insert("code", code);
map.insert("grant_type", "authorization_code");
map.insert("redirect_uri", &redirect_uri);
@@ -399,12 +398,12 @@ impl AuthProvider {
let code = query
.get("code")
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
let client_id = dotenvy::var("MICROSOFT_CLIENT_ID")?;
let client_secret = dotenvy::var("MICROSOFT_CLIENT_SECRET")?;
let client_id = ENV.MICROSOFT_CLIENT_ID.as_str();
let client_secret = ENV.MICROSOFT_CLIENT_SECRET.as_str();
let mut map = HashMap::new();
map.insert("client_id", &*client_id);
map.insert("client_secret", &*client_secret);
map.insert("client_id", client_id);
map.insert("client_secret", client_secret);
map.insert("code", code);
map.insert("grant_type", "authorization_code");
map.insert("redirect_uri", &redirect_uri);
@@ -424,12 +423,12 @@ impl AuthProvider {
let code = query
.get("code")
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
let client_id = dotenvy::var("GITLAB_CLIENT_ID")?;
let client_secret = dotenvy::var("GITLAB_CLIENT_SECRET")?;
let client_id = ENV.GITLAB_CLIENT_ID.as_str();
let client_secret = ENV.GITLAB_CLIENT_SECRET.as_str();
let mut map = HashMap::new();
map.insert("client_id", &*client_id);
map.insert("client_secret", &*client_secret);
map.insert("client_id", client_id);
map.insert("client_secret", client_secret);
map.insert("code", code);
map.insert("grant_type", "authorization_code");
map.insert("redirect_uri", &redirect_uri);
@@ -449,12 +448,12 @@ impl AuthProvider {
let code = query
.get("code")
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
let client_id = dotenvy::var("GOOGLE_CLIENT_ID")?;
let client_secret = dotenvy::var("GOOGLE_CLIENT_SECRET")?;
let client_id = ENV.GOOGLE_CLIENT_ID.as_str();
let client_secret = ENV.GOOGLE_CLIENT_SECRET.as_str();
let mut map = HashMap::new();
map.insert("client_id", &*client_id);
map.insert("client_secret", &*client_secret);
map.insert("client_id", client_id);
map.insert("client_secret", client_secret);
map.insert("code", code);
map.insert("grant_type", "authorization_code");
map.insert("redirect_uri", &redirect_uri);
@@ -529,9 +528,9 @@ impl AuthProvider {
let code = query
.get("code")
.ok_or_else(|| AuthenticationError::InvalidCredentials)?;
let api_url = dotenvy::var("PAYPAL_API_URL")?;
let client_id = dotenvy::var("PAYPAL_CLIENT_ID")?;
let client_secret = dotenvy::var("PAYPAL_CLIENT_SECRET")?;
let api_url = ENV.PAYPAL_API_URL.as_str();
let client_id = ENV.PAYPAL_CLIENT_ID.as_str();
let client_secret = ENV.PAYPAL_CLIENT_SECRET.as_str();
let mut map = HashMap::new();
map.insert("code", code.as_str());
@@ -580,9 +579,7 @@ impl AuthProvider {
.get("x-oauth-client-id")
.and_then(|x| x.to_str().ok());
if client_id
!= Some(&*dotenvy::var("GITHUB_CLIENT_ID").unwrap())
{
if client_id != Some(ENV.GITHUB_CLIENT_ID.as_str()) {
return Err(AuthenticationError::InvalidClientId);
}
}
@@ -732,7 +729,7 @@ impl AuthProvider {
}
}
AuthProvider::Steam => {
let api_key = dotenvy::var("STEAM_API_KEY")?;
let api_key = &ENV.STEAM_API_KEY;
#[derive(Deserialize)]
struct SteamResponse {
@@ -797,7 +794,7 @@ impl AuthProvider {
pub country: String,
}
let api_url = dotenvy::var("PAYPAL_API_URL")?;
let api_url = &ENV.PAYPAL_API_URL;
let paypal_user: PayPalUser = reqwest::Client::new()
.get(format!(
@@ -1100,10 +1097,11 @@ pub async fn init(
let url =
url::Url::parse(&info.url).map_err(|_| AuthenticationError::Url)?;
let allowed_callback_urls =
parse_strings_from_var("ALLOWED_CALLBACK_URLS").unwrap_or_default();
let domain = url.host_str().ok_or(AuthenticationError::Url)?;
if !allowed_callback_urls.iter().any(|x| domain.ends_with(x))
if !ENV
.ALLOWED_CALLBACK_URLS
.iter()
.any(|x| domain.ends_with(x))
&& domain != "modrinth.com"
{
return Err(AuthenticationError::Url);
@@ -1396,9 +1394,9 @@ pub async fn delete_auth_provider(
pub async fn check_sendy_subscription(
email: &str,
) -> Result<bool, AuthenticationError> {
let url = dotenvy::var("SENDY_URL")?;
let id = dotenvy::var("SENDY_LIST_ID")?;
let api_key = dotenvy::var("SENDY_API_KEY")?;
let url = &ENV.SENDY_URL;
let id = &ENV.SENDY_LIST_ID;
let api_key = &ENV.SENDY_API_KEY;
if url.is_empty() || url == "none" {
tracing::info!(
@@ -1408,9 +1406,9 @@ pub async fn check_sendy_subscription(
}
let mut form = HashMap::new();
form.insert("api_key", &*api_key);
form.insert("api_key", api_key.as_str());
form.insert("email", email);
form.insert("list_id", &*id);
form.insert("list_id", id.as_str());
let client = reqwest::Client::new();
let response = client

View File

@@ -1,5 +1,6 @@
use std::{collections::HashMap, sync::LazyLock};
use crate::env::ENV;
use actix_web::{get, web};
use serde::{Deserialize, Serialize};
@@ -28,7 +29,8 @@ static GLOBALS: LazyLock<Globals> = LazyLock::new(|| Globals {
tax_compliance_thresholds: [(2025, 600), (2026, 2000)]
.into_iter()
.collect(),
captcha_enabled: dotenvy::var("HCAPTCHA_SECRET").is_ok_and(|x| x != "none"),
captcha_enabled: !ENV.HCAPTCHA_SECRET.is_empty()
&& ENV.HCAPTCHA_SECRET != "none",
});
/// Gets configured global non-secret variables for this backend instance.

View File

@@ -4,11 +4,11 @@ use crate::database::models::session_item::DBSession;
use crate::database::models::session_item::SessionBuilder;
use crate::database::redis::RedisPool;
use crate::database::{PgPool, PgTransaction};
use crate::env::ENV;
use crate::models::pats::Scopes;
use crate::models::sessions::Session;
use crate::queue::session::AuthQueue;
use crate::routes::ApiError;
use crate::util::env::parse_var;
use actix_web::http::header::AUTHORIZATION;
use actix_web::web::{Data, ServiceConfig, scope};
use actix_web::{HttpRequest, HttpResponse, delete, get, post, web};
@@ -41,7 +41,7 @@ pub async fn get_session_metadata(
req: &HttpRequest,
) -> Result<SessionMetadata, AuthenticationError> {
let conn_info = req.connection_info().clone();
let ip_addr = if parse_var("CLOUDFLARE_INTEGRATION").unwrap_or(false) {
let ip_addr = if ENV.CLOUDFLARE_INTEGRATION {
if let Some(header) = req.headers().get("CF-Connecting-IP") {
header.to_str().ok()
} else {

View File

@@ -1,8 +1,8 @@
use crate::database::models::DelphiReportIssueDetailsId;
use crate::env::ENV;
use crate::file_hosting::FileHostingError;
use crate::routes::analytics::{page_view_ingest, playtime_ingest};
use crate::util::cors::default_cors;
use crate::util::env::parse_strings_from_var;
use actix_cors::Cors;
use actix_files::Files;
use actix_web::http::StatusCode;
@@ -40,10 +40,7 @@ pub fn root_config(cfg: &mut web::ServiceConfig) {
.wrap(
Cors::default()
.allowed_origin_fn(|origin, _req_head| {
let allowed_origins =
parse_strings_from_var("ANALYTICS_ALLOWED_ORIGINS")
.unwrap_or_default();
let allowed_origins = &ENV.ANALYTICS_ALLOWED_ORIGINS;
allowed_origins.contains(&"*".to_string())
|| allowed_origins.contains(
&origin

View File

@@ -1,6 +1,7 @@
use std::collections::HashMap;
use crate::database::PgPool;
use crate::env::ENV;
use actix_web::{HttpRequest, HttpResponse, get, web};
use serde::{Deserialize, Serialize};
@@ -94,11 +95,7 @@ pub async fn forge_updates(
}
let mut response = ForgeUpdates {
homepage: format!(
"{}/mod/{}",
dotenvy::var("SITE_URL").unwrap_or_default(),
id
),
homepage: format!("{}/mod/{}", ENV.SITE_URL, id),
promos: HashMap::new(),
};

View File

@@ -4,6 +4,7 @@ use crate::database::PgPool;
use crate::database::models::DBUserId;
use crate::database::models::{generate_payout_id, users_compliance};
use crate::database::redis::RedisPool;
use crate::env::ENV;
use crate::models::ids::PayoutId;
use crate::models::pats::Scopes;
use crate::models::payouts::{PayoutMethodType, PayoutStatus, Withdrawal};
@@ -212,7 +213,7 @@ pub async fn paypal_webhook(
\"webhook_id\": \"{}\",
\"webhook_event\": {body}
}}",
dotenvy::var("PAYPAL_WEBHOOK_ID")?
ENV.PAYPAL_WEBHOOK_ID,
)),
None,
)
@@ -322,7 +323,7 @@ pub async fn tremendous_webhook(
})?;
let mut mac: Hmac<Sha256> = Hmac::new_from_slice(
dotenvy::var("TREMENDOUS_PRIVATE_KEY")?.as_bytes(),
ENV.TREMENDOUS_PRIVATE_KEY.as_bytes(),
)
.map_err(|_| ApiError::Payments("error initializing HMAC".to_string()))?;
mac.update(body.as_bytes());
@@ -1114,9 +1115,7 @@ async fn update_compliance_status(
}
fn tax_compliance_payout_threshold() -> Option<Decimal> {
dotenvy::var("COMPLIANCE_PAYOUT_THRESHOLD")
.ok()
.and_then(|s| s.parse().ok())
ENV.COMPLIANCE_PAYOUT_THRESHOLD.parse().ok()
}
#[derive(Deserialize)]

View File

@@ -12,6 +12,7 @@ use crate::database::models::{
use crate::database::redis::RedisPool;
use crate::database::{self, models as db_models};
use crate::database::{PgPool, PgTransaction};
use crate::env::ENV;
use crate::file_hosting::{FileHost, FileHostPublicity};
use crate::models;
use crate::models::ids::{ProjectId, VersionId};
@@ -427,13 +428,13 @@ pub async fn project_edit(
if status.is_searchable()
&& !project_item.inner.webhook_sent
&& let Ok(webhook_url) = dotenvy::var("PUBLIC_DISCORD_WEBHOOK")
&& !ENV.PUBLIC_DISCORD_WEBHOOK.is_empty()
{
crate::util::webhook::send_discord_webhook(
project_item.inner.id.into(),
&pool,
&redis,
webhook_url,
&ENV.PUBLIC_DISCORD_WEBHOOK,
None,
)
.await
@@ -451,18 +452,16 @@ pub async fn project_edit(
.await?;
}
if user.role.is_mod()
&& let Ok(webhook_url) = dotenvy::var("MODERATION_SLACK_WEBHOOK")
{
if user.role.is_mod() && !ENV.MODERATION_SLACK_WEBHOOK.is_empty() {
crate::util::webhook::send_slack_project_webhook(
project_item.inner.id.into(),
&pool,
&redis,
webhook_url,
&ENV.MODERATION_SLACK_WEBHOOK,
Some(
format!(
"*<{}/user/{}|{}>* changed project status from *{}* to *{}*",
dotenvy::var("SITE_URL")?,
ENV.SITE_URL,
user.username,
user.username,
&project_item.inner.status.as_friendly_str(),

View File

@@ -7,6 +7,7 @@ use crate::database::models::image_item;
use crate::database::models::notification_item::NotificationBuilder;
use crate::database::models::thread_item::ThreadMessageBuilder;
use crate::database::redis::RedisPool;
use crate::env::ENV;
use crate::file_hosting::{FileHost, FileHostPublicity};
use crate::models::ids::{ThreadId, ThreadMessageId};
use crate::models::images::{Image, ImageContext};
@@ -631,9 +632,8 @@ pub async fn message_delete(
let images =
database::DBImage::get_many_contexted(context, &mut transaction)
.await?;
let cdn_url = dotenvy::var("CDN_URL")?;
for image in images {
let name = image.url.split(&format!("{cdn_url}/")).nth(1);
let name = image.url.split(&format!("{}/", ENV.CDN_URL)).nth(1);
if let Some(icon_path) = name {
file_host
.delete_file(

View File

@@ -11,6 +11,7 @@ use crate::database::models::version_item::{
};
use crate::database::models::{self, DBOrganization, image_item};
use crate::database::redis::RedisPool;
use crate::env::ENV;
use crate::file_hosting::{FileHost, FileHostPublicity};
use crate::models::ids::{ImageId, ProjectId, VersionId};
use crate::models::images::{Image, ImageContext};
@@ -974,7 +975,7 @@ pub async fn upload_file(
version_files.push(VersionFileBuilder {
filename: file_name.to_string(),
url: format!("{}/{file_path_encode}", dotenvy::var("CDN_URL")?),
url: format!("{}/{file_path_encode}", ENV.CDN_URL),
hashes: vec![
models::version_item::HashBuilder {
algorithm: "sha1".to_string(),