40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
|
|
import { desc, eq } from "drizzle-orm";
|
||
|
|
|
||
|
|
import { router, protectedProcedure } from "@/server/trpc";
|
||
|
|
import { userEmailIdentities } from "@tasks/database/schema";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Procedures backing the "Linked emails" surface on the profile page and
|
||
|
|
* (eventually) the manual-verification flow. Read-only in v1.
|
||
|
|
*
|
||
|
|
* Everything is protected — only the authenticated user can see their own
|
||
|
|
* identities. We do not expose any cross-user identity lookup here; that
|
||
|
|
* surface (auto-suggest invitees by typing a name) lives in `invites.ts`
|
||
|
|
* with its own tenancy fence.
|
||
|
|
*/
|
||
|
|
export const identityRouter = router({
|
||
|
|
listMine: protectedProcedure.query(async ({ ctx }) => {
|
||
|
|
const userId = ctx.session!.user.id;
|
||
|
|
|
||
|
|
const rows = await ctx.db
|
||
|
|
.select({
|
||
|
|
id: userEmailIdentities.id,
|
||
|
|
email: userEmailIdentities.email,
|
||
|
|
source: userEmailIdentities.source,
|
||
|
|
verifiedAt: userEmailIdentities.verifiedAt,
|
||
|
|
createdAt: userEmailIdentities.createdAt,
|
||
|
|
lastUsedAt: userEmailIdentities.lastUsedAt,
|
||
|
|
})
|
||
|
|
.from(userEmailIdentities)
|
||
|
|
.where(eq(userEmailIdentities.userId, userId))
|
||
|
|
.orderBy(desc(userEmailIdentities.verifiedAt));
|
||
|
|
|
||
|
|
return rows.map((row) => ({
|
||
|
|
...row,
|
||
|
|
verified: row.verifiedAt !== null,
|
||
|
|
}));
|
||
|
|
}),
|
||
|
|
});
|
||
|
|
|
||
|
|
export type IdentityRouter = typeof identityRouter;
|