Compare commits

..

No commits in common. "c786084dc48114cd42592396be68ce2be5e31251" and "ffef41c289a5dd2e74f84bd60999f42adf66d32a" have entirely different histories.

7 changed files with 23 additions and 232 deletions

View file

@ -1 +0,0 @@
target

33
Cargo.lock generated
View file

@ -513,12 +513,6 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "http-range-header"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
[[package]] [[package]]
name = "httparse" name = "httparse"
version = "1.10.1" version = "1.10.1"
@ -888,16 +882,6 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
dependencies = [
"mime",
"unicase",
]
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.8.9" version = "0.8.9"
@ -977,7 +961,6 @@ dependencies = [
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"tokio", "tokio",
"tower-http",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"zip", "zip",
@ -1620,24 +1603,14 @@ checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"bytes", "bytes",
"futures-core",
"futures-util", "futures-util",
"http", "http",
"http-body", "http-body",
"http-body-util",
"http-range-header",
"httpdate",
"iri-string", "iri-string",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite", "pin-project-lite",
"tokio",
"tokio-util",
"tower", "tower",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
"tracing",
] ]
[[package]] [[package]]
@ -1722,12 +1695,6 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]]
name = "unicase"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.18" version = "1.0.18"

View file

@ -11,7 +11,6 @@ serde = "1.0.219"
serde_derive = "1.0.219" serde_derive = "1.0.219"
serde_json = "1.0.142" serde_json = "1.0.142"
tokio = { version = "1.47.1", features = ["full"] } tokio = { version = "1.47.1", features = ["full"] }
tower-http = { version = "0.6.6", features = ["fs"] }
tracing = "0.1.41" tracing = "0.1.41"
tracing-subscriber = "0.3.19" tracing-subscriber = "0.3.19"
zip = "4.3.0" zip = "4.3.0"

View file

@ -1,13 +0,0 @@
FROM rust:latest AS builder
COPY ./ /src
RUN cd /src && cargo build --release
FROM debian:trixie
RUN apt-get update && apt-get install -y ca-certificates
COPY --from=builder /src/target/release/openeuicc-site /openeuicc-site
CMD [ "/openeuicc-site" ]

View file

@ -1,35 +0,0 @@
app = 'openeuicc'
primary_region = 'yyz'
[build]
[mounts]
source = "openeuicc_site_cache"
destination = "/cache"
[env]
CACHE_DIR = "/cache"
ROOT_DOMAIN = "openeuicc.com"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[http_service.concurrency]
type = "requests"
soft_limit = 7000
hard_limit = 8000
[[http_service.checks]]
grace_period = "10s"
interval = "30s"
method = "GET"
timeout = "5s"
path = "/"
[[vm]]
size = 'shared-cpu-1x'

View file

