FRE-696 Wire up API client to mail/contact/attachment endpoints
- Create ProtonMail API client (src/lib/mail/protonmail-client.ts) - Add tRPC mail router with 8 endpoints (server/trpc/mail-router.ts) - Wire mail router into appRouter (server/trpc/index.ts) - Add module exports (src/lib/mail/index.ts) Endpoints: - mail.messages, mail.message, mail.send - mail.contact, mail.contacts, mail.addContact - mail.attachment, mail.attachmentDownload Router uses Zod validation and includes placeholders for ProtonMail API calls. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -4,6 +4,7 @@ import { revisionsRouter } from './revisions-router';
|
|||||||
import { scriptsRouter } from './scripts-router';
|
import { scriptsRouter } from './scripts-router';
|
||||||
import { waitlistRouter } from './waitlist-router';
|
import { waitlistRouter } from './waitlist-router';
|
||||||
import { betaRouter } from './beta-router';
|
import { betaRouter } from './beta-router';
|
||||||
|
import { mailRouter } from './mail-router';
|
||||||
import type { TRPCContext } from './types';
|
import type { TRPCContext } from './types';
|
||||||
import type { TRPCError } from '@trpc/server';
|
import type { TRPCError } from '@trpc/server';
|
||||||
import { t } from './router';
|
import { t } from './router';
|
||||||
@@ -15,6 +16,7 @@ export const appRouter = t.router({
|
|||||||
scripts: scriptsRouter,
|
scripts: scriptsRouter,
|
||||||
waitlist: waitlistRouter,
|
waitlist: waitlistRouter,
|
||||||
beta: betaRouter,
|
beta: betaRouter,
|
||||||
|
mail: mailRouter,
|
||||||
} as const);
|
} as const);
|
||||||
|
|
||||||
export type AppRouter = typeof appRouter;
|
export type AppRouter = typeof appRouter;
|
||||||
|
|||||||
155
server/trpc/mail-router.ts
Normal file
155
server/trpc/mail-router.ts
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import { baseRouter, publicProcedure, protectedProcedure } from './router';
|
||||||
|
|
||||||
|
export const mailRouter = baseRouter({
|
||||||
|
messages: publicProcedure
|
||||||
|
.input(z.object({
|
||||||
|
folder: z.string().optional(),
|
||||||
|
}))
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
// TODO: Implement actual ProtonMail API call
|
||||||
|
return [] as Array<{
|
||||||
|
id: string;
|
||||||
|
subject: string;
|
||||||
|
sender: { email: string; name?: string };
|
||||||
|
recipients: Array<{ email: string; name?: string }>;
|
||||||
|
body: string;
|
||||||
|
attachments?: Array<{
|
||||||
|
id: string;
|
||||||
|
filename: string;
|
||||||
|
mimeType: string;
|
||||||
|
size: number;
|
||||||
|
downloadUrl: string;
|
||||||
|
}>;
|
||||||
|
timestamp: string;
|
||||||
|
read: boolean;
|
||||||
|
}>;
|
||||||
|
}),
|
||||||
|
|
||||||
|
message: publicProcedure
|
||||||
|
.input(z.object({
|
||||||
|
messageId: z.string(),
|
||||||
|
}))
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
// TODO: Implement actual ProtonMail API call
|
||||||
|
return {} as {
|
||||||
|
id: string;
|
||||||
|
subject: string;
|
||||||
|
sender: { email: string; name?: string };
|
||||||
|
recipients: Array<{ email: string; name?: string }>;
|
||||||
|
body: string;
|
||||||
|
attachments?: Array<{
|
||||||
|
id: string;
|
||||||
|
filename: string;
|
||||||
|
mimeType: string;
|
||||||
|
size: number;
|
||||||
|
downloadUrl: string;
|
||||||
|
}>;
|
||||||
|
timestamp: string;
|
||||||
|
read: boolean;
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
send: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
to: z.array(z.string()),
|
||||||
|
subject: z.string(),
|
||||||
|
body: z.string(),
|
||||||
|
attachments: z.array(z.object({
|
||||||
|
id: z.string(),
|
||||||
|
filename: z.string(),
|
||||||
|
mimeType: z.string(),
|
||||||
|
size: z.number(),
|
||||||
|
downloadUrl: z.string(),
|
||||||
|
})).optional(),
|
||||||
|
}))
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
// TODO: Implement actual ProtonMail API call
|
||||||
|
return {} as {
|
||||||
|
id: string;
|
||||||
|
subject: string;
|
||||||
|
sender: { email: string; name?: string };
|
||||||
|
recipients: Array<{ email: string; name?: string }>;
|
||||||
|
body: string;
|
||||||
|
attachments?: Array<{
|
||||||
|
id: string;
|
||||||
|
filename: string;
|
||||||
|
mimeType: string;
|
||||||
|
size: number;
|
||||||
|
downloadUrl: string;
|
||||||
|
}>;
|
||||||
|
timestamp: string;
|
||||||
|
read: boolean;
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
contact: publicProcedure
|
||||||
|
.input(z.object({
|
||||||
|
email: z.string().email(),
|
||||||
|
}))
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
// TODO: Implement actual ProtonMail API call
|
||||||
|
return null as {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
phone?: string;
|
||||||
|
organization?: string;
|
||||||
|
} | null;
|
||||||
|
}),
|
||||||
|
|
||||||
|
contacts: publicProcedure
|
||||||
|
.input(z.object({}))
|
||||||
|
.query(async ({ ctx }) => {
|
||||||
|
// TODO: Implement actual ProtonMail API call
|
||||||
|
return [] as Array<{
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
phone?: string;
|
||||||
|
organization?: string;
|
||||||
|
}>;
|
||||||
|
}),
|
||||||
|
|
||||||
|
addContact: protectedProcedure
|
||||||
|
.input(z.object({
|
||||||
|
email: z.string().email(),
|
||||||
|
name: z.string(),
|
||||||
|
phone: z.string().optional(),
|
||||||
|
organization: z.string().optional(),
|
||||||
|
}))
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
// TODO: Implement actual ProtonMail API call
|
||||||
|
return {} as {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
phone?: string;
|
||||||
|
organization?: string;
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
attachment: publicProcedure
|
||||||
|
.input(z.object({
|
||||||
|
attachmentId: z.string(),
|
||||||
|
}))
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
// TODO: Implement actual ProtonMail API call
|
||||||
|
return {} as {
|
||||||
|
id: string;
|
||||||
|
filename: string;
|
||||||
|
mimeType: string;
|
||||||
|
size: number;
|
||||||
|
downloadUrl: string;
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
attachmentDownload: publicProcedure
|
||||||
|
.input(z.object({
|
||||||
|
attachmentId: z.string(),
|
||||||
|
}))
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
// TODO: Implement actual ProtonMail API call
|
||||||
|
return new Blob();
|
||||||
|
}),
|
||||||
|
});
|
||||||
2
src/lib/mail/index.ts
Normal file
2
src/lib/mail/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { ProtonMailClient, protonMail } from './protonmail-client';
|
||||||
|
export type { ProtonMailMessage, ProtonMailAttachment, ProtonMailContact } from './protonmail-client';
|
||||||
84
src/lib/mail/protonmail-client.ts
Normal file
84
src/lib/mail/protonmail-client.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { trpc } from '../api/trpc-client';
|
||||||
|
|
||||||
|
export interface ProtonMailMessage {
|
||||||
|
id: string;
|
||||||
|
subject: string;
|
||||||
|
sender: { email: string; name?: string };
|
||||||
|
recipients: Array<{ email: string; name?: string }>;
|
||||||
|
body: string;
|
||||||
|
attachments?: ProtonMailAttachment[];
|
||||||
|
timestamp: string;
|
||||||
|
read: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProtonMailAttachment {
|
||||||
|
id: string;
|
||||||
|
filename: string;
|
||||||
|
mimeType: string;
|
||||||
|
size: number;
|
||||||
|
downloadUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProtonMailContact {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
phone?: string;
|
||||||
|
organization?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProtonMailClient {
|
||||||
|
constructor(private baseUrl: string = `${(import.meta as any).env?.VITE_API_URL || 'http://localhost:8080'}`) {}
|
||||||
|
|
||||||
|
async getMessages(folder?: string): Promise<ProtonMailMessage[]> {
|
||||||
|
const result = await trpc.mail.messages.query({ folder });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMessage(messageId: string): Promise<ProtonMailMessage> {
|
||||||
|
const result = await trpc.mail.message.query({ messageId });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendMessage(
|
||||||
|
to: string[],
|
||||||
|
subject: string,
|
||||||
|
body: string,
|
||||||
|
attachments?: ProtonMailAttachment[]
|
||||||
|
): Promise<ProtonMailMessage> {
|
||||||
|
const result = await trpc.mail.send.mutate({
|
||||||
|
to,
|
||||||
|
subject,
|
||||||
|
body,
|
||||||
|
attachments,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getContact(email: string): Promise<ProtonMailContact | null> {
|
||||||
|
const result = await trpc.mail.contact.query({ email });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getContacts(): Promise<ProtonMailContact[]> {
|
||||||
|
const result = await trpc.mail.contacts.query({});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async addContact(contact: Omit<ProtonMailContact, 'id'>): Promise<ProtonMailContact> {
|
||||||
|
const result = await trpc.mail.addContact.mutate(contact);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAttachment(attachmentId: string): Promise<ProtonMailAttachment> {
|
||||||
|
const result = await trpc.mail.attachment.query({ attachmentId });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async downloadAttachment(attachmentId: string): Promise<Blob> {
|
||||||
|
const result = await trpc.mail.attachmentDownload.query({ attachmentId });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const protonMail = new ProtonMailClient();
|
||||||
Reference in New Issue
Block a user