fix: recording save functionality and authentication
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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" });
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ function isActiveLink(link: any) {
|
||||
|
||||
<LogoutButton
|
||||
user={{
|
||||
name: authStatus.user!.artist_name,
|
||||
name: authStatus.user!.artist_name || authStatus.user!.email.split('@')[0] || 'User',
|
||||
avatar: getAssetUrl(authStatus.user!.avatar?.id, 'mini')!,
|
||||
email: authStatus.user!.email
|
||||
}}
|
||||
|
||||
@@ -566,6 +566,38 @@ export async function getRecordings(fetch?: typeof globalThis.fetch) {
|
||||
);
|
||||
}
|
||||
|
||||
export async function createRecording(
|
||||
recording: {
|
||||
title: string;
|
||||
description?: string;
|
||||
duration: number;
|
||||
events: unknown[];
|
||||
device_info: unknown[];
|
||||
tags?: string[];
|
||||
status?: string;
|
||||
},
|
||||
fetch?: typeof globalThis.fetch,
|
||||
) {
|
||||
return loggedApiCall(
|
||||
"createRecording",
|
||||
async () => {
|
||||
const directus = getDirectusInstance(fetch);
|
||||
const response = await directus.request<Recording>(
|
||||
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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user