use local proxy for media so that media doesnt need to be requested from r2 everytime but can be cached locally. this also fixes some csp issues ive been having.
215 lines
6.2 KiB
TypeScript
215 lines
6.2 KiB
TypeScript
import { json, error } from '@sveltejs/kit';
|
|
import type { RequestHandler } from './$types';
|
|
import { db } from '$lib/server/db';
|
|
import { friendship, user } from '$lib/server/db/schema';
|
|
import { eq, and, or } from 'drizzle-orm';
|
|
import { encodeBase64url } from '@oslojs/encoding';
|
|
import { getLocalR2Url } from '$lib/server/r2';
|
|
import { notificationService } from '$lib/server/notifications';
|
|
import { pushService } from '$lib/server/push';
|
|
|
|
function generateFriendshipId(): string {
|
|
const bytes = crypto.getRandomValues(new Uint8Array(15));
|
|
return encodeBase64url(bytes);
|
|
}
|
|
|
|
export const GET: RequestHandler = async ({ url, locals }) => {
|
|
if (!locals.user) {
|
|
throw error(401, 'Unauthorized');
|
|
}
|
|
|
|
const status = url.searchParams.get('status') || 'accepted';
|
|
const type = url.searchParams.get('type') || 'friends'; // 'friends', 'sent', 'received'
|
|
|
|
try {
|
|
let friendships;
|
|
|
|
if (type === 'friends') {
|
|
// Get accepted friendships where user is either sender or receiver
|
|
const friendshipsRaw = await db
|
|
.select({
|
|
id: friendship.id,
|
|
userId: friendship.userId,
|
|
friendId: friendship.friendId,
|
|
status: friendship.status,
|
|
createdAt: friendship.createdAt
|
|
})
|
|
.from(friendship)
|
|
.where(
|
|
and(
|
|
eq(friendship.status, status),
|
|
or(eq(friendship.userId, locals.user.id), eq(friendship.friendId, locals.user.id))
|
|
)
|
|
);
|
|
|
|
// Get friend details for each friendship
|
|
friendships = await Promise.all(
|
|
friendshipsRaw.map(async (f) => {
|
|
const friendUserId = f.userId === locals.user!.id ? f.friendId : f.userId;
|
|
const friendUser = await db
|
|
.select({
|
|
username: user.username,
|
|
profilePictureUrl: user.profilePictureUrl
|
|
})
|
|
.from(user)
|
|
.where(eq(user.id, friendUserId))
|
|
.limit(1);
|
|
|
|
return {
|
|
id: f.id,
|
|
userId: f.userId,
|
|
friendId: f.friendId,
|
|
status: f.status,
|
|
createdAt: f.createdAt,
|
|
friendUsername: friendUser[0]?.username || '',
|
|
friendProfilePictureUrl: friendUser[0]?.profilePictureUrl || null
|
|
};
|
|
})
|
|
);
|
|
} else if (type === 'sent') {
|
|
// Get friend requests sent by current user
|
|
friendships = await db
|
|
.select({
|
|
id: friendship.id,
|
|
userId: friendship.userId,
|
|
friendId: friendship.friendId,
|
|
status: friendship.status,
|
|
createdAt: friendship.createdAt,
|
|
friendUsername: user.username,
|
|
friendProfilePictureUrl: user.profilePictureUrl
|
|
})
|
|
.from(friendship)
|
|
.innerJoin(user, eq(friendship.friendId, user.id))
|
|
.where(and(eq(friendship.userId, locals.user.id), eq(friendship.status, status)));
|
|
} else if (type === 'received') {
|
|
// Get friend requests received by current user
|
|
friendships = await db
|
|
.select({
|
|
id: friendship.id,
|
|
userId: friendship.userId,
|
|
friendId: friendship.friendId,
|
|
status: friendship.status,
|
|
createdAt: friendship.createdAt,
|
|
friendUsername: user.username,
|
|
friendProfilePictureUrl: user.profilePictureUrl
|
|
})
|
|
.from(friendship)
|
|
.innerJoin(user, eq(friendship.userId, user.id))
|
|
.where(and(eq(friendship.friendId, locals.user.id), eq(friendship.status, status)));
|
|
}
|
|
|
|
// Generate local proxy URLs for profile pictures
|
|
const friendshipsWithSignedUrls = (friendships || []).map((friendship) => {
|
|
let profilePictureUrl = friendship.friendProfilePictureUrl;
|
|
if (profilePictureUrl && !profilePictureUrl.startsWith('http')) {
|
|
profilePictureUrl = getLocalR2Url(profilePictureUrl);
|
|
}
|
|
|
|
return {
|
|
...friendship,
|
|
friendProfilePictureUrl: profilePictureUrl
|
|
};
|
|
});
|
|
|
|
return json(friendshipsWithSignedUrls);
|
|
} catch (err) {
|
|
console.error('Error loading friendships:', err);
|
|
throw error(500, 'Failed to load friendships');
|
|
}
|
|
};
|
|
|
|
export const POST: RequestHandler = async ({ request, locals }) => {
|
|
if (!locals.user) {
|
|
throw error(401, 'Unauthorized');
|
|
}
|
|
|
|
const data = await request.json();
|
|
const { friendId } = data;
|
|
|
|
if (!friendId) {
|
|
throw error(400, 'Friend ID is required');
|
|
}
|
|
|
|
if (friendId === locals.user.id) {
|
|
throw error(400, 'Cannot send friend request to yourself');
|
|
}
|
|
|
|
try {
|
|
// Check if friend exists
|
|
const friendExists = await db.select().from(user).where(eq(user.id, friendId)).limit(1);
|
|
if (friendExists.length === 0) {
|
|
throw error(404, 'User not found');
|
|
}
|
|
|
|
// Check if friendship already exists
|
|
const existingFriendship = await db
|
|
.select()
|
|
.from(friendship)
|
|
.where(
|
|
or(
|
|
and(eq(friendship.userId, locals.user.id), eq(friendship.friendId, friendId)),
|
|
and(eq(friendship.userId, friendId), eq(friendship.friendId, locals.user.id))
|
|
)
|
|
)
|
|
.limit(1);
|
|
|
|
if (existingFriendship.length > 0) {
|
|
const status = existingFriendship[0].status;
|
|
if (status === 'accepted') {
|
|
throw error(400, 'Already friends');
|
|
} else if (status === 'pending') {
|
|
throw error(400, 'Friend request already sent');
|
|
} else if (status === 'blocked') {
|
|
throw error(400, 'Cannot send friend request');
|
|
}
|
|
}
|
|
|
|
// Create new friend request
|
|
const newFriendship = await db
|
|
.insert(friendship)
|
|
.values({
|
|
id: generateFriendshipId(),
|
|
userId: locals.user.id,
|
|
friendId,
|
|
status: 'pending'
|
|
})
|
|
.returning();
|
|
|
|
// Send notification to friend
|
|
const shouldNotify = await notificationService.shouldNotify(friendId, 'friend_request');
|
|
if (shouldNotify) {
|
|
await notificationService.createNotification({
|
|
userId: friendId,
|
|
type: 'friend_request',
|
|
title: 'New friend request',
|
|
message: `${locals.user.username} sent you a friend request`,
|
|
data: {
|
|
friendshipId: newFriendship[0].id,
|
|
senderId: locals.user.id,
|
|
senderUsername: locals.user.username
|
|
}
|
|
});
|
|
|
|
// Send push notification
|
|
await pushService.sendPushNotification(friendId, {
|
|
title: 'New friend request',
|
|
message: `${locals.user.username} sent you a friend request`,
|
|
url: '/friends',
|
|
tag: 'friend_request',
|
|
data: {
|
|
friendshipId: newFriendship[0].id,
|
|
senderId: locals.user.id
|
|
}
|
|
});
|
|
}
|
|
|
|
return json({ success: true, friendship: newFriendship[0] });
|
|
} catch (err) {
|
|
console.error('Error sending friend request:', err);
|
|
if (err instanceof Error && 'status' in err) {
|
|
throw err;
|
|
}
|
|
throw error(500, 'Failed to send friend request');
|
|
}
|
|
};
|