import { z } from "zod"; import { COOKIE_NAME } from "../shared/const.js"; import { getSessionCookieOptions } from "./_core/cookies"; import { systemRouter } from "./_core/systemRouter"; import { publicProcedure, router } from "./_core/trpc"; import { transcribeAudio } from "./_core/voiceTranscription"; import { storagePut } from "./storage"; export const appRouter = router({ system: systemRouter, auth: router({ me: publicProcedure.query((opts) => opts.ctx.user), logout: publicProcedure.mutation(({ ctx }) => { const cookieOptions = getSessionCookieOptions(ctx.req); ctx.res.clearCookie(COOKIE_NAME, { ...cookieOptions, maxAge: -1 }); return { success: true } as const; }), }), voice: router({ /** * يستقبل ملف صوتي مُرمَّز بـ Base64، يرفعه إلى S3، ثم يُرسله لـ Whisper للتعرف على الكلام العربي */ transcribe: publicProcedure .input( z.object({ audioBase64: z.string(), // ملف الصوت مُرمَّز بـ base64 mimeType: z.string().default("audio/m4a"), // نوع الملف language: z.string().default("ar"), // اللغة (ar للعربية) }) ) .mutation(async ({ input }) => { // 1. تحويل base64 إلى Buffer const audioBuffer = Buffer.from(input.audioBase64, "base64"); // 2. رفع الملف إلى S3 بمفتاح عشوائي const suffix = Date.now().toString(36); const ext = input.mimeType.split("/")[1]?.split(";")[0] ?? "m4a"; const fileKey = `voice-recordings/rec-${suffix}.${ext}`; const { url: audioUrl } = await storagePut( fileKey, audioBuffer, input.mimeType ); // 3. إرسال الرابط لـ Whisper مع تحديد اللغة العربية const result = await transcribeAudio({ audioUrl, language: input.language, prompt: "هذا تسجيل صوتي باللغة العربية", }); // التحقق من نوع النتيجة (نجاح أو خطأ) if ("error" in result) { throw new Error(result.details ?? result.error); } return { text: result.text ?? "", language: result.language ?? input.language, segments: result.segments ?? [], }; }), }), }); export type AppRouter = typeof appRouter;