Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit2cd3bb4

fix(casing): lowercase-on-create + case-insensitive passkey login

fix(casing): lowercase-on-create + case-insensitive passkey login

Safe, contained slice of the lowercase work (no data migration; existing
repos stay put and rely on case-insensitive resolution per owner decision):
- passkeys.tsx: passkey login matched a lowercased input against the
  case-sensitive username column, so a mixed-case user ("Ccantynz") could
  never sign in with a passkey. Now lower(username)=lower(input).
- import-helper sanitizeRepoName + api.ts repo-create: normalize new repo
  names to lowercase so "MyRepo"/"myrepo" can't both exist. Stops the
  "Vapron" vs "vapron" confusion from recurring on new repos.

Typecheck clean; suite 3116 pass / 4 pre-existing fails.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ccantynz-alt committed on July 24, 2026Parent: f4a1547
3 files changed+1532cd3bb4badd485dda348f8e2129e962e93163a59
3 changed files+15−3
Modifiedsrc/lib/import-helper.ts+6−1View fileUnifiedSplit
9292 * else with a hyphen so an imported repo is always addressable.
9393 */
9494export function sanitizeRepoName(name: string): string {
95 const cleaned = name.replace(/[^A-Za-z0-9._-]/g, "-").replace(/^-+|-+$/g, "");
95 // Lowercase on create — repo slugs are canonically lowercase to avoid the
96 // "Vapron" vs "vapron" confusion. Imported names are normalized here.
97 const cleaned = name
98 .replace(/[^A-Za-z0-9._-]/g, "-")
99 .replace(/^-+|-+$/g, "")
100 .toLowerCase();
96101 return cleaned || "imported-repo";
97102}
98103
Modifiedsrc/routes/api.ts+4−0View fileUnifiedSplit
3636 if (!/^[a-zA-Z0-9._-]+$/.test(body.name)) {
3737 return c.json({ error: "Invalid repository name" }, 400);
3838 }
39 // Normalize to lowercase — repo slugs are canonically lowercase so
40 // "MyRepo" and "myrepo" can't both exist and cause "Vapron" vs "vapron"
41 // confusion. Applied before the duplicate checks + on-disk init below.
42 body.name = body.name.toLowerCase();
3943
4044 try {
4145 // Find creator (user who is performing the action)
Modifiedsrc/routes/passkeys.tsx+5−2View fileUnifiedSplit
2424
2525import { Hono } from "hono";
2626import { setCookie } from "hono/cookie";
27import { eq } from "drizzle-orm";
27import { eq, sql } from "drizzle-orm";
2828import { db } from "../db";
2929import { users, userPasskeys, sessions } from "../db/schema";
3030import type { AuthEnv } from "../middleware/auth";
11051105 const [u] = await db
11061106 .select({ id: users.id })
11071107 .from(users)
1108 .where(eq(users.username, body.username.trim().toLowerCase()))
1108 // Case-insensitive: a user stored with mixed case (e.g. "Ccantynz")
1109 // must still be found by their passkey login. Comparing a lowercased
1110 // input against the case-sensitive column silently failed before.
1111 .where(sql`lower(${users.username}) = lower(${body.username.trim()})`)
11091112 .limit(1);
11101113 if (u) {
11111114 userId = u.id;
11121115