feat: configurable OAuth (Google + TikTok SSO), project membership, inline file preview
- Auth: configurable OAuthProvider enum supporting Google OAuth and TikTok SSO - Auth: /auth/provider endpoint for frontend to detect active provider - Auth: user role system (admin via ADMIN_USERS env var sees all projects) - Projects: project_members many-to-many table with role (owner/member) - Projects: membership-based access control, auto-add creator as owner - Projects: member management API (list/add/remove) - Files: remove Content-Disposition attachment header, let browser decide - Health: public /tori/api/health endpoint for k8s probes
This commit is contained in:
50
src/main.rs
50
src/main.rs
@@ -123,25 +123,41 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
let obj_root = std::env::var("OBJ_ROOT").unwrap_or_else(|_| "/data/obj".to_string());
|
||||
|
||||
let auth_config = match (
|
||||
std::env::var("GOOGLE_CLIENT_ID"),
|
||||
std::env::var("GOOGLE_CLIENT_SECRET"),
|
||||
) {
|
||||
(Ok(client_id), Ok(client_secret)) => {
|
||||
let jwt_secret = std::env::var("JWT_SECRET")
|
||||
.unwrap_or_else(|_| uuid::Uuid::new_v4().to_string());
|
||||
let public_url = std::env::var("PUBLIC_URL")
|
||||
.unwrap_or_else(|_| "https://tori.euphon.cloud".to_string());
|
||||
tracing::info!("Google OAuth enabled (public_url={})", public_url);
|
||||
let auth_config = {
|
||||
let jwt_secret = std::env::var("JWT_SECRET")
|
||||
.unwrap_or_else(|_| uuid::Uuid::new_v4().to_string());
|
||||
let public_url = std::env::var("PUBLIC_URL")
|
||||
.unwrap_or_else(|_| "https://tori.euphon.cloud".to_string());
|
||||
|
||||
// Try TikTok SSO first, then Google OAuth
|
||||
if let (Ok(id), Ok(secret)) = (
|
||||
std::env::var("SSO_CLIENT_ID"),
|
||||
std::env::var("SSO_CLIENT_SECRET"),
|
||||
) {
|
||||
tracing::info!("TikTok SSO enabled (public_url={})", public_url);
|
||||
Some(api::auth::AuthConfig {
|
||||
google_client_id: client_id,
|
||||
google_client_secret: client_secret,
|
||||
provider: api::auth::OAuthProvider::TikTokSso {
|
||||
client_id: id,
|
||||
client_secret: secret,
|
||||
},
|
||||
jwt_secret,
|
||||
public_url,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
tracing::warn!("GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET not set, auth disabled");
|
||||
} else if let (Ok(id), Ok(secret)) = (
|
||||
std::env::var("GOOGLE_CLIENT_ID"),
|
||||
std::env::var("GOOGLE_CLIENT_SECRET"),
|
||||
) {
|
||||
tracing::info!("Google OAuth enabled (public_url={})", public_url);
|
||||
Some(api::auth::AuthConfig {
|
||||
provider: api::auth::OAuthProvider::Google {
|
||||
client_id: id,
|
||||
client_secret: secret,
|
||||
},
|
||||
jwt_secret,
|
||||
public_url,
|
||||
})
|
||||
} else {
|
||||
tracing::warn!("No OAuth configured (set SSO_CLIENT_ID/SSO_CLIENT_SECRET or GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET)");
|
||||
None
|
||||
}
|
||||
};
|
||||
@@ -156,6 +172,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
});
|
||||
|
||||
let app = Router::new()
|
||||
// Health check (public, for k8s probes)
|
||||
.route("/tori/api/health", axum::routing::get(|| async {
|
||||
axum::Json(serde_json::json!({"status": "ok"}))
|
||||
}))
|
||||
// Auth routes are public
|
||||
.nest("/tori/api/auth", api::auth::router(state.clone()))
|
||||
// Protected API routes
|
||||
|
||||
Reference in New Issue
Block a user