@naviyra/cloud-panel 0.0.2 → 0.0.3-beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { cookies } from "next/headers";
|
|
4
4
|
import { db } from "../../../lib/db.mjs";
|
|
5
5
|
import { session, signIn } from "../../../lib/auth.mjs";
|
|
6
|
+
import { originAllowed } from "../../../lib/request-origin.mjs";
|
|
6
7
|
import { agent, reconcile } from "../../../lib/agent.mjs";
|
|
7
8
|
import { projectInput, idSchema, MAX_ZIP_BYTES } from "@naviyra/shared";
|
|
8
9
|
import {
|
|
@@ -31,7 +32,7 @@ async function handle(req, { params }) {
|
|
|
31
32
|
const method = req.method;
|
|
32
33
|
if (method !== "GET") {
|
|
33
34
|
const expected = process.env.PUBLIC_ORIGIN || new URL(req.url).origin;
|
|
34
|
-
if (req.headers.get("origin")
|
|
35
|
+
if (!originAllowed(req.headers.get("origin"), expected))
|
|
35
36
|
return json({ error: "Invalid request origin" }, 403);
|
|
36
37
|
}
|
|
37
38
|
if (route === "auth/login" && method === "POST") {
|
|
@@ -1710,26 +1710,73 @@ html[data-theme="dark"] .danger-text {
|
|
|
1710
1710
|
margin-bottom: 10px;
|
|
1711
1711
|
}
|
|
1712
1712
|
.database-create {
|
|
1713
|
-
display:
|
|
1714
|
-
|
|
1715
|
-
align-items:
|
|
1713
|
+
display: grid;
|
|
1714
|
+
grid-template-columns: minmax(240px, 340px) minmax(220px, 1fr) auto;
|
|
1715
|
+
align-items: end;
|
|
1716
1716
|
gap: 12px 16px;
|
|
1717
1717
|
padding: 18px 23px 20px;
|
|
1718
1718
|
}
|
|
1719
1719
|
.database-create label {
|
|
1720
|
-
flex: 1 1 220px;
|
|
1721
|
-
max-width: 460px;
|
|
1722
1720
|
margin-bottom: 0;
|
|
1721
|
+
min-width: 0;
|
|
1723
1722
|
}
|
|
1724
1723
|
.database-create small {
|
|
1725
1724
|
margin-top: 4px;
|
|
1725
|
+
line-height: 1.45;
|
|
1726
1726
|
}
|
|
1727
|
-
.database-create input
|
|
1727
|
+
.database-create input,
|
|
1728
|
+
.database-create select {
|
|
1728
1729
|
height: 40px;
|
|
1729
1730
|
margin-top: 8px;
|
|
1731
|
+
box-sizing: border-box;
|
|
1732
|
+
}
|
|
1733
|
+
.database-create select {
|
|
1734
|
+
display: block;
|
|
1735
|
+
width: 100%;
|
|
1736
|
+
padding: 0 36px 0 12px;
|
|
1737
|
+
border: 1px solid #dde4e7;
|
|
1738
|
+
border-radius: 6px;
|
|
1739
|
+
background-color: var(--surface);
|
|
1740
|
+
color: #3e5360;
|
|
1741
|
+
font-size: 12px;
|
|
1742
|
+
appearance: none;
|
|
1743
|
+
background-image:
|
|
1744
|
+
linear-gradient(45deg, transparent 50%, var(--muted) 50%),
|
|
1745
|
+
linear-gradient(135deg, var(--muted) 50%, transparent 50%);
|
|
1746
|
+
background-position:
|
|
1747
|
+
calc(100% - 18px) center,
|
|
1748
|
+
calc(100% - 13px) center;
|
|
1749
|
+
background-size:
|
|
1750
|
+
5px 5px,
|
|
1751
|
+
5px 5px;
|
|
1752
|
+
background-repeat: no-repeat;
|
|
1730
1753
|
}
|
|
1731
1754
|
.database-create .primary {
|
|
1732
1755
|
height: 40px;
|
|
1756
|
+
min-height: 40px;
|
|
1757
|
+
}
|
|
1758
|
+
html[data-theme="dark"] .database-create select {
|
|
1759
|
+
background-color: var(--surface);
|
|
1760
|
+
color: var(--ink);
|
|
1761
|
+
border-color: var(--line);
|
|
1762
|
+
background-image:
|
|
1763
|
+
linear-gradient(45deg, transparent 50%, var(--muted) 50%),
|
|
1764
|
+
linear-gradient(135deg, var(--muted) 50%, transparent 50%);
|
|
1765
|
+
background-position:
|
|
1766
|
+
calc(100% - 18px) center,
|
|
1767
|
+
calc(100% - 13px) center;
|
|
1768
|
+
background-size:
|
|
1769
|
+
5px 5px,
|
|
1770
|
+
5px 5px;
|
|
1771
|
+
background-repeat: no-repeat;
|
|
1772
|
+
}
|
|
1773
|
+
@media (max-width: 720px) {
|
|
1774
|
+
.database-create {
|
|
1775
|
+
grid-template-columns: 1fr;
|
|
1776
|
+
}
|
|
1777
|
+
.database-create .primary {
|
|
1778
|
+
justify-self: start;
|
|
1779
|
+
}
|
|
1733
1780
|
}
|
|
1734
1781
|
.mail-address {
|
|
1735
1782
|
display: flex;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const LOOPBACK = new Set(["localhost", "127.0.0.1", "::1"]);
|
|
2
|
+
|
|
3
|
+
function parseOrigin(value) {
|
|
4
|
+
if (!value || typeof value !== "string") return null;
|
|
5
|
+
try {
|
|
6
|
+
const url = new URL(value);
|
|
7
|
+
if (url.origin === "null") return null;
|
|
8
|
+
return url;
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function hostname(url) {
|
|
15
|
+
return url.hostname.replace(/^\[|\]$/g, "");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function sameSite(left, right) {
|
|
19
|
+
if (left.origin === right.origin) return true;
|
|
20
|
+
if (left.protocol !== right.protocol || left.port !== right.port) return false;
|
|
21
|
+
return LOOPBACK.has(hostname(left)) && LOOPBACK.has(hostname(right));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function originAllowed(originHeader, expected) {
|
|
25
|
+
const origin = parseOrigin(originHeader);
|
|
26
|
+
const target = parseOrigin(expected);
|
|
27
|
+
if (!origin || !target) return false;
|
|
28
|
+
return sameSite(origin, target);
|
|
29
|
+
}
|
package/bin/naviyra.mjs
CHANGED
|
@@ -189,6 +189,11 @@ See docs/managed-runtime.md in the installed folder.`);
|
|
|
189
189
|
// created pg already while leaving Next.js or the agent dependencies missing.
|
|
190
190
|
console.log("Checking and installing dependencies…");
|
|
191
191
|
await run("npm", ["install", "--omit=dev"], target);
|
|
192
|
+
const controlPath = path.join(source, "scripts", "panel-control.mjs");
|
|
193
|
+
if (await exists(controlPath)) {
|
|
194
|
+
const { linkBin } = await import(pathToFileURL(controlPath).href);
|
|
195
|
+
await linkBin(target);
|
|
196
|
+
}
|
|
192
197
|
await run(process.execPath, ["scripts/setup-local.mjs"], target);
|
|
193
198
|
const background = await enableBackground(target);
|
|
194
199
|
if (background) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naviyra/cloud-panel",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3-beta2",
|
|
4
4
|
"description": "Self-hosted Naviyra panel for projects, databases, DNS, and email routing.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"README.md"
|
|
22
22
|
],
|
|
23
23
|
"publishConfig": {
|
|
24
|
-
"access": "public"
|
|
24
|
+
"access": "public",
|
|
25
|
+
"tag": "beta"
|
|
25
26
|
},
|
|
26
27
|
"workspaces": [
|
|
27
28
|
"apps/*",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { access, readFile, rm } from "node:fs/promises";
|
|
2
|
+
import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import {
|
|
5
5
|
TASK_NAME,
|
|
@@ -137,6 +137,24 @@ async function readStatus(dir, taskState) {
|
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
export async function linkBin(root) {
|
|
141
|
+
const binDir = path.join(root, "node_modules", ".bin");
|
|
142
|
+
await mkdir(binDir, { recursive: true });
|
|
143
|
+
await writeFile(
|
|
144
|
+
path.join(binDir, "cloud-panel.cmd"),
|
|
145
|
+
'@ECHO off\r\nnode "%~dp0\\..\\..\\bin\\naviyra.mjs" %*\r\n',
|
|
146
|
+
);
|
|
147
|
+
await writeFile(
|
|
148
|
+
path.join(binDir, "cloud-panel.ps1"),
|
|
149
|
+
[
|
|
150
|
+
"$node = (Get-Command node -ErrorAction Stop).Source",
|
|
151
|
+
'& $node (Join-Path $PSScriptRoot "..\\..\\bin\\naviyra.mjs") @args',
|
|
152
|
+
"exit $LASTEXITCODE",
|
|
153
|
+
"",
|
|
154
|
+
].join("\n"),
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
140
158
|
export async function execute(command, options = {}) {
|
|
141
159
|
const located = await locate(options);
|
|
142
160
|
if (command === "status") {
|
|
@@ -186,6 +204,7 @@ export async function execute(command, options = {}) {
|
|
|
186
204
|
throw Error("Run npx @naviyra/cloud-panel update so the newest package can replace this install.");
|
|
187
205
|
await options.copy(options.source, root);
|
|
188
206
|
await options.install(root);
|
|
207
|
+
await linkBin(root);
|
|
189
208
|
const started = await startBootTask(run);
|
|
190
209
|
if (!started.ok) throw Error(started.message || "Update copied, but Naviyra did not start.");
|
|
191
210
|
console.log(`Updated Naviyra in ${root}. Check it with: npx @naviyra/cloud-panel status`);
|