Embed UI templates and static assets into binary; simplify deployment to single file; Update script files
This commit is contained in:
84
Cargo.lock
generated
84
Cargo.lock
generated
@@ -1460,6 +1460,27 @@ dependencies = [
|
|||||||
"crypto-common 0.2.1",
|
"crypto-common 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"option-ext",
|
||||||
|
"redox_users",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
@@ -2733,6 +2754,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
|
"rust-embed",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
@@ -2872,6 +2894,12 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "option-ext"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "outref"
|
name = "outref"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -3325,6 +3353,17 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.17",
|
||||||
|
"libredox",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.12.3"
|
version = "1.12.3"
|
||||||
@@ -3471,6 +3510,42 @@ version = "0.20.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed"
|
||||||
|
version = "8.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27"
|
||||||
|
dependencies = [
|
||||||
|
"rust-embed-impl",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-impl"
|
||||||
|
version = "8.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"shellexpand",
|
||||||
|
"syn 2.0.117",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-utils"
|
||||||
|
version = "8.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1"
|
||||||
|
dependencies = [
|
||||||
|
"globset",
|
||||||
|
"sha2 0.10.9",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust_decimal"
|
name = "rust_decimal"
|
||||||
version = "1.41.0"
|
version = "1.41.0"
|
||||||
@@ -3801,6 +3876,15 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shellexpand"
|
||||||
|
version = "3.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8"
|
||||||
|
dependencies = [
|
||||||
|
"dirs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ aws-smithy-types = { workspace = true }
|
|||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
tera = { workspace = true }
|
tera = { workspace = true }
|
||||||
|
rust-embed = { version = "8", features = ["debug-embed", "include-exclude", "interpolate-folder-path"] }
|
||||||
cookie = { workspace = true }
|
cookie = { workspace = true }
|
||||||
subtle = { workspace = true }
|
subtle = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
|
|||||||
25
crates/myfsio-server/src/embedded.rs
Normal file
25
crates/myfsio-server/src/embedded.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use rust_embed::{EmbeddedFile, RustEmbed};
|
||||||
|
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "$CARGO_MANIFEST_DIR/templates"]
|
||||||
|
#[include = "*.html"]
|
||||||
|
pub struct EmbeddedTemplates;
|
||||||
|
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "$CARGO_MANIFEST_DIR/static"]
|
||||||
|
pub struct EmbeddedStatic;
|
||||||
|
|
||||||
|
pub fn template_names() -> Vec<String> {
|
||||||
|
EmbeddedTemplates::iter()
|
||||||
|
.map(|c: std::borrow::Cow<'static, str>| c.into_owned())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn template_contents(name: &str) -> Option<String> {
|
||||||
|
let file = EmbeddedTemplates::get(name)?;
|
||||||
|
String::from_utf8(file.data.into_owned()).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn static_file(path: &str) -> Option<EmbeddedFile> {
|
||||||
|
EmbeddedStatic::get(path)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ mod chunked;
|
|||||||
mod config;
|
mod config;
|
||||||
pub mod kms;
|
pub mod kms;
|
||||||
mod select;
|
mod select;
|
||||||
|
pub mod static_assets;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
pub mod ui_api;
|
pub mod ui_api;
|
||||||
pub mod ui_pages;
|
pub mod ui_pages;
|
||||||
|
|||||||
56
crates/myfsio-server/src/handlers/static_assets.rs
Normal file
56
crates/myfsio-server/src/handlers/static_assets.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
use axum::{
|
||||||
|
body::Body,
|
||||||
|
extract::{Path, State},
|
||||||
|
http::{header, HeaderValue, StatusCode},
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::embedded;
|
||||||
|
use crate::state::AppState;
|
||||||
|
|
||||||
|
pub async fn serve(State(state): State<AppState>, Path(path): Path<String>) -> Response {
|
||||||
|
let normalized = path.trim_start_matches('/').to_string();
|
||||||
|
if normalized.is_empty() || normalized.contains("..") {
|
||||||
|
return StatusCode::NOT_FOUND.into_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
let use_disk = std::env::var("STATIC_DIR").is_ok() && state.config.static_dir.is_dir();
|
||||||
|
if use_disk {
|
||||||
|
let candidate = state.config.static_dir.join(&normalized);
|
||||||
|
if let Ok(canonical) = candidate.canonicalize() {
|
||||||
|
if canonical.starts_with(
|
||||||
|
state
|
||||||
|
.config
|
||||||
|
.static_dir
|
||||||
|
.canonicalize()
|
||||||
|
.unwrap_or_else(|_| state.config.static_dir.clone()),
|
||||||
|
) {
|
||||||
|
if let Ok(bytes) = tokio::fs::read(&canonical).await {
|
||||||
|
let mime = mime_guess::from_path(&canonical).first_or_octet_stream();
|
||||||
|
return build_response(&normalized, bytes, mime.as_ref());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return StatusCode::NOT_FOUND.into_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
match embedded::static_file(&normalized) {
|
||||||
|
Some(file) => {
|
||||||
|
let mime = mime_guess::from_path(&normalized).first_or_octet_stream();
|
||||||
|
build_response(&normalized, file.data.into_owned(), mime.as_ref())
|
||||||
|
}
|
||||||
|
None => StatusCode::NOT_FOUND.into_response(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_response(_path: &str, bytes: Vec<u8>, mime: &str) -> Response {
|
||||||
|
let mut response = Response::new(Body::from(bytes));
|
||||||
|
if let Ok(value) = HeaderValue::from_str(mime) {
|
||||||
|
response.headers_mut().insert(header::CONTENT_TYPE, value);
|
||||||
|
}
|
||||||
|
response.headers_mut().insert(
|
||||||
|
header::CACHE_CONTROL,
|
||||||
|
HeaderValue::from_static("no-cache"),
|
||||||
|
);
|
||||||
|
response
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod embedded;
|
||||||
pub mod handlers;
|
pub mod handlers;
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
pub mod services;
|
pub mod services;
|
||||||
@@ -321,14 +322,12 @@ pub fn create_ui_router(state: state::AppState) -> Router {
|
|||||||
secure: false,
|
secure: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let static_service = tower::ServiceBuilder::new()
|
let static_router = Router::new()
|
||||||
.layer(tower_http::set_header::SetResponseHeaderLayer::overriding(
|
.route(
|
||||||
axum::http::header::CACHE_CONTROL,
|
"/static/{*path}",
|
||||||
axum::http::HeaderValue::from_static("no-cache"),
|
axum::routing::get(handlers::static_assets::serve),
|
||||||
))
|
)
|
||||||
.service(tower_http::services::ServeDir::new(
|
.with_state(state.clone());
|
||||||
&state.config.static_dir,
|
|
||||||
));
|
|
||||||
|
|
||||||
protected
|
protected
|
||||||
.merge(public)
|
.merge(public)
|
||||||
@@ -346,7 +345,7 @@ pub fn create_ui_router(state: state::AppState) -> Router {
|
|||||||
middleware::ui_metrics_layer,
|
middleware::ui_metrics_layer,
|
||||||
))
|
))
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
.nest_service("/static", static_service)
|
.merge(static_router)
|
||||||
.layer(axum::middleware::from_fn(middleware::server_header))
|
.layer(axum::middleware::from_fn(middleware::server_header))
|
||||||
.layer(tower_http::compression::CompressionLayer::new())
|
.layer(tower_http::compression::CompressionLayer::new())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,8 +260,16 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init_templates(templates_dir: &std::path::Path) -> Option<Arc<TemplateEngine>> {
|
fn init_templates(templates_dir: &std::path::Path) -> Option<Arc<TemplateEngine>> {
|
||||||
let glob = format!("{}/*.html", templates_dir.display()).replace('\\', "/");
|
let use_disk = std::env::var("TEMPLATES_DIR").is_ok() && templates_dir.is_dir();
|
||||||
match TemplateEngine::new(&glob) {
|
let result = if use_disk {
|
||||||
|
let glob = format!("{}/*.html", templates_dir.display()).replace('\\', "/");
|
||||||
|
tracing::info!("Loading templates from disk: {}", templates_dir.display());
|
||||||
|
TemplateEngine::new(&glob)
|
||||||
|
} else {
|
||||||
|
tracing::info!("Loading templates from embedded assets");
|
||||||
|
TemplateEngine::from_embedded()
|
||||||
|
};
|
||||||
|
match result {
|
||||||
Ok(engine) => {
|
Ok(engine) => {
|
||||||
crate::handlers::ui_pages::register_ui_endpoints(&engine);
|
crate::handlers::ui_pages::register_ui_endpoints(&engine);
|
||||||
Some(Arc::new(engine))
|
Some(Arc::new(engine))
|
||||||
|
|||||||
@@ -31,6 +31,33 @@ impl TemplateEngine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_embedded() -> Result<Self, TeraError> {
|
||||||
|
let mut tera = Tera::default();
|
||||||
|
tera.set_escape_fn(html_escape);
|
||||||
|
register_filters(&mut tera);
|
||||||
|
|
||||||
|
let names = crate::embedded::template_names();
|
||||||
|
let mut entries: Vec<(String, String)> = Vec::with_capacity(names.len());
|
||||||
|
for name in names {
|
||||||
|
if let Some(contents) = crate::embedded::template_contents(&name) {
|
||||||
|
entries.push((name, contents));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let refs: Vec<(&str, &str)> = entries
|
||||||
|
.iter()
|
||||||
|
.map(|(n, c)| (n.as_str(), c.as_str()))
|
||||||
|
.collect();
|
||||||
|
tera.add_raw_templates(refs)?;
|
||||||
|
|
||||||
|
let endpoints: Arc<RwLock<HashMap<String, String>>> = Arc::new(RwLock::new(HashMap::new()));
|
||||||
|
register_functions(&mut tera, endpoints.clone());
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
tera: Arc::new(RwLock::new(tera)),
|
||||||
|
endpoints,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register_endpoint(&self, name: &str, path_template: &str) {
|
pub fn register_endpoint(&self, name: &str, path_template: &str) {
|
||||||
self.endpoints
|
self.endpoints
|
||||||
.write()
|
.write()
|
||||||
@@ -343,6 +370,24 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn embedded_templates_parse() {
|
||||||
|
let engine = TemplateEngine::from_embedded().expect("Embedded Tera parse failed");
|
||||||
|
let names: Vec<String> = engine
|
||||||
|
.tera
|
||||||
|
.read()
|
||||||
|
.get_template_names()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect();
|
||||||
|
assert!(
|
||||||
|
names.len() >= 10,
|
||||||
|
"expected 10+ embedded templates, got {}",
|
||||||
|
names.len()
|
||||||
|
);
|
||||||
|
assert!(names.iter().any(|n| n == "login.html"));
|
||||||
|
assert!(names.iter().any(|n| n == "404.html"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn format_datetime_rfc3339() {
|
fn format_datetime_rfc3339() {
|
||||||
let v = format_datetime_filter(
|
let v = format_datetime_filter(
|
||||||
|
|||||||
@@ -1,292 +0,0 @@
|
|||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
TEMPLATE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
|
|
||||||
TERNARY_RE = re.compile(
|
|
||||||
r"""(\{\{\s*)
|
|
||||||
(?:"([^"]*)"|'([^']*)') # literal A
|
|
||||||
\s+if\s+
|
|
||||||
([^{}]+?) # condition
|
|
||||||
\s+else\s+
|
|
||||||
(?:"([^"]*)"|'([^']*)') # literal B
|
|
||||||
(\s*\}\})""",
|
|
||||||
re.VERBOSE,
|
|
||||||
)
|
|
||||||
|
|
||||||
TERNARY_SET_RE = re.compile(
|
|
||||||
r"""(\{%\s*set\s+([A-Za-z_][A-Za-z_0-9]*)\s*=\s*)
|
|
||||||
(?:"([^"]*)"|'([^']*)')
|
|
||||||
\s+if\s+
|
|
||||||
([^{}]+?)
|
|
||||||
\s+else\s+
|
|
||||||
(?:"([^"]*)"|'([^']*)')
|
|
||||||
(\s*%\})""",
|
|
||||||
re.VERBOSE,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def convert_single_quoted_strings_in_expressions(text: str) -> str:
|
|
||||||
"""Inside {{...}} or {%...%}, swap ' for " around tokens that look like strings."""
|
|
||||||
def fix(m):
|
|
||||||
body = m.group(2)
|
|
||||||
body_fixed = re.sub(r"'([^'\\\n]*)'", r'"\1"', body)
|
|
||||||
return m.group(1) + body_fixed + m.group(3)
|
|
||||||
|
|
||||||
return re.sub(
|
|
||||||
r"(\{[{%])([^{}]*?)([}%]\})",
|
|
||||||
fix,
|
|
||||||
text,
|
|
||||||
flags=re.DOTALL,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def convert_inline_ternary(text: str) -> str:
|
|
||||||
def repl_expr(m):
|
|
||||||
a = m.group(2) if m.group(2) is not None else m.group(3)
|
|
||||||
cond = m.group(4)
|
|
||||||
b = m.group(5) if m.group(5) is not None else m.group(6)
|
|
||||||
return (
|
|
||||||
'{% if ' + cond + ' %}' + a + '{% else %}' + b + '{% endif %}'
|
|
||||||
)
|
|
||||||
|
|
||||||
def repl_set(m):
|
|
||||||
varname = m.group(2)
|
|
||||||
a = m.group(3) if m.group(3) is not None else m.group(4)
|
|
||||||
cond = m.group(5)
|
|
||||||
b = m.group(6) if m.group(6) is not None else m.group(7)
|
|
||||||
return (
|
|
||||||
'{% if ' + cond + ' %}{% set ' + varname + ' = "' + a + '" %}'
|
|
||||||
'{% else %}{% set ' + varname + ' = "' + b + '" %}{% endif %}'
|
|
||||||
)
|
|
||||||
|
|
||||||
prev = None
|
|
||||||
while prev != text:
|
|
||||||
prev = text
|
|
||||||
text = TERNARY_SET_RE.sub(repl_set, text)
|
|
||||||
text = TERNARY_RE.sub(repl_expr, text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_request_args(text: str) -> str:
|
|
||||||
text = re.sub(
|
|
||||||
r'request\.args\.get\(\s*"([^"]+)"\s*,\s*"([^"]*)"\s*\)',
|
|
||||||
r'request_args.\1 | default(value="\2")',
|
|
||||||
text,
|
|
||||||
)
|
|
||||||
text = re.sub(
|
|
||||||
r'request\.args\.get\(\s*"([^"]+)"\s*\)',
|
|
||||||
r'request_args.\1',
|
|
||||||
text,
|
|
||||||
)
|
|
||||||
text = text.replace('request.endpoint', 'current_endpoint')
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_items_keys(text: str) -> str:
|
|
||||||
text = re.sub(r'\.items\(\)', '', text)
|
|
||||||
text = re.sub(r'\.keys\(\)', '', text)
|
|
||||||
text = re.sub(r'\.values\(\)', '', text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_tojson(text: str) -> str:
|
|
||||||
text = re.sub(r'\|\s*tojson\b', '| json_encode | safe', text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_is_none(text: str) -> str:
|
|
||||||
text = re.sub(r'\bis\s+not\s+none\b', '!= null', text)
|
|
||||||
text = re.sub(r'\bis\s+none\b', '== null', text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_namespace(text: str) -> str:
|
|
||||||
def repl(m):
|
|
||||||
body = m.group(1)
|
|
||||||
assigns = [a.strip() for a in body.split(',')]
|
|
||||||
return '{# namespace shim #}'
|
|
||||||
|
|
||||||
text = re.sub(
|
|
||||||
r'\{%\s*set\s+ns\s*=\s*namespace\(([^)]*)\)\s*%\}',
|
|
||||||
repl,
|
|
||||||
text,
|
|
||||||
)
|
|
||||||
text = re.sub(r'\bns\.([A-Za-z_][A-Za-z_0-9]*)\s*=\s*', r'{% set_global \1 = ', text)
|
|
||||||
text = re.sub(r'\bns\.([A-Za-z_][A-Za-z_0-9]*)', r'\1', text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_url_for_positional(text: str) -> str:
|
|
||||||
"""url_for("x", ...) -> url_for(endpoint="x", ...)"""
|
|
||||||
def repl(m):
|
|
||||||
prefix = m.group(1)
|
|
||||||
endpoint = m.group(2)
|
|
||||||
rest = m.group(3) or ''
|
|
||||||
rest = rest.strip()
|
|
||||||
if rest.startswith(','):
|
|
||||||
rest = rest[1:].strip()
|
|
||||||
if rest:
|
|
||||||
return f'{prefix}(endpoint="{endpoint}", {rest})'
|
|
||||||
return f'{prefix}(endpoint="{endpoint}")'
|
|
||||||
|
|
||||||
pattern = re.compile(r'(url_for)\(\s*"([^"]+)"\s*((?:,[^()]*)?)\)')
|
|
||||||
prev = None
|
|
||||||
while prev != text:
|
|
||||||
prev = text
|
|
||||||
text = pattern.sub(repl, text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_d_filter(text: str) -> str:
|
|
||||||
text = re.sub(r'\|\s*d\(\s*([^)]*?)\s*\)', lambda m: f'| default(value={m.group(1) or 0})', text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_replace_filter(text: str) -> str:
|
|
||||||
def repl(m):
|
|
||||||
a = m.group(1)
|
|
||||||
b = m.group(2)
|
|
||||||
return f'| replace(from="{a}", to="{b}")'
|
|
||||||
text = re.sub(r'\|\s*replace\(\s*"([^"]*)"\s*,\s*"([^"]*)"\s*\)', repl, text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_truncate_filter(text: str) -> str:
|
|
||||||
def repl(m):
|
|
||||||
n = m.group(1)
|
|
||||||
return f'| truncate(length={n})'
|
|
||||||
text = re.sub(r'\|\s*truncate\(\s*(\d+)\s*(?:,[^)]*)?\)', repl, text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_strip_method(text: str) -> str:
|
|
||||||
text = re.sub(r'(\b[A-Za-z_][A-Za-z_0-9.\[\]"]*)\s*\.\s*strip\(\s*\)', r'\1 | trim', text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_split_method(text: str) -> str:
|
|
||||||
def repl(m):
|
|
||||||
obj = m.group(1)
|
|
||||||
sep = m.group(2)
|
|
||||||
return f'{obj} | split(pat="{sep}")'
|
|
||||||
text = re.sub(r'(\b[A-Za-z_][A-Za-z_0-9.]*)\s*\.\s*split\(\s*"([^"]*)"\s*\)', repl, text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_python_slice(text: str) -> str:
|
|
||||||
def repl_colon(m):
|
|
||||||
obj = m.group(1)
|
|
||||||
start = m.group(2) or '0'
|
|
||||||
end = m.group(3)
|
|
||||||
if start.startswith('-') or (end and end.startswith('-')):
|
|
||||||
return m.group(0)
|
|
||||||
if end:
|
|
||||||
return f'{obj} | slice(start={start}, end={end})'
|
|
||||||
return f'{obj} | slice(start={start})'
|
|
||||||
|
|
||||||
def repl_neg_end(m):
|
|
||||||
obj = m.group(1)
|
|
||||||
n = m.group(2)
|
|
||||||
return f'{obj} | slice(start=-{n})'
|
|
||||||
|
|
||||||
text = re.sub(
|
|
||||||
r'(\b[A-Za-z_][A-Za-z_0-9.]*)\[\s*(-?\d*)\s*:\s*(-?\d*)\s*\]',
|
|
||||||
repl_colon,
|
|
||||||
text,
|
|
||||||
)
|
|
||||||
text = re.sub(
|
|
||||||
r'(\b[A-Za-z_][A-Za-z_0-9.]*)\|\s*slice\(start=-(\d+)\s*,\s*end=\s*\)',
|
|
||||||
repl_neg_end,
|
|
||||||
text,
|
|
||||||
)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_inline_ternary_expr(text: str) -> str:
|
|
||||||
"""Handle arbitrary ternary inside {{ ... }}: A if COND else B -> {% if COND %}A{% else %}B{% endif %}"""
|
|
||||||
out_lines = []
|
|
||||||
for line in text.split('\n'):
|
|
||||||
out_lines.append(_convert_line_ternary(line))
|
|
||||||
return '\n'.join(out_lines)
|
|
||||||
|
|
||||||
|
|
||||||
def _convert_line_ternary(line: str) -> str:
|
|
||||||
if '{{' not in line or ' if ' not in line or ' else ' not in line:
|
|
||||||
return line
|
|
||||||
prev = None
|
|
||||||
while prev != line:
|
|
||||||
prev = line
|
|
||||||
m = re.search(r'\{\{\s*([^{}]+?)\s+if\s+([^{}]+?)\s+else\s+([^{}]+?)\s*\}\}', line)
|
|
||||||
if not m:
|
|
||||||
break
|
|
||||||
replacement = '{% if ' + m.group(2) + ' %}{{ ' + m.group(1) + ' }}{% else %}{{ ' + m.group(3) + ' }}{% endif %}'
|
|
||||||
line = line[:m.start()] + replacement + line[m.end():]
|
|
||||||
return line
|
|
||||||
|
|
||||||
|
|
||||||
def convert_dict_get(text: str) -> str:
|
|
||||||
"""Convert X.get("key", default) -> X.key | default(value=default) when simple."""
|
|
||||||
pattern = re.compile(
|
|
||||||
r'([A-Za-z_][A-Za-z_0-9]*(?:\.[A-Za-z_][A-Za-z_0-9]*)*)'
|
|
||||||
r'\.get\(\s*"([A-Za-z_][A-Za-z_0-9]*)"\s*(?:,\s*([^(){}]+?))?\s*\)'
|
|
||||||
)
|
|
||||||
|
|
||||||
def repl(m):
|
|
||||||
obj = m.group(1)
|
|
||||||
key = m.group(2)
|
|
||||||
default = (m.group(3) or '').strip()
|
|
||||||
if default:
|
|
||||||
return f'{obj}.{key} | default(value={default})'
|
|
||||||
return f'{obj}.{key}'
|
|
||||||
|
|
||||||
prev = None
|
|
||||||
while prev != text:
|
|
||||||
prev = text
|
|
||||||
text = pattern.sub(repl, text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def convert_file(path: str) -> bool:
|
|
||||||
with open(path, 'r', encoding='utf-8') as f:
|
|
||||||
original = f.read()
|
|
||||||
text = original
|
|
||||||
text = convert_single_quoted_strings_in_expressions(text)
|
|
||||||
text = convert_inline_ternary(text)
|
|
||||||
text = convert_request_args(text)
|
|
||||||
text = convert_items_keys(text)
|
|
||||||
text = convert_tojson(text)
|
|
||||||
text = convert_is_none(text)
|
|
||||||
text = convert_namespace(text)
|
|
||||||
text = convert_dict_get(text)
|
|
||||||
text = convert_url_for_positional(text)
|
|
||||||
text = convert_d_filter(text)
|
|
||||||
text = convert_replace_filter(text)
|
|
||||||
text = convert_truncate_filter(text)
|
|
||||||
text = convert_strip_method(text)
|
|
||||||
text = convert_split_method(text)
|
|
||||||
text = convert_python_slice(text)
|
|
||||||
text = convert_inline_ternary_expr(text)
|
|
||||||
if text != original:
|
|
||||||
with open(path, 'w', encoding='utf-8', newline='\n') as f:
|
|
||||||
f.write(text)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
changed = []
|
|
||||||
for name in sorted(os.listdir(TEMPLATE_DIR)):
|
|
||||||
if not name.endswith('.html'):
|
|
||||||
continue
|
|
||||||
p = os.path.join(TEMPLATE_DIR, name)
|
|
||||||
if convert_file(p):
|
|
||||||
changed.append(name)
|
|
||||||
print('Changed:', len(changed))
|
|
||||||
for c in changed:
|
|
||||||
print(' -', c)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@@ -11,11 +11,12 @@
|
|||||||
# --data-dir DIR Data directory (default: /var/lib/myfsio)
|
# --data-dir DIR Data directory (default: /var/lib/myfsio)
|
||||||
# --log-dir DIR Log directory (default: /var/log/myfsio)
|
# --log-dir DIR Log directory (default: /var/log/myfsio)
|
||||||
# --user USER System user to run as (default: myfsio)
|
# --user USER System user to run as (default: myfsio)
|
||||||
|
# --host HOST Bind host (default: 0.0.0.0)
|
||||||
# --port PORT API port (default: 5000)
|
# --port PORT API port (default: 5000)
|
||||||
# --ui-port PORT UI port (default: 5100)
|
# --ui-port PORT UI port (default: 5100)
|
||||||
# --api-url URL Public API URL (for presigned URLs behind proxy)
|
# --api-url URL Public API URL (for presigned URLs behind proxy)
|
||||||
# --no-systemd Skip systemd service creation
|
# --no-systemd Skip systemd service creation
|
||||||
# --binary PATH Path to myfsio binary (will download if not provided)
|
# --binary PATH Path to myfsio binary (default: ./myfsio)
|
||||||
# -y, --yes Skip confirmation prompts
|
# -y, --yes Skip confirmation prompts
|
||||||
#
|
#
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ INSTALL_DIR="/opt/myfsio"
|
|||||||
DATA_DIR="/var/lib/myfsio"
|
DATA_DIR="/var/lib/myfsio"
|
||||||
LOG_DIR="/var/log/myfsio"
|
LOG_DIR="/var/log/myfsio"
|
||||||
SERVICE_USER="myfsio"
|
SERVICE_USER="myfsio"
|
||||||
|
BIND_HOST="0.0.0.0"
|
||||||
API_PORT="5000"
|
API_PORT="5000"
|
||||||
UI_PORT="5100"
|
UI_PORT="5100"
|
||||||
API_URL=""
|
API_URL=""
|
||||||
@@ -34,54 +36,19 @@ AUTO_YES=false
|
|||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
--install-dir)
|
--install-dir) INSTALL_DIR="$2"; shift 2 ;;
|
||||||
INSTALL_DIR="$2"
|
--data-dir) DATA_DIR="$2"; shift 2 ;;
|
||||||
shift 2
|
--log-dir) LOG_DIR="$2"; shift 2 ;;
|
||||||
;;
|
--user) SERVICE_USER="$2"; shift 2 ;;
|
||||||
--data-dir)
|
--host) BIND_HOST="$2"; shift 2 ;;
|
||||||
DATA_DIR="$2"
|
--port) API_PORT="$2"; shift 2 ;;
|
||||||
shift 2
|
--ui-port) UI_PORT="$2"; shift 2 ;;
|
||||||
;;
|
--api-url) API_URL="$2"; shift 2 ;;
|
||||||
--log-dir)
|
--no-systemd) SKIP_SYSTEMD=true; shift ;;
|
||||||
LOG_DIR="$2"
|
--binary) BINARY_PATH="$2"; shift 2 ;;
|
||||||
shift 2
|
-y|--yes) AUTO_YES=true; shift ;;
|
||||||
;;
|
-h|--help) head -22 "$0" | tail -17; exit 0 ;;
|
||||||
--user)
|
*) echo "Unknown option: $1"; exit 1 ;;
|
||||||
SERVICE_USER="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--port)
|
|
||||||
API_PORT="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--ui-port)
|
|
||||||
UI_PORT="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--api-url)
|
|
||||||
API_URL="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--no-systemd)
|
|
||||||
SKIP_SYSTEMD=true
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--binary)
|
|
||||||
BINARY_PATH="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-y|--yes)
|
|
||||||
AUTO_YES=true
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-h|--help)
|
|
||||||
head -30 "$0" | tail -25
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unknown option: $1"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -107,14 +74,11 @@ echo " Install directory: $INSTALL_DIR"
|
|||||||
echo " Data directory: $DATA_DIR"
|
echo " Data directory: $DATA_DIR"
|
||||||
echo " Log directory: $LOG_DIR"
|
echo " Log directory: $LOG_DIR"
|
||||||
echo " Service user: $SERVICE_USER"
|
echo " Service user: $SERVICE_USER"
|
||||||
|
echo " Bind host: $BIND_HOST"
|
||||||
echo " API port: $API_PORT"
|
echo " API port: $API_PORT"
|
||||||
echo " UI port: $UI_PORT"
|
echo " UI port: $UI_PORT"
|
||||||
if [[ -n "$API_URL" ]]; then
|
[[ -n "$API_URL" ]] && echo " Public API URL: $API_URL"
|
||||||
echo " Public API URL: $API_URL"
|
[[ -n "$BINARY_PATH" ]] && echo " Binary: $BINARY_PATH"
|
||||||
fi
|
|
||||||
if [[ -n "$BINARY_PATH" ]]; then
|
|
||||||
echo " Binary path: $BINARY_PATH"
|
|
||||||
fi
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [[ "$AUTO_YES" != true ]]; then
|
if [[ "$AUTO_YES" != true ]]; then
|
||||||
@@ -143,12 +107,9 @@ echo "------------------------------------------------------------"
|
|||||||
echo "STEP 3: Creating Directories"
|
echo "STEP 3: Creating Directories"
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo ""
|
echo ""
|
||||||
mkdir -p "$INSTALL_DIR"
|
mkdir -p "$INSTALL_DIR" && echo " [OK] Created $INSTALL_DIR"
|
||||||
echo " [OK] Created $INSTALL_DIR"
|
mkdir -p "$DATA_DIR" && echo " [OK] Created $DATA_DIR"
|
||||||
mkdir -p "$DATA_DIR"
|
mkdir -p "$LOG_DIR" && echo " [OK] Created $LOG_DIR"
|
||||||
echo " [OK] Created $DATA_DIR"
|
|
||||||
mkdir -p "$LOG_DIR"
|
|
||||||
echo " [OK] Created $LOG_DIR"
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
@@ -156,13 +117,12 @@ echo "STEP 4: Installing Binary"
|
|||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo ""
|
echo ""
|
||||||
if [[ -n "$BINARY_PATH" ]]; then
|
if [[ -n "$BINARY_PATH" ]]; then
|
||||||
if [[ -f "$BINARY_PATH" ]]; then
|
if [[ ! -f "$BINARY_PATH" ]]; then
|
||||||
cp "$BINARY_PATH" "$INSTALL_DIR/myfsio"
|
|
||||||
echo " [OK] Copied binary from $BINARY_PATH"
|
|
||||||
else
|
|
||||||
echo " [ERROR] Binary not found at $BINARY_PATH"
|
echo " [ERROR] Binary not found at $BINARY_PATH"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
cp "$BINARY_PATH" "$INSTALL_DIR/myfsio"
|
||||||
|
echo " [OK] Copied binary from $BINARY_PATH"
|
||||||
elif [[ -f "./myfsio" ]]; then
|
elif [[ -f "./myfsio" ]]; then
|
||||||
cp "./myfsio" "$INSTALL_DIR/myfsio"
|
cp "./myfsio" "$INSTALL_DIR/myfsio"
|
||||||
echo " [OK] Copied binary from ./myfsio"
|
echo " [OK] Copied binary from ./myfsio"
|
||||||
@@ -173,20 +133,53 @@ else
|
|||||||
fi
|
fi
|
||||||
chmod +x "$INSTALL_DIR/myfsio"
|
chmod +x "$INSTALL_DIR/myfsio"
|
||||||
echo " [OK] Set executable permissions"
|
echo " [OK] Set executable permissions"
|
||||||
|
echo " [INFO] Templates and static UI assets are embedded in the binary"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo "STEP 5: Generating Secret Key"
|
echo "STEP 5: Creating Configuration File"
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo ""
|
echo ""
|
||||||
SECRET_KEY=$(openssl rand -base64 32)
|
|
||||||
echo " [OK] Generated secure SECRET_KEY"
|
|
||||||
|
|
||||||
echo ""
|
SECRET_FILE="$DATA_DIR/.myfsio.sys/config/.secret"
|
||||||
echo "------------------------------------------------------------"
|
mkdir -p "$(dirname "$SECRET_FILE")"
|
||||||
echo "STEP 6: Creating Configuration File"
|
if [[ -s "$SECRET_FILE" ]]; then
|
||||||
echo "------------------------------------------------------------"
|
echo " [OK] Existing secret found at $SECRET_FILE - preserving"
|
||||||
echo ""
|
elif [[ -n "${SECRET_KEY:-}" ]]; then
|
||||||
|
printf '%s' "$SECRET_KEY" > "$SECRET_FILE"
|
||||||
|
chmod 600 "$SECRET_FILE"
|
||||||
|
echo " [OK] Wrote SECRET_KEY from environment to $SECRET_FILE"
|
||||||
|
else
|
||||||
|
if command -v openssl &>/dev/null; then
|
||||||
|
printf '%s' "$(openssl rand -base64 32)" > "$SECRET_FILE"
|
||||||
|
elif [[ -r /dev/urandom ]]; then
|
||||||
|
printf '%s' "$(head -c 32 /dev/urandom | base64)" > "$SECRET_FILE"
|
||||||
|
else
|
||||||
|
echo " [ERROR] Neither openssl nor /dev/urandom available; cannot generate secret"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
chmod 600 "$SECRET_FILE"
|
||||||
|
echo " [OK] Generated secret key at $SECRET_FILE"
|
||||||
|
fi
|
||||||
|
unset SECRET_KEY
|
||||||
|
|
||||||
|
if [[ -n "$API_URL" ]]; then
|
||||||
|
EFFECTIVE_API_URL="$API_URL"
|
||||||
|
else
|
||||||
|
case "$BIND_HOST" in
|
||||||
|
0.0.0.0|::|"")
|
||||||
|
DETECTED_IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||||
|
[[ -z "$DETECTED_IP" ]] && DETECTED_IP="127.0.0.1"
|
||||||
|
EFFECTIVE_API_URL="http://$DETECTED_IP:$API_PORT"
|
||||||
|
echo " [INFO] Bind host is $BIND_HOST; deriving API_BASE_URL=$EFFECTIVE_API_URL"
|
||||||
|
echo " Pass --api-url to set a public URL for presigned access."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
EFFECTIVE_API_URL="http://$BIND_HOST:$API_PORT"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
cat > "$INSTALL_DIR/myfsio.env" << EOF
|
cat > "$INSTALL_DIR/myfsio.env" << EOF
|
||||||
# MyFSIO Configuration
|
# MyFSIO Configuration
|
||||||
# Generated by install.sh on $(date)
|
# Generated by install.sh on $(date)
|
||||||
@@ -196,61 +189,63 @@ cat > "$INSTALL_DIR/myfsio.env" << EOF
|
|||||||
# STORAGE PATHS
|
# STORAGE PATHS
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
STORAGE_ROOT=$DATA_DIR
|
STORAGE_ROOT=$DATA_DIR
|
||||||
LOG_DIR=$LOG_DIR
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# NETWORK
|
# NETWORK
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
APP_HOST=0.0.0.0
|
HOST=$BIND_HOST
|
||||||
APP_PORT=$API_PORT
|
PORT=$API_PORT
|
||||||
|
UI_PORT=$UI_PORT
|
||||||
|
|
||||||
# Public URL (set this if behind a reverse proxy for presigned URLs)
|
# Public URL used to sign presigned URLs (override with --api-url for proxies)
|
||||||
$(if [[ -n "$API_URL" ]]; then echo "API_BASE_URL=$API_URL"; else echo "# API_BASE_URL=https://s3.example.com"; fi)
|
API_BASE_URL=$EFFECTIVE_API_URL
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# SECURITY
|
# SECURITY
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Secret key for session signing (auto-generated if not set)
|
|
||||||
SECRET_KEY=$SECRET_KEY
|
|
||||||
|
|
||||||
# CORS settings - restrict in production
|
# CORS settings - restrict in production
|
||||||
CORS_ORIGINS=*
|
CORS_ORIGINS=*
|
||||||
|
# CORS_METHODS=GET,PUT,POST,DELETE,OPTIONS,HEAD
|
||||||
|
# CORS_ALLOW_HEADERS=*
|
||||||
|
# CORS_EXPOSE_HEADERS=*
|
||||||
|
|
||||||
# Brute-force protection
|
# Reverse proxy settings (number of trusted proxies in front)
|
||||||
AUTH_MAX_ATTEMPTS=5
|
|
||||||
AUTH_LOCKOUT_MINUTES=15
|
|
||||||
|
|
||||||
# Reverse proxy settings (set to number of trusted proxies in front)
|
|
||||||
# NUM_TRUSTED_PROXIES=1
|
# NUM_TRUSTED_PROXIES=1
|
||||||
|
|
||||||
# Allow internal admin endpoints (only enable on trusted networks)
|
# Allow internal/diagnostic admin endpoints (only on trusted networks)
|
||||||
# ALLOW_INTERNAL_ENDPOINTS=false
|
# ALLOW_INTERNAL_ENDPOINTS=false
|
||||||
|
|
||||||
# Allowed hosts for redirects (comma-separated, empty = restrict all)
|
# Comma-separated external hosts allowed for UI login redirects
|
||||||
# ALLOWED_REDIRECT_HOSTS=
|
# ALLOWED_REDIRECT_HOSTS=
|
||||||
|
|
||||||
|
# UI session lifetime in days
|
||||||
|
# SESSION_LIFETIME_DAYS=1
|
||||||
|
|
||||||
|
# SigV4 timestamp tolerance (seconds)
|
||||||
|
# SIGV4_TIMESTAMP_TOLERANCE_SECONDS=900
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# UI ASSET OVERRIDES (optional - assets are embedded in the binary by default)
|
||||||
|
# =============================================================================
|
||||||
|
# Set these only when developing UI changes against an unpacked source tree.
|
||||||
|
# TEMPLATES_DIR=/path/to/templates
|
||||||
|
# STATIC_DIR=/path/to/static
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# LOGGING
|
# LOGGING
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
LOG_LEVEL=INFO
|
LOG_LEVEL=INFO
|
||||||
LOG_TO_FILE=true
|
# RUST_LOG=info,myfsio_server=info
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# RATE LIMITING
|
# RATE LIMITING
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
RATE_LIMIT_DEFAULT=200 per minute
|
RATE_LIMIT_DEFAULT=500 per minute
|
||||||
# RATE_LIMIT_LIST_BUCKETS=60 per minute
|
# RATE_LIMIT_LIST_BUCKETS=500 per minute
|
||||||
# RATE_LIMIT_BUCKET_OPS=120 per minute
|
# RATE_LIMIT_BUCKET_OPS=500 per minute
|
||||||
# RATE_LIMIT_OBJECT_OPS=240 per minute
|
# RATE_LIMIT_OBJECT_OPS=500 per minute
|
||||||
# RATE_LIMIT_ADMIN=60 per minute
|
# RATE_LIMIT_HEAD_OPS=500 per minute
|
||||||
|
RATE_LIMIT_ADMIN=60 per minute
|
||||||
# =============================================================================
|
|
||||||
# SERVER TUNING (0 = auto-detect based on system resources)
|
|
||||||
# =============================================================================
|
|
||||||
# SERVER_THREADS=0
|
|
||||||
# SERVER_CONNECTION_LIMIT=0
|
|
||||||
# SERVER_BACKLOG=0
|
|
||||||
# SERVER_CHANNEL_TIMEOUT=120
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# ENCRYPTION (uncomment to enable)
|
# ENCRYPTION (uncomment to enable)
|
||||||
@@ -259,39 +254,50 @@ RATE_LIMIT_DEFAULT=200 per minute
|
|||||||
# KMS_ENABLED=true
|
# KMS_ENABLED=true
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# SITE SYNC / REPLICATION (for multi-site deployments)
|
# SITE SYNC / REPLICATION (multi-site deployments)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# SITE_ID=site-1
|
# SITE_ID=site-1
|
||||||
# SITE_ENDPOINT=https://s3-site1.example.com
|
# SITE_ENDPOINT=https://s3-site1.example.com
|
||||||
# SITE_REGION=us-east-1
|
# SITE_REGION=us-east-1
|
||||||
|
# SITE_PRIORITY=100
|
||||||
# SITE_SYNC_ENABLED=false
|
# SITE_SYNC_ENABLED=false
|
||||||
|
# SITE_SYNC_INTERVAL_SECONDS=60
|
||||||
|
# SITE_SYNC_BATCH_SIZE=100
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# OPTIONAL FEATURES
|
# OPTIONAL FEATURES
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
# WEBSITE_HOSTING_ENABLED=false
|
||||||
# LIFECYCLE_ENABLED=false
|
# LIFECYCLE_ENABLED=false
|
||||||
# METRICS_HISTORY_ENABLED=false
|
# METRICS_HISTORY_ENABLED=false
|
||||||
# OPERATION_METRICS_ENABLED=false
|
# OPERATION_METRICS_ENABLED=false
|
||||||
|
# GC_ENABLED=false
|
||||||
|
# GC_INTERVAL_HOURS=6
|
||||||
|
# GC_DRY_RUN=false
|
||||||
|
# INTEGRITY_ENABLED=false
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# FIRST-RUN ADMIN OVERRIDE (optional)
|
||||||
|
# =============================================================================
|
||||||
|
# ADMIN_ACCESS_KEY=
|
||||||
|
# ADMIN_SECRET_KEY=
|
||||||
EOF
|
EOF
|
||||||
chmod 600 "$INSTALL_DIR/myfsio.env"
|
chmod 600 "$INSTALL_DIR/myfsio.env"
|
||||||
echo " [OK] Created $INSTALL_DIR/myfsio.env"
|
echo " [OK] Created $INSTALL_DIR/myfsio.env"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo "STEP 7: Setting Permissions"
|
echo "STEP 6: Setting Permissions"
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo ""
|
echo ""
|
||||||
chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR"
|
chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR" && echo " [OK] Set ownership for $INSTALL_DIR"
|
||||||
echo " [OK] Set ownership for $INSTALL_DIR"
|
chown -R "$SERVICE_USER:$SERVICE_USER" "$DATA_DIR" && echo " [OK] Set ownership for $DATA_DIR"
|
||||||
chown -R "$SERVICE_USER:$SERVICE_USER" "$DATA_DIR"
|
chown -R "$SERVICE_USER:$SERVICE_USER" "$LOG_DIR" && echo " [OK] Set ownership for $LOG_DIR"
|
||||||
echo " [OK] Set ownership for $DATA_DIR"
|
|
||||||
chown -R "$SERVICE_USER:$SERVICE_USER" "$LOG_DIR"
|
|
||||||
echo " [OK] Set ownership for $LOG_DIR"
|
|
||||||
|
|
||||||
if [[ "$SKIP_SYSTEMD" != true ]]; then
|
if [[ "$SKIP_SYSTEMD" != true ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo "STEP 8: Creating Systemd Service"
|
echo "STEP 7: Creating Systemd Service"
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo ""
|
echo ""
|
||||||
cat > /etc/systemd/system/myfsio.service << EOF
|
cat > /etc/systemd/system/myfsio.service << EOF
|
||||||
@@ -306,18 +312,16 @@ User=$SERVICE_USER
|
|||||||
Group=$SERVICE_USER
|
Group=$SERVICE_USER
|
||||||
WorkingDirectory=$INSTALL_DIR
|
WorkingDirectory=$INSTALL_DIR
|
||||||
EnvironmentFile=$INSTALL_DIR/myfsio.env
|
EnvironmentFile=$INSTALL_DIR/myfsio.env
|
||||||
ExecStart=$INSTALL_DIR/myfsio
|
ExecStart=$INSTALL_DIR/myfsio serve
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
|
|
||||||
# Security hardening
|
|
||||||
NoNewPrivileges=true
|
NoNewPrivileges=true
|
||||||
ProtectSystem=strict
|
ProtectSystem=strict
|
||||||
ProtectHome=true
|
ProtectHome=true
|
||||||
ReadWritePaths=$DATA_DIR $LOG_DIR
|
ReadWritePaths=$DATA_DIR $LOG_DIR
|
||||||
PrivateTmp=true
|
PrivateTmp=true
|
||||||
|
|
||||||
# Resource limits (adjust as needed)
|
|
||||||
# LimitNOFILE=65535
|
# LimitNOFILE=65535
|
||||||
# MemoryMax=2G
|
# MemoryMax=2G
|
||||||
|
|
||||||
@@ -331,7 +335,7 @@ EOF
|
|||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo "STEP 8: Skipping Systemd Service (--no-systemd flag used)"
|
echo "STEP 7: Skipping Systemd Service (--no-systemd flag used)"
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -343,30 +347,33 @@ echo ""
|
|||||||
|
|
||||||
if [[ "$SKIP_SYSTEMD" != true ]]; then
|
if [[ "$SKIP_SYSTEMD" != true ]]; then
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo "STEP 9: Start the Service"
|
echo "STEP 8: Start the Service"
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [[ "$AUTO_YES" != true ]]; then
|
if [[ "$AUTO_YES" != true ]]; then
|
||||||
read -p "Would you like to start MyFSIO now? [Y/n] " -n 1 -r
|
read -p "Would you like to start MyFSIO now? [Y/n] " -n 1 -r
|
||||||
echo
|
echo
|
||||||
START_SERVICE=true
|
START_SERVICE=true
|
||||||
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
[[ $REPLY =~ ^[Nn]$ ]] && START_SERVICE=false
|
||||||
START_SERVICE=false
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
START_SERVICE=true
|
START_SERVICE=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$START_SERVICE" == true ]]; then
|
if [[ "$START_SERVICE" == true ]]; then
|
||||||
echo " Starting MyFSIO service..."
|
echo " Starting MyFSIO service..."
|
||||||
systemctl start myfsio
|
systemctl start myfsio
|
||||||
echo " [OK] Service started"
|
echo " [OK] Service started"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
read -p "Would you like to enable MyFSIO to start on boot? [Y/n] " -n 1 -r
|
if [[ "$AUTO_YES" != true ]]; then
|
||||||
echo
|
read -p "Would you like to enable MyFSIO to start on boot? [Y/n] " -n 1 -r
|
||||||
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
||||||
|
systemctl enable myfsio
|
||||||
|
echo " [OK] Service enabled on boot"
|
||||||
|
fi
|
||||||
|
else
|
||||||
systemctl enable myfsio
|
systemctl enable myfsio
|
||||||
echo " [OK] Service enabled on boot"
|
echo " [OK] Service enabled on boot"
|
||||||
fi
|
fi
|
||||||
@@ -383,21 +390,18 @@ if [[ "$SKIP_SYSTEMD" != true ]]; then
|
|||||||
echo " ============================================"
|
echo " ============================================"
|
||||||
echo " ADMIN CREDENTIALS (save these securely!)"
|
echo " ADMIN CREDENTIALS (save these securely!)"
|
||||||
echo " ============================================"
|
echo " ============================================"
|
||||||
CRED_OUTPUT=$(journalctl -u myfsio --no-pager -n 50 2>/dev/null | grep -A 5 "FIRST RUN - ADMIN CREDENTIALS")
|
CRED_OUTPUT=$(journalctl -u myfsio --no-pager -n 100 2>/dev/null | grep -A 5 "FIRST RUN - ADMIN CREDENTIALS")
|
||||||
ACCESS_KEY=$(echo "$CRED_OUTPUT" | grep "Access Key:" | head -1 | sed 's/.*Access Key: //' | awk '{print $1}')
|
ACCESS_KEY=$(echo "$CRED_OUTPUT" | grep "Access Key:" | head -1 | sed 's/.*Access Key: //' | awk '{print $1}')
|
||||||
SECRET_KEY=$(echo "$CRED_OUTPUT" | grep "Secret Key:" | head -1 | sed 's/.*Secret Key: //' | awk '{print $1}')
|
SECRET_KEY=$(echo "$CRED_OUTPUT" | grep "Secret Key:" | head -1 | sed 's/.*Secret Key: //' | awk '{print $1}')
|
||||||
if [[ -n "$ACCESS_KEY" && "$ACCESS_KEY" != *"from"* && -n "$SECRET_KEY" && "$SECRET_KEY" != *"from"* ]]; then
|
if [[ -n "$ACCESS_KEY" && -n "$SECRET_KEY" ]]; then
|
||||||
echo " Access Key: $ACCESS_KEY"
|
echo " Access Key: $ACCESS_KEY"
|
||||||
echo " Secret Key: $SECRET_KEY"
|
echo " Secret Key: $SECRET_KEY"
|
||||||
else
|
else
|
||||||
echo " [!] Could not extract credentials from service logs."
|
echo " [!] Could not extract credentials from service logs."
|
||||||
echo " Check startup output: journalctl -u myfsio --no-pager | grep -A 5 'ADMIN CREDENTIALS'"
|
echo " Check: journalctl -u myfsio --no-pager | grep -A 5 'ADMIN CREDENTIALS'"
|
||||||
echo " Or reset credentials: $INSTALL_DIR/myfsio reset-cred"
|
echo " Or reset: $INSTALL_DIR/myfsio --reset-cred"
|
||||||
fi
|
fi
|
||||||
echo " ============================================"
|
echo " ============================================"
|
||||||
echo ""
|
|
||||||
echo " NOTE: The IAM config file is encrypted at rest."
|
|
||||||
echo " Credentials are only shown on first run or after reset."
|
|
||||||
else
|
else
|
||||||
echo " [WARNING] MyFSIO may not have started correctly"
|
echo " [WARNING] MyFSIO may not have started correctly"
|
||||||
echo " Check logs with: journalctl -u myfsio -f"
|
echo " Check logs with: journalctl -u myfsio -f"
|
||||||
@@ -405,27 +409,21 @@ if [[ "$SKIP_SYSTEMD" != true ]]; then
|
|||||||
else
|
else
|
||||||
echo " [SKIPPED] Service not started"
|
echo " [SKIPPED] Service not started"
|
||||||
echo ""
|
echo ""
|
||||||
echo " To start manually, run:"
|
echo " Start manually: sudo systemctl start myfsio"
|
||||||
echo " sudo systemctl start myfsio"
|
echo " Enable on boot: sudo systemctl enable myfsio"
|
||||||
echo ""
|
|
||||||
echo " To enable on boot, run:"
|
|
||||||
echo " sudo systemctl enable myfsio"
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
HOST_IP=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost")
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "============================================================"
|
echo "============================================================"
|
||||||
echo " Summary"
|
echo " Summary"
|
||||||
echo "============================================================"
|
echo "============================================================"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Access Points:"
|
echo "Access Points:"
|
||||||
echo " API: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost"):$API_PORT"
|
echo " S3 API: http://$HOST_IP:$API_PORT"
|
||||||
echo " UI: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost"):$UI_PORT/ui"
|
echo " Web UI: http://$HOST_IP:$UI_PORT/ui"
|
||||||
echo ""
|
|
||||||
echo "Credentials:"
|
|
||||||
echo " Admin credentials are shown on first service start (see above)."
|
|
||||||
echo " The IAM config is encrypted at rest and cannot be read directly."
|
|
||||||
echo " To reset credentials: $INSTALL_DIR/myfsio reset-cred"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Configuration Files:"
|
echo "Configuration Files:"
|
||||||
echo " Environment: $INSTALL_DIR/myfsio.env"
|
echo " Environment: $INSTALL_DIR/myfsio.env"
|
||||||
@@ -433,18 +431,14 @@ echo " IAM Users: $DATA_DIR/.myfsio.sys/config/iam.json (encrypted)"
|
|||||||
echo " Bucket Policies: $DATA_DIR/.myfsio.sys/config/bucket_policies.json"
|
echo " Bucket Policies: $DATA_DIR/.myfsio.sys/config/bucket_policies.json"
|
||||||
echo " Secret Key: $DATA_DIR/.myfsio.sys/config/.secret (auto-generated)"
|
echo " Secret Key: $DATA_DIR/.myfsio.sys/config/.secret (auto-generated)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Security Notes:"
|
|
||||||
echo " - Rate limiting is enabled by default (200 req/min)"
|
|
||||||
echo " - Brute-force protection: 5 attempts, 15 min lockout"
|
|
||||||
echo " - Set CORS_ORIGINS to specific domains in production"
|
|
||||||
echo " - Set NUM_TRUSTED_PROXIES if behind a reverse proxy"
|
|
||||||
echo ""
|
|
||||||
echo "Useful Commands:"
|
echo "Useful Commands:"
|
||||||
echo " Check status: sudo systemctl status myfsio"
|
echo " Check status: sudo systemctl status myfsio"
|
||||||
echo " View logs: sudo journalctl -u myfsio -f"
|
echo " View logs: sudo journalctl -u myfsio -f"
|
||||||
echo " Validate config: $INSTALL_DIR/myfsio --check-config"
|
echo " Validate config: $INSTALL_DIR/myfsio --check-config"
|
||||||
echo " Restart: sudo systemctl restart myfsio"
|
echo " Show config: $INSTALL_DIR/myfsio --show-config"
|
||||||
echo " Stop: sudo systemctl stop myfsio"
|
echo " Reset admin: sudo -u $SERVICE_USER $INSTALL_DIR/myfsio --reset-cred"
|
||||||
|
echo " Restart: sudo systemctl restart myfsio"
|
||||||
|
echo " Stop: sudo systemctl stop myfsio"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Documentation: https://go.jzwsite.com/myfsio"
|
echo "Documentation: https://go.jzwsite.com/myfsio"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
# --data-dir DIR Data directory (default: /var/lib/myfsio)
|
# --data-dir DIR Data directory (default: /var/lib/myfsio)
|
||||||
# --log-dir DIR Log directory (default: /var/log/myfsio)
|
# --log-dir DIR Log directory (default: /var/log/myfsio)
|
||||||
# --user USER System user (default: myfsio)
|
# --user USER System user (default: myfsio)
|
||||||
|
# --no-systemd Skip systemd service teardown
|
||||||
# -y, --yes Skip confirmation prompts
|
# -y, --yes Skip confirmation prompts
|
||||||
#
|
#
|
||||||
|
|
||||||
@@ -24,46 +25,21 @@ LOG_DIR="/var/log/myfsio"
|
|||||||
SERVICE_USER="myfsio"
|
SERVICE_USER="myfsio"
|
||||||
KEEP_DATA=false
|
KEEP_DATA=false
|
||||||
KEEP_LOGS=false
|
KEEP_LOGS=false
|
||||||
|
SKIP_SYSTEMD=false
|
||||||
AUTO_YES=false
|
AUTO_YES=false
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
--keep-data)
|
--keep-data) KEEP_DATA=true; shift ;;
|
||||||
KEEP_DATA=true
|
--keep-logs) KEEP_LOGS=true; shift ;;
|
||||||
shift
|
--install-dir) INSTALL_DIR="$2"; shift 2 ;;
|
||||||
;;
|
--data-dir) DATA_DIR="$2"; shift 2 ;;
|
||||||
--keep-logs)
|
--log-dir) LOG_DIR="$2"; shift 2 ;;
|
||||||
KEEP_LOGS=true
|
--user) SERVICE_USER="$2"; shift 2 ;;
|
||||||
shift
|
--no-systemd) SKIP_SYSTEMD=true; shift ;;
|
||||||
;;
|
-y|--yes) AUTO_YES=true; shift ;;
|
||||||
--install-dir)
|
-h|--help) head -21 "$0" | tail -16; exit 0 ;;
|
||||||
INSTALL_DIR="$2"
|
*) echo "Unknown option: $1"; exit 1 ;;
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--data-dir)
|
|
||||||
DATA_DIR="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--log-dir)
|
|
||||||
LOG_DIR="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--user)
|
|
||||||
SERVICE_USER="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
-y|--yes)
|
|
||||||
AUTO_YES=true
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-h|--help)
|
|
||||||
head -20 "$0" | tail -15
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unknown option: $1"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -125,42 +101,51 @@ if [[ "$AUTO_YES" != true ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
if [[ "$SKIP_SYSTEMD" != true ]]; then
|
||||||
echo "------------------------------------------------------------"
|
echo ""
|
||||||
echo "STEP 2: Stopping Service"
|
echo "------------------------------------------------------------"
|
||||||
echo "------------------------------------------------------------"
|
echo "STEP 2: Stopping Service"
|
||||||
echo ""
|
echo "------------------------------------------------------------"
|
||||||
if systemctl is-active --quiet myfsio 2>/dev/null; then
|
echo ""
|
||||||
systemctl stop myfsio
|
if systemctl is-active --quiet myfsio 2>/dev/null; then
|
||||||
echo " [OK] Stopped myfsio service"
|
systemctl stop myfsio
|
||||||
else
|
echo " [OK] Stopped myfsio service"
|
||||||
echo " [SKIP] Service not running"
|
else
|
||||||
fi
|
echo " [SKIP] Service not running"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo "STEP 3: Disabling Service"
|
echo "STEP 3: Disabling Service"
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo ""
|
echo ""
|
||||||
if systemctl is-enabled --quiet myfsio 2>/dev/null; then
|
if systemctl is-enabled --quiet myfsio 2>/dev/null; then
|
||||||
systemctl disable myfsio
|
systemctl disable myfsio
|
||||||
echo " [OK] Disabled myfsio service"
|
echo " [OK] Disabled myfsio service"
|
||||||
else
|
else
|
||||||
echo " [SKIP] Service not enabled"
|
echo " [SKIP] Service not enabled"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo "STEP 4: Removing Systemd Service File"
|
echo "STEP 4: Removing Systemd Service File"
|
||||||
echo "------------------------------------------------------------"
|
echo "------------------------------------------------------------"
|
||||||
echo ""
|
echo ""
|
||||||
if [[ -f /etc/systemd/system/myfsio.service ]]; then
|
if [[ -f /etc/systemd/system/myfsio.service ]]; then
|
||||||
rm -f /etc/systemd/system/myfsio.service
|
rm -f /etc/systemd/system/myfsio.service
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
echo " [OK] Removed /etc/systemd/system/myfsio.service"
|
echo " [OK] Removed /etc/systemd/system/myfsio.service"
|
||||||
echo " [OK] Reloaded systemd daemon"
|
echo " [OK] Reloaded systemd daemon"
|
||||||
|
else
|
||||||
|
echo " [SKIP] Service file not found"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo " [SKIP] Service file not found"
|
echo ""
|
||||||
|
echo "------------------------------------------------------------"
|
||||||
|
echo "STEPS 2-4: Skipping Systemd Teardown (--no-systemd)"
|
||||||
|
echo "------------------------------------------------------------"
|
||||||
|
echo ""
|
||||||
|
echo " Stop any running myfsio process manually before continuing."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -235,11 +220,11 @@ if [[ "$KEEP_DATA" == true ]]; then
|
|||||||
echo " - Secret key: $DATA_DIR/.myfsio.sys/config/.secret"
|
echo " - Secret key: $DATA_DIR/.myfsio.sys/config/.secret"
|
||||||
echo " - Encryption keys: $DATA_DIR/.myfsio.sys/keys/ (if encryption was enabled)"
|
echo " - Encryption keys: $DATA_DIR/.myfsio.sys/keys/ (if encryption was enabled)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "NOTE: The IAM config is encrypted and requires the SECRET_KEY to read."
|
echo "NOTE: The IAM config is encrypted at rest and is unlocked by the .secret"
|
||||||
echo " Keep the .secret file intact for reinstallation."
|
echo " file in the data directory. Keep that file intact for reinstallation."
|
||||||
echo ""
|
echo ""
|
||||||
echo "To reinstall MyFSIO with existing data:"
|
echo "To reinstall MyFSIO with existing data:"
|
||||||
echo " ./install.sh --data-dir $DATA_DIR"
|
echo " ./install.sh --binary ./myfsio --data-dir $DATA_DIR"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user