feat: add Keycloak authentication

This commit is contained in:
2026-08-19 12:11:18 +08:00
parent bdd5eff776
commit d08cf2abc1
27 changed files with 533 additions and 128 deletions
+47 -8
View File
@@ -22,14 +22,53 @@ const browserHttpUrl = z
}
});
const runtimeConfigSchema = z.object({
TJWATER_MAPBOX_ACCESS_TOKEN: z.string().default(""),
TJWATER_MAP_URL: browserHttpUrl.default("https://geoserver.waternetwork.cn/geoserver"),
TJWATER_GEOSERVER_WORKSPACE: z.string().trim().min(1).default("tjwater"),
TJWATER_AGENT_API_BASE_URL: browserHttpUrl.default("http://127.0.0.1:8787"),
TJWATER_ENABLE_DEV_PANEL: runtimeBoolean(false),
TJWATER_ENABLE_MSW: runtimeBoolean(false)
});
const keycloakIssuer = z
.string()
.trim()
.default("")
.superRefine((value, context) => {
if (!value) return;
const parsed = browserHttpUrl.safeParse(value);
if (!parsed.success) {
context.addIssue({ code: z.ZodIssueCode.custom, message: "Keycloak issuer 必须是安全的 HTTP(S) 地址" });
return;
}
const url = new URL(value);
if (url.search || !/\/realms\/[^/]+\/?$/.test(url.pathname)) {
context.addIssue({ code: z.ZodIssueCode.custom, message: "Keycloak issuer 必须指向具体 Realm" });
}
})
.transform((value) => value.replace(/\/+$/, ""));
const runtimeConfigSchema = z
.object({
TJWATER_AUTH_MODE: z.enum(["required", "disabled"]).default("required"),
TJWATER_KEYCLOAK_ISSUER: keycloakIssuer,
TJWATER_KEYCLOAK_CLIENT_ID: z.string().trim().default("next-tjwater"),
TJWATER_MAPBOX_ACCESS_TOKEN: z.string().default(""),
TJWATER_MAP_URL: browserHttpUrl.default("https://geoserver.waternetwork.cn/geoserver"),
TJWATER_GEOSERVER_WORKSPACE: z.string().trim().min(1).default("tjwater"),
TJWATER_AGENT_API_BASE_URL: browserHttpUrl.default("http://127.0.0.1:8787"),
TJWATER_ENABLE_DEV_PANEL: runtimeBoolean(false),
TJWATER_ENABLE_MSW: runtimeBoolean(false)
})
.superRefine((config, context) => {
if (config.TJWATER_AUTH_MODE !== "required") return;
if (!config.TJWATER_KEYCLOAK_ISSUER) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ["TJWATER_KEYCLOAK_ISSUER"],
message: "启用认证时必须配置 Keycloak issuer"
});
}
if (!config.TJWATER_KEYCLOAK_CLIENT_ID) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ["TJWATER_KEYCLOAK_CLIENT_ID"],
message: "启用认证时必须配置 Keycloak client ID"
});
}
});
declare global {
var __TJWATER_CONFIG__: unknown;