mirror of
https://github.com/SrIzan10/helium.git
synced 2026-06-06 00:56:58 +00:00
refactor: move stuff to presetsDb and start work on sharing
This commit is contained in:
@@ -1,16 +1,10 @@
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<form
|
||||
:id="formId"
|
||||
@submit.prevent="form.handleSubmit"
|
||||
class="space-y-6"
|
||||
>
|
||||
<form :id="formId" @submit.prevent="form.handleSubmit" class="space-y-6">
|
||||
<FieldGroup>
|
||||
<form.Field v-slot="{ field }" name="name">
|
||||
<Field :data-invalid="isInvalid(field)">
|
||||
<FieldLabel :for="`${formId}-name`">
|
||||
Preset name
|
||||
</FieldLabel>
|
||||
<FieldLabel :for="`${formId}-name`"> Preset name </FieldLabel>
|
||||
<Input
|
||||
:id="`${formId}-name`"
|
||||
:name="field.name"
|
||||
@@ -101,10 +95,12 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'success'): void
|
||||
(e: "success"): void;
|
||||
}>();
|
||||
|
||||
const formId = computed(() => props.isEdit ? `form-edit-preset-${props.presetId}` : 'form-new-preset');
|
||||
const formId = computed(() =>
|
||||
props.isEdit ? `form-edit-preset-${props.presetId}` : "form-new-preset",
|
||||
);
|
||||
|
||||
const editorOptions = {
|
||||
automaticLayout: true,
|
||||
@@ -140,25 +136,32 @@ const form = useForm({
|
||||
...value,
|
||||
iceServers: JSON.parse(value.iceServers),
|
||||
};
|
||||
|
||||
|
||||
let url = "/api/presets/create";
|
||||
let method = "POST";
|
||||
|
||||
|
||||
if (props.isEdit && props.presetId) {
|
||||
url = `/api/presets/${props.presetId}`;
|
||||
method = "PUT";
|
||||
}
|
||||
|
||||
const request = await $fetch(url, {
|
||||
method: method as any,
|
||||
body: JSON.stringify(parsedValue),
|
||||
});
|
||||
|
||||
if (request.success) {
|
||||
toast.success(props.isEdit ? "Preset updated successfully!" : "Preset created successfully!");
|
||||
emit('success');
|
||||
} else {
|
||||
toast.error(props.isEdit ? "Failed to update preset." : "Failed to create preset.");
|
||||
try {
|
||||
const request = await $fetch<{ success: boolean; message: string }>(url, {
|
||||
method: method as any,
|
||||
body: JSON.stringify(parsedValue),
|
||||
});
|
||||
if (request.success) {
|
||||
toast.success(
|
||||
props.isEdit
|
||||
? "Preset updated successfully!"
|
||||
: "Preset created successfully!",
|
||||
);
|
||||
emit("success");
|
||||
}
|
||||
} catch (e) {
|
||||
toast.error(
|
||||
props.isEdit ? "Failed to update preset." : "Failed to create preset.",
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import { db } from "~/lib/db/index";
|
||||
import * as schema from "~/lib/db/schema";
|
||||
|
||||
@@ -10,3 +10,117 @@ export async function getUserPresets(clerkUserId: string) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getPresetById(presetId: string) {
|
||||
return await db.query.presets.findFirst({
|
||||
where: eq(schema.presets.id, presetId),
|
||||
});
|
||||
}
|
||||
|
||||
export async function userHasPresetAccess(
|
||||
presetId: string,
|
||||
userId: string,
|
||||
): Promise<boolean> {
|
||||
const preset = await getPresetById(presetId);
|
||||
if (!preset) return false;
|
||||
|
||||
if (preset.createdBy === userId) return true;
|
||||
|
||||
const userPreset = await db.query.presetUsers.findFirst({
|
||||
where: and(
|
||||
eq(schema.presetUsers.presetId, presetId),
|
||||
eq(schema.presetUsers.userId, userId),
|
||||
),
|
||||
});
|
||||
|
||||
return !!userPreset;
|
||||
}
|
||||
|
||||
export async function createPreset(
|
||||
userId: string,
|
||||
name: string,
|
||||
iceServers: string,
|
||||
isDefault: boolean = false,
|
||||
) {
|
||||
const presetCreate = await db
|
||||
.insert(schema.presets)
|
||||
.values({
|
||||
createdBy: userId,
|
||||
name: name,
|
||||
iceServers: iceServers,
|
||||
})
|
||||
.returning({ insertedId: schema.presets.id });
|
||||
|
||||
const insertedId = presetCreate[0]?.insertedId;
|
||||
if (!insertedId) {
|
||||
throw new Error("Failed to get inserted preset ID");
|
||||
}
|
||||
|
||||
await db.insert(schema.presetUsers).values({
|
||||
presetId: insertedId,
|
||||
userId: userId,
|
||||
isDefault: isDefault,
|
||||
});
|
||||
|
||||
return insertedId;
|
||||
}
|
||||
|
||||
export async function updatePreset(
|
||||
presetId: string,
|
||||
name: string,
|
||||
iceServers: string,
|
||||
) {
|
||||
await db
|
||||
.update(schema.presets)
|
||||
.set({
|
||||
name: name,
|
||||
iceServers: iceServers,
|
||||
})
|
||||
.where(eq(schema.presets.id, presetId));
|
||||
}
|
||||
|
||||
export async function setPresetAsDefault(presetId: string, userId: string) {
|
||||
await db
|
||||
.update(schema.presetUsers)
|
||||
.set({ isDefault: false })
|
||||
.where(eq(schema.presetUsers.userId, userId));
|
||||
|
||||
// set as default
|
||||
await db
|
||||
.update(schema.presetUsers)
|
||||
.set({ isDefault: true })
|
||||
.where(
|
||||
and(
|
||||
eq(schema.presetUsers.presetId, presetId),
|
||||
eq(schema.presetUsers.userId, userId),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export async function unsetPresetAsDefault(presetId: string, userId: string) {
|
||||
await db
|
||||
.update(schema.presetUsers)
|
||||
.set({ isDefault: false })
|
||||
.where(
|
||||
and(
|
||||
eq(schema.presetUsers.presetId, presetId),
|
||||
eq(schema.presetUsers.userId, userId),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export async function updatePresetDefaultStatus(
|
||||
presetId: string,
|
||||
userId: string,
|
||||
isDefault: boolean,
|
||||
) {
|
||||
if (isDefault) {
|
||||
await setPresetAsDefault(presetId, userId);
|
||||
} else {
|
||||
await unsetPresetAsDefault(presetId, userId);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deletePreset(presetId: string) {
|
||||
await db.delete(schema.presets).where(eq(schema.presets.id, presetId));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 px-4">
|
||||
<div v-for="presetUser in data!.data" :key="presetUser.preset.id">
|
||||
<Card class="flex flex-col h-full">
|
||||
<CardHeader>
|
||||
@@ -124,9 +124,22 @@ async function deletePreset(id: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleShare(preset: any) {
|
||||
// To be implemented by user
|
||||
console.log("Share preset:", preset.id);
|
||||
async function handleShare(preset: any) {
|
||||
if (!confirm("Do you want to share this preset?")) return;
|
||||
try {
|
||||
const response = await $fetch(`/api/presets/${preset.id}/share`, {
|
||||
method: "POST",
|
||||
});
|
||||
if (!response.success || !response.shareId) {
|
||||
toast.error("Failed to generate shareable link");
|
||||
return;
|
||||
}
|
||||
const shareableLink = `${window.location.origin}/presets/shared/${response.shareId}`;
|
||||
navigator.clipboard.writeText(shareableLink);
|
||||
toast.success("Link copied to clipboard");
|
||||
} catch (error) {
|
||||
toast.error("Failed to share preset");
|
||||
}
|
||||
}
|
||||
|
||||
// below types are ai generated
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import { db } from "~/lib/db";
|
||||
import { presets, presetUsers } from "~/lib/db/schema";
|
||||
import { getPresetById, deletePreset } from "~/lib/utils/presetsDb";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { isAuthenticated, userId } = event.context.auth();
|
||||
|
||||
|
||||
if (!isAuthenticated || !userId) {
|
||||
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
||||
}
|
||||
@@ -15,20 +13,21 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
|
||||
// Check if the user is the creator of the preset
|
||||
const preset = await db.query.presets.findFirst({
|
||||
where: eq(presets.id, id),
|
||||
});
|
||||
const preset = await getPresetById(id);
|
||||
|
||||
if (!preset) {
|
||||
throw createError({ statusCode: 404, statusMessage: "Preset not found" });
|
||||
}
|
||||
|
||||
if (preset.createdBy !== userId) {
|
||||
throw createError({ statusCode: 403, statusMessage: "Forbidden: You can only delete your own presets" });
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
statusMessage: "Forbidden: You can only delete your own presets",
|
||||
});
|
||||
}
|
||||
|
||||
// Delete the preset (cascades to presetUsers)
|
||||
await db.delete(presets).where(eq(presets.id, id));
|
||||
await deletePreset(id);
|
||||
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "~/lib/db";
|
||||
import { presets, presetUsers } from "~/lib/db/schema";
|
||||
import { getPresetById, userHasPresetAccess } from "~/lib/utils/presetsDb";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { isAuthenticated, userId } = event.context.auth();
|
||||
|
||||
|
||||
if (!isAuthenticated || !userId) {
|
||||
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
||||
}
|
||||
@@ -15,20 +13,15 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
|
||||
// Fetch the preset
|
||||
const preset = await db.query.presets.findFirst({
|
||||
where: eq(presets.id, id),
|
||||
});
|
||||
const preset = await getPresetById(id);
|
||||
|
||||
if (!preset) {
|
||||
throw createError({ statusCode: 404, statusMessage: "Preset not found" });
|
||||
}
|
||||
|
||||
// Check if user has access (either creator or has it in their presetUsers)
|
||||
const userPreset = await db.query.presetUsers.findFirst({
|
||||
where: eq(presetUsers.presetId, id),
|
||||
});
|
||||
|
||||
if (preset.createdBy !== userId && (!userPreset || userPreset.userId !== userId)) {
|
||||
// Check if user has access
|
||||
const hasAccess = await userHasPresetAccess(id, userId);
|
||||
if (!hasAccess) {
|
||||
throw createError({ statusCode: 403, statusMessage: "Forbidden" });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import { db } from "~/lib/db";
|
||||
import { presets, presetUsers } from "~/lib/db/schema";
|
||||
import {
|
||||
getPresetById,
|
||||
updatePreset,
|
||||
updatePresetDefaultStatus,
|
||||
} from "~/lib/utils/presetsDb";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { isAuthenticated, userId } = event.context.auth();
|
||||
|
||||
|
||||
if (!isAuthenticated || !userId) {
|
||||
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
||||
}
|
||||
@@ -15,44 +17,27 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
|
||||
const body = await readBody(event);
|
||||
|
||||
|
||||
// Verify ownership
|
||||
const preset = await db.query.presets.findFirst({
|
||||
where: eq(presets.id, id),
|
||||
});
|
||||
const preset = await getPresetById(id);
|
||||
|
||||
if (!preset) {
|
||||
throw createError({ statusCode: 404, statusMessage: "Preset not found" });
|
||||
}
|
||||
|
||||
if (preset.createdBy !== userId) {
|
||||
throw createError({ statusCode: 403, statusMessage: "Forbidden: You can only edit your own presets" });
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
statusMessage: "Forbidden: You can only edit your own presets",
|
||||
});
|
||||
}
|
||||
|
||||
// Update preset
|
||||
await db.update(presets)
|
||||
.set({
|
||||
name: body.name,
|
||||
iceServers: JSON.stringify(body.iceServers),
|
||||
})
|
||||
.where(eq(presets.id, id));
|
||||
await updatePreset(id, body.name, JSON.stringify(body.iceServers));
|
||||
|
||||
// Update default status in presetUsers
|
||||
if (body.default !== undefined) {
|
||||
// If setting as default, first unset all other defaults for this user
|
||||
if (body.default) {
|
||||
await db.update(presetUsers)
|
||||
.set({ isDefault: false })
|
||||
.where(eq(presetUsers.userId, userId));
|
||||
}
|
||||
|
||||
// Update the default status for this preset
|
||||
await db.update(presetUsers)
|
||||
.set({ isDefault: body.default })
|
||||
.where(and(
|
||||
eq(presetUsers.presetId, id),
|
||||
eq(presetUsers.userId, userId)
|
||||
));
|
||||
await updatePresetDefaultStatus(id, userId, body.default);
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
|
||||
0
server/api/presets/[id]/share.post.ts
Normal file
0
server/api/presets/[id]/share.post.ts
Normal file
@@ -1,65 +1,51 @@
|
||||
import { clerkClient } from "@clerk/nuxt/server";
|
||||
import { db } from "~/lib/db";
|
||||
import * as schema from "~/lib/db/schema";
|
||||
import { schema as zodSchema } from "~/lib/schema/new-preset";
|
||||
import { createPreset } from "~/lib/utils/presetsDb";
|
||||
|
||||
export default defineEventHandler(async (req) => {
|
||||
const reqBody = await readBody(req);
|
||||
if (reqBody.iceServers) {
|
||||
if (reqBody && reqBody.iceServers) {
|
||||
reqBody.iceServers = JSON.stringify(reqBody.iceServers);
|
||||
}
|
||||
|
||||
const body = zodSchema.safeParse(reqBody);
|
||||
if (body.success === false) {
|
||||
console.log(body.error, JSON.stringify(reqBody));
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Invalid request body",
|
||||
});
|
||||
setResponseStatus(req, 400);
|
||||
return {
|
||||
success: false,
|
||||
message: "Invalid request body",
|
||||
};
|
||||
}
|
||||
|
||||
const { isAuthenticated, userId } = req.context.auth();
|
||||
|
||||
if (!isAuthenticated) {
|
||||
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
||||
setResponseStatus(req, 401);
|
||||
return {
|
||||
success: false,
|
||||
message: "Unauthorized",
|
||||
};
|
||||
}
|
||||
|
||||
const user = await clerkClient(req).users.getUser(userId);
|
||||
|
||||
try {
|
||||
const presetCreate = await db
|
||||
.insert(schema.presets)
|
||||
.values({
|
||||
createdBy: user.id,
|
||||
name: body.data.name,
|
||||
iceServers: body.data.iceServers,
|
||||
})
|
||||
.returning({ insertedId: schema.presets.id });
|
||||
await db.insert(schema.presetUsers).values({
|
||||
presetId: presetCreate[0].insertedId,
|
||||
userId: user.id,
|
||||
isDefault: body.data.default,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Error creating preset:", e);
|
||||
const error = e as PostgresError;
|
||||
if (error.code === "23505") {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "A preset with this name already exists",
|
||||
});
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: "Failed to create preset",
|
||||
});
|
||||
await createPreset(
|
||||
user.id,
|
||||
body.data.name,
|
||||
body.data.iceServers,
|
||||
body.data.default,
|
||||
);
|
||||
} catch (e: any) {
|
||||
setResponseStatus(req, 500);
|
||||
return {
|
||||
success: false,
|
||||
message: "Database error",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Preset created successfully",
|
||||
};
|
||||
});
|
||||
interface PostgresError extends Error {
|
||||
code: string;
|
||||
detail?: string;
|
||||
constraint?: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user