changed the navbar to work.
All checks were successful
Build Frontend / Build Frontend (push) Successful in 35s

This commit is contained in:
2026-01-11 02:48:49 +00:00
parent e7f909678b
commit 4b00a2b4fe
5 changed files with 897 additions and 3 deletions

225
DEPLOY_NAVBAR_FIX.md Normal file
View File

@@ -0,0 +1,225 @@
# Deploy Navbar Login Fix - Quick Checklist
## 🎯 What This Fixes
**Problem:** After logging in via Steam, the navbar doesn't update to show the logged-in state (avatar, username, balance).
**Solution:** WebSocket now triggers auth state refresh when connection is established.
---
## ✅ Pre-Deployment Checklist
- [x] Source code changes committed
- [x] Frontend build successful
- [x] Verification script passed
- [ ] Backend is running
- [ ] Ready to deploy
---
## 🚀 Quick Deploy (Production)
### On Your Server
```bash
# 1. Navigate to project
cd /path/to/TurboTrades
# 2. Pull latest changes
git pull origin main
# 3. Build frontend
cd frontend
npm install # only if package.json changed
npm run build
# 4. Deploy to web root
sudo cp -r dist/* /var/www/html/turbotrades/
# 5. Verify deployment
curl -I https://turbotrades.dev # Should return 200
# Done! 🎉
```
---
## 🧪 Testing After Deployment
### 1. Clear Browser Cache
- **Chrome/Edge:** `Ctrl + Shift + R` (Windows/Linux) or `Cmd + Shift + R` (Mac)
- **Firefox:** `Ctrl + Shift + R` (Windows/Linux) or `Cmd + Shift + R` (Mac)
- **Or use Incognito/Private mode**
### 2. Open Browser Console
Press `F12`**Console** tab
### 3. Clear All Site Data
In DevTools:
- **Application** tab → **Clear storage****Clear site data**
### 4. Test Login Flow
1. Click "Login to Steam" button
2. Complete Steam authentication
3. Watch console for these logs:
```
🔵 Auth store initialize called - isInitialized: false
🔵 fetchUser called - fetching user from /api/auth/me
✅ fetchUser response: { success: true, user: {...} }
✅ Setting user in auth store: { steamId: "...", ... }
🔵 fetchUser complete - isAuthenticated: true
Connecting to WebSocket: wss://api.turbotrades.dev/ws
WebSocket connected
🟢 WebSocket received 'connected' message: { ... }
🔵 Calling authStore.fetchUser() from WebSocket connected handler
🔵 fetchUser called - fetching user from /api/auth/me
✅ fetchUser response: { success: true, user: {...} }
```
### 5. Verify Navbar
Should now show:
- ✅ Your Steam avatar
- ✅ Your username
- ✅ Your balance with deposit button
- ✅ User dropdown menu (Profile, Inventory, etc.)
---
## 🔧 If It Doesn't Work
### Common Issues
#### Navbar Still Not Updating
```bash
# 1. Hard refresh browser (clear all cache)
Ctrl + Shift + Delete → Clear all browsing data
# 2. Check if build was actually deployed
curl -I https://turbotrades.dev/assets/index-*.js
# 3. Check console for errors
# Look for CORS errors, 404s, or auth failures
```
#### WebSocket Not Connecting
```bash
# Check Nginx config has WebSocket proxy
sudo cat /etc/nginx/sites-available/turbotrades-api
# Should have:
location /ws {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://127.0.0.1:3000/ws;
}
# Reload Nginx if changed
sudo nginx -t
sudo systemctl reload nginx
```
#### Backend Issues
```bash
# Check backend is running
pm2 status
# Check logs
pm2 logs turbotrades-backend --lines 50
# Restart if needed
pm2 restart turbotrades-backend
```
---
## 📊 Verification Script
Run this to check everything is ready:
```bash
node scripts/verify-login-fix.cjs
```
**Expected output:**
```
✅ WebSocket store has the navbar login fix
✅ Frontend build exists and looks valid
✅ API Health Endpoint: Reachable
✅ WebSocket: Connection successful
All checks passed! ✨
```
---
## 🔄 Rollback (If Needed)
```bash
# Revert commits
git revert HEAD~2..HEAD
git push origin main
# Rebuild and redeploy
cd frontend
npm run build
sudo cp -r dist/* /var/www/html/turbotrades/
```
---
## 📝 Files Changed
- `frontend/src/stores/websocket.js` - Added `authStore.fetchUser()` call on WebSocket connected
- `frontend/src/stores/auth.js` - Added debug logging for troubleshooting
---
## 🎓 How It Works
1. **User logs in** → Steam OAuth → Backend sets session cookie
2. **Frontend loads**`App.vue` calls `authStore.initialize()`
3. **WebSocket connects** → Server sends `{ type: "connected", data: {...} }`
4. **NEW:** WebSocket handler calls `authStore.fetchUser()`
5. **Auth store updates**`isAuthenticated = true`
6. **Navbar reactively updates** → Shows logged-in UI
---
## ✨ Success Criteria
- [x] Source code has the fix
- [x] Frontend build is recent
- [x] API endpoints reachable
- [x] WebSocket connection works
- [ ] **User can login and see navbar update**
---
**Status:** Ready to Deploy
**Build Date:** 2025-01-10
**Estimated Deploy Time:** 5 minutes
**Risk Level:** Low (only frontend changes)
---
## 🆘 Need Help?
Check these docs:
- Full details: `NAVBAR_LOGIN_FIX.md`
- Troubleshooting: `DEPLOYMENT_FIXES.md`
- Environment setup: `ENV_SETUP.md`
Or check the logs:
```bash
pm2 logs turbotrades-backend
```

