Fix GitHub/Google OAuth redirect URI configuration (GRO-546) #257

Merged
groombook-engineer[bot] merged 7 commits from fleaflicker/gro-546-fix-oauth-redirect-uri into main 2026-04-11 03:01:13 +00:00
16 changed files with 193 additions and 5 deletions
+4
View File
@@ -171,17 +171,21 @@ export async function initAuth(): Promise<void> {
const hasGoogle = !!(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET);
const hasGitHub = !!(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET);
const callbackBase = `${BETTER_AUTH_URL}/api/auth/callback`;
const socialPlugins = [];
if (hasGoogle) {
socialPlugins.push(google({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
redirectURI: `${callbackBase}/google`,
}));
}
if (hasGitHub) {
socialPlugins.push(github({
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
redirectURI: `${callbackBase}/github`,
}));
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>You have no right to access this object because of bucket acl.</Message>
<RequestId>69D96C853FAECD363909C4A0</RequestId>
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
<EC>0003-00000001</EC>
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
</Error>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>You have no right to access this object because of bucket acl.</Message>
<RequestId>69D96CFC84D7A9333708F278</RequestId>
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
<EC>0003-00000001</EC>
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
</Error>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>You have no right to access this object because of bucket acl.</Message>
<RequestId>69D96D48D7892E37386B9ACB</RequestId>
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
<EC>0003-00000001</EC>
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
</Error>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>You have no right to access this object because of bucket acl.</Message>
<RequestId>69D96C25663D703833F23607</RequestId>
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
<EC>0003-00000001</EC>
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
</Error>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>You have no right to access this object because of bucket acl.</Message>
<RequestId>69D96D89851C843332073968</RequestId>
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
<EC>0003-00000001</EC>
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
</Error>
Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>You have no right to access this object because of bucket acl.</Message>
<RequestId>69D96C9C5A03D33730C61AD8</RequestId>
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
<EC>0003-00000001</EC>
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
</Error>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 269 KiB

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>You have no right to access this object because of bucket acl.</Message>
<RequestId>69D96BEB91911B30317E3BE8</RequestId>
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
<EC>0003-00000001</EC>
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
</Error>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>You have no right to access this object because of bucket acl.</Message>
<RequestId>69D96BFB7B92D33535D6D90D</RequestId>
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
<EC>0003-00000001</EC>
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
</Error>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>You have no right to access this object because of bucket acl.</Message>
<RequestId>69D96B8BDF4B473630A2E120</RequestId>
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
<EC>0003-00000001</EC>
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
</Error>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>You have no right to access this object because of bucket acl.</Message>
<RequestId>69D96D78BFFCAD343037C27C</RequestId>
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
<EC>0003-00000001</EC>
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
</Error>
+1 -1
Submodule infra updated: d43a016f3f...aaedafcb9a
+98 -4
View File
@@ -184,7 +184,7 @@ const dogBreeds = [
"Brittany", "English Springer Spaniel", "Maltese", "Bichon Frise",
"West Highland White Terrier", "Vizsla", "Chihuahua", "Collie",
"Basset Hound", "Newfoundland", "Samoyed", "Australian Shepherd",
"Pembroke Welsh Corgi", "French Bulldog", "Weimaraner",
"Pembroke Welsh Corgi", "French Bulldog", "Weimaraner", "Puggle",
"Mixed Breed", "Mixed Breed", "Mixed Breed",
];
@@ -281,6 +281,44 @@ const productsUsed = [
"Coconut oil shampoo, leave-in conditioner, cologne",
];
const demoPetImages = [
"/demo-pets/dog-golden-after.png",
"/demo-pets/dog-poodle-groomed.png",
"/demo-pets/dog-black-lab.png",
"/demo-pets/dog-shih-tzu.png",
"/demo-pets/dog-cocker-spaniel.png",
"/demo-pets/dog-schnauzer.png",
"/demo-pets/dog-maltese.png",
"/demo-pets/dog-dachshund.png",
"/demo-pets/dog-pomeranian.png",
"/demo-pets/dog-bichon-frise.png",
"/demo-pets/dog-golden-retriever.png",
"/demo-pets/dog-labrador.png",
"/demo-pets/dog-mixed-breed.png",
"/demo-pets/dog-poodle.png",
"/demo-pets/dog-terrier.png",
"/demo-pets/dog-afghan-hound.png",
"/demo-pets/dog-basset-brown-white.png",
"/demo-pets/dog-bichon-white-groomed.png",
"/demo-pets/dog-boxer-fawn-athletic.png",
"/demo-pets/dog-cavalier-cream-gentle.png",
"/demo-pets/dog-cocker-buff-friendly.png",
"/demo-pets/dog-corgi.png",
"/demo-pets/dog-dachshund-black-tan.png",
"/demo-pets/dog-golden-before.png",
"/demo-pets/dog-pomeranian-white-studio.png",
"/demo-pets/dog-schnauzer-black-groomed.png",
"/demo-pets/dog-setter-red-sunlit.png",
"/demo-pets/dog-sheepdog-merle-running.png",
];
const puggleImages = [
"/demo-pets/dog-puggle-fawn-playful.png",
"/demo-pets/dog-puggle-black-sitting.png",
"/demo-pets/dog-puggle-cream-groomed.png",
"/demo-pets/dog-puggle-fawn-grooming.png",
];
// ── Service definitions ──────────────────────────────────────────────────────
// Deterministic service IDs + UNIQUE(name) constraint make seed fully idempotent:
// first run inserts, subsequent runs update existing rows via ON CONFLICT (name).
@@ -368,6 +406,58 @@ async function seedKnownUsers() {
}
}
// ── Staff: UAT Super User (oidcSub from SEED_UAT_SUPER_OIDC_SUB env var) ──
const uatSuperOidcSub = process.env.SEED_UAT_SUPER_OIDC_SUB;
if (uatSuperOidcSub) {
const UAT_SUPER_STAFF_ID = "00000000-0000-0000-0000-000000000003";
const [existingUatSuper] = await db
.select()
.from(schema.staff)
.where(eq(schema.staff.email, "uat-super@groombook.dev"))
.limit(1);
if (existingUatSuper) {
console.log(`✓ Staff 'UAT Super User' already exists — skipping`);
} else {
await db.insert(schema.staff).values({
id: UAT_SUPER_STAFF_ID,
name: "UAT Super User",
email: "uat-super@groombook.dev",
oidcSub: uatSuperOidcSub,
role: "manager",
isSuperUser: true,
active: true,
});
console.log(`✓ Created staff 'UAT Super User' (oidcSub: ${uatSuperOidcSub})`);
}
}
// ── Staff: UAT Staff Groomer (oidcSub from SEED_UAT_STAFF_OIDC_SUB env var) ──
const uatStaffOidcSub = process.env.SEED_UAT_STAFF_OIDC_SUB;
if (uatStaffOidcSub) {
const UAT_STAFF_STAFF_ID = "00000000-0000-0000-0000-000000000004";
const [existingUatStaff] = await db
.select()
.from(schema.staff)
.where(eq(schema.staff.email, "uat-groomer@groombook.dev"))
.limit(1);
if (existingUatStaff) {
console.log(`✓ Staff 'UAT Staff Groomer' already exists — skipping`);
} else {
await db.insert(schema.staff).values({
id: UAT_STAFF_STAFF_ID,
name: "UAT Staff Groomer",
email: "uat-groomer@groombook.dev",
oidcSub: uatStaffOidcSub,
role: "groomer",
isSuperUser: false,
active: true,
});
console.log(`✓ Created staff 'UAT Staff Groomer' (oidcSub: ${uatStaffOidcSub})`);
}
}
// ── Services: idempotent upsert using name as unique key ─────────────────────
// UNIQUE constraint on services.name (migration 0020) must exist first.
// Uses b0000001-... IDs to match main seed servicesDef for same-named services.
@@ -569,6 +659,7 @@ async function seed() {
const clientRecords: ClientRecord[] = [];
const petRecords: PetRecord[] = [];
let petIndex = 0; // Track pet count to assign Puggle images to first 250 pets
const clientBatchSize = 50;
for (let batch = 0; batch < Math.ceil(cfg.clientCount / clientBatchSize); batch++) {
const clientBatch: (typeof schema.clients.$inferInsert)[] = [];
@@ -600,7 +691,7 @@ async function seed() {
const petCount = rand() < 0.5 ? 1 : rand() < 0.7 ? 2 : 3;
for (let p = 0; p < petCount; p++) {
const petId = uuid();
const breed = pick(dogBreeds);
const breed = petIndex < 250 ? "Puggle" : pick(dogBreeds);
const dob = new Date(now);
dob.setFullYear(dob.getFullYear() - randInt(1, 14));
dob.setMonth(randInt(0, 11));
@@ -619,9 +710,11 @@ async function seed() {
shampooPreference: pick(shampoos),
specialCareNotes: rand() < 0.1 ? "Vet clearance required before grooming" : null,
customFields: {},
image: petIndex < 250 ? pick(puggleImages) : pick(demoPetImages),
});
petRecords.push({ id: petId, clientId });
petIndex++;
}
}
@@ -652,6 +745,7 @@ async function seed() {
shampooPreference: pet.shampooPreference,
specialCareNotes: pet.specialCareNotes,
customFields: pet.customFields,
image: pet.image,
},
});
}
@@ -686,8 +780,8 @@ async function seed() {
.values({ id: uc.id, name: uc.name, email: uc.email, phone: uc.phone, address: uc.address })
.onConflictDoUpdate({ target: schema.clients.id, set: { name: uc.name, email: uc.email, phone: uc.phone, address: uc.address } });
await db.insert(schema.pets)
.values({ id: uc.petId, clientId: uc.id, name: uc.petName, species: "Dog", breed: uc.petBreed, weightKg: "25.00", dateOfBirth: new Date("2021-03-15T00:00:00Z") })
.onConflictDoUpdate({ target: schema.pets.id, set: { clientId: uc.id, name: uc.petName, species: "Dog", breed: uc.petBreed, weightKg: "25.00", dateOfBirth: new Date("2021-03-15T00:00:00Z") } });
.values({ id: uc.petId, clientId: uc.id, name: uc.petName, species: "Dog", breed: uc.petBreed, weightKg: "25.00", dateOfBirth: new Date("2021-03-15T00:00:00Z"), image: pick(demoPetImages) })
.onConflictDoUpdate({ target: schema.pets.id, set: { clientId: uc.id, name: uc.petName, species: "Dog", breed: uc.petBreed, weightKg: "25.00", dateOfBirth: new Date("2021-03-15T00:00:00Z"), image: pick(demoPetImages) } });
// Create one completed appointment for this client
const apptId = uuid();
const svcIdx = 0;