You are a helpful assistant.
system
Viết một ứng dụng Node.js tải tệp lên nhóm Google Cloud Storage, yêu cầu xác thực người dùng, kiểm tra loại và kích thước tệp, tạo URL đã ký cho tệp đã tải lên và in URL đó ra bảng điều khiển, đồng thời sử dụng biến môi trường cho cấu hình và ghi nhật ký lỗi.
user
Để viết một ứng dụng Node.js tải tệp lên Google Cloud Storage với các yêu cầu như bạn đã nêu, bạn cần thực hiện các bước sau:

1. **Cài đặt các thư viện cần thiết**: Sử dụng `npm` để cài đặt các thư viện cần thiết như `express`, `multer`, `@google-cloud/storage`, và `dotenv`.

2. **Cấu hình biến môi trường**: Sử dụng `dotenv` để quản lý các biến môi trường cho cấu hình.

3. **Xác thực người dùng**: Bạn có thể sử dụng một middleware đơn giản để kiểm tra xác thực người dùng.

4. **Kiểm tra loại và kích thước tệp**: Sử dụng `multer` để xử lý việc tải tệp lên và kiểm tra loại, kích thước tệp.

5. **Tải tệp lên Google Cloud Storage**: Sử dụng thư viện `@google-cloud/storage` để tải tệp lên.

6. **Tạo URL đã ký**: Tạo URL đã ký cho tệp đã tải lên.

7. **Ghi nhật ký lỗi**: Sử dụng `console.error` để ghi nhật ký lỗi.

Dưới đây là mã mẫu cho ứng dụng này:

```javascript
require('dotenv').config();
const express = require('express');
const multer = require('multer');
const { Storage } = require('@google-cloud/storage');
const path = require('path');

// Khởi tạo ứng dụng Express
const app = express();

// Cấu hình Google Cloud Storage
const storage = new Storage({
  projectId: process.env.GCLOUD_PROJECT_ID,
  keyFilename: process.env.GCLOUD_KEY_FILE
});

const bucketName = process.env.GCLOUD_STORAGE_BUCKET;
const bucket = storage.bucket(bucketName);

// Cấu hình multer để xử lý tệp tải lên
const upload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 5 * 1024 * 1024 }, // Giới hạn kích thước tệp 5MB
  fileFilter: (req, file, cb) => {
    const filetypes = /jpeg|jpg|png|gif/;
    const mimetype = filetypes.test(file.mimetype);
    const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
    if (mimetype && extname) {
      return cb(null, true);
    }
    cb(new Error('File type not supported'));
  }
});

// Middleware xác thực người dùng
const authenticateUser = (req, res, next) => {
  const token = req.headers['authorization'];
  if (token === process.env.AUTH_TOKEN) {
    next();
  } else {
    res.status(401).send('Unauthorized');
  }
};

// Route để tải tệp lên
app.post('/upload', authenticateUser, upload.single('file'), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).send('No file uploaded.');
    }

    const blob = bucket.file(req.file.originalname);
    const blobStream = blob.createWriteStream({
      resumable: false
    });

    blobStream.on('error', err => {
      console.error('Blob stream error:', err);
      res.status(500).send('Error uploading file.');
    });

    blobStream.on('finish', async () => {
      try {
        const [url] = await blob.getSignedUrl({
          action: 'read',
          expires: '03-01-2500'
        });
        console.log('File uploaded successfully. Signed URL:', url);
        res.status(200).send(`File uploaded successfully. Signed URL: ${url}`);
      } catch (err) {
        console.error('Error getting signed URL:', err);
        res.status(500).send('Error getting signed URL.');
      }
    });

    blobStream.end(req.file.buffer);
  } catch (err) {
    console.error('Upload error:', err);
    res.status(500).send('Error uploading file.');
  }
});

// Khởi động máy chủ
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
```

**Lưu ý**: 
- Bạn cần tạo một tệp `.env` để lưu trữ các biến môi trường như `GCLOUD_PROJECT_ID`, `GCLOUD_KEY_FILE`, `GCLOUD_STORAGE_BUCKET`, `AUTH_TOKEN`, và `PORT`.
- Đảm bảo rằng bạn đã cấu hình đúng quyền truy cập cho tài khoản dịch vụ Google Cloud của bạn để có thể tải tệp lên và tạo URL đã ký.
assistant