Skip to content

Commit

Permalink
feat: add boilerplate s3-client sdk code connected to digital ocean s…
Browse files Browse the repository at this point in the history
…paces
  • Loading branch information
0-vortex committed Apr 4, 2023
1 parent 564e146 commit a4a71e3
Show file tree
Hide file tree
Showing 9 changed files with 1,448 additions and 4 deletions.
1,321 changes: 1,321 additions & 0 deletions npm-shrinkwrap.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"docs:serve": "npm run docs -- --serve"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.306.0",
"@fastify/express": "^2.3.0",
"@fastify/helmet": "^10.1.0",
"@fastify/rate-limit": "^8.0.0",
Expand Down
6 changes: 5 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ import { TerminusModule } from "@nestjs/terminus";
import { LoggerModule } from "nestjs-pino";
import { clc } from "@nestjs/common/utils/cli-colors.util";

import { SocialCardModule } from "./social-card/social-card.module";
import ApiConfig from "./config/api.config";
import GitHubConfig from "./config/github.config";
import DigitalOceanConfig from "./config/digital-ocean.config";
import { SocialCardModule } from "./social-card/social-card.module";
import { S3FileStorageModule } from "./s3-file-storage/s3-file-storage.module";

@Module({
imports: [
ConfigModule.forRoot({
load: [
ApiConfig,
GitHubConfig,
DigitalOceanConfig,
],
isGlobal: true,
}),
Expand Down Expand Up @@ -44,6 +47,7 @@ import GitHubConfig from "./config/github.config";
TerminusModule,
HttpModule,
SocialCardModule,
S3FileStorageModule,
],
controllers: [],
providers: [],
Expand Down
11 changes: 11 additions & 0 deletions src/config/digital-ocean.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { registerAs } from "@nestjs/config";

const DigitalOceanConfig = registerAs("digitalOcean", () => ({
endpoint: String(process.env.DIGITAL_OCEAN_ENDPOINT ?? "https://sfo3.digitaloceanspaces.com"),
region: String(process.env.DIGITAL_OCEAN_REGION ?? "us-east-1"),
accessKeyId: String(process.env.DIGITAL_OCEAN_ACCESS_KEY_ID ?? ""),
secretAccessKey: String(process.env.DIGITAL_OCEAN_SECRET_ACCESS_KEY ?? ""),
bucketName: String(process.env.DIGITAL_OCEAN_BUCKET_NAME ?? "opengraph-dev"),
}));

export default DigitalOceanConfig;
1 change: 1 addition & 0 deletions src/github/github.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Module } from "@nestjs/common";

import { GithubService } from "./github.service";

@Module({
Expand Down
10 changes: 10 additions & 0 deletions src/s3-file-storage/s3-file-storage.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from "@nestjs/common";

import { S3FileStorageService } from "./s3-file-storage.service";

@Module({
imports: [],
providers: [S3FileStorageService],
exports: [S3FileStorageService],
})
export class S3FileStorageModule {}
92 changes: 92 additions & 0 deletions src/s3-file-storage/s3-file-storage.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Injectable, Inject } from "@nestjs/common";
import { ConfigType } from "@nestjs/config";
import { S3, HeadObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { createHash } from "node:crypto";
import { Readable } from "node:stream";

import DigitalOceanConfig from "../config/digital-ocean.config";

@Injectable()
export class S3FileStorageService {
private s3Client: S3Client;

constructor (
@Inject(DigitalOceanConfig.KEY)
private readonly config: ConfigType<typeof DigitalOceanConfig>,
) {
this.s3Client = new S3({
forcePathStyle: false,
endpoint: config.endpoint,
region: config.region,
credentials: {
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
},
});
}

async fileExists (hash: string): Promise<boolean> {
try {
await this.s3Client.send(
new HeadObjectCommand({
Bucket: this.config.bucketName,
Key: hash,
}),
);
return true;
} catch (error) {
console.error(error);

if (error instanceof Error) {
if (error.name === "ResourceNotFoundException") {
return false;
}
}

throw error;
}
}

async getFileLastModified (hash: string): Promise<Date | null> {
try {
const response = await this.s3Client.send(
new HeadObjectCommand({
Bucket: this.config.bucketName,
Key: hash,
}),
);

return response.LastModified ?? null;
} catch (error) {
console.error(error);

if (error instanceof Error) {
if (error.name === "ResourceNotFoundException") {
return null;
}
}

throw error;
}
}

async uploadFile (
fileContent: Buffer | Readable,
hash: string,
contentType: string,
): Promise<void> {
await this.s3Client.send(
new PutObjectCommand({
Bucket: this.config.bucketName,
Key: hash,
Body: fileContent,
ContentType: contentType,
}),
);
}

generateHash (content: Buffer): string {
return createHash("sha256").update(content)
.digest("hex");
}
}
4 changes: 3 additions & 1 deletion src/social-card/social-card.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Module } from "@nestjs/common";
import { HttpModule } from "@nestjs/axios";

import { SocialCardService } from "./social-card.service";
import { SocialCardController } from "./social-card.controller";
import { GithubModule } from "../github/github.module";
import { S3FileStorageModule } from "../s3-file-storage/s3-file-storage.module";

@Module({
imports: [HttpModule, GithubModule],
imports: [HttpModule, GithubModule, S3FileStorageModule],
providers: [SocialCardService],
controllers: [SocialCardController],
})
Expand Down
6 changes: 4 additions & 2 deletions src/social-card/social-card.service.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { Injectable } from "@nestjs/common";
import { HttpService } from "@nestjs/axios";
import { readFile } from "node:fs/promises";
import { Resvg } from "@resvg/resvg-js";
import { Repository, Language, User } from "@octokit/graphql-schema";
import { readFile } from "node:fs/promises";

import userLangs from "./templates/user-langs";
import userProfileRepos from "./templates/user-profile-repos";
import userProfileCard from "./templates/user-profile-card";
import { GithubService } from "../github/github.service";
import { Repository, Language, User } from "@octokit/graphql-schema";
import { S3FileStorageService } from "../s3-file-storage/s3-file-storage.service";

@Injectable()
export class SocialCardService {
constructor (
private readonly httpService: HttpService,
private readonly githubService: GithubService,
private readonly s3FileStorageService: S3FileStorageService,
) {}

async getUserData (username: string): Promise<{
Expand Down

0 comments on commit a4a71e3

Please sign in to comment.