Spaces:
Running
Running
Update server.js
Browse files
server.js
CHANGED
|
@@ -8,7 +8,6 @@ const cors = require('cors');
|
|
| 8 |
const app = express();
|
| 9 |
const upload = multer({ dest: 'uploads/' });
|
| 10 |
|
| 11 |
-
// Cho phép CORS
|
| 12 |
app.use(cors({ origin: '*', credentials: true, allowedHeaders: ['Content-Type', 'Authorization', 'x-refresh-token'] }));
|
| 13 |
app.use(express.json());
|
| 14 |
|
|
@@ -16,19 +15,15 @@ const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
|
|
| 16 |
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET;
|
| 17 |
const REDIRECT_URI = process.env.REDIRECT_URI;
|
| 18 |
|
| 19 |
-
// Biến lưu token tạm thời (sẽ mất khi server restart)
|
| 20 |
let adminRefreshToken = null;
|
| 21 |
|
| 22 |
const oauth2Client = new google.auth.OAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, REDIRECT_URI);
|
| 23 |
|
| 24 |
-
// Hàm lấy Access Token (Tự động khôi phục từ Header nếu biến local bị mất)
|
| 25 |
async function getAccessToken(req) {
|
| 26 |
-
// 1. Nếu biến server null, thử lấy từ Header của request
|
| 27 |
if (!adminRefreshToken && req && req.headers['x-refresh-token']) {
|
| 28 |
-
console.log('
|
| 29 |
adminRefreshToken = req.headers['x-refresh-token'];
|
| 30 |
}
|
| 31 |
-
|
| 32 |
if (!adminRefreshToken) throw new Error('Chưa đăng nhập Admin (Session expired)');
|
| 33 |
|
| 34 |
oauth2Client.setCredentials({ refresh_token: adminRefreshToken });
|
|
@@ -36,50 +31,59 @@ async function getAccessToken(req) {
|
|
| 36 |
return credentials.access_token;
|
| 37 |
}
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
// 1. Login
|
| 40 |
app.post('/api/oauth/token', async (req, res) => {
|
| 41 |
try {
|
| 42 |
const { code, redirect_uri } = req.body;
|
| 43 |
const client = new google.auth.OAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, redirect_uri || REDIRECT_URI);
|
| 44 |
const { tokens } = await client.getToken(code);
|
| 45 |
-
if (tokens.refresh_token)
|
| 46 |
-
adminRefreshToken = tokens.refresh_token;
|
| 47 |
-
}
|
| 48 |
res.json(tokens);
|
| 49 |
} catch (error) {
|
| 50 |
res.status(500).json({ error: error.message });
|
| 51 |
}
|
| 52 |
});
|
| 53 |
|
| 54 |
-
// 2. Upload
|
| 55 |
app.post('/api/upload', upload.array('files'), async (req, res) => {
|
| 56 |
try {
|
| 57 |
-
|
| 58 |
-
await getAccessToken(req);
|
| 59 |
-
|
| 60 |
const drive = google.drive({ version: 'v3', auth: oauth2Client });
|
| 61 |
-
const { docName } = req.body;
|
| 62 |
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
const
|
| 69 |
-
const
|
| 70 |
|
| 71 |
const uploadedFiles = [];
|
| 72 |
for (const file of req.files) {
|
| 73 |
const media = { mimeType: file.mimetype, body: fs.createReadStream(file.path) };
|
| 74 |
const driveFile = await drive.files.create({
|
| 75 |
-
resource: { name: file.originalname, parents: [
|
| 76 |
media: media,
|
| 77 |
fields: 'id, name, webViewLink, webContentLink'
|
| 78 |
});
|
| 79 |
uploadedFiles.push(driveFile.data);
|
| 80 |
fs.unlinkSync(file.path);
|
| 81 |
}
|
| 82 |
-
res.json({ folderId, files: uploadedFiles });
|
| 83 |
} catch (error) {
|
| 84 |
console.error(error);
|
| 85 |
res.status(500).json({ error: error.message });
|
|
@@ -89,8 +93,7 @@ app.post('/api/upload', upload.array('files'), async (req, res) => {
|
|
| 89 |
// 3. Delete
|
| 90 |
app.delete('/api/delete/:fileId', async (req, res) => {
|
| 91 |
try {
|
| 92 |
-
await getAccessToken(req);
|
| 93 |
-
|
| 94 |
const drive = google.drive({ version: 'v3', auth: oauth2Client });
|
| 95 |
await drive.files.delete({ fileId: req.params.fileId });
|
| 96 |
res.json({ success: true });
|
|
|
|
| 8 |
const app = express();
|
| 9 |
const upload = multer({ dest: 'uploads/' });
|
| 10 |
|
|
|
|
| 11 |
app.use(cors({ origin: '*', credentials: true, allowedHeaders: ['Content-Type', 'Authorization', 'x-refresh-token'] }));
|
| 12 |
app.use(express.json());
|
| 13 |
|
|
|
|
| 15 |
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET;
|
| 16 |
const REDIRECT_URI = process.env.REDIRECT_URI;
|
| 17 |
|
|
|
|
| 18 |
let adminRefreshToken = null;
|
| 19 |
|
| 20 |
const oauth2Client = new google.auth.OAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, REDIRECT_URI);
|
| 21 |
|
|
|
|
| 22 |
async function getAccessToken(req) {
|
|
|
|
| 23 |
if (!adminRefreshToken && req && req.headers['x-refresh-token']) {
|
| 24 |
+
console.log('Restoring session from client header...');
|
| 25 |
adminRefreshToken = req.headers['x-refresh-token'];
|
| 26 |
}
|
|
|
|
| 27 |
if (!adminRefreshToken) throw new Error('Chưa đăng nhập Admin (Session expired)');
|
| 28 |
|
| 29 |
oauth2Client.setCredentials({ refresh_token: adminRefreshToken });
|
|
|
|
| 31 |
return credentials.access_token;
|
| 32 |
}
|
| 33 |
|
| 34 |
+
// Hàm Tìm hoặc Tạo Folder (Đã khôi phục)
|
| 35 |
+
async function findOrCreateFolder(drive, name, parentId = 'root') {
|
| 36 |
+
const q = `mimeType='application/vnd.google-apps.folder' and name='${name}' and '${parentId}' in parents and trashed=false`;
|
| 37 |
+
const res = await drive.files.list({ q, fields: 'files(id, name)' });
|
| 38 |
+
|
| 39 |
+
if (res.data.files.length > 0) return res.data.files[0];
|
| 40 |
+
|
| 41 |
+
const folder = await drive.files.create({
|
| 42 |
+
resource: { name, mimeType: 'application/vnd.google-apps.folder', parents: [parentId] },
|
| 43 |
+
fields: 'id, name'
|
| 44 |
+
});
|
| 45 |
+
return folder.data;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
// 1. Login
|
| 49 |
app.post('/api/oauth/token', async (req, res) => {
|
| 50 |
try {
|
| 51 |
const { code, redirect_uri } = req.body;
|
| 52 |
const client = new google.auth.OAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, redirect_uri || REDIRECT_URI);
|
| 53 |
const { tokens } = await client.getToken(code);
|
| 54 |
+
if (tokens.refresh_token) adminRefreshToken = tokens.refresh_token;
|
|
|
|
|
|
|
| 55 |
res.json(tokens);
|
| 56 |
} catch (error) {
|
| 57 |
res.status(500).json({ error: error.message });
|
| 58 |
}
|
| 59 |
});
|
| 60 |
|
| 61 |
+
// 2. Upload (Đã sửa logic tạo thư mục lồng nhau)
|
| 62 |
app.post('/api/upload', upload.array('files'), async (req, res) => {
|
| 63 |
try {
|
| 64 |
+
await getAccessToken(req); // Khôi phục session
|
|
|
|
|
|
|
| 65 |
const drive = google.drive({ version: 'v3', auth: oauth2Client });
|
|
|
|
| 66 |
|
| 67 |
+
const { docName, type, month } = req.body;
|
| 68 |
+
|
| 69 |
+
// Tạo cấu trúc thư mục
|
| 70 |
+
const rootFolder = await findOrCreateFolder(drive, 'QLVB-DATA');
|
| 71 |
+
const typeFolder = await findOrCreateFolder(drive, type === 'incoming' ? 'VanBanDen' : 'VanBanDi', rootFolder.id);
|
| 72 |
+
const monthFolder = await findOrCreateFolder(drive, month || 'Khac', typeFolder.id);
|
| 73 |
+
const docFolder = await findOrCreateFolder(drive, `${docName} - ${Date.now()}`, monthFolder.id);
|
| 74 |
|
| 75 |
const uploadedFiles = [];
|
| 76 |
for (const file of req.files) {
|
| 77 |
const media = { mimeType: file.mimetype, body: fs.createReadStream(file.path) };
|
| 78 |
const driveFile = await drive.files.create({
|
| 79 |
+
resource: { name: file.originalname, parents: [docFolder.id] },
|
| 80 |
media: media,
|
| 81 |
fields: 'id, name, webViewLink, webContentLink'
|
| 82 |
});
|
| 83 |
uploadedFiles.push(driveFile.data);
|
| 84 |
fs.unlinkSync(file.path);
|
| 85 |
}
|
| 86 |
+
res.json({ folderId: docFolder.id, files: uploadedFiles });
|
| 87 |
} catch (error) {
|
| 88 |
console.error(error);
|
| 89 |
res.status(500).json({ error: error.message });
|
|
|
|
| 93 |
// 3. Delete
|
| 94 |
app.delete('/api/delete/:fileId', async (req, res) => {
|
| 95 |
try {
|
| 96 |
+
await getAccessToken(req);
|
|
|
|
| 97 |
const drive = google.drive({ version: 'v3', auth: oauth2Client });
|
| 98 |
await drive.files.delete({ fileId: req.params.fileId });
|
| 99 |
res.json({ success: true });
|