修复token缺失bug
This commit is contained in:
1 parent
1e032637de
commit
13ddc66cfe
6 files changed
+123
-2
No files matched your search
+19
-1
@@ -18,12 +18,30 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f .env.local ]; then
|
if command -v node >/dev/null 2>&1; then
|
||||||
|
node scripts/ensure-deploy-env.mjs "$ROOT_DIR"
|
||||||
|
elif [ ! -f .env.local ]; then
|
||||||
cp .env.example .env.local
|
cp .env.example .env.local
|
||||||
echo "[deploy] Created .env.local from .env.example"
|
echo "[deploy] Created .env.local from .env.example"
|
||||||
echo "[deploy] Real generation requires API keys in .env.local. Empty keys keep mock/local flows available."
|
echo "[deploy] Real generation requires API keys in .env.local. Empty keys keep mock/local flows available."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! grep -q '^ZHINIAN_INTERNAL_WORKER_TOKEN=' .env.local || grep -q '^ZHINIAN_INTERNAL_WORKER_TOKEN=$\|^ZHINIAN_INTERNAL_WORKER_TOKEN=change-me-worker-token$' .env.local; then
|
||||||
|
if command -v openssl >/dev/null 2>&1; then
|
||||||
|
WORKER_TOKEN="$(openssl rand -hex 32)"
|
||||||
|
if grep -q '^ZHINIAN_INTERNAL_WORKER_TOKEN=' .env.local; then
|
||||||
|
sed -i.bak "s/^ZHINIAN_INTERNAL_WORKER_TOKEN=.*/ZHINIAN_INTERNAL_WORKER_TOKEN=${WORKER_TOKEN}/" .env.local
|
||||||
|
rm -f .env.local.bak
|
||||||
|
else
|
||||||
|
printf '\nZHINIAN_INTERNAL_WORKER_TOKEN=%s\n' "$WORKER_TOKEN" >> .env.local
|
||||||
|
fi
|
||||||
|
echo "[deploy] Added ZHINIAN_INTERNAL_WORKER_TOKEN to .env.local"
|
||||||
|
else
|
||||||
|
echo "[deploy] ZHINIAN_INTERNAL_WORKER_TOKEN is required in .env.local, and openssl was not found to generate one."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -p .runtime/data .runtime/uploads .runtime/generated-results
|
mkdir -p .runtime/data .runtime/uploads .runtime/generated-results
|
||||||
|
|
||||||
if [ -z "${APP_PORT:-}" ] && [ -f .env.local ]; then
|
if [ -z "${APP_PORT:-}" ] && [ -f .env.local ]; then
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { randomBytes } from "node:crypto";
|
||||||
|
import { copyFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
|
import { join, resolve } from "node:path";
|
||||||
|
|
||||||
|
const rootDir = resolve(process.argv[2] || process.cwd());
|
||||||
|
const envPath = join(rootDir, ".env.local");
|
||||||
|
const examplePath = join(rootDir, ".env.example");
|
||||||
|
|
||||||
|
if (!existsSync(envPath)) {
|
||||||
|
if (!existsSync(examplePath)) {
|
||||||
|
console.error("[deploy] .env.local was not found and .env.example is missing.");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
copyFileSync(examplePath, envPath);
|
||||||
|
console.log("[deploy] Created .env.local from .env.example");
|
||||||
|
console.log("[deploy] Real generation requires API keys in .env.local. Empty keys keep mock/local flows available.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let envText = readFileSync(envPath, "utf8");
|
||||||
|
const token = readEnvValue(envText, "ZHINIAN_INTERNAL_WORKER_TOKEN");
|
||||||
|
|
||||||
|
if (!token || token === "change-me-worker-token") {
|
||||||
|
envText = setEnvValue(envText, "ZHINIAN_INTERNAL_WORKER_TOKEN", randomBytes(32).toString("hex"));
|
||||||
|
writeFileSync(envPath, envText);
|
||||||
|
console.log("[deploy] Added ZHINIAN_INTERNAL_WORKER_TOKEN to .env.local");
|
||||||
|
}
|
||||||
|
|
||||||
|
function readEnvValue(text, name) {
|
||||||
|
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
const match = text.match(new RegExp(`^${escaped}=(.*)$`, "m"));
|
||||||
|
return match?.[1]?.trim().replace(/^['"]|['"]$/g, "") || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function setEnvValue(text, name, value) {
|
||||||
|
const line = `${name}=${value}`;
|
||||||
|
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
if (new RegExp(`^${escaped}=`, "m").test(text)) {
|
||||||
|
return text.replace(new RegExp(`^${escaped}=.*$`, "m"), line);
|
||||||
|
}
|
||||||
|
const suffix = text.endsWith("\n") ? "" : "\n";
|
||||||
|
return `${text}${suffix}${line}\n`;
|
||||||
|
}
|
||||||
+6
-1
@@ -1,12 +1,17 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const baseUrl = (process.env.ZHINIAN_WORKER_BASE_URL || process.env.NEXT_PUBLIC_APP_URL || "http://127.0.0.1:3000").replace(/\/$/, "");
|
const baseUrl = (process.env.ZHINIAN_WORKER_BASE_URL || process.env.NEXT_PUBLIC_APP_URL || "http://127.0.0.1:3000").replace(/\/$/, "");
|
||||||
const token = process.env.ZHINIAN_INTERNAL_WORKER_TOKEN || "";
|
const token = (process.env.ZHINIAN_INTERNAL_WORKER_TOKEN || "").trim();
|
||||||
const intervalMs = positiveInt(process.env.ZHINIAN_WORKER_INTERVAL_MS, 5000);
|
const intervalMs = positiveInt(process.env.ZHINIAN_WORKER_INTERVAL_MS, 5000);
|
||||||
const limit = positiveInt(process.env.ZHINIAN_WORKER_BATCH_SIZE, 3);
|
const limit = positiveInt(process.env.ZHINIAN_WORKER_BATCH_SIZE, 3);
|
||||||
const once = process.argv.includes("--once");
|
const once = process.argv.includes("--once");
|
||||||
const workerId = process.env.ZHINIAN_WORKER_ID || `worker-${Math.random().toString(16).slice(2)}`;
|
const workerId = process.env.ZHINIAN_WORKER_ID || `worker-${Math.random().toString(16).slice(2)}`;
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "production" && !token) {
|
||||||
|
console.error("[zhinian-worker] ZHINIAN_INTERNAL_WORKER_TOKEN is required in production. Set the same value for zhinian-aigc and zhinian-worker.");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
async function tick() {
|
async function tick() {
|
||||||
const response = await fetch(`${baseUrl}/api/internal/worker/tick`, {
|
const response = await fetch(`${baseUrl}/api/internal/worker/tick`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||||
|
import { execFile } from "node:child_process";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { promisify } from "node:util";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const execFileAsync = promisify(execFile);
|
||||||
|
|
||||||
|
describe("deployment environment preparation", () => {
|
||||||
|
it("adds a strong internal worker token when .env.local does not define one", async () => {
|
||||||
|
const dir = await mkdtemp(join(tmpdir(), "zhinian-deploy-env-"));
|
||||||
|
try {
|
||||||
|
await writeFile(join(dir, ".env.example"), "APP_PORT=3000\nZHINIAN_INTERNAL_WORKER_TOKEN=change-me-worker-token\n");
|
||||||
|
await writeFile(join(dir, ".env.local"), "APP_PORT=3001\n");
|
||||||
|
|
||||||
|
const { stdout } = await execFileAsync(process.execPath, ["scripts/ensure-deploy-env.mjs", dir], {
|
||||||
|
cwd: new URL("../", import.meta.url)
|
||||||
|
});
|
||||||
|
const envLocal = await readFile(join(dir, ".env.local"), "utf8");
|
||||||
|
|
||||||
|
expect(stdout).toContain("Added ZHINIAN_INTERNAL_WORKER_TOKEN");
|
||||||
|
expect(envLocal).toMatch(/^ZHINIAN_INTERNAL_WORKER_TOKEN=[a-f0-9]{64}$/m);
|
||||||
|
expect(envLocal).toContain("APP_PORT=3001");
|
||||||
|
} finally {
|
||||||
|
await rm(dir, { force: true, recursive: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { execFile } from "node:child_process";
|
||||||
|
import { promisify } from "node:util";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const execFileAsync = promisify(execFile);
|
||||||
|
const rootDir = fileURLToPath(new URL("../", import.meta.url));
|
||||||
|
|
||||||
|
describe("worker script configuration", () => {
|
||||||
|
it("fails fast in production when the internal worker token is missing", async () => {
|
||||||
|
await expect(execFileAsync(process.execPath, ["scripts/worker.mjs", "--once"], {
|
||||||
|
cwd: rootDir,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
NODE_ENV: "production",
|
||||||
|
ZHINIAN_INTERNAL_WORKER_TOKEN: "",
|
||||||
|
ZHINIAN_WORKER_BASE_URL: "http://127.0.0.1:9"
|
||||||
|
}
|
||||||
|
})).rejects.toMatchObject({
|
||||||
|
code: 1,
|
||||||
|
stderr: expect.stringContaining("ZHINIAN_INTERNAL_WORKER_TOKEN is required")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./.next/types/routes.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/shared/lib/amp.d.ts","./node_modules/next/amp.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.dLine truncated
|
||||||
Reference in new issue
Block a user