Compare commits
10 commits
3b48e804da
...
a8f2078e5b
Author | SHA1 | Date | |
---|---|---|---|
|
a8f2078e5b | ||
047456b1d8 | |||
98d5bda641 | |||
5c3cb388cd | |||
|
7325a30f94 | ||
|
543ad96109 | ||
|
4610796392 | ||
|
ec60d6bd2a | ||
87fef8309d | |||
|
c7a2bb6848 |
17 changed files with 407 additions and 14 deletions
41
CLAUDE.md
41
CLAUDE.md
|
@ -2,8 +2,49 @@
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## ⚠️ CRITICAL RULES - READ FIRST
|
||||||
|
|
||||||
|
### Git Workflow - ABSOLUTELY MANDATORY
|
||||||
|
- **NEVER COMMIT DIRECTLY TO MAIN/MASTER BRANCH**
|
||||||
|
- **ALWAYS CREATE FEATURE BRANCHES FOR ANY CHANGES**
|
||||||
|
- **USE TEA CLI FOR GIT.DECO.SH REPOSITORY**
|
||||||
|
|
||||||
|
### Required Git Workflow
|
||||||
|
```bash
|
||||||
|
# STEP 1: ALWAYS create a feature branch first
|
||||||
|
git checkout -b feature/brief-description
|
||||||
|
|
||||||
|
# STEP 2: Make your changes and commit
|
||||||
|
git add .
|
||||||
|
git commit -m "Your commit message"
|
||||||
|
|
||||||
|
# STEP 3: Push branch and create PR using tea cli
|
||||||
|
git push -u origin feature/brief-description
|
||||||
|
tea pr create
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pre-Commit Checklist
|
||||||
|
- [ ] Confirm you are NOT on main/master branch: `git branch --show-current`
|
||||||
|
- [ ] Run tests: `npm test`
|
||||||
|
- [ ] Run linting: `npm run lint`
|
||||||
|
- [ ] Build succeeds: `npm run build`
|
||||||
|
|
||||||
|
**VIOLATION OF THESE RULES IS UNACCEPTABLE**
|
||||||
|
|
||||||
## Development Commands
|
## Development Commands
|
||||||
|
|
||||||
|
### Quick Update Scripts
|
||||||
|
|
||||||
|
For easier development and deployment updates:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Development update (pull, install, build, test)
|
||||||
|
./scripts/dev-update.sh
|
||||||
|
|
||||||
|
# Production server update (requires sudo, handles service restart)
|
||||||
|
sudo ./scripts/server-update.sh
|
||||||
|
```
|
||||||
|
|
||||||
### Running the Application
|
### Running the Application
|
||||||
```bash
|
```bash
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
|
|
|
@ -13,10 +13,11 @@
|
||||||
"watch-css": "sass src/scss/main.scss public/style.css --watch --style=expanded --source-map",
|
"watch-css": "sass src/scss/main.scss public/style.css --watch --style=expanded --source-map",
|
||||||
"dev-with-css": "concurrently \"npm run watch-css\" \"npm run dev\"",
|
"dev-with-css": "concurrently \"npm run watch-css\" \"npm run dev\"",
|
||||||
"dev-with-css:ts": "concurrently \"npm run watch-css\" \"npm run dev:ts\"",
|
"dev-with-css:ts": "concurrently \"npm run watch-css\" \"npm run dev:ts\"",
|
||||||
"build": "npm run build:ts && npm run build-css && npm run build:frontend && npm run copy-i18n",
|
"build": "npm run generate-version && npm run build:ts && npm run build-css && npm run build:frontend && npm run copy-i18n",
|
||||||
"build:ts": "tsc",
|
"build:ts": "tsc",
|
||||||
"copy-i18n": "mkdir -p dist/i18n/locales && cp -r src/i18n/locales/* dist/i18n/locales/",
|
"copy-i18n": "mkdir -p dist/i18n/locales && cp -r src/i18n/locales/* dist/i18n/locales/",
|
||||||
"test": "jest --runInBand --forceExit",
|
"generate-version": "node scripts/generate-version.js",
|
||||||
|
"test": "jest --runInBand --forceExit --detectOpenHandles",
|
||||||
"test:coverage": "jest --coverage",
|
"test:coverage": "jest --coverage",
|
||||||
"lint": "eslint src/ tests/",
|
"lint": "eslint src/ tests/",
|
||||||
"lint:fix": "eslint src/ tests/ --fix",
|
"lint:fix": "eslint src/ tests/ --fix",
|
||||||
|
|
|
@ -188,8 +188,8 @@
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<!-- Load shared theme utility -->
|
<!-- Load shared theme utility -->
|
||||||
<script src="theme-utils.js"></script>
|
|
||||||
<script src="utils.js"></script>
|
<script src="utils.js"></script>
|
||||||
<script src="admin.js"></script>
|
<script src="admin.js"></script>
|
||||||
|
<script src="version-footer.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const map = L.map('map').setView([42.9634, -85.6681], 10);
|
const map = L.map('map', {
|
||||||
|
scrollWheelZoom: false
|
||||||
|
}).setView([42.9634, -85.6681], 10);
|
||||||
|
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
maxZoom: 18,
|
maxZoom: 18,
|
||||||
|
|
|
@ -9,7 +9,6 @@ class I18nService {
|
||||||
this.defaultLocale = 'en';
|
this.defaultLocale = 'en';
|
||||||
this.availableLocales = ['en', 'es-MX'];
|
this.availableLocales = ['en', 'es-MX'];
|
||||||
this.currentLocale = this.getStoredLocale() || this.detectBrowserLocale();
|
this.currentLocale = this.getStoredLocale() || this.detectBrowserLocale();
|
||||||
this.loadTranslations();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -165,18 +165,17 @@ placeholder="Enter address, intersection (e.g., Main St & Second St, City), or l
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p><strong data-i18n="footer.safetyNotice">Safety Notice:</strong> This is a community tool for awareness. Stay safe and <a href="https://www.aclu.org/know-your-rights/immigrants-rights" target="_blank" rel="noopener noreferrer" style="color: #007bff; text-decoration: underline;" data-i18n="footer.knowRights">know your rights</a>.</p>
|
<p><span data-i18n="footer.safetyNotice">Safety Notice: This is a community tool for awareness. Stay safe and</span> <a href="https://www.aclu.org/know-your-rights/immigrants-rights" target="_blank" rel="noopener noreferrer" style="color: #007bff; text-decoration: underline;" data-i18n="footer.knowRights">know your rights</a>.</p>
|
||||||
<div class="disclaimer">
|
<div class="disclaimer">
|
||||||
<small><span data-i18n="footer.disclaimer">This website is for informational purposes only. Verify information independently. Reports are automatically deleted after 48 hours. •</span> <a href="/privacy" style="color: #007bff; text-decoration: underline;" data-i18n="common.privacyPolicy">Privacy Policy</a></small>
|
<small><span data-i18n="footer.disclaimer">This website is for informational purposes only. Verify information independently. Reports are automatically deleted after 48 hours.</span> • <a href="/privacy" style="color: #007bff; text-decoration: underline;" data-i18n="common.privacyPolicy">Privacy Policy</a></small>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||||
<!-- Load shared theme utility -->
|
|
||||||
<script src="theme-utils.js"></script>
|
|
||||||
<script src="utils.js"></script>
|
<script src="utils.js"></script>
|
||||||
<script src="app-mapbox.js"></script>
|
<script src="app-mapbox.js"></script>
|
||||||
|
<script src="version-footer.js"></script>
|
||||||
|
|
||||||
<!-- PWA Service Worker Registration -->
|
<!-- PWA Service Worker Registration -->
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -139,7 +139,7 @@
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<!-- Load shared theme utility -->
|
<!-- Load shared theme utility -->
|
||||||
<script src="theme-utils.js"></script>
|
<script src="version-footer.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// Initialize theme when page loads
|
// Initialize theme when page loads
|
||||||
document.addEventListener('DOMContentLoaded', initializeTheme);
|
document.addEventListener('DOMContentLoaded', initializeTheme);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Service Worker for Great Lakes Ice Report PWA
|
// Service Worker for Great Lakes Ice Report PWA
|
||||||
const CACHE_NAME = 'ice-report-v1';
|
const CACHE_NAME = 'ice-report-v4';
|
||||||
const OFFLINE_URL = '/offline.html';
|
const OFFLINE_URL = '/offline.html';
|
||||||
|
|
||||||
// Files to cache for offline functionality
|
// Files to cache for offline functionality
|
||||||
|
@ -12,6 +12,7 @@ const CACHE_FILES = [
|
||||||
'/app.js',
|
'/app.js',
|
||||||
'/admin.js',
|
'/admin.js',
|
||||||
'/utils.js',
|
'/utils.js',
|
||||||
|
'/version-footer.js',
|
||||||
'/manifest.json',
|
'/manifest.json',
|
||||||
OFFLINE_URL
|
OFFLINE_URL
|
||||||
];
|
];
|
||||||
|
|
101
public/version-footer.js
Normal file
101
public/version-footer.js
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
* Version Footer Utility
|
||||||
|
* Fetches version information and adds it to page footers
|
||||||
|
*/
|
||||||
|
|
||||||
|
class VersionFooter {
|
||||||
|
constructor() {
|
||||||
|
this.versionData = null;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
try {
|
||||||
|
await this.fetchVersionData();
|
||||||
|
this.addVersionToFooter();
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Could not load version information:', error);
|
||||||
|
this.addFallbackVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchVersionData() {
|
||||||
|
const response = await fetch('/api/version');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
this.versionData = await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
addVersionToFooter() {
|
||||||
|
const footers = document.querySelectorAll('footer');
|
||||||
|
|
||||||
|
footers.forEach(footer => {
|
||||||
|
// Check if version info already exists
|
||||||
|
if (footer.querySelector('.version-info')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionDiv = document.createElement('div');
|
||||||
|
versionDiv.className = 'version-info';
|
||||||
|
versionDiv.style.cssText = `
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 8px;
|
||||||
|
border-top: 1px solid #e0e0e0;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #666;
|
||||||
|
text-align: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (this.versionData) {
|
||||||
|
const commitUrl = `${this.versionData.gitUrl}/commit/${this.versionData.sha}`;
|
||||||
|
const commitDate = new Date(this.versionData.commitDate).toLocaleDateString();
|
||||||
|
|
||||||
|
versionDiv.innerHTML = `
|
||||||
|
<span>Version: <a href="${commitUrl}" target="_blank" rel="noopener noreferrer"
|
||||||
|
style="color: #666; text-decoration: none; font-family: monospace;"
|
||||||
|
title="View commit: ${this.versionData.commitMessage}">${this.versionData.shortSha}</a></span>
|
||||||
|
<span style="margin-left: 8px;">• ${commitDate}</span>
|
||||||
|
<span style="margin-left: 8px;">• Branch: ${this.versionData.branch}</span>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
versionDiv.innerHTML = '<span>Version information unavailable</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
footer.appendChild(versionDiv);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addFallbackVersion() {
|
||||||
|
const footers = document.querySelectorAll('footer');
|
||||||
|
|
||||||
|
footers.forEach(footer => {
|
||||||
|
if (footer.querySelector('.version-info')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionDiv = document.createElement('div');
|
||||||
|
versionDiv.className = 'version-info';
|
||||||
|
versionDiv.style.cssText = `
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 8px;
|
||||||
|
border-top: 1px solid #e0e0e0;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #666;
|
||||||
|
text-align: center;
|
||||||
|
`;
|
||||||
|
versionDiv.innerHTML = '<span>Version information unavailable</span>';
|
||||||
|
|
||||||
|
footer.appendChild(versionDiv);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-initialize when DOM is ready
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
new VersionFooter();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
new VersionFooter();
|
||||||
|
}
|
40
scripts/dev-update.sh
Executable file
40
scripts/dev-update.sh
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Development Update Script for Ice Watch Application
|
||||||
|
# Quick script for updating during development
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log "Starting development update..."
|
||||||
|
|
||||||
|
# Pull latest changes
|
||||||
|
log "Pulling latest changes from git..."
|
||||||
|
git pull origin main
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
log "Installing/updating dependencies..."
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
log "Building application..."
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
log "Running tests..."
|
||||||
|
npm test
|
||||||
|
|
||||||
|
log "✅ Development update completed successfully!"
|
||||||
|
log "You can now run 'npm start' or 'npm run dev:full' to start the application"
|
72
scripts/generate-version.js
Normal file
72
scripts/generate-version.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get the current commit SHA (short version)
|
||||||
|
const fullSha = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim();
|
||||||
|
const shortSha = fullSha.substring(0, 7);
|
||||||
|
|
||||||
|
// Get the current branch name
|
||||||
|
const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim();
|
||||||
|
|
||||||
|
// Get the commit date
|
||||||
|
const commitDate = execSync('git log -1 --format=%cI', { encoding: 'utf8' }).trim();
|
||||||
|
|
||||||
|
// Get the commit message (first line only)
|
||||||
|
const commitMessage = execSync('git log -1 --format=%s', { encoding: 'utf8' }).trim();
|
||||||
|
|
||||||
|
const versionInfo = {
|
||||||
|
sha: fullSha,
|
||||||
|
shortSha: shortSha,
|
||||||
|
branch: branch,
|
||||||
|
commitDate: commitDate,
|
||||||
|
commitMessage: commitMessage,
|
||||||
|
buildDate: new Date().toISOString(),
|
||||||
|
gitUrl: 'https://git.deco.sh/signal-works/icewatch'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure dist directory exists
|
||||||
|
const distDir = path.join(__dirname, '..', 'dist');
|
||||||
|
if (!fs.existsSync(distDir)) {
|
||||||
|
fs.mkdirSync(distDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write version info to dist directory
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(distDir, 'version.json'),
|
||||||
|
JSON.stringify(versionInfo, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`✅ Generated version info: ${shortSha} (${branch})`);
|
||||||
|
console.log(`📅 Commit date: ${commitDate}`);
|
||||||
|
console.log(`📝 Message: ${commitMessage}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('⚠️ Could not generate version info (not in git repository?):', error.message);
|
||||||
|
|
||||||
|
// Create fallback version info
|
||||||
|
const fallbackVersion = {
|
||||||
|
sha: 'unknown',
|
||||||
|
shortSha: 'unknown',
|
||||||
|
branch: 'unknown',
|
||||||
|
commitDate: new Date().toISOString(),
|
||||||
|
commitMessage: 'No git information available',
|
||||||
|
buildDate: new Date().toISOString(),
|
||||||
|
gitUrl: 'https://git.deco.sh/signal-works/icewatch'
|
||||||
|
};
|
||||||
|
|
||||||
|
const distDir = path.join(__dirname, '..', 'dist');
|
||||||
|
if (!fs.existsSync(distDir)) {
|
||||||
|
fs.mkdirSync(distDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(distDir, 'version.json'),
|
||||||
|
JSON.stringify(fallbackVersion, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('📦 Created fallback version info');
|
||||||
|
}
|
86
scripts/server-update.sh
Executable file
86
scripts/server-update.sh
Executable file
|
@ -0,0 +1,86 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Server Update Script for Ice Watch Application
|
||||||
|
# This script handles updating the production server with proper user permissions
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
APP_DIR="/opt/icewatch"
|
||||||
|
APP_USER="icewatch"
|
||||||
|
SERVICE_NAME="icewatch"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Functions
|
||||||
|
log() {
|
||||||
|
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
error "This script must be run as root (use sudo)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if app directory exists
|
||||||
|
if [[ ! -d "$APP_DIR" ]]; then
|
||||||
|
error "Application directory $APP_DIR does not exist"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if service exists
|
||||||
|
if ! systemctl list-unit-files | grep -q "$SERVICE_NAME.service"; then
|
||||||
|
error "Service $SERVICE_NAME does not exist"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Starting server update process..."
|
||||||
|
|
||||||
|
# Stop the service
|
||||||
|
log "Stopping $SERVICE_NAME service..."
|
||||||
|
systemctl stop "$SERVICE_NAME"
|
||||||
|
|
||||||
|
# Navigate to app directory as app user and update
|
||||||
|
log "Updating application code..."
|
||||||
|
sudo -u "$APP_USER" bash -c "
|
||||||
|
cd '$APP_DIR' && \
|
||||||
|
git fetch origin && \
|
||||||
|
git reset --hard origin/main && \
|
||||||
|
npm install --production && \
|
||||||
|
npm run build
|
||||||
|
"
|
||||||
|
|
||||||
|
# Check if build was successful
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
error "Build failed! Check the logs above."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start the service
|
||||||
|
log "Starting $SERVICE_NAME service..."
|
||||||
|
systemctl start "$SERVICE_NAME"
|
||||||
|
|
||||||
|
# Check service status
|
||||||
|
sleep 2
|
||||||
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||||
|
log "✅ Service $SERVICE_NAME is running successfully"
|
||||||
|
else
|
||||||
|
error "❌ Service $SERVICE_NAME failed to start"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show recent logs
|
||||||
|
log "Recent service logs:"
|
||||||
|
journalctl -u "$SERVICE_NAME" -n 10 --no-pager
|
||||||
|
|
||||||
|
log "🎉 Server update completed successfully!"
|
||||||
|
log "Application is now running the latest version from main branch"
|
|
@ -42,7 +42,7 @@ export class SharedFooter {
|
||||||
${this.config.showDisclaimer ? `
|
${this.config.showDisclaimer ? `
|
||||||
<div class="disclaimer">
|
<div class="disclaimer">
|
||||||
<small>
|
<small>
|
||||||
<span data-i18n="footer.disclaimer">This website is for informational purposes only. Verify information independently. Reports are automatically deleted after 48 hours. •</span>
|
<span data-i18n="footer.disclaimer">This website is for informational purposes only. Verify information independently. Reports are automatically deleted after 48 hours.</span>
|
||||||
<a href="/privacy"
|
<a href="/privacy"
|
||||||
style="color: #007bff; text-decoration: underline;"
|
style="color: #007bff; text-decoration: underline;"
|
||||||
data-i18n="common.privacyPolicy">Privacy Policy</a>
|
data-i18n="common.privacyPolicy">Privacy Policy</a>
|
||||||
|
|
|
@ -193,7 +193,7 @@
|
||||||
"footer": {
|
"footer": {
|
||||||
"safetyNotice": "Safety Notice: This is a community tool for awareness. Stay safe and",
|
"safetyNotice": "Safety Notice: This is a community tool for awareness. Stay safe and",
|
||||||
"knowRights": "know your rights",
|
"knowRights": "know your rights",
|
||||||
"disclaimer": "This website is for informational purposes only. Verify information independently. Reports are automatically deleted after 48 hours. •"
|
"disclaimer": "This website is for informational purposes only. Verify information independently. Reports are automatically deleted after 48 hours."
|
||||||
},
|
},
|
||||||
"nojs": {
|
"nojs": {
|
||||||
"notice": "JavaScript is disabled. For the best experience with interactive maps, please enable JavaScript.",
|
"notice": "JavaScript is disabled. For the best experience with interactive maps, please enable JavaScript.",
|
||||||
|
|
49
src/routes/version.ts
Normal file
49
src/routes/version.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { Router, Request, Response } from 'express';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version API routes
|
||||||
|
* Provides build and git version information
|
||||||
|
*/
|
||||||
|
export default function createVersionRoutes(): Router {
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/version
|
||||||
|
* Returns version information including git commit SHA and build details
|
||||||
|
*/
|
||||||
|
router.get('/', (req: Request, res: Response): void => {
|
||||||
|
try {
|
||||||
|
const versionPath = path.join(__dirname, '../version.json');
|
||||||
|
|
||||||
|
if (!fs.existsSync(versionPath)) {
|
||||||
|
res.status(404).json({
|
||||||
|
error: 'Version information not available',
|
||||||
|
message: 'Version file not found. Run build process to generate version info.'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionData = JSON.parse(fs.readFileSync(versionPath, 'utf8'));
|
||||||
|
|
||||||
|
// Add some additional runtime info
|
||||||
|
const response = {
|
||||||
|
...versionData,
|
||||||
|
serverStartTime: process.env.SERVER_START_TIME || new Date().toISOString(),
|
||||||
|
nodeVersion: process.version,
|
||||||
|
environment: process.env.NODE_ENV || 'development'
|
||||||
|
};
|
||||||
|
|
||||||
|
res.json(response);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading version information:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
error: 'Failed to read version information',
|
||||||
|
message: 'Internal server error while reading version data'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ footer {
|
||||||
padding: $spacing-lg;
|
padding: $spacing-lg;
|
||||||
background-color: var(--bg-secondary);
|
background-color: var(--bg-secondary);
|
||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
border-radius: $border-radius-md $border-radius-md 0 0;
|
border-radius: $border-radius-md;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: $spacing-sm 0;
|
margin: $spacing-sm 0;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import configRoutes from './routes/config';
|
||||||
import locationRoutes from './routes/locations';
|
import locationRoutes from './routes/locations';
|
||||||
import adminRoutes from './routes/admin';
|
import adminRoutes from './routes/admin';
|
||||||
import i18nRoutes from './routes/i18n';
|
import i18nRoutes from './routes/i18n';
|
||||||
|
import versionRoutes from './routes/version';
|
||||||
|
|
||||||
|
|
||||||
const app: Application = express();
|
const app: Application = express();
|
||||||
|
@ -186,6 +187,7 @@ function setupRoutes(): void {
|
||||||
app.use('/api/locations', locationRoutes(locationModel, profanityFilter));
|
app.use('/api/locations', locationRoutes(locationModel, profanityFilter));
|
||||||
app.use('/api/admin', adminRoutes(locationModel, profanityWordModel, profanityFilter, authenticateAdmin));
|
app.use('/api/admin', adminRoutes(locationModel, profanityWordModel, profanityFilter, authenticateAdmin));
|
||||||
app.use('/api/i18n', i18nRoutes());
|
app.use('/api/i18n', i18nRoutes());
|
||||||
|
app.use('/api/version', versionRoutes());
|
||||||
|
|
||||||
// Static page routes
|
// Static page routes
|
||||||
app.get('/', (req: Request, res: Response): void => {
|
app.get('/', (req: Request, res: Response): void => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue