added playground and userroute
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
sqlite.db
|
sqlite.db
|
||||||
|
.DS_Store
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ CREATE TABLE `opinions` (
|
|||||||
`type` text
|
`type` text
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> 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` (
|
CREATE TABLE `provinces` (
|
||||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
`name` text NOT NULL
|
`name` text NOT NULL
|
||||||
@@ -16,21 +22,26 @@ CREATE TABLE `provinces` (
|
|||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
CREATE TABLE `users` (
|
CREATE TABLE `users` (
|
||||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
`name` text,
|
`firstName` text NOT NULL,
|
||||||
|
`lastName` text NOT NULL,
|
||||||
|
`title` text NOT NULL,
|
||||||
`phone` text NOT NULL,
|
`phone` text NOT NULL,
|
||||||
`email` text,
|
`email` text,
|
||||||
`job` text,
|
`job` text NOT NULL,
|
||||||
`education` text,
|
`education` text NOT NULL,
|
||||||
`vision` text,
|
`vision` text,
|
||||||
`reason` text,
|
`reason` text,
|
||||||
`group_id` integer,
|
`group_id` integer NOT NULL,
|
||||||
FOREIGN KEY (`group_id`) REFERENCES `groups`(`id`) ON UPDATE no action ON DELETE no action
|
`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
|
--> statement-breakpoint
|
||||||
CREATE TABLE `user_opinions` (
|
CREATE TABLE `user_opinions` (
|
||||||
`user_id` integer,
|
`user_id` integer,
|
||||||
`opinion_id` integer,
|
`opinion_id` integer,
|
||||||
`choice` text DEFAULT 'ignore',
|
`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 (`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
|
FOREIGN KEY (`opinion_id`) REFERENCES `opinions`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
);
|
);
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": "5",
|
"version": "5",
|
||||||
"dialect": "sqlite",
|
"dialect": "sqlite",
|
||||||
"id": "71187b23-6b9a-4f78-9f3c-0af6fb035f1b",
|
"id": "86633c77-c16c-46e0-92a5-f0e696d3276c",
|
||||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
"tables": {
|
"tables": {
|
||||||
"groups": {
|
"groups": {
|
||||||
@@ -57,6 +57,37 @@
|
|||||||
"compositePrimaryKeys": {},
|
"compositePrimaryKeys": {},
|
||||||
"uniqueConstraints": {}
|
"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": {
|
"provinces": {
|
||||||
"name": "provinces",
|
"name": "provinces",
|
||||||
"columns": {
|
"columns": {
|
||||||
@@ -90,11 +121,25 @@
|
|||||||
"notNull": true,
|
"notNull": true,
|
||||||
"autoincrement": true
|
"autoincrement": true
|
||||||
},
|
},
|
||||||
"name": {
|
"firstName": {
|
||||||
"name": "name",
|
"name": "firstName",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": 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
|
"autoincrement": false
|
||||||
},
|
},
|
||||||
"phone": {
|
"phone": {
|
||||||
@@ -115,14 +160,14 @@
|
|||||||
"name": "job",
|
"name": "job",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false,
|
"notNull": true,
|
||||||
"autoincrement": false
|
"autoincrement": false
|
||||||
},
|
},
|
||||||
"education": {
|
"education": {
|
||||||
"name": "education",
|
"name": "education",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false,
|
"notNull": true,
|
||||||
"autoincrement": false
|
"autoincrement": false
|
||||||
},
|
},
|
||||||
"vision": {
|
"vision": {
|
||||||
@@ -143,7 +188,14 @@
|
|||||||
"name": "group_id",
|
"name": "group_id",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false,
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"zone_id": {
|
||||||
|
"name": "zone_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
"autoincrement": false
|
"autoincrement": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -169,6 +221,19 @@
|
|||||||
],
|
],
|
||||||
"onDelete": "no action",
|
"onDelete": "no action",
|
||||||
"onUpdate": "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": {},
|
"compositePrimaryKeys": {},
|
||||||
@@ -229,7 +294,15 @@
|
|||||||
"onUpdate": "no action"
|
"onUpdate": "no action"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"compositePrimaryKeys": {},
|
"compositePrimaryKeys": {
|
||||||
|
"user_opinions_user_id_opinion_id_pk": {
|
||||||
|
"columns": [
|
||||||
|
"opinion_id",
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"name": "user_opinions_user_id_opinion_id_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
"uniqueConstraints": {}
|
"uniqueConstraints": {}
|
||||||
},
|
},
|
||||||
"zones": {
|
"zones": {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
{
|
{
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"version": "5",
|
"version": "5",
|
||||||
"when": 1713465576653,
|
"when": 1713506374595,
|
||||||
"tag": "0000_aspiring_paibok",
|
"tag": "0000_hot_gressill",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -11,10 +11,15 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@trpc/client": "11.0.0-rc.340",
|
"@trpc/client": "^10.45.2",
|
||||||
"@trpc/server": "11.0.0-rc.340",
|
"@trpc/server": "^10.45.2",
|
||||||
"better-sqlite3": "^9.5.0",
|
"better-sqlite3": "^9.5.0",
|
||||||
"drizzle-orm": "^0.30.8"
|
"cors": "^2.8.5",
|
||||||
|
"drizzle-orm": "^0.30.8",
|
||||||
|
"drizzle-zod": "^0.5.1",
|
||||||
|
"express": "^4.19.2",
|
||||||
|
"trpc-playground": "^1.0.4",
|
||||||
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@swc/core": "^1.4.16",
|
"@swc/core": "^1.4.16",
|
||||||
|
|||||||
721
pnpm-lock.yaml
generated
721
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
31
src/app.ts
31
src/app.ts
@@ -1,21 +1,24 @@
|
|||||||
import { drizzle } from "drizzle-orm/better-sqlite3";
|
import { createContext, router } from "./trpc";
|
||||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
import { createHTTPServer } from "@trpc/server/adapters/standalone";
|
||||||
import Database from "better-sqlite3";
|
import { db } from "./db";
|
||||||
import { user } from "./schema.ts";
|
import { userRoute } from "./userRoute";
|
||||||
|
import { runPlayground } from "./playgroud";
|
||||||
|
import cors from "cors";
|
||||||
|
|
||||||
const sqlite = new Database("sqlite.db");
|
export const appRouter = router({
|
||||||
const db = drizzle(sqlite);
|
user: userRoute,
|
||||||
migrate(db, { migrationsFolder: "drizzle" });
|
});
|
||||||
|
export type AppRouter = typeof appRouter;
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
await db.insert(user).values({
|
const server = createHTTPServer({
|
||||||
job: "Software Engineer",
|
createContext: createContext,
|
||||||
phone: "1234567890",
|
router: appRouter,
|
||||||
group: 1,
|
middleware: cors(),
|
||||||
name: "John Doe",
|
|
||||||
education: "Bachelor",
|
|
||||||
});
|
});
|
||||||
console.log("User inserted");
|
|
||||||
|
server.listen(3000);
|
||||||
|
runPlayground(appRouter);
|
||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|||||||
6
src/config.ts
Normal file
6
src/config.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export const Config = {
|
||||||
|
sms_api_key: "1796570121771765",
|
||||||
|
sms_api_secret: "0957b611d575febff1ae0fc51070c8b7",
|
||||||
|
sms_api_request_endpoint: "https://otp.thaibulksms.com/v2/otp/request",
|
||||||
|
sms_api_verify_endpoint: "https://otp.thaibulksms.com/v2/otp/verify",
|
||||||
|
};
|
||||||
8
src/db.ts
Normal file
8
src/db.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
||||||
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||||
|
import Database from "better-sqlite3";
|
||||||
|
import * as schema from "./schema.ts";
|
||||||
|
|
||||||
|
const sqlite = new Database("sqlite.db");
|
||||||
|
export const db = drizzle(sqlite, { schema });
|
||||||
|
migrate(db, { migrationsFolder: "drizzle" });
|
||||||
23
src/playgroud.ts
Normal file
23
src/playgroud.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import express from "express";
|
||||||
|
import { expressHandler } from "trpc-playground/handlers/express";
|
||||||
|
import { AppRouter } from "./app";
|
||||||
|
|
||||||
|
export const runPlayground = async (appRouter: AppRouter) => {
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const trpcApiEndpoint = "http://localhost:3000";
|
||||||
|
const playgroundEndpoint = "/";
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
playgroundEndpoint,
|
||||||
|
await expressHandler({
|
||||||
|
trpcApiEndpoint,
|
||||||
|
playgroundEndpoint,
|
||||||
|
router: appRouter,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
app.listen(3001, () => {
|
||||||
|
console.log("listening at http://localhost:3001");
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -2,14 +2,16 @@ import {
|
|||||||
sqliteTable,
|
sqliteTable,
|
||||||
text,
|
text,
|
||||||
integer,
|
integer,
|
||||||
uniqueIndex,
|
primaryKey,
|
||||||
} from "drizzle-orm/sqlite-core";
|
} from "drizzle-orm/sqlite-core";
|
||||||
import { relations } from "drizzle-orm";
|
import { relations, sql } from "drizzle-orm";
|
||||||
|
|
||||||
//----------------User
|
//----------------User
|
||||||
export const user = sqliteTable("users", {
|
export const user = sqliteTable("users", {
|
||||||
id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
|
id: integer("id", { mode: "number" }).primaryKey({ autoIncrement: true }),
|
||||||
name: text("name").notNull(),
|
firstName: text("firstName").notNull(),
|
||||||
|
lastName: text("lastName").notNull(),
|
||||||
|
title: text("title").notNull(),
|
||||||
phone: text("phone").unique().notNull(),
|
phone: text("phone").unique().notNull(),
|
||||||
email: text("email"),
|
email: text("email"),
|
||||||
job: text("job").notNull(),
|
job: text("job").notNull(),
|
||||||
@@ -19,6 +21,9 @@ export const user = sqliteTable("users", {
|
|||||||
group: integer("group_id")
|
group: integer("group_id")
|
||||||
.references(() => group.id)
|
.references(() => group.id)
|
||||||
.notNull(),
|
.notNull(),
|
||||||
|
zone: integer("zone_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => zone.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userRelation = relations(user, ({ many, one }) => ({
|
export const userRelation = relations(user, ({ many, one }) => ({
|
||||||
@@ -27,6 +32,10 @@ export const userRelation = relations(user, ({ many, one }) => ({
|
|||||||
fields: [user.group],
|
fields: [user.group],
|
||||||
references: [group.id],
|
references: [group.id],
|
||||||
}),
|
}),
|
||||||
|
zone: one(zone, {
|
||||||
|
fields: [user.zone],
|
||||||
|
references: [zone.id],
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//----------------Group
|
//----------------Group
|
||||||
@@ -47,13 +56,19 @@ export const opinion = sqliteTable("opinions", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//----------------UserOpinion
|
//----------------UserOpinion
|
||||||
export const userOpinion = sqliteTable("user_opinions", {
|
export const userOpinion = sqliteTable(
|
||||||
|
"user_opinions",
|
||||||
|
{
|
||||||
userId: integer("user_id").references(() => user.id),
|
userId: integer("user_id").references(() => user.id),
|
||||||
opinionId: integer("opinion_id").references(() => opinion.id),
|
opinionId: integer("opinion_id").references(() => opinion.id),
|
||||||
choice: text("choice", {
|
choice: text("choice", {
|
||||||
enum: ["agree", "disagree", "deciding", "ignore"],
|
enum: ["agree", "disagree", "deciding", "ignore"],
|
||||||
}).default("ignore"),
|
}).default("ignore"),
|
||||||
});
|
},
|
||||||
|
(t) => ({
|
||||||
|
pk: primaryKey({ columns: [t.userId, t.opinionId] }),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
export const userOpinionRelation = relations(userOpinion, ({ one }) => ({
|
export const userOpinionRelation = relations(userOpinion, ({ one }) => ({
|
||||||
user: one(user, {
|
user: one(user, {
|
||||||
@@ -84,5 +99,14 @@ export const province = sqliteTable("provinces", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const provinceRelation = relations(province, ({ many }) => ({
|
export const provinceRelation = relations(province, ({ many }) => ({
|
||||||
province: many(zone),
|
zones: many(zone),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
//----------------PhoneToken
|
||||||
|
export const phoneToken = sqliteTable("phone_tokens", {
|
||||||
|
phone: text("phone").primaryKey(),
|
||||||
|
token: text("token").notNull(),
|
||||||
|
createdOn: integer("created_on", { mode: "timestamp" }).default(
|
||||||
|
sql`CURRENT_TIMESTAMP`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|||||||
59
src/trpc.ts
Normal file
59
src/trpc.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { initTRPC } from "@trpc/server";
|
||||||
|
import type { CreateHTTPContextOptions } from "@trpc/server/adapters/standalone";
|
||||||
|
import { db } from "./db";
|
||||||
|
const t = initTRPC.context<Context>().create();
|
||||||
|
|
||||||
|
export const router = t.router;
|
||||||
|
export const publicProcedure = t.procedure;
|
||||||
|
export const protectedProcedure = t.procedure.use(async (opts) => {
|
||||||
|
const { ctx } = opts;
|
||||||
|
if (ctx.user === undefined) {
|
||||||
|
throw new Error("Unauthorized");
|
||||||
|
} else {
|
||||||
|
return opts.next({
|
||||||
|
ctx: {
|
||||||
|
...ctx,
|
||||||
|
user: ctx.user,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
export const verifiedPhone = t.procedure.use(async (opts) => {
|
||||||
|
const { ctx } = opts;
|
||||||
|
if (ctx.phone === undefined) {
|
||||||
|
throw new Error("Unauthorized");
|
||||||
|
} else {
|
||||||
|
return opts.next({
|
||||||
|
ctx: {
|
||||||
|
phone: ctx.phone,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
type Context = Awaited<ReturnType<typeof createContext>>;
|
||||||
|
|
||||||
|
export const createContext = async (opts: CreateHTTPContextOptions) => {
|
||||||
|
const authorizationHeader = opts.req.headers.authorization || "";
|
||||||
|
const bearerToken = authorizationHeader.split(" ")[1];
|
||||||
|
const phone = verifyToken(bearerToken);
|
||||||
|
if (phone !== null) {
|
||||||
|
let user = await db.query.user.findFirst({
|
||||||
|
where: (user, { eq }) => eq(user.phone, phone),
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
phone,
|
||||||
|
user: user,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
phone: undefined,
|
||||||
|
user: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function verifyToken(token: string): string | null {
|
||||||
|
//TODO: Implement token verification
|
||||||
|
return "08999";
|
||||||
|
}
|
||||||
170
src/userRoute.ts
Normal file
170
src/userRoute.ts
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import {
|
||||||
|
router,
|
||||||
|
verifiedPhone,
|
||||||
|
publicProcedure,
|
||||||
|
protectedProcedure,
|
||||||
|
} from "./trpc";
|
||||||
|
import { db } from "./db";
|
||||||
|
import { phoneToken, user } from "./schema";
|
||||||
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { SQL, eq } from "drizzle-orm";
|
||||||
|
import { Config } from "./config";
|
||||||
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
|
const userInsertSchema = createInsertSchema(user);
|
||||||
|
type UserInsertSchema = z.infer<typeof userInsertSchema>;
|
||||||
|
|
||||||
|
export const userRoute = router({
|
||||||
|
getAllUser: publicProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
offset: z.number().default(0),
|
||||||
|
limit: z.number().max(50).default(10),
|
||||||
|
group: z.number().optional(),
|
||||||
|
zone: z.number().optional(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.query(
|
||||||
|
async ({ input }) =>
|
||||||
|
await getAllUser(input.offset, input.limit, input.group, input.zone)
|
||||||
|
),
|
||||||
|
createUser: verifiedPhone
|
||||||
|
.input(userInsertSchema.omit({ id: true, phone: true }))
|
||||||
|
.mutation(
|
||||||
|
async ({ input, ctx }) => await createUser({ ...input, phone: ctx.phone })
|
||||||
|
),
|
||||||
|
requestOtp: publicProcedure
|
||||||
|
.input(z.object({ phone: z.string().trim().min(5) }))
|
||||||
|
.mutation(async ({ input }) => await requestOtp(input.phone)),
|
||||||
|
verifyOtp: publicProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
pin: z.string().trim(),
|
||||||
|
token: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => await verifyOtp(input.token, input.pin)),
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getAllUser(
|
||||||
|
offset: number,
|
||||||
|
limit: number,
|
||||||
|
group?: number,
|
||||||
|
zone?: number
|
||||||
|
) {
|
||||||
|
let users = await db.query.user.findMany({
|
||||||
|
with: {
|
||||||
|
group: true,
|
||||||
|
opinions: true,
|
||||||
|
zone: {
|
||||||
|
with: { province: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
where: (user, { eq, and }) => {
|
||||||
|
let conditions: SQL[] = [];
|
||||||
|
if (group) {
|
||||||
|
conditions.push(eq(user.group, group));
|
||||||
|
}
|
||||||
|
if (zone) {
|
||||||
|
conditions.push(eq(user.zone, zone));
|
||||||
|
}
|
||||||
|
return and(...conditions);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
async function createUser(newUser: UserInsertSchema) {
|
||||||
|
try {
|
||||||
|
let result = await db.insert(user).values(newUser);
|
||||||
|
return result.lastInsertRowid;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
throw new Error(`Unable to create new user:\n${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function requestOtp(phone: string) {
|
||||||
|
const _phone = phone.trim();
|
||||||
|
try {
|
||||||
|
let rs = await fetch(Config.sms_api_request_endpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
key: Config.sms_api_key,
|
||||||
|
secret: Config.sms_api_secret,
|
||||||
|
msisdn: _phone,
|
||||||
|
}),
|
||||||
|
}).then((res) => res.json());
|
||||||
|
|
||||||
|
if (rs.errors) {
|
||||||
|
console.error(rs);
|
||||||
|
throw new TRPCError({
|
||||||
|
message: `Unable to request OTP`,
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await db.insert(phoneToken).values({ phone: _phone, token: rs.token });
|
||||||
|
return {
|
||||||
|
status: rs.status as string,
|
||||||
|
token: rs.token as string,
|
||||||
|
refno: rs.refno as string,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
throw new TRPCError({
|
||||||
|
message: `Unable to request OTP:\n${e}`,
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyOtp(token: string, pin: string) {
|
||||||
|
try {
|
||||||
|
console.log(token, pin);
|
||||||
|
let pt = await db.query.phoneToken.findFirst({
|
||||||
|
where: (pt, { eq }) => eq(pt.token, token),
|
||||||
|
orderBy: (pt, { desc }) => desc(pt.createdOn),
|
||||||
|
});
|
||||||
|
if (pt === undefined) {
|
||||||
|
throw new TRPCError({
|
||||||
|
message: `Invalid token`,
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pt;
|
||||||
|
let rs = await fetch(Config.sms_api_verify_endpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
key: Config.sms_api_key,
|
||||||
|
secret: Config.sms_api_secret,
|
||||||
|
token,
|
||||||
|
pin,
|
||||||
|
}),
|
||||||
|
}).then((res) => res.json());
|
||||||
|
if (rs.errors) {
|
||||||
|
console.error(rs);
|
||||||
|
throw new TRPCError({
|
||||||
|
message: `Unable to verify OTP`,
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await db.delete(phoneToken).where(eq(phoneToken.phone, pt.phone));
|
||||||
|
console.log(rs, pt.phone);
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
throw new TRPCError({
|
||||||
|
message: `Unable to verify OTP:\n${e}`,
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user