337
NAVBAR_LOGIN_FIX.md Normal file
View File

@@ -0,0 +1,337 @@
# Navbar Login Fix - WebSocket Authentication State Update
## Problem Description
After successfully logging in via Steam, the WebSocket would connect and receive user data from the server, but the navbar would not update to show the logged-in state.
### Root Cause
The WebSocket `connected` message handler was receiving user data (steamId, username, userId) but was only logging it to the console. It was **not updating the auth store**, which the navbar component relies on to display authentication state.
**Console logs showed:**
```
🔵 AnnouncementBanner mounted - loading announcements...
WebSocket connected
WebSocket message received: { type: "connected", data: {...} }
Server confirmed connection: { steamId: "...", username: "...", userId: "..." }
```
But the navbar remained in logged-out state because `authStore.isAuthenticated` was still `false`.
---
## Solution
### Changes Made
#### 1. WebSocket Store (`frontend/src/stores/websocket.js`)
- **Modified the `"connected"` message handler** to call `authStore.fetchUser()` when the WebSocket connection is confirmed
- **Fixed duplicate `authStore` declarations** by declaring it once at the top of `handleMessage` function
- **Added debug logging** to trace the authentication flow
**Before:**
```javascript
case "connected":
console.log("Server confirmed connection:", payload);
break;
```
**After:**
```javascript
const handleMessage = (data) => {
const { type, data: payload, timestamp } = data;
const authStore = useAuthStore(); // Declared once at top
switch (type) {
case "connected":
console.log("🟢 WebSocket received 'connected' message:", payload);
// Fetch updated user data from the auth endpoint
console.log("🔵 Calling authStore.fetchUser() from WebSocket connected handler");
authStore.fetchUser();
break;
case "balance_update":
// Now uses authStore declared at top
if (payload?.balance !== undefined) {
authStore.updateBalance(payload.balance);
}
break;
// ... rest of cases
}
};
```
#### 2. Auth Store (`frontend/src/stores/auth.js`)
- **Added debug logging** to trace authentication initialization and user fetch operations
**Added logs:**
```javascript
console.log("🔵 fetchUser called - fetching user from /api/auth/me");
console.log("✅ fetchUser response:", response.data);
console.log("✅ Setting user in auth store:", response.data.user);
console.log("🔵 fetchUser complete - isAuthenticated:", isAuthenticated.value);
console.log("🔵 Auth store initialize called - isInitialized:", isInitialized.value);
```
---
## How It Works Now
### Authentication Flow After Steam Login
1. **User clicks "Login to Steam"** button
- Frontend redirects to: `${VITE_API_URL}/api/auth/steam`
2. **Steam OAuth flow completes**
- Backend receives callback from Steam
- Backend creates session and sets HTTP-only cookie
- Backend redirects user back to frontend
3. **Frontend loads** (`App.vue` `onMounted`)
- `authStore.initialize()` → calls `fetchUser()` → requests `/api/auth/me`
- `wsStore.connect()` → establishes WebSocket connection
4. **WebSocket connects successfully**
- Backend sends `{ type: "connected", data: { steamId, username, userId } }`
5. **WebSocket receives "connected" message****NEW**
- Handler calls `authStore.fetchUser()`
- Fetches user data from `/api/auth/me` with credentials
- Updates `authStore.user` and sets `isAuthenticated = true`
6. **Navbar reactively updates** 🎉
- `authStore.isAuthenticated` changes from `false``true`
- Vue's reactivity triggers navbar to show user menu, avatar, balance, etc.
---
## Deployment Instructions
### 1. Build the Frontend
```bash
cd frontend
npm run build
```
**Expected output:**
```
✓ 1588 modules transformed.
✓ built in 2.80s
```
### 2. Deploy to Production
**Option A: Manual deployment on server**
```bash
# SSH into your server
ssh user@your-server
# Navigate to project
cd /path/to/TurboTrades/frontend
# Pull latest changes
git pull origin main
# Build
npm install # if dependencies changed
npm run build
# Copy to web root
sudo cp -r dist/* /var/www/html/turbotrades/
```
**Option B: CI/CD Pipeline**
```bash
# Commit and push changes
git add frontend/src/stores/websocket.js frontend/src/stores/auth.js
git commit -m "Fix navbar not updating after Steam login - WebSocket auth state sync"
git push origin main
# Your CI/CD pipeline will automatically:
# 1. Pull changes
# 2. Build frontend
# 3. Deploy to production
```
### 3. Clear Browser Cache
After deployment, users need to hard-refresh to get the new JavaScript bundle:
- **Windows/Linux:** `Ctrl + Shift + R` or `Ctrl + F5`
- **Mac:** `Cmd + Shift + R`
- **Or use Incognito/Private mode** for testing
---
## Testing the Fix
### 1. Open Browser Console
Press `F12` to open DevTools and go to Console tab.
### 2. Clear Cookies and Storage
In DevTools:
- Go to **Application** tab
- Click **Clear storage****Clear site data**
### 3. Attempt Login
Click "Login to Steam" button and complete authentication.
### 4. Watch Console Logs
You should see this sequence:
```
🔵 Auth store initialize called - isInitialized: false
🔵 fetchUser called - fetching user from /api/auth/me
✅ fetchUser response: { success: true, user: {...} }
✅ Setting user in auth store: { steamId: "...", username: "...", ... }
🔵 fetchUser complete - isAuthenticated: true
Connecting to WebSocket: wss://api.turbotrades.dev/ws
WebSocket connected
🟢 WebSocket received 'connected' message: { steamId: "...", username: "...", userId: "..." }
🔵 Calling authStore.fetchUser() from WebSocket connected handler
🔵 fetchUser called - fetching user from /api/auth/me
✅ fetchUser response: { success: true, user: {...} }
✅ Setting user in auth store: { steamId: "...", username: "...", ... }
🔵 fetchUser complete - isAuthenticated: true
```
### 5. Verify Navbar Updates
The navbar should now show:
- ✅ Your Steam avatar
- ✅ Your username
- ✅ Your balance with deposit button
- ✅ User dropdown menu with Profile, Inventory, etc.
---
## Rollback Plan
If issues occur after deployment:
### Quick Rollback
```bash
# Revert the commits
git revert HEAD~2..HEAD
# Rebuild and redeploy
cd frontend
npm run build
sudo cp -r dist/* /var/www/html/turbotrades/
```
### Remove Debug Logs (Production Cleanup)
Once confirmed working, you may want to remove the verbose debug logs:
**In `frontend/src/stores/auth.js`** - remove all `console.log` statements added
**In `frontend/src/stores/websocket.js`** - remove or reduce debug logs:
```javascript
case "connected":
// Keep minimal logging
console.log("WebSocket connected");
authStore.fetchUser();
break;
```
Then rebuild and redeploy.
---
## Additional Notes
### Why Two `fetchUser()` Calls?
You might notice `fetchUser()` is called twice:
1. **On App Mount** (`App.vue``authStore.initialize()`)
- Handles the case where user already has a valid session
- Runs immediately when page loads
2. **On WebSocket Connected** (new addition)
- Handles the case where user just logged in via Steam
- Ensures auth state is fresh after Steam OAuth redirect
- Provides a fallback if the initial fetch happened before the session cookie was set
This redundancy is intentional and ensures authentication works reliably in all scenarios.
### Session Persistence
The authentication uses HTTP-only cookies set by the backend. These persist across:
- Page refreshes
- Browser restarts (unless expired)
- Tab closures
The WebSocket connection authenticates using the same session cookie.
---
## Related Files
- `frontend/src/stores/websocket.js` - WebSocket message handling
- `frontend/src/stores/auth.js` - Authentication state management
- `frontend/src/components/NavBar.vue` - Navbar component (reactive to auth state)
- `frontend/src/App.vue` - App initialization flow
- `backend/routes/auth.js` - Steam OAuth and session management
---
## Troubleshooting
### Navbar Still Not Updating
1. **Clear browser cache completely** (not just refresh)
2. **Check browser console for errors**
3. **Verify `/api/auth/me` returns user data:**
```bash
curl -s https://api.turbotrades.dev/api/auth/me -b cookies.txt | jq
```
4. **Check WebSocket connection:**
- Look for "WebSocket connected" in console
- Verify no CORS errors
### "fetchUser called" But No Response
- **Backend issue:** Check PM2 logs
```bash
pm2 logs turbotrades-backend --lines 50
```
- **CORS issue:** Check for CORS errors in browser console
- **Session cookie not set:** Check backend logs for Steam OAuth errors
### WebSocket Doesn't Receive "connected" Message
- **Backend WebSocket handler issue:** Check `backend/services/websocket.js`
- **Nginx not proxying WS correctly:** Verify Nginx config has:
```nginx
location /ws {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://127.0.0.1:3000/ws;
}
```
---
## Success Criteria
✅ User can click "Login to Steam" and complete OAuth flow
✅ Upon redirect back to site, navbar shows logged-in state
✅ User's avatar, username, and balance are displayed
✅ WebSocket connects and shows user data in console
✅ User can access protected routes (Inventory, Profile, etc.)
✅ No console errors related to authentication or WebSocket
---
**Status:** Ready for deployment
**Last Updated:** 2025-01-10
**Build Tested:** ✅ Success
**Files Modified:** 2 (websocket.js, auth.js)

View File

@@ -44,26 +44,35 @@ export const useAuthStore = defineStore("auth", () => {
const fetchUser = async () => {
if (isLoading.value) return;
console.log("🔵 fetchUser called - fetching user from /api/auth/me");
isLoading.value = true;
try {
const response = await axios.get("/api/auth/me", {
withCredentials: true,
});
console.log("✅ fetchUser response:", response.data);
if (response.data.success && response.data.user) {
console.log("✅ Setting user in auth store:", response.data.user);
setUser(response.data.user);
return response.data.user;
} else {
console.log("❌ No user in response, clearing auth");
clearUser();
return null;
}
} catch (error) {
console.error("Failed to fetch user:", error);
console.error("Failed to fetch user:", error);
clearUser();
return null;
} finally {
isLoading.value = false;
isInitialized.value = true;
console.log(
"🔵 fetchUser complete - isAuthenticated:",
isAuthenticated.value
);
}
};
@@ -229,6 +238,10 @@ export const useAuthStore = defineStore("auth", () => {
// Initialize on store creation
const initialize = async () => {
console.log(
"🔵 Auth store initialize called - isInitialized:",
isInitialized.value
);
if (!isInitialized.value) {
await fetchUser();
}

View File

@@ -208,10 +208,16 @@ export const useWebSocketStore = defineStore("websocket", () => {
const handleMessage = (data) => {
const { type, data: payload, timestamp } = data;
const authStore = useAuthStore();
switch (type) {
case "connected":
console.log("Server confirmed connection:", payload);
console.log("🟢 WebSocket received 'connected' message:", payload);
// Fetch updated user data from the auth endpoint
console.log(
"🔵 Calling authStore.fetchUser() from WebSocket connected handler"
);
authStore.fetchUser();
break;
case "pong":
@@ -226,7 +232,6 @@ export const useWebSocketStore = defineStore("websocket", () => {
case "balance_update":
// Update user balance
const authStore = useAuthStore();
if (payload?.balance !== undefined) {
authStore.updateBalance(payload.balance);
}

View File

@@ -0,0 +1,314 @@
#!/usr/bin/env node
/**
* Verification Script for Navbar Login Fix
*
* This script verifies that the WebSocket authentication state synchronization
* is working correctly after the navbar login fix deployment.
*
* Usage:
* node scripts/verify-login-fix.js
*/
const https = require('https');
const http = require('http');
const API_URL = process.env.VITE_API_URL || 'https://api.turbotrades.dev';
const WS_URL = process.env.VITE_WS_URL || 'wss://api.turbotrades.dev/ws';
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m',
};
function log(message, color = colors.reset) {
console.log(`${color}${message}${colors.reset}`);
}
function success(message) {
log(`${message}`, colors.green);
}
function error(message) {
log(`${message}`, colors.red);
}
function warning(message) {
log(`⚠️ ${message}`, colors.yellow);
}
function info(message) {
log(` ${message}`, colors.cyan);
}
function section(message) {
log(`\n${colors.bright}${message}${colors.reset}`);
log('='.repeat(60));
}
// Check if endpoint is reachable
function checkEndpoint(url, description) {
return new Promise((resolve) => {
const urlObj = new URL(url);
const client = urlObj.protocol === 'https:' ? https : http;
const options = {
hostname: urlObj.hostname,
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
path: urlObj.pathname,
method: 'GET',
timeout: 5000,
};
const req = client.request(options, (res) => {
if (res.statusCode >= 200 && res.statusCode < 400) {
success(`${description}: Reachable (Status: ${res.statusCode})`);
resolve(true);
} else {
warning(`${description}: Returned status ${res.statusCode}`);
resolve(false);
}
});
req.on('error', (err) => {
error(`${description}: ${err.message}`);
resolve(false);
});
req.on('timeout', () => {
error(`${description}: Request timed out`);
req.destroy();
resolve(false);
});
req.end();
});
}
// Check WebSocket endpoint
function checkWebSocket(url) {
return new Promise((resolve) => {
try {
const WebSocket = require('ws');
const ws = new WebSocket(url);
const timeout = setTimeout(() => {
ws.close();
error('WebSocket: Connection timeout');
resolve(false);
}, 5000);
ws.on('open', () => {
clearTimeout(timeout);
success('WebSocket: Connection successful');
ws.close();
resolve(true);
});
ws.on('error', (err) => {
clearTimeout(timeout);
error(`WebSocket: ${err.message}`);
resolve(false);
});
} catch (err) {
error(`WebSocket: ws module not available (${err.message})`);
warning('Install with: npm install ws');
resolve(false);
}
});
}
// Read and verify frontend build files
function checkFrontendBuild() {
const fs = require('fs');
const path = require('path');
const distPath = path.join(__dirname, '..', 'frontend', 'dist');
const indexPath = path.join(distPath, 'index.html');
if (!fs.existsSync(distPath)) {
error('Frontend dist directory not found');
warning('Run: cd frontend && npm run build');
return false;
}
if (!fs.existsSync(indexPath)) {
error('Frontend index.html not found in dist/');
return false;
}
const indexContent = fs.readFileSync(indexPath, 'utf8');
// Check for recent build by looking for assets
const hasAssets = indexContent.includes('/assets/');
const hasModernJS = indexContent.includes('type="module"');
if (hasAssets && hasModernJS) {
success('Frontend build exists and looks valid');
// Get build timestamp from file modification time
const stats = fs.statSync(indexPath);
const buildTime = stats.mtime;
const now = new Date();
const ageMinutes = Math.floor((now - buildTime) / 1000 / 60);
if (ageMinutes < 60) {
success(`Build age: ${ageMinutes} minutes (recent)`);
} else if (ageMinutes < 1440) {
info(`Build age: ${Math.floor(ageMinutes / 60)} hours`);
} else {
warning(`Build age: ${Math.floor(ageMinutes / 1440)} days (consider rebuilding)`);
}
return true;
} else {
error('Frontend build appears invalid');
return false;
}
}
// Check source files for the fix
function checkSourceCode() {
const fs = require('fs');
const path = require('path');
const websocketStorePath = path.join(__dirname, '..', 'frontend', 'src', 'stores', 'websocket.js');
const authStorePath = path.join(__dirname, '..', 'frontend', 'src', 'stores', 'auth.js');
if (!fs.existsSync(websocketStorePath)) {
error('WebSocket store not found');
return false;
}
if (!fs.existsSync(authStorePath)) {
error('Auth store not found');
return false;
}
const websocketCode = fs.readFileSync(websocketStorePath, 'utf8');
const authCode = fs.readFileSync(authStorePath, 'utf8');
// Check for the fix in websocket store
const hasConnectedHandler = websocketCode.includes('case "connected":');
const callsFetchUser = websocketCode.includes('authStore.fetchUser()');
const declaresAuthStore = websocketCode.includes('const authStore = useAuthStore()');
if (hasConnectedHandler && callsFetchUser && declaresAuthStore) {
success('WebSocket store has the navbar login fix');
} else {
error('WebSocket store missing the fix');
if (!hasConnectedHandler) warning(' - Missing "connected" case handler');
if (!callsFetchUser) warning(' - Not calling authStore.fetchUser()');
if (!declaresAuthStore) warning(' - Not declaring authStore');
return false;
}
// Check for debug logging (optional)
const hasDebugLogs = websocketCode.includes('🟢 WebSocket received') ||
authCode.includes('🔵 fetchUser called');
if (hasDebugLogs) {
info('Debug logging is present (helpful for troubleshooting)');
} else {
info('Debug logging not present (clean for production)');
}
return true;
}
// Main verification flow
async function main() {
log('\n' + '='.repeat(60), colors.bright);
log(' NAVBAR LOGIN FIX - VERIFICATION SCRIPT', colors.bright);
log('='.repeat(60) + '\n', colors.bright);
let allChecks = true;
// 1. Check source code
section('1. Checking Source Code');
const sourceCheck = checkSourceCode();
allChecks = allChecks && sourceCheck;
// 2. Check frontend build
section('2. Checking Frontend Build');
const buildCheck = checkFrontendBuild();
allChecks = allChecks && buildCheck;
// 3. Check API endpoints
section('3. Checking API Endpoints');
const apiHealthCheck = await checkEndpoint(
`${API_URL}/api/health`,
'API Health Endpoint'
);
const apiAuthCheck = await checkEndpoint(
`${API_URL}/api/auth/me`,
'Auth Endpoint (/api/auth/me)'
);
const publicConfigCheck = await checkEndpoint(
`${API_URL}/api/config/public`,
'Public Config Endpoint'
);
allChecks = allChecks && apiHealthCheck && apiAuthCheck && publicConfigCheck;
// 4. Check WebSocket
section('4. Checking WebSocket Connection');
const wsCheck = await checkWebSocket(WS_URL);
allChecks = allChecks && wsCheck;
// Summary
section('Verification Summary');
if (allChecks) {
success('All checks passed! ✨');
log('\n📋 Next Steps:', colors.bright);
log(' 1. Deploy the frontend build to production');
log(' 2. Clear browser cache (Ctrl+Shift+R)');
log(' 3. Test Steam login in browser');
log(' 4. Verify navbar updates after login\n');
} else {
error('Some checks failed!');
log('\n📋 Recommended Actions:', colors.bright);
if (!sourceCheck) {
log(' • Verify the code changes are committed');
log(' • Pull latest changes: git pull origin main');
}
if (!buildCheck) {
log(' • Rebuild frontend: cd frontend && npm run build');
}
if (!apiHealthCheck || !apiAuthCheck || !publicConfigCheck) {
log(' • Check backend is running: pm2 status');
log(' • Check backend logs: pm2 logs turbotrades-backend');
log(' • Verify Nginx configuration');
}
if (!wsCheck) {
log(' • Check WebSocket endpoint in Nginx config');
log(' • Verify backend WebSocket service is running');
log(' • Check firewall rules for WebSocket connections');
}
log('');
}
process.exit(allChecks ? 0 : 1);
}
// Run the script
main().catch((err) => {
error(`Unexpected error: ${err.message}`);
console.error(err);
process.exit(1);
});