This commit is contained in:
Flatlogic Bot 2026-03-02 00:52:09 +00:00
parent 9bcf455037
commit 2c36af7989
4 changed files with 39 additions and 21 deletions

View File

@ -113,7 +113,7 @@ app.use('/api/tracks', passport.authenticate('jwt', {session: false}), tracksRou
app.use('/api/midi_clips', passport.authenticate('jwt', {session: false}), midi_clipsRoutes);
app.use('/api/audio_clips', passport.authenticate('jwt', {session: false}), audio_clipsRoutes);
app.use('/api/lyrics', passport.authenticate('jwt', {session: false}), lyricsRoutes);
app.use('/api/ai_song_requests', passport.authenticate('jwt', {session: false}), ai_song_requestsRoutes);
app.use('/api/ai_song_requests', ai_song_requestsRoutes);
app.use('/api/exports', passport.authenticate('jwt', {session: false}), exportsRoutes);
app.use('/api/collaborations', passport.authenticate('jwt', {session: false}), collaborationsRoutes);
app.use('/api/project_assets', passport.authenticate('jwt', {session: false}), project_assetsRoutes);

View File

@ -1,4 +1,5 @@
const express = require('express');
const passport = require('passport');
const Ai_song_requestsService = require('../services/ai_song_requests');
const Ai_song_requestsDBApi = require('../db/api/ai_song_requests');
@ -15,8 +16,7 @@ const {
checkCrudPermissions,
} = require('../middlewares/check-permissions');
router.use(checkCrudPermissions('ai_song_requests'));
// Proxy route should be PUBLIC for browser <audio> tag to work
router.get('/proxy-audio', wrapAsync(async (req, res) => {
const url = req.query.url;
if (!url) {
@ -34,7 +34,7 @@ router.get('/proxy-audio', wrapAsync(async (req, res) => {
}
});
res.setHeader('Content-Type', response.headers['content-type']);
res.setHeader('Content-Type', response.headers['content-type'] || 'audio/mpeg');
if (response.headers['content-length']) {
res.setHeader('Content-Length', response.headers['content-length']);
}
@ -46,6 +46,10 @@ router.get('/proxy-audio', wrapAsync(async (req, res) => {
}
}));
// Apply authentication to ALL other routes
router.use(passport.authenticate('jwt', { session: false }));
router.use(checkCrudPermissions('ai_song_requests'));
router.post('/generate-lyrics', wrapAsync(async (req, res) => {
const payload = await Ai_song_requestsService.generateLyrics(req.body.data, req.currentUser);
res.status(200).send(payload);

View File

@ -106,8 +106,8 @@ module.exports = class Ai_song_requestsService {
// Selection logic for "Real" sounding audio samples
const rawAudioUrl = this.getRealAudioUrl(style, voiceType, instrumental);
// Use proxy to avoid 403/CORS issues
const audioUrl = `/api/ai_song_requests/proxy-audio?url=${encodeURIComponent(rawAudioUrl)}`;
// Use proxy to avoid 403/CORS issues - Store WITHOUT /api prefix for axios compatibility
const audioUrl = `/ai_song_requests/proxy-audio?url=${encodeURIComponent(rawAudioUrl)}`;
const project = await ProjectsDBApi.create({
title: aiData.title,
@ -117,7 +117,7 @@ module.exports = class Ai_song_requestsService {
owner: currentUser.id,
audio_url: audioUrl,
ai_data: aiData,
createdBy: currentUser.id
createdById: currentUser.id
}, { transaction });
// Create tracks and clips for the studio engine
@ -127,7 +127,7 @@ module.exports = class Ai_song_requestsService {
projectId: project.id,
order_index: 0,
volume: 1.0,
createdBy: currentUser.id
createdById: currentUser.id
}, { transaction });
await db.audio_clips.create({
@ -136,7 +136,7 @@ module.exports = class Ai_song_requestsService {
start_bar: 0,
length_bars: 32,
gain: 1.0,
createdBy: currentUser.id
createdById: currentUser.id
}, { transaction });
await Ai_song_requestsDBApi.create(
@ -159,9 +159,7 @@ module.exports = class Ai_song_requestsService {
await transaction.commit();
return {
...project,
id: project.id,
title: project.title,
...project.get({ plain: true }),
ai_data: aiData,
audio_url: audioUrl
};

View File

@ -160,11 +160,17 @@ const SunoStudio = () => {
setIsPlaying(true);
if (audioRef.current) {
// Ensure audio_url is present
const url = track?.audio_url;
let url = track?.audio_url;
if (!url) {
toast.error('Áudio não disponível');
return;
}
// Handle proxy URL for <audio> tag (needs /api prefix to be proxied by Apache)
if (url.startsWith('/') && !url.startsWith('/api/') && !url.startsWith('http')) {
url = `/api${url}`;
}
audioRef.current.src = url;
audioRef.current.load();
audioRef.current.play().catch((e: any) => {
@ -212,24 +218,34 @@ const SunoStudio = () => {
if (!track?.audio_url) return;
try {
toast.info('Preparando download...', { theme: 'dark' });
const response = await axios.get(track.audio_url, {
// Remove /api/ prefix if present for axios because it already has it in baseURL
let url = track.audio_url;
if (url.startsWith('/api/')) {
url = url.substring(4);
}
const response = await axios.get(url, {
responseType: 'blob'
});
const url = window.URL.createObjectURL(new Blob([response.data]));
const blobUrl = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
// If user mentioned MP4, maybe they want it as MP4? We provide MP3/MPEG
link.href = blobUrl;
const extension = track.audio_url.includes('.mp3') ? 'mp3' : 'mp4';
link.setAttribute('download', `${track.title || 'ai-song'}.${extension}`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
window.URL.revokeObjectURL(blobUrl);
toast.success('Download concluído!', { theme: 'dark' });
} catch (e) {
console.error('Download error:', e);
// Fallback to direct link
window.open(track.audio_url, '_blank');
// Fallback to direct link with /api prefix for browser
let url = track.audio_url;
if (url.startsWith('/') && !url.startsWith('/api/') && !url.startsWith('http')) {
url = `/api${url}`;
}
window.open(url, '_blank');
}
};
@ -621,4 +637,4 @@ SunoStudio.getLayout = function getLayout(page: ReactElement) {
return <LayoutAuthenticated>{page}</LayoutAuthenticated>;
};
export default SunoStudio;
export default SunoStudio;