Skip to content

4lexty/FastFeedbackApp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MVP „Voice → Transkript → DB“ (Vehicle-ID ignoriert)

Diese Version ist auf deinen gewünschten Stand umgebaut:

  • Backend: FastAPI
  • DB: PostgreSQL
  • Storage: lokales Dateisystem (MVP)
  • Transkription: OpenAI audio/transcriptions mit gpt-4o-mini-transcribe
  • Auth: simples Bearer-Token (MVP_API_TOKEN)
  • iOS-first: ios/ Ordner vorbereitet + Swift Multipart-Beispiel

Wichtig: Der OPENAI_API_KEY liegt nur auf dem Server (nie in der iOS-App).

Projektstruktur

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/

1) Voraussetzungen

  • Docker + Docker Compose installiert
  • OpenAI API Key vorhanden

2) Umgebungsvariablen setzen

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).

3) Starten

docker compose up --build

Danach läuft die API auf:

  • http://localhost:8000/health

4) Endpunkte testen

Health

curl http://localhost:8000/health

Audio hochladen + Transkript erzeugen

curl -H "Authorization: Bearer dev-token-123" \
     -H "X-User-Id: testuser" \
     -F "audio=@/path/to/test.m4a" \
     http://localhost:8000/feedback

Beispiel-Response:

{
  "id": "...",
  "status": "transcribed",
  "transcript_text": "...",
  "error_message": null,
  "created_at": "2026-..."
}

Letzte Einträge abrufen

curl -H "Authorization: Bearer dev-token-123" \
     http://localhost:8000/feedback

5) iOS-Upload (Multipart, minimal)

import 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) ?? ""
    }
}

6) Nächste Schritte

  1. In SwiftUI AVAudioRecorder hinzufügen (Record/Stop)
  2. Nach Stop Datei-URL an uploadAudio(...) übergeben
  3. JSON (Status + Transkript + Fehler) in UI anzeigen
  4. Später: S3-Storage + Async-Worker + Cluster/Topic-Auswertung

Hinweis zu Altbestand

Der vorherige Stand wurde in legacy/ verschoben, damit die neue Zielstruktur sauber parallel vorliegt.

About

An App that allows fast speech based product feedback.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors