Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e7340c1
feat: new stores add, version and user stores updated
RAMTO Dec 22, 2025
8f19d4e
feat: service and modal for compatibility checks added
RAMTO Dec 22, 2025
90c4aaa
chore: flow in modals updated, axios interceptor added
RAMTO Dec 22, 2025
36e3534
chore: check version flow updated
RAMTO Dec 22, 2025
cbcafe1
chore: disconnect logic updated to track per org
RAMTO Dec 22, 2025
80339d1
chore: mandatory update flow updated
RAMTO Dec 22, 2025
7ca8b90
feat: reconnect logic added
RAMTO Dec 22, 2025
eb921e2
feat: organization settings UI updated
RAMTO Dec 23, 2025
9c980f5
fix: build errors fixed
RAMTO Dec 23, 2025
c4e1f86
chore: integration and edge cases handled
RAMTO Dec 23, 2025
3d7d50d
chore: more user feedback added
RAMTO Dec 23, 2025
26eb0c2
chore: refactor and improvements
RAMTO Dec 23, 2025
93b28e6
chore: small refactor
RAMTO Dec 23, 2025
9f54503
chore: guard response updated
RAMTO Dec 23, 2025
f929296
version check feature fixes
Dec 29, 2025
1fb3b5c
fixed test variable name missmatch
Dec 29, 2025
de50320
remove Live status from org Connection Status
Dec 30, 2025
324053c
chore: update prompt fixes
Jan 5, 2026
7d1f86e
chore: remove quit, use disconnect instead
RAMTO Jan 5, 2026
7c313f4
chore: code clean up
RAMTO Jan 6, 2026
b908477
chore: setTimeout removed
RAMTO Jan 7, 2026
04ebd0c
chore: event removed
RAMTO Jan 7, 2026
500a36e
chore: toast type updated
RAMTO Jan 7, 2026
6f71773
chore: payload updated
RAMTO Jan 7, 2026
73b5bb6
chore: refactor function for setting org version
RAMTO Jan 7, 2026
a575705
chore: toast updated from info to warning
RAMTO Jan 7, 2026
2d99c32
fix: fix for socket
RAMTO Jan 7, 2026
9b34260
chore: comment added for clarification
RAMTO Jan 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ describe('FrontendVersionGuard', () => {
expect(error).toBeInstanceOf(HttpException);
expect((error as HttpException).getStatus()).toBe(426);
const response = (error as HttpException).getResponse() as Record<string, unknown>;
expect(response.minimumVersion).toBe('1.0.0');
expect(response.currentVersion).toBe('0.9.0');
expect(response.minimumSupportedVersion).toBe('1.0.0');
}
});

Expand Down Expand Up @@ -487,4 +486,3 @@ describe('FrontendVersionGuard', () => {
});
});
});

