diff --git a/app/(app)/servers/[serverId]/channels/[channelId]/page.tsx b/app/(app)/servers/[serverId]/channels/[channelId]/page.tsx
index 6ba4be9..83c49a4 100644
--- a/app/(app)/servers/[serverId]/channels/[channelId]/page.tsx
+++ b/app/(app)/servers/[serverId]/channels/[channelId]/page.tsx
@@ -3,7 +3,7 @@
* @description Page component for viewing a specific channel and its messages.
*/
-import { notFound } from "next/navigation";
+import { redirect } from "next/navigation";
import { AppHeader } from "@/components/layout/AppHeader";
import { ChatInput } from "@/components/chat/ChatInput";
import { ChatMessages } from "@/components/chat/ChatMessages";
@@ -24,13 +24,13 @@ export default async function ChannelPage({
}) {
const { channelId } = await params;
- // Paralleles Laden von Kanal-Daten und Nachrichten
+ // Parallel loading of channel data and messages
const [channel, channelMessages] = await Promise.all([
getChannelById(channelId),
getChannelMessages(channelId),
]);
- if (!channel) return notFound();
+ if (!channel) redirect("/");
return (
diff --git a/lib/services/channel.service.ts b/lib/services/channel.service.ts
index 9ca7bfd..7a51bac 100644
--- a/lib/services/channel.service.ts
+++ b/lib/services/channel.service.ts
@@ -1,20 +1,34 @@
/**
* @file lib/services/channel.service.ts
- * @description Service module providing database access methods for channel management.
+ * @description Service module providing database access methods for channel management and validation.
*/
import { db } from "@/db";
import { channels } from "@/db/schema";
import { eq } from "drizzle-orm";
+import { z } from "zod";
+
+const uuidSchema = z.uuid();
/**
* Retrieves a single channel record from the database by its unique identifier.
+ * Validates the UUID format before executing the database query.
*
* @param {string} channelId - The unique identifier of the channel to fetch.
- * @returns {Promise
} The channel object if found, or undefined if no matching channel exists.
+ * @returns {Promise} The channel object if found, or null if no matching channel exists or an invalid ID was provided.
*/
export async function getChannelById(channelId: string) {
- return await db.query.channels.findFirst({
- where: eq(channels.id, channelId),
- });
+ if (!uuidSchema.safeParse(channelId).success) {
+ return null;
+ }
+
+ try {
+ const channel = await db.query.channels.findFirst({
+ where: eq(channels.id, channelId),
+ });
+ return channel ?? null;
+ } catch (error) {
+ console.error(`Error fetching channel ${channelId}:`, error);
+ return null;
+ }
}
diff --git a/lib/services/message.service.ts b/lib/services/message.service.ts
index 0812512..bda0e7a 100644
--- a/lib/services/message.service.ts
+++ b/lib/services/message.service.ts
@@ -1,29 +1,43 @@
/**
* @file lib/services/message.service.ts
- * @description Data access service for retrieving and managing message entities from the database.
+ * @description Service module providing database access methods for fetching and managing channel messages and associated member profiles.
*/
import { db } from "@/db";
import { messages } from "@/db/schema";
-import { eq } from "drizzle-orm";
+import { eq, asc } from "drizzle-orm";
+import { z } from "zod";
+
+const uuidSchema = z.uuid();
/**
- * Retrieves all messages belonging to a specific channel, ordered chronologically ascending,
- * including associated member and user details.
+ * Retrieves all messages for a specific channel sorted chronologically, including member and user details.
+ * Validates the UUID format before querying the database and handles potential runtime errors gracefully.
*
- * @param {string} channelId - The unique identifier of the target channel.
- * @returns {Promise>} A promise resolving to a list of message objects populated with nested member and user data.
+ * @param {string} channelId - The unique identifier of the channel whose messages are to be fetched.
+ * @returns {Promise>} An array of message objects with nested member and user data, or an empty array if invalid or failed.
*/
export async function getChannelMessages(channelId: string) {
- return await db.query.messages.findMany({
- where: eq(messages.channelId, channelId),
- with: {
- member: {
- with: {
- user: true,
+ if (!uuidSchema.safeParse(channelId).success) {
+ return [];
+ }
+
+ try {
+ const channelMessages = await db.query.messages.findMany({
+ where: eq(messages.channelId, channelId),
+ orderBy: [asc(messages.createdAt)],
+ with: {
+ member: {
+ with: {
+ user: true,
+ },
},
},
- },
- orderBy: (messages, { asc }) => [asc(messages.createdAt)],
- });
+ });
+
+ return channelMessages;
+ } catch (error) {
+ console.error(`Error fetching messages for channel ${channelId}:`, error);
+ return [];
+ }
}
diff --git a/lib/services/server.service.ts b/lib/services/server.service.ts
index 37c75c5..16ab768 100644
--- a/lib/services/server.service.ts
+++ b/lib/services/server.service.ts
@@ -6,31 +6,47 @@
import { db } from "@/db";
import { members, servers } from "@/db/schema";
import { eq, and } from "drizzle-orm";
+import { z } from "zod";
+
+const uuidSchema = z.uuid();
/**
* Retrieves a server along with its channels sorted chronologically if the specified user is a verified member.
+ * Validates UUID formats prior to database execution to prevent database errors.
*
* @param {string} serverId - The unique identifier of the server to retrieve.
* @param {string} userId - The unique identifier of the requesting user.
- * @returns {Promise} The server record with nested channels array, or null if the user is not a member or the server is not found.
+ * @returns {Promise} The server record with nested channels array, or null if the user is not a member, the server does not exist, or an invalid ID was provided.
*/
export async function getServerWithChannels(serverId: string, userId: string) {
- const isMember = await db.query.members.findFirst({
- where: and(eq(members.serverId, serverId), eq(members.userId, userId)),
- });
+ if (
+ !uuidSchema.safeParse(serverId).success ||
+ !uuidSchema.safeParse(userId).success
+ ) {
+ return null;
+ }
- if (!isMember) return null;
+ try {
+ const isMember = await db.query.members.findFirst({
+ where: and(eq(members.serverId, serverId), eq(members.userId, userId)),
+ });
- const server = await db.query.servers.findFirst({
- where: eq(servers.id, serverId),
- with: {
- channels: {
- orderBy: (channels, { asc }) => [asc(channels.createdAt)],
+ if (!isMember) return null;
+
+ const server = await db.query.servers.findFirst({
+ where: eq(servers.id, serverId),
+ with: {
+ channels: {
+ orderBy: (channels, { asc }) => [asc(channels.createdAt)],
+ },
},
- },
- });
+ });
- return server;
+ return server ?? null;
+ } catch (error) {
+ console.error(`Error fetching server ${serverId}:`, error);
+ return null;
+ }
}
/**
@@ -40,18 +56,29 @@ export async function getServerWithChannels(serverId: string, userId: string) {
* @returns {Promise>} An array of server objects associated with the user.
*/
export async function getUserServers(userId: string) {
- const userMemberships = await db.query.members.findMany({
- where: eq(members.userId, userId),
- with: {
- server: {
- with: {
- channels: {
- orderBy: (channels, { asc }) => [asc(channels.createdAt)],
+ if (!uuidSchema.safeParse(userId).success) {
+ return [];
+ }
+
+ try {
+ const userMemberships = await db.query.members.findMany({
+ where: eq(members.userId, userId),
+ with: {
+ server: {
+ with: {
+ channels: {
+ orderBy: (channels, { asc }) => [asc(channels.createdAt)],
+ },
},
},
},
- },
- });
+ });
- return userMemberships.map((membership) => membership.server).filter(Boolean);
+ return userMemberships
+ .map((membership) => membership.server)
+ .filter(Boolean);
+ } catch (error) {
+ console.error(`Error fetching servers for user ${userId}:`, error);
+ return [];
+ }
}