Files
smanab/app-Izin_Keluar/server.js

143 lines
5.4 KiB
JavaScript

import express from 'express';
import path from 'path';
import { fileURLToPath } from 'url';
import cors from 'cors';
const app = express();
const PORT = process.env.PORT || 3004;
// Karena kita menggunakan ES module, __dirname perlu didefinisikan secara manual
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// URL yang akan di-proxy
const GOOGLE_APPS_SCRIPT_URL = 'https://script.google.com/macros/s/AKfycbybbQyKyVPSVPXOqOjaOFMKetuYfyweGcjResfLi-B-8scqaoyXeSsmsqNTXeTLe7_Wvg/exec';
const STUDENT_DATA_URL = 'https://docs.google.com/spreadsheets/d/18nVeas2TG5SbkTUsc1mQWmPP_0xu3fek-UfTQdjjvUc/gviz/tq?tqx=out:csv&sheet=DATABASE';
const PERMIT_DATA_URL = 'https://docs.google.com/spreadsheets/d/18nVeas2TG5SbkTUsc1mQWmPP_0xu3fek-UfTQdjjvUc/gviz/tq?tqx=out:csv&sheet=REKAP IZIN';
// === CACHING SYSTEM ===
const cache = {
students: { data: null, timestamp: 0 },
permits: { data: null, timestamp: 0 }
};
// Cache TTL (Time To Live) in milliseconds
const STUDENT_CACHE_TTL = 5 * 60 * 1000; // 5 menit - data siswa jarang berubah
const PERMIT_CACHE_TTL = 30 * 1000; // 30 detik - data izin lebih sering berubah
const isCacheValid = (cacheEntry, ttl) => {
return cacheEntry.data && (Date.now() - cacheEntry.timestamp) < ttl;
};
// === END CACHING SYSTEM ===
app.use(cors());
app.use(express.json());
// Proxy untuk data CSV siswa (dengan cache)
app.get('/api/students', async (req, res) => {
try {
// Check cache first
if (isCacheValid(cache.students, STUDENT_CACHE_TTL)) {
console.log('📦 Serving students from cache');
return res.type('text/csv').send(cache.students.data);
}
console.log('🌐 Fetching students from Google Sheets...');
const response = await fetch(STUDENT_DATA_URL);
if (!response.ok) throw new Error(`Network response was not ok: ${response.statusText}`);
const csvText = await response.text();
// Update cache
cache.students = { data: csvText, timestamp: Date.now() };
console.log('✅ Students cached successfully');
res.type('text/csv').send(csvText);
} catch (error) {
console.error("Error proxying student data:", error);
res.status(500).json({ error: "Failed to fetch student data." });
}
});
// Proxy untuk data CSV izin (dengan cache)
app.get('/api/permits-csv', async (req, res) => {
try {
// Check cache first (shorter TTL for permits)
if (isCacheValid(cache.permits, PERMIT_CACHE_TTL)) {
console.log('📦 Serving permits from cache');
return res.type('text/csv').send(cache.permits.data);
}
console.log('🌐 Fetching permits from Google Sheets...');
const url = `${PERMIT_DATA_URL}&t=${new Date().getTime()}`;
const response = await fetch(url);
if (!response.ok) throw new Error(`Network response was not ok: ${response.statusText}`);
const csvText = await response.text();
// Update cache
cache.permits = { data: csvText, timestamp: Date.now() };
console.log('✅ Permits cached successfully');
res.type('text/csv').send(csvText);
} catch (error) {
console.error("Error proxying permit data:", error);
res.status(500).json({ error: "Failed to fetch permit data." });
}
});
// Proxy untuk semua aksi Google Apps Script (submit, update, delete)
app.post('/api/action', async (req, res) => {
try {
const response = await fetch(GOOGLE_APPS_SCRIPT_URL, {
method: 'POST',
redirect: 'follow',
body: JSON.stringify(req.body),
headers: { "Content-Type": "text/plain;charset=utf-8" },
});
if (!response.ok) {
const errorBody = await response.text();
console.error('Apps Script Proxy Error Body:', errorBody);
throw new Error(`Apps Script responded with status: ${response.status}`);
}
const result = await response.json();
// Invalidate permits cache after any action (submit, update, delete)
cache.permits = { data: null, timestamp: 0 };
console.log('🗑️ Permits cache invalidated after action');
res.json(result);
} catch (error) {
console.error("Error proxying to Google Apps Script:", error);
res.status(500).json({ status: 'error', message: 'Proxy request to Google Apps Script failed.' });
}
});
// Endpoint untuk force refresh cache (opsional, berguna untuk admin)
app.post('/api/refresh-cache', (req, res) => {
cache.students = { data: null, timestamp: 0 };
cache.permits = { data: null, timestamp: 0 };
console.log('🗑️ All caches cleared');
res.json({ status: 'success', message: 'Cache cleared successfully' });
});
// Tentukan path absolut ke folder 'dist'
const distPath = path.resolve(__dirname, 'dist');
// Menyajikan file statis dari direktori build React
app.use(express.static(distPath));
// "Catchall" handler: untuk permintaan apa pun yang tidak cocok dengan rute di atas, kirim file index.html React.
app.get('*', (req, res) => {
res.sendFile(path.resolve(distPath, 'index.html'));
});
app.listen(PORT, () => {
console.log(`🚀 Server is running on port ${PORT}`);
console.log(`📂 Serving static files from ${distPath}`);
console.log(`⏱️ Student cache TTL: ${STUDENT_CACHE_TTL / 1000}s, Permit cache TTL: ${PERMIT_CACHE_TTL / 1000}s`);
});