bold-peaches-cheer/apps/server/dist/index.js
2026-05-13 12:22:02 +05:00

355 lines
15 KiB
JavaScript

// index.ts
import express from "express";
import cors from "cors";
import cookieParser from "cookie-parser";
import * as trpcExpress from "@trpc/server/adapters/express";
// trpc.ts
import { createQueryBuilder } from "@taylordb/query-builder";
import { initTRPC } from "@trpc/server";
var createContext = ({ req, res }) => {
const appAccessToken = req.cookies?.app_access_token;
if (!appAccessToken) {
throw new Error("Unauthorized: app_access_token cookie is required");
}
const queryBuilder = createQueryBuilder({
baseUrl: process.env.TAYLORDB_BASE_URL,
baseId: process.env.TAYLORDB_SERVER_ID,
apiKey: appAccessToken
});
return {
req,
res,
queryBuilder
};
};
var t = initTRPC.context().create();
var router = t.router;
var publicProcedure = t.procedure;
// routers/companies.ts
import { z } from "zod";
var companiesRouter = router({
getAll: publicProcedure.query(async ({ ctx }) => {
return await ctx.queryBuilder.selectFrom("companies").select(["id", "name", "website", "notes", "createdAt", "updatedAt"]).orderBy("createdAt", "desc").execute();
}),
search: publicProcedure.input(z.object({ query: z.string(), limit: z.number().default(5) })).query(async ({ ctx, input }) => {
const searchTerm = input.query.trim();
if (!searchTerm) {
return await ctx.queryBuilder.selectFrom("companies").select(["id", "name", "website"]).orderBy("createdAt", "desc").limit(input.limit).execute();
}
return await ctx.queryBuilder.selectFrom("companies").select(["id", "name", "website"]).where("name", "contains", searchTerm).orWhere("website", "contains", searchTerm).orderBy("createdAt", "desc").limit(input.limit).execute();
}),
getById: publicProcedure.input(z.object({ id: z.number() })).query(async ({ ctx, input }) => {
return await ctx.queryBuilder.selectFrom("companies").select(["id", "name", "website", "notes", "createdAt", "updatedAt"]).where("id", "=", input.id).executeTakeFirst();
}),
create: publicProcedure.input(
z.object({
name: z.string().min(1),
website: z.string().optional(),
notes: z.string().optional()
})
).mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder.insertInto("companies").values(input).executeTakeFirst();
}),
update: publicProcedure.input(
z.object({
id: z.number(),
name: z.string().optional(),
website: z.string().optional(),
notes: z.string().optional()
})
).mutation(async ({ ctx, input }) => {
const { id, ...data } = input;
return await ctx.queryBuilder.update("companies").set(data).where("id", "=", id).execute();
}),
delete: publicProcedure.input(z.object({ id: z.number() })).mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder.deleteFrom("companies").where("id", "=", input.id).execute();
})
});
// routers/contacts.ts
import { z as z2 } from "zod";
var contactsRouter = router({
getAll: publicProcedure.query(async ({ ctx }) => {
return await ctx.queryBuilder.selectFrom("contacts").select(["id", "firstName", "lastName", "mail", "phone1", "phone2", "notes", "createdAt", "updatedAt"]).orderBy("createdAt", "desc").execute();
}),
search: publicProcedure.input(z2.object({ query: z2.string(), limit: z2.number().default(5) })).query(async ({ ctx, input }) => {
const searchTerm = input.query.trim();
if (!searchTerm) {
return await ctx.queryBuilder.selectFrom("contacts").select(["id", "firstName", "lastName", "mail"]).orderBy("createdAt", "desc").limit(input.limit).execute();
}
return await ctx.queryBuilder.selectFrom("contacts").select(["id", "firstName", "lastName", "mail"]).where("firstName", "contains", searchTerm).orWhere("lastName", "contains", searchTerm).orWhere("mail", "contains", searchTerm).orderBy("createdAt", "desc").limit(input.limit).execute();
}),
getById: publicProcedure.input(z2.object({ id: z2.number() })).query(async ({ ctx, input }) => {
return await ctx.queryBuilder.selectFrom("contacts").select(["id", "firstName", "lastName", "mail", "phone1", "phone2", "notes", "createdAt", "updatedAt"]).with({ company: (qb) => qb.select(["id", "name", "website"]) }).where("id", "=", input.id).executeTakeFirst();
}),
create: publicProcedure.input(
z2.object({
firstName: z2.string().min(1),
lastName: z2.string().min(1),
mail: z2.string().email().optional().or(z2.literal("")),
phone1: z2.string().optional(),
phone2: z2.string().optional(),
notes: z2.string().optional(),
company: z2.array(z2.number()).optional()
})
).mutation(async ({ ctx, input }) => {
const values = {
...input,
mail: input.mail || void 0
};
return await ctx.queryBuilder.insertInto("contacts").values(values).executeTakeFirst();
}),
update: publicProcedure.input(
z2.object({
id: z2.number(),
firstName: z2.string().optional(),
lastName: z2.string().optional(),
mail: z2.string().email().optional().or(z2.literal("")),
phone1: z2.string().optional(),
phone2: z2.string().optional(),
notes: z2.string().optional(),
company: z2.array(z2.number()).optional()
})
).mutation(async ({ ctx, input }) => {
const { id, ...data } = input;
const updateData = Object.fromEntries(
Object.entries(data).filter(([key, v]) => {
if (v === void 0) return false;
if (key === "mail" && v === "") return false;
return true;
})
);
return await ctx.queryBuilder.update("contacts").set(updateData).where("id", "=", id).execute();
}),
delete: publicProcedure.input(z2.object({ id: z2.number() })).mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder.deleteFrom("contacts").where("id", "=", input.id).execute();
})
});
// routers/deals.ts
import { z as z3 } from "zod";
var DealsPhaseOptions = [
"New",
"Demo scheduled",
"Contract signed",
"Lost"
];
var phaseEnum = z3.enum(DealsPhaseOptions);
var dealsRouter = router({
getAll: publicProcedure.query(async ({ ctx }) => {
return await ctx.queryBuilder.selectFrom("deals").select(["id", "name", "phase", "createdAt", "updatedAt"]).with({
mainContact: (qb) => qb.select(["id", "firstName", "lastName", "mail"])
}).orderBy("createdAt", "desc").execute();
}),
getById: publicProcedure.input(z3.object({ id: z3.number() })).query(async ({ ctx, input }) => {
const deal = await ctx.queryBuilder.selectFrom("deals").select(["id", "name", "phase", "createdAt", "updatedAt"]).with({ mainContact: (qb) => qb.select(["id", "firstName", "lastName", "mail"]) }).where("id", "=", input.id).executeTakeFirst();
return deal;
}),
search: publicProcedure.input(z3.object({ query: z3.string(), limit: z3.number().optional() })).query(async ({ ctx, input }) => {
let query = ctx.queryBuilder.selectFrom("deals").select(["id", "name", "phase"]);
if (input.query.trim()) {
query = query.where("name", "contains", input.query);
}
return await query.orderBy("name", "asc").execute();
}),
// Get detailed deal info with contact and company for details page
getDetails: publicProcedure.input(z3.object({ id: z3.number() })).query(async ({ ctx, input }) => {
const deal = await ctx.queryBuilder.selectFrom("deals").select(["id", "name", "phase", "lostReason", "createdAt", "updatedAt"]).with({
mainContact: (qb) => qb.select(["id", "firstName", "lastName", "mail", "phone1", "phone2", "notes"]).with({ company: (companyQb) => companyQb.select(["id", "name", "website", "notes"]) })
}).with({
activities: (qb) => qb.select(["id", "name", "type", "plannedAt", "doneAt", "createdAt"]).with({ notes: (notesQb) => notesQb.select(["id", "name", "createdAt"]) })
}).where("id", "=", input.id).executeTakeFirst();
return deal;
}),
create: publicProcedure.input(
z3.object({
name: z3.string().min(1),
phase: phaseEnum.default("New"),
mainContact: z3.array(z3.number()).optional()
})
).mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder.insertInto("deals").values(input).executeTakeFirst();
}),
update: publicProcedure.input(
z3.object({
id: z3.number(),
name: z3.string().optional(),
phase: phaseEnum.optional(),
mainContact: z3.array(z3.number()).optional()
})
).mutation(async ({ ctx, input }) => {
const { id, ...data } = input;
const updateData = Object.fromEntries(
Object.entries(data).filter(([, v]) => v !== void 0)
);
return await ctx.queryBuilder.update("deals").set(updateData).where("id", "=", id).execute();
}),
delete: publicProcedure.input(z3.object({ id: z3.number() })).mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder.deleteFrom("deals").where("id", "=", input.id).execute();
})
});
// routers/activities.ts
import { z as z4 } from "zod";
var ActivitiesTypeOptions = [
"Call",
"Meeting",
"Task",
"Demo"
];
var activitiesRouter = router({
getAll: publicProcedure.input(
z4.object({
dateFilter: z4.enum(["all", "today", "thisWeek", "thisMonth"]).optional(),
typeFilter: z4.enum(["all", ...ActivitiesTypeOptions]).optional()
}).optional()
).query(async ({ ctx, input }) => {
let query = ctx.queryBuilder.selectFrom("activities").select(["id", "name", "type", "plannedAt", "doneAt", "createdAt", "updatedAt"]).with({ deal: (qb) => qb.select(["id", "name"]) }).with({ notes: (qb) => qb.select(["id", "name"]) });
if (input?.dateFilter && input.dateFilter !== "all") {
if (input.dateFilter === "today") {
query = query.where("plannedAt", "=", "today");
} else if (input.dateFilter === "thisWeek") {
query = query.where("plannedAt", "isWithIn", "currentWeek");
} else if (input.dateFilter === "thisMonth") {
query = query.where("plannedAt", "isWithIn", "currentMonth");
}
}
if (input?.typeFilter && input.typeFilter !== "all") {
query = query.where("type", "=", input.typeFilter);
}
return await query.orderBy("plannedAt", "desc").execute();
}),
getByDeal: publicProcedure.input(z4.object({ dealId: z4.number() })).query(async ({ ctx, input }) => {
return await ctx.queryBuilder.selectFrom("activities").select(["id", "name", "type", "plannedAt", "doneAt", "createdAt", "updatedAt"]).with({ notes: (qb) => qb.select(["id", "name"]) }).where("deal", "=", input.dealId).orderBy("plannedAt", "asc").execute();
}),
getById: publicProcedure.input(z4.object({ id: z4.number() })).query(async ({ ctx, input }) => {
return await ctx.queryBuilder.selectFrom("activities").select(["id", "name", "type", "plannedAt", "doneAt", "createdAt", "updatedAt"]).with({ deal: (qb) => qb.select(["id", "name"]) }).with({ notes: (qb) => qb.select(["id", "name"]) }).where("id", "=", input.id).executeTakeFirst();
}),
create: publicProcedure.input(
z4.object({
name: z4.string().min(1),
deal: z4.array(z4.number()).optional(),
plannedAt: z4.string().optional(),
doneAt: z4.string().optional(),
type: z4.enum(ActivitiesTypeOptions).optional()
})
).mutation(async ({ ctx, input }) => {
const values = {
name: input.name
};
if (input.deal) values.deal = input.deal;
if (input.plannedAt) values.plannedAt = input.plannedAt;
if (input.doneAt) values.doneAt = input.doneAt;
if (input.type) values.type = input.type;
return await ctx.queryBuilder.insertInto("activities").values(values).executeTakeFirst();
}),
update: publicProcedure.input(
z4.object({
id: z4.number(),
name: z4.string().optional(),
plannedAt: z4.string().optional(),
doneAt: z4.string().optional(),
notes: z4.array(z4.number()).optional(),
type: z4.enum(ActivitiesTypeOptions).optional()
})
).mutation(async ({ ctx, input }) => {
const { id, ...data } = input;
const updateData = Object.fromEntries(
Object.entries(data).filter(([, v]) => v !== void 0)
);
return await ctx.queryBuilder.update("activities").set(updateData).where("id", "=", id).execute();
}),
complete: publicProcedure.input(z4.object({ id: z4.number() })).mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder.update("activities").set({ doneAt: (/* @__PURE__ */ new Date()).toISOString() }).where("id", "=", input.id).execute();
}),
uncomplete: publicProcedure.input(z4.object({ id: z4.number() })).mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder.update("activities").set({ doneAt: null }).where("id", "=", input.id).execute();
}),
delete: publicProcedure.input(z4.object({ id: z4.number() })).mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder.deleteFrom("activities").where("id", "=", input.id).execute();
})
});
// routers/notes.ts
import { z as z5 } from "zod";
var notesRouter = router({
getByActivity: publicProcedure.input(z5.object({ activityId: z5.number() })).query(async ({ ctx, input }) => {
return await ctx.queryBuilder.selectFrom("notes").select(["id", "name", "createdAt", "updatedAt"]).where("activities1", "=", input.activityId).orderBy("createdAt", "desc").execute();
}),
getById: publicProcedure.input(z5.object({ id: z5.number() })).query(async ({ ctx, input }) => {
return await ctx.queryBuilder.selectFrom("notes").select(["id", "name", "createdAt", "updatedAt"]).where("id", "=", input.id).executeTakeFirst();
}),
create: publicProcedure.input(
z5.object({
name: z5.string().min(1),
activities1: z5.array(z5.number())
})
).mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder.insertInto("notes").values(input).executeTakeFirst();
}),
update: publicProcedure.input(
z5.object({
id: z5.number(),
name: z5.string().optional()
})
).mutation(async ({ ctx, input }) => {
const { id, ...data } = input;
const updateData = Object.fromEntries(
Object.entries(data).filter(([, v]) => v !== void 0)
);
return await ctx.queryBuilder.update("notes").set(updateData).where("id", "=", id).execute();
}),
delete: publicProcedure.input(z5.object({ id: z5.number() })).mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder.deleteFrom("notes").where("id", "=", input.id).execute();
})
});
// router.ts
var appRouter = router({
companies: companiesRouter,
contacts: contactsRouter,
deals: dealsRouter,
activities: activitiesRouter,
notes: notesRouter
});
// index.ts
var app = express();
var PORT = process.env.PORT || 3001;
app.use(cookieParser());
app.use(
cors({
origin: [
process.env.FRONTEND_URL || "http://localhost:5173",
"http://localhost:5174"
],
credentials: true
})
);
app.get("/api/health", (_req, res) => {
res.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
});
app.use(
"/api/trpc",
(req, res, next) => {
if (req.path === "/" || req.path === "") {
return res.json({
message: "TaylorDB tRPC server is running!",
health: `http://${req.headers.host}/api/trpc/health`,
timestamp: (/* @__PURE__ */ new Date()).toISOString()
});
}
next();
},
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext
})
);
app.listen(PORT, () => {
console.log(`\u{1F680} Server running on http://localhost:${PORT}`);
console.log(`\u{1F4E1} tRPC endpoint: http://localhost:${PORT}/api/trpc`);
});