added remaining user api

This commit is contained in:
2024-04-19 15:05:01 +07:00
parent a3f61b35c1
commit e0ec4c2b49
7 changed files with 83 additions and 443 deletions

View File

@@ -1,10 +1,15 @@
version: "3" version: "3"
tasks: tasks:
generate: db:generate:
cmds: cmds:
- pnpm drizzle-kit generate:sqlite - pnpm drizzle-kit generate:sqlite
db:push:
cmds:
- pnpm drizzle-kit push:sqlite
db:drop:
cmds:
- pnpm drizzle-kit drop
studio: studio:
cmds: cmds:
- pnpm drizzle-kit studio - pnpm drizzle-kit studio

View File

@@ -1,56 +0,0 @@
CREATE TABLE `groups` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`name` text
);
--> statement-breakpoint
CREATE TABLE `opinions` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`name` text,
`type` text
);
--> statement-breakpoint
CREATE TABLE `phone_tokens` (
`phone` text PRIMARY KEY NOT NULL,
`token` text NOT NULL,
`created_on` integer DEFAULT CURRENT_TIMESTAMP
);
--> statement-breakpoint
CREATE TABLE `provinces` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`name` text NOT NULL
);
--> statement-breakpoint
CREATE TABLE `users` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`firstName` text NOT NULL,
`lastName` text NOT NULL,
`title` text NOT NULL,
`phone` text NOT NULL,
`email` text,
`job` text NOT NULL,
`education` text NOT NULL,
`vision` text,
`reason` text,
`group_id` integer NOT NULL,
`zone_id` integer NOT NULL,
FOREIGN KEY (`group_id`) REFERENCES `groups`(`id`) ON UPDATE no action ON DELETE no action,
FOREIGN KEY (`zone_id`) REFERENCES `zones`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `user_opinions` (
`user_id` integer,
`opinion_id` integer,
`choice` text DEFAULT 'ignore',
PRIMARY KEY(`opinion_id`, `user_id`),
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action,
FOREIGN KEY (`opinion_id`) REFERENCES `opinions`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `zones` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`name` text NOT NULL,
`province_id` integer NOT NULL,
FOREIGN KEY (`province_id`) REFERENCES `provinces`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE UNIQUE INDEX `users_phone_unique` ON `users` (`phone`);

View File

@@ -1,359 +0,0 @@
{
"version": "5",
"dialect": "sqlite",
"id": "86633c77-c16c-46e0-92a5-f0e696d3276c",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"groups": {
"name": "groups",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"opinions": {
"name": "opinions",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"phone_tokens": {
"name": "phone_tokens",
"columns": {
"phone": {
"name": "phone",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"token": {
"name": "token",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"created_on": {
"name": "created_on",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"provinces": {
"name": "provinces",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"firstName": {
"name": "firstName",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"lastName": {
"name": "lastName",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"phone": {
"name": "phone",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"job": {
"name": "job",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"education": {
"name": "education",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"vision": {
"name": "vision",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"reason": {
"name": "reason",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"group_id": {
"name": "group_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"zone_id": {
"name": "zone_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"users_phone_unique": {
"name": "users_phone_unique",
"columns": [
"phone"
],
"isUnique": true
}
},
"foreignKeys": {
"users_group_id_groups_id_fk": {
"name": "users_group_id_groups_id_fk",
"tableFrom": "users",
"tableTo": "groups",
"columnsFrom": [
"group_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"users_zone_id_zones_id_fk": {
"name": "users_zone_id_zones_id_fk",
"tableFrom": "users",
"tableTo": "zones",
"columnsFrom": [
"zone_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"user_opinions": {
"name": "user_opinions",
"columns": {
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"opinion_id": {
"name": "opinion_id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'ignore'"
}
},
"indexes": {},
"foreignKeys": {
"user_opinions_user_id_users_id_fk": {
"name": "user_opinions_user_id_users_id_fk",
"tableFrom": "user_opinions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"user_opinions_opinion_id_opinions_id_fk": {
"name": "user_opinions_opinion_id_opinions_id_fk",
"tableFrom": "user_opinions",
"tableTo": "opinions",
"columnsFrom": [
"opinion_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"user_opinions_user_id_opinion_id_pk": {
"columns": [
"opinion_id",
"user_id"
],
"name": "user_opinions_user_id_opinion_id_pk"
}
},
"uniqueConstraints": {}
},
"zones": {
"name": "zones",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"province_id": {
"name": "province_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"zones_province_id_provinces_id_fk": {
"name": "zones_province_id_provinces_id_fk",
"tableFrom": "zones",
"tableTo": "provinces",
"columnsFrom": [
"province_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View File

@@ -1,13 +1 @@
{ {"version":"5","dialect":"sqlite","entries":[]}
"version": "5",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1713506374595,
"tag": "0000_hot_gressill",
"breakpoints": true
}
]
}

View File

@@ -1,6 +1,5 @@
import { createContext, router } from "./trpc"; import { createContext, router } from "./trpc";
import { createHTTPServer } from "@trpc/server/adapters/standalone"; import { createHTTPServer } from "@trpc/server/adapters/standalone";
import { db } from "./db";
import { userRoute } from "./userRoute"; import { userRoute } from "./userRoute";
import { runPlayground } from "./playgroud"; import { runPlayground } from "./playgroud";
import cors from "cors"; import cors from "cors";

View File

@@ -41,7 +41,7 @@ export const userRelation = relations(user, ({ many, one }) => ({
//----------------Group //----------------Group
export const group = sqliteTable("groups", { export const group = sqliteTable("groups", {
id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }), id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
name: text("name"), name: text("name").notNull(),
}); });
export const groupRelation = relations(group, ({ many }) => ({ export const groupRelation = relations(group, ({ many }) => ({
@@ -51,19 +51,25 @@ export const groupRelation = relations(group, ({ many }) => ({
//----------------Opinion //----------------Opinion
export const opinion = sqliteTable("opinions", { export const opinion = sqliteTable("opinions", {
id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }), id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
name: text("name"), name: text("name").notNull(),
type: text("type", { enum: ["3Choice", "4Choice"] }), type: text("type", { enum: ["3Choice", "4Choice"] })
.default("3Choice")
.notNull(),
}); });
//----------------UserOpinion //----------------UserOpinion
export const userOpinion = sqliteTable( export const userOpinion = sqliteTable(
"user_opinions", "user_opinions",
{ {
userId: integer("user_id").references(() => user.id), userId: integer("user_id")
opinionId: integer("opinion_id").references(() => opinion.id), .notNull()
.references(() => user.id),
opinionId: integer("opinion_id")
.notNull()
.references(() => opinion.id),
choice: text("choice", { choice: text("choice", {
enum: ["agree", "disagree", "deciding", "ignore"], enum: ["agree", "disagree", "deciding", "ignore"],
}).default("ignore"), }).default("deciding"),
}, },
(t) => ({ (t) => ({
pk: primaryKey({ columns: [t.userId, t.opinionId] }), pk: primaryKey({ columns: [t.userId, t.opinionId] }),

View File

@@ -5,7 +5,7 @@ import {
protectedProcedure, protectedProcedure,
} from "./trpc"; } from "./trpc";
import { db } from "./db"; import { db } from "./db";
import { phoneToken, user } from "./schema"; import { phoneToken, user, userOpinion } from "./schema";
import { createInsertSchema } from "drizzle-zod"; import { createInsertSchema } from "drizzle-zod";
import { z } from "zod"; import { z } from "zod";
import { SQL, eq } from "drizzle-orm"; import { SQL, eq } from "drizzle-orm";
@@ -13,6 +13,19 @@ import { Config } from "./config";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
const userInsertSchema = createInsertSchema(user); const userInsertSchema = createInsertSchema(user);
const opinionInsertSchema = createInsertSchema(userOpinion)
.omit({
userId: true,
})
.array()
.default([]);
const opinionUpdateSchema = createInsertSchema(userOpinion)
.omit({
userId: true,
})
.required({ opinionId: true });
type OpinionInsertSchema = z.infer<typeof opinionInsertSchema>;
type UserInsertSchema = z.infer<typeof userInsertSchema>; type UserInsertSchema = z.infer<typeof userInsertSchema>;
export const userRoute = router({ export const userRoute = router({
@@ -30,9 +43,14 @@ export const userRoute = router({
await getAllUser(input.offset, input.limit, input.group, input.zone) await getAllUser(input.offset, input.limit, input.group, input.zone)
), ),
createUser: verifiedPhone createUser: verifiedPhone
.input(userInsertSchema.omit({ id: true, phone: true })) .input(
userInsertSchema.omit({ id: true, phone: true }).extend({
opinions: opinionInsertSchema,
})
)
.mutation( .mutation(
async ({ input, ctx }) => await createUser({ ...input, phone: ctx.phone }) async ({ input, ctx }) =>
await createUser({ ...input, phone: ctx.phone }, input.opinions)
), ),
requestOtp: publicProcedure requestOtp: publicProcedure
.input(z.object({ phone: z.string().trim().min(5) })) .input(z.object({ phone: z.string().trim().min(5) }))
@@ -45,6 +63,12 @@ export const userRoute = router({
}) })
) )
.mutation(async ({ input }) => await verifyOtp(input.token, input.pin)), .mutation(async ({ input }) => await verifyOtp(input.token, input.pin)),
changeOpinion: protectedProcedure
.input(opinionUpdateSchema)
.mutation(
async ({ input, ctx }) =>
await changeOpinion(input.opinionId, ctx.user.id, input.choice)
),
}); });
async function getAllUser( async function getAllUser(
@@ -76,10 +100,18 @@ async function getAllUser(
}); });
return users; return users;
} }
async function createUser(newUser: UserInsertSchema) { async function createUser(
newUser: UserInsertSchema,
opinions: OpinionInsertSchema
) {
try { try {
let result = await db.insert(user).values(newUser); let result = (
return result.lastInsertRowid; await db.insert(user).values(newUser).returning({ id: user.id })
)[0];
for (let op of opinions) {
await db.insert(userOpinion).values({ ...op, userId: result.id });
}
return { status: "OK" };
} catch (e) { } catch (e) {
console.error(e); console.error(e);
throw new Error(`Unable to create new user:\n${e}`); throw new Error(`Unable to create new user:\n${e}`);
@@ -168,3 +200,28 @@ async function verifyOtp(token: string, pin: string) {
}); });
} }
} }
async function changeOpinion(
opinionId: number,
userId: number,
opinion: OpinionInsertSchema[0]["choice"]
) {
try {
db.insert(userOpinion)
.values({
opinionId,
userId,
choice: opinion,
})
.onConflictDoUpdate({
target: [userOpinion.userId, userOpinion.opinionId],
set: { choice: opinion },
});
} catch (e) {
console.error(e);
throw new TRPCError({
message: "Error updating schema",
code: "INTERNAL_SERVER_ERROR",
});
}
}