Notes route
This commit is contained in:
parent
0fe2b335ed
commit
9f706092a7
14 changed files with 546 additions and 141 deletions
|
|
@ -3,7 +3,7 @@ import { defineConfig } from "drizzle-kit";
|
|||
|
||||
export default defineConfig({
|
||||
out: "./drizzle",
|
||||
schema: "./src/db/schema.ts",
|
||||
schema: "./src/db/schema",
|
||||
dialect: "postgresql",
|
||||
dbCredentials: {
|
||||
url: process.env.DATABASE_URL!,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
CREATE TABLE "account" (
|
||||
CREATE SCHEMA "auth";
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "auth"."account" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"account_id" text NOT NULL,
|
||||
"provider_id" text NOT NULL,
|
||||
|
|
@ -14,14 +16,14 @@ CREATE TABLE "account" (
|
|||
"updated_at" timestamp NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "rate_limit" (
|
||||
CREATE TABLE "auth"."rate_limit" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"key" text,
|
||||
"count" integer,
|
||||
"last_request" integer
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "session" (
|
||||
CREATE TABLE "auth"."session" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"expires_at" timestamp NOT NULL,
|
||||
"token" text NOT NULL,
|
||||
|
|
@ -33,7 +35,7 @@ CREATE TABLE "session" (
|
|||
CONSTRAINT "session_token_unique" UNIQUE("token")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user" (
|
||||
CREATE TABLE "auth"."user" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"email" text NOT NULL,
|
||||
|
|
@ -44,7 +46,7 @@ CREATE TABLE "user" (
|
|||
CONSTRAINT "user_email_unique" UNIQUE("email")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "verification" (
|
||||
CREATE TABLE "auth"."verification" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"identifier" text NOT NULL,
|
||||
"value" text NOT NULL,
|
||||
|
|
@ -53,5 +55,15 @@ CREATE TABLE "verification" (
|
|||
"updated_at" timestamp
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;
|
||||
CREATE TABLE "note" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"title" text,
|
||||
"content" text,
|
||||
"createdAt" timestamp DEFAULT now(),
|
||||
"updatedAt" timestamp,
|
||||
"ownerId" text NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "auth"."account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "auth"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "auth"."session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "auth"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "note" ADD CONSTRAINT "note_ownerId_user_id_fk" FOREIGN KEY ("ownerId") REFERENCES "auth"."user"("id") ON DELETE no action ON UPDATE no action;
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"id": "e9e52dc2-cfab-453f-8e23-57a32259b8e9",
|
||||
"id": "806f9895-fec6-43da-9c78-46e599e611e8",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.account": {
|
||||
"auth.account": {
|
||||
"name": "account",
|
||||
"schema": "",
|
||||
"schema": "auth",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
|
|
@ -93,6 +93,7 @@
|
|||
"name": "account_user_id_user_id_fk",
|
||||
"tableFrom": "account",
|
||||
"tableTo": "user",
|
||||
"schemaTo": "auth",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
|
|
@ -109,9 +110,9 @@
|
|||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.rate_limit": {
|
||||
"auth.rate_limit": {
|
||||
"name": "rate_limit",
|
||||
"schema": "",
|
||||
"schema": "auth",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
|
|
@ -146,9 +147,9 @@
|
|||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.session": {
|
||||
"auth.session": {
|
||||
"name": "session",
|
||||
"schema": "",
|
||||
"schema": "auth",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
|
|
@ -205,6 +206,7 @@
|
|||
"name": "session_user_id_user_id_fk",
|
||||
"tableFrom": "session",
|
||||
"tableTo": "user",
|
||||
"schemaTo": "auth",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
|
|
@ -229,9 +231,9 @@
|
|||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.user": {
|
||||
"auth.user": {
|
||||
"name": "user",
|
||||
"schema": "",
|
||||
"schema": "auth",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
|
|
@ -292,9 +294,9 @@
|
|||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.verification": {
|
||||
"auth.verification": {
|
||||
"name": "verification",
|
||||
"schema": "",
|
||||
"schema": "auth",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
|
|
@ -340,10 +342,77 @@
|
|||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.note": {
|
||||
"name": "note",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"content": {
|
||||
"name": "content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updatedAt": {
|
||||
"name": "updatedAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"ownerId": {
|
||||
"name": "ownerId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"note_ownerId_user_id_fk": {
|
||||
"name": "note_ownerId_user_id_fk",
|
||||
"tableFrom": "note",
|
||||
"tableTo": "user",
|
||||
"schemaTo": "auth",
|
||||
"columnsFrom": [
|
||||
"ownerId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"schemas": {
|
||||
"auth": "auth"
|
||||
},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1736964339376,
|
||||
"tag": "0000_bright_meltdown",
|
||||
"when": 1736970092723,
|
||||
"tag": "0000_pretty_banshee",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,25 +1,141 @@
|
|||
import { Memo } from "./note.model";
|
||||
import { and, eq, notExists } from "drizzle-orm";
|
||||
import { db } from "../../db";
|
||||
import { note } from "../../db/schema/note";
|
||||
import { CreateNote } from "./note.model";
|
||||
|
||||
export class Note {
|
||||
constructor(
|
||||
public data: Memo[] = [
|
||||
{
|
||||
data: "Moonhalo",
|
||||
},
|
||||
]
|
||||
) {}
|
||||
|
||||
add(note: Memo) {
|
||||
this.data.push(note);
|
||||
|
||||
return this.data;
|
||||
export class NoteController {
|
||||
async createNote(new_note: CreateNote, ownerId: string) {
|
||||
const new_note_data = { ...new_note, ownerId: ownerId };
|
||||
const result = await db
|
||||
.insert(note)
|
||||
.values(new_note_data)
|
||||
.returning({
|
||||
id: note.id,
|
||||
title: note.title,
|
||||
content: note.content,
|
||||
createdAt: note.createdAt,
|
||||
updatedAt: note.updatedAt,
|
||||
})
|
||||
.execute();
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
message: "Note created successfully",
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
|
||||
remove(index: number) {
|
||||
return this.data.splice(index, 1);
|
||||
async getOwnerNotes(ownerId: string, limit:number=10, offset:number=0) {
|
||||
const result = await db
|
||||
.select({
|
||||
id: note.id,
|
||||
title: note.title,
|
||||
content: note.content,
|
||||
createdAt: note.createdAt,
|
||||
updatedAt: note.updatedAt,
|
||||
})
|
||||
.from(note)
|
||||
.where(and(eq(note.ownerId, ownerId), notExists(note.deletedAt)))
|
||||
.limit(limit).offset(offset)
|
||||
.execute();
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
message: "",
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
|
||||
update(index: number, note: Partial<Memo>) {
|
||||
return (this.data[index] = { ...this.data[index], ...note });
|
||||
async getNoteById(noteId: string, ownerId: string) {
|
||||
const result = await db
|
||||
.select({
|
||||
id: note.id,
|
||||
title: note.title,
|
||||
content: note.content,
|
||||
createdAt: note.createdAt,
|
||||
updatedAt: note.updatedAt,
|
||||
})
|
||||
.from(note)
|
||||
.where(
|
||||
and(
|
||||
eq(note.id, noteId),
|
||||
eq(note.ownerId, ownerId),
|
||||
notExists(note.deletedAt)
|
||||
)
|
||||
)
|
||||
.execute();
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
message: "",
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
|
||||
async updateNoteById(
|
||||
noteId: string,
|
||||
updated_note: CreateNote,
|
||||
ownerId: string
|
||||
) {
|
||||
const new_note_data = { ...updated_note, updatedAt: new Date() };
|
||||
const result = await db
|
||||
.update(note)
|
||||
.set(new_note_data)
|
||||
.where(
|
||||
and(
|
||||
eq(note.id, noteId),
|
||||
eq(note.ownerId, ownerId),
|
||||
notExists(note.deletedAt)
|
||||
)
|
||||
)
|
||||
.returning({
|
||||
id: note.id,
|
||||
title: note.title,
|
||||
content: note.content,
|
||||
createdAt: note.createdAt,
|
||||
updatedAt: note.updatedAt,
|
||||
})
|
||||
.execute();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
message: "Note updated successfully",
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
|
||||
async deleteNoteById(noteId: string, ownerId: string) {
|
||||
await db
|
||||
.update(note)
|
||||
.set({ deletedAt: new Date() })
|
||||
.where(
|
||||
and(
|
||||
eq(note.id, noteId),
|
||||
eq(note.ownerId, ownerId),
|
||||
notExists(note.deletedAt)
|
||||
)
|
||||
)
|
||||
.execute();
|
||||
return {
|
||||
success: true,
|
||||
data: null,
|
||||
message: "Note deleted successfully",
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
|
||||
async deleteAllNotes(ownerId: string) {
|
||||
await db
|
||||
.update(note)
|
||||
.set({ deletedAt: new Date() })
|
||||
.where(and(eq(note.ownerId, ownerId), notExists(note.deletedAt)))
|
||||
.execute();
|
||||
return {
|
||||
success: true,
|
||||
data: null,
|
||||
message: "Notes deleted successfully",
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,37 @@
|
|||
import { createSelectSchema } from "drizzle-typebox";
|
||||
import { t } from "elysia";
|
||||
import { note } from "../../db/schema/note";
|
||||
import { InferInsertModel, InferSelectModel } from "drizzle-orm";
|
||||
|
||||
export const memoSchema = t.Object({
|
||||
data: t.String(),
|
||||
});
|
||||
|
||||
export type Memo = typeof memoSchema.static;
|
||||
export const SelectNoteSchema = createSelectSchema(note);
|
||||
|
||||
export const NoteSchema = t.Omit(SelectNoteSchema, ["deletedAt", "ownerId"]);
|
||||
|
||||
export type Note = InferSelectModel<typeof note>;
|
||||
export type CreateNote = Pick<
|
||||
InferInsertModel<typeof note>,
|
||||
"title" | "content"
|
||||
>;
|
||||
export const createNoteSchema = t.Pick(NoteSchema, [
|
||||
"title",
|
||||
"content",
|
||||
]);
|
||||
|
||||
export const successGetNoteResponse = t.Object({
|
||||
success:t.Boolean({default:true}),
|
||||
data: t.Array(NoteSchema),
|
||||
error: t.Null(),
|
||||
message: t.String()
|
||||
}, {
|
||||
description:"Success"
|
||||
})
|
||||
|
||||
export const successDeleteNoteResponse = t.Object({
|
||||
success:t.Boolean({default:true}),
|
||||
data: t.Null(),
|
||||
error: t.Null(),
|
||||
message: t.String({default:"Note deletion succesful"})
|
||||
}, {
|
||||
description:"Success"
|
||||
})
|
||||
|
|
@ -1,44 +1,129 @@
|
|||
import { Elysia, t } from "elysia";
|
||||
import { Note } from "./note.controller";
|
||||
import { memoSchema } from "./note.model";
|
||||
import { Elysia, error, t } from "elysia";
|
||||
import { createNoteSchema, NoteSchema, successDeleteNoteResponse, successGetNoteResponse } from "./note.model";
|
||||
import { NoteController } from "./note.controller";
|
||||
import { userMiddleware } from "../../middlewares/auth-middleware";
|
||||
import { commonResponses } from "../../lib/utils/common";
|
||||
|
||||
export const note = new Elysia({ prefix: "/note" })
|
||||
.decorate("note", new Note())
|
||||
export const noteRouter = new Elysia({
|
||||
prefix: "/note",
|
||||
name: "CRUD Operations for Notes",
|
||||
"analytic":true,
|
||||
tags: ["Note"],
|
||||
detail: {
|
||||
description: "Notes CRUD operations",
|
||||
},
|
||||
})
|
||||
.decorate("note", new NoteController())
|
||||
.model({
|
||||
memo: t.Omit(memoSchema, ["author"]),
|
||||
})
|
||||
.get("/", ({ note }) => note.data)
|
||||
.put("/", ({ note, body: { data } }) => note.add({ data }), {
|
||||
body: "memo",
|
||||
note: NoteSchema,
|
||||
})
|
||||
.derive(({ request }) => userMiddleware(request))
|
||||
.get(
|
||||
"/:index",
|
||||
({ note, params: { index }, error }) => {
|
||||
return note.data[index] ?? error(404, "Not Found :(");
|
||||
"",
|
||||
async ({ note, user, query}) => {
|
||||
return await note.getOwnerNotes(user.id, query.limit, query.offset);
|
||||
},
|
||||
{
|
||||
params: t.Object({
|
||||
index: t.Number(),
|
||||
query:t.Object({
|
||||
limit: t.Optional(t.Number()),
|
||||
offset: t.Optional(t.Number())
|
||||
}),
|
||||
response:{
|
||||
200: successGetNoteResponse,
|
||||
...commonResponses
|
||||
},
|
||||
detail:{
|
||||
"description":"Get all notes of the user",
|
||||
"summary":"Get all notes"
|
||||
}
|
||||
}
|
||||
)
|
||||
.guard({
|
||||
params: t.Object({
|
||||
index: t.Number(),
|
||||
}),
|
||||
})
|
||||
.delete("/:index", ({ note, params: { index }, error }) => {
|
||||
if (index in note.data) return note.remove(index);
|
||||
|
||||
return error(422);
|
||||
})
|
||||
.patch(
|
||||
"/:index",
|
||||
({ note, params: { index }, body: { data }, error }) => {
|
||||
if (index in note.data) return note.update(index, { data });
|
||||
return error(422);
|
||||
.get(
|
||||
":id",
|
||||
async ({ note, user, params:{id} }) => {
|
||||
return await note.getNoteById(id, user.id);
|
||||
},
|
||||
{
|
||||
body: "memo",
|
||||
params:t.Object({
|
||||
id: t.String(),
|
||||
}),
|
||||
response: {
|
||||
200: successGetNoteResponse,
|
||||
...commonResponses
|
||||
},
|
||||
detail:{
|
||||
"description":"Get a note by Id",
|
||||
"summary":"Get a note"
|
||||
}
|
||||
}
|
||||
);
|
||||
)
|
||||
.post(
|
||||
"",
|
||||
async ({ body, note, user }) => {
|
||||
return await note.createNote(body, user.id);
|
||||
},
|
||||
{
|
||||
body: createNoteSchema,
|
||||
response: {
|
||||
200: successGetNoteResponse,
|
||||
...commonResponses
|
||||
},
|
||||
detail:{
|
||||
"description":"Create a new note",
|
||||
"summary":"Create a note"
|
||||
}
|
||||
}
|
||||
).patch(
|
||||
":id",
|
||||
async ({ body, note, user, params:{id} }) => {
|
||||
return await note.updateNoteById(id, body, user.id);
|
||||
},
|
||||
{
|
||||
body: createNoteSchema,
|
||||
params:t.Object({
|
||||
id: t.String(),
|
||||
}),
|
||||
response: {
|
||||
200: successGetNoteResponse,
|
||||
...commonResponses
|
||||
},
|
||||
detail:{
|
||||
"description":"Update a note by Id",
|
||||
"summary":"Update a note"
|
||||
}
|
||||
}
|
||||
).delete(
|
||||
":id",
|
||||
async ({ note, user, params:{id} }) => {
|
||||
return await note.deleteNoteById(id, user.id);
|
||||
},
|
||||
{
|
||||
params:t.Object({
|
||||
id: t.String(),
|
||||
}),
|
||||
response: {
|
||||
200: successDeleteNoteResponse,
|
||||
...commonResponses
|
||||
},
|
||||
detail:{
|
||||
"description":"Delete a note by Id",
|
||||
"summary":"Delete a note"
|
||||
}
|
||||
}
|
||||
)
|
||||
.delete(
|
||||
"",
|
||||
async ({ note, user }) => {
|
||||
return await note.deleteAllNotes(user.id);
|
||||
},
|
||||
{
|
||||
response: {
|
||||
200: successDeleteNoteResponse,
|
||||
...commonResponses
|
||||
},
|
||||
detail:{
|
||||
"description":"Delete all notes of an user",
|
||||
"summary":"Delete all notes"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
import { pgTable, text, integer, timestamp, boolean } from "drizzle-orm/pg-core";
|
||||
|
||||
export const user = pgTable("user", {
|
||||
id: text("id").primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
email: text('email').notNull().unique(),
|
||||
emailVerified: boolean('email_verified').notNull(),
|
||||
image: text('image'),
|
||||
createdAt: timestamp('created_at').notNull(),
|
||||
updatedAt: timestamp('updated_at').notNull()
|
||||
});
|
||||
|
||||
export const session = pgTable("session", {
|
||||
id: text("id").primaryKey(),
|
||||
expiresAt: timestamp('expires_at').notNull(),
|
||||
token: text('token').notNull().unique(),
|
||||
createdAt: timestamp('created_at').notNull(),
|
||||
updatedAt: timestamp('updated_at').notNull(),
|
||||
ipAddress: text('ip_address'),
|
||||
userAgent: text('user_agent'),
|
||||
userId: text('user_id').notNull().references(()=> user.id)
|
||||
});
|
||||
|
||||
export const account = pgTable("account", {
|
||||
id: text("id").primaryKey(),
|
||||
accountId: text('account_id').notNull(),
|
||||
providerId: text('provider_id').notNull(),
|
||||
userId: text('user_id').notNull().references(()=> user.id),
|
||||
accessToken: text('access_token'),
|
||||
refreshToken: text('refresh_token'),
|
||||
idToken: text('id_token'),
|
||||
accessTokenExpiresAt: timestamp('access_token_expires_at'),
|
||||
refreshTokenExpiresAt: timestamp('refresh_token_expires_at'),
|
||||
scope: text('scope'),
|
||||
password: text('password'),
|
||||
createdAt: timestamp('created_at').notNull(),
|
||||
updatedAt: timestamp('updated_at').notNull()
|
||||
});
|
||||
|
||||
export const verification = pgTable("verification", {
|
||||
id: text("id").primaryKey(),
|
||||
identifier: text('identifier').notNull(),
|
||||
value: text('value').notNull(),
|
||||
expiresAt: timestamp('expires_at').notNull(),
|
||||
createdAt: timestamp('created_at'),
|
||||
updatedAt: timestamp('updated_at')
|
||||
});
|
||||
|
||||
export const rateLimit = pgTable("rate_limit", {
|
||||
id: text("id").primaryKey(),
|
||||
key: text('key'),
|
||||
count: integer('count'),
|
||||
lastRequest: integer('last_request')
|
||||
});
|
||||
56
src/db/schema/auth.ts
Normal file
56
src/db/schema/auth.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { text, integer, timestamp, boolean, pgSchema } from "drizzle-orm/pg-core";
|
||||
|
||||
export const authSchema = pgSchema('auth');
|
||||
|
||||
export const user = authSchema.table("user", {
|
||||
id: text("id").primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
email: text('email').notNull().unique(),
|
||||
emailVerified: boolean('email_verified').notNull(),
|
||||
image: text('image'),
|
||||
createdAt: timestamp('created_at').notNull(),
|
||||
updatedAt: timestamp('updated_at').notNull()
|
||||
});
|
||||
|
||||
export const session = authSchema.table("session", {
|
||||
id: text("id").primaryKey(),
|
||||
expiresAt: timestamp('expires_at').notNull(),
|
||||
token: text('token').notNull().unique(),
|
||||
createdAt: timestamp('created_at').notNull(),
|
||||
updatedAt: timestamp('updated_at').notNull(),
|
||||
ipAddress: text('ip_address'),
|
||||
userAgent: text('user_agent'),
|
||||
userId: text('user_id').notNull().references(() => user.id)
|
||||
});
|
||||
|
||||
export const account = authSchema.table("account", {
|
||||
id: text("id").primaryKey(),
|
||||
accountId: text('account_id').notNull(),
|
||||
providerId: text('provider_id').notNull(),
|
||||
userId: text('user_id').notNull().references(() => user.id),
|
||||
accessToken: text('access_token'),
|
||||
refreshToken: text('refresh_token'),
|
||||
idToken: text('id_token'),
|
||||
accessTokenExpiresAt: timestamp('access_token_expires_at'),
|
||||
refreshTokenExpiresAt: timestamp('refresh_token_expires_at'),
|
||||
scope: text('scope'),
|
||||
password: text('password'),
|
||||
createdAt: timestamp('created_at').notNull(),
|
||||
updatedAt: timestamp('updated_at').notNull()
|
||||
});
|
||||
|
||||
export const verification = authSchema.table("verification", {
|
||||
id: text("id").primaryKey(),
|
||||
identifier: text('identifier').notNull(),
|
||||
value: text('value').notNull(),
|
||||
expiresAt: timestamp('expires_at').notNull(),
|
||||
createdAt: timestamp('created_at'),
|
||||
updatedAt: timestamp('updated_at')
|
||||
});
|
||||
|
||||
export const rateLimit = authSchema.table("rate_limit", {
|
||||
id: text("id").primaryKey(),
|
||||
key: text('key'),
|
||||
count: integer('count'),
|
||||
lastRequest: integer('last_request')
|
||||
});
|
||||
13
src/db/schema/note.ts
Normal file
13
src/db/schema/note.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import { user } from "./auth";
|
||||
|
||||
export const note = pgTable("note", {
|
||||
id: text("id").primaryKey().$defaultFn(()=> `note_${createId()}`),
|
||||
title: text("title"),
|
||||
content: text("content"),
|
||||
createdAt: timestamp().notNull().defaultNow(),
|
||||
updatedAt: timestamp(),
|
||||
deletedAt: timestamp(),
|
||||
ownerId: text().notNull().references(() => user.id)
|
||||
})
|
||||
|
|
@ -3,9 +3,8 @@ import { swagger } from "@elysiajs/swagger";
|
|||
import { opentelemetry } from "@elysiajs/opentelemetry";
|
||||
import { serverTiming } from "@elysiajs/server-timing";
|
||||
import { cors } from '@elysiajs/cors'
|
||||
import { note } from "./api/note/note.route";
|
||||
import { noteRouter } from "./api/note/note.route";
|
||||
import { betterAuthView } from "./lib/auth/auth-view";
|
||||
import { userMiddleware, userInfo } from "./middlewares/auth-middleware";
|
||||
import { getBaseConfig, validateEnv } from "./lib/utils/env";
|
||||
|
||||
const baseConfig = getBaseConfig()
|
||||
|
|
@ -23,10 +22,8 @@ const app = new Elysia()
|
|||
if (code === "NOT_FOUND") return "Not Found :(";
|
||||
console.error(error);
|
||||
})
|
||||
.derive(({ request }) => userMiddleware(request))
|
||||
.all("/api/auth/*", betterAuthView)
|
||||
.use(note)
|
||||
.get("/user", ({ user, session }) => userInfo(user, session))
|
||||
.use(noteRouter)
|
||||
.get("/", () => `${baseConfig.SERVICE_NAME} Server is Running`)
|
||||
|
||||
validateEnv();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { betterAuth } from "better-auth";
|
|||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { db } from "../../db/index";
|
||||
import { openAPI } from "better-auth/plugins"
|
||||
import { user, account, verification, session } from "../../db/schema";
|
||||
import { user, account, verification, session, rateLimit } from "../../db/schema/auth";
|
||||
import { sendMail } from "../mail/mail";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { createElement } from "react";
|
||||
|
|
@ -15,7 +15,8 @@ export const auth = betterAuth({
|
|||
user: user,
|
||||
session: session,
|
||||
account: account,
|
||||
verification: verification
|
||||
verification: verification,
|
||||
rateLimit: rateLimit,
|
||||
}
|
||||
}),
|
||||
rateLimit: {
|
||||
|
|
|
|||
82
src/lib/utils/common.ts
Normal file
82
src/lib/utils/common.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { t } from "elysia";
|
||||
|
||||
export const commonResponses = {
|
||||
400: t.Object(
|
||||
{
|
||||
data: t.Null(),
|
||||
success: t.Boolean({ default: false }),
|
||||
message: t.String({ default: "Bad Request" }),
|
||||
error: t.String({
|
||||
default: "Missing parameters, or invalid parameters.",
|
||||
}),
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Bad Request. Usually due to missing parameters, or invalid parameters.",
|
||||
}
|
||||
),
|
||||
401: t.Object(
|
||||
{
|
||||
data: t.Null(),
|
||||
success: t.Boolean({ default: false }),
|
||||
message: t.String({ default: "Unauthorized" }),
|
||||
error: t.String({
|
||||
default: "User needs to sign in to access this resource",
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: "Unauthorized. Due to missing or invalid authentication.",
|
||||
}
|
||||
),
|
||||
403: t.Object(
|
||||
{
|
||||
data: t.Null(),
|
||||
success: t.Boolean({ default: false }),
|
||||
message: t.String({ default: "Forbidden" }),
|
||||
error: t.String({
|
||||
default: "User does not have permission to access this resource",
|
||||
}),
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Forbidden. You do not have permission to access this resource or to perform this action.",
|
||||
}
|
||||
),
|
||||
404: t.Object(
|
||||
{
|
||||
data: t.Null(),
|
||||
success: t.Boolean({ default: false }),
|
||||
message: t.String({ default: "Not Found" }),
|
||||
error: t.String({ default: "Requested resource has not found" }),
|
||||
},
|
||||
{
|
||||
description: "Not Found. The requested resource was not found.",
|
||||
}
|
||||
),
|
||||
429: t.Object(
|
||||
{
|
||||
data: t.Null(),
|
||||
success: t.Boolean({ default: false }),
|
||||
message: t.String({ default: "Too Many Requests" }),
|
||||
error: t.String({
|
||||
default: "RUser has exceeded the rate limit. Try again later.",
|
||||
}),
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Too Many Requests. You have exceeded the rate limit. Try again later.",
|
||||
}
|
||||
),
|
||||
500: t.Object(
|
||||
{
|
||||
data: t.Null(),
|
||||
success: t.Boolean({ default: false }),
|
||||
message: t.String({ default: "Internal Server Error" }),
|
||||
error: t.String({ default: "Server faced an error" }),
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Internal Server Error. This is a problem with the server that you cannot fix.",
|
||||
}
|
||||
),
|
||||
};
|
||||
|
|
@ -1,15 +1,13 @@
|
|||
import { Session, User } from "better-auth/types";
|
||||
import { auth } from "../lib/auth/auth";
|
||||
import { error } from "elysia";
|
||||
|
||||
// user middleware (compute user and session and pass to routes)
|
||||
export const userMiddleware = async (request: Request) => {
|
||||
const session = await auth.api.getSession({ headers: request.headers });
|
||||
|
||||
if (!session) {
|
||||
return {
|
||||
user: null,
|
||||
session: null,
|
||||
};
|
||||
return error("Unauthorized", 401);
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue