Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/s3/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ export { default as FileService } from "./model/files/service";
export { default as S3Client } from "./utils/s3Client";
export type { FilePayload, Multipart, S3Config } from "./types";
export type { File, FileCreateInput, FileUpdateInput } from "./types/file";
export type { FileUpload as GraphQLFileUpload } from "graphql-upload-minimal";
export type {
FileUpload as GraphQLFileUpload,
Upload as GraphQLUpload,
} from "graphql-upload-minimal";

export { default } from "./plugin";
export { default as ajvFilePlugin } from "./plugins/ajvFile";
Expand Down
6 changes: 3 additions & 3 deletions packages/s3/src/plugins/multipartParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ const plugin = async (fastify: FastifyInstance) => {
req.routeOptions.url?.startsWith(req.config.graphql.path as string)
) {
req.graphqlFileUploadMultipart = true;

// eslint-disable-next-line unicorn/no-null
done(null);
} else {
processMultipartFormData(req, _payload, done);
}
}

// eslint-disable-next-line unicorn/no-null
done(null);
});
};

Expand Down
5 changes: 4 additions & 1 deletion packages/user/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Register the user plugin with your Fastify instance:
```typescript
import configPlugin from "@prefabs.tech/fastify-config";
import mailerPlugin from "@prefabs.tech/fastify-mailer";
import s3Plugin from "@prefabs.tech/fastify-s3";
import s3Plugin, { multipartParserPlugin } from "@prefabs.tech/fastify-s3";
import slonikPlugin, { migrationPlugin } from "@prefabs.tech/fastify-slonik";
import userPlugin from "@prefabs.tech/fastify-user";
import Fastify from "fastify";
Expand All @@ -56,6 +56,9 @@ const start = async () => {

// Register mailer plugin
await fastify.register(mailerPlugin, config.mailer);

// Register multipart content-type parser plugin
await api.register(multipartParserPlugin);

// Register mailer plugin
await fastify.register(s3Plugin);
Expand Down
15 changes: 7 additions & 8 deletions packages/user/src/model/users/graphql/resolver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GraphQLFileUpload, Multipart } from "@prefabs.tech/fastify-s3";
import { GraphQLUpload, Multipart } from "@prefabs.tech/fastify-s3";
import { mercurius } from "mercurius";
import EmailVerification, {
EmailVerificationClaim,
Expand Down Expand Up @@ -310,22 +310,21 @@ const Mutation = {
uploadPhoto: async (
parent: unknown,
arguments_: {
photo: {
file: GraphQLFileUpload;
};
photo: GraphQLUpload;
},
context: MercuriusContext,
) => {
const { app, config, database, dbSchema, reply, user } = context;
const { photo } = arguments_;
const photo = await arguments_.photo;
const { file: photoFile } = photo;

const service = getUserService(config, database, dbSchema);

if (!user) {
return new mercurius.ErrorWithProps("unauthorized", {}, 401);
}

if (!photo) {
if (!photoFile) {
throw new CustomApiError({
message: "Missing photo file in the request body",
name: "ERROR_FILE_MISSING",
Expand All @@ -334,10 +333,10 @@ const Mutation = {
}

try {
const fileData = photo.file.createReadStream();
const fileData = photoFile.createReadStream();

const fileToUpload: Multipart = {
...photo.file,
...photoFile,
data: fileData,
limit: false,
};
Expand Down
215 changes: 2 additions & 213 deletions packages/user/src/model/users/graphql/schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { gql } from "@prefabs.tech/fastify-graphql";

const user = gql`
scalar Upload

type User {
id: String!
deletedAt: Float
Expand Down Expand Up @@ -89,217 +91,4 @@ const user = gql`
}
`;

const errorSchema = {
type: "object",
properties: {
code: { type: "string" },
error: { type: "object" },
message: { type: "string" },
statusCode: { type: "number" },
status: { type: "string" },
},
};

const userSchema = {
type: "object",
properties: {
id: { type: "string" },
email: { type: "string", format: "email" },
firstName: { type: "string" },
lastName: { type: "string" },
role: { type: "string" },
status: { type: "string" },
createdAt: { type: "number" },
updatedAt: { type: "number" },
},
required: ["id", "email", "role", "status", "createdAt", "updatedAt"],
};

export const createUserSchema = {
description: "Create a new user",
operationId: "createUser",
body: {
type: "object",
required: ["email", "password", "role"],
properties: {
email: { type: "string", format: "email" },
password: { type: "string", format: "password" },
firstName: { type: "string" },
lastName: { type: "string" },
role: { type: "string" },
},
},
response: {
201: userSchema,
400: {
description: "Bad Request",
...errorSchema,
},
401: {
description: "Unauthorized",
...errorSchema,
},
403: {
description: "Forbidden",
...errorSchema,
},
500: {
...errorSchema,
},
},
tags: ["users"],
};

export const deleteUserSchema = {
description: "Delete a user by ID",
operationId: "deleteUser",
params: {
type: "object",
required: ["id"],
properties: {
id: { type: "string" },
},
},
response: {
200: {
type: "object",
properties: {
status: { type: "string" },
},
},
401: {
description: "Unauthorized",
...errorSchema,
},
403: {
description: "Forbidden",
...errorSchema,
},
404: {
description: "User not found",
...errorSchema,
},
500: {
...errorSchema,
},
},
tags: ["users"],
};

export const getUserSchema = {
description: "Get a user by ID",
operationId: "getUser",
params: {
type: "object",
required: ["id"],
properties: {
id: { type: "string" },
},
},
response: {
200: userSchema,
401: {
description: "Unauthorized",
...errorSchema,
},
403: {
description: "Forbidden",
...errorSchema,
},
404: {
description: "User not found",
...errorSchema,
},
500: {
...errorSchema,
},
},
tags: ["users"],
};

export const getUsersSchema = {
description:
"Get a paginated list of users with optional filtering and sorting",
operationId: "getUsers",
querystring: {
type: "object",
properties: {
limit: { type: "number" },
offset: { type: "number" },
filters: { type: "string" },
sort: { type: "string" },
},
},
response: {
200: {
type: "object",
required: ["totalCount", "filteredCount", "data"],
properties: {
totalCount: { type: "integer" },
filteredCount: { type: "integer" },
data: {
type: "array",
items: userSchema,
},
},
},
401: {
description: "Unauthorized",
...errorSchema,
},
403: {
description: "Forbidden",
...errorSchema,
},
500: {
...errorSchema,
},
},
tags: ["users"],
};

export const updateUserSchema = {
description: "Update a user's information",
operationId: "updateUser",
params: {
type: "object",
required: ["id"],
properties: {
id: { type: "string" },
},
},
body: {
type: "object",
properties: {
firstName: { type: "string" },
lastName: { type: "string" },
role: { type: "string" },
status: { type: "string" },
},
},
response: {
200: userSchema,
400: {
description: "Bad Request",
...errorSchema,
},
401: {
description: "Unauthorized",
...errorSchema,
},
403: {
description: "Forbidden",
...errorSchema,
},
404: {
description: "User not found",
...errorSchema,
},
500: {
...errorSchema,
},
},
tags: ["users"],
};

export default user;