d35c806d5b
- Added comprehensive comments and JSDoc-style documentation to NotificationProvider, ThemeProvider, UserProvider, and WebsiteThemeProvider for better clarity and maintainability. - Improved type definitions in index.ts for better code understanding and usage. - Introduced Docker support with Dockerfiles for various services including AI server, signaling server, and browser-use service. - Created a docker-compose.yml file to orchestrate multiple services including PostgreSQL, AI, scraper, and frontend. - Added a startup guide (startup.txt) for setting up the CRM environment on Ubuntu with Docker. - Included a .dockerignore file to exclude unnecessary files from Docker builds.
41 lines
1.9 KiB
TypeScript
41 lines
1.9 KiB
TypeScript
// ── Upload: Voice Message ────────────────────────────────────────────────────
|
|
// POST /api/upload/voice — Upload a voice note audio file
|
|
//
|
|
// Auth: authenticated
|
|
// Accepts multipart/form-data with an "audio" file field and "duration" field.
|
|
// Saves the file to public/uploads/voice/ with a UUID filename.
|
|
// Returns the public URL path and duration for use in chat messages.
|
|
|
|
import { NextRequest, NextResponse } from "next/server"
|
|
import { getSessionUser } from "@/lib/auth"
|
|
import { writeFile } from "node:fs/promises"
|
|
import { join } from "node:path"
|
|
import crypto from "node:crypto"
|
|
|
|
// ── POST ─────────────────────────────────────────────────────────────────────
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const user = await getSessionUser()
|
|
if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
|
|
|
const formData = await request.formData()
|
|
const file = formData.get("audio") as File | null
|
|
if (!file) return NextResponse.json({ error: "No audio file" }, { status: 400 })
|
|
|
|
const duration = parseFloat(formData.get("duration")?.toString() || "0")
|
|
|
|
// Always use .webm extension (normalize from whatever the client sends)
|
|
const ext = file.name.endsWith(".webm") ? "webm" : "webm"
|
|
const filename = `${crypto.randomUUID()}.${ext}`
|
|
const buffer = Buffer.from(await file.arrayBuffer())
|
|
const savePath = join(process.cwd(), "public", "uploads", "voice", filename)
|
|
await writeFile(savePath, buffer)
|
|
|
|
return NextResponse.json({ url: `/uploads/voice/${filename}`, duration })
|
|
} catch (error) {
|
|
console.error("Voice upload error:", error)
|
|
return NextResponse.json({ error: "Upload failed" }, { status: 500 })
|
|
}
|
|
}
|