@naviyra/cloud-panel 0.0.3-beta → 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") !== expected)
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: flex;
1714
- flex-wrap: wrap;
1715
- align-items: flex-end;
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naviyra/cloud-panel",
3
- "version": "0.0.3-beta",
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": {