262 lines
7.3 KiB
TypeScript
262 lines
7.3 KiB
TypeScript
|
import { db } from '@/lib/db';
|
||
|
import { scriptCollections, collectionScripts, scripts } from '@/lib/db/schema';
|
||
|
import { eq, and, desc } from 'drizzle-orm';
|
||
|
import { generateId, ApiError } from './index';
|
||
|
|
||
|
export interface CreateCollectionData {
|
||
|
name: string;
|
||
|
description?: string;
|
||
|
authorId: string;
|
||
|
isPublic?: boolean;
|
||
|
}
|
||
|
|
||
|
export interface UpdateCollectionData {
|
||
|
name?: string;
|
||
|
description?: string;
|
||
|
isPublic?: boolean;
|
||
|
}
|
||
|
|
||
|
// Create a new collection
|
||
|
export async function createCollection(data: CreateCollectionData) {
|
||
|
try {
|
||
|
const collectionId = generateId();
|
||
|
const now = new Date();
|
||
|
|
||
|
const [collection] = await db.insert(scriptCollections).values({
|
||
|
id: collectionId,
|
||
|
name: data.name,
|
||
|
description: data.description,
|
||
|
authorId: data.authorId,
|
||
|
isPublic: data.isPublic ?? true,
|
||
|
createdAt: now,
|
||
|
updatedAt: now,
|
||
|
}).returning();
|
||
|
|
||
|
return collection;
|
||
|
} catch (error) {
|
||
|
throw new ApiError(`Failed to create collection: ${error}`, 500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get collection by ID
|
||
|
export async function getCollectionById(id: string) {
|
||
|
try {
|
||
|
const collection = await db.query.scriptCollections.findFirst({
|
||
|
where: eq(scriptCollections.id, id),
|
||
|
with: {
|
||
|
author: {
|
||
|
columns: {
|
||
|
id: true,
|
||
|
username: true,
|
||
|
displayName: true,
|
||
|
avatarUrl: true,
|
||
|
},
|
||
|
},
|
||
|
scripts: {
|
||
|
with: {
|
||
|
script: {
|
||
|
with: {
|
||
|
author: {
|
||
|
columns: {
|
||
|
id: true,
|
||
|
username: true,
|
||
|
displayName: true,
|
||
|
avatarUrl: true,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
orderBy: desc(collectionScripts.addedAt),
|
||
|
},
|
||
|
},
|
||
|
});
|
||
|
|
||
|
if (!collection) {
|
||
|
throw new ApiError('Collection not found', 404);
|
||
|
}
|
||
|
|
||
|
return collection;
|
||
|
} catch (error) {
|
||
|
if (error instanceof ApiError) throw error;
|
||
|
throw new ApiError(`Failed to get collection: ${error}`, 500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get collections by user
|
||
|
export async function getUserCollections(userId: string) {
|
||
|
try {
|
||
|
const collections = await db.query.scriptCollections.findMany({
|
||
|
where: eq(scriptCollections.authorId, userId),
|
||
|
with: {
|
||
|
scripts: {
|
||
|
with: {
|
||
|
script: true,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
orderBy: desc(scriptCollections.createdAt),
|
||
|
});
|
||
|
|
||
|
return collections;
|
||
|
} catch (error) {
|
||
|
throw new ApiError(`Failed to get user collections: ${error}`, 500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get public collections
|
||
|
export async function getPublicCollections(limit: number = 20, offset: number = 0) {
|
||
|
try {
|
||
|
const collections = await db.query.scriptCollections.findMany({
|
||
|
where: eq(scriptCollections.isPublic, true),
|
||
|
with: {
|
||
|
author: {
|
||
|
columns: {
|
||
|
id: true,
|
||
|
username: true,
|
||
|
displayName: true,
|
||
|
avatarUrl: true,
|
||
|
},
|
||
|
},
|
||
|
scripts: {
|
||
|
with: {
|
||
|
script: true,
|
||
|
},
|
||
|
limit: 5, // Preview of scripts in collection
|
||
|
},
|
||
|
},
|
||
|
orderBy: desc(scriptCollections.createdAt),
|
||
|
limit,
|
||
|
offset,
|
||
|
});
|
||
|
|
||
|
return collections;
|
||
|
} catch (error) {
|
||
|
throw new ApiError(`Failed to get public collections: ${error}`, 500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update collection
|
||
|
export async function updateCollection(id: string, data: UpdateCollectionData, userId: string) {
|
||
|
try {
|
||
|
// Check if user owns the collection
|
||
|
const collection = await getCollectionById(id);
|
||
|
if (collection.authorId !== userId) {
|
||
|
throw new ApiError('Unauthorized to update this collection', 403);
|
||
|
}
|
||
|
|
||
|
const updateData = {
|
||
|
...data,
|
||
|
updatedAt: new Date(),
|
||
|
};
|
||
|
|
||
|
const [updatedCollection] = await db
|
||
|
.update(scriptCollections)
|
||
|
.set(updateData)
|
||
|
.where(eq(scriptCollections.id, id))
|
||
|
.returning();
|
||
|
|
||
|
return updatedCollection;
|
||
|
} catch (error) {
|
||
|
if (error instanceof ApiError) throw error;
|
||
|
throw new ApiError(`Failed to update collection: ${error}`, 500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Delete collection
|
||
|
export async function deleteCollection(id: string, userId: string) {
|
||
|
try {
|
||
|
const collection = await getCollectionById(id);
|
||
|
if (collection.authorId !== userId) {
|
||
|
throw new ApiError('Unauthorized to delete this collection', 403);
|
||
|
}
|
||
|
|
||
|
// Delete all scripts in collection first
|
||
|
await db.delete(collectionScripts).where(eq(collectionScripts.collectionId, id));
|
||
|
|
||
|
// Delete the collection
|
||
|
await db.delete(scriptCollections).where(eq(scriptCollections.id, id));
|
||
|
|
||
|
return { success: true };
|
||
|
} catch (error) {
|
||
|
if (error instanceof ApiError) throw error;
|
||
|
throw new ApiError(`Failed to delete collection: ${error}`, 500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add script to collection
|
||
|
export async function addScriptToCollection(collectionId: string, scriptId: string, userId: string) {
|
||
|
try {
|
||
|
// Check if user owns the collection
|
||
|
const collection = await getCollectionById(collectionId);
|
||
|
if (collection.authorId !== userId) {
|
||
|
throw new ApiError('Unauthorized to modify this collection', 403);
|
||
|
}
|
||
|
|
||
|
// Check if script is already in collection
|
||
|
const existing = await db.query.collectionScripts.findFirst({
|
||
|
where: and(
|
||
|
eq(collectionScripts.collectionId, collectionId),
|
||
|
eq(collectionScripts.scriptId, scriptId)
|
||
|
),
|
||
|
});
|
||
|
|
||
|
if (existing) {
|
||
|
throw new ApiError('Script is already in this collection', 400);
|
||
|
}
|
||
|
|
||
|
const [collectionScript] = await db.insert(collectionScripts).values({
|
||
|
id: generateId(),
|
||
|
collectionId,
|
||
|
scriptId,
|
||
|
addedAt: new Date(),
|
||
|
}).returning();
|
||
|
|
||
|
return collectionScript;
|
||
|
} catch (error) {
|
||
|
if (error instanceof ApiError) throw error;
|
||
|
throw new ApiError(`Failed to add script to collection: ${error}`, 500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Remove script from collection
|
||
|
export async function removeScriptFromCollection(collectionId: string, scriptId: string, userId: string) {
|
||
|
try {
|
||
|
// Check if user owns the collection
|
||
|
const collection = await getCollectionById(collectionId);
|
||
|
if (collection.authorId !== userId) {
|
||
|
throw new ApiError('Unauthorized to modify this collection', 403);
|
||
|
}
|
||
|
|
||
|
await db
|
||
|
.delete(collectionScripts)
|
||
|
.where(
|
||
|
and(
|
||
|
eq(collectionScripts.collectionId, collectionId),
|
||
|
eq(collectionScripts.scriptId, scriptId)
|
||
|
)
|
||
|
);
|
||
|
|
||
|
return { success: true };
|
||
|
} catch (error) {
|
||
|
if (error instanceof ApiError) throw error;
|
||
|
throw new ApiError(`Failed to remove script from collection: ${error}`, 500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check if script is in collection
|
||
|
export async function isScriptInCollection(collectionId: string, scriptId: string) {
|
||
|
try {
|
||
|
const collectionScript = await db.query.collectionScripts.findFirst({
|
||
|
where: and(
|
||
|
eq(collectionScripts.collectionId, collectionId),
|
||
|
eq(collectionScripts.scriptId, scriptId)
|
||
|
),
|
||
|
});
|
||
|
|
||
|
return !!collectionScript;
|
||
|
} catch (error) {
|
||
|
throw new ApiError(`Failed to check if script is in collection: ${error}`, 500);
|
||
|
}
|
||
|
}
|