diff --git a/src/components/widgets/user-nav-dropdown.tsx b/src/components/widgets/user-nav-dropdown.tsx
index d36a456..8e8801d 100644
--- a/src/components/widgets/user-nav-dropdown.tsx
+++ b/src/components/widgets/user-nav-dropdown.tsx
@@ -15,7 +15,6 @@ const UserNavDropdown: React.FC
= ({ session }) => {
session?.user?.image || "/images/default-profile.jpg",
);
React.useEffect(() => {
- logger.debug("UserNavDropdown", "session", session);
setProfileImage(session?.user?.image || "/images/default-profile.jpg");
}, [session]);
diff --git a/src/lib/models/post.ts b/src/lib/models/post.ts
new file mode 100644
index 0000000..171bfac
--- /dev/null
+++ b/src/lib/models/post.ts
@@ -0,0 +1,10 @@
+export type Post = {
+ slug: string;
+ title: string;
+ description: string;
+ imageUrl: string;
+ likes: number;
+ tags: string[];
+ dislikes: number;
+ datePosted: Date;
+};
diff --git a/src/server/api/root.ts b/src/server/api/root.ts
index 5469a85..810b110 100644
--- a/src/server/api/root.ts
+++ b/src/server/api/root.ts
@@ -1,6 +1,6 @@
import { createCallerFactory, createTRPCRouter } from "@/server/api/trpc";
-import { authRouter } from "./routers/auth";
-import { imageRouter } from "@/server/api/routers/image";
+import { authRouter } from "@/server/api/routers/auth";
+import { postRouter } from "@/server/api/routers/post";
/**
* This is the primary router for your server.
@@ -9,7 +9,7 @@ import { imageRouter } from "@/server/api/routers/image";
*/
export const appRouter = createTRPCRouter({
auth: authRouter,
- image: imageRouter,
+ post: postRouter,
});
// export type definition of API
diff --git a/src/server/api/routers/image.ts b/src/server/api/routers/image.ts
deleted file mode 100644
index 41af296..0000000
--- a/src/server/api/routers/image.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import {
- createTRPCRouter,
- protectedProcedure,
- publicProcedure,
-} from "@/server/api/trpc";
-import { z } from "zod";
-import { images } from "@/server/db/schema";
-import { env } from "@/env";
-
-const imageCreateType = {
- title: z.string().min(5),
- description: z.string(),
- tags: z.array(z.string()),
-};
-
-export const imageRouter = createTRPCRouter({
- getTrending: publicProcedure.query(async ({ ctx }) => {
- const trending = await ctx.db.query.images.findMany({
- orderBy: (images, { desc }) => [desc(images.createdAt)],
- });
- return (
- trending.map((t) => {
- return {
- id: t.id,
- title: t.title,
- description: t.description,
- tags: t.tags,
- url: `${env.IMAGE_BASE_URL}/${t.filePath}`,
- };
- }) ?? null
- );
- }),
- create: protectedProcedure
- .input(z.object(imageCreateType))
- .mutation(async ({ ctx, input }) => {
- const post = await ctx.db
- .insert(images)
- .values({
- title: input.title,
- description: input.description,
- tags: input.tags,
- createdById: ctx.session.user.id,
- })
- .returning();
- if (!post[0]) {
- throw new Error("Failed to create image");
- }
- return {
- id: post[0].id,
- };
- }),
-});
diff --git a/src/server/api/routers/post.ts b/src/server/api/routers/post.ts
new file mode 100644
index 0000000..2496527
--- /dev/null
+++ b/src/server/api/routers/post.ts
@@ -0,0 +1,96 @@
+import {
+ createTRPCRouter,
+ protectedProcedure,
+ publicProcedure,
+} from "@/server/api/trpc";
+import { slugifyWithCounter } from "@sindresorhus/slugify";
+import { z } from "zod";
+import { images, users } from "@/server/db/schema";
+import { env } from "@/env";
+import { and, eq } from "drizzle-orm";
+import { Post } from "@/lib/models/post";
+
+export const postRouter = createTRPCRouter({
+ getBySlug: publicProcedure
+ .input(z.object({ slug: z.string() }))
+ .query(async ({ ctx, input }) => {
+ const image = (
+ await ctx.db
+ .select()
+ .from(images)
+ .where(and(eq(images.slug, input.slug)))
+ .limit(1)
+ )[0];
+ if (!image) {
+ throw new Error("Image not found");
+ }
+ return {
+ slug: image.id,
+ title: image.title,
+ description: image.description,
+ tags: image.tags,
+ imageUrl: `${env.IMAGE_BASE_URL}/${image.filePath}`,
+ likes: 0,
+ dislikes: 0,
+ } as Post;
+ }),
+ getTrending: publicProcedure.query(async ({ ctx }) => {
+ const trending = await ctx.db.query.images.findMany({
+ orderBy: (images, { desc }) => [desc(images.createdAt)],
+ });
+ return (
+ trending.map((t) => {
+ return {
+ slug: t.slug,
+ title: t.title,
+ description: t.description,
+ tags: t.tags,
+ imageUrl: `${env.IMAGE_BASE_URL}/${t.filePath}`,
+ likes: 0,
+ dislikes: 0,
+ } as Post;
+ }) ?? null
+ );
+ }),
+ create: protectedProcedure
+ .input(
+ z.object({
+ title: z.string().min(5),
+ description: z.string(),
+ tags: z.array(z.string()),
+ }),
+ )
+ .mutation(async ({ ctx, input }) => {
+ var slugify = slugifyWithCounter();
+ var found = false;
+ var slug = "";
+ do {
+ slug = slugify(input.title);
+ const existing = await ctx.db
+ .select()
+ .from(images)
+ .where(and(eq(images.slug, slug)))
+ .limit(1);
+ if (!existing[0]) {
+ found = true;
+ }
+ } while (!found);
+
+ const post = await ctx.db
+ .insert(images)
+ .values({
+ title: input.title,
+ slug: slug,
+ description: input.description,
+ tags: input.tags,
+ createdById: ctx.session.user.id,
+ })
+ .returning();
+ if (!post[0]) {
+ throw new Error("Failed to create image");
+ }
+ return {
+ id: post[0].id,
+ };
+ }),
+});
diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts
index e3665c0..946eada 100644
--- a/src/server/db/schema.ts
+++ b/src/server/db/schema.ts
@@ -13,25 +13,35 @@ import { type AdapterAccount } from "next-auth/adapters";
export const createTable = pgTableCreator((name) => `${name}`);
-export const images = createTable("images", {
- id: varchar("id", { length: 255 })
- .notNull()
- .primaryKey()
- .$defaultFn(() => crypto.randomUUID()),
- title: varchar("title", { length: 256 }),
- description: varchar("description"),
- tags: text("tags").array(),
- filePath: varchar("filepath", { length: 256 }),
- createdById: varchar("created_by", { length: 255 })
- .notNull()
- .references(() => users.id),
- createdAt: timestamp("created_at", { withTimezone: true })
- .default(sql`CURRENT_TIMESTAMP`)
- .notNull(),
- updatedAt: timestamp("updated_at", { withTimezone: true }).$onUpdate(
- () => new Date(),
- ),
-});
+export const images = createTable(
+ "images",
+ {
+ id: varchar("id", { length: 255 })
+ .notNull()
+ .primaryKey()
+ .$defaultFn(() => crypto.randomUUID()),
+ slug: varchar("slug", { length: 255 }).notNull().unique(),
+ title: varchar("title", { length: 256 }),
+ description: varchar("description"),
+ tags: text("tags").array(),
+ filePath: varchar("filepath", { length: 256 }),
+ createdById: varchar("created_by", { length: 255 })
+ .notNull()
+ .references(() => users.id),
+ createdAt: timestamp("created_at", { withTimezone: true })
+ .default(sql`CURRENT_TIMESTAMP`)
+ .notNull(),
+ updatedAt: timestamp("updated_at", { withTimezone: true }).$onUpdate(
+ () => new Date(),
+ ),
+ },
+ (table) => {
+ return {
+ userIndex: index("image_user_id_idx").on(table.createdById),
+ slugIndex: index("image_slug_idx").on(table.slug),
+ };
+ },
+);
export const users = createTable("users", {
id: varchar("id", { length: 255 })