Skip to content

Commit

Permalink
Merge pull request #7 from La-404-Devinci/feat/avatar-edition
Browse files Browse the repository at this point in the history
feat: add minio arch & user avatar upload
  • Loading branch information
Kan-A-Pesh authored Nov 4, 2024
2 parents 3c8dccb + f538caf commit 572a9cc
Show file tree
Hide file tree
Showing 20 changed files with 1,104 additions and 27 deletions.
4 changes: 2 additions & 2 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ POSTGRES_HOST=postgres
REDIS_HOST=redis

MINIO_ROOT_USER=minio
MINIO_ROOT_PASSWORD=minio
MINIO_ROOT_PASSWORD=password
MINIO_HOST=minio
MINIO_DEFAULT_BUCKETS=george

Expand Down Expand Up @@ -49,4 +49,4 @@ MAIL_REDIRECT_URL="http://localhost:1337/auth/confirm#{token}"

# Profile redirect URL
# The {uuid} placeholder will be replaced by the user's UUID
PROFILE_REDIRECT_URL="http://localhost:1337/profile/{uuid}"
PROFILE_REDIRECT_URL="/profile/{uuid}"
2 changes: 1 addition & 1 deletion controllers/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default abstract class UserController {
}
}

public static async updateUser(uuid: string, username?: string, avatarUrl?: string, quote?: string) {
public static async updateUser(uuid: string, username?: string, avatarUrl?: string | null, quote?: string) {
try {
const user = await DB.instance
.update(users)
Expand Down
2 changes: 2 additions & 0 deletions database/init.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { initDrizzle } from "./config";
import Redis from "./redis";
import S3 from "./s3";

export function initDatabase() {
initDrizzle();
Redis.init();
S3.init();

return () => {
Redis.close();
Expand Down
93 changes: 93 additions & 0 deletions database/s3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import globals from "@/env/env";
import Logger from "@/log/logger";
import compress from "@/utils/compress";
import { randomUUID } from "crypto";
import { Client } from "minio";
import internal from "stream";

export default abstract class S3 {
public static client: Client;

public static init() {
S3.client = new Client({
endPoint: globals.env.MINIO_HOST,
port: globals.env.MINIO_PORT,
useSSL: false,
accessKey: globals.env.MINIO_ROOT_USER,
secretKey: globals.env.MINIO_ROOT_PASSWORD,
pathStyle: true
});

S3.client
.bucketExists(globals.env.MINIO_DEFAULT_BUCKETS)
.then(
(exists) =>
!exists &&
Logger.error(`s3.ts::init | Default bucket ${globals.env.MINIO_DEFAULT_BUCKETS} does not exist`)
);
}

/**
* Uploads a compressed image to Minio as a base64 string.
* @param {string} data The base64 string of the image.
* @param {string} options.path The path to upload the image to.
* @param {string} options.creator The creator of the image.
* @returns {Promise<string | null>} The path of the uploaded image.
*/
public static async putImage(data: string, options?: { path?: string; creator?: string }): Promise<string | null> {
try {
const imagePath = options?.path ?? randomUUID();
const image = await compress(data);

await S3.client.putObject(globals.env.MINIO_DEFAULT_BUCKETS, imagePath, image, image.length, {
"Content-Type": "application/octet-stream",
"Last-Modified": new Date().toUTCString(),
"x-amz-acl": "public-read",
"x-amz-meta-creator": options?.creator
});

return imagePath;
} catch (err) {
Logger.error("s3.ts::putImage | Error compressing image", err);
return null;
}
}

/**
* Gets a base64 image.
* @param {string} path
* @returns {Promise<string | null>}
*/
public static async getImage(path: string): Promise<string | null> {
try {
const data: internal.Readable = await S3.client.getObject(globals.env.MINIO_DEFAULT_BUCKETS, path);
if (!data) return null;

const buffer = await new Promise<Buffer>((resolve, reject) => {
const chunks: Buffer[] = [];
data.on("data", (chunk: Buffer) => {
chunks.push(chunk);
})
.on("end", () => {
resolve(Buffer.concat(chunks));
})
.on("error", (err) => {
reject(err);
});
});

return buffer.toString("base64");
} catch (err) {
Logger.error("s3.ts::getImage | Error getting image", err);
return null;
}
}

/**
* @description Deletes an image from the S3 bucket.
* @param {string} path The path of the image to delete.
*/
public static async deleteImage(path: string) {
await S3.client.removeObject(globals.env.MINIO_DEFAULT_BUCKETS, path);
}
}
8 changes: 5 additions & 3 deletions docker/compose.ci.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ services:
REDIS_HOST: redis

MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio
MINIO_ROOT_PASSWORD: password
MINIO_HOST: minio
MINIO_DEFAULT_BUCKETS: george

Expand All @@ -33,6 +33,8 @@ services:
PROFILE_REDIRECT_URL: http://localhost:1337/profile/{uuid}
depends_on:
- postgres
- redis
- minio

postgres:
image: postgres:17-alpine
Expand All @@ -46,8 +48,8 @@ services:
image: redis:7.4

minio:
image: "minio/minio:latest"
image: "bitnami/minio:latest"
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio
MINIO_ROOT_PASSWORD: password
MINIO_DEFAULT_BUCKETS: george
18 changes: 12 additions & 6 deletions docker/compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ services:
ports:
- "0.0.0.0:3000:3000"
volumes:
- /data/app/node_modules
- ..:/data/app
environment:
NODE_ENV: development
Expand All @@ -17,27 +18,32 @@ services:
REDIS_HOST: redis

MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio
MINIO_ROOT_PASSWORD: password
MINIO_HOST: minio
MINIO_DEFAULT_BUCKETS: george
stdin_open: true
tty: true
depends_on:
- postgres
- redis
- minio

redis:
image: redis:7.4
ports:
- "0.0.0.0:6379:6379"
- "0.0.0.0:3001:6379"

minio:
image: minio/minio
image: bitnami/minio
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio
MINIO_ROOT_PASSWORD: password
MINIO_DEFAULT_BUCKETS: george
ports:
- "0.0.0.0:8900:9000"
- "0.0.0.0:3002:9000"
# # Uncomment the next line to have a persistent volume for the Minio server
# volumes:
# - minio:/bitnami/minio/data

postgres:
image: postgres:17-alpine
Expand All @@ -47,7 +53,7 @@ services:
POSTGRES_PASSWORD: postgres
POSTGRES_HOST: postgres
ports:
- "0.0.0.0:3030:5432" # Use port 3030 for external debugging
- "0.0.0.0:3003:5432" # Use port 3030 for external debugging
# # Uncomment the next line to have a persistent volume for the database
# volumes:
# - postgres:/var/lib/postgresql/data
8 changes: 5 additions & 3 deletions docker/compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ services:
REDIS_HOST: redis

MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio
MINIO_ROOT_PASSWORD: password
MINIO_HOST: minio
MINIO_DEFAULT_BUCKETS: george

Expand All @@ -39,6 +39,8 @@ services:
PROFILE_REDIRECT_URL: http://localhost:1337/profile/{uuid}
depends_on:
- postgres
- redis
- minio

postgres:
image: postgres:17-alpine
Expand All @@ -52,8 +54,8 @@ services:
image: redis:7.4

minio:
image: "minio/minio:latest"
image: "bitnami/minio:latest"
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio
MINIO_ROOT_PASSWORD: password
MINIO_DEFAULT_BUCKETS: george
4 changes: 3 additions & 1 deletion docker/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ services:
- ../logs:/logs
depends_on:
- postgres
- redis
- minio

postgres:
image: postgres:17-alpine
Expand All @@ -29,7 +31,7 @@ services:
image: redis:7.4

minio:
image: "minio/minio:latest"
image: "bitnami/minio:latest"
env_file:
- path: ../.env
required: true
Expand Down
2 changes: 1 addition & 1 deletion env/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const envSchema = z.object({
REDIS_PORT: znumber().default("6379"),

MINIO_ROOT_USER: z.string().default("minio"),
MINIO_ROOT_PASSWORD: z.string().default("minio"),
MINIO_ROOT_PASSWORD: z.string().default("password"),
MINIO_HOST: z.string().default("minio"),
MINIO_PORT: znumber().default("9000"),
MINIO_DEFAULT_BUCKETS: z.string().default("george"),
Expand Down
4 changes: 4 additions & 0 deletions i18n/en/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
"template": "This is a template error message",
"validation": "Validation error",
"database": "Database error",
"image": {
"invalid": "Invalid image",
"notFound": "Image not found"
},
"auth": {
"admin": "Invalid X-ADMIN-KEY header",
"toomany": "Too many requests",
Expand Down
Loading

0 comments on commit 572a9cc

Please sign in to comment.