Spaces:
Running
Running
| 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(` | |
| <div style="font-family: sans-serif; text-align: center; padding-top: 50px;"> | |
| <h1 style="color: green;">QLVB Server is Running! 🚀</h1> | |
| <p>Backend API đang hoạt động bình thường.</p> | |
| <p>Trạng thái Google Auth: ${adminRefreshToken ? '<b style="color:blue">Đã kết nối</b>' : '<b style="color:red">Chưa đăng nhập</b>'}</p> | |
| </div> | |
| `); | |
| }); | |
| // ================================================ | |
| // 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}`)); |