1use axum::Json;
2use axum::http::{HeaderMap, HeaderValue, header};
3use platform_http::{ApiOpenApiRouter, OpenApiRouter, routes};
4use serde_json::{Value, json};
5use sha2::{Digest as _, Sha256};
6
7pub const AUTH_CONSOLE_ARTIFACT_PATH: &str = "/v1/auth/console/artifact";
8pub const AUTH_CONSOLE_RELEASE_PATH: &str = "/v1/auth/console/release";
9pub const AUTH_CONSOLE_RELEASE_PROTOCOL: &str = "lenso.console-artifact-template.v1";
10
11const AUTH_CONSOLE_ARTIFACT: &[u8] = include_bytes!("../artifacts/auth-console.tgz");
12const AUTH_CONTRACT_DOCUMENT: &str = include_str!("../artifacts/auth-business-api.v1.json");
13
14pub fn router() -> ApiOpenApiRouter {
15 OpenApiRouter::new()
16 .routes(routes!(get_console_artifact))
17 .routes(routes!(get_console_release))
18}
19
20#[utoipa::path(
21 get,
22 path = "/v1/auth/console/artifact",
23 operation_id = "auth_console_artifact",
24 tag = "auth-console",
25 responses((status = 200, description = "Exact Auth Console ESM artifact", content_type = "application/gzip"))
26)]
27async fn get_console_artifact() -> (HeaderMap, &'static [u8]) {
28 let mut headers = HeaderMap::new();
29 headers.insert(
30 header::CONTENT_TYPE,
31 HeaderValue::from_static("application/gzip"),
32 );
33 headers.insert(
34 header::CACHE_CONTROL,
35 HeaderValue::from_static("public, max-age=31536000, immutable"),
36 );
37 (headers, AUTH_CONSOLE_ARTIFACT)
38}
39
40#[utoipa::path(
41 get,
42 path = "/v1/auth/console/release",
43 operation_id = "auth_console_release",
44 tag = "auth-console",
45 responses((status = 200, description = "Auth Module-owned Console artifact release template", body = serde_json::Value, content_type = "application/json"))
46)]
47async fn get_console_release() -> Json<Value> {
48 Json(console_release_template())
49}
50
51#[must_use]
52pub fn console_release_template() -> Value {
53 let manifest = crate::module::manifest();
54 let console_manifest = manifest.console_module_manifest("^2.1.0", "^2.0.0");
55 json!({
56 "protocol": AUTH_CONSOLE_RELEASE_PROTOCOL,
57 "moduleId": manifest.module_id,
58 "version": env!("CARGO_PKG_VERSION"),
59 "manifest": manifest,
60 "artifactPath": AUTH_CONSOLE_ARTIFACT_PATH,
61 "artifact": {
62 "digest": artifact_digest(),
63 "format": "console_ui_esm",
64 "protocolMajor": 1,
65 "entry": "index.js",
66 "entries": [
67 { "name": "module", "path": "index.js" },
68 { "name": "sessions", "path": "index.js?surface=sessions" },
69 { "name": "users", "path": "index.js?surface=users" },
70 { "name": "style", "path": "assets/stylex.css" }
71 ],
72 "styleAssets": [
73 { "path": "assets/stylex.css", "order": 0 }
74 ],
75 "manifest": console_manifest,
76 "requestedPermissions": []
77 },
78 "delivery": {
79 "kind": "linked",
80 "package": "lenso-module-auth",
81 "crateVersion": env!("CARGO_PKG_VERSION"),
82 "defaultFeatures": false,
83 "features": ["redis"],
84 "binding": "auth"
85 },
86 "compatibility": {
87 "lensoRequirement": "^0.3.0",
88 "hostApiRequirement": "^2.1.0",
89 "consoleUiRequirement": "^2.0.0",
90 "rustRequirement": ">=1.94"
91 },
92 "surfaceApi": {
93 "contractDigest": crate::console_api::AUTH_CONTRACT_DIGEST,
94 "operationIds": [
95 crate::console_api::LIST_SESSIONS_OPERATION,
96 crate::console_api::LIST_USERS_OPERATION,
97 crate::console_api::REVOKE_SESSION_OPERATION,
98 crate::console_api::DISABLE_USER_OPERATION,
99 crate::console_api::ENABLE_USER_OPERATION
100 ],
101 "contractArtifact": {
102 "format": "openapi_3_1_json",
103 "document": AUTH_CONTRACT_DOCUMENT
104 }
105 }
106 })
107}
108
109fn artifact_digest() -> String {
110 let hex = Sha256::digest(AUTH_CONSOLE_ARTIFACT)
111 .iter()
112 .map(|byte| format!("{byte:02x}"))
113 .collect::<String>();
114 format!("sha256:{hex}")
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn release_template_binds_the_current_module_and_surface_contract() {
123 let release = console_release_template();
124 assert_eq!(release["moduleId"], "lenso/auth");
125 assert_eq!(release["version"], env!("CARGO_PKG_VERSION"));
126 assert_eq!(
127 release["artifact"]["digest"],
128 "sha256:01d607ec71232d7973cbce6787f4cd713f49eefb18002bf5764d65656708cf89"
129 );
130 assert_eq!(
131 release["surfaceApi"]["contractDigest"],
132 crate::console_api::AUTH_CONTRACT_DIGEST
133 );
134 assert_eq!(
135 release["artifact"]["manifest"],
136 serde_json::to_value(
137 crate::module::manifest().console_module_manifest("^2.1.0", "^2.0.0")
138 )
139 .unwrap()
140 );
141 }
142}