5 changes: 3 additions & 2 deletions back-end/apps/api/src/guards/frontend-version.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class FrontendVersionGuard implements CanActivate {
const request = context.switchToHttp().getRequest();
const frontendVersion = request.headers['x-frontend-version'];
const minimumVersion = this.configService.get<string>('MINIMUM_SUPPORTED_FRONTEND_VERSION');
const latestVersion = this.configService.get<string>('LATEST_SUPPORTED_FRONTEND_VERSION');
const clientIp = request.headers['x-forwarded-for'] || request.ip || 'unknown';

const UPGRADE_REQUIRED = 426;
Expand Down Expand Up @@ -95,8 +96,8 @@ export class FrontendVersionGuard implements CanActivate {
statusCode: UPGRADE_REQUIRED,
message: `Your application version (${cleanFrontendVersion}) is no longer supported. Minimum required version is ${cleanMinimumVersion}. Please update your application.`,
error: 'Upgrade Required',
minimumVersion: cleanMinimumVersion,
currentVersion: cleanFrontendVersion,
minimumSupportedVersion: cleanMinimumVersion,
latestSupportedVersion: latestVersion,
updateUrl,
},
UPGRADE_REQUIRED,
Expand Down
2 changes: 2 additions & 0 deletions front-end/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"msgpackr": "1.11.5",
"node-forge": "1.3.1",
"pinia": "2.3.1",
"semver": "7.7.1",
"socket.io-client": "4.8.1",
"unzipper": "0.12.3",
"vue-router": "4.5.0",
Expand All @@ -62,6 +63,7 @@
"@types/bootstrap": "5.2.10",
"@types/node": "22.12.0",
"@types/node-forge": "1.3.11",
"@types/semver": "7.5.8",
"@types/unzipper": "0.10.10",
"@vitejs/plugin-vue": "5.2.1",
"@vitest/coverage-v8": "3.0.5",
Expand Down
49 changes: 30 additions & 19 deletions front-end/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ import useLoader from '@renderer/composables/useLoader';
import useSetupStores from '@renderer/composables/user/useSetupStores';
import useRecoveryPhraseHashMigrate from '@renderer/composables/useRecoveryPhraseHashMigrate';
import useDefaultOrganization from '@renderer/composables/user/useDefaultOrganization';
import useVersionCheck from '@renderer/composables/useVersionCheck';

import { getUseKeychain } from '@renderer/services/safeStorageService';
import { getUsersCount, resetDataLocal } from '@renderer/services/userService';
import { getStoredClaim } from '@renderer/services/claimService';
import { checkCompatibilityAcrossOrganizations } from '@renderer/services/organization/versionCompatibility';
import {
getVersionStatusForOrg,
organizationCompatibilityResults,
} from '@renderer/stores/versionState';

import AutoLoginInOrganization from '@renderer/components/Organization/AutoLoginInOrganization.vue';
import AppUpdate from './components/AppUpdate.vue';
Expand All @@ -31,6 +37,7 @@ const tryAutoLogin = useAutoLogin();
const setupStores = useSetupStores();
const { select: selectDefaultOrganization } = useDefaultOrganization();
const { redirectIfRequiredKeysToMigrate } = useRecoveryPhraseHashMigrate();
const { performVersionCheck, getAllOrganizationVersionData } = useVersionCheck();

/* State */
const importantNoteRef = ref<InstanceType<typeof ImportantNote> | null>(null);
Expand All @@ -56,8 +63,83 @@ const handleBeginMigrationReadyState = async () => {
}

await setupStores();

if (user.personal?.isLoggedIn && user.organizations.length > 0) {
await checkAllOrganizationVersions();
}
};

async function checkAllOrganizationVersions(): Promise<void> {
try {
const versionChecks = user.organizations.map(org => performVersionCheck(org.serverUrl));
await Promise.allSettled(versionChecks);

const orgsRequiringUpdate = user.organizations.filter(org => {
const status = getVersionStatusForOrg(org.serverUrl);
return status === 'updateAvailable' || status === 'belowMinimum';
});

if (orgsRequiringUpdate.length > 0) {
await checkCompatibilityForUpgrades(orgsRequiringUpdate);
}
} catch (error) {
console.error('Failed to check organization versions on launch:', error);
}
}

async function checkCompatibilityForUpgrades(
orgsRequiringUpdate: typeof user.organizations,
): Promise<void> {
const allVersionData = getAllOrganizationVersionData();

for (const org of orgsRequiringUpdate) {
const versionData = allVersionData[org.serverUrl];
if (!versionData || !versionData.latestSupportedVersion) {
continue;
}

try {
const compatibilityResult = await checkCompatibilityAcrossOrganizations(
versionData.latestSupportedVersion,
org.serverUrl, // Exclude the current org from conflict check
);

organizationCompatibilityResults.value[org.serverUrl] = compatibilityResult;

if (compatibilityResult.hasConflict) {
console.warn(
`[${new Date().toISOString()}] COMPATIBILITY_CHECK App launch check for ${org.serverUrl}`,
);
console.warn(
`Conflicts found with ${compatibilityResult.conflicts.length} organization(s):`,
);
compatibilityResult.conflicts.forEach(conflict => {
console.warn(
` - ${conflict.organizationName} (${conflict.serverUrl}): Latest supported: ${conflict.latestSupportedVersion}`,
);
});
} else {
console.log(
`[${new Date().toISOString()}] COMPATIBILITY_CHECK App launch check for ${org.serverUrl}: No conflicts`,
);
}
} catch (error) {
console.error(`Failed to check compatibility for ${org.serverUrl}:`, error);
organizationCompatibilityResults.value[org.serverUrl] = null;
}
}

if (orgsRequiringUpdate.length > 1) {
console.log(
`[${new Date().toISOString()}] MULTIPLE_ORGS_REQUIRING_UPDATE: ${orgsRequiringUpdate.length} organization(s) require updates`,
);
orgsRequiringUpdate.forEach(org => {
const status = getVersionStatusForOrg(org.serverUrl);
console.log(` - ${org.nickname || org.serverUrl}: ${status}`);
});
}
}

/* Hooks */
onMounted(async () => {
try {
Expand Down
Loading
Loading