Skip to content

Commit

Permalink
refactor(controllers): move controllers logic to separate services
Browse files Browse the repository at this point in the history
  • Loading branch information
Veirt committed Nov 14, 2024
1 parent 5750975 commit 9a03346
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 103 deletions.
21 changes: 6 additions & 15 deletions src/controllers/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
import { Request, Response } from "express";
import type { Controller } from "@/types";
import { registerSchema } from "@schemas/auth.schema";
import { auth, firestore } from "@/config";
import * as authService from "@services/auth.service";
import { z } from "zod";
import type { Controller } from "@/types";

export const helloAuth = (_req: Request, res: Response) => {
res.json({ message: "hello auth" });
};

type RegisterSchema = z.infer<typeof registerSchema>;
export const register: Controller<RegisterSchema> = async (req, res) => {
try {
const { name: displayName, email, password } = req.body;
const { name, email, password } = req.body;

const user = await auth.createUser({ displayName, email, password });
firestore.collection("users").doc(user.uid).set({
name: displayName,
createdAt: new Date(),
});
const result = await authService.registerUser(name, email, password);

res.status(201).json({
status: "success",
message: "register success",
data: { userId: user.uid },
message: "Registration successful",
data: result,
});
} catch (error) {
if (error instanceof Error) {
Expand Down
17 changes: 4 additions & 13 deletions src/controllers/subject.controller.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
import { firestore } from "@/config";
import { Controller, Subject } from "@/types";
import * as subjectService from "@/services/subject.service";
import { Controller } from "@/types";
import { logger } from "@middleware/logging.middleware";

export const getAllSubjects: Controller = async (_req, res) => {
try {
const subjectsRef = firestore.collection("subjects");
const snapshot = await subjectsRef.get();

const subjects = snapshot.docs.map((doc) => {
return {
id: doc.id,
name: doc.data().name,
iconUrl: doc.data().iconUrl,
};
});
const subjects = await subjectService.getAllSubjects();

res.json({ status: "success", data: subjects });
} catch (error) {
logger.error(`Error when getting all subjects: ${error}`);
res.status(400).json({
status: "fail",
message: "Failed to get all subjects.",
message: "Failed to get all subjects",
});
}
};
73 changes: 29 additions & 44 deletions src/controllers/user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { firestore } from "@/config";
import { Controller } from "@/types";
import { logger } from "@middleware/logging.middleware";
import { updateProfileSchema } from "@schemas/user.schema";
import { uploadProfilePicture } from "@services/upload.service";
import type { RequestHandler } from "express";
import firebase from "firebase-admin";
import * as userService from "@services/user.service";
import { z } from "zod";

type UpdateProfileSchema = z.infer<typeof updateProfileSchema>;
Expand All @@ -13,56 +10,44 @@ export const updateProfile: Controller<UpdateProfileSchema> = async (
res,
) => {
try {
const { location, ...restOfData } = req.body;
await userService.updateUserProfile(req.user.id, req.body);

// Handle location separately if present
const newData = location
? {
...restOfData,
location: new firebase.firestore.GeoPoint(
location.latitude,
location.longitude,
),
}
: restOfData;

firestore
.collection("users")
.doc(req.user.id)
.update({
...newData,
updatedAt: new Date(),
});
res.status(200).json({
status: "success",
message: "User profile updated successfully",
});
} catch (error) {
logger.debug("Failed to update profile", error);

res
.status(500)
.json({ status: "error", message: "Failed to update profile" });
return;
res.status(500).json({
status: "error",
message: "Failed to update profile",
});
}

res
.status(200)
.json({ status: "success", message: "User profile updated successfully" });
};

export const updateProfilePicture: RequestHandler = async (req, res) => {
const url = await uploadProfilePicture(req.file!, req.user.id);
export const updateProfilePicture: Controller = async (req, res) => {
try {
// Extract the file and call the service to upload the profile picture
const url = await userService.updateUserProfilePicture(
req.file!,
req.user.id,
);

res.json({
status: "success",
message: "Profile picture updated successfully",
data: { url },
});
} catch (error) {
logger.debug("Failed to upload profile picture", error);

if (!url) {
res.status(500).json({
status: "error",
message: "Failed to upload profile picture",
message:
error instanceof Error
? error.message
: "Failed to upload profile picture",
});
return;
}

res.json({
status: "success",
message: "Profile picture updated successfully",
data: {
url,
},
});
};
6 changes: 6 additions & 0 deletions src/helpers/image.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import sharp from "sharp";

// Downscale image to 1024x1024 and convert to JPEG
export const downscaleImage = async (buffer: Buffer) => {
return sharp(buffer).resize(1024, 1024).jpeg({ quality: 80 }).toBuffer();
};
16 changes: 16 additions & 0 deletions src/services/auth.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { auth, firestore } from "@/config";

export const registerUser = async (
name: string,
email: string,
password: string,
) => {
const user = await auth.createUser({ displayName: name, email, password });

await firestore.collection("users").doc(user.uid).set({
name,
createdAt: new Date(),
});

return { userId: user.uid };
};
20 changes: 20 additions & 0 deletions src/services/subject.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { firestore } from "@/config";

export const getAllSubjects = async () => {
try {
const subjectsRef = firestore.collection("subjects");
const snapshot = await subjectsRef.get();

const subjects = snapshot.docs.map((doc) => {
return {
id: doc.id,
name: doc.data().name,
iconUrl: doc.data().iconUrl,
};
});

return subjects;
} catch (error) {
throw new Error(`Error when getting all subjects: ${error}`);
}
};
31 changes: 0 additions & 31 deletions src/services/upload.service.ts

This file was deleted.

59 changes: 59 additions & 0 deletions src/services/user.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { firestore, GCS_BUCKET_NAME } from "@/config";
import { downscaleImage } from "@/helpers/image.helper";
import { Storage } from "@google-cloud/storage";
import { logger } from "@middleware/logging.middleware";
import { updateProfileSchema } from "@schemas/user.schema";
import firebase from "firebase-admin";
import { z } from "zod";

const storage = new Storage();

export const updateUserProfile = async (
userId: string,
data: z.infer<typeof updateProfileSchema>["body"],
) => {
const { location, ...restOfData } = data;

// Handle location separately if present
const newData = location
? {
...restOfData,
location: new firebase.firestore.GeoPoint(
location.latitude,
location.longitude,
),
}
: restOfData;

try {
await firestore
.collection("users")
.doc(userId)
.update({
...newData,
updatedAt: new Date(),
});
} catch (error) {
throw new Error("Failed to update profile");
}
};

// Upload to: profile-pictures/{uid}.jpg
export const updateUserProfilePicture = async (
file: Express.Multer.File,
userId: string,
) => {
try {
const image = await downscaleImage(file.buffer);
const bucket = storage.bucket(GCS_BUCKET_NAME);
const bucketFile = bucket.file(`profile-pictures/${userId}.jpg`);

await bucketFile.save(image, { public: true });

return `https://storage.googleapis.com/${GCS_BUCKET_NAME}/${bucketFile.name}`;
} catch (error) {
logger.error(`Failed to upload profile picture: ${error}`);

return undefined;
}
};

0 comments on commit 9a03346

Please sign in to comment.