robodev 0.18.0 → 0.20.0
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/package.json +1 -1
- package/src/config.test.ts +66 -0
- package/src/config.ts +9 -4
- package/templates/auth-chat/README.md +7 -3
- package/templates/auth-chat/apps/fe/openapi-codegen.config.ts +43 -8
- package/templates/auth-chat/apps/fe/vite.config.ts +34 -3
- package/templates/auth-chat/package.json +1 -1
- package/templates/backend/package.json +1 -1
- package/templates/empty/package.json +1 -1
- package/templates/space/README.md +7 -3
- package/templates/space/apps/fe/openapi-codegen.config.ts +43 -8
- package/templates/space/apps/fe/src/utils/media-upload-headers.ts +8 -0
- package/templates/space/apps/fe/src/utils/media-upload.test.ts +19 -0
- package/templates/space/apps/fe/src/utils/media-upload.ts +3 -0
- package/templates/space/apps/fe/vite.config.ts +34 -3
- package/templates/space/package.json +1 -1
package/package.json
CHANGED
package/src/config.test.ts
CHANGED
|
@@ -25,12 +25,78 @@ test("writeViteApiUrl sets Vite and Povio public API URLs", async () => {
|
|
|
25
25
|
);
|
|
26
26
|
const spa = await readFile(join(root, ".config/local.spa.yml"), "utf8");
|
|
27
27
|
assert.match(spa, /^\s*configs\s*:/m);
|
|
28
|
+
assert.match(spa, /name:\s*resolved/);
|
|
29
|
+
assert.match(spa, /-\s*name:\s*resolved/);
|
|
28
30
|
assert.match(spa, /templateModule:\s*spa\.template/);
|
|
29
31
|
} finally {
|
|
30
32
|
await rm(root, { recursive: true, force: true });
|
|
31
33
|
}
|
|
32
34
|
});
|
|
33
35
|
|
|
36
|
+
test("writeViteApiUrl upgrades unnamed spa manifest to resolved target", async () => {
|
|
37
|
+
const root = await mkdtemp(join(tmpdir(), "rd-spa-upgrade-"));
|
|
38
|
+
try {
|
|
39
|
+
await mkdir(join(root, ".config"), { recursive: true });
|
|
40
|
+
await writeFile(
|
|
41
|
+
join(root, ".config/local.spa.template.yml"),
|
|
42
|
+
`APP_PUBLIC_API_URL: &APP_PUBLIC_API_URL "http://localhost:4000"\nVITE_PUBLIC_API_URL: *APP_PUBLIC_API_URL\n`,
|
|
43
|
+
);
|
|
44
|
+
await writeFile(
|
|
45
|
+
join(root, ".config/local.spa.yml"),
|
|
46
|
+
`configs:
|
|
47
|
+
values:
|
|
48
|
+
- name: "@"
|
|
49
|
+
templateModule: spa.template
|
|
50
|
+
`,
|
|
51
|
+
);
|
|
52
|
+
await writeViteApiUrl("https://prj.example.test", root);
|
|
53
|
+
const spa = await readFile(join(root, ".config/local.spa.yml"), "utf8");
|
|
54
|
+
assert.match(spa, /name:\s*resolved/);
|
|
55
|
+
assert.match(spa, /-\s*name:\s*resolved/);
|
|
56
|
+
assert.match(spa, /templateModule:\s*spa\.template/);
|
|
57
|
+
const env = await readFile(join(root, ".env"), "utf8");
|
|
58
|
+
assert.match(env, /^VITE_PUBLIC_API_URL=https:\/\/prj\.example\.test$/m);
|
|
59
|
+
const template = await readFile(join(root, ".config/local.spa.template.yml"), "utf8");
|
|
60
|
+
assert.match(
|
|
61
|
+
template,
|
|
62
|
+
/APP_PUBLIC_API_URL: &APP_PUBLIC_API_URL "https:\/\/prj\.example\.test"/,
|
|
63
|
+
);
|
|
64
|
+
} finally {
|
|
65
|
+
await rm(root, { recursive: true, force: true });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("writeViteApiUrl leaves a resolved spa manifest in place", async () => {
|
|
70
|
+
const root = await mkdtemp(join(tmpdir(), "rd-spa-ok-"));
|
|
71
|
+
try {
|
|
72
|
+
await mkdir(join(root, ".config"), { recursive: true });
|
|
73
|
+
await writeFile(
|
|
74
|
+
join(root, ".config/local.spa.template.yml"),
|
|
75
|
+
`APP_PUBLIC_API_URL: &APP_PUBLIC_API_URL "http://localhost:4000"\nVITE_PUBLIC_API_URL: *APP_PUBLIC_API_URL\n`,
|
|
76
|
+
);
|
|
77
|
+
const good = `configs:
|
|
78
|
+
- name: resolved
|
|
79
|
+
values:
|
|
80
|
+
- name: "@"
|
|
81
|
+
templateModule: spa.template
|
|
82
|
+
`;
|
|
83
|
+
await writeFile(join(root, ".config/local.spa.yml"), good);
|
|
84
|
+
await writeViteApiUrl("https://prj.example.test", root);
|
|
85
|
+
const spa = await readFile(join(root, ".config/local.spa.yml"), "utf8");
|
|
86
|
+
assert.match(spa, /name:\s*resolved/);
|
|
87
|
+
assert.match(spa, /templateModule:\s*spa\.template/);
|
|
88
|
+
const env = await readFile(join(root, ".env"), "utf8");
|
|
89
|
+
assert.match(env, /^VITE_PUBLIC_API_URL=https:\/\/prj\.example\.test$/m);
|
|
90
|
+
const template = await readFile(join(root, ".config/local.spa.template.yml"), "utf8");
|
|
91
|
+
assert.match(
|
|
92
|
+
template,
|
|
93
|
+
/APP_PUBLIC_API_URL: &APP_PUBLIC_API_URL "https:\/\/prj\.example\.test"/,
|
|
94
|
+
);
|
|
95
|
+
} finally {
|
|
96
|
+
await rm(root, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
34
100
|
test("writeLink merges frontend and unknown keys", async () => {
|
|
35
101
|
const root = await mkdtemp(join(tmpdir(), "rd-link-"));
|
|
36
102
|
try {
|
package/src/config.ts
CHANGED
|
@@ -23,9 +23,10 @@ export type ProjectLink = {
|
|
|
23
23
|
const PROD_URL = "https://robodev.povio.dev";
|
|
24
24
|
|
|
25
25
|
const SPA_MANIFEST = `configs:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
- name: resolved
|
|
27
|
+
values:
|
|
28
|
+
- name: "@"
|
|
29
|
+
templateModule: spa.template
|
|
29
30
|
`;
|
|
30
31
|
|
|
31
32
|
/** Control-plane API. Override with `STARBASE_URL` for local Starbase. */
|
|
@@ -157,6 +158,10 @@ function isSpaManifest(content: string): boolean {
|
|
|
157
158
|
return /^\s*configs\s*:/m.test(content);
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
function hasResolvedSpaTarget(content: string): boolean {
|
|
162
|
+
return /name:\s*resolved/.test(content);
|
|
163
|
+
}
|
|
164
|
+
|
|
160
165
|
function rewriteApiUrlInSpaValues(source: string, projectApiUrl: string): string {
|
|
161
166
|
const quoted = JSON.stringify(projectApiUrl);
|
|
162
167
|
const next = source.replace(/^(APP_PUBLIC_API_URL:\s*&APP_PUBLIC_API_URL\s+).+$/m, `$1${quoted}`);
|
|
@@ -209,7 +214,7 @@ async function rewriteLocalSpaConfig(projectApiUrl: string, cwd: string): Promis
|
|
|
209
214
|
await writeFile(templatePath, next.endsWith("\n") ? next : `${next}\n`);
|
|
210
215
|
}
|
|
211
216
|
|
|
212
|
-
if (!dest || !isSpaManifest(dest)) {
|
|
217
|
+
if (!dest || !isSpaManifest(dest) || !hasResolvedSpaTarget(dest)) {
|
|
213
218
|
await writeFile(destPath, SPA_MANIFEST);
|
|
214
219
|
}
|
|
215
220
|
}
|
|
@@ -2,16 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
Starter from `robodev create --starter auth-chat`: Tiny chat APIs (`chat` database, `messages` table), authenticated invite email, and a Povio UI in `apps/fe`.
|
|
4
4
|
|
|
5
|
-
After a CLI create or deploy with `.robodev` `frontend` set (the default for this starter), the project host serves the built Tiny SPA at `GET /`. APIs and Swagger still win over static files.
|
|
5
|
+
After a CLI create or deploy with `.robodev` `frontend` set (the default for this starter), the project host serves the built Tiny SPA at `GET /`. APIs and Swagger still win over static files.
|
|
6
|
+
|
|
7
|
+
Local Starbase occupies FE **:3000** and BE **:4000**. Point the CLI at it, then run starter Vite on **3001** so it does not steal the dashboard port:
|
|
6
8
|
|
|
7
9
|
```bash
|
|
10
|
+
STARBASE_URL=http://localhost:4000 STARBASE_FE_URL=http://localhost:3000
|
|
11
|
+
# from starter root: auth / link / deploy as today
|
|
8
12
|
cd apps/fe
|
|
9
13
|
bun install
|
|
10
14
|
bun openapi:gen
|
|
11
|
-
bun
|
|
15
|
+
bunx --bun vite --port 3001
|
|
12
16
|
```
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
`bun` is required for `apps/fe`.
|
|
15
19
|
|
|
16
20
|
Auth, link, and deploy from this **root**:
|
|
17
21
|
|
|
@@ -1,21 +1,56 @@
|
|
|
1
1
|
/* oxlint-disable import/no-nodejs-modules */
|
|
2
2
|
import type { OpenAPICodegenConfig } from "@povio/openapi-codegen-cli";
|
|
3
3
|
import { resolveConfigSync } from "@povio/resolve-config";
|
|
4
|
-
import {
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
5
6
|
|
|
6
7
|
const COMPACT_OPENAPI_INPUT = "../../openapi.json";
|
|
7
8
|
const repoRoot = resolve(process.cwd(), "../..");
|
|
8
|
-
const env = resolveConfigSync({ cwd: repoRoot, module: "spa", target: "resolved" }) as Record<
|
|
9
|
-
string,
|
|
10
|
-
string | undefined
|
|
11
|
-
>;
|
|
12
9
|
|
|
13
|
-
function
|
|
14
|
-
|
|
10
|
+
function readDotEnvKey(root: string, key: string): string | undefined {
|
|
11
|
+
try {
|
|
12
|
+
const content = readFileSync(join(root, ".env"), "utf8");
|
|
13
|
+
const line = content.split(/\r?\n/).find((row) => row.startsWith(`${key}=`));
|
|
14
|
+
const value = line?.slice(key.length + 1).trim();
|
|
15
|
+
return value || undefined;
|
|
16
|
+
} catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function loadResolvedSpaEnv(cwd: string): Record<string, string | undefined> {
|
|
22
|
+
try {
|
|
23
|
+
const resolved = resolveConfigSync({ cwd, module: "spa", target: "resolved" });
|
|
24
|
+
if (resolved && typeof resolved === "object") {
|
|
25
|
+
return resolved as Record<string, string | undefined>;
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
// unnamed or missing spa manifest
|
|
29
|
+
}
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const env = loadResolvedSpaEnv(repoRoot);
|
|
34
|
+
|
|
35
|
+
function fallbackApiUrl(): string {
|
|
36
|
+
return (
|
|
37
|
+
env.VITE_PUBLIC_API_URL ||
|
|
38
|
+
process.env.VITE_PUBLIC_API_URL ||
|
|
39
|
+
readDotEnvKey(repoRoot, "VITE_PUBLIC_API_URL") ||
|
|
40
|
+
"http://localhost:4000"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveLiveOpenApiInput(apiUrl = fallbackApiUrl()) {
|
|
45
|
+
const usable = apiUrl.trim();
|
|
46
|
+
if (!usable) return COMPACT_OPENAPI_INPUT;
|
|
47
|
+
return `${usable.replace(/\/+$/, "")}/openapi.json`;
|
|
15
48
|
}
|
|
16
49
|
|
|
17
50
|
export function resolveOpenApiInput() {
|
|
18
|
-
|
|
51
|
+
if (process.env.OPENAPI_INPUT) return process.env.OPENAPI_INPUT;
|
|
52
|
+
if (process.env.OPENAPI_LIVE === "true") return resolveLiveOpenApiInput();
|
|
53
|
+
return COMPACT_OPENAPI_INPUT;
|
|
19
54
|
}
|
|
20
55
|
|
|
21
56
|
const config: OpenAPICodegenConfig = {
|
|
@@ -4,16 +4,47 @@ import tailwindCSS from "@tailwindcss/vite";
|
|
|
4
4
|
import { devtools } from "@tanstack/devtools-vite";
|
|
5
5
|
import { tanstackRouter } from "@tanstack/router-plugin/vite";
|
|
6
6
|
import viteReact from "@vitejs/plugin-react";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
7
9
|
import { defineConfig as defineViteConfig, mergeConfig } from "vite";
|
|
8
10
|
import devtoolsJson from "vite-plugin-devtools-json";
|
|
9
11
|
import { defineConfig as defineVitestConfig } from "vitest/config";
|
|
10
12
|
import { reactIconsSprite } from "react-icons-sprite/vite";
|
|
11
13
|
|
|
12
14
|
const repoRoot = new URL("../..", import.meta.url).pathname;
|
|
13
|
-
const env = resolveConfigSync({ cwd: repoRoot, module: "spa", target: "resolved" }) as any;
|
|
14
15
|
|
|
16
|
+
function readDotEnvKey(root: string, key: string): string | undefined {
|
|
17
|
+
try {
|
|
18
|
+
const content = readFileSync(join(root, ".env"), "utf8");
|
|
19
|
+
const line = content.split(/\r?\n/).find((row) => row.startsWith(`${key}=`));
|
|
20
|
+
const value = line?.slice(key.length + 1).trim();
|
|
21
|
+
return value || undefined;
|
|
22
|
+
} catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loadResolvedSpaEnv(cwd: string): Record<string, unknown> {
|
|
28
|
+
try {
|
|
29
|
+
const resolved = resolveConfigSync({ cwd, module: "spa", target: "resolved" });
|
|
30
|
+
if (resolved && typeof resolved === "object") {
|
|
31
|
+
return resolved as Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// unnamed or missing spa manifest
|
|
35
|
+
}
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const env = loadResolvedSpaEnv(repoRoot);
|
|
15
40
|
applyEnv(env, "__");
|
|
16
41
|
|
|
42
|
+
const apiUrl =
|
|
43
|
+
getString(env, "VITE_PUBLIC_API_URL") ||
|
|
44
|
+
process.env.VITE_PUBLIC_API_URL ||
|
|
45
|
+
readDotEnvKey(repoRoot, "VITE_PUBLIC_API_URL") ||
|
|
46
|
+
"http://localhost:4000";
|
|
47
|
+
|
|
17
48
|
const isInTestMode = process.env.VITEST === "true";
|
|
18
49
|
const iconsSpritePlugin = reactIconsSprite();
|
|
19
50
|
const iconsSpriteTransform = iconsSpritePlugin.transform;
|
|
@@ -51,11 +82,11 @@ const createViteConfig = (isDevServer: boolean) => ({
|
|
|
51
82
|
],
|
|
52
83
|
server: {
|
|
53
84
|
open: false,
|
|
54
|
-
port: getNumber(env, "VITE_DEV_PORT"),
|
|
85
|
+
port: getNumber(env, "VITE_DEV_PORT") ?? 3000,
|
|
55
86
|
strictPort: false,
|
|
56
87
|
proxy: {
|
|
57
88
|
"^/api(?:/|$)": {
|
|
58
|
-
target:
|
|
89
|
+
target: apiUrl,
|
|
59
90
|
changeOrigin: true,
|
|
60
91
|
secure: false,
|
|
61
92
|
},
|
|
@@ -2,16 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
Starter from `robodev create`: Tiny Template schema (`planets`, `aliens`, `media`, `planet_likes`), file-based APIs in `api/`, and a Povio UI in `apps/fe`.
|
|
4
4
|
|
|
5
|
-
After a CLI create or deploy with `.robodev` `frontend` set (the default for this starter), the project host serves the built Tiny SPA at `GET /`. APIs and Swagger still win over static files.
|
|
5
|
+
After a CLI create or deploy with `.robodev` `frontend` set (the default for this starter), the project host serves the built Tiny SPA at `GET /`. APIs and Swagger still win over static files.
|
|
6
|
+
|
|
7
|
+
Local Starbase occupies FE **:3000** and BE **:4000**. Point the CLI at it, then run starter Vite on **3001** so it does not steal the dashboard port:
|
|
6
8
|
|
|
7
9
|
```bash
|
|
10
|
+
STARBASE_URL=http://localhost:4000 STARBASE_FE_URL=http://localhost:3000
|
|
11
|
+
# from starter root: auth / link / deploy as today
|
|
8
12
|
cd apps/fe
|
|
9
13
|
bun install
|
|
10
14
|
bun openapi:gen
|
|
11
|
-
bun
|
|
15
|
+
bunx --bun vite --port 3001
|
|
12
16
|
```
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
`bun` is required for `apps/fe`.
|
|
15
19
|
|
|
16
20
|
Auth, link, and deploy from this **root**:
|
|
17
21
|
|
|
@@ -1,21 +1,56 @@
|
|
|
1
1
|
/* oxlint-disable import/no-nodejs-modules */
|
|
2
2
|
import type { OpenAPICodegenConfig } from "@povio/openapi-codegen-cli";
|
|
3
3
|
import { resolveConfigSync } from "@povio/resolve-config";
|
|
4
|
-
import {
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
5
6
|
|
|
6
7
|
const COMPACT_OPENAPI_INPUT = "../../openapi.json";
|
|
7
8
|
const repoRoot = resolve(process.cwd(), "../..");
|
|
8
|
-
const env = resolveConfigSync({ cwd: repoRoot, module: "spa", target: "resolved" }) as Record<
|
|
9
|
-
string,
|
|
10
|
-
string | undefined
|
|
11
|
-
>;
|
|
12
9
|
|
|
13
|
-
function
|
|
14
|
-
|
|
10
|
+
function readDotEnvKey(root: string, key: string): string | undefined {
|
|
11
|
+
try {
|
|
12
|
+
const content = readFileSync(join(root, ".env"), "utf8");
|
|
13
|
+
const line = content.split(/\r?\n/).find((row) => row.startsWith(`${key}=`));
|
|
14
|
+
const value = line?.slice(key.length + 1).trim();
|
|
15
|
+
return value || undefined;
|
|
16
|
+
} catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function loadResolvedSpaEnv(cwd: string): Record<string, string | undefined> {
|
|
22
|
+
try {
|
|
23
|
+
const resolved = resolveConfigSync({ cwd, module: "spa", target: "resolved" });
|
|
24
|
+
if (resolved && typeof resolved === "object") {
|
|
25
|
+
return resolved as Record<string, string | undefined>;
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
// unnamed or missing spa manifest
|
|
29
|
+
}
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const env = loadResolvedSpaEnv(repoRoot);
|
|
34
|
+
|
|
35
|
+
function fallbackApiUrl(): string {
|
|
36
|
+
return (
|
|
37
|
+
env.VITE_PUBLIC_API_URL ||
|
|
38
|
+
process.env.VITE_PUBLIC_API_URL ||
|
|
39
|
+
readDotEnvKey(repoRoot, "VITE_PUBLIC_API_URL") ||
|
|
40
|
+
"http://localhost:4000"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveLiveOpenApiInput(apiUrl = fallbackApiUrl()) {
|
|
45
|
+
const usable = apiUrl.trim();
|
|
46
|
+
if (!usable) return COMPACT_OPENAPI_INPUT;
|
|
47
|
+
return `${usable.replace(/\/+$/, "")}/openapi.json`;
|
|
15
48
|
}
|
|
16
49
|
|
|
17
50
|
export function resolveOpenApiInput() {
|
|
18
|
-
|
|
51
|
+
if (process.env.OPENAPI_INPUT) return process.env.OPENAPI_INPUT;
|
|
52
|
+
if (process.env.OPENAPI_LIVE === "true") return resolveLiveOpenApiInput();
|
|
53
|
+
return COMPACT_OPENAPI_INPUT;
|
|
19
54
|
}
|
|
20
55
|
|
|
21
56
|
const config: OpenAPICodegenConfig = {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Raw FormData upload headers. Never set Content-Type — the browser must add the multipart boundary. */
|
|
2
|
+
export function mediaUploadHeaders(accessToken: string | null): Headers {
|
|
3
|
+
const headers = new Headers();
|
|
4
|
+
if (accessToken) {
|
|
5
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
6
|
+
}
|
|
7
|
+
return headers;
|
|
8
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { mediaUploadHeaders } from "./media-upload-headers";
|
|
4
|
+
|
|
5
|
+
describe("raw FormData upload headers", () => {
|
|
6
|
+
it("sends Bearer when a token exists and does not set Content-Type", () => {
|
|
7
|
+
const headers = mediaUploadHeaders("test-access-token");
|
|
8
|
+
|
|
9
|
+
expect(headers.get("Authorization")).toBe("Bearer test-access-token");
|
|
10
|
+
expect(headers.get("Content-Type")).toBeNull();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("omits Authorization when no token exists and still does not set Content-Type", () => {
|
|
14
|
+
const headers = mediaUploadHeaders(null);
|
|
15
|
+
|
|
16
|
+
expect(headers.get("Authorization")).toBeNull();
|
|
17
|
+
expect(headers.get("Content-Type")).toBeNull();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { FileUploadRequest } from "@povio/ui";
|
|
2
2
|
|
|
3
|
+
import { authTokenStore } from "@/clients/auth-token-store";
|
|
3
4
|
import { AppConfig } from "@/config/app.config";
|
|
4
5
|
import type { MediaModels } from "@/openapi/media/media.models";
|
|
5
6
|
import { MediaQueries } from "@/openapi/media/media.queries";
|
|
7
|
+
import { mediaUploadHeaders } from "@/utils/media-upload-headers";
|
|
6
8
|
|
|
7
9
|
interface UseMediaUploadHandlerOptions {
|
|
8
10
|
resourceName: MediaModels.MediaResourceName;
|
|
@@ -53,6 +55,7 @@ export function useMediaUploadHandler({ resourceName, method, onUploaded }: UseM
|
|
|
53
55
|
const response = await fetch(uploadUrl, {
|
|
54
56
|
method: instructions.method.toUpperCase(),
|
|
55
57
|
body: formData,
|
|
58
|
+
headers: mediaUploadHeaders(authTokenStore.getAccessToken()),
|
|
56
59
|
signal: options?.abortController?.signal,
|
|
57
60
|
});
|
|
58
61
|
|
|
@@ -4,16 +4,47 @@ import tailwindCSS from "@tailwindcss/vite";
|
|
|
4
4
|
import { devtools } from "@tanstack/devtools-vite";
|
|
5
5
|
import { tanstackRouter } from "@tanstack/router-plugin/vite";
|
|
6
6
|
import viteReact from "@vitejs/plugin-react";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
7
9
|
import { defineConfig as defineViteConfig, mergeConfig } from "vite";
|
|
8
10
|
import devtoolsJson from "vite-plugin-devtools-json";
|
|
9
11
|
import { defineConfig as defineVitestConfig } from "vitest/config";
|
|
10
12
|
import { reactIconsSprite } from "react-icons-sprite/vite";
|
|
11
13
|
|
|
12
14
|
const repoRoot = new URL("../..", import.meta.url).pathname;
|
|
13
|
-
const env = resolveConfigSync({ cwd: repoRoot, module: "spa", target: "resolved" }) as any;
|
|
14
15
|
|
|
16
|
+
function readDotEnvKey(root: string, key: string): string | undefined {
|
|
17
|
+
try {
|
|
18
|
+
const content = readFileSync(join(root, ".env"), "utf8");
|
|
19
|
+
const line = content.split(/\r?\n/).find((row) => row.startsWith(`${key}=`));
|
|
20
|
+
const value = line?.slice(key.length + 1).trim();
|
|
21
|
+
return value || undefined;
|
|
22
|
+
} catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loadResolvedSpaEnv(cwd: string): Record<string, unknown> {
|
|
28
|
+
try {
|
|
29
|
+
const resolved = resolveConfigSync({ cwd, module: "spa", target: "resolved" });
|
|
30
|
+
if (resolved && typeof resolved === "object") {
|
|
31
|
+
return resolved as Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// unnamed or missing spa manifest
|
|
35
|
+
}
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const env = loadResolvedSpaEnv(repoRoot);
|
|
15
40
|
applyEnv(env, "__");
|
|
16
41
|
|
|
42
|
+
const apiUrl =
|
|
43
|
+
getString(env, "VITE_PUBLIC_API_URL") ||
|
|
44
|
+
process.env.VITE_PUBLIC_API_URL ||
|
|
45
|
+
readDotEnvKey(repoRoot, "VITE_PUBLIC_API_URL") ||
|
|
46
|
+
"http://localhost:4000";
|
|
47
|
+
|
|
17
48
|
const isInTestMode = process.env.VITEST === "true";
|
|
18
49
|
const iconsSpritePlugin = reactIconsSprite();
|
|
19
50
|
const iconsSpriteTransform = iconsSpritePlugin.transform;
|
|
@@ -51,11 +82,11 @@ const createViteConfig = (isDevServer: boolean) => ({
|
|
|
51
82
|
],
|
|
52
83
|
server: {
|
|
53
84
|
open: false,
|
|
54
|
-
port: getNumber(env, "VITE_DEV_PORT"),
|
|
85
|
+
port: getNumber(env, "VITE_DEV_PORT") ?? 3000,
|
|
55
86
|
strictPort: false,
|
|
56
87
|
proxy: {
|
|
57
88
|
"^/api(?:/|$)": {
|
|
58
|
-
target:
|
|
89
|
+
target: apiUrl,
|
|
59
90
|
changeOrigin: true,
|
|
60
91
|
secure: false,
|
|
61
92
|
},
|