AttendAIPro / js /config.js
ashrithagowthami's picture
Upload 2277 files
f759742 verified
Raw
History Blame Contribute Delete
9.28 kB
/**
* AttendAI Pro - Application Configuration
* Production-ready configuration management
*/
const APP_CONFIG = {
name: 'AttendAI Pro',
version: '2.0.0',
tagline: 'AI-Powered Anti-Proxy Attendance System',
// Google OAuth - Replace with your actual Google OAuth Client ID
// To get: console.cloud.google.com → Create Project → OAuth 2.0 Client IDs
googleClientId: '578078243530-ridjf8bv9ie0cbustjmefvf56b77qfe5.apps.googleusercontent.com',
// API Base URL (relative paths for the Table API)
apiBase: 'tables',
// Attendance Settings
qrRotateInterval: 60, // seconds - QR code rotates every 60s
geofenceRadius: 100, // meters - default geofence radius
faceMatchThreshold: 0.6, // 0-1 face match confidence threshold
attendanceCutoff: 75, // percent - attendance warning threshold
lateThresholdMinutes: 10, // minutes after session start = late
// Security
sessionTimeout: 8 * 60 * 60 * 1000, // 8 hours in ms
maxAttemptsPerSession: 1, // max attendance attempts per session
fraudDetectionEnabled: true,
// Allowed institutional email domains (configure per institution)
// Leave empty to allow all emails (admin manages via database)
allowedStudentDomains: [], // e.g., ['university.edu', 'college.ac.in']
// Supported institution types
institutionTypes: ['University', 'College', 'High School', 'Corporate', 'Training Center'],
// Routes
routes: {
landing: 'index.html',
studentLogin: 'student-login.html',
teacherLogin: 'teacher-login.html',
studentDash: 'student-dashboard.html',
teacherDash: 'teacher-dashboard.html',
adminPanel: 'admin-panel.html',
}
};
/**
* Auth State Manager
*/
const AuthState = {
KEY: 'attendai_session',
save(userData) {
const session = {
...userData,
loginTime: Date.now(),
expires: Date.now() + APP_CONFIG.sessionTimeout
};
sessionStorage.setItem(this.KEY, JSON.stringify(session));
localStorage.setItem(this.KEY + '_persist', JSON.stringify(session));
},
get() {
try {
const s = sessionStorage.getItem(this.KEY) || localStorage.getItem(this.KEY + '_persist');
if (!s) return null;
const data = JSON.parse(s);
if (Date.now() > data.expires) { this.clear(); return null; }
return data;
} catch { return null; }
},
clear() {
sessionStorage.removeItem(this.KEY);
localStorage.removeItem(this.KEY + '_persist');
},
isLoggedIn() { return !!this.get(); },
getRole() {
const s = this.get();
return s ? s.role : null;
}
};
/**
* API Helper
*/
const API = {
async get(table, params = {}) {
const q = new URLSearchParams(params).toString();
const url = `${APP_CONFIG.apiBase}/${table}${q ? '?' + q : ''}`;
const res = await fetch(url);
if (!res.ok) throw new Error(`API Error: ${res.status}`);
return res.json();
},
async getOne(table, id) {
const res = await fetch(`${APP_CONFIG.apiBase}/${table}/${id}`);
if (!res.ok) throw new Error(`API Error: ${res.status}`);
return res.json();
},
async post(table, data) {
const res = await fetch(`${APP_CONFIG.apiBase}/${table}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!res.ok) throw new Error(`API Error: ${res.status}`);
return res.json();
},
async put(table, id, data) {
const res = await fetch(`${APP_CONFIG.apiBase}/${table}/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!res.ok) throw new Error(`API Error: ${res.status}`);
return res.json();
},
async patch(table, id, data) {
const res = await fetch(`${APP_CONFIG.apiBase}/${table}/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!res.ok) throw new Error(`API Error: ${res.status}`);
return res.json();
},
async delete(table, id) {
const res = await fetch(`${APP_CONFIG.apiBase}/${table}/${id}`, { method: 'DELETE' });
if (!res.ok) throw new Error(`API Error: ${res.status}`);
return true;
}
};
/**
* Utility Functions
*/
const Utils = {
formatDate(ts) {
if (!ts) return '—';
return new Date(ts).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
},
formatTime(ts) {
if (!ts) return '—';
return new Date(ts).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
},
formatDateTime(ts) {
if (!ts) return '—';
return new Date(ts).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
},
timeAgo(ts) {
const s = Math.floor((Date.now() - ts) / 1000);
if (s < 60) return `${s}s ago`;
if (s < 3600) return `${Math.floor(s / 60)}m ago`;
if (s < 86400) return `${Math.floor(s / 3600)}h ago`;
return `${Math.floor(s / 86400)}d ago`;
},
generateToken(len = 32) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let token = '';
for (let i = 0; i < len; i++) token += chars[Math.floor(Math.random() * chars.length)];
return token;
},
generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
},
getDistance(lat1, lon1, lat2, lon2) {
const R = 6371000;
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
},
async getGeoLocation() {
return new Promise((resolve, reject) => {
if (!navigator.geolocation) { reject(new Error('Geolocation not supported')); return; }
navigator.geolocation.getCurrentPosition(
pos => resolve({ lat: pos.coords.latitude, lng: pos.coords.longitude, accuracy: pos.coords.accuracy }),
err => reject(new Error(err.message)),
{ enableHighAccuracy: true, timeout: 10000 }
);
});
},
getDeviceFingerprint() {
const data = `${navigator.userAgent}|${screen.width}x${screen.height}|${navigator.language}|${Intl.DateTimeFormat().resolvedOptions().timeZone}`;
let hash = 0;
for (let i = 0; i < data.length; i++) { hash = ((hash << 5) - hash) + data.charCodeAt(i); hash |= 0; }
return Math.abs(hash).toString(16);
},
debounce(fn, ms) {
let t; return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); };
},
percentColor(pct) {
if (pct >= 75) return 'success';
if (pct >= 60) return 'warning';
return 'danger';
}
};
/**
* Toast Notification System
*/
const Toast = {
container: null,
init() {
if (!this.container) {
this.container = document.createElement('div');
this.container.className = 'toast-container';
document.body.appendChild(this.container);
}
},
show(message, type = 'info', duration = 4000) {
this.init();
const icons = { success: '✅', error: '❌', warning: '⚠️', info: 'ℹ️' };
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.innerHTML = `
<span style="font-size:18px">${icons[type] || icons.info}</span>
<div style="flex:1"><p style="color:#1e293b;margin:0;font-size:14px">${message}</p></div>
<button class="toast-close" onclick="this.parentElement.remove()">×</button>
`;
this.container.appendChild(toast);
if (duration > 0) setTimeout(() => { toast.style.opacity = '0'; toast.style.transform = 'translateX(100%)'; toast.style.transition = '0.3s'; setTimeout(() => toast.remove(), 300); }, duration);
return toast;
},
success(msg, d) { return this.show(msg, 'success', d); },
error(msg, d) { return this.show(msg, 'error', d); },
warning(msg, d) { return this.show(msg, 'warning', d); },
info(msg, d) { return this.show(msg, 'info', d); }
};
/**
* Guard: Redirect if not authenticated
*/
function requireAuth(role) {
const user = AuthState.get();
if (!user) {
window.location.href = 'index.html';
return null;
}
if (role && user.role !== role && user.role !== 'admin') {
window.location.href = 'index.html';
return null;
}
return user;
}
/**
* Logout handler
*/
function logout() {
AuthState.clear();
// Revoke Google session if available
if (typeof google !== 'undefined' && google.accounts) {
google.accounts.id.disableAutoSelect();
}
window.location.href = 'index.html';
}
/**
* System Logger
*/
const Logger = {
async log(action, resource, details, status = 'success') {
try {
const user = AuthState.get();
await API.post('system_logs', {
id: Utils.generateUUID(),
user_id: user?.id || 'system',
action,
resource,
details: JSON.stringify(details),
ip_address: 'client',
user_agent: navigator.userAgent.substring(0, 200),
status,
timestamp: Date.now()
});
} catch (e) { console.warn('Log failed:', e); }
}
};