Diese Version ist auf deinen gewünschten Stand umgebaut:
- Backend: FastAPI
- DB: PostgreSQL
- Storage: lokales Dateisystem (MVP)
- Transkription: OpenAI
audio/transcriptionsmitgpt-4o-mini-transcribe - Auth: simples Bearer-Token (
MVP_API_TOKEN) - iOS-first:
ios/Ordner vorbereitet + Swift Multipart-Beispiel
Wichtig: Der
OPENAI_API_KEYliegt nur auf dem Server (nie in der iOS-App).
mvp-voice-feedback/
backend/
app/
__init__.py
main.py
db.py
models.py
settings.py
openai_transcribe.py
requirements.txt
Dockerfile
docker-compose.yml
ios/
- Docker + Docker Compose installiert
- OpenAI API Key vorhanden
export OPENAI_API_KEY="sk-..."
export MVP_API_TOKEN="dev-token-123"Optional statt export: .env Datei im Repo-Root (Docker Compose liest sie automatisch).
docker compose up --buildDanach läuft die API auf:
http://localhost:8000/health
curl http://localhost:8000/healthcurl -H "Authorization: Bearer dev-token-123" \
-H "X-User-Id: testuser" \
-F "audio=@/path/to/test.m4a" \
http://localhost:8000/feedbackBeispiel-Response:
{
"id": "...",
"status": "transcribed",
"transcript_text": "...",
"error_message": null,
"created_at": "2026-..."
}curl -H "Authorization: Bearer dev-token-123" \
http://localhost:8000/feedbackimport Foundation
struct APIClient {
let baseURL: URL
let token: String
let userId: String?
func uploadAudio(fileURL: URL) async throws -> String {
let url = baseURL.appendingPathComponent("feedback")
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
if let userId { request.setValue(userId, forHTTPHeaderField: "X-User-Id") }
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var body = Data()
let filename = fileURL.lastPathComponent
let fileData = try Data(contentsOf: fileURL)
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"audio\"; filename=\"\(filename)\"\r\n".data(using: .utf8)!)
body.append("Content-Type: audio/mp4\r\n\r\n".data(using: .utf8)!)
body.append(fileData)
body.append("\r\n".data(using: .utf8)!)
body.append("--\(boundary)--\r\n".data(using: .utf8)!)
request.httpBody = body
let (data, response) = try await URLSession.shared.data(for: request)
guard let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode) else {
throw NSError(domain: "Upload", code: (response as? HTTPURLResponse)?.statusCode ?? -1)
}
return String(data: data, encoding: .utf8) ?? ""
}
}- In SwiftUI
AVAudioRecorderhinzufügen (Record/Stop) - Nach Stop Datei-URL an
uploadAudio(...)übergeben - JSON (Status + Transkript + Fehler) in UI anzeigen
- Später: S3-Storage + Async-Worker + Cluster/Topic-Auswertung
Der vorherige Stand wurde in legacy/ verschoben, damit die neue Zielstruktur sauber parallel vorliegt.