@ -1,8 +1,9 @@
use std::sync::LazyLock; use std::sync::LazyLock;
use axum::{Router, routing::get}; use axum::{Router, routing::get};
use tower_http::services::ServeDir; use tracing::info;
use tracing::{info, warn};
use crate::update::update_latest_build_loop;
mod update; mod update;
@ -13,21 +14,9 @@ pub static ROOT_DOMAIN: LazyLock<String> = LazyLock::new(|| std::env::var("ROOT_
#[tokio::main] #[tokio::main]
async fn main() -> eyre::Result<()> { async fn main() -> eyre::Result<()> {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
tokio::fs::create_dir_all(&*CACHE_DIR).await?; tokio::spawn(update_latest_build_loop());
if let Err(e) = crate::update::maybe_init().await {
warn!("Error while trying to init from existing cache: {e:?}, skipping");
}
tokio::spawn(crate::update::update_latest_build_loop());
let static_cache_files = ServeDir::new(&*CACHE_DIR); let app = Router::new().route("/", get(root));
let app = Router::new()
.route("/", get(root))
.route(
"/magisk/magisk-debug.json",
get(crate::update::serve_latest_metadata),
)
.nest_service("/magisk", static_cache_files);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?; let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
info!("Listening on port 3000"); info!("Listening on port 3000");
@ -36,5 +25,5 @@ async fn main() -> eyre::Result<()> {
} }
async fn root() -> &'static str { async fn root() -> &'static str {
"This is the OpenEUICC homepage." "Hello, world"
} }

View file

@ -1,10 +1,5 @@
use std::{ use std::{io::Read, sync::RwLock, time::Duration};
io::Read,
sync::{LazyLock, RwLock},
time::Duration,
};
use axum::Json;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use tracing::info; use tracing::info;
@ -12,33 +7,6 @@ use tracing::info;
use crate::{CACHE_DIR, ROOT_DOMAIN}; use crate::{CACHE_DIR, ROOT_DOMAIN};
pub static LATEST_METADATA: RwLock<Option<MagiskZipMetadata>> = RwLock::new(None); pub static LATEST_METADATA: RwLock<Option<MagiskZipMetadata>> = RwLock::new(None);
pub static INITIALIZE_CHANNEL: LazyLock<
RwLock<
Option<(
tokio::sync::broadcast::Sender<()>,
tokio::sync::broadcast::Receiver<()>,
)>,
>,
> = LazyLock::new(|| RwLock::new(Some(tokio::sync::broadcast::channel(1))));
pub async fn serve_latest_metadata() -> Json<MagiskZipMetadata> {
if let Some(metadata) = &*LATEST_METADATA.read().unwrap() {
return Json(metadata.clone());
}
// Try waiting for initialization
let rx = if let Some((tx, _)) = &*INITIALIZE_CHANNEL.read().unwrap() {
Some(tx.subscribe())
} else {
None
};
if let Some(mut rx) = rx {
rx.recv().await.ok();
}
Json(LATEST_METADATA.read().unwrap().clone().unwrap())
}
#[allow(unused)] #[allow(unused)]
#[derive(Clone, Deserialize)] #[derive(Clone, Deserialize)]
@ -63,43 +31,27 @@ struct WorkflowTasksResponse {
workflow_runs: Vec<WorkflowRun>, workflow_runs: Vec<WorkflowRun>,
} }
pub async fn maybe_init() -> eyre::Result<()> {
let Some((latest_zip, latest_run)) = cleanup_and_get_latest_zip().await? else {
info!(
"Could not find existing, cached latest magisk zip; will initialize after the first update loop run"
);
return Ok(());
};
let path = format!("{}/{}", *CACHE_DIR, latest_zip);
info!("Found existing Magisk zip {path}, initializing metadata with it");
parse_magisk_zip_and_update(path, latest_run).await
}
pub async fn update_latest_build_loop() { pub async fn update_latest_build_loop() {
let mut interval = tokio::time::interval(Duration::from_secs(5 * 60)); let mut interval = tokio::time::interval(Duration::from_mins(15));
loop { loop {
interval.tick().await; interval.tick().await;
let mut cur_attempts = 0; let mut cur_attempts = 0;
while let Err(e) = update_latest_build().await while let Err(e) = update_latest_build().await
&& cur_attempts < 20 && cur_attempts < 5
{ {
tracing::error!("Failed to fetch latest build: {e:?}, retrying in 30 seconds"); tracing::error!("Failed to fetch latest build: {e:?}, retrying in 60 seconds");
cur_attempts += 1; cur_attempts += 1;
tokio::time::sleep(Duration::from_secs(30)).await; tokio::time::sleep(Duration::from_secs(60)).await;
} }
cleanup_and_get_latest_zip().await.ok();
} }
} }
async fn update_latest_build() -> eyre::Result<()> { async fn update_latest_build() -> eyre::Result<()> {
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let req = client let req = client
.get("https://gitea.angry.im/api/v1/repos/PeterCxy/OpenEUICC/actions/tasks?limit=200") .get("https://gitea.angry.im/api/v1/repos/PeterCxy/OpenEUICC/actions/tasks")
.header("Accept-Encoding", "application/json") .header("Accept-Encoding", "application/json")
.build()?; .build()?;
let mut resp: WorkflowTasksResponse = client.execute(req).await?.json().await?; let mut resp: WorkflowTasksResponse = client.execute(req).await?.json().await?;
@ -109,22 +61,7 @@ async fn update_latest_build() -> eyre::Result<()> {
resp.workflow_runs.sort_by_key(|run| run.run_number); resp.workflow_runs.sort_by_key(|run| run.run_number);
resp.workflow_runs.reverse(); resp.workflow_runs.reverse();
if resp.workflow_runs.is_empty() {
tracing::error!("Couldn't find the latest successful workflow, aborting");
return Ok(());
}
let latest_run = resp.workflow_runs[0].clone(); let latest_run = resp.workflow_runs[0].clone();
if let Some(metadata) = &*LATEST_METADATA.read().unwrap()
&& metadata.run_number == latest_run.run_number
{
info!(
"Latest run has the same run number as existing {}, skipping",
latest_run.run_number
);
return Ok(());
}
let latest_module_url = format!( let latest_module_url = format!(
"https://gitea.angry.im/PeterCxy/OpenEUICC/actions/runs/{}/artifacts/magisk-debug", "https://gitea.angry.im/PeterCxy/OpenEUICC/actions/runs/{}/artifacts/magisk-debug",
latest_run.run_number latest_run.run_number
@ -134,10 +71,7 @@ async fn update_latest_build() -> eyre::Result<()> {
let download_req = client.get(latest_module_url).build()?; let download_req = client.get(latest_module_url).build()?;
let download_buf = client.execute(download_req).await?.bytes().await?; let download_buf = client.execute(download_req).await?.bytes().await?;
let target_path = format!( let target_path = format!("{}/magisk-{}.zip", *CACHE_DIR, latest_run.run_number);
"{}/magisk-openeuicc-debug-{}.zip",
*CACHE_DIR, latest_run.run_number
);
info!("Downloading Magisk zip to {target_path}"); info!("Downloading Magisk zip to {target_path}");
{ {
let mut target_file = tokio::fs::File::create(&target_path).await?; let mut target_file = tokio::fs::File::create(&target_path).await?;
@ -145,12 +79,17 @@ async fn update_latest_build() -> eyre::Result<()> {
} }
info!("Magisk zip downloaded"); info!("Magisk zip downloaded");
parse_magisk_zip_and_update(target_path, latest_run.run_number).await?; let metadata = parse_magisk_zip(target_path, latest_run.run_number).await?;
info!(
"Generated update manifest: {}",
serde_json::to_string_pretty(&metadata)?
);
*LATEST_METADATA.write().unwrap() = Some(metadata);
Ok(()) Ok(())
} }
#[derive(Clone, Serialize)] #[derive(Serialize)]
pub struct MagiskZipMetadata { pub struct MagiskZipMetadata {
version: String, version: String,
#[serde(rename = "versionCode")] #[serde(rename = "versionCode")]
@ -158,27 +97,10 @@ pub struct MagiskZipMetadata {
#[serde(rename = "zipUrl")] #[serde(rename = "zipUrl")]
zip_url: String, zip_url: String,
changelog: String, changelog: String,
#[serde(skip)]
run_number: u64,
} }
async fn parse_magisk_zip_and_update(path: String, run_number: u64) -> eyre::Result<()> { async fn parse_magisk_zip(path: String, run_number: u64) -> eyre::Result<MagiskZipMetadata> {
info!("Parsing {path}"); Ok(tokio::task::spawn_blocking(move || do_parse_magisk_zip(path, run_number)).await??)
let res = tokio::task::spawn_blocking(move || do_parse_magisk_zip(path, run_number)).await??;
info!(
"Generated update manifest: {}",
serde_json::to_string_pretty(&res)?
);
*LATEST_METADATA.write().unwrap() = Some(res);
// If the initialize channel is still there, tell everyone we have finished initialization and LATEST_METADATA is no longer None
if let Some((tx, _)) = &*INITIALIZE_CHANNEL.read().unwrap() {
tx.send(()).ok();
};
// Drop the sender here; any subscriber that might have raced with us will receive a closed error
INITIALIZE_CHANNEL.write().unwrap().take();
Ok(())
} }
fn do_parse_magisk_zip(path: String, run_number: u64) -> eyre::Result<MagiskZipMetadata> { fn do_parse_magisk_zip(path: String, run_number: u64) -> eyre::Result<MagiskZipMetadata> {
@ -192,12 +114,8 @@ fn do_parse_magisk_zip(path: String, run_number: u64) -> eyre::Result<MagiskZipM
let mut ret = MagiskZipMetadata { let mut ret = MagiskZipMetadata {
version: "".to_string(), version: "".to_string(),
version_code: 0, version_code: 0,
zip_url: format!( zip_url: format!("https://{}/magisk/magisk-{}.zip", *ROOT_DOMAIN, run_number),
"https://{}/magisk/magisk-openeuicc-debug-{}.zip",
*ROOT_DOMAIN, run_number
),
changelog: "https://gitea.angry.im/PeterCxy/OpenEUICC/commits/branch/master".to_string(), changelog: "https://gitea.angry.im/PeterCxy/OpenEUICC/commits/branch/master".to_string(),
run_number,
}; };
for line in module_prop.lines() { for line in module_prop.lines() {
@ -216,36 +134,3 @@ fn do_parse_magisk_zip(path: String, run_number: u64) -> eyre::Result<MagiskZipM
Ok(ret) Ok(ret)
} }
async fn cleanup_and_get_latest_zip() -> eyre::Result<Option<(String, u64)>> {
let mut dir = tokio::fs::read_dir(&*CACHE_DIR).await?;
let mut zips = Vec::new();
while let Some(entry) = dir.next_entry().await? {
if entry.file_type().await?.is_file() {
let name = entry.file_name().into_string().unwrap();
if name.ends_with(".zip") && name.starts_with("magisk-openeuicc-debug-") {
let run_number: u64 = name
.replacen("magisk-openeuicc-debug-", "", 1)
.replacen(".zip", "", 1)
.parse()?;
zips.push((name, run_number));
}
}
}
if zips.is_empty() {
return Ok(None);
}
zips.sort_by_key(|z| z.1);
zips.reverse();
for zip in zips.iter().skip(1) {
let path = format!("{}/{}", *CACHE_DIR, zip.0);
info!("Deleting {path}");
tokio::fs::remove_file(&path).await.ok();
}
Ok(Some(zips[0].clone()))
}