From a252da6d9d9950f70fe4edfae6fb4093909071db Mon Sep 17 00:00:00 2001 From: Valknar XXX Date: Tue, 28 Oct 2025 05:16:36 +0100 Subject: [PATCH] fix: recording save functionality and authentication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix artist_name null handling in header component with email fallback - Fix authentication in recording endpoints to use req.accountability - Change duration field type from integer to double precision for millisecond precision - Add createRecording service function with proper authentication - Update play page to use fetch API for recording saves 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- directus.yaml | 8 ++-- packages/bundle/src/endpoint/index.ts | 16 +++++--- .../src/lib/components/header/header.svelte | 2 +- packages/frontend/src/lib/services.ts | 32 ++++++++++++++++ .../frontend/src/routes/play/+page.svelte | 38 +++++++++---------- 5 files changed, 65 insertions(+), 31 deletions(-) diff --git a/directus.yaml b/directus.yaml index 641c2b0..fc84b2e 100644 --- a/directus.yaml +++ b/directus.yaml @@ -2259,7 +2259,7 @@ fields: foreign_key_column: null - collection: sexy_recordings field: duration - type: integer + type: float meta: collection: sexy_recordings conditions: null @@ -2282,11 +2282,11 @@ fields: schema: name: duration table: sexy_recordings - data_type: integer + data_type: double precision default_value: null max_length: null - numeric_precision: 32 - numeric_scale: 0 + numeric_precision: 53 + numeric_scale: null is_nullable: false is_unique: false is_indexed: false diff --git a/packages/bundle/src/endpoint/index.ts b/packages/bundle/src/endpoint/index.ts index 7b754a5..8a08517 100644 --- a/packages/bundle/src/endpoint/index.ts +++ b/packages/bundle/src/endpoint/index.ts @@ -60,7 +60,7 @@ export default { // GET /sexy/recordings - List user's recordings router.get("/recordings", async (req, res) => { - const { accountability } = context; + const accountability = req.accountability; if (!accountability?.user) { return res.status(401).json({ error: "Unauthorized" }); } @@ -93,7 +93,7 @@ export default { // GET /sexy/recordings/:id - Get single recording router.get("/recordings/:id", async (req, res) => { - const { accountability } = context; + const accountability = req.accountability; if (!accountability?.user) { return res.status(401).json({ error: "Unauthorized" }); } @@ -122,7 +122,7 @@ export default { // POST /sexy/recordings - Create new recording router.post("/recordings", async (req, res) => { - const { accountability } = context; + const accountability = req.accountability; if (!accountability?.user) { return res.status(401).json({ error: "Unauthorized" }); } @@ -168,13 +168,17 @@ export default { res.status(201).json(recording); } catch (error: any) { - res.status(500).json({ error: error.message || "Failed to create recording" }); + console.error("Failed to create recording:", error); + res.status(500).json({ + error: error.message || "Failed to create recording", + details: error.toString() + }); } }); // PATCH /sexy/recordings/:id - Update recording router.patch("/recordings/:id", async (req, res) => { - const { accountability } = context; + const accountability = req.accountability; if (!accountability?.user) { return res.status(401).json({ error: "Unauthorized" }); } @@ -217,7 +221,7 @@ export default { // DELETE /sexy/recordings/:id - Delete (archive) recording router.delete("/recordings/:id", async (req, res) => { - const { accountability } = context; + const accountability = req.accountability; if (!accountability?.user) { return res.status(401).json({ error: "Unauthorized" }); } diff --git a/packages/frontend/src/lib/components/header/header.svelte b/packages/frontend/src/lib/components/header/header.svelte index d5a20ec..1ce1f2b 100644 --- a/packages/frontend/src/lib/components/header/header.svelte +++ b/packages/frontend/src/lib/components/header/header.svelte @@ -127,7 +127,7 @@ function isActiveLink(link: any) { { + const directus = getDirectusInstance(fetch); + const response = await directus.request( + customEndpoint({ + method: "POST", + path: "/sexy/recordings", + body: JSON.stringify(recording), + headers: { + "Content-Type": "application/json", + }, + }), + ); + return response; + }, + { title: recording.title, eventCount: recording.events.length }, + ); +} + export async function deleteRecording(id: string) { return loggedApiCall( "deleteRecording", diff --git a/packages/frontend/src/routes/play/+page.svelte b/packages/frontend/src/routes/play/+page.svelte index 7b035f8..ecb01ef 100644 --- a/packages/frontend/src/routes/play/+page.svelte +++ b/packages/frontend/src/routes/play/+page.svelte @@ -22,8 +22,6 @@ import DeviceCard from "$lib/components/device-card/device-card.svelte"; import RecordingSaveDialog from "./components/recording-save-dialog.svelte"; import type { BluetoothDevice, RecordedEvent, DeviceInfo } from "$lib/types"; import { toast } from "svelte-sonner"; -import { customEndpoint } from "@directus/sdk"; -import { getDirectusInstance } from "$lib/directus"; const client = new ButtplugClient("Sexy.Art"); let connected = $state(client.connected); @@ -191,25 +189,25 @@ async function handleSaveRecording(data: { })); try { - const directus = getDirectusInstance(); - await directus.request( - customEndpoint({ - method: "POST", - path: "/sexy/recordings", - body: JSON.stringify({ - title: data.title, - description: data.description, - duration: recordingDuration, - events: recordedEvents, - device_info: deviceInfo, - tags: data.tags, - status: "draft", - }), - headers: { - "Content-Type": "application/json", - }, + const response = await fetch("/api/sexy/recordings", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + title: data.title, + description: data.description, + duration: recordingDuration, + events: recordedEvents, + device_info: deviceInfo, + tags: data.tags, + status: "draft", }), - ); + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } toast.success("Recording saved successfully!"); showSaveDialog = false;