diff --git a/hub/admin-web/src/routes/admin/org/[slug]/teams/+page.svelte b/hub/admin-web/src/routes/admin/org/[slug]/teams/+page.svelte
index 96bc22d..3ea7374 100644
--- a/hub/admin-web/src/routes/admin/org/[slug]/teams/+page.svelte
+++ b/hub/admin-web/src/routes/admin/org/[slug]/teams/+page.svelte
@@ -38,12 +38,19 @@
}
}
+ const SLUG_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
+
async function createTeam() {
if (!newSlug.trim() || !newName.trim()) return;
+ const teamSlug = newSlug.trim().toLowerCase();
+ if (!SLUG_RE.test(teamSlug)) {
+ toastError('标识须为小写字母数字,可用连字符连接');
+ return;
+ }
adding = true;
try {
await api.createTeam(slug, {
- slug: newSlug.trim(),
+ slug: teamSlug,
name: newName.trim(),
...(newDesc.trim() ? { description: newDesc.trim() } : {})
});
@@ -134,7 +141,7 @@
新建团队
-
+
diff --git a/hub/src/admin/errors.ts b/hub/src/admin/errors.ts
index 01ecf26..ee9fbb7 100644
--- a/hub/src/admin/errors.ts
+++ b/hub/src/admin/errors.ts
@@ -35,9 +35,11 @@ export async function handleRouteError(reply: FastifyReply, err: unknown): Promi
await sendError(reply, mapped.statusCode, mapped.code, mapped.message);
return;
}
+ reply.log.error({ err }, "unmapped route error");
await sendError(reply, 500, "internal_error", "internal error");
return;
}
+ reply.log.error({ err }, "unmapped route error");
await sendError(reply, 500, "internal_error", "internal error");
}
@@ -71,7 +73,8 @@ function mapDomainError(message: string): { statusCode: number; code: string; me
lower.includes("is required") ||
lower.includes("already") ||
lower.includes("invalid") ||
- lower.includes("accepts only")
+ lower.includes("accepts only") ||
+ lower.includes("must be")
) {
return { statusCode: 400, code: "bad_request", message };
}
diff --git a/hub/src/admin/routes/teamsRoutes.ts b/hub/src/admin/routes/teamsRoutes.ts
index 9a76af1..4d34533 100644
--- a/hub/src/admin/routes/teamsRoutes.ts
+++ b/hub/src/admin/routes/teamsRoutes.ts
@@ -34,7 +34,7 @@ export async function registerTeamsRoutes(
const { orgSlug } = request.params as { orgSlug: string };
const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug });
if (auth === null) return;
- const body = request.body as { slug?: unknown; name?: unknown; description?: unknown };
+ const body = (request.body ?? {}) as { slug?: unknown; name?: unknown; description?: unknown };
if (typeof body.slug !== "string" || typeof body.name !== "string") {
return reply.status(400).send({
error: { code: "bad_request", message: "slug and name are required" },