Refactor API files to provide mock implementations for analytics, auth, collections, ratings, scripts, and users, streamlining the codebase for frontend-only functionality. Remove legacy database schema and browser compatibility files to enhance clarity and maintainability.
This commit is contained in:
274
temp_api_backup/api/analytics.ts
Normal file
274
temp_api_backup/api/analytics.ts
Normal file
@ -0,0 +1,274 @@
|
||||
import { db } from '@/lib/db';
|
||||
import { scriptAnalytics, scripts } from '@/lib/db/schema';
|
||||
import { eq, and, gte, lte, desc, count, sql } from 'drizzle-orm';
|
||||
import { generateId, ApiError } from './index';
|
||||
|
||||
export interface TrackEventData {
|
||||
scriptId: string;
|
||||
eventType: 'view' | 'download' | 'share';
|
||||
userId?: string;
|
||||
userAgent?: string;
|
||||
ipAddress?: string;
|
||||
referrer?: string;
|
||||
}
|
||||
|
||||
export interface AnalyticsFilters {
|
||||
scriptId?: string;
|
||||
eventType?: string;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
// Track an analytics event
|
||||
export async function trackEvent(data: TrackEventData) {
|
||||
try {
|
||||
await db.insert(scriptAnalytics).values({
|
||||
id: generateId(),
|
||||
scriptId: data.scriptId,
|
||||
eventType: data.eventType,
|
||||
userId: data.userId,
|
||||
userAgent: data.userAgent,
|
||||
ipAddress: data.ipAddress,
|
||||
referrer: data.referrer,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
// Update script counters based on event type
|
||||
if (data.eventType === 'view') {
|
||||
await db
|
||||
.update(scripts)
|
||||
.set({
|
||||
viewCount: sql`${scripts.viewCount} + 1`,
|
||||
})
|
||||
.where(eq(scripts.id, data.scriptId));
|
||||
} else if (data.eventType === 'download') {
|
||||
await db
|
||||
.update(scripts)
|
||||
.set({
|
||||
downloadCount: sql`${scripts.downloadCount} + 1`,
|
||||
})
|
||||
.where(eq(scripts.id, data.scriptId));
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to track event: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get analytics events with filters
|
||||
export async function getAnalyticsEvents(filters: AnalyticsFilters = {}) {
|
||||
try {
|
||||
let query = db.select().from(scriptAnalytics);
|
||||
let conditions: any[] = [];
|
||||
|
||||
if (filters.scriptId) {
|
||||
conditions.push(eq(scriptAnalytics.scriptId, filters.scriptId));
|
||||
}
|
||||
|
||||
if (filters.eventType) {
|
||||
conditions.push(eq(scriptAnalytics.eventType, filters.eventType));
|
||||
}
|
||||
|
||||
if (filters.userId) {
|
||||
conditions.push(eq(scriptAnalytics.userId, filters.userId));
|
||||
}
|
||||
|
||||
if (filters.startDate) {
|
||||
conditions.push(gte(scriptAnalytics.createdAt, filters.startDate));
|
||||
}
|
||||
|
||||
if (filters.endDate) {
|
||||
conditions.push(lte(scriptAnalytics.createdAt, filters.endDate));
|
||||
}
|
||||
|
||||
if (conditions.length > 0) {
|
||||
query = query.where(and(...conditions)) as any;
|
||||
}
|
||||
|
||||
const events = await query.orderBy(desc(scriptAnalytics.createdAt));
|
||||
return events;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get analytics events: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get analytics summary for a script
|
||||
export async function getScriptAnalytics(scriptId: string, days: number = 30) {
|
||||
try {
|
||||
const startDate = new Date();
|
||||
startDate.setDate(startDate.getDate() - days);
|
||||
|
||||
// Get event counts by type
|
||||
const eventCounts = await db
|
||||
.select({
|
||||
eventType: scriptAnalytics.eventType,
|
||||
count: count(scriptAnalytics.id),
|
||||
})
|
||||
.from(scriptAnalytics)
|
||||
.where(
|
||||
and(
|
||||
eq(scriptAnalytics.scriptId, scriptId),
|
||||
gte(scriptAnalytics.createdAt, startDate)
|
||||
)
|
||||
)
|
||||
.groupBy(scriptAnalytics.eventType);
|
||||
|
||||
// Get daily activity
|
||||
const dailyActivity = await db
|
||||
.select({
|
||||
date: sql<string>`DATE(${scriptAnalytics.createdAt})`,
|
||||
eventType: scriptAnalytics.eventType,
|
||||
count: count(scriptAnalytics.id),
|
||||
})
|
||||
.from(scriptAnalytics)
|
||||
.where(
|
||||
and(
|
||||
eq(scriptAnalytics.scriptId, scriptId),
|
||||
gte(scriptAnalytics.createdAt, startDate)
|
||||
)
|
||||
)
|
||||
.groupBy(sql`DATE(${scriptAnalytics.createdAt})`, scriptAnalytics.eventType);
|
||||
|
||||
// Get referrer statistics
|
||||
const referrers = await db
|
||||
.select({
|
||||
referrer: scriptAnalytics.referrer,
|
||||
count: count(scriptAnalytics.id),
|
||||
})
|
||||
.from(scriptAnalytics)
|
||||
.where(
|
||||
and(
|
||||
eq(scriptAnalytics.scriptId, scriptId),
|
||||
gte(scriptAnalytics.createdAt, startDate)
|
||||
)
|
||||
)
|
||||
.groupBy(scriptAnalytics.referrer)
|
||||
.orderBy(desc(count(scriptAnalytics.id)))
|
||||
.limit(10);
|
||||
|
||||
return {
|
||||
eventCounts,
|
||||
dailyActivity,
|
||||
referrers,
|
||||
periodDays: days,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get script analytics: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get platform-wide analytics (admin only)
|
||||
export async function getPlatformAnalytics(days: number = 30) {
|
||||
try {
|
||||
const startDate = new Date();
|
||||
startDate.setDate(startDate.getDate() - days);
|
||||
|
||||
// Total scripts and activity
|
||||
const [totals] = await db
|
||||
.select({
|
||||
totalScripts: count(scripts.id),
|
||||
approvedScripts: sql<number>`SUM(CASE WHEN ${scripts.isApproved} = 1 THEN 1 ELSE 0 END)`,
|
||||
pendingScripts: sql<number>`SUM(CASE WHEN ${scripts.isApproved} = 0 THEN 1 ELSE 0 END)`,
|
||||
})
|
||||
.from(scripts);
|
||||
|
||||
// Activity by event type
|
||||
const activityByType = await db
|
||||
.select({
|
||||
eventType: scriptAnalytics.eventType,
|
||||
count: count(scriptAnalytics.id),
|
||||
})
|
||||
.from(scriptAnalytics)
|
||||
.where(gte(scriptAnalytics.createdAt, startDate))
|
||||
.groupBy(scriptAnalytics.eventType);
|
||||
|
||||
// Most popular scripts
|
||||
const popularScripts = await db
|
||||
.select({
|
||||
scriptId: scriptAnalytics.scriptId,
|
||||
scriptName: scripts.name,
|
||||
views: count(scriptAnalytics.id),
|
||||
})
|
||||
.from(scriptAnalytics)
|
||||
.innerJoin(scripts, eq(scriptAnalytics.scriptId, scripts.id))
|
||||
.where(
|
||||
and(
|
||||
eq(scriptAnalytics.eventType, 'view'),
|
||||
gte(scriptAnalytics.createdAt, startDate)
|
||||
)
|
||||
)
|
||||
.groupBy(scriptAnalytics.scriptId, scripts.name)
|
||||
.orderBy(desc(count(scriptAnalytics.id)))
|
||||
.limit(10);
|
||||
|
||||
// Daily activity trends
|
||||
const dailyTrends = await db
|
||||
.select({
|
||||
date: sql<string>`DATE(${scriptAnalytics.createdAt})`,
|
||||
views: sql<number>`SUM(CASE WHEN ${scriptAnalytics.eventType} = 'view' THEN 1 ELSE 0 END)`,
|
||||
downloads: sql<number>`SUM(CASE WHEN ${scriptAnalytics.eventType} = 'download' THEN 1 ELSE 0 END)`,
|
||||
})
|
||||
.from(scriptAnalytics)
|
||||
.where(gte(scriptAnalytics.createdAt, startDate))
|
||||
.groupBy(sql`DATE(${scriptAnalytics.createdAt})`)
|
||||
.orderBy(sql`DATE(${scriptAnalytics.createdAt})`);
|
||||
|
||||
return {
|
||||
totals,
|
||||
activityByType,
|
||||
popularScripts,
|
||||
dailyTrends,
|
||||
periodDays: days,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get platform analytics: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get user analytics
|
||||
export async function getUserAnalytics(userId: string, days: number = 30) {
|
||||
try {
|
||||
const startDate = new Date();
|
||||
startDate.setDate(startDate.getDate() - days);
|
||||
|
||||
// User's scripts performance
|
||||
const userScriptsAnalytics = await db
|
||||
.select({
|
||||
scriptId: scripts.id,
|
||||
scriptName: scripts.name,
|
||||
views: scripts.viewCount,
|
||||
downloads: scripts.downloadCount,
|
||||
rating: scripts.rating,
|
||||
ratingCount: scripts.ratingCount,
|
||||
})
|
||||
.from(scripts)
|
||||
.where(eq(scripts.authorId, userId))
|
||||
.orderBy(desc(scripts.viewCount));
|
||||
|
||||
// Recent activity on user's scripts
|
||||
const recentActivity = await db
|
||||
.select({
|
||||
eventType: scriptAnalytics.eventType,
|
||||
count: count(scriptAnalytics.id),
|
||||
})
|
||||
.from(scriptAnalytics)
|
||||
.innerJoin(scripts, eq(scriptAnalytics.scriptId, scripts.id))
|
||||
.where(
|
||||
and(
|
||||
eq(scripts.authorId, userId),
|
||||
gte(scriptAnalytics.createdAt, startDate)
|
||||
)
|
||||
)
|
||||
.groupBy(scriptAnalytics.eventType);
|
||||
|
||||
return {
|
||||
userScripts: userScriptsAnalytics,
|
||||
recentActivity,
|
||||
periodDays: days,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get user analytics: ${error}`, 500);
|
||||
}
|
||||
}
|
217
temp_api_backup/api/auth.ts
Normal file
217
temp_api_backup/api/auth.ts
Normal file
@ -0,0 +1,217 @@
|
||||
import bcrypt from 'bcrypt';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { getUserByEmail, getUserByUsername, createUser } from './users';
|
||||
import { ApiError } from './index';
|
||||
|
||||
export interface LoginCredentials {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface RegisterData {
|
||||
email: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface AuthToken {
|
||||
token: string;
|
||||
user: {
|
||||
id: string;
|
||||
email: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
isAdmin: boolean;
|
||||
isModerator: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'default-secret-key';
|
||||
const SALT_ROUNDS = 12;
|
||||
|
||||
// Hash password
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
try {
|
||||
return await bcrypt.hash(password, SALT_ROUNDS);
|
||||
} catch (error) {
|
||||
throw new ApiError('Failed to hash password', 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify password
|
||||
export async function verifyPassword(password: string, hashedPassword: string): Promise<boolean> {
|
||||
try {
|
||||
return await bcrypt.compare(password, hashedPassword);
|
||||
} catch (error) {
|
||||
throw new ApiError('Failed to verify password', 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
export function generateToken(user: any): string {
|
||||
const payload = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
username: user.username,
|
||||
displayName: user.displayName,
|
||||
isAdmin: user.isAdmin,
|
||||
isModerator: user.isModerator,
|
||||
};
|
||||
|
||||
return jwt.sign(payload, JWT_SECRET, { expiresIn: '7d' });
|
||||
}
|
||||
|
||||
// Verify JWT token
|
||||
export function verifyToken(token: string): any {
|
||||
try {
|
||||
return jwt.verify(token, JWT_SECRET);
|
||||
} catch (error) {
|
||||
throw new ApiError('Invalid or expired token', 401);
|
||||
}
|
||||
}
|
||||
|
||||
// Login user
|
||||
export async function login(credentials: LoginCredentials): Promise<AuthToken> {
|
||||
try {
|
||||
const user = await getUserByEmail(credentials.email);
|
||||
if (!user) {
|
||||
throw new ApiError('Invalid email or password', 401);
|
||||
}
|
||||
|
||||
// Note: In a real implementation, you would verify the password against a hash
|
||||
// For this demo, we'll assume password verification passes
|
||||
// const isValidPassword = await verifyPassword(credentials.password, user.passwordHash);
|
||||
// if (!isValidPassword) {
|
||||
// throw new ApiError('Invalid email or password', 401);
|
||||
// }
|
||||
|
||||
const token = generateToken(user);
|
||||
|
||||
return {
|
||||
token,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
username: user.username,
|
||||
displayName: user.displayName,
|
||||
isAdmin: user.isAdmin || false,
|
||||
isModerator: user.isModerator || false,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) throw error;
|
||||
throw new ApiError('Login failed', 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Register user
|
||||
export async function register(data: RegisterData): Promise<AuthToken> {
|
||||
try {
|
||||
// Check if email already exists
|
||||
const existingEmail = await getUserByEmail(data.email);
|
||||
if (existingEmail) {
|
||||
throw new ApiError('Email already registered', 400);
|
||||
}
|
||||
|
||||
// Check if username already exists
|
||||
const existingUsername = await getUserByUsername(data.username);
|
||||
if (existingUsername) {
|
||||
throw new ApiError('Username already taken', 400);
|
||||
}
|
||||
|
||||
// Validate email format
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(data.email)) {
|
||||
throw new ApiError('Invalid email format', 400);
|
||||
}
|
||||
|
||||
// Validate username format
|
||||
const usernameRegex = /^[a-zA-Z0-9_]{3,20}$/;
|
||||
if (!usernameRegex.test(data.username)) {
|
||||
throw new ApiError('Username must be 3-20 characters and contain only letters, numbers, and underscores', 400);
|
||||
}
|
||||
|
||||
// Validate password strength
|
||||
if (data.password.length < 6) {
|
||||
throw new ApiError('Password must be at least 6 characters long', 400);
|
||||
}
|
||||
|
||||
// Hash password and create user
|
||||
// const passwordHash = await hashPassword(data.password);
|
||||
|
||||
const user = await createUser({
|
||||
email: data.email,
|
||||
username: data.username,
|
||||
displayName: data.displayName,
|
||||
// passwordHash, // In a real implementation
|
||||
});
|
||||
|
||||
const token = generateToken(user);
|
||||
|
||||
return {
|
||||
token,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
username: user.username,
|
||||
displayName: user.displayName,
|
||||
isAdmin: user.isAdmin || false,
|
||||
isModerator: user.isModerator || false,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) throw error;
|
||||
throw new ApiError('Registration failed', 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh token
|
||||
export async function refreshToken(token: string): Promise<AuthToken> {
|
||||
try {
|
||||
const decoded = verifyToken(token);
|
||||
const user = await getUserByEmail(decoded.email);
|
||||
|
||||
if (!user) {
|
||||
throw new ApiError('User not found', 404);
|
||||
}
|
||||
|
||||
const newToken = generateToken(user);
|
||||
|
||||
return {
|
||||
token: newToken,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
username: user.username,
|
||||
displayName: user.displayName,
|
||||
isAdmin: user.isAdmin || false,
|
||||
isModerator: user.isModerator || false,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) throw error;
|
||||
throw new ApiError('Token refresh failed', 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Change password
|
||||
export async function changePassword(_userId: string, _currentPassword: string, newPassword: string): Promise<boolean> {
|
||||
try {
|
||||
// In a real implementation, you would:
|
||||
// 1. Get user by ID
|
||||
// 2. Verify current password
|
||||
// 3. Hash new password
|
||||
// 4. Update user record
|
||||
|
||||
if (newPassword.length < 6) {
|
||||
throw new ApiError('New password must be at least 6 characters long', 400);
|
||||
}
|
||||
|
||||
// Placeholder for password change logic
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) throw error;
|
||||
throw new ApiError('Password change failed', 500);
|
||||
}
|
||||
}
|
274
temp_api_backup/api/collections.ts
Normal file
274
temp_api_backup/api/collections.ts
Normal file
@ -0,0 +1,274 @@
|
||||
import { db } from '@/lib/db';
|
||||
import { scriptCollections, collectionScripts } 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();
|
||||
|
||||
await db.insert(scriptCollections).values({
|
||||
id: collectionId,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
authorId: data.authorId,
|
||||
isPublic: data.isPublic ?? true,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
|
||||
const collection = {
|
||||
id: collectionId,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
authorId: data.authorId,
|
||||
isPublic: data.isPublic ?? true,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
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(),
|
||||
};
|
||||
|
||||
await db
|
||||
.update(scriptCollections)
|
||||
.set(updateData)
|
||||
.where(eq(scriptCollections.id, id));
|
||||
|
||||
const updatedCollection = { ...collection, ...updateData };
|
||||
|
||||
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 collectionScriptData = {
|
||||
id: generateId(),
|
||||
collectionId,
|
||||
scriptId,
|
||||
addedAt: new Date(),
|
||||
};
|
||||
|
||||
await db.insert(collectionScripts).values(collectionScriptData);
|
||||
|
||||
return collectionScriptData;
|
||||
} 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);
|
||||
}
|
||||
}
|
20
temp_api_backup/api/index.ts
Normal file
20
temp_api_backup/api/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
// Generate unique IDs
|
||||
export const generateId = () => nanoid();
|
||||
|
||||
// Error handling
|
||||
export class ApiError extends Error {
|
||||
constructor(message: string, public status: number = 500) {
|
||||
super(message);
|
||||
this.name = 'ApiError';
|
||||
}
|
||||
}
|
||||
|
||||
// Export all service modules
|
||||
export * from './scripts';
|
||||
export * from './users';
|
||||
export * from './ratings';
|
||||
export * from './analytics';
|
||||
export * from './collections';
|
||||
export * from './auth';
|
45
temp_api_backup/api/mock.ts
Normal file
45
temp_api_backup/api/mock.ts
Normal file
@ -0,0 +1,45 @@
|
||||
// Mock API implementations for demo purposes
|
||||
// In a real app, these would be actual database operations
|
||||
|
||||
import { generateId } from './index';
|
||||
|
||||
// For demo purposes, we'll use these mock functions instead of real database calls
|
||||
// This avoids the MySQL-specific .returning() issues and provides working functionality
|
||||
|
||||
export const mockApiResponses = {
|
||||
createScript: (data: any) => ({
|
||||
id: generateId(),
|
||||
...data,
|
||||
isApproved: false,
|
||||
isPublic: true,
|
||||
viewCount: 0,
|
||||
downloadCount: 0,
|
||||
rating: 0,
|
||||
ratingCount: 0,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}),
|
||||
|
||||
createUser: (data: any) => ({
|
||||
id: generateId(),
|
||||
...data,
|
||||
isAdmin: false,
|
||||
isModerator: false,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}),
|
||||
|
||||
createRating: (data: any) => ({
|
||||
id: generateId(),
|
||||
...data,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}),
|
||||
|
||||
createCollection: (data: any) => ({
|
||||
id: generateId(),
|
||||
...data,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}),
|
||||
};
|
191
temp_api_backup/api/ratings.ts
Normal file
191
temp_api_backup/api/ratings.ts
Normal file
@ -0,0 +1,191 @@
|
||||
import { db } from '@/lib/db';
|
||||
import { ratings, scripts } from '@/lib/db/schema';
|
||||
import { eq, and, avg, count } from 'drizzle-orm';
|
||||
import { generateId, ApiError } from './index';
|
||||
|
||||
export interface CreateRatingData {
|
||||
scriptId: string;
|
||||
userId: string;
|
||||
rating: number; // 1-5 stars
|
||||
}
|
||||
|
||||
// Create or update a rating
|
||||
export async function rateScript(data: CreateRatingData) {
|
||||
try {
|
||||
if (data.rating < 1 || data.rating > 5) {
|
||||
throw new ApiError('Rating must be between 1 and 5', 400);
|
||||
}
|
||||
|
||||
// Check if user already rated this script
|
||||
const existingRating = await db.query.ratings.findFirst({
|
||||
where: and(
|
||||
eq(ratings.scriptId, data.scriptId),
|
||||
eq(ratings.userId, data.userId)
|
||||
),
|
||||
});
|
||||
|
||||
let ratingRecord;
|
||||
if (existingRating) {
|
||||
// Update existing rating
|
||||
await db
|
||||
.update(ratings)
|
||||
.set({
|
||||
rating: data.rating,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(ratings.id, existingRating.id));
|
||||
|
||||
ratingRecord = {
|
||||
...existingRating,
|
||||
rating: data.rating,
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
} else {
|
||||
// Create new rating
|
||||
ratingRecord = {
|
||||
id: generateId(),
|
||||
scriptId: data.scriptId,
|
||||
userId: data.userId,
|
||||
rating: data.rating,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
await db.insert(ratings).values(ratingRecord);
|
||||
}
|
||||
|
||||
// Update script's average rating and count
|
||||
await updateScriptRating(data.scriptId);
|
||||
|
||||
return ratingRecord;
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) throw error;
|
||||
throw new ApiError(`Failed to rate script: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get user's rating for a script
|
||||
export async function getUserRating(scriptId: string, userId: string) {
|
||||
try {
|
||||
const userRating = await db.query.ratings.findFirst({
|
||||
where: and(
|
||||
eq(ratings.scriptId, scriptId),
|
||||
eq(ratings.userId, userId)
|
||||
),
|
||||
});
|
||||
|
||||
return userRating;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get user rating: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get all ratings for a script
|
||||
export async function getScriptRatings(scriptId: string) {
|
||||
try {
|
||||
const scriptRatings = await db.query.ratings.findMany({
|
||||
where: eq(ratings.scriptId, scriptId),
|
||||
with: {
|
||||
user: {
|
||||
columns: {
|
||||
id: true,
|
||||
username: true,
|
||||
displayName: true,
|
||||
avatarUrl: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return scriptRatings;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get script ratings: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Update script's average rating and count
|
||||
async function updateScriptRating(scriptId: string) {
|
||||
try {
|
||||
const [stats] = await db
|
||||
.select({
|
||||
avgRating: avg(ratings.rating),
|
||||
ratingCount: count(ratings.id),
|
||||
})
|
||||
.from(ratings)
|
||||
.where(eq(ratings.scriptId, scriptId));
|
||||
|
||||
const avgRating = stats.avgRating ? Math.round(Number(stats.avgRating) * 10) / 10 : 0;
|
||||
const ratingCount = stats.ratingCount || 0;
|
||||
|
||||
await db
|
||||
.update(scripts)
|
||||
.set({
|
||||
rating: avgRating,
|
||||
ratingCount: ratingCount,
|
||||
})
|
||||
.where(eq(scripts.id, scriptId));
|
||||
|
||||
return { avgRating, ratingCount };
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to update script rating: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete a rating
|
||||
export async function deleteRating(scriptId: string, userId: string) {
|
||||
try {
|
||||
await db
|
||||
.delete(ratings)
|
||||
.where(
|
||||
and(
|
||||
eq(ratings.scriptId, scriptId),
|
||||
eq(ratings.userId, userId)
|
||||
)
|
||||
);
|
||||
|
||||
// Update script's average rating and count
|
||||
await updateScriptRating(scriptId);
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to delete rating: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get rating statistics for a script
|
||||
export async function getScriptRatingStats(scriptId: string) {
|
||||
try {
|
||||
const stats = await db
|
||||
.select({
|
||||
rating: ratings.rating,
|
||||
count: count(ratings.id),
|
||||
})
|
||||
.from(ratings)
|
||||
.where(eq(ratings.scriptId, scriptId))
|
||||
.groupBy(ratings.rating);
|
||||
|
||||
const distribution = [1, 2, 3, 4, 5].map(star => {
|
||||
const found = stats.find(s => s.rating === star);
|
||||
return {
|
||||
stars: star,
|
||||
count: found ? found.count : 0,
|
||||
};
|
||||
});
|
||||
|
||||
const [totals] = await db
|
||||
.select({
|
||||
avgRating: avg(ratings.rating),
|
||||
totalRatings: count(ratings.id),
|
||||
})
|
||||
.from(ratings)
|
||||
.where(eq(ratings.scriptId, scriptId));
|
||||
|
||||
return {
|
||||
averageRating: totals.avgRating ? Math.round(Number(totals.avgRating) * 10) / 10 : 0,
|
||||
totalRatings: totals.totalRatings || 0,
|
||||
distribution,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get rating stats: ${error}`, 500);
|
||||
}
|
||||
}
|
367
temp_api_backup/api/scripts.ts
Normal file
367
temp_api_backup/api/scripts.ts
Normal file
@ -0,0 +1,367 @@
|
||||
import { db } from '@/lib/db';
|
||||
import { scripts, scriptVersions, ratings } from '@/lib/db/schema';
|
||||
import { eq, desc, asc, and, or, like, count, sql } from 'drizzle-orm';
|
||||
import { generateId, ApiError } from './index';
|
||||
|
||||
export interface CreateScriptData {
|
||||
name: string;
|
||||
description: string;
|
||||
content: string;
|
||||
compatibleOs: string[];
|
||||
categories: string[];
|
||||
tags?: string[];
|
||||
gitRepositoryUrl?: string;
|
||||
authorId: string;
|
||||
authorName: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface UpdateScriptData {
|
||||
name?: string;
|
||||
description?: string;
|
||||
content?: string;
|
||||
compatibleOs?: string[];
|
||||
categories?: string[];
|
||||
tags?: string[];
|
||||
gitRepositoryUrl?: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface ScriptFilters {
|
||||
categories?: string[];
|
||||
compatibleOs?: string[];
|
||||
search?: string;
|
||||
authorId?: string;
|
||||
isApproved?: boolean;
|
||||
sortBy?: 'newest' | 'oldest' | 'popular' | 'rating';
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
// Create a new script
|
||||
export async function createScript(data: CreateScriptData) {
|
||||
try {
|
||||
const scriptId = generateId();
|
||||
const now = new Date();
|
||||
|
||||
await db.insert(scripts).values({
|
||||
id: scriptId,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
content: data.content,
|
||||
compatibleOs: data.compatibleOs,
|
||||
categories: data.categories,
|
||||
tags: data.tags || [],
|
||||
gitRepositoryUrl: data.gitRepositoryUrl,
|
||||
authorId: data.authorId,
|
||||
authorName: data.authorName,
|
||||
version: data.version || '1.0.0',
|
||||
isApproved: false,
|
||||
isPublic: true,
|
||||
viewCount: 0,
|
||||
downloadCount: 0,
|
||||
rating: 0,
|
||||
ratingCount: 0,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
|
||||
const script = {
|
||||
id: scriptId,
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
content: data.content,
|
||||
compatibleOs: data.compatibleOs,
|
||||
categories: data.categories,
|
||||
tags: data.tags || [],
|
||||
gitRepositoryUrl: data.gitRepositoryUrl,
|
||||
authorId: data.authorId,
|
||||
authorName: data.authorName,
|
||||
version: data.version || '1.0.0',
|
||||
isApproved: false,
|
||||
isPublic: true,
|
||||
viewCount: 0,
|
||||
downloadCount: 0,
|
||||
rating: 0,
|
||||
ratingCount: 0,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
// Create initial version
|
||||
await db.insert(scriptVersions).values({
|
||||
id: generateId(),
|
||||
scriptId: scriptId,
|
||||
version: data.version || '1.0.0',
|
||||
content: data.content,
|
||||
changelog: 'Initial version',
|
||||
createdAt: now,
|
||||
createdBy: data.authorId,
|
||||
});
|
||||
|
||||
return script;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to create script: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get script by ID
|
||||
export async function getScriptById(id: string) {
|
||||
try {
|
||||
const script = await db.query.scripts.findFirst({
|
||||
where: eq(scripts.id, id),
|
||||
with: {
|
||||
author: true,
|
||||
versions: {
|
||||
orderBy: desc(scriptVersions.createdAt),
|
||||
},
|
||||
ratings: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!script) {
|
||||
throw new ApiError('Script not found', 404);
|
||||
}
|
||||
|
||||
return script;
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) throw error;
|
||||
throw new ApiError(`Failed to get script: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get scripts with filters
|
||||
export async function getScripts(filters: ScriptFilters = {}) {
|
||||
try {
|
||||
const {
|
||||
categories,
|
||||
compatibleOs,
|
||||
search,
|
||||
authorId,
|
||||
isApproved = true,
|
||||
sortBy = 'newest',
|
||||
limit = 20,
|
||||
offset = 0,
|
||||
} = filters;
|
||||
|
||||
let query = db.select().from(scripts);
|
||||
let conditions: any[] = [];
|
||||
|
||||
// Apply filters
|
||||
if (isApproved !== undefined) {
|
||||
conditions.push(eq(scripts.isApproved, isApproved));
|
||||
}
|
||||
|
||||
if (authorId) {
|
||||
conditions.push(eq(scripts.authorId, authorId));
|
||||
}
|
||||
|
||||
if (search) {
|
||||
conditions.push(
|
||||
or(
|
||||
like(scripts.name, `%${search}%`),
|
||||
like(scripts.description, `%${search}%`)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (categories && categories.length > 0) {
|
||||
conditions.push(
|
||||
sql`JSON_OVERLAPS(${scripts.categories}, ${JSON.stringify(categories)})`
|
||||
);
|
||||
}
|
||||
|
||||
if (compatibleOs && compatibleOs.length > 0) {
|
||||
conditions.push(
|
||||
sql`JSON_OVERLAPS(${scripts.compatibleOs}, ${JSON.stringify(compatibleOs)})`
|
||||
);
|
||||
}
|
||||
|
||||
if (conditions.length > 0) {
|
||||
query = query.where(and(...conditions)) as any;
|
||||
}
|
||||
|
||||
// Apply sorting
|
||||
switch (sortBy) {
|
||||
case 'newest':
|
||||
query = query.orderBy(desc(scripts.createdAt)) as any;
|
||||
break;
|
||||
case 'oldest':
|
||||
query = query.orderBy(asc(scripts.createdAt)) as any;
|
||||
break;
|
||||
case 'popular':
|
||||
query = query.orderBy(desc(scripts.viewCount)) as any;
|
||||
break;
|
||||
case 'rating':
|
||||
query = query.orderBy(desc(scripts.rating)) as any;
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply pagination
|
||||
query = query.limit(limit).offset(offset) as any;
|
||||
|
||||
const results = await query;
|
||||
|
||||
// Get total count for pagination
|
||||
const [{ total }] = await db
|
||||
.select({ total: count() })
|
||||
.from(scripts)
|
||||
.where(conditions.length > 0 ? and(...conditions) : undefined);
|
||||
|
||||
return {
|
||||
scripts: results,
|
||||
total,
|
||||
hasMore: offset + limit < total,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get scripts: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Update script
|
||||
export async function updateScript(id: string, data: UpdateScriptData, userId: string) {
|
||||
try {
|
||||
// Check if user owns the script or is admin
|
||||
const script = await getScriptById(id);
|
||||
if (script.authorId !== userId) {
|
||||
throw new ApiError('Unauthorized to update this script', 403);
|
||||
}
|
||||
|
||||
const updateData = {
|
||||
...data,
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
await db
|
||||
.update(scripts)
|
||||
.set(updateData)
|
||||
.where(eq(scripts.id, id));
|
||||
|
||||
const updatedScript = { ...script, ...updateData };
|
||||
|
||||
// If content changed, create new version
|
||||
if (data.content && data.version) {
|
||||
await db.insert(scriptVersions).values({
|
||||
id: generateId(),
|
||||
scriptId: id,
|
||||
version: data.version,
|
||||
content: data.content,
|
||||
changelog: 'Updated script content',
|
||||
createdAt: new Date(),
|
||||
createdBy: userId,
|
||||
});
|
||||
}
|
||||
|
||||
return updatedScript;
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) throw error;
|
||||
throw new ApiError(`Failed to update script: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete script
|
||||
export async function deleteScript(id: string, userId: string) {
|
||||
try {
|
||||
const script = await getScriptById(id);
|
||||
if (script.authorId !== userId) {
|
||||
throw new ApiError('Unauthorized to delete this script', 403);
|
||||
}
|
||||
|
||||
// Delete all related data
|
||||
await db.delete(scriptVersions).where(eq(scriptVersions.scriptId, id));
|
||||
await db.delete(ratings).where(eq(ratings.scriptId, id));
|
||||
await db.delete(scripts).where(eq(scripts.id, id));
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) throw error;
|
||||
throw new ApiError(`Failed to delete script: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Approve/reject script (admin only)
|
||||
export async function moderateScript(id: string, isApproved: boolean, _moderatorId: string) {
|
||||
try {
|
||||
const script = await getScriptById(id);
|
||||
if (!script) {
|
||||
throw new ApiError('Script not found', 404);
|
||||
}
|
||||
|
||||
await db
|
||||
.update(scripts)
|
||||
.set({
|
||||
isApproved,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(scripts.id, id));
|
||||
|
||||
const moderatedScript = { ...script, isApproved, updatedAt: new Date() };
|
||||
return moderatedScript;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to moderate script: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Increment view count
|
||||
export async function incrementViewCount(id: string) {
|
||||
try {
|
||||
await db
|
||||
.update(scripts)
|
||||
.set({
|
||||
viewCount: sql`${scripts.viewCount} + 1`,
|
||||
})
|
||||
.where(eq(scripts.id, id));
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to increment view count: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Increment download count
|
||||
export async function incrementDownloadCount(id: string) {
|
||||
try {
|
||||
await db
|
||||
.update(scripts)
|
||||
.set({
|
||||
downloadCount: sql`${scripts.downloadCount} + 1`,
|
||||
})
|
||||
.where(eq(scripts.id, id));
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to increment download count: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get popular scripts
|
||||
export async function getPopularScripts(limit: number = 10) {
|
||||
try {
|
||||
const popularScripts = await db
|
||||
.select()
|
||||
.from(scripts)
|
||||
.where(eq(scripts.isApproved, true))
|
||||
.orderBy(desc(scripts.viewCount))
|
||||
.limit(limit);
|
||||
|
||||
return popularScripts;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get popular scripts: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get recent scripts
|
||||
export async function getRecentScripts(limit: number = 10) {
|
||||
try {
|
||||
const recentScripts = await db
|
||||
.select()
|
||||
.from(scripts)
|
||||
.where(eq(scripts.isApproved, true))
|
||||
.orderBy(desc(scripts.createdAt))
|
||||
.limit(limit);
|
||||
|
||||
return recentScripts;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get recent scripts: ${error}`, 500);
|
||||
}
|
||||
}
|
174
temp_api_backup/api/users.ts
Normal file
174
temp_api_backup/api/users.ts
Normal file
@ -0,0 +1,174 @@
|
||||
import { db } from '@/lib/db';
|
||||
import { users } from '@/lib/db/schema';
|
||||
import { eq, like } from 'drizzle-orm';
|
||||
import { generateId, ApiError } from './index';
|
||||
|
||||
export interface CreateUserData {
|
||||
email: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
avatarUrl?: string;
|
||||
bio?: string;
|
||||
}
|
||||
|
||||
export interface UpdateUserData {
|
||||
username?: string;
|
||||
displayName?: string;
|
||||
avatarUrl?: string;
|
||||
bio?: string;
|
||||
}
|
||||
|
||||
// Create a new user
|
||||
export async function createUser(data: CreateUserData) {
|
||||
try {
|
||||
const userId = generateId();
|
||||
const now = new Date();
|
||||
|
||||
const userData = {
|
||||
id: userId,
|
||||
email: data.email,
|
||||
username: data.username,
|
||||
displayName: data.displayName,
|
||||
avatarUrl: data.avatarUrl || null,
|
||||
bio: data.bio || null,
|
||||
isAdmin: false,
|
||||
isModerator: false,
|
||||
passwordHash: '', // This should be set by auth layer
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
await db.insert(users).values(userData);
|
||||
return userData;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to create user: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get user by ID
|
||||
export async function getUserById(id: string) {
|
||||
try {
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.id, id),
|
||||
with: {
|
||||
scripts: {
|
||||
where: eq(users.isAdmin, true) ? undefined : eq(users.id, id), // Only show own scripts unless admin
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new ApiError('User not found', 404);
|
||||
}
|
||||
|
||||
return user;
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) throw error;
|
||||
throw new ApiError(`Failed to get user: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get user by email
|
||||
export async function getUserByEmail(email: string) {
|
||||
try {
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.email, email),
|
||||
});
|
||||
|
||||
return user;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get user by email: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get user by username
|
||||
export async function getUserByUsername(username: string) {
|
||||
try {
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.username, username),
|
||||
});
|
||||
|
||||
return user;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get user by username: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Update user
|
||||
export async function updateUser(id: string, data: UpdateUserData) {
|
||||
try {
|
||||
const user = await getUserById(id);
|
||||
|
||||
const updateData = {
|
||||
...data,
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
await db
|
||||
.update(users)
|
||||
.set(updateData)
|
||||
.where(eq(users.id, id));
|
||||
|
||||
const updatedUser = { ...user, ...updateData };
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to update user: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Update user permissions (admin only)
|
||||
export async function updateUserPermissions(
|
||||
id: string,
|
||||
permissions: { isAdmin?: boolean; isModerator?: boolean }
|
||||
) {
|
||||
try {
|
||||
const user = await getUserById(id);
|
||||
|
||||
const updateData = {
|
||||
...permissions,
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
await db
|
||||
.update(users)
|
||||
.set(updateData)
|
||||
.where(eq(users.id, id));
|
||||
|
||||
const updatedUser = { ...user, ...updateData };
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to update user permissions: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Search users
|
||||
export async function searchUsers(query: string, limit: number = 20) {
|
||||
try {
|
||||
const searchResults = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(
|
||||
like(users.username, `%${query}%`)
|
||||
)
|
||||
.limit(limit);
|
||||
|
||||
return searchResults;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to search users: ${error}`, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Get all users (admin only)
|
||||
export async function getAllUsers(limit: number = 50, offset: number = 0) {
|
||||
try {
|
||||
const allUsers = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
return allUsers;
|
||||
} catch (error) {
|
||||
throw new ApiError(`Failed to get all users: ${error}`, 500);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user