Skip to content

Commit

Permalink
[Enhance] Get service-account-keyfile secret
Browse files Browse the repository at this point in the history
  • Loading branch information
maituongluanvn committed Aug 9, 2024
1 parent f9047b2 commit 85c65c9
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 31 deletions.
9 changes: 3 additions & 6 deletions .github/workflows/docker-build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,9 @@ jobs:
- name: Build Docker image
run: |
printf "%s" "${{ secrets.SERVICE_ACCOUNT_KEYFILE }}" > /tmp/service-account-keyfile.json
DOCKER_BUILDKIT=1 docker build \
--secret id=service_account_keyfile,src=/tmp/service-account-keyfile.json \
-f Dockerfile.api \
-t asia-east2-docker.pkg.dev/maituongluan/dashboard/api:$VERSION .
rm /tmp/service-account-keyfile.json
docker build \
-f Dockerfile.api \
-t asia-east2-docker.pkg.dev/maituongluan/dashboard/api:$VERSION .
- name: Push Docker image
run: |
Expand Down
10 changes: 1 addition & 9 deletions Dockerfile.api
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# syntax=docker/dockerfile:1.3-labs

# Stage 1: Build
FROM --platform=$BUILDPLATFORM node:20.15.0-slim AS builder

Expand All @@ -16,11 +14,6 @@ COPY . .
# Install dependencies for the whole monorepo
RUN pnpm install

# Securely use the service-account-keyfile.json during the build process
# Make sure to activate BuildKit by using the syntax directive at the top of the file
RUN --mount=type=secret,id=service_account_keyfile \
cp /run/secrets/service_account_keyfile /app/service-account-keyfile.json

# Run Turborepo build for the specific project (api)
RUN pnpm run build:api
RUN pnpm run build:definition
Expand All @@ -29,6 +22,7 @@ RUN pnpm run build:definition
FROM --platform=$TARGETPLATFORM node:20.15.0-slim AS production

ENV MONGO_URI=""
ENV GCLOUD_SECRET_NAME="service-account-keyfile"

# Install pnpm
RUN npm install -g pnpm
Expand All @@ -39,8 +33,6 @@ WORKDIR /app
COPY package.json pnpm-lock.yaml turbo.json pnpm-workspace.yaml ./

# Copy the needed folder
COPY --from=builder /app/service-account-keyfile.json /app/apps/api/dist/service_account_keyfile.json
COPY --from=builder /app/service-account-keyfile.json /app/apps/api/service_account_keyfile.json
COPY --from=builder /app/apps/api ./apps/api
COPY --from=builder /app/packages ./packages

Expand Down
1 change: 1 addition & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@google-cloud/secret-manager": "^5.6.0",
"@google-cloud/storage": "^7.12.0",
"@nestjs/axios": "^3.0.1",
"@nestjs/common": "^10.3.10",
Expand Down
48 changes: 38 additions & 10 deletions apps/api/src/image/image.service.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,52 @@
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { Storage } from '@google-cloud/storage';
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
import { Readable } from 'stream';
import * as path from 'path';
import * as fs from 'fs'; // Thêm import module fs
import { v4 as uuidv4 } from 'uuid';

@Injectable()
export class ImageService {
private readonly storage: Storage;
private storage: Storage | null = null; // Không dùng readonly ở đây
private readonly bucketName: string = 'hoangphuc'; // Tên bucket của bạn
private readonly keyFilePath: string = path.join(__dirname, '..', '..', 'service-account-keyfile.json'); // Đường dẫn đến tệp khóa

constructor() {
if (!fs.existsSync(this.keyFilePath)) {
// Kiểm tra sự tồn tại của tệp khóa
throw new HttpException('Service account key file does not exist', HttpStatus.INTERNAL_SERVER_ERROR);
this.initializeStorage().catch(err => {
throw new HttpException(
`Failed to initialize storage: ${err.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
});
}

private async initializeStorage(): Promise<void> {
const secretClient = new SecretManagerServiceClient();
const secretName = process.env.GCLOUD_SECRET_NAME;

if (!secretName) {
throw new HttpException('Secret name not provided', HttpStatus.INTERNAL_SERVER_ERROR);
}

this.storage = new Storage({
keyFilename: this.keyFilePath, // Đường dẫn đến tệp khóa
});
try {
// Truy cập Secret
const [version] = await secretClient.accessSecretVersion({ name: secretName });
const keyFileContents = version.payload.data.toString();

// Khởi tạo Storage với tệp khóa JSON
this.storage = new Storage({ credentials: JSON.parse(keyFileContents) });
} catch (err) {
throw new HttpException(`Failed to access secret: ${err.message}`, HttpStatus.INTERNAL_SERVER_ERROR);
}
}

private checkStorageInitialized() {
if (!this.storage) {
throw new HttpException('Storage not initialized', HttpStatus.INTERNAL_SERVER_ERROR);
}
}

async uploadFile(file: Express.Multer.File): Promise<string> {
this.checkStorageInitialized();

const bucket = this.storage.bucket(this.bucketName);
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
const blob = bucket.file(`uploads/${uuidv4()}_${file.originalname}`); // Thư mục uploads
Expand All @@ -43,6 +67,8 @@ export class ImageService {
}

async fetchImage(filename: string): Promise<Buffer> {
this.checkStorageInitialized();

const bucket = this.storage.bucket(this.bucketName);
const file = bucket.file(`uploads/${filename}`); // Đảm bảo đường dẫn đúng

Expand All @@ -59,6 +85,8 @@ export class ImageService {
}

async deleteFile(filename: string): Promise<void> {
this.checkStorageInitialized();

const bucket = this.storage.bucket(this.bucketName);
const file = bucket.file(`uploads/${filename}`); // Đảm bảo đường dẫn đúng

Expand Down
Loading

0 comments on commit 85c65c9

Please sign in to comment.