chore: adds versions and copyright #16

Merged
admin merged 1 commits from feat/sidebar-versions-copyright into main 2026-03-17 03:44:11 +00:00
8 changed files with 74 additions and 7 deletions
Showing only changes of commit 6dd8c01f5e - Show all commits

View File

@@ -1,6 +1,6 @@
{ {
"name": "@family-budget/backend", "name": "@family-budget/backend",
"version": "0.1.0", "version": "0.5.12",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "tsx watch src/app.ts", "dev": "tsx watch src/app.ts",

View File

@@ -1,3 +1,5 @@
import fs from 'fs';
import path from 'path';
import express from 'express'; import express from 'express';
import cookieParser from 'cookie-parser'; import cookieParser from 'cookie-parser';
import cors from 'cors'; import cors from 'cors';
@@ -5,6 +7,10 @@ import { config } from './config';
import { runMigrations } from './db/migrate'; import { runMigrations } from './db/migrate';
import { requireAuth } from './middleware/auth'; import { requireAuth } from './middleware/auth';
const pkg = JSON.parse(
fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'),
) as { version: string };
import authRouter from './routes/auth'; import authRouter from './routes/auth';
import importRouter from './routes/import'; import importRouter from './routes/import';
import importsRouter from './routes/imports'; import importsRouter from './routes/imports';
@@ -24,6 +30,10 @@ app.use(cors({ origin: true, credentials: true }));
// Auth routes (login is public; me/logout apply auth internally) // Auth routes (login is public; me/logout apply auth internally)
app.use('/api/auth', authRouter); app.use('/api/auth', authRouter);
app.get('/api/version', (_req, res) => {
res.json({ version: pkg.version });
});
// All remaining /api routes require authentication // All remaining /api routes require authentication
app.use('/api', requireAuth); app.use('/api', requireAuth);
app.use('/api/import', importRouter); app.use('/api/import', importRouter);

View File

@@ -1,6 +1,6 @@
{ {
"name": "@family-budget/frontend", "name": "@family-budget/frontend",
"version": "0.1.0", "version": "0.8.5",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@@ -0,0 +1,5 @@
import { api } from './client';
export async function getBackendVersion(): Promise<{ version: string }> {
return api.get<{ version: string }>('/api/version');
}

View File

@@ -1,13 +1,21 @@
import { useState, type ReactNode } from 'react'; import { useState, useEffect, type ReactNode } from 'react';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import { useAuth } from '../context/AuthContext'; import { useAuth } from '../context/AuthContext';
import { getBackendVersion } from '../api/version';
export function Layout({ children }: { children: ReactNode }) { export function Layout({ children }: { children: ReactNode }) {
const { user, logout } = useAuth(); const { user, logout } = useAuth();
const [drawerOpen, setDrawerOpen] = useState(false); const [drawerOpen, setDrawerOpen] = useState(false);
const [beVersion, setBeVersion] = useState<string | null>(null);
const closeDrawer = () => setDrawerOpen(false); const closeDrawer = () => setDrawerOpen(false);
useEffect(() => {
getBackendVersion()
.then((r) => setBeVersion(r.version))
.catch(() => setBeVersion(null));
}, []);
return ( return (
<div className="layout"> <div className="layout">
<button <button
@@ -86,10 +94,18 @@ export function Layout({ children }: { children: ReactNode }) {
</nav> </nav>
<div className="sidebar-footer"> <div className="sidebar-footer">
<span className="sidebar-user">{user?.login}</span> <div className="sidebar-footer-top">
<button className="btn-logout" onClick={() => logout()}> <span className="sidebar-user">{user?.login}</span>
Выход <button className="btn-logout" onClick={() => logout()}>
</button> Выход
</button>
</div>
<div className="sidebar-footer-bottom">
<span className="sidebar-version">
FE {__FE_VERSION__} · BE {beVersion ?? '…'}
</span>
<span className="sidebar-copyright">© 2025 Семейный бюджет</span>
</div>
</div> </div>
</aside> </aside>

View File

@@ -138,11 +138,34 @@ body {
.sidebar-footer { .sidebar-footer {
padding: 16px 20px; padding: 16px 20px;
border-top: 1px solid rgba(255, 255, 255, 0.1); border-top: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
flex-direction: column;
gap: 10px;
}
.sidebar-footer-top {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
} }
.sidebar-footer-bottom {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 11px;
color: var(--color-sidebar-text);
opacity: 0.85;
}
.sidebar-version {
font-family: ui-monospace, monospace;
}
.sidebar-copyright {
font-size: 10px;
}
.sidebar-user { .sidebar-user {
font-size: 13px; font-size: 13px;
color: var(--color-sidebar-text); color: var(--color-sidebar-text);

View File

@@ -1 +1,3 @@
/// <reference types="vite/client" /> /// <reference types="vite/client" />
declare const __FE_VERSION__: string;

View File

@@ -1,8 +1,19 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
import { readFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const pkg = JSON.parse(
readFileSync(join(__dirname, 'package.json'), 'utf-8'),
) as { version: string };
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
define: {
__FE_VERSION__: JSON.stringify(pkg.version),
},
server: { server: {
port: 5173, port: 5173,
proxy: { proxy: {