restructured drizzle schema; added comments schema and server side operations
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 0s
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 0s
This commit is contained in:
parent
91a5241220
commit
6a32dcced7
@ -3,7 +3,7 @@ import { type Config } from "drizzle-kit";
|
||||
import { env } from "@/env";
|
||||
|
||||
export default {
|
||||
schema: "./src/server/db/schema.ts",
|
||||
schema: "./src/server/db/schema/index.ts",
|
||||
dialect: "postgresql",
|
||||
dbCredentials: {
|
||||
url: env.DATABASE_URL,
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { z } from "zod";
|
||||
import { editorContentSchema } from ".";
|
||||
|
||||
export const articleSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
slug: z.string().min(1),
|
||||
content: z.any().optional(),
|
||||
content: editorContentSchema.optional(),
|
||||
authorId: z.string().optional(),
|
||||
categoryId: z.string().optional(),
|
||||
published: z.boolean(),
|
||||
|
||||
7
src/lib/validation/zod/comment.ts
Normal file
7
src/lib/validation/zod/comment.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { z } from "zod";
|
||||
import { editorContentSchema } from ".";
|
||||
|
||||
export const commentSchema = z.object({
|
||||
content: editorContentSchema,
|
||||
parentId: z.string().optional(),
|
||||
});
|
||||
3
src/lib/validation/zod/index.ts
Normal file
3
src/lib/validation/zod/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const editorContentSchema = z.any(); // TODO: define editor content schema
|
||||
@ -1,7 +0,0 @@
|
||||
"use server";
|
||||
|
||||
import { signIn } from "@/server/auth";
|
||||
|
||||
export async function loginOAuth(provider: string) {
|
||||
return await signIn(provider);
|
||||
}
|
||||
43
src/server/actions/comment.ts
Normal file
43
src/server/actions/comment.ts
Normal file
@ -0,0 +1,43 @@
|
||||
"use server";
|
||||
|
||||
import { appRoutes } from "@/config";
|
||||
import { commentSchema } from "@/lib/validation/zod/comment";
|
||||
import { api } from "@/trpc/server";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { z } from "zod";
|
||||
|
||||
export async function createComment(
|
||||
comment: z.infer<typeof commentSchema>,
|
||||
articleId: string,
|
||||
) {
|
||||
const commentId = await api.comment.create({
|
||||
comment,
|
||||
articleId,
|
||||
});
|
||||
if (!commentId)
|
||||
return {
|
||||
success: false,
|
||||
message: "Error creating comment",
|
||||
};
|
||||
revalidatePath(appRoutes.article(articleId));
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
|
||||
export async function deleteComment(commentId: string) {
|
||||
const articleId = (
|
||||
await api.comment.delete({
|
||||
commentId,
|
||||
})
|
||||
)?.articleId;
|
||||
if (!articleId)
|
||||
return {
|
||||
success: false,
|
||||
message: "Error deleting comment",
|
||||
};
|
||||
revalidatePath(appRoutes.article(articleId));
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
@ -1,22 +1,20 @@
|
||||
import { articleRouter } from "./routers/article";
|
||||
import { categoryRouter } from "@/server/api/routers/category";
|
||||
import { createCallerFactory, createTRPCRouter } from "@/server/api/trpc";
|
||||
import { usersRouter } from "./routers/users";
|
||||
import { authorRouter } from "./routers/author";
|
||||
import { appRouter as globalRouter } from "./routers/app";
|
||||
// import { authRouter } from "./routers/auth";
|
||||
/**
|
||||
* This is the primary router for your server.
|
||||
*
|
||||
* All routers added in /api/routers should be manually added here.
|
||||
*/
|
||||
import {
|
||||
articleRouter,
|
||||
categoryRouter,
|
||||
usersRouter,
|
||||
authorRouter,
|
||||
appRouter as globalRouter,
|
||||
commentRouter,
|
||||
} from "./routers";
|
||||
|
||||
export const appRouter = createTRPCRouter({
|
||||
article: articleRouter,
|
||||
category: categoryRouter,
|
||||
comment: commentRouter,
|
||||
users: usersRouter,
|
||||
author: authorRouter,
|
||||
app: globalRouter,
|
||||
// auth: authRouter,
|
||||
});
|
||||
|
||||
// export type definition of API
|
||||
|
||||
49
src/server/api/routers/comment.ts
Normal file
49
src/server/api/routers/comment.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||
import { articles, comments } from "@/server/db/schema";
|
||||
import { z } from "zod";
|
||||
import { commentSchema } from "@/lib/validation/zod/comment";
|
||||
|
||||
export const commentRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
comment: commentSchema,
|
||||
articleId: z.string(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const [comment] = await ctx.db
|
||||
.insert(comments)
|
||||
.values({
|
||||
...input.comment,
|
||||
authorId: ctx.session.user.id,
|
||||
articleId: input.articleId,
|
||||
})
|
||||
.returning({
|
||||
id: comments.id,
|
||||
});
|
||||
return comment?.id;
|
||||
}),
|
||||
delete: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
commentId: z.string(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const [comment] = await ctx.db
|
||||
.delete(comments)
|
||||
.where(
|
||||
and(
|
||||
eq(comments.id, input.commentId),
|
||||
eq(comments.authorId, ctx.session.user.id),
|
||||
),
|
||||
)
|
||||
.returning({
|
||||
id: comments.id,
|
||||
articleId: comments.articleId,
|
||||
});
|
||||
return comment;
|
||||
}),
|
||||
});
|
||||
@ -1,9 +1,18 @@
|
||||
// import { passwordSchema, userSchema } from "@/lib/validation/zod/user";
|
||||
// import { createTRPCRouter, publicProcedure } from "../trpc";
|
||||
// import { z } from "zod";
|
||||
// import { eq } from "drizzle-orm";
|
||||
// import { users } from "@/server/db/schema";
|
||||
// import argon from "argon2";
|
||||
import { articleRouter } from "./article";
|
||||
import { usersRouter } from "./users";
|
||||
import { authorRouter } from "./author";
|
||||
import { appRouter } from "./app";
|
||||
import { commentRouter } from "./comment";
|
||||
import { categoryRouter } from "./category";
|
||||
|
||||
export {
|
||||
articleRouter,
|
||||
categoryRouter,
|
||||
commentRouter,
|
||||
usersRouter,
|
||||
authorRouter,
|
||||
appRouter,
|
||||
};
|
||||
|
||||
// export const authRouter = createTRPCRouter({
|
||||
// register: publicProcedure
|
||||
@ -52,3 +61,10 @@
|
||||
// }
|
||||
// }),
|
||||
// });
|
||||
// "use server";
|
||||
|
||||
// import { signIn } from "@/server/auth";
|
||||
|
||||
// export async function loginOAuth(provider: string) {
|
||||
// return await signIn(provider);
|
||||
// }
|
||||
52
src/server/db/schema/article.ts
Normal file
52
src/server/db/schema/article.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { boolean, index, jsonb, timestamp, varchar } from "drizzle-orm/pg-core";
|
||||
import { createId, createTable } from "./schema-utils";
|
||||
import { JSONContent } from "novel";
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import { categories, Category } from "./category";
|
||||
import { User } from "next-auth";
|
||||
import { users } from "./auth";
|
||||
import { comments } from "./comments";
|
||||
|
||||
export const articles = createTable(
|
||||
"article",
|
||||
{
|
||||
id: varchar("id", { length: 255 })
|
||||
.primaryKey()
|
||||
.$defaultFn(() => createId())
|
||||
.notNull(),
|
||||
title: varchar("title", { length: 256 }).notNull(),
|
||||
slug: varchar("slug", { length: 256 }).unique().notNull(),
|
||||
authorId: varchar("author_id", { length: 255 }),
|
||||
content: jsonb("content").$type<JSONContent>(),
|
||||
categoryId: varchar("category_id", { length: 255 }),
|
||||
published: boolean("published").default(false),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
},
|
||||
(example) => ({
|
||||
titleIndex: index("article_title_idx").on(example.title),
|
||||
slugIndex: index("article_slug_idx").on(example.slug),
|
||||
createdAtIndex: index("article_created_at_idx").on(example.createdAt),
|
||||
}),
|
||||
);
|
||||
|
||||
export const articleRelations = relations(articles, ({ one, many }) => ({
|
||||
category: one(categories, {
|
||||
fields: [articles.categoryId],
|
||||
references: [categories.id],
|
||||
}),
|
||||
author: one(users, {
|
||||
fields: [articles.authorId],
|
||||
references: [users.id],
|
||||
}),
|
||||
comments: many(comments),
|
||||
}));
|
||||
|
||||
export type Article = typeof articles.$inferSelect & {
|
||||
author?: User;
|
||||
category?: Category;
|
||||
};
|
||||
@ -1,101 +1,21 @@
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import {
|
||||
boolean,
|
||||
index,
|
||||
integer,
|
||||
jsonb,
|
||||
pgTableCreator,
|
||||
primaryKey,
|
||||
text,
|
||||
timestamp,
|
||||
varchar,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { User } from "next-auth";
|
||||
|
||||
import { createId, createTable } from "./schema-utils";
|
||||
import { type AdapterAccount } from "next-auth/adapters";
|
||||
import { JSONContent } from "novel";
|
||||
|
||||
export const createTable = pgTableCreator((name) => `logipedia_${name}`);
|
||||
|
||||
export const articles = createTable(
|
||||
"article",
|
||||
{
|
||||
id: varchar("id", { length: 255 })
|
||||
.primaryKey()
|
||||
.$defaultFn(() => createId())
|
||||
.notNull(),
|
||||
title: varchar("title", { length: 256 }).notNull(),
|
||||
slug: varchar("slug", { length: 256 }).unique().notNull(),
|
||||
authorId: varchar("author_id", { length: 255 }),
|
||||
content: jsonb("content").$type<JSONContent>(),
|
||||
categoryId: varchar("category_id", { length: 255 }),
|
||||
published: boolean("published").default(false),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
},
|
||||
(example) => ({
|
||||
titleIndex: index("article_title_idx").on(example.title),
|
||||
slugIndex: index("article_slug_idx").on(example.slug),
|
||||
createdAtIndex: index("article_created_at_idx").on(example.createdAt),
|
||||
}),
|
||||
);
|
||||
export type Article = typeof articles.$inferSelect & {
|
||||
author?: User;
|
||||
category?: Category;
|
||||
};
|
||||
export const articleRelations = relations(articles, ({ one }) => ({
|
||||
category: one(categories, {
|
||||
fields: [articles.categoryId],
|
||||
references: [categories.id],
|
||||
}),
|
||||
author: one(users, {
|
||||
fields: [articles.authorId],
|
||||
references: [users.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const categories = createTable(
|
||||
"category",
|
||||
{
|
||||
id: varchar("id", { length: 255 })
|
||||
.primaryKey()
|
||||
.$defaultFn(() => createId())
|
||||
.notNull(),
|
||||
name: varchar("name", { length: 256 }).notNull(),
|
||||
description: text("description"),
|
||||
slug: varchar("slug", { length: 256 }).unique().notNull(),
|
||||
|
||||
image: varchar("image", { length: 255 }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
},
|
||||
(example) => ({
|
||||
nameIndex: index("category_name_idx").on(example.name),
|
||||
slugameIndex: index("category_slug_idx").on(example.slug),
|
||||
createdAtIndex: index("category_created_at_idx").on(example.createdAt),
|
||||
}),
|
||||
);
|
||||
export type Category = typeof categories.$inferSelect & {
|
||||
articles?: Article[];
|
||||
};
|
||||
|
||||
export const categoryRelations = relations(categories, ({ many }) => ({
|
||||
articles: many(articles),
|
||||
}));
|
||||
|
||||
export const users = createTable("user", {
|
||||
id: varchar("id", { length: 255 })
|
||||
.notNull()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
.$defaultFn(() => createId()),
|
||||
name: varchar("name", { length: 255 }),
|
||||
email: varchar("email", { length: 255 }).notNull(),
|
||||
role: integer("role").default(1).notNull(),
|
||||
37
src/server/db/schema/category.ts
Normal file
37
src/server/db/schema/category.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { index, text, timestamp, varchar } from "drizzle-orm/pg-core";
|
||||
import { createId, createTable } from "./schema-utils";
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import { Article, articles } from "./article";
|
||||
|
||||
export const categories = createTable(
|
||||
"category",
|
||||
{
|
||||
id: varchar("id", { length: 255 })
|
||||
.primaryKey()
|
||||
.$defaultFn(() => createId())
|
||||
.notNull(),
|
||||
name: varchar("name", { length: 256 }).notNull(),
|
||||
description: text("description"),
|
||||
slug: varchar("slug", { length: 256 }).unique().notNull(),
|
||||
|
||||
image: varchar("image", { length: 255 }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
},
|
||||
(example) => ({
|
||||
nameIndex: index("category_name_idx").on(example.name),
|
||||
slugameIndex: index("category_slug_idx").on(example.slug),
|
||||
createdAtIndex: index("category_created_at_idx").on(example.createdAt),
|
||||
}),
|
||||
);
|
||||
export type Category = typeof categories.$inferSelect & {
|
||||
articles?: Article[];
|
||||
};
|
||||
|
||||
export const categoryRelations = relations(categories, ({ many }) => ({
|
||||
articles: many(articles),
|
||||
}));
|
||||
39
src/server/db/schema/comments.ts
Normal file
39
src/server/db/schema/comments.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { index, jsonb, timestamp, varchar } from "drizzle-orm/pg-core";
|
||||
import { articles } from "./article";
|
||||
import { JSONContent } from "novel";
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import { createId, createTable } from "./schema-utils";
|
||||
import { users } from "./auth";
|
||||
|
||||
export const comments = createTable(
|
||||
"comment",
|
||||
{
|
||||
id: varchar("id", { length: 255 })
|
||||
.primaryKey()
|
||||
.$defaultFn(() => createId())
|
||||
.notNull(),
|
||||
articleId: varchar("article_id", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => articles.id),
|
||||
authorId: varchar("author_id", { length: 255 })
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
parentId: varchar("parent_id", { length: 255 }),
|
||||
content: jsonb("content").$type<JSONContent>(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).$onUpdate(
|
||||
() => new Date(),
|
||||
),
|
||||
},
|
||||
(example) => ({
|
||||
articleIdIndex: index("comment_article_id_idx").on(example.articleId),
|
||||
authorIdIndex: index("comment_author_id_idx").on(example.authorId),
|
||||
createdAtIndex: index("comment_created_at_idx").on(example.createdAt),
|
||||
}),
|
||||
);
|
||||
|
||||
export const commentsRelations = relations(comments, ({ many }) => ({
|
||||
comments: many(comments),
|
||||
}));
|
||||
4
src/server/db/schema/index.ts
Normal file
4
src/server/db/schema/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from "./auth";
|
||||
export * from "./article";
|
||||
export * from "./category";
|
||||
export * from "./comments";
|
||||
7
src/server/db/schema/schema-utils.ts
Normal file
7
src/server/db/schema/schema-utils.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { createId as creatIdCuuid2 } from "@paralleldrive/cuid2";
|
||||
|
||||
import { pgTableCreator } from "drizzle-orm/pg-core";
|
||||
|
||||
export const createTable = pgTableCreator((name) => `logipedia_${name}`);
|
||||
|
||||
export const createId = () => creatIdCuuid2();
|
||||
Loading…
x
Reference in New Issue
Block a user