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";
|
import { env } from "@/env";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
schema: "./src/server/db/schema.ts",
|
schema: "./src/server/db/schema/index.ts",
|
||||||
dialect: "postgresql",
|
dialect: "postgresql",
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
url: env.DATABASE_URL,
|
url: env.DATABASE_URL,
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { editorContentSchema } from ".";
|
||||||
|
|
||||||
export const articleSchema = z.object({
|
export const articleSchema = z.object({
|
||||||
title: z.string().min(1),
|
title: z.string().min(1),
|
||||||
slug: z.string().min(1),
|
slug: z.string().min(1),
|
||||||
content: z.any().optional(),
|
content: editorContentSchema.optional(),
|
||||||
authorId: z.string().optional(),
|
authorId: z.string().optional(),
|
||||||
categoryId: z.string().optional(),
|
categoryId: z.string().optional(),
|
||||||
published: z.boolean(),
|
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 { createCallerFactory, createTRPCRouter } from "@/server/api/trpc";
|
||||||
import { usersRouter } from "./routers/users";
|
import {
|
||||||
import { authorRouter } from "./routers/author";
|
articleRouter,
|
||||||
import { appRouter as globalRouter } from "./routers/app";
|
categoryRouter,
|
||||||
// import { authRouter } from "./routers/auth";
|
usersRouter,
|
||||||
/**
|
authorRouter,
|
||||||
* This is the primary router for your server.
|
appRouter as globalRouter,
|
||||||
*
|
commentRouter,
|
||||||
* All routers added in /api/routers should be manually added here.
|
} from "./routers";
|
||||||
*/
|
|
||||||
export const appRouter = createTRPCRouter({
|
export const appRouter = createTRPCRouter({
|
||||||
article: articleRouter,
|
article: articleRouter,
|
||||||
category: categoryRouter,
|
category: categoryRouter,
|
||||||
|
comment: commentRouter,
|
||||||
users: usersRouter,
|
users: usersRouter,
|
||||||
author: authorRouter,
|
author: authorRouter,
|
||||||
app: globalRouter,
|
app: globalRouter,
|
||||||
// auth: authRouter,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// export type definition of API
|
// 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 { articleRouter } from "./article";
|
||||||
// import { createTRPCRouter, publicProcedure } from "../trpc";
|
import { usersRouter } from "./users";
|
||||||
// import { z } from "zod";
|
import { authorRouter } from "./author";
|
||||||
// import { eq } from "drizzle-orm";
|
import { appRouter } from "./app";
|
||||||
// import { users } from "@/server/db/schema";
|
import { commentRouter } from "./comment";
|
||||||
// import argon from "argon2";
|
import { categoryRouter } from "./category";
|
||||||
|
|
||||||
|
export {
|
||||||
|
articleRouter,
|
||||||
|
categoryRouter,
|
||||||
|
commentRouter,
|
||||||
|
usersRouter,
|
||||||
|
authorRouter,
|
||||||
|
appRouter,
|
||||||
|
};
|
||||||
|
|
||||||
// export const authRouter = createTRPCRouter({
|
// export const authRouter = createTRPCRouter({
|
||||||
// register: publicProcedure
|
// 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 { relations, sql } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
boolean,
|
|
||||||
index,
|
index,
|
||||||
integer,
|
integer,
|
||||||
jsonb,
|
|
||||||
pgTableCreator,
|
|
||||||
primaryKey,
|
primaryKey,
|
||||||
text,
|
text,
|
||||||
timestamp,
|
timestamp,
|
||||||
varchar,
|
varchar,
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
import { User } from "next-auth";
|
|
||||||
|
import { createId, createTable } from "./schema-utils";
|
||||||
import { type AdapterAccount } from "next-auth/adapters";
|
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", {
|
export const users = createTable("user", {
|
||||||
id: varchar("id", { length: 255 })
|
id: varchar("id", { length: 255 })
|
||||||
.notNull()
|
.notNull()
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
.$defaultFn(() => crypto.randomUUID()),
|
.$defaultFn(() => createId()),
|
||||||
name: varchar("name", { length: 255 }),
|
name: varchar("name", { length: 255 }),
|
||||||
email: varchar("email", { length: 255 }).notNull(),
|
email: varchar("email", { length: 255 }).notNull(),
|
||||||
role: integer("role").default(1).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