diff --git a/package-lock.json b/package-lock.json index a3195012..8193800a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,6 +69,7 @@ "react-hook-form": "^7.60.0", "react-i18next": "^15.7.3", "react-resizable-panels": "^3.0.3", + "react-simple-keyboard": "^3.8.120", "react-xtermjs": "^1.0.10", "sonner": "^2.0.7", "speakeasy": "^2.0.0", @@ -7818,6 +7819,16 @@ "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, + "node_modules/react-simple-keyboard": { + "version": "3.8.120", + "resolved": "https://registry.npmjs.org/react-simple-keyboard/-/react-simple-keyboard-3.8.120.tgz", + "integrity": "sha512-VREEGZWXUeqRKvRVg0n8hmoAqz/TSWZEs5UwbfLuan4yKvOQZUFHtS11QGnvIVYjkThh+JYslO2CHT4Lxf5d0w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-style-singleton": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", diff --git a/package.json b/package.json index 74f44274..0c4ff32c 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,15 @@ { "name": "termix", "private": true, - "version": "1.6.0", - "description": "Open-source server management platform with SSH terminal access, tunnel management, and file editing", - "author": { - "name": "LukeGus", - "email": "support@termix.site" - }, + "version": "0.0.0", "type": "module", - "main": "electron/main-simple.cjs", "scripts": { "dev": "vite", - "build": "vite build && npm run build:backend", - "build:frontend": "vite build", + "build": "vite build", "build:backend": "tsc -p tsconfig.node.json", "dev:backend": "tsc -p tsconfig.node.json && node ./dist/backend/starter.js", "lint": "eslint .", - "preview": "vite preview", - "electron": "electron .", - "electron:dev": "npm run build:backend && NODE_ENV=development electron .", - "electron:package": "npm run build && electron-packager . Termix --platform=win32 --arch=x64 --out=release --overwrite --ignore=\"^/src|^/public|^/node_modules|^/repo-images\" --prune=true --icon=public/favicon.ico", - "electron:package:win": "npm run build && electron-packager . Termix --platform=win32 --arch=x64 --out=release --overwrite --ignore=\"^/src|^/public|^/node_modules|^/repo-images\" --prune=true --icon=public/favicon.ico", - "electron:package:mac": "npm run build && electron-packager . Termix --platform=darwin --arch=universal --out=release --overwrite --ignore=\"^/src|^/public|^/node_modules|^/repo-images\" --prune=true --icon=public/icon.png", - "electron:package:linux": "npm run build && electron-packager . Termix --platform=linux --arch=x64 --out=release --overwrite --ignore=\"^/src|^/public|^/node_modules|^/repo-images\" --prune=true --icon=public/icon.png", - "electron:package:all": "npm run build && npm run electron:package:win && npm run electron:package:mac && npm run electron:package:linux" + "preview": "vite preview" }, "dependencies": { "@hookform/resolvers": "^5.1.1", @@ -44,6 +30,11 @@ "@radix-ui/react-switch": "^1.2.5", "@radix-ui/react-tabs": "^1.1.12", "@radix-ui/react-tooltip": "^1.2.8", + "@tailwindcss/vite": "^4.1.11", + "@types/bcryptjs": "^2.4.6", + "@types/multer": "^2.0.0", + "@types/qrcode": "^1.5.5", + "@types/speakeasy": "^2.0.10", "@uiw/codemirror-extensions-hyper-link": "^4.24.1", "@uiw/codemirror-extensions-langs": "^4.24.1", "@uiw/codemirror-themes": "^4.24.1", @@ -82,43 +73,34 @@ "react-hook-form": "^7.60.0", "react-i18next": "^15.7.3", "react-resizable-panels": "^3.0.3", - "react-responsive": "^10.0.1", "react-simple-keyboard": "^3.8.120", "react-xtermjs": "^1.0.10", "sonner": "^2.0.7", "speakeasy": "^2.0.0", "ssh2": "^1.16.0", "tailwind-merge": "^3.3.1", + "tailwindcss": "^4.1.11", "validator": "^13.15.15", "ws": "^8.18.3", - "xterm": "^5.3.0", "zod": "^4.0.5" }, "devDependencies": { "@eslint/js": "^9.34.0", - "@tailwindcss/vite": "^4.1.12", - "@types/bcryptjs": "^2.4.6", "@types/better-sqlite3": "^7.6.13", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/jsonwebtoken": "^9.0.10", - "@types/multer": "^2.0.0", "@types/node": "^24.3.0", - "@types/qrcode": "^1.5.5", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", - "@types/speakeasy": "^2.0.10", "@types/ssh2": "^1.15.5", "@types/ws": "^8.18.1", "@vitejs/plugin-react-swc": "^3.10.2", "autoprefixer": "^10.4.21", - "electron": "^31.7.0", - "electron-packager": "^17.1.2", "eslint": "^9.34.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", - "tailwindcss": "^4.1.12", "ts-node": "^10.9.2", "tw-animate-css": "^1.3.5", "typescript": "~5.9.2", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index bb804eac..ce060c7f 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -199,6 +199,7 @@ "register": "Register", "username": "Username", "password": "Password", + "version" : "Version", "confirmPassword": "Confirm Password", "back": "Back", "email": "Email", diff --git a/src/backend/database/database.ts b/src/backend/database/database.ts index a56faf1d..40466490 100644 --- a/src/backend/database/database.ts +++ b/src/backend/database/database.ts @@ -147,22 +147,16 @@ app.get('/health', (req, res) => { app.get('/version', async (req, res) => { let localVersion = process.env.VERSION; - - // Fallback to package.json version if env variable not set + if (!localVersion) { try { const packagePath = path.resolve(process.cwd(), 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8')); localVersion = packageJson.version; - logger.info(`Using version from package.json: ${localVersion}`); } catch (error) { logger.error('Failed to read version from package.json:', error); } } - - // Debug logging - logger.debug(`Final version: ${localVersion}`); - logger.debug(`Working directory: ${process.cwd()}`); if (!localVersion) { logger.error('No version information available'); @@ -186,6 +180,7 @@ app.get('/version', async (req, res) => { const response = { status: localVersion === remoteVersion ? 'up_to_date' : 'requires_update', + localVersion: localVersion, version: remoteVersion, latest_release: { tag_name: releaseData.data.tag_name, diff --git a/src/ui/Desktop/User/UserProfile.tsx b/src/ui/Desktop/User/UserProfile.tsx index 8fdc0d31..2cc462b0 100644 --- a/src/ui/Desktop/User/UserProfile.tsx +++ b/src/ui/Desktop/User/UserProfile.tsx @@ -8,11 +8,13 @@ import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs.tsx import {User, Shield, Key, AlertCircle} from "lucide-react"; import {TOTPSetup} from "@/ui/Desktop/User/TOTPSetup.tsx"; import {getUserInfo} from "@/ui/main-axios.ts"; +import {getVersionInfo} from "@/ui/main-axios.ts"; import {toast} from "sonner"; import {PasswordReset} from "@/ui/Desktop/User/PasswordReset.tsx"; import {useTranslation} from "react-i18next"; import {LanguageSwitcher} from "@/components/LanguageSwitcher.tsx"; + interface UserProfileProps { isTopbarOpen?: boolean; } @@ -27,11 +29,23 @@ export function UserProfile({isTopbarOpen = true}: UserProfileProps) { } | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [versionInfo, setVersionInfo] = useState<{ version: string } | null>(null); + useEffect(() => { fetchUserInfo(); + fetchVersion(); }, []); + const fetchVersion = async () => { + try { + const info = await getVersionInfo(); + setVersionInfo({version: info.localVersion}); + } catch (err) { + console.error("Failed to load version info", err); + } + }; + const fetchUserInfo = async () => { setLoading(true); setError(null); @@ -146,15 +160,22 @@ export function UserProfile({isTopbarOpen = true}: UserProfileProps) { )}

+
+ +

+ {versionInfo?.version || t('common.loading')} +

+
+ - +

{t('profile.selectPreferredLanguage')}

- +
diff --git a/src/ui/main-axios.ts b/src/ui/main-axios.ts index 37ffb7b1..705260ae 100644 --- a/src/ui/main-axios.ts +++ b/src/ui/main-axios.ts @@ -142,6 +142,7 @@ interface AuthResponse { } interface UserInfo { + totp_enabled: boolean; id: string; username: string; is_admin: boolean;