require('dotenv').config();
const express = require('express');
const multer = require('multer');
const { google } = require('googleapis');
const fs = require('fs');
const cors = require('cors');
const app = express();
const upload = multer({ dest: 'uploads/' });
app.use(cors({ origin: '*', credentials: true, allowedHeaders: ['Content-Type', 'Authorization', 'x-refresh-token'] }));
app.use(express.json());
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET;
const REDIRECT_URI = process.env.REDIRECT_URI;
let adminRefreshToken = null;
const oauth2Client = new google.auth.OAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, REDIRECT_URI);
async function getAccessToken(req) {
if (!adminRefreshToken && req && req.headers['x-refresh-token']) {
console.log('Restoring session from client header...');
adminRefreshToken = req.headers['x-refresh-token'];
}
if (!adminRefreshToken) throw new Error('Chưa đăng nhập Admin (Session expired)');
oauth2Client.setCredentials({ refresh_token: adminRefreshToken });
const { credentials } = await oauth2Client.refreshAccessToken();
return credentials.access_token;
}
// Hàm Tìm hoặc Tạo Folder (Đã khôi phục)
async function findOrCreateFolder(drive, name, parentId = 'root') {
const q = `mimeType='application/vnd.google-apps.folder' and name='${name}' and '${parentId}' in parents and trashed=false`;
const res = await drive.files.list({ q, fields: 'files(id, name)' });
if (res.data.files.length > 0) return res.data.files[0];
const folder = await drive.files.create({
resource: { name, mimeType: 'application/vnd.google-apps.folder', parents: [parentId] },
fields: 'id, name'
});
return folder.data;
}
// === CẤU HÌNH CHO UPTIMEROBOT & TRANG CHỦ ===
// 1. Xử lý HEAD request (UptimeRobot ping): Trả về 200 OK nhanh, không tốn tài nguyên render HTML
app.head('/', (req, res) => {
res.status(200).end();
});
// 2. Xử lý GET request (Người dùng truy cập): Hiển thị giao diện chào mừng
app.get('/', (req, res) => {
res.send(`
QLVB Server is Running! 🚀
Backend API đang hoạt động bình thường.
Trạng thái Google Auth: ${adminRefreshToken ? 'Đã kết nối' : 'Chưa đăng nhập'}
`);
});
// ================================================
// 1. Login
app.post('/api/oauth/token', async (req, res) => {
try {
const { code, redirect_uri } = req.body;
const client = new google.auth.OAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, redirect_uri || REDIRECT_URI);
const { tokens } = await client.getToken(code);
if (tokens.refresh_token) adminRefreshToken = tokens.refresh_token;
res.json(tokens);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 2. Upload (Đã sửa logic tạo thư mục lồng nhau)
app.post('/api/upload', upload.array('files'), async (req, res) => {
try {
await getAccessToken(req); // Khôi phục session
const drive = google.drive({ version: 'v3', auth: oauth2Client });
const { docName, type, month } = req.body;
// Tạo cấu trúc thư mục
const rootFolder = await findOrCreateFolder(drive, 'QLVB-DATA');
const typeFolder = await findOrCreateFolder(drive, type === 'incoming' ? 'VanBanDen' : 'VanBanDi', rootFolder.id);
const monthFolder = await findOrCreateFolder(drive, month || 'Khac', typeFolder.id);
const docFolder = await findOrCreateFolder(drive, `${docName} - ${Date.now()}`, monthFolder.id);
const uploadedFiles = [];
for (const file of req.files) {
const media = { mimeType: file.mimetype, body: fs.createReadStream(file.path) };
const driveFile = await drive.files.create({
resource: { name: file.originalname, parents: [docFolder.id] },
media: media,
fields: 'id, name, webViewLink, webContentLink'
});
uploadedFiles.push(driveFile.data);
fs.unlinkSync(file.path);
}
res.json({ folderId: docFolder.id, files: uploadedFiles });
} catch (error) {
console.error(error);
res.status(500).json({ error: error.message });
}
});
// 3. Delete
app.delete('/api/delete/:fileId', async (req, res) => {
try {
await getAccessToken(req);
const drive = google.drive({ version: 'v3', auth: oauth2Client });
await drive.files.delete({ fileId: req.params.fileId });
res.json({ success: true });
} catch (error) {
console.error(error);
res.status(500).json({ error: error.message });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));