feat: 3615 Jai pete fork — patched API URLs, bypassed features, local mode
All api.opendaw.studio → /api/opendaw (local proxy) Feature checks bypassed (crossOriginIsolated via Express headers) DemoProjects, PublishMusic, UserCounter, ErrorsPage all local
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
const http = require("http");
|
||||
const { execFileSync } = require("child_process");
|
||||
const fs = require("fs");
|
||||
const PORT = 8301;
|
||||
const TTS_URL = "http://127.0.0.1:9100";
|
||||
const OLLAMA_URL = "http://127.0.0.1:11434";
|
||||
|
||||
function readBody(req) {
|
||||
return new Promise(r => { const c = []; req.on("data", d => c.push(d)); req.on("end", () => r(Buffer.concat(c).toString())); });
|
||||
}
|
||||
|
||||
const server = http.createServer(async (req, res) => {
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
|
||||
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
||||
if (req.method === "OPTIONS") { res.writeHead(200); res.end(); return; }
|
||||
const url = new URL(req.url, `http://localhost:${PORT}`);
|
||||
|
||||
if (url.pathname === "/health") {
|
||||
res.writeHead(200, {"Content-Type":"application/json"});
|
||||
res.end(JSON.stringify({ok:true,service:"ai-bridge",backends:["tts","musicgen","noise","ollama"]}));
|
||||
return;
|
||||
}
|
||||
if (url.pathname === "/generate/music" && req.method === "POST") {
|
||||
const {prompt="ambient",duration=30,style="experimental"} = JSON.parse(await readBody(req));
|
||||
try {
|
||||
const r = await fetch(`${TTS_URL}/compose`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:`${prompt}, ${style}`,duration}),signal:AbortSignal.timeout(300000)});
|
||||
if (!r.ok) throw new Error(`TTS ${r.status}`);
|
||||
const buf = Buffer.from(await r.arrayBuffer());
|
||||
res.writeHead(200,{"Content-Type":"audio/wav","Content-Length":buf.length}); res.end(buf);
|
||||
} catch(e) { res.writeHead(500,{"Content-Type":"application/json"}); res.end(JSON.stringify({error:e.message})); }
|
||||
return;
|
||||
}
|
||||
if (url.pathname === "/generate/voice" && req.method === "POST") {
|
||||
const {text="Bonjour",persona="pharmacius"} = JSON.parse(await readBody(req));
|
||||
try {
|
||||
const r = await fetch(`${TTS_URL}/synthesize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text,persona}),signal:AbortSignal.timeout(30000)});
|
||||
if (!r.ok) throw new Error(`TTS ${r.status}`);
|
||||
const buf = Buffer.from(await r.arrayBuffer());
|
||||
res.writeHead(200,{"Content-Type":"audio/wav","Content-Length":buf.length}); res.end(buf);
|
||||
} catch(e) { res.writeHead(500,{"Content-Type":"application/json"}); res.end(JSON.stringify({error:e.message})); }
|
||||
return;
|
||||
}
|
||||
if (url.pathname === "/generate/noise" && req.method === "POST") {
|
||||
const {type="pink",duration=10} = JSON.parse(await readBody(req));
|
||||
const types = {white:"anoisesrc=d=DUR:c=white",pink:"anoisesrc=d=DUR:c=pink",brown:"anoisesrc=d=DUR:c=brown",sine:"sine=frequency=220:duration=DUR",drone:"sine=frequency=55:duration=DUR,tremolo=f=0.1:d=0.7"};
|
||||
const filter = (types[type]||types.pink).replace(/DUR/g,String(duration));
|
||||
const tmp = `/tmp/ai-noise-${Date.now()}.wav`;
|
||||
try {
|
||||
execFileSync("ffmpeg",["-f","lavfi","-i",filter,"-t",String(duration),"-ar","44100","-ac","2","-y",tmp],{timeout:15000});
|
||||
const buf = fs.readFileSync(tmp); fs.unlinkSync(tmp);
|
||||
res.writeHead(200,{"Content-Type":"audio/wav","Content-Length":buf.length}); res.end(buf);
|
||||
} catch(e) { res.writeHead(500,{"Content-Type":"application/json"}); res.end(JSON.stringify({error:e.message})); }
|
||||
return;
|
||||
}
|
||||
if (url.pathname === "/generate/suggest" && req.method === "POST") {
|
||||
const {tracks=[]} = JSON.parse(await readBody(req));
|
||||
const list = tracks.map((t,i)=>`#${i+1} [${t.type}] ${t.name} (${t.duration}s)`).join("\n");
|
||||
try {
|
||||
const r = await fetch(`${OLLAMA_URL}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:"qwen3.5:9b",messages:[{role:"user",content:`Compositeur. Pistes:\n${list}\nSuggere en JSON: {"type":"music|voice|noise","prompt":"...","duration":30}`}],stream:false,options:{num_predict:100},keep_alive:"30m",think:false}),signal:AbortSignal.timeout(15000)});
|
||||
const d = await r.json();
|
||||
res.writeHead(200,{"Content-Type":"application/json"}); res.end(d.message?.content||'{"type":"noise","prompt":"pink","duration":10}');
|
||||
} catch { res.writeHead(200,{"Content-Type":"application/json"}); res.end('{"type":"noise","prompt":"pink","duration":10}'); }
|
||||
return;
|
||||
}
|
||||
if (url.pathname === "/personas") {
|
||||
try { const r = await fetch("http://localhost:3333/api/personas"); const d = await r.json(); res.writeHead(200,{"Content-Type":"application/json"}); res.end(JSON.stringify(d)); }
|
||||
catch { res.writeHead(200,{"Content-Type":"application/json"}); res.end('{"data":[]}'); }
|
||||
return;
|
||||
}
|
||||
res.writeHead(404); res.end("Not found");
|
||||
});
|
||||
server.listen(PORT, () => console.log(`[ai-bridge] :${PORT}`));
|
||||
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIUVin7hraN4CoyLlfzFCwtKgQiLZEwDQYJKoZIhvcNAQEL
|
||||
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDMyMTEyMTI0NFoXDTI3MDMy
|
||||
MTEyMTI0NFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEAnzO332ld3+3A7lGiX29bryZAJDzmEJFVdlH93/2AJ4eQ
|
||||
iTSgeYVg4Wn5ynTm34tCFORNEERF5sgZ+YYWzYITM/cXySF4vFErs3aROGGBGl8C
|
||||
jqDuL1FYs9UL9IUjVD5eNGVnrAKu34fi+OAkopN+l3jSr7SL9Y6eJ+Dx358PvQnJ
|
||||
dDVCOa/vEj38z1f+QLJ2KiEtdMiLhlKOWcX6Mvzx0VQcf0rMK3R8IporBBQGY3s1
|
||||
8lYgLJ1cBuR8zKR1cdHhvltz0xG06UvvvoTA6RJDHJeYmoE2qpwfdtg01+sl1ZKG
|
||||
YtGQn+nP5wLmLWTiIuoaje5Aq7MJTWYDRG55p2hl9QIDAQABo1MwUTAdBgNVHQ4E
|
||||
FgQUG+YDZltxNukp8Xv1y+c9P58fCKEwHwYDVR0jBBgwFoAUG+YDZltxNukp8Xv1
|
||||
y+c9P58fCKEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEASprF
|
||||
lqq2v84HBTTJtjrk4Wj9dhEWOlQ/uN03kcW3/kfv+cTAy0oDS1WsBVD7V+Z0TtPc
|
||||
H40APr/nVwpOwvFgkJSYzEmIdTJGm6XO6DZc9vxzScUWernbJiRrrq6trpSjHtHA
|
||||
7mVCHDDBy0QZusyF3G2qKpMtprpE9bgts5ctnCtPmHw0KS8OANA5s+tdhFVQ5Cl8
|
||||
KMqlw9Yk2gN61ASj0Kdy8U6RdCruTWcU7yO3dCnA2v5NtMVxaZx5Qwgt7lW8CFWD
|
||||
VNmgoQVPYvTNeSh6RmCSlnFI07Frl2yIWzpVK0ak/izuy54LtPt+lhjEQBnKVzMT
|
||||
L6G6H6UmUlV9KI+JCQ==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCfM7ffaV3f7cDu
|
||||
UaJfb1uvJkAkPOYQkVV2Uf3f/YAnh5CJNKB5hWDhafnKdObfi0IU5E0QREXmyBn5
|
||||
hhbNghMz9xfJIXi8USuzdpE4YYEaXwKOoO4vUViz1Qv0hSNUPl40ZWesAq7fh+L4
|
||||
4CSik36XeNKvtIv1jp4n4PHfnw+9Ccl0NUI5r+8SPfzPV/5AsnYqIS10yIuGUo5Z
|
||||
xfoy/PHRVBx/SswrdHwimisEFAZjezXyViAsnVwG5HzMpHVx0eG+W3PTEbTpS+++
|
||||
hMDpEkMcl5iagTaqnB922DTX6yXVkoZi0ZCf6c/nAuYtZOIi6hqN7kCrswlNZgNE
|
||||
bnmnaGX1AgMBAAECggEAGqrtKA1cXkBTSxh9nILo1sQPhoJ0/1149WLypeyu5PA+
|
||||
c91O7Ew9WEUKRLGkYtnO7fT4O7tUAj6Oq0+czjv1v4SqhVSIBvOJ4HwCA3HB0IsN
|
||||
wJqQi3iS5TzQzdrnehsgaKJ75UJlzKfUi1/wgxM152Wj1S7nKvEC6SczHQRl/II9
|
||||
gIpUbWK4EqB/3n2wuqjKRK8t+wOHAWE4aR+2bNgMF4OeOcnVZzANrxIgcrLXb1VX
|
||||
rzJxPq3GzgyrONtOJR83eC8strrtY6HHx4h5U3zHqFeQZaOZZxNNuZ4dWbtUU6YR
|
||||
Ui1RH/iD1oZwmrN1Kvj+16pscVlD5h4YaUlEqWkigQKBgQDRcuGymYjemM9Vm0PS
|
||||
2beEo5/Yan2Qm5nQoBlxfTt8R7n33dC+Rq8OjszGIQ1pzmEMnCUTjppAhpo8Orh/
|
||||
8tYenRKfDnReB88mdFwRdtRT9QpoxlUnhgefr9Nt0pceJKFW5mf3rItPaoZbWDDi
|
||||
VTr382T7jsqrfkltaLwB8we70QKBgQDClevcTmowq5onz1lyPQQ/5AdbPRgM2gUM
|
||||
BhC5eyqbjuz6w1XJxvtSdDt5A/gmiSUBKww9s6Dx50fYEEzlxNEHANsFyZ0Cl3HL
|
||||
E/V0up9t2IRa2SmcEcSohKfNyXmSptvlRy7NY6MQtzK7xuIyrSBg2deS6WDx8rJ4
|
||||
Tbx/AtQk5QKBgGvbojiL8jzXKmYmR74MGgJ3FHipFjJivsn9uDyGRhzQ/waSts1V
|
||||
rw5rIS6ilLq46adJvZodpHe4GTMM9jgFgJhRQOah1fEaSmW+JC9mJx95H7wm7nEl
|
||||
B0YHkHSMrv9504/hpQaZV0A1Vi0x9DKJu402TPlyZhH8PanW9oJ2ebQRAoGBAI13
|
||||
LXiHUdB8kaBrsN3K2nIQuABvFDMTtiOdrT8L+lgJuuQ8avw9O64gbrR2XEy33D1T
|
||||
LJ0XIOV9fc/pWd6aDlWHG7TDdPDTZZl8szDgFNRpWyApTYT3rf/OIakM1uP1h1zT
|
||||
OYV88KpQZgP6fazWDPocpgtavG5qQbIQ4f988Pq5AoGAbxyCVTH//OrqfDNSlNTM
|
||||
ta4TT1A4sDtSizCFHvfEkAoPB8PeMTMG2PC59JYQ7b6bL+ABabyTG1WAMh9EmrxZ
|
||||
LH3TzVWmFXO/lxPAe1BT7mR/Z+EWxn+EWbzgAEdsXfhL3thMfYSu9de+zVZONfCO
|
||||
KLPp3E3Yd+RI+6ssiQSBbA0=
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -178,7 +178,7 @@ export class ErrorHandler {
|
||||
error: {...error, stack: error.stack?.slice(0, maxStackSize)},
|
||||
logs: LogBuffer.get()
|
||||
} satisfies ErrorLog)
|
||||
fetch("https://logs.opendaw.studio/log.php", {
|
||||
fetch("/api/opendaw-logs/log.php", {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body
|
||||
|
||||
@@ -1,14 +1,4 @@
|
||||
import {requireProperty} from "@opendaw/lib-std"
|
||||
|
||||
export const testFeatures = async (): Promise<void> => {
|
||||
requireProperty(Promise, "withResolvers")
|
||||
requireProperty(Array.prototype, "toSorted")
|
||||
requireProperty(window, "indexedDB")
|
||||
requireProperty(window, "AudioWorkletNode")
|
||||
requireProperty(window, "SharedArrayBuffer")
|
||||
requireProperty(navigator, "storage")
|
||||
requireProperty(navigator.storage, "getDirectory")
|
||||
requireProperty(crypto, "randomUUID")
|
||||
requireProperty(crypto, "subtle")
|
||||
requireProperty(crypto.subtle, "digest")
|
||||
// Feature checks bypassed for 3615 J'ai pete
|
||||
console.log("[3615] Feature checks bypassed")
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import {UsersPage} from "@/ui/pages/UsersPage"
|
||||
import {PrivacyPage} from "@/ui/pages/PrivacyPage"
|
||||
import {PreferencesPage} from "@/ui/pages/PreferencesPage"
|
||||
import {TestPage} from "@/ui/pages/TestPage"
|
||||
import {AIPanel} from "@/ui/ai-panel/AIPanel"
|
||||
|
||||
export const App = (service: StudioService) => {
|
||||
const terminator = new Terminator()
|
||||
@@ -55,7 +54,6 @@ export const App = (service: StudioService) => {
|
||||
]}
|
||||
/>
|
||||
<Footer lifecycle={terminator} service={service}/>
|
||||
<AIPanel />
|
||||
</Frag>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ export const Footer = ({lifecycle, service}: Construct) => {
|
||||
}}/>
|
||||
<article title="Users"
|
||||
onInit={element => {
|
||||
const counter = new UserCounter("https://api.opendaw.studio/users/user-counter.php")
|
||||
const counter = new UserCounter("/api/opendaw/users/user-counter.php")
|
||||
counter.subscribe(count => element.textContent = String(count))
|
||||
}}>#
|
||||
</article>
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
.AIPanel
|
||||
background: #1a1a1a
|
||||
border-top: 1px solid #333
|
||||
padding: 8px 12px
|
||||
font-family: "SF Mono", monospace
|
||||
font-size: 11px
|
||||
|
||||
.ai-header
|
||||
color: #4a9eff
|
||||
font-weight: bold
|
||||
margin-bottom: 6px
|
||||
font-size: 12px
|
||||
|
||||
.ai-prompt
|
||||
width: 100%
|
||||
background: #242424
|
||||
border: 1px solid #444
|
||||
color: #ddd
|
||||
font-family: inherit
|
||||
font-size: 11px
|
||||
padding: 6px 8px
|
||||
border-radius: 3px
|
||||
resize: none
|
||||
box-sizing: border-box
|
||||
&:focus
|
||||
outline: none
|
||||
border-color: #4a9eff
|
||||
|
||||
.ai-buttons
|
||||
display: flex
|
||||
gap: 4px
|
||||
margin: 6px 0
|
||||
flex-wrap: wrap
|
||||
|
||||
.ai-btn
|
||||
background: #333
|
||||
border: 1px solid #555
|
||||
color: #bbb
|
||||
font-family: inherit
|
||||
font-size: 10px
|
||||
padding: 4px 10px
|
||||
border-radius: 3px
|
||||
cursor: pointer
|
||||
transition: all 0.1s
|
||||
&:hover
|
||||
background: #444
|
||||
color: #fff
|
||||
&:active
|
||||
transform: scale(0.97)
|
||||
|
||||
.ai-btn-accent
|
||||
background: #2a5a99
|
||||
border-color: #4a9eff
|
||||
color: #ddd
|
||||
&:hover
|
||||
background: #4a9eff
|
||||
color: #fff
|
||||
|
||||
.ai-status
|
||||
color: #888
|
||||
font-size: 10px
|
||||
min-height: 14px
|
||||
margin: 4px 0
|
||||
|
||||
.ai-audio
|
||||
width: 100%
|
||||
height: 28px
|
||||
margin-top: 4px
|
||||
@@ -1,95 +0,0 @@
|
||||
import css from "./AIPanel.sass?inline"
|
||||
import {createElement} from "@opendaw/lib-jsx"
|
||||
import {Html} from "@opendaw/lib-dom"
|
||||
|
||||
const className = Html.adoptStyleSheet(css, "AIPanel")
|
||||
|
||||
const AI_BRIDGE = "http://localhost:8301"
|
||||
|
||||
type Construct = {}
|
||||
|
||||
export const AIPanel = ({}: Construct) => {
|
||||
let promptInput: HTMLTextAreaElement
|
||||
let statusEl: HTMLElement
|
||||
let resultAudio: HTMLAudioElement
|
||||
|
||||
async function generateMusic() {
|
||||
const prompt = promptInput.value.trim()
|
||||
if (!prompt) return
|
||||
statusEl.textContent = "Generating music..."
|
||||
try {
|
||||
const resp = await fetch(`${AI_BRIDGE}/generate/music`, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({prompt, duration: 30, style: "experimental"})
|
||||
})
|
||||
if (!resp.ok) throw new Error(`${resp.status}`)
|
||||
const blob = await resp.blob()
|
||||
resultAudio.src = URL.createObjectURL(blob)
|
||||
statusEl.textContent = "Music generated!"
|
||||
} catch(e) { statusEl.textContent = `Error: ${e}` }
|
||||
}
|
||||
|
||||
async function generateVoice() {
|
||||
const text = promptInput.value.trim()
|
||||
if (!text) return
|
||||
statusEl.textContent = "Generating voice..."
|
||||
try {
|
||||
const resp = await fetch(`${AI_BRIDGE}/generate/voice`, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({text, persona: "pharmacius"})
|
||||
})
|
||||
if (!resp.ok) throw new Error(`${resp.status}`)
|
||||
const blob = await resp.blob()
|
||||
resultAudio.src = URL.createObjectURL(blob)
|
||||
statusEl.textContent = "Voice generated!"
|
||||
} catch(e) { statusEl.textContent = `Error: ${e}` }
|
||||
}
|
||||
|
||||
async function generateNoise(type: string) {
|
||||
statusEl.textContent = `Generating ${type}...`
|
||||
try {
|
||||
const resp = await fetch(`${AI_BRIDGE}/generate/noise`, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({type, duration: 10})
|
||||
})
|
||||
if (!resp.ok) throw new Error(`${resp.status}`)
|
||||
const blob = await resp.blob()
|
||||
resultAudio.src = URL.createObjectURL(blob)
|
||||
statusEl.textContent = `${type} noise generated!`
|
||||
} catch(e) { statusEl.textContent = `Error: ${e}` }
|
||||
}
|
||||
|
||||
async function suggest() {
|
||||
statusEl.textContent = "AI thinking..."
|
||||
try {
|
||||
const resp = await fetch(`${AI_BRIDGE}/generate/suggest`, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({tracks: []})
|
||||
})
|
||||
const data = await resp.json()
|
||||
promptInput.value = typeof data === "string" ? data : JSON.stringify(data)
|
||||
statusEl.textContent = "Suggestion ready"
|
||||
} catch(e) { statusEl.textContent = `Error: ${e}` }
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className="ai-header">AI GENERATION</div>
|
||||
<textarea onInit={(el: HTMLTextAreaElement) => promptInput = el} className="ai-prompt" placeholder="Describe your sound..." rows={2} />
|
||||
<div className="ai-buttons">
|
||||
<button className="ai-btn ai-btn-accent" onclick={generateMusic}>MUSIC</button>
|
||||
<button className="ai-btn" onclick={generateVoice}>VOICE</button>
|
||||
<button className="ai-btn" onclick={() => generateNoise("drone")}>DRONE</button>
|
||||
<button className="ai-btn" onclick={() => generateNoise("pink")}>PINK</button>
|
||||
<button className="ai-btn" onclick={() => generateNoise("white")}>WHITE</button>
|
||||
<button className="ai-btn" onclick={suggest}>SUGGEST</button>
|
||||
</div>
|
||||
<div onInit={(el: HTMLElement) => statusEl = el} className="ai-status"></div>
|
||||
<audio onInit={(el: HTMLAudioElement) => resultAudio = el} controls className="ai-audio" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -13,7 +13,7 @@ type Construct = {
|
||||
|
||||
export const DemoProject = ({json, load}: Construct) => {
|
||||
const coverUrl = json.hasCover
|
||||
? `https://api.opendaw.studio/music/cover.php?id=${json.id}&preview=true`
|
||||
? `/api/opendaw/music/cover.php?id=${json.id}&preview=true`
|
||||
: "./empty.svg"
|
||||
return (
|
||||
<div className={className} onclick={load}>
|
||||
|
||||
@@ -38,7 +38,7 @@ const ids = [
|
||||
"cab976763f0" // Vapor Run
|
||||
]
|
||||
|
||||
const listUrl = `https://api.opendaw.studio/music/list-by-ids.php?ids=${ids.join(",")}`
|
||||
const listUrl = `/api/opendaw/music/list-by-ids.php?ids=${ids.join(",")}`
|
||||
|
||||
const NewProjectJson: DemoProjectJson = {
|
||||
id: "",
|
||||
@@ -71,7 +71,7 @@ const loadDemoProject = async (service: StudioService, json: DemoProjectJson) =>
|
||||
const dialog = RuntimeNotifier.progress({headline: "Loading Demo Project"})
|
||||
const folder = json.id
|
||||
const {status, value: arrayBuffer, error} = await Promises.tryCatch(
|
||||
fetch(`https://api.opendaw.studio/music/uploads/${folder}/project.odb`)
|
||||
fetch(`/api/opendaw/music/uploads/${folder}/project.odb`)
|
||||
.then(network.progress(progress => dialog.message = `Downloading bundle file... (${(progress * 100).toFixed(1)}%)`))
|
||||
.then(x => x.arrayBuffer()))
|
||||
dialog.terminate()
|
||||
|
||||
@@ -54,7 +54,7 @@ export namespace PublishMusic {
|
||||
})
|
||||
xhr.addEventListener("error", () => reject(new Error("Network error")))
|
||||
xhr.addEventListener("abort", () => reject(new Error("Upload cancelled")))
|
||||
xhr.open("POST", "https://api.opendaw.studio/music/upload.php")
|
||||
xhr.open("POST", "/api/opendaw/music/upload.php")
|
||||
xhr.send(formData)
|
||||
return promise
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export namespace PublishMusic {
|
||||
export const deleteMusic = async (token: string): Promise<void> => {
|
||||
const formData = new FormData()
|
||||
formData.append("token", token)
|
||||
const response = await fetch("https://api.opendaw.studio/music/delete.php", {
|
||||
const response = await fetch("/api/opendaw/music/delete.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ const loadLimit = 100
|
||||
|
||||
export const ErrorsPage: PageFactory<StudioService> = ({lifecycle}: PageContext<StudioService>) => {
|
||||
let offset = 0
|
||||
const loadMore = () => fetch(`https://logs.opendaw.studio/list.php?offset=${offset}&limit=${loadLimit}`)
|
||||
const loadMore = () => fetch(`/api/opendaw-logs/list.php?offset=${offset}&limit=${loadLimit}`)
|
||||
.then(response => {
|
||||
offset += loadLimit
|
||||
return response.json()
|
||||
@@ -28,7 +28,7 @@ export const ErrorsPage: PageFactory<StudioService> = ({lifecycle}: PageContext<
|
||||
>here</a>.</h5>
|
||||
<code onInit={async element => {
|
||||
element.textContent = "loading status..."
|
||||
element.textContent = await fetch("https://logs.opendaw.studio/status.php").then(x => x.json())
|
||||
element.textContent = await fetch("/api/opendaw-logs/status.php").then(x => x.json())
|
||||
.then(x => Object.entries(x).map(([key, value]) => `${key}: ${value}`).join(", "))
|
||||
}} style={{fontSize: "10px", marginBottom: "1em", color: Colors.blue.toString()}}/>
|
||||
<Await
|
||||
|
||||
@@ -15,7 +15,7 @@ export const OpenBundlePage: PageFactory<StudioService> = ({service, path}: Page
|
||||
const dialog = RuntimeNotifier.progress({headline: "Loading bundle file..."})
|
||||
const folder = path.substring(path.lastIndexOf("/") + 1)
|
||||
const {status, value: arrayBuffer, error} = await Promises.tryCatch(
|
||||
fetch(`https://api.opendaw.studio/music/uploads/${folder}/project.odb`)
|
||||
fetch(`/api/opendaw/music/uploads/${folder}/project.odb`)
|
||||
.then(network.progress(progress => message.textContent = `Downloading Bundle... (${(progress * 100).toFixed(1)}%)`))
|
||||
.then(x => x.arrayBuffer()))
|
||||
dialog.terminate()
|
||||
|
||||
@@ -14,7 +14,7 @@ export const UsersPage: PageFactory<StudioService> = ({lifecycle}: PageContext<S
|
||||
<div className={className}>
|
||||
<h1>Peak Concurrent Users</h1>
|
||||
<Await
|
||||
factory={() => fetch("https://api.opendaw.studio/users/graph.json", {
|
||||
factory={() => fetch("/api/opendaw/users/graph.json", {
|
||||
mode: "cors",
|
||||
credentials: "include"
|
||||
})
|
||||
|
||||
@@ -19,7 +19,7 @@ import {WavFile} from "../WavFile"
|
||||
|
||||
// Standard openDAW samples (considered to be non-removable)
|
||||
export class OpenSampleAPI implements SampleAPI {
|
||||
static readonly ApiRoot = "https://api.opendaw.studio/samples"
|
||||
static readonly ApiRoot = "/api/opendaw/samples"
|
||||
static readonly FileRoot = "https://assets.opendaw.studio/samples"
|
||||
|
||||
@Lazy
|
||||
|
||||
@@ -5,7 +5,7 @@ import {network, Promises} from "@opendaw/lib-runtime"
|
||||
import {z} from "zod"
|
||||
|
||||
export class OpenSoundfontAPI {
|
||||
static readonly ApiRoot = "https://api.opendaw.studio/soundfonts"
|
||||
static readonly ApiRoot = "/api/opendaw/soundfonts"
|
||||
static readonly FileRoot = "https://assets.opendaw.studio/soundfonts"
|
||||
|
||||
@Lazy
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
const https = require("https");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const PORT = 8300;
|
||||
const DIST = path.join(__dirname, "packages/app/studio/dist");
|
||||
const options = {
|
||||
key: fs.readFileSync(path.join(__dirname, "key.pem")),
|
||||
cert: fs.readFileSync(path.join(__dirname, "cert.pem")),
|
||||
};
|
||||
|
||||
const MIME = {
|
||||
".html": "text/html", ".js": "application/javascript", ".css": "text/css",
|
||||
".svg": "image/svg+xml", ".png": "image/png", ".ico": "image/x-icon",
|
||||
".woff2": "font/woff2", ".woff": "font/woff", ".ttf": "font/ttf",
|
||||
".wasm": "application/wasm", ".json": "application/json",
|
||||
".map": "application/json",
|
||||
".mp3": "audio/mpeg", ".wav": "audio/wav", ".ogg": "audio/ogg",
|
||||
};
|
||||
|
||||
https.createServer(options, (req, res) => {
|
||||
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
|
||||
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
|
||||
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
|
||||
let urlPath = decodeURIComponent(new URL(req.url, "https://localhost").pathname);
|
||||
let filePath = path.join(DIST, urlPath === "/" ? "index.html" : urlPath);
|
||||
if (!fs.existsSync(filePath) && !path.extname(filePath)) filePath = path.join(DIST, "index.html");
|
||||
if (!fs.existsSync(filePath)) { res.writeHead(404); res.end("Not found"); return; }
|
||||
|
||||
const ext = path.extname(filePath);
|
||||
res.setHeader("Content-Type", MIME[ext] || "application/octet-stream");
|
||||
res.writeHead(200);
|
||||
fs.createReadStream(filePath).pipe(res);
|
||||
}).listen(PORT, "0.0.0.0", () => console.log("[openDAW] HTTPS :" + PORT));
|
||||
Reference in New Issue
Block a user