first commit
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
import type { FastifyPluginAsync } from "fastify";
|
||||
import { z } from "zod";
|
||||
import { requireAuth, requireRestaurantMember } from "../lib/auth.js";
|
||||
import { generateUniqueRestaurantSlug } from "../lib/slug.js";
|
||||
import { supabase } from "../lib/supabase.js";
|
||||
|
||||
const createRestaurantSchema = z.object({
|
||||
name: z.string().min(1).max(120),
|
||||
logoUrl: z.string().nullish(),
|
||||
phone: z.string().max(30).nullish(),
|
||||
address: z.string().max(500).nullish(),
|
||||
});
|
||||
|
||||
const updateRestaurantSchema = createRestaurantSchema.partial();
|
||||
|
||||
export const restaurantsRoutes: FastifyPluginAsync = async (app) => {
|
||||
app.post("/restaurants", async (req, reply) => {
|
||||
const userId = await requireAuth(req, reply);
|
||||
if (!userId || !supabase) return;
|
||||
|
||||
const parsed = createRestaurantSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
return reply.code(400).send({ message: "invalid body", issues: parsed.error.issues });
|
||||
}
|
||||
|
||||
const { name, logoUrl, phone, address } = parsed.data;
|
||||
const slug = await generateUniqueRestaurantSlug(name);
|
||||
|
||||
const { data: restaurant, error: restaurantError } = await supabase
|
||||
.from("restaurants")
|
||||
.insert({
|
||||
name,
|
||||
slug,
|
||||
logo_url: logoUrl ?? null,
|
||||
phone: phone ?? null,
|
||||
address: address ?? null,
|
||||
created_by: userId,
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (restaurantError || !restaurant) {
|
||||
req.log.error(restaurantError);
|
||||
return reply.code(500).send({ message: "failed to create restaurant" });
|
||||
}
|
||||
|
||||
const { error: memberError } = await supabase
|
||||
.from("restaurant_members")
|
||||
.insert({ restaurant_id: restaurant.id, user_id: userId, role: "owner" });
|
||||
|
||||
const { data: location, error: locationError } = await supabase
|
||||
.from("locations")
|
||||
.insert({ restaurant_id: restaurant.id, name: "Ana Şube" })
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (memberError || locationError || !location) {
|
||||
req.log.error(memberError ?? locationError);
|
||||
return reply.code(500).send({ message: "failed to finish restaurant setup" });
|
||||
}
|
||||
|
||||
const { data: menu, error: menuError } = await supabase
|
||||
.from("menus")
|
||||
.insert({ location_id: location.id, name: "Menüm" })
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (menuError || !menu) {
|
||||
req.log.error(menuError);
|
||||
return reply.code(500).send({ message: "failed to create default menu" });
|
||||
}
|
||||
|
||||
return reply.code(201).send({ restaurant, location, menu });
|
||||
});
|
||||
|
||||
app.get("/restaurants/:id", async (req, reply) => {
|
||||
const userId = await requireAuth(req, reply);
|
||||
if (!userId || !supabase) return;
|
||||
|
||||
const { id } = req.params as { id: string };
|
||||
const isMember = await requireRestaurantMember(userId, id);
|
||||
if (!isMember) return reply.code(403).send({ message: "forbidden" });
|
||||
|
||||
const { data, error } = await supabase.from("restaurants").select().eq("id", id).single();
|
||||
if (error || !data) return reply.code(404).send({ message: "not found" });
|
||||
|
||||
return reply.send(data);
|
||||
});
|
||||
|
||||
app.patch("/restaurants/:id", async (req, reply) => {
|
||||
const userId = await requireAuth(req, reply);
|
||||
if (!userId || !supabase) return;
|
||||
|
||||
const { id } = req.params as { id: string };
|
||||
const isMember = await requireRestaurantMember(userId, id);
|
||||
if (!isMember) return reply.code(403).send({ message: "forbidden" });
|
||||
|
||||
const parsed = updateRestaurantSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
return reply.code(400).send({ message: "invalid body", issues: parsed.error.issues });
|
||||
}
|
||||
|
||||
const { name, logoUrl, phone, address } = parsed.data;
|
||||
const { data, error } = await supabase
|
||||
.from("restaurants")
|
||||
.update({
|
||||
...(name !== undefined ? { name } : {}),
|
||||
...(logoUrl !== undefined ? { logo_url: logoUrl } : {}),
|
||||
...(phone !== undefined ? { phone } : {}),
|
||||
...(address !== undefined ? { address } : {}),
|
||||
updated_at: new Date().toISOString(),
|
||||
})
|
||||
.eq("id", id)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error || !data) {
|
||||
req.log.error(error);
|
||||
return reply.code(500).send({ message: "failed to update restaurant" });
|
||||
}
|
||||
|
||||
return reply.send(data);
|
||||
});
|
||||
|
||||
app.get("/restaurants/:id/theme", async (req, reply) => {
|
||||
const userId = await requireAuth(req, reply);
|
||||
if (!userId || !supabase) return;
|
||||
|
||||
const { id } = req.params as { id: string };
|
||||
const isMember = await requireRestaurantMember(userId, id);
|
||||
if (!isMember) return reply.code(403).send({ message: "forbidden" });
|
||||
|
||||
const { data: themeRow } = await supabase
|
||||
.from("restaurant_themes")
|
||||
.select("theme_id, overrides, themes(key, name, config)")
|
||||
.eq("restaurant_id", id)
|
||||
.maybeSingle();
|
||||
|
||||
return reply.send({
|
||||
themeKey: (themeRow?.themes as unknown as { key?: string } | null)?.key ?? "elegant",
|
||||
overrides: themeRow?.overrides ?? {},
|
||||
});
|
||||
});
|
||||
|
||||
app.put("/restaurants/:id/theme", async (req, reply) => {
|
||||
const userId = await requireAuth(req, reply);
|
||||
if (!userId || !supabase) return;
|
||||
|
||||
const { id } = req.params as { id: string };
|
||||
const isMember = await requireRestaurantMember(userId, id);
|
||||
if (!isMember) return reply.code(403).send({ message: "forbidden" });
|
||||
|
||||
const { themeKey } = (req.body as { themeKey?: string }) ?? {};
|
||||
if (!themeKey) {
|
||||
return reply.code(400).send({ message: "themeKey is required" });
|
||||
}
|
||||
|
||||
const { data: themeData } = await supabase
|
||||
.from("themes")
|
||||
.select("id")
|
||||
.eq("key", themeKey)
|
||||
.maybeSingle();
|
||||
|
||||
if (!themeData) {
|
||||
return reply.code(404).send({ message: "Theme not found" });
|
||||
}
|
||||
|
||||
const { error } = await supabase.from("restaurant_themes").upsert(
|
||||
{
|
||||
restaurant_id: id,
|
||||
theme_id: themeData.id,
|
||||
},
|
||||
{ onConflict: "restaurant_id" },
|
||||
);
|
||||
|
||||
if (error) {
|
||||
req.log.error(error);
|
||||
return reply.code(500).send({ message: "failed to update theme" });
|
||||
}
|
||||
|
||||
return reply.send({ success: true, themeKey });
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user