Move panel name and version into .env, bump to Beta 0.2.0

The product name, the channel word "Alpha" and the version were scattered
across install.sh, the CLI banner, two spots in App.tsx and three
package.json files, so a rename or a bump meant editing strings in code.

They now live in one committed .env at the project root (BRAND, CHANNEL,
VERSION), shipped inside the release archive and read at runtime: the CLI
loads it after cli.env — so real env and machine settings still win — and
injects the three values into awg-ui, which serves them from GET /ui/brand
for the wordmark. awg-ui also parses the same file directly, so
`npm run server` in dev shows what production shows.

install.sh keeps its own VERSION and CHANNEL and says why: it prints the
banner and builds the release URL before the archive holding .env has been
downloaded. It writes BRAND into cli.env only when the operator passed one
explicitly, so the file stays the single source otherwise.

Version bumped to Beta 0.2.0 across .env, install.sh and the three
package.json files.
This commit is contained in:
2026-08-29 03:08:51 +03:00
parent 18972ed58f
commit 602c0416ba
11 changed files with 86 additions and 40 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "awg-ui",
"version": "0.1.4+2",
"version": "0.2.0",
"private": true,
"scripts": {
"dev": "vite",
+12 -9
View File
@@ -16,16 +16,19 @@ const UI_PASS = process.env.UI_PASS ?? "";
const JWT_SECRET = process.env.JWT_SECRET ?? crypto.randomBytes(32).toString("hex");
const CTRL = `http://127.0.0.1:${process.env.AWGCTRL_PORT ?? "3005"}`;
// Название продукта и версия для панели. BRAND приходит из cli.env (пишет
// install.sh), версия — из package.json: в semver она в форме 0.1.4+2, в
// баннерах и релизах — через точку.
const BRAND = process.env.BRAND || "Forgetting";
const VERSION = ((): string => {
// Имя панели, канал и версия. В проде их прокидывает CLI (он читает .env в
// корне проекта и cli.env); при самостоятельном запуске `npm run server`
// читаем тот же .env сами, чтобы дев-режим показывал то же, что прод.
function envFromFile(file: string, key: string): string {
try {
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8"));
return String(pkg.version ?? "").replace("+", ".");
const m = fs.readFileSync(file, "utf8").match(new RegExp(`^\\s*${key}\\s*=\\s*(.*)$`, "m"));
return m ? m[1].trim() : "";
} catch { return ""; }
})();
}
const ROOT_ENV = path.join(__dirname, "..", ".env");
const BRAND = process.env.BRAND || envFromFile(ROOT_ENV, "BRAND") || "Forgetting";
const CHANNEL = process.env.CHANNEL || envFromFile(ROOT_ENV, "CHANNEL") || "Beta";
const VERSION = process.env.VERSION || envFromFile(ROOT_ENV, "VERSION") || "";
const INTERNAL_AUTH_KEY_FILE = process.env.INTERNAL_AUTH_KEY_FILE
?? path.join(__dirname, "internal_auth_private.key");
@@ -123,7 +126,7 @@ app.post("/logout", requireAuth, (req: Request, res: Response) => {
// Единственный роут без авторизации: вордмарк нужен уже на экране логина,
// до выдачи JWT. Наружу уходит только имя продукта и версия.
app.get("/ui/brand", (_req: Request, res: Response) => {
res.json({ brand: BRAND, version: VERSION });
res.json({ brand: BRAND, channel: CHANNEL, version: VERSION });
});
async function proxy(req: Request, res: Response) {
+3 -2
View File
@@ -22,7 +22,7 @@ export default function App() {
const [drawerOpen, setDrawerOpen] = useState(false);
const [activeTab, setActiveTab] = useState(TABS[0].id);
const [restarting, setRestarting] = useState(false);
const [brand, setBrand] = useState<Brand>({ brand: BRAND_FALLBACK, version: '' });
const [brand, setBrand] = useState<Brand>({ brand: BRAND_FALLBACK, channel: '', version: '' });
const [theme, setTheme] = useState<'light' | 'dark'>(() =>
localStorage.getItem(THEME_KEY) === 'dark' ? 'dark' : 'light');
const touchStartX = useRef(0);
@@ -117,6 +117,7 @@ export default function App() {
axios.get('/ui/brand')
.then(({ data }) => setBrand({
brand: data.brand || BRAND_FALLBACK,
channel: data.channel || '',
version: data.version || '',
}))
.catch(() => { /* остаётся фолбэк */ });
@@ -230,7 +231,7 @@ export default function App() {
<div className="sidebar-logo">
<span className="logo-name">{brand.brand}</span>
<span className="sidebar-version">
{brand.version ? `Alpha ${brand.version}` : 'Alpha'}
{[brand.channel, brand.version].filter(Boolean).join(' ')}
</span>
</div>
<hr className="drawer-divider" />
+4 -4
View File
@@ -31,11 +31,11 @@ export interface PageProps {
token: string;
showMsg: (text: string) => void;
}
// Название продукта и версия приходят с сервера (GET /ui/brand, значения из
// BRAND в cli.env и package.json) — форк переименовывается в одном месте, в
// установщике. Эти константы — фолбэк на время загрузки и при недоступности.
// Имя панели, канал и версия приходят с сервера (GET /ui/brand; значения — из
// .env в корне проекта) — переименование и бамп версии живут в одном файле.
// Эта константа — фолбэк на время загрузки и при недоступности роута.
export const BRAND_FALLBACK = 'Forgetting';
export interface Brand { brand: string; version: string }
export interface Brand { brand: string; channel: string; version: string }
export const TOKEN_KEY = 'awg_token';
export const THEME_KEY = 'awg_theme';