diff --git a/.dockerignore b/.dockerignore
index f925df8..3dd638e 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,6 +1,5 @@
node_modules/
dist/
-.git/
documentation/
*.log
.env
@@ -9,3 +8,8 @@ documentation/
# Lomitko na zacatku je nutne: bez nej by se vzorec shodl s KAZDOU
# slozkou 'data', tedy i se src/data - a zdrojove soubory by tise chybely.
/data/
+
+# `.git/` se zamerne NEvylucuje. Build z nej cte cislo commitu do oznaceni
+# verze - bez nej se na nasazene aplikaci pozna jen cas buildu, ne co v ni je.
+# Do vysledneho image se nedostane: ten vznika z ciste zakladni image
+# a kopiruje se do nej jen `dist`, `scripts` a migrace.
diff --git a/documentation/02-appfactory-proxy.md b/documentation/02-appfactory-proxy.md
index 04fa62d..480884d 100644
--- a/documentation/02-appfactory-proxy.md
+++ b/documentation/02-appfactory-proxy.md
@@ -104,6 +104,41 @@ Definici sestavuje `src/openapi.ts`.
Pozn.: `/api/dashboard/stream` je Server-Sent Events. Swagger UI streamovanou
odpoved rozumne nezobrazi, testuje se prohlizecem nebo curlem.
+## Jak se pozna, ktera verze bezi
+
+Nasazena aplikace, ktera je o dva commity pozadu, funguje a vypada skoro
+stejne jako nova. Bez oznaceni buildu se to nepozna a hleda se chyba tam, kde
+zadna neni - presne to nas jednou stalo pul hodiny.
+
+Kazdy build proto nese svoje oznaceni. Sklada ho Vite pri buildu
+(`vite.config.ts`, sekce `buildInfo`) a je videt na dvou mistech:
+
+| Kde | Jak se k tomu dostat |
+| --------------------- | ------------------------------------------- |
+| Pata postranniho menu | pod tlacitkem Odhlasit se, staci prihlaseni |
+| Hlavicka stranky | `curl` na adresu aplikace, bez prihlaseni |
+
+```bash
+curl -s https://services.csbot.cz/apps/csbot-prototype/ | grep app-build
+#
+```
+
+Tri udaje a kazdy rika neco jineho:
+
+| Udaj | K cemu |
+| ---------- | ------------------------------------------------------ |
+| Verze | z `package.json`, meni se zridka |
+| Cas buildu | **hlavni udaj.** Rika, jestli nasazeni vubec probehlo |
+| Commit | rika, **co** v tom je. Bez nej se pozna jen kdy, ne co |
+
+Commit se cte z `.git`, takze se `.git/` **zamerne nevylucuje**
+v `.dockerignore`. Do vysledneho image se nedostane: ten vznika z ciste
+zakladni image a kopiruje se do nej jen `dist`, `scripts` a migrace. Kdyby
+`.git` pri buildu chybelo, oznaceni se nerozbije, jen commit zustane prazdny.
+
+Ve vyvoji se misto casu pise "vyvoj". Cas buildu by tam byl pokazde jiny
+a nikomu by nic nerekl.
+
## Overeni po zmene
```bash
diff --git a/documentation/99-zmeny.md b/documentation/99-zmeny.md
index 947d2a4..e81cdcc 100644
--- a/documentation/99-zmeny.md
+++ b/documentation/99-zmeny.md
@@ -2,6 +2,30 @@
Nejnovejsi nahore.
+## 2026-08-26 - na aplikaci je videt, ktery build to je
+
+Nasazena aplikace o dva commity pozadu funguje a vypada skoro stejne jako
+nova. Bez oznaceni buildu se to nepozna a hleda se chyba tam, kde zadna neni.
+
+### Pridano
+
+- **Oznaceni buildu v pate postranniho menu**, pod tlacitkem Odhlasit se:
+ verze, cas buildu a cislo commitu. Text jde oznacit jednim kliknutim, aby
+ se dal poslat dal.
+- **Totez jako `` v hlavicce stranky.** V JS balicku uz
+ to je, ale ten se musi stahnout a rozbalit. Meta znacka je videt na jeden
+ `curl` bez prihlaseni - a prave to clovek potrebuje, kdyz zjistuje, co bezi
+ na produkci.
+- Hodnoty dosazuje Vite pri buildu (`define` a plugin `build-stamp`
+ ve `vite.config.ts`), takze neplati pro repozitar, ale pro ten konkretni
+ balicek.
+
+### Zmeneno
+
+- **`.dockerignore` uz nevylucuje `.git/`.** Build z nej cte cislo commitu;
+ bez nej by se poznal jen cas buildu, ne co v nem je. Do vysledneho image se
+ `.git` nedostane, ten se sklada z ciste zakladni image.
+
## 2026-08-26 - rebrand na WorkNuke
Prevzeti vizualniho smeru z predlohy. Popis, jak znacka funguje v kodu, je
diff --git a/vite.config.ts b/vite.config.ts
index c3c44a8..2acee19 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,11 +1,72 @@
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
+import { execSync } from 'node:child_process';
+import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
-import { defineConfig } from 'vite';
+import { defineConfig, type Plugin } from 'vite';
const here = path.dirname(fileURLToPath(import.meta.url));
+/**
+ * Oznaceni buildu.
+ *
+ * Bez nej se nasazena verze nepozna: aplikace bezi, jen je stara, a z venku
+ * to vypada stejne jako kdyz je nova. Prave to nas jednou stalo pul hodiny
+ * hadani, proc je na produkci porad stary vzhled.
+ *
+ * Cas buildu je hlavni udaj, protoze funguje vzdycky. Commit je bonus: do
+ * image se `.git` nekopiruje (`.dockerignore`), takze v produkcnim buildu
+ * chybi a zustane prazdny.
+ */
+const pkg: { version: string } = JSON.parse(
+ fs.readFileSync(path.resolve(here, 'package.json'), 'utf8'),
+);
+
+function gitCommit(): string {
+ try {
+ return execSync('git rev-parse --short HEAD', { stdio: ['ignore', 'pipe', 'ignore'] })
+ .toString()
+ .trim();
+ } catch {
+ // V containeru `.git` neni. Neni to chyba, jen se commit nedozvime.
+ return '';
+ }
+}
+
+const buildInfo = {
+ version: pkg.version,
+ builtAt: new Date().toISOString(),
+ commit: gitCommit(),
+};
+
+/**
+ * Vlozi oznaceni buildu i do hlavicky stranky.
+ *
+ * V JS balicku uz je, ale ten se musi stahnout a rozbalit. Meta znacka je
+ * videt na jeden `curl` bez prihlaseni - a presne to clovek potrebuje, kdyz
+ * zjistuje, co vlastne bezi na produkci.
+ */
+function buildStamp(): Plugin {
+ return {
+ name: 'build-stamp',
+ transformIndexHtml() {
+ return [
+ {
+ tag: 'meta',
+ attrs: {
+ name: 'app-build',
+ content: [buildInfo.version, buildInfo.builtAt, buildInfo.commit]
+ .filter(Boolean)
+ .join(' '),
+ },
+ injectTo: 'head',
+ },
+ ];
+ },
+ };
+}
+
export default defineConfig({
root: path.resolve(here, 'web'),
/**
@@ -14,7 +75,10 @@ export default defineConfig({
* podle ROOT_PATH a relativni odkazy se podle nej slozi spravne.
*/
base: './',
- plugins: [react(), tailwindcss()],
+ plugins: [react(), tailwindcss(), buildStamp()],
+ define: {
+ __BUILD_INFO__: JSON.stringify(buildInfo),
+ },
resolve: {
// Musi zustat v souladu s "paths" ve web/tsconfig.json
alias: {
diff --git a/web/src/components/dashboard/DashboardLayout.tsx b/web/src/components/dashboard/DashboardLayout.tsx
index 1040979..7769b7a 100644
--- a/web/src/components/dashboard/DashboardLayout.tsx
+++ b/web/src/components/dashboard/DashboardLayout.tsx
@@ -27,6 +27,7 @@ import { EventToasts } from '@/components/dashboard/EventToasts';
import { LiveIndicator } from '@/components/dashboard/LiveIndicator';
import { Logo } from '@/components/layout/Logo';
import { cn } from '@/lib/cn';
+import { buildInfo, versionLabel } from '@/config/version';
import { setActiveTenant, useActiveTenant } from '@/lib/tenant';
import { useApiQuery } from '@/lib/useApiQuery';
import type { Access } from '@/types/dashboard';
@@ -247,6 +248,18 @@ function DashboardShell() {
+ {versionLabel()} +
diff --git a/web/src/config/version.ts b/web/src/config/version.ts new file mode 100644 index 0000000..c4b83d6 --- /dev/null +++ b/web/src/config/version.ts @@ -0,0 +1,46 @@ +/** + * Ktera verze zrovna bezi. + * + * Hodnoty dosazuje Vite pri buildu (`define` ve `vite.config.ts`), takze + * neplati pro repozitar, ale **pro tenhle konkretni balicek** - presne to, + * co clovek potrebuje vedet, kdyz se diva na nasazenou aplikaci. + * + * Cas buildu je hlavni udaj: rozliseni na sekundy staci, aby se poznalo, ze + * nasazeni probehlo nebo neprobehlo. Commit je nepovinny, do image se `.git` + * nekopiruje. + */ + +const info: { version: string; builtAt: string; commit: string } = + typeof __BUILD_INFO__ === 'undefined' + ? { version: '0.0.0', builtAt: '', commit: '' } + : __BUILD_INFO__; + +export const buildInfo = info; + +/** + * Kratky popis verze pro patu postranniho menu. + * + * Ve vyvoji se cas buildu nepise: mel by tam kazdou minutu jiny a nikomu nic + * nerekne. Na nasazene aplikaci je naopak to hlavni. + */ +export function versionLabel(): string { + const parts = [`v${info.version}`]; + + if (import.meta.env.DEV) { + parts.push('vývoj'); + } else if (info.builtAt) { + parts.push( + new Date(info.builtAt).toLocaleString('cs-CZ', { + day: 'numeric', + month: 'numeric', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + }), + ); + } + + if (info.commit) parts.push(info.commit); + + return parts.join(' · '); +} diff --git a/web/src/vite-env.d.ts b/web/src/vite-env.d.ts index 05dd671..591bd29 100644 --- a/web/src/vite-env.d.ts +++ b/web/src/vite-env.d.ts @@ -1,6 +1,12 @@ ///