Merge pull request 'feature/version-badge' (#40) from feature/version-badge into master
Reviewed-on: #40
This commit was merged in pull request #40.
This commit is contained in:
12
Dockerfile
12
Dockerfile
@@ -7,6 +7,15 @@ RUN cd client && npm install
|
|||||||
COPY client/ ./client/
|
COPY client/ ./client/
|
||||||
RUN cd client && npm run build
|
RUN cd client && npm run build
|
||||||
|
|
||||||
|
# ── Version metadata ──────────────────────────────────────────────────────────
|
||||||
|
# Pass these at build time:
|
||||||
|
# docker build --build-arg GIT_SHA=$(git rev-parse HEAD) \
|
||||||
|
# --build-arg BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) .
|
||||||
|
ARG GIT_SHA=dev
|
||||||
|
ARG BUILD_TIME=unknown
|
||||||
|
RUN echo "{\"sha\":\"${GIT_SHA}\",\"shortSha\":\"${GIT_SHA:0:7}\",\"buildTime\":\"${BUILD_TIME}\"}" \
|
||||||
|
> /build/client/dist/version.json
|
||||||
|
|
||||||
FROM node:20-alpine AS production
|
FROM node:20-alpine AS production
|
||||||
RUN apk add --no-cache chromium nss freetype harfbuzz ca-certificates ttf-freefont
|
RUN apk add --no-cache chromium nss freetype harfbuzz ca-certificates ttf-freefont
|
||||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||||
@@ -25,5 +34,6 @@ COPY demo/ ./demo/
|
|||||||
COPY client/public/static ./client/dist/static
|
COPY client/public/static ./client/dist/static
|
||||||
RUN mkdir -p /data
|
RUN mkdir -p /data
|
||||||
EXPOSE 3001
|
EXPOSE 3001
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD wget -qO- http://localhost:3001/api/health || exit 1
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD wget -qO- http://localhost:3001/api/health || exit 1
|
||||||
CMD ["node", "server.js"]
|
CMD ["node", "server.js"]
|
||||||
|
|||||||
5
client/public/version.json
Normal file
5
client/public/version.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"sha": "dev",
|
||||||
|
"shortSha": "dev",
|
||||||
|
"buildTime": null
|
||||||
|
}
|
||||||
@@ -42,8 +42,13 @@ function GiteaIcon() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AppFooter() {
|
function AppFooter({ version }) {
|
||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
|
const sha = version?.shortSha || null;
|
||||||
|
const built = version?.buildTime
|
||||||
|
? new Date(version.buildTime).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<style>{`
|
<style>{`
|
||||||
@@ -53,13 +58,27 @@ function AppFooter() {
|
|||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
<footer style={sf.footer}>
|
<footer style={sf.footer}>
|
||||||
<span style={sf.copy}>© {year} Jason Stedwell</span>
|
<span style={sf.copy}>© {year} Jason Stedwell</span>
|
||||||
<span style={sf.sep}>·</span>
|
<span style={sf.sep}>·</span>
|
||||||
<DevTicker />
|
<DevTicker />
|
||||||
<span style={sf.sep}>·</span>
|
<span style={sf.sep}>·</span>
|
||||||
<a href={REPO_URL} target="_blank" rel="noopener noreferrer" style={sf.link}>
|
<a href={REPO_URL} target="_blank" rel="noopener noreferrer" style={sf.link}>
|
||||||
<GiteaIcon /> cpas
|
<GiteaIcon /> cpas
|
||||||
</a>
|
</a>
|
||||||
|
{sha && sha !== 'dev' && (
|
||||||
|
<>
|
||||||
|
<span style={sf.sep}>·</span>
|
||||||
|
<a
|
||||||
|
href={`${REPO_URL}/commit/${version.sha}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
style={sf.link}
|
||||||
|
title={built ? `Built ${built}` : 'View commit'}
|
||||||
|
>
|
||||||
|
{sha}
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</footer>
|
</footer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -129,6 +148,14 @@ const sf = {
|
|||||||
export default function App() {
|
export default function App() {
|
||||||
const [tab, setTab] = useState('dashboard');
|
const [tab, setTab] = useState('dashboard');
|
||||||
const [showReadme, setShowReadme] = useState(false);
|
const [showReadme, setShowReadme] = useState(false);
|
||||||
|
const [version, setVersion] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/version.json')
|
||||||
|
.then(r => r.ok ? r.json() : null)
|
||||||
|
.then(v => { if (v) setVersion(v); })
|
||||||
|
.catch(() => {});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
@@ -156,7 +183,7 @@ export default function App() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AppFooter />
|
<AppFooter version={version} />
|
||||||
|
|
||||||
{showReadme && <ReadmeModal onClose={() => setShowReadme(false)} />}
|
{showReadme && <ReadmeModal onClose={() => setShowReadme(false)} />}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
13
server.js
13
server.js
@@ -29,8 +29,19 @@ function audit(action, entityType, entityId, performedBy, details) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Version info (written by Dockerfile at build time) ───────────────────────
|
||||||
|
// Falls back to { sha: 'dev' } when running outside a Docker build (local dev).
|
||||||
|
let BUILD_VERSION = { sha: 'dev', shortSha: 'dev', buildTime: null };
|
||||||
|
try {
|
||||||
|
BUILD_VERSION = require('./client/dist/version.json');
|
||||||
|
} catch (_) { /* pre-build or local dev — stub values are fine */ }
|
||||||
|
|
||||||
// Health
|
// Health
|
||||||
app.get('/api/health', (req, res) => res.json({ status: 'ok', timestamp: new Date().toISOString() }));
|
app.get('/api/health', (req, res) => res.json({
|
||||||
|
status: 'ok',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
version: BUILD_VERSION,
|
||||||
|
}));
|
||||||
|
|
||||||
// ── Employees ────────────────────────────────────────────────────────────────
|
// ── Employees ────────────────────────────────────────────────────────────────
|
||||||
app.get('/api/employees', (req, res) => {
|
app.get('/api/employees', (req, res) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user