alinea 2.0.0-preview.4 → 2.0.0-preview.6
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.
- package/dist/.init/content/pages/welcome.json +1 -1
- package/dist/.init/src/cms.ts +1 -4
- package/dist/LICENSES.md +137 -137
- package/dist/adapter/next/cms.js +5 -1
- package/dist/adapter/next/handler.js +35 -6
- package/dist/adapter/next/previews.js +7 -1
- package/dist/adapter/next/syncCheck.d.ts +12 -0
- package/dist/adapter/next/syncCheck.js +59 -0
- package/dist/adapter/next/with-alinea.d.ts +0 -8
- package/dist/adapter/next/with-alinea.js +43 -19
- package/dist/adapter/vite/plugin.js +9 -4
- package/dist/backend/Handler.js +106 -0
- package/dist/backend/api/GithubApi.js +16 -7
- package/dist/backend/api/ProxyMedia.d.ts +3 -0
- package/dist/backend/api/ProxyMedia.js +39 -0
- package/dist/backend/api/S3Uploads.d.ts +1 -1
- package/dist/backend/router/Router.js +2 -0
- package/dist/backend/util/Syncable.js +1 -1
- package/dist/bundled.d.ts +166 -73
- package/dist/chunks/{chunk-XMOVGDFI.js → chunk-7IOKMUKO.js} +1 -1
- package/dist/chunks/{chunk-O7XWTMCB.js → chunk-D6RGFNUW.js} +73 -72
- package/dist/chunks/{chunk-VXZM6V4P.js → chunk-OFEKTDRF.js} +41 -41
- package/dist/cli/Generate.js +3 -4
- package/dist/cli/Serve.js +3 -2
- package/dist/cli/bin.js +1 -1
- package/dist/cli/generate/FillCache.d.ts +1 -1
- package/dist/cli/generate/FillCache.js +20 -9
- package/dist/cli/generate/GenerateDashboard.js +1 -0
- package/dist/cli/serve/CreateLocalServer.d.ts +1 -1
- package/dist/cli/serve/CreateLocalServer.js +42 -34
- package/dist/cli/serve/DevServer.js +1 -0
- package/dist/cli/static/init/cms.js +1 -4
- package/dist/cli/util/ContentType.d.ts +1 -0
- package/dist/cli/util/ContentType.js +36 -0
- package/dist/cli/util/Emitter.js +1 -1
- package/dist/cloud/CloudHandshake.d.ts +6 -0
- package/dist/cloud/CloudHandshake.js +67 -10
- package/dist/cloud/CloudRemote.js +24 -11
- package/dist/components/Breadcrumbs.js +7 -7
- package/dist/components/Button.js +5 -5
- package/dist/components/Calendar.js +7 -7
- package/dist/components/Checkbox.js +8 -8
- package/dist/components/CheckboxGroup.js +5 -5
- package/dist/components/ColorSwatch.js +5 -5
- package/dist/components/ColorSwatchPicker.js +7 -7
- package/dist/components/ComboBox.js +14 -14
- package/dist/components/DateField.js +8 -8
- package/dist/components/DatePicker.js +10 -10
- package/dist/components/DateRangePicker.js +11 -11
- package/dist/components/Dialog.js +5 -5
- package/dist/components/Disclosure.js +11 -11
- package/dist/components/DropZone.js +5 -5
- package/dist/components/Label.js +9 -9
- package/dist/components/Link.js +5 -5
- package/dist/components/List.d.ts +1 -1
- package/dist/components/List.js +37 -36
- package/dist/components/Menu.js +9 -9
- package/dist/components/Modal.js +8 -8
- package/dist/components/MultipleSelect.js +33 -33
- package/dist/components/NumberField.js +11 -11
- package/dist/components/Popover.js +5 -5
- package/dist/components/ProgressCircle.js +8 -8
- package/dist/components/RadioGroup.js +7 -7
- package/dist/components/SearchField.js +10 -10
- package/dist/components/Select.js +12 -12
- package/dist/components/Surface.js +3 -3
- package/dist/components/Switch.js +7 -7
- package/dist/components/Table.js +11 -11
- package/dist/components/Tabs.js +9 -9
- package/dist/components/TagGroup.js +7 -7
- package/dist/components/TextField.js +10 -10
- package/dist/components/TimeField.js +5 -5
- package/dist/components/Toggle.js +5 -5
- package/dist/components/Toolbar.js +6 -6
- package/dist/components/Tooltip.js +8 -8
- package/dist/components/Tree.js +13 -13
- package/dist/core/Config.d.ts +10 -3
- package/dist/core/Config.js +16 -1
- package/dist/core/Graph.d.ts +2 -2
- package/dist/core/Role.js +16 -14
- package/dist/core/TextDoc.d.ts +2 -0
- package/dist/core/TextDoc.js +1 -0
- package/dist/core/Workspace.d.ts +6 -5
- package/dist/core/db/EntryIndex.d.ts +3 -0
- package/dist/core/db/EntryIndex.js +55 -0
- package/dist/core/db/EntryTransaction.js +34 -32
- package/dist/core/db/EntryUrlConflictError.js +1 -1
- package/dist/core/db/LinkResolver.d.ts +8 -2
- package/dist/core/db/LinkResolver.js +27 -5
- package/dist/core/field/RichTextField.js +16 -10
- package/dist/core/media/MediaLocation.d.ts +9 -15
- package/dist/core/media/MediaLocation.js +29 -19
- package/dist/core/media/MediaTypes.js +1 -2
- package/dist/dashboard/App.js +4 -4
- package/dist/dashboard/app/ActivityStatus.js +31 -37
- package/dist/dashboard/app/AppShell.js +2 -2
- package/dist/dashboard/app/AuthView.js +12 -12
- package/dist/dashboard/app/Badge.js +2 -2
- package/dist/dashboard/app/CompactField.js +9 -9
- package/dist/dashboard/app/DashboardErrorBoundary.js +6 -6
- package/dist/dashboard/app/EntryFields.d.ts +3 -1
- package/dist/dashboard/app/EntryFields.js +11 -8
- package/dist/dashboard/app/EntryHeader.js +116 -84
- package/dist/dashboard/app/EntryHeaderActions.d.ts +21 -4
- package/dist/dashboard/app/EntryHeaderActions.js +48 -27
- package/dist/dashboard/app/EntryReferences.js +6 -6
- package/dist/dashboard/app/EntrySidebar.js +15 -15
- package/dist/dashboard/app/EntrySidebarPreview.js +4 -4
- package/dist/dashboard/app/EntryTranslationBanner.d.ts +3 -1
- package/dist/dashboard/app/EntryTranslationBanner.js +23 -20
- package/dist/dashboard/app/Explorer.d.ts +4 -2
- package/dist/dashboard/app/Explorer.js +53 -49
- package/dist/dashboard/app/ExplorerCards.js +31 -31
- package/dist/dashboard/app/ExplorerFileCard.js +16 -16
- package/dist/dashboard/app/ExplorerList.js +14 -15
- package/dist/dashboard/app/ExplorerModal.js +4 -4
- package/dist/dashboard/app/ExplorerTable.js +54 -54
- package/dist/dashboard/app/LinkPicker.js +5 -5
- package/dist/dashboard/app/LocaleMenu.d.ts +7 -0
- package/dist/dashboard/app/LocaleMenu.js +34 -18
- package/dist/dashboard/app/ReadOnlyBadge.d.ts +4 -0
- package/dist/dashboard/app/ReadOnlyBadge.js +33 -0
- package/dist/dashboard/app/SidebarTree.d.ts +3 -6
- package/dist/dashboard/app/SidebarTree.js +62 -57
- package/dist/dashboard/app/WorkspaceMenu.js +16 -16
- package/dist/dashboard/app/WorkspaceRoots.js +21 -21
- package/dist/dashboard/app/editor/FileEditor.d.ts +2 -3
- package/dist/dashboard/app/editor/FileEditor.js +24 -27
- package/dist/dashboard/app/editor/FilePreview.js +4 -4
- package/dist/dashboard/app/modals/CreateEntry.js +5 -5
- package/dist/dashboard/app/pages/EntryPage.js +72 -29
- package/dist/dashboard/app/pages/RootPage.js +3 -3
- package/dist/dashboard/app/pages/SplashPage.js +45 -45
- package/dist/dashboard/app/pages/UsersPage.js +36 -36
- package/dist/dashboard/app/ui/DashboardModal.js +21 -21
- package/dist/dashboard/app/ui/Rail.js +5 -5
- package/dist/dashboard/app/ui/Sidebar.js +2 -2
- package/dist/dashboard/atoms/activity.d.ts +0 -2
- package/dist/dashboard/atoms/activity.js +4 -10
- package/dist/dashboard/atoms/editor.d.ts +2 -1
- package/dist/dashboard/atoms/editor.js +4 -2
- package/dist/dashboard/atoms/entry.d.ts +2 -0
- package/dist/dashboard/atoms/entry.js +32 -14
- package/dist/dashboard/atoms/explorer.d.ts +4 -3
- package/dist/dashboard/atoms/explorer.js +61 -22
- package/dist/dashboard/atoms/link.d.ts +1 -1
- package/dist/dashboard/atoms/link.js +39 -55
- package/dist/dashboard/atoms/root.d.ts +7 -2
- package/dist/dashboard/atoms/root.js +51 -16
- package/dist/dashboard/atoms/upload.d.ts +1 -0
- package/dist/dashboard/atoms/upload.js +2 -1
- package/dist/dashboard/boot/ActivityEvent.d.ts +1 -1
- package/dist/dashboard/boot/DashboardWorker.js +2 -28
- package/dist/dashboard/fixture/cms.js +1 -4
- package/dist/dashboard/hook/UseSaveShortcut.d.ts +1 -0
- package/dist/dashboard/hook/UseSaveShortcut.js +24 -0
- package/dist/dashboard/hooks.d.ts +1 -1
- package/dist/dashboard/hooks.js +12 -3
- package/dist/field/check/CheckField.view.js +2 -2
- package/dist/field/code/CodeField.view.js +11 -11
- package/dist/field/date/DateField.view.js +5 -5
- package/dist/field/link/ImageLink.js +1 -3
- package/dist/field/link/LinkField.view.js +88 -62
- package/dist/field/list/ListField.view.js +25 -25
- package/dist/field/metadata/MetadataField.d.ts +3 -3
- package/dist/field/metadata/MetadataField.view.js +27 -20
- package/dist/field/object/ObjectField.view.js +11 -4
- package/dist/field/richtext/PickTextLink.js +5 -2
- package/dist/field/richtext/ReferenceLink.d.ts +2 -0
- package/dist/field/richtext/ReferenceLink.js +5 -0
- package/dist/field/richtext/RichTextBlock.js +2 -2
- package/dist/field/richtext/RichTextExtensions.js +24 -24
- package/dist/field/richtext/RichTextField.view.js +2 -2
- package/dist/field/richtext/RichTextInsertMenu.js +3 -3
- package/dist/field/richtext/extensions/Link.d.ts +1 -0
- package/dist/field/richtext/extensions/Link.js +1 -0
- package/dist/field/select/SelectField.view.js +1 -1
- package/dist/field/time/TimeField.d.ts +0 -2
- package/dist/field/time/TimeField.view.js +1 -1
- package/dist/index.css +44 -38
- package/dist/next.cjs +96 -19
- package/dist/picker/entry/EntryPicker.js +10 -12
- package/dist/picker/entry/EntryReference.d.ts +2 -0
- package/dist/picker/entry/EntryReference.js +1 -0
- package/dist/types.d.ts +0 -1
- package/package.json +1 -1
- package/dist/chunks/{chunk-6V4TAD2N.js → chunk-DNJX6CRV.js} +4 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "../../chunks/chunk-CQ52GEHY.js";
|
|
2
2
|
|
|
3
3
|
// src/adapter/next/with-alinea.ts
|
|
4
|
+
import { join } from "alinea/core/util/Paths";
|
|
4
5
|
import { readFileSync } from "node:fs";
|
|
5
6
|
import { createRequire } from "node:module";
|
|
6
7
|
import { resolve } from "node:path";
|
|
@@ -26,23 +27,13 @@ function withAlinea(config = {}) {
|
|
|
26
27
|
} catch {
|
|
27
28
|
console.warn("Alinea could not determine Next.js version, assuming 15+");
|
|
28
29
|
}
|
|
29
|
-
const imagesConfig = config.images ?? {};
|
|
30
|
-
const remotePatterns = [
|
|
31
|
-
...imagesConfig.remotePatterns ?? [],
|
|
32
|
-
{
|
|
33
|
-
protocol: "https",
|
|
34
|
-
hostname: "uploads.alinea.cloud"
|
|
35
|
-
}
|
|
36
|
-
];
|
|
37
|
-
const images = {
|
|
38
|
-
...imagesConfig,
|
|
39
|
-
remotePatterns
|
|
40
|
-
};
|
|
41
30
|
const redirects = adminPath ? createRedirects(config, adminPath) : config.redirects;
|
|
42
|
-
const rewrites = adminPath ? createRewrites(config, adminPath) : config.rewrites;
|
|
31
|
+
const rewrites = adminPath ? createRewrites(config, adminPath, settings.handlerUrl) : config.rewrites;
|
|
32
|
+
const images = adminPath ? createImages(config, adminPath) : config.images;
|
|
43
33
|
const env = settings ? {
|
|
44
34
|
...config.env,
|
|
45
|
-
ALINEA_ADMIN_PATH: settings.adminPath
|
|
35
|
+
ALINEA_ADMIN_PATH: settings.adminPath,
|
|
36
|
+
ALINEA_HANDLER_URL: settings.handlerUrl
|
|
46
37
|
} : config.env;
|
|
47
38
|
if (nextVersion < 15)
|
|
48
39
|
return {
|
|
@@ -54,9 +45,9 @@ function withAlinea(config = {}) {
|
|
|
54
45
|
"@alinea/generated"
|
|
55
46
|
]
|
|
56
47
|
},
|
|
57
|
-
images,
|
|
58
48
|
redirects,
|
|
59
49
|
rewrites,
|
|
50
|
+
images,
|
|
60
51
|
env
|
|
61
52
|
};
|
|
62
53
|
return {
|
|
@@ -65,12 +56,32 @@ function withAlinea(config = {}) {
|
|
|
65
56
|
...config.serverExternalPackages ?? [],
|
|
66
57
|
"@alinea/generated"
|
|
67
58
|
],
|
|
68
|
-
images,
|
|
69
59
|
redirects,
|
|
70
60
|
rewrites,
|
|
61
|
+
images,
|
|
71
62
|
env
|
|
72
63
|
};
|
|
73
64
|
}
|
|
65
|
+
function createImages(config, adminPath) {
|
|
66
|
+
const filePattern = `${adminPath}/file/**`;
|
|
67
|
+
const localPatterns = config.images?.localPatterns;
|
|
68
|
+
if (!localPatterns)
|
|
69
|
+
return {
|
|
70
|
+
...config.images,
|
|
71
|
+
localPatterns: [{ pathname: filePattern }, { pathname: "/**" }]
|
|
72
|
+
};
|
|
73
|
+
if (localPatterns.some((pattern) => pattern.pathname === filePattern))
|
|
74
|
+
return config.images;
|
|
75
|
+
return {
|
|
76
|
+
...config.images,
|
|
77
|
+
localPatterns: [
|
|
78
|
+
...localPatterns,
|
|
79
|
+
{
|
|
80
|
+
pathname: filePattern
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
};
|
|
84
|
+
}
|
|
74
85
|
function createRedirects(config, adminPath) {
|
|
75
86
|
const dev = process.env.ALINEA_DEV_SERVER;
|
|
76
87
|
if (!dev) return config.redirects;
|
|
@@ -91,7 +102,7 @@ var emptyRewrites = {
|
|
|
91
102
|
afterFiles: [],
|
|
92
103
|
fallback: []
|
|
93
104
|
};
|
|
94
|
-
function createRewrites(config, adminPath) {
|
|
105
|
+
function createRewrites(config, adminPath, handlerUrl) {
|
|
95
106
|
return async () => {
|
|
96
107
|
const devServer = process.env.ALINEA_DEV_SERVER;
|
|
97
108
|
const nodeEnv = process.env.NODE_ENV;
|
|
@@ -112,6 +123,10 @@ function createRewrites(config, adminPath) {
|
|
|
112
123
|
...rewrites,
|
|
113
124
|
beforeFiles: [
|
|
114
125
|
...rewrites.beforeFiles,
|
|
126
|
+
{
|
|
127
|
+
source: `${adminPath}/file/:file*`,
|
|
128
|
+
destination: `${devServer}/api?file=:file*&delivery=proxy`
|
|
129
|
+
},
|
|
115
130
|
{
|
|
116
131
|
source: `${adminPath}/:path*`,
|
|
117
132
|
destination: `${devServer}${adminPath}/:path*`
|
|
@@ -121,6 +136,14 @@ function createRewrites(config, adminPath) {
|
|
|
121
136
|
}
|
|
122
137
|
return {
|
|
123
138
|
...rewrites,
|
|
139
|
+
beforeFiles: [
|
|
140
|
+
...rewrites.beforeFiles,
|
|
141
|
+
{
|
|
142
|
+
source: `${adminPath}/file/:file*`,
|
|
143
|
+
// Next's internal image optimizer does not follow redirects.
|
|
144
|
+
destination: `${handlerUrl}?file=:file*&delivery=proxy`
|
|
145
|
+
}
|
|
146
|
+
],
|
|
124
147
|
afterFiles: [
|
|
125
148
|
...rewrites.afterFiles,
|
|
126
149
|
{
|
|
@@ -135,11 +158,12 @@ function resolveSettings(config) {
|
|
|
135
158
|
const adminPath = config.env?.ALINEA_ADMIN_PATH ?? process.env.ALINEA_ADMIN_PATH;
|
|
136
159
|
if (!adminPath) return;
|
|
137
160
|
return {
|
|
138
|
-
adminPath: normalizeBasePath(adminPath)
|
|
161
|
+
adminPath: normalizeBasePath(adminPath),
|
|
162
|
+
handlerUrl: config.env?.ALINEA_HANDLER_URL ?? process.env.ALINEA_HANDLER_URL ?? "/api/cms"
|
|
139
163
|
};
|
|
140
164
|
}
|
|
141
165
|
function normalizeBasePath(value) {
|
|
142
|
-
return
|
|
166
|
+
return join("/", value, ".");
|
|
143
167
|
}
|
|
144
168
|
export {
|
|
145
169
|
createCMS,
|
|
@@ -3,6 +3,7 @@ import "../../chunks/chunk-CQ52GEHY.js";
|
|
|
3
3
|
// src/adapter/vite/plugin.ts
|
|
4
4
|
import { nodeHandler } from "alinea/backend/router/NodeHandler";
|
|
5
5
|
import { createDevServer } from "alinea/cli/serve/DevServer";
|
|
6
|
+
import { Config } from "alinea/core/Config";
|
|
6
7
|
function createFallbackUrl(server) {
|
|
7
8
|
const address = server.httpServer?.address();
|
|
8
9
|
const protocol = server.config.server.https ? "https" : "http";
|
|
@@ -28,19 +29,23 @@ function waitForServerUrl(server) {
|
|
|
28
29
|
server.httpServer?.once("listening", resolveUrl);
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
|
-
function isAlineaRequest(request) {
|
|
32
|
+
function isAlineaRequest(request, adminPath) {
|
|
32
33
|
const url = new URL(request.url);
|
|
33
|
-
return url.pathname ===
|
|
34
|
+
return url.pathname === adminPath || url.pathname.startsWith(`${adminPath}/`) || url.pathname === "/api" || url.pathname.startsWith("/api/") || url.pathname === "/~dev" || url.pathname.startsWith("/~dev/") || url.search.startsWith("?/upload");
|
|
34
35
|
}
|
|
35
36
|
function alineaPlugin(dir) {
|
|
36
37
|
let devServer;
|
|
38
|
+
let adminPath = "/admin";
|
|
37
39
|
return {
|
|
38
40
|
name: "alinea-plugin",
|
|
39
41
|
configureServer(server) {
|
|
40
42
|
const serverUrl = waitForServerUrl(server);
|
|
41
43
|
const devServerPromise = createDevServer(dir, {
|
|
42
44
|
cmd: "dev",
|
|
43
|
-
dashboardUrl: serverUrl
|
|
45
|
+
dashboardUrl: serverUrl,
|
|
46
|
+
onAfterGenerate(_, config) {
|
|
47
|
+
adminPath = Config.adminPath(config);
|
|
48
|
+
}
|
|
44
49
|
}).then((result) => {
|
|
45
50
|
devServer = result;
|
|
46
51
|
return result;
|
|
@@ -50,7 +55,7 @@ function alineaPlugin(dir) {
|
|
|
50
55
|
});
|
|
51
56
|
server.middlewares.use(
|
|
52
57
|
nodeHandler(async (request) => {
|
|
53
|
-
if (!isAlineaRequest(request)) return void 0;
|
|
58
|
+
if (!isAlineaRequest(request, adminPath)) return void 0;
|
|
54
59
|
const localServer = await devServerPromise;
|
|
55
60
|
return localServer.handle(request);
|
|
56
61
|
})
|
package/dist/backend/Handler.js
CHANGED
|
@@ -17,16 +17,20 @@ import {
|
|
|
17
17
|
BLOB_SEQUENCE_CONTENT_TYPE,
|
|
18
18
|
encodeBlobSequence
|
|
19
19
|
} from "alinea/core/BlobTransport";
|
|
20
|
+
import { Config } from "alinea/core/Config";
|
|
20
21
|
import { developmentKeyHeader } from "alinea/core/Connection";
|
|
21
22
|
import { ErrorCode, HttpError } from "alinea/core/HttpError";
|
|
23
|
+
import { MediaLocation } from "alinea/core/media/MediaLocation";
|
|
22
24
|
import { assertUploadSize } from "alinea/core/media/UploadLimits";
|
|
23
25
|
import { Permission, Policy } from "alinea/core/Role";
|
|
24
26
|
import { getScope } from "alinea/core/Scope";
|
|
25
27
|
import { ShaMismatchError } from "alinea/core/source/ShaMismatchError";
|
|
26
28
|
import { base64 } from "alinea/core/util/Encoding";
|
|
27
29
|
import { isRecord } from "alinea/core/util/Objects";
|
|
30
|
+
import { isAbsolute, normalize } from "alinea/core/util/Paths";
|
|
28
31
|
import { InvalidCredentialsError, MissingCredentialsError } from "./Auth.js";
|
|
29
32
|
import { HandleAction } from "./HandleAction.js";
|
|
33
|
+
import { isRedirectStatus, proxyMediaUrl } from "./api/ProxyMedia.js";
|
|
30
34
|
import { applyPreview, decodePreviewRequest } from "./resolver/ParsePreview.js";
|
|
31
35
|
import { compressResponse } from "./router/Router.js";
|
|
32
36
|
import { createThrottledSync } from "./util/Syncable.js";
|
|
@@ -75,6 +79,73 @@ function createHandler({
|
|
|
75
79
|
}
|
|
76
80
|
});
|
|
77
81
|
}
|
|
82
|
+
if (!params.has("action") && params.has("file") && (request.method === "GET" || request.method === "HEAD")) {
|
|
83
|
+
const file = string(params.get("file"));
|
|
84
|
+
const proxy = params.get("delivery") === "proxy";
|
|
85
|
+
const normalized = normalize(file);
|
|
86
|
+
if (isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../"))
|
|
87
|
+
throw new HttpError(400, "Invalid file path");
|
|
88
|
+
await periodicSync(cnx);
|
|
89
|
+
const requestedUrl = Config.filePathname(cms.config, normalized);
|
|
90
|
+
const entry = local.index.findByUrl(
|
|
91
|
+
requestedUrl,
|
|
92
|
+
(entry2) => entry2.type === "MediaFile" && entry2.status === "published" && entry2.main
|
|
93
|
+
);
|
|
94
|
+
if (!entry) return new Response("Not found", { status: 404 });
|
|
95
|
+
if (entry.url !== requestedUrl && !proxy)
|
|
96
|
+
return new Response(null, {
|
|
97
|
+
status: 308,
|
|
98
|
+
headers: { location: entry.url }
|
|
99
|
+
});
|
|
100
|
+
const wasBuilt = local.index.initialSync?.has(entry.filePath) ?? false;
|
|
101
|
+
const previewUrl = entry.data.previewUrl;
|
|
102
|
+
const location = entry.data.location;
|
|
103
|
+
if (typeof location !== "string")
|
|
104
|
+
return new Response("Not found", { status: 404 });
|
|
105
|
+
const previewSource = typeof previewUrl === "string" && previewUrl ? new URL(previewUrl, request.url).href : void 0;
|
|
106
|
+
async function proxyMedia(source2, cacheControl, isAllowed) {
|
|
107
|
+
const previewEntryId = uploadPreviewEntryId(
|
|
108
|
+
source2,
|
|
109
|
+
context.handlerUrl
|
|
110
|
+
);
|
|
111
|
+
let response;
|
|
112
|
+
if (previewEntryId && cnx.previewUpload) {
|
|
113
|
+
response = await cnx.previewUpload(previewEntryId);
|
|
114
|
+
} else {
|
|
115
|
+
response = await proxyMediaUrl(
|
|
116
|
+
request,
|
|
117
|
+
new URL(source2, request.url),
|
|
118
|
+
isAllowed
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return mediaResponse(request, response, cacheControl);
|
|
122
|
+
}
|
|
123
|
+
if (!wasBuilt && previewSource) {
|
|
124
|
+
if (proxy) return proxyMedia(previewSource, "private, no-store");
|
|
125
|
+
return redirectFile(previewSource, "private, no-store");
|
|
126
|
+
}
|
|
127
|
+
const source = MediaLocation.sourceUrl(
|
|
128
|
+
cms.config,
|
|
129
|
+
entry.workspace,
|
|
130
|
+
location
|
|
131
|
+
);
|
|
132
|
+
if (!source || source === requestedUrl)
|
|
133
|
+
return new Response("Not found", { status: 404 });
|
|
134
|
+
if (!proxy) return redirectFile(source, "public, max-age=60");
|
|
135
|
+
const configuredBase = Config.baseUrl(
|
|
136
|
+
cms.config,
|
|
137
|
+
context.isDev ? "development" : "production"
|
|
138
|
+
);
|
|
139
|
+
const deliveryBase = configuredBase ?? context.handlerUrl;
|
|
140
|
+
const sourceUrl = new URL(source, deliveryBase);
|
|
141
|
+
const deliveryOrigin = new URL(deliveryBase).origin;
|
|
142
|
+
const handlerPath = context.handlerUrl.pathname;
|
|
143
|
+
return proxyMedia(
|
|
144
|
+
sourceUrl.href,
|
|
145
|
+
"public, max-age=60",
|
|
146
|
+
(url2) => url2.origin === deliveryOrigin && url2.pathname !== handlerPath && !url2.pathname.startsWith(`${handlerPath}/`)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
78
149
|
const action = params.get("action");
|
|
79
150
|
const expectJson = () => {
|
|
80
151
|
const acceptsJson = request.headers.get("accept")?.includes("application/json");
|
|
@@ -347,6 +418,41 @@ function createHandler({
|
|
|
347
418
|
}
|
|
348
419
|
};
|
|
349
420
|
}
|
|
421
|
+
function redirectFile(location, cacheControl) {
|
|
422
|
+
return new Response(null, {
|
|
423
|
+
status: 307,
|
|
424
|
+
headers: { "cache-control": cacheControl, location }
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
function uploadPreviewEntryId(previewSource, handlerUrl) {
|
|
428
|
+
const previewUrl = new URL(previewSource);
|
|
429
|
+
if (previewUrl.origin !== handlerUrl.origin || previewUrl.pathname !== handlerUrl.pathname || previewUrl.searchParams.get("action") !== HandleAction.Upload)
|
|
430
|
+
return;
|
|
431
|
+
return previewUrl.searchParams.get("entryId") ?? void 0;
|
|
432
|
+
}
|
|
433
|
+
function mediaResponse(request, upstream, cacheControl) {
|
|
434
|
+
if (isRedirectStatus(upstream.status))
|
|
435
|
+
return new Response("Media backend returned a redirect", { status: 502 });
|
|
436
|
+
const responseHeaders = new Headers({
|
|
437
|
+
"cache-control": upstream.ok || upstream.status === 304 ? cacheControl : "no-store"
|
|
438
|
+
});
|
|
439
|
+
for (const name of [
|
|
440
|
+
"accept-ranges",
|
|
441
|
+
"content-disposition",
|
|
442
|
+
"content-length",
|
|
443
|
+
"content-range",
|
|
444
|
+
"content-type",
|
|
445
|
+
"etag",
|
|
446
|
+
"last-modified"
|
|
447
|
+
]) {
|
|
448
|
+
const value = upstream.headers.get(name);
|
|
449
|
+
if (value) responseHeaders.set(name, value);
|
|
450
|
+
}
|
|
451
|
+
return new Response(request.method === "HEAD" ? null : upstream.body, {
|
|
452
|
+
status: upstream.status,
|
|
453
|
+
headers: responseHeaders
|
|
454
|
+
});
|
|
455
|
+
}
|
|
350
456
|
function parseUser(input) {
|
|
351
457
|
if (!isRecord(input)) throw new HttpError(400, "Expected user object");
|
|
352
458
|
const { sub, name, email, roles } = input;
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { ShaMismatchError } from "alinea/core/source/ShaMismatchError";
|
|
11
11
|
import { base64, btoa } from "alinea/core/util/Encoding";
|
|
12
12
|
import { fileVersions } from "alinea/core/util/EntryFilenames";
|
|
13
|
-
import { join } from "alinea/core/util/Paths";
|
|
13
|
+
import { join, relative } from "alinea/core/util/Paths";
|
|
14
14
|
var GithubApi = class extends GithubSource {
|
|
15
15
|
#options;
|
|
16
16
|
constructor(options) {
|
|
@@ -129,8 +129,8 @@ Co-authored-by: ${user.name} <${user.email}>`;
|
|
|
129
129
|
const prev = fileEntry?.previous_filename;
|
|
130
130
|
if (prev) {
|
|
131
131
|
const prefix = rootDir ? `${rootDir}/` : "";
|
|
132
|
-
const
|
|
133
|
-
queue.push(...fileVersions(
|
|
132
|
+
const relative2 = prev.startsWith(prefix) ? prev.slice(prefix.length) : prev;
|
|
133
|
+
queue.push(...fileVersions(relative2));
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
}
|
|
@@ -194,6 +194,12 @@ Co-authored-by: ${user.name} <${user.email}>`;
|
|
|
194
194
|
throw new ShaMismatchError(actualSha, expectedHeadOid);
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
|
+
console.error("Alinea GitHub commit failed", {
|
|
198
|
+
expectedHeadOid,
|
|
199
|
+
additions: additions.map((change) => change.path),
|
|
200
|
+
deletions: deletions.map((change) => change.path),
|
|
201
|
+
error
|
|
202
|
+
});
|
|
197
203
|
throw error;
|
|
198
204
|
});
|
|
199
205
|
}
|
|
@@ -205,13 +211,13 @@ Co-authored-by: ${user.name} <${user.email}>`;
|
|
|
205
211
|
switch (change.op) {
|
|
206
212
|
case "addContent": {
|
|
207
213
|
additions.push({
|
|
208
|
-
path:
|
|
214
|
+
path: repositoryPath(this.contentLocation, change.path),
|
|
209
215
|
contents: btoa(change.contents)
|
|
210
216
|
});
|
|
211
217
|
break;
|
|
212
218
|
}
|
|
213
219
|
case "uploadFile": {
|
|
214
|
-
const file =
|
|
220
|
+
const file = repositoryPath(rootDir, change.location);
|
|
215
221
|
additions.push({
|
|
216
222
|
path: file,
|
|
217
223
|
contents: await this.#fetchUploadedContent(change.url)
|
|
@@ -219,12 +225,12 @@ Co-authored-by: ${user.name} <${user.email}>`;
|
|
|
219
225
|
break;
|
|
220
226
|
}
|
|
221
227
|
case "deleteContent": {
|
|
222
|
-
const file =
|
|
228
|
+
const file = repositoryPath(this.contentLocation, change.path);
|
|
223
229
|
deletions.push({ path: file });
|
|
224
230
|
break;
|
|
225
231
|
}
|
|
226
232
|
case "removeFile": {
|
|
227
|
-
const file =
|
|
233
|
+
const file = repositoryPath(rootDir, change.location);
|
|
228
234
|
deletions.push({ path: file });
|
|
229
235
|
break;
|
|
230
236
|
}
|
|
@@ -255,6 +261,9 @@ Co-authored-by: ${user.name} <${user.email}>`;
|
|
|
255
261
|
return base64.stringify(new Uint8Array(await response.arrayBuffer()));
|
|
256
262
|
}
|
|
257
263
|
};
|
|
264
|
+
function repositoryPath(...segments) {
|
|
265
|
+
return relative("/", join("/", ...segments));
|
|
266
|
+
}
|
|
258
267
|
export {
|
|
259
268
|
GithubApi
|
|
260
269
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import "../../chunks/chunk-CQ52GEHY.js";
|
|
2
|
+
|
|
3
|
+
// src/backend/api/ProxyMedia.ts
|
|
4
|
+
import { Headers, Response } from "@alinea/iso";
|
|
5
|
+
async function proxyMediaUrl(request, source, isAllowed = (url) => url.origin === source.origin) {
|
|
6
|
+
const headers = new Headers();
|
|
7
|
+
for (const name of [
|
|
8
|
+
"range",
|
|
9
|
+
"if-range",
|
|
10
|
+
"if-none-match",
|
|
11
|
+
"if-modified-since"
|
|
12
|
+
]) {
|
|
13
|
+
const value = request.headers.get(name);
|
|
14
|
+
if (value) headers.set(name, value);
|
|
15
|
+
}
|
|
16
|
+
let current = source;
|
|
17
|
+
for (let redirects = 0; ; redirects++) {
|
|
18
|
+
if (!isAllowed(current))
|
|
19
|
+
return new Response("Invalid media source", { status: 502 });
|
|
20
|
+
const response = await fetch(current, {
|
|
21
|
+
method: request.method,
|
|
22
|
+
headers,
|
|
23
|
+
redirect: "manual",
|
|
24
|
+
signal: request.signal
|
|
25
|
+
});
|
|
26
|
+
const location = response.headers.get("location");
|
|
27
|
+
if (!isRedirectStatus(response.status) || !location) return response;
|
|
28
|
+
if (redirects === 3)
|
|
29
|
+
return new Response("Too many media redirects", { status: 502 });
|
|
30
|
+
current = new URL(location, current);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function isRedirectStatus(status) {
|
|
34
|
+
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
isRedirectStatus,
|
|
38
|
+
proxyMediaUrl
|
|
39
|
+
};
|
|
@@ -195,6 +195,8 @@ function callHandler(handler, input) {
|
|
|
195
195
|
function compressResponse(request, response) {
|
|
196
196
|
const body = response.body;
|
|
197
197
|
if (!body) return response;
|
|
198
|
+
if (response.status === 206 || response.headers.has("content-range"))
|
|
199
|
+
return response;
|
|
198
200
|
const isCompressed = response.headers.get("content-encoding");
|
|
199
201
|
if (isCompressed) return response;
|
|
200
202
|
const accept = request.headers.get("accept-encoding");
|
|
@@ -5,7 +5,7 @@ import "../../chunks/chunk-CQ52GEHY.js";
|
|
|
5
5
|
|
|
6
6
|
// src/backend/util/Syncable.ts
|
|
7
7
|
function createThrottledSync() {
|
|
8
|
-
let lastSync =
|
|
8
|
+
let lastSync = 0;
|
|
9
9
|
const limit = pLimit(1);
|
|
10
10
|
return async function throttle(sync, intervalSeconds = 60) {
|
|
11
11
|
return limit(async () => {
|