{
  "slug": "oauth",
  "runtimes": {
    "node": {
      "frameworks": {
        "express": {
          "prompt": "Select OAuth provider: ",
          "variants": {
            "google": {
              "label": "Google",
              "dependencies": {
                "runtime": [
                  "dotenv-flow",
                  "cross-env",
                  "pino",
                  "pino-pretty",
                  "passport",
                  "passport-google-oauth20",
                  "zod"
                ],
                "dev": [
                  "@types/passport-google-oauth20",
                  "@types/passport"
                ]
              },
              "env": [
                "LOG_LEVEL",
                "GOOGLE_CLIENT_ID",
                "GOOGLE_CLIENT_SECRET",
                "GOOGLE_REDIRECT_URI"
              ],
              "architectures": {
                "mvc": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.google-oauth.ts",
                      "content": "import express, { Express, Request, Response } from \"express\";\n\nimport { notFoundHandler } from \"./middlewares/not-found-handler\";\nimport { errorHandler } from \"./middlewares/error-handler\";\n\nimport AuthRoutes from \"./routes/google-oauth.routes\";\n\nimport \"./configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api/auth\", AuthRoutes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/google-oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\n\nimport { googleOAuth } from \"../controllers/google-oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/google\",\n  passport.authenticate(\"google\", {\n    scope: [\"email\", \"profile\", \"openid\"],\n    prompt: \"consent\"\n  })\n);\n\nrouter.get(\n  \"/google/callback\",\n  passport.authenticate(\"google\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed\n    session: false\n  }),\n  googleOAuth\n);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../utils/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../utils/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/controllers/google-oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile } from \"passport-google-oauth20\";\n\nimport { ApiResponse } from \"../utils/api-response\";\nimport { AsyncHandler } from \"../utils/async-handler\";\nimport { ApiError } from \"../utils/api-error\";\n\n//? login with google\nexport const googleOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as Profile | undefined;\n    const user = data?._json;\n\n    if (!user || !data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: data?.emails && data?.emails[0]?.verified,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    const userInfo2 = {\n      provider: data?.provider,\n      providerId: user.sub,\n      name: user.name,\n      email: user.email,\n      isEmailVerified: user.email_verified,\n      avatar: user.picture\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      userInfo,\n      userInfo2\n    });\n  }\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport { Strategy as GoogleStrategy } from \"passport-google-oauth20\";\nimport env from \"./env\";\n\nconst clientId = env.GOOGLE_CLIENT_ID;\nconst clientSecret = env.GOOGLE_CLIENT_SECRET;\nconst redirectUri = env.GOOGLE_REDIRECT_URI;\n\npassport.use(\n  new GoogleStrategy(\n    {\n      clientID: clientId,\n      clientSecret,\n      callbackURL: redirectUri\n    },\n    function (accessToken, refreshToken, profile, cb) {\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/env.ts",
                      "content": "import \"dotenv-flow/config\";\n\ninterface Config {\n  PORT: number;\n  NODE_ENV: string;\n  LOG_LEVEL: string;\n  CORS_ORIGIN: string;\n\n  GOOGLE_CLIENT_ID: string;\n  GOOGLE_CLIENT_SECRET: string;\n  GOOGLE_REDIRECT_URI: string;\n}\n\nconst env: Config = {\n  PORT: Number(process.env.PORT) || 1111,\n  NODE_ENV: process.env.NODE_ENV || \"development\",\n  LOG_LEVEL: process.env.LOG_LEVEL || \"info\",\n  CORS_ORIGIN: process.env.CORS_ORIGIN || \"*\",\n\n  GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID!,\n  GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET!,\n  GOOGLE_REDIRECT_URI: process.env.GOOGLE_REDIRECT_URI!\n};\n\nexport default env;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    }
                  ]
                },
                "feature": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.google-oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./shared/middlewares/not-found-handler\";\nimport { errorHandler } from \"./shared/middlewares/error-handler\";\n\nimport Routes from \"./routes/index\";\n\nimport \"./shared/configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api\", Routes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/index.ts",
                      "content": "import { Router } from \"express\";\nimport OAuthRoutes from \"../modules/google-oauth/google-oauth.routes\";\n\nconst router = Router();\n\nrouter.use(\"/auth\", OAuthRoutes);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../errors/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../errors/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/errors/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport { Strategy as GoogleStrategy } from \"passport-google-oauth20\";\nimport env from \"./env\";\n\nconst clientId = env.GOOGLE_CLIENT_ID;\nconst clientSecret = env.GOOGLE_CLIENT_SECRET;\nconst redirectUri = env.GOOGLE_REDIRECT_URI;\n\npassport.use(\n  new GoogleStrategy(\n    {\n      clientID: clientId,\n      clientSecret,\n      callbackURL: redirectUri\n    },\n    function (accessToken, refreshToken, profile, cb) {\n      return cb(null, profile);\n    }\n  )\n);\n\npassport.serializeUser((user, done) => {\n  done(null, user);\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/env.ts",
                      "content": "import \"dotenv-flow/config\";\n\ninterface Config {\n  PORT: number;\n  NODE_ENV: string;\n  LOG_LEVEL: string;\n  CORS_ORIGIN: string;\n\n  GOOGLE_CLIENT_ID: string;\n  GOOGLE_CLIENT_SECRET: string;\n  GOOGLE_REDIRECT_URI: string;\n}\n\nconst env: Config = {\n  PORT: Number(process.env.PORT) || 1111,\n  NODE_ENV: process.env.NODE_ENV || \"development\",\n  LOG_LEVEL: process.env.LOG_LEVEL || \"info\",\n  CORS_ORIGIN: process.env.CORS_ORIGIN || \"*\",\n\n  GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID!,\n  GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET!,\n  GOOGLE_REDIRECT_URI: process.env.GOOGLE_REDIRECT_URI!\n};\n\nexport default env;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/google-oauth/google-oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\n\nimport { googleOAuth } from \"./google-oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/google\",\n  passport.authenticate(\"google\", {\n    scope: [\"email\", \"profile\", \"openid\"],\n    prompt: \"consent\"\n  })\n);\n\nrouter.get(\n  \"/google/callback\",\n  passport.authenticate(\"google\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed\n    session: false\n  }),\n  googleOAuth\n);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/google-oauth/google-oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile } from \"passport-google-oauth20\";\n\nimport { AsyncHandler } from \"../../shared/utils/async-handler\";\nimport { ApiError } from \"../../shared/errors/api-error\";\nimport { ApiResponse } from \"../../shared/utils/api-response\";\n\n//? login with google\nexport const googleOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as Profile | undefined;\n    const user = data?._json;\n\n    if (!user || !data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: data?.emails && data?.emails[0]?.verified,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    const userInfo2 = {\n      provider: data?.provider,\n      providerId: user.sub,\n      name: user.name,\n      email: user.email,\n      isEmailVerified: user.email_verified,\n      avatar: user.picture\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      userInfo,\n      userInfo2\n    });\n  }\n);\n"
                    }
                  ]
                }
              }
            },
            "github": {
              "label": "GitHub",
              "dependencies": {
                "runtime": [
                  "dotenv-flow",
                  "cross-env",
                  "pino",
                  "pino-pretty",
                  "passport",
                  "passport-github2"
                ],
                "dev": [
                  "@types/passport-github2",
                  "@types/passport"
                ]
              },
              "env": [
                "LOG_LEVEL",
                "GITHUB_CLIENT_ID",
                "GITHUB_CLIENT_SECRET",
                "GITHUB_REDIRECT_URI"
              ],
              "architectures": {
                "mvc": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.google-oauth.ts",
                      "content": "import express, { Express, Request, Response } from \"express\";\n\nimport { notFoundHandler } from \"./middlewares/not-found-handler\";\nimport { errorHandler } from \"./middlewares/error-handler\";\n\nimport AuthRoutes from \"./routes/github-oauth.routes\";\n\nimport \"./configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api/auth\", AuthRoutes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/github-oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\nimport { githubOAuth } from \"../controllers/github-oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/github\",\n  passport.authenticate(\"github\", { scope: [\"user:email\"] })\n);\n\nrouter.get(\n  \"/github/callback\",\n  passport.authenticate(\"github\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false\n  }),\n  githubOAuth\n);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../utils/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../utils/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/controllers/github-oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile } from \"passport-github2\";\n\nimport { ApiResponse } from \"../utils/api-response\";\nimport { AsyncHandler } from \"../utils/async-handler\";\nimport { ApiError } from \"../utils/api-error\";\n\n//? login with github\nexport const githubOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as Profile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    // console.log(data);\n\n    const user = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.photos && data.photos[0].value\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user\n    });\n  }\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport { Strategy as GitHubStrategy, Profile } from \"passport-github2\";\nimport env from \"./env\";\n\npassport.use(\n  new GitHubStrategy(\n    {\n      clientID: env.GITHUB_CLIENT_ID,\n      clientSecret: env.GITHUB_CLIENT_SECRET,\n      callbackURL: env.GITHUB_REDIRECT_URI\n    },\n    function (\n      accessToken: string,\n      refreshToken: string,\n      profile: Profile,\n      cb: (error: Error | null, user?: any) => void\n    ) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/env.ts",
                      "content": "import \"dotenv-flow/config\";\n\ninterface Config {\n  PORT: number;\n  NODE_ENV: string;\n  LOG_LEVEL: string;\n  CORS_ORIGIN: string;\n\n  GITHUB_CLIENT_ID: string;\n  GITHUB_CLIENT_SECRET: string;\n  GITHUB_REDIRECT_URI: string;\n}\n\nconst env: Config = {\n  PORT: Number(process.env.PORT) || 1111,\n  NODE_ENV: process.env.NODE_ENV || \"development\",\n  LOG_LEVEL: process.env.LOG_LEVEL || \"info\",\n  CORS_ORIGIN: process.env.CORS_ORIGIN || \"*\",\n\n  GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID!,\n  GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET!,\n  GITHUB_REDIRECT_URI: process.env.GITHUB_REDIRECT_URI!\n};\n\nexport default env;\n"
                    }
                  ]
                },
                "feature": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.github-oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./shared/middlewares/not-found-handler\";\nimport { errorHandler } from \"./shared/middlewares/error-handler\";\n\nimport Routes from \"./routes/index\";\n\nimport \"./shared/configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api\", Routes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/index.ts",
                      "content": "import { Router } from \"express\";\nimport OAuthRoutes from \"../modules/oauth/github-oauth.routes\";\n\nconst router = Router();\n\nrouter.use(\"/auth\", OAuthRoutes);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../shared/errors/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../../configs/env\";\n\nimport { ApiError } from \"../../utils/api-error\";\nimport { logger } from \"../../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  let statusCode = 500;\n  let message = \"Internal server error\";\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../errors/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../errors/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/errors/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport { Strategy as GitHubStrategy, Profile } from \"passport-github2\";\nimport env from \"./env\";\n\npassport.use(\n  new GitHubStrategy(\n    {\n      clientID: env.GITHUB_CLIENT_ID,\n      clientSecret: env.GITHUB_CLIENT_SECRET,\n      callbackURL: env.GITHUB_REDIRECT_URI\n    },\n    function (\n      accessToken: string,\n      refreshToken: string,\n      profile: Profile,\n      cb: (error: Error | null, user?: any) => void\n    ) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/env.ts",
                      "content": "import \"dotenv-flow/config\";\n\ninterface Config {\n  PORT: number;\n  NODE_ENV: string;\n  LOG_LEVEL: string;\n  CORS_ORIGIN: string;\n\n  GITHUB_CLIENT_ID: string;\n  GITHUB_CLIENT_SECRET: string;\n  GITHUB_REDIRECT_URI: string;\n}\n\nconst env: Config = {\n  PORT: Number(process.env.PORT) || 1111,\n  NODE_ENV: process.env.NODE_ENV || \"development\",\n  LOG_LEVEL: process.env.LOG_LEVEL || \"info\",\n  CORS_ORIGIN: process.env.CORS_ORIGIN || \"*\",\n\n  GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID!,\n  GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET!,\n  GITHUB_REDIRECT_URI: process.env.GITHUB_REDIRECT_URI!\n};\n\nexport default env;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/oauth/github-oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\nimport { githubOAuth } from \"./github-oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/github\",\n  passport.authenticate(\"github\", { scope: [\"user:email\"] })\n);\n\nrouter.get(\n  \"/github/callback\",\n  passport.authenticate(\"github\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false\n  }),\n  githubOAuth\n);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/oauth/github-oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile } from \"passport-github2\";\n\nimport { AsyncHandler } from \"../../shared/utils/async-handler\";\nimport { ApiError } from \"../../shared/errors/api-error\";\nimport { ApiResponse } from \"../../shared/utils/api-response\";\n\n//? login with github\nexport const githubOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as Profile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    // console.log(data);\n\n    const user = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.photos && data.photos[0].value\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user\n    });\n  }\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    }
                  ]
                }
              }
            },
            "facebook": {
              "label": "Facebook",
              "dependencies": {
                "runtime": [
                  "dotenv-flow",
                  "cross-env",
                  "pino",
                  "pino-pretty",
                  "passport",
                  "passport-facebook",
                  "zod"
                ],
                "dev": [
                  "@types/passport-facebook",
                  "@types/passport"
                ]
              },
              "env": [
                "LOG_LEVEL",
                "FACEBOOK_APP_ID",
                "FACEBOOK_APP_SECRET",
                "FACEBOOK_REDIRECT_URI"
              ],
              "architectures": {
                "mvc": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.facebook-oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./middlewares/not-found-handler\";\nimport { errorHandler } from \"./middlewares/error-handler\";\n\nimport AuthRoutes from \"./routes/facebook-oauth.routes\";\n\nimport \"./configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api/auth\", AuthRoutes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../utils/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../utils/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/facebook-oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\n\nimport { facebookOAuth } from \"../controllers/facebook-oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/facebook\",\n  passport.authenticate(\"facebook\", { scope: [\"email\", \"user_location\"] })\n);\n\nrouter.get(\n  \"/facebook/callback\",\n  passport.authenticate(\"facebook\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false,\n    failureMessage: true\n  }),\n  facebookOAuth\n);\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/controllers/facebook-oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile as FacebookProfile } from \"passport-facebook\";\n\nimport { ApiResponse } from \"../utils/api-response\";\nimport { AsyncHandler } from \"../utils/async-handler\";\nimport { ApiError } from \"../utils/api-error\";\n\n//? LOGIN WITH FACEBOOK\nexport const facebookOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as FacebookProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    ApiResponse.ok(res, \"Signin Successfull\", {\n      user: userInfo\n    });\n  }\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport {\n  Strategy as FacebookStrategy,\n  Profile as FacebookProfile\n} from \"passport-facebook\";\nimport env from \"./env\";\n\n//? FACEBOOK STRATEGY\npassport.use(\n  new FacebookStrategy(\n    {\n      clientID: env.FACEBOOK_APP_ID,\n      clientSecret: env.FACEBOOK_APP_SECRET,\n      callbackURL: env.FACEBOOK_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: FacebookProfile, cb) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/env.ts",
                      "content": "/* eslint-disable no-console */\nimport \"dotenv-flow/config\";\nimport { z } from \"zod\";\n\nexport const envSchema = z.object({\n  NODE_ENV: z\n    .enum([\"development\", \"test\", \"production\"])\n    .default(\"development\"),\n  PORT: z.string().regex(/^\\d+$/, \"PORT must be a number\").transform(Number),\n  CORS_ORIGIN: z.string(),\n  LOG_LEVEL: z\n    .enum([\"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\"])\n    .default(\"info\"),\n\n\n  FACEBOOK_APP_ID: z.string(),\n  FACEBOOK_APP_SECRET: z.string(),\n  FACEBOOK_REDIRECT_URI: z.url(),\n});\n\nexport type Env = z.infer<typeof envSchema>;\n\nconst result = envSchema.safeParse(process.env);\n\nif (!result.success) {\n  console.error(\"❌ Invalid environment configuration\");\n  console.error(z.treeifyError(result.error));\n  process.exit(1);\n}\n\nexport const env: Readonly<Env> = Object.freeze(result.data);\n\nexport default env;\n\n"
                    }
                  ]
                },
                "feature": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.facebook-oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./shared/middlewares/not-found-handler\";\nimport { errorHandler } from \"./shared/middlewares/error-handler\";\n\nimport Routes from \"./routes/index\";\n\nimport \"./shared/configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api\", Routes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/index.ts",
                      "content": "import { Router } from \"express\";\nimport OAuthRoutes from \"../modules/facebook-oauth/facebook-oauth.routes\";\n\nconst router = Router();\n\nrouter.use(\"/auth\", OAuthRoutes);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../errors/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../errors/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/errors/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport {\n  Strategy as FacebookStrategy,\n  Profile as FacebookProfile\n} from \"passport-facebook\";\nimport env from \"./env\";\n\n//? FACEBOOK STRATEGY\npassport.use(\n  new FacebookStrategy(\n    {\n      clientID: env.FACEBOOK_APP_ID,\n      clientSecret: env.FACEBOOK_APP_SECRET,\n      callbackURL: env.FACEBOOK_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: FacebookProfile, cb) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/env.ts",
                      "content": "/* eslint-disable no-console */\nimport \"dotenv-flow/config\";\nimport { z } from \"zod\";\n\nexport const envSchema = z.object({\n  NODE_ENV: z\n    .enum([\"development\", \"test\", \"production\"])\n    .default(\"development\"),\n  PORT: z.string().regex(/^\\d+$/, \"PORT must be a number\").transform(Number),\n  CORS_ORIGIN: z.string(),\n  LOG_LEVEL: z\n    .enum([\"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\"])\n    .default(\"info\"),\n\n  FACEBOOK_APP_ID: z.string(),\n  FACEBOOK_APP_SECRET: z.string(),\n  FACEBOOK_REDIRECT_URI: z.url()\n});\n\nexport type Env = z.infer<typeof envSchema>;\n\nconst result = envSchema.safeParse(process.env);\n\nif (!result.success) {\n  console.error(\"❌ Invalid environment configuration\");\n  console.error(z.treeifyError(result.error));\n  process.exit(1);\n}\n\nexport const env: Readonly<Env> = Object.freeze(result.data);\n\nexport default env;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/facebook-oauth/facebook-oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\n\nimport { facebookOAuth } from \"./facebook-oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/facebook\",\n  passport.authenticate(\"facebook\", { scope: [\"email\", \"user_location\"] })\n);\n\nrouter.get(\n  \"/facebook/callback\",\n  passport.authenticate(\"facebook\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false,\n    failureMessage: true\n  }),\n  facebookOAuth\n);\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/facebook-oauth/facebook-oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile as FacebookProfile } from \"passport-facebook\";\n\nimport { AsyncHandler } from \"../../shared/utils/async-handler\";\nimport { ApiError } from \"../../shared/errors/api-error\";\nimport { ApiResponse } from \"../../shared/utils/api-response\";\n\n//? LOGIN WITH FACEBOOK\nexport const facebookOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as FacebookProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    ApiResponse.ok(res, \"Signin Successfull\", {\n      user: userInfo\n    });\n  }\n);\n"
                    }
                  ]
                }
              }
            },
            "google-github": {
              "label": "Google + GitHub",
              "dependencies": {
                "runtime": [
                  "dotenv-flow",
                  "cross-env",
                  "pino",
                  "pino-pretty",
                  "passport",
                  "passport-github2",
                  "passport-google-oauth20",
                  "zod"
                ],
                "dev": [
                  "@types/passport-google-oauth20",
                  "@types/passport-github2",
                  "@types/passport"
                ]
              },
              "env": [
                "LOG_LEVEL",
                "GITHUB_CLIENT_ID",
                "GITHUB_CLIENT_SECRET",
                "GITHUB_REDIRECT_URI",
                "GOOGLE_CLIENT_ID",
                "GOOGLE_CLIENT_SECRET",
                "GOOGLE_REDIRECT_URI"
              ],
              "architectures": {
                "mvc": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.google-oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./middlewares/not-found-handler\";\nimport { errorHandler } from \"./middlewares/error-handler\";\n\nimport AuthRoutes from \"./routes/oauth.routes\";\n\nimport \"./configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api/auth\", AuthRoutes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\nimport { githubOAuth, googleOAuth } from \"../controllers/oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/github\",\n  passport.authenticate(\"github\", { scope: [\"user:email\"] })\n);\n\nrouter.get(\n  \"/github/callback\",\n  passport.authenticate(\"github\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false\n  }),\n  githubOAuth\n);\n\nrouter.get(\n  \"/google\",\n  passport.authenticate(\"google\", {\n    scope: [\"email\", \"profile\", \"openid\"],\n    prompt: \"consent\"\n  })\n);\n\nrouter.get(\n  \"/google/callback\",\n  passport.authenticate(\"google\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed\n    session: false\n  }),\n  googleOAuth\n);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/controllers/oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile as GithubProfile } from \"passport-github2\";\n\nimport { Profile as GoogleProfile } from \"passport-google-oauth20\";\n\nimport { ApiResponse } from \"../utils/api-response\";\nimport { AsyncHandler } from \"../utils/async-handler\";\nimport { ApiError } from \"../utils/api-error\";\n\n//? login with github\nexport const githubOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GithubProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    // console.log(data);\n\n    const user = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.photos && data.photos[0].value\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user\n    });\n  }\n);\n\n//? login with google\nexport const googleOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GoogleProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: data?.emails && data?.emails[0]?.verified,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user: userInfo\n    });\n  }\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport {\n  Strategy as GitHubStrategy,\n  Profile as GithubProfile\n} from \"passport-github2\";\n\nimport {\n  Strategy as GoogleStrategy,\n  Profile as GoogleProfile\n} from \"passport-google-oauth20\";\n\nimport env from \"./env\";\n\npassport.use(\n  new GitHubStrategy(\n    {\n      clientID: env.GITHUB_CLIENT_ID,\n      clientSecret: env.GITHUB_CLIENT_SECRET,\n      callbackURL: env.GITHUB_REDIRECT_URI\n    },\n    function (\n      accessToken: string,\n      refreshToken: string,\n      profile: GithubProfile,\n      cb: (error: Error | null, user?: any) => void\n    ) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n\npassport.use(\n  new GoogleStrategy(\n    {\n      clientID: env.GOOGLE_CLIENT_ID,\n      clientSecret: env.GOOGLE_CLIENT_SECRET,\n      callbackURL: env.GOOGLE_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: GoogleProfile, cb) {\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/env.ts",
                      "content": "import \"dotenv-flow/config\";\n\ninterface Config {\n  PORT: number;\n  NODE_ENV: string;\n  LOG_LEVEL: string;\n  CORS_ORIGIN: string;\n\n  GOOGLE_CLIENT_ID: string;\n  GOOGLE_CLIENT_SECRET: string;\n  GOOGLE_REDIRECT_URI: string;\n\n  GITHUB_REDIRECT_URI: string;\n  GITHUB_CLIENT_SECRET: string;\n  GITHUB_CLIENT_ID: string;\n}\n\nconst env: Config = {\n  PORT: Number(process.env.PORT) || 1111,\n  NODE_ENV: process.env.NODE_ENV || \"dev\",\n  LOG_LEVEL: process.env.LOG_LEVEL || \"info\",\n  CORS_ORIGIN: process.env.CORS_ORIGIN || \"*\",\n\n  GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID!,\n  GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET!,\n  GOOGLE_REDIRECT_URI: process.env.GOOGLE_REDIRECT_URI!,\n\n  GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID!,\n  GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET!,\n  GITHUB_REDIRECT_URI: process.env.GITHUB_REDIRECT_URI!\n};\n\nexport default env;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../utils/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../utils/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    }
                  ]
                },
                "feature": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./shared/middlewares/not-found-handler\";\nimport { errorHandler } from \"./shared/middlewares/error-handler\";\n\nimport Routes from \"./routes/index\";\n\nimport \"./shared/configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api\", Routes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/index.ts",
                      "content": "import { Router } from \"express\";\nimport OAuthRoutes from \"../modules/oauth/oauth.routes\";\n\nconst router = Router();\n\nrouter.use(\"/auth\", OAuthRoutes);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../errors/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../errors/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/errors/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport {\n  Strategy as GitHubStrategy,\n  Profile as GithubProfile\n} from \"passport-github2\";\n\nimport {\n  Strategy as GoogleStrategy,\n  Profile as GoogleProfile\n} from \"passport-google-oauth20\";\n\nimport env from \"./env\";\n\npassport.use(\n  new GitHubStrategy(\n    {\n      clientID: env.GITHUB_CLIENT_ID,\n      clientSecret: env.GITHUB_CLIENT_SECRET,\n      callbackURL: env.GITHUB_REDIRECT_URI\n    },\n    function (\n      accessToken: string,\n      refreshToken: string,\n      profile: GithubProfile,\n      cb: (error: Error | null, user?: any) => void\n    ) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n\npassport.use(\n  new GoogleStrategy(\n    {\n      clientID: env.GOOGLE_CLIENT_ID,\n      clientSecret: env.GOOGLE_CLIENT_SECRET,\n      callbackURL: env.GOOGLE_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: GoogleProfile, cb) {\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/env.ts",
                      "content": "import \"dotenv-flow/config\";\n\ninterface Config {\n  PORT: number;\n  NODE_ENV: string;\n  LOG_LEVEL: string;\n  CORS_ORIGIN: string;\n\n  GOOGLE_CLIENT_ID: string;\n  GOOGLE_CLIENT_SECRET: string;\n  GOOGLE_REDIRECT_URI: string;\n\n  GITHUB_REDIRECT_URI: string;\n  GITHUB_CLIENT_SECRET: string;\n  GITHUB_CLIENT_ID: string;\n}\n\nconst env: Config = {\n  PORT: Number(process.env.PORT) || 1111,\n  NODE_ENV: process.env.NODE_ENV || \"dev\",\n  LOG_LEVEL: process.env.LOG_LEVEL || \"info\",\n  CORS_ORIGIN: process.env.CORS_ORIGIN || \"*\",\n\n  GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID!,\n  GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET!,\n  GOOGLE_REDIRECT_URI: process.env.GOOGLE_REDIRECT_URI!,\n\n  GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID!,\n  GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET!,\n  GITHUB_REDIRECT_URI: process.env.GITHUB_REDIRECT_URI!\n};\n\nexport default env;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/oauth/oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\nimport { githubOAuth, googleOAuth } from \"./oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/github\",\n  passport.authenticate(\"github\", { scope: [\"user:email\"] })\n);\n\nrouter.get(\n  \"/github/callback\",\n  passport.authenticate(\"github\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false\n  }),\n  githubOAuth\n);\n\nrouter.get(\n  \"/google\",\n  passport.authenticate(\"google\", {\n    scope: [\"email\", \"profile\", \"openid\"],\n    prompt: \"consent\"\n  })\n);\n\nrouter.get(\n  \"/google/callback\",\n  passport.authenticate(\"google\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed\n    session: false\n  }),\n  googleOAuth\n);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/oauth/oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile as GithubProfile } from \"passport-github2\";\n\nimport { Profile as GoogleProfile } from \"passport-google-oauth20\";\n\nimport { AsyncHandler } from \"../../shared/utils/async-handler\";\nimport { ApiError } from \"../../shared/errors/api-error\";\nimport { ApiResponse } from \"../../shared/utils/api-response\";\n\n//? login with github\nexport const githubOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GithubProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    // console.log(data);\n\n    const user = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.photos && data.photos[0].value\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user\n    });\n  }\n);\n\n//? login with google\nexport const googleOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GoogleProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: data?.emails && data?.emails[0]?.verified,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user: userInfo\n    });\n  }\n);\n"
                    }
                  ]
                }
              }
            },
            "github-facebook": {
              "label": "GitHub + Facebook",
              "dependencies": {
                "runtime": [
                  "dotenv-flow",
                  "cross-env",
                  "pino",
                  "pino-pretty",
                  "passport",
                  "passport-github2",
                  "passport-facebook",
                  "zod"
                ],
                "dev": [
                  "@types/passport-facebook",
                  "@types/passport-github2",
                  "@types/passport"
                ]
              },
              "env": [
                "LOG_LEVEL",
                "GITHUB_CLIENT_ID",
                "GITHUB_CLIENT_SECRET",
                "GITHUB_REDIRECT_URI",
                "FACEBOOK_APP_ID",
                "FACEBOOK_APP_SECRET",
                "FACEBOOK_REDIRECT_URI"
              ],
              "architectures": {
                "mvc": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./middlewares/not-found-handler\";\nimport { errorHandler } from \"./middlewares/error-handler\";\n\nimport AuthRoutes from \"./routes/oauth.routes\";\n\nimport \"./configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api/auth\", AuthRoutes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\nimport { githubOAuth, facebookOAuth } from \"../controllers/oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/github\",\n  passport.authenticate(\"github\", { scope: [\"user:email\"] })\n);\n\nrouter.get(\n  \"/github/callback\",\n  passport.authenticate(\"github\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false\n  }),\n  githubOAuth\n);\n\nrouter.get(\n  \"/facebook\",\n  passport.authenticate(\"facebook\", { scope: [\"email\", \"user_location\"] })\n);\n\nrouter.get(\n  \"/facebook/callback\",\n  passport.authenticate(\"facebook\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false,\n    failureMessage: true\n  }),\n  facebookOAuth\n);\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/controllers/oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile as GithubProfile } from \"passport-github2\";\n\nimport { Profile as FacebookProfile } from \"passport-facebook\";\n\nimport { ApiResponse } from \"../utils/api-response\";\nimport { AsyncHandler } from \"../utils/async-handler\";\nimport { ApiError } from \"../utils/api-error\";\n\n//? login with github\nexport const githubOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GithubProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    // console.log(data);\n\n    const user = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.photos && data.photos[0].value\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user\n    });\n  }\n);\n\n//? login with facebook\nexport const facebookOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as FacebookProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    ApiResponse.ok(res, \"Signin Successfull\", {\n      user: userInfo\n    });\n  }\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../utils/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../utils/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport {\n  Strategy as GitHubStrategy,\n  Profile as GithubProfile\n} from \"passport-github2\";\n\nimport {\n  Strategy as FacebookStrategy,\n  Profile as FacebookProfile\n} from \"passport-facebook\";\n\nimport env from \"./env\";\n\npassport.use(\n  new GitHubStrategy(\n    {\n      clientID: env.GITHUB_CLIENT_ID,\n      clientSecret: env.GITHUB_CLIENT_SECRET,\n      callbackURL: env.GITHUB_REDIRECT_URI\n    },\n    function (\n      accessToken: string,\n      refreshToken: string,\n      profile: GithubProfile,\n      cb: (error: Error | null, user?: any) => void\n    ) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n\npassport.use(\n  new FacebookStrategy(\n    {\n      clientID: env.FACEBOOK_APP_ID,\n      clientSecret: env.FACEBOOK_APP_SECRET,\n      callbackURL: env.FACEBOOK_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: FacebookProfile, cb) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/env.ts",
                      "content": "/* eslint-disable no-console */\nimport \"dotenv-flow/config\";\nimport { z } from \"zod\";\n\nexport const envSchema = z.object({\n  NODE_ENV: z\n    .enum([\"development\", \"test\", \"production\"])\n    .default(\"development\"),\n  PORT: z.string().regex(/^\\d+$/, \"PORT must be a number\").transform(Number),\n  CORS_ORIGIN: z.string(),\n  LOG_LEVEL: z\n    .enum([\"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\"])\n    .default(\"info\"),\n\n  FACEBOOK_APP_ID: z.string(),\n  FACEBOOK_APP_SECRET: z.string(),\n  FACEBOOK_REDIRECT_URI: z.url(),\n\n  GITHUB_CLIENT_ID: z.string(),\n  GITHUB_CLIENT_SECRET: z.string(),\n  GITHUB_REDIRECT_URI: z.url()\n});\n\nexport type Env = z.infer<typeof envSchema>;\n\nconst result = envSchema.safeParse(process.env);\n\nif (!result.success) {\n  console.error(\"❌ Invalid environment configuration\");\n  console.error(z.treeifyError(result.error));\n  process.exit(1);\n}\n\nexport const env: Readonly<Env> = Object.freeze(result.data);\n\nexport default env;\n"
                    }
                  ]
                },
                "feature": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./shared/middlewares/not-found-handler\";\nimport { errorHandler } from \"./shared/middlewares/error-handler\";\n\nimport AuthRoutes from \"./routes/index.ts\";\n\nimport \"./configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api/auth\", AuthRoutes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/index.ts",
                      "content": "import { Router } from \"express\";\r\nimport OAuthRoutes from \"../modules/oauth/oauth.routes\";\r\n\r\nconst router = Router();\r\n\r\nrouter.use(\"/auth\", OAuthRoutes);\r\n\r\nexport default router;\r\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../utils/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../utils/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport {\n  Strategy as GitHubStrategy,\n  Profile as GithubProfile\n} from \"passport-github2\";\n\nimport {\n  Strategy as FacebookStrategy,\n  Profile as FacebookProfile\n} from \"passport-facebook\";\n\nimport env from \"./env\";\n\npassport.use(\n  new GitHubStrategy(\n    {\n      clientID: env.GITHUB_CLIENT_ID,\n      clientSecret: env.GITHUB_CLIENT_SECRET,\n      callbackURL: env.GITHUB_REDIRECT_URI\n    },\n    function (\n      accessToken: string,\n      refreshToken: string,\n      profile: GithubProfile,\n      cb: (error: Error | null, user?: any) => void\n    ) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n\npassport.use(\n  new FacebookStrategy(\n    {\n      clientID: env.FACEBOOK_APP_ID,\n      clientSecret: env.FACEBOOK_APP_SECRET,\n      callbackURL: env.FACEBOOK_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: FacebookProfile, cb) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/env.ts",
                      "content": "/* eslint-disable no-console */\nimport \"dotenv-flow/config\";\nimport { z } from \"zod\";\n\nexport const envSchema = z.object({\n  NODE_ENV: z\n    .enum([\"development\", \"test\", \"production\"])\n    .default(\"development\"),\n  PORT: z.string().regex(/^\\d+$/, \"PORT must be a number\").transform(Number),\n  CORS_ORIGIN: z.string(),\n  LOG_LEVEL: z\n    .enum([\"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\"])\n    .default(\"info\"),\n\n  FACEBOOK_APP_ID: z.string(),\n  FACEBOOK_APP_SECRET: z.string(),\n  FACEBOOK_REDIRECT_URI: z.url(),\n\n  GITHUB_CLIENT_ID: z.string(),\n  GITHUB_CLIENT_SECRET: z.string(),\n  GITHUB_REDIRECT_URI: z.url()\n});\n\nexport type Env = z.infer<typeof envSchema>;\n\nconst result = envSchema.safeParse(process.env);\n\nif (!result.success) {\n  console.error(\"❌ Invalid environment configuration\");\n  console.error(z.treeifyError(result.error));\n  process.exit(1);\n}\n\nexport const env: Readonly<Env> = Object.freeze(result.data);\n\nexport default env;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/oauth/oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\nimport { githubOAuth, facebookOAuth } from \"./oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/github\",\n  passport.authenticate(\"github\", { scope: [\"user:email\"] })\n);\n\nrouter.get(\n  \"/github/callback\",\n  passport.authenticate(\"github\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false\n  }),\n  githubOAuth\n);\n\nrouter.get(\n  \"/facebook\",\n  passport.authenticate(\"facebook\", { scope: [\"email\", \"user_location\"] })\n);\n\nrouter.get(\n  \"/facebook/callback\",\n  passport.authenticate(\"facebook\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false,\n    failureMessage: true\n  }),\n  facebookOAuth\n);\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/oauth/oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile as GithubProfile } from \"passport-github2\";\n\nimport { Profile as FacebookProfile } from \"passport-facebook\";\n\nimport { ApiResponse } from \"../../shared/utils/api-response\";\nimport { AsyncHandler } from \"../../shared/utils/async-handler\";\nimport { ApiError } from \"../../shared/utils/api-error\";\n\n//? login with github\nexport const githubOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GithubProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    // console.log(data);\n\n    const user = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.photos && data.photos[0].value\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user\n    });\n  }\n);\n\n//? login with facebook\nexport const facebookOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as FacebookProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    ApiResponse.ok(res, \"Signin Successfull\", {\n      user: userInfo\n    });\n  }\n);\n"
                    }
                  ]
                }
              }
            },
            "google-facebook": {
              "label": "Google + Facebook",
              "dependencies": {
                "runtime": [
                  "dotenv-flow",
                  "cross-env",
                  "pino",
                  "pino-pretty",
                  "passport",
                  "passport-facebook",
                  "passport-google-oauth20",
                  "zod"
                ],
                "dev": [
                  "@types/passport-google-oauth20",
                  "@types/passport-facebook",
                  "@types/passport"
                ]
              },
              "env": [
                "LOG_LEVEL",
                "FACEBOOK_APP_ID",
                "FACEBOOK_APP_SECRET",
                "FACEBOOK_REDIRECT_URI",
                "GOOGLE_CLIENT_ID",
                "GOOGLE_CLIENT_SECRET",
                "GOOGLE_REDIRECT_URI"
              ],
              "architectures": {
                "mvc": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./middlewares/not-found-handler\";\nimport { errorHandler } from \"./middlewares/error-handler\";\n\nimport AuthRoutes from \"./routes/oauth.routes\";\n\nimport \"./configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api/auth\", AuthRoutes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../utils/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../utils/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\nimport { facebookOAuth, googleOAuth } from \"../controllers/oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/facebook\",\n  passport.authenticate(\"facebook\", { scope: [\"email\", \"user_location\"] })\n);\n\nrouter.get(\n  \"/facebook/callback\",\n  passport.authenticate(\"facebook\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false,\n    failureMessage: true\n  }),\n  facebookOAuth\n);\n\nrouter.get(\n  \"/google\",\n  passport.authenticate(\"google\", {\n    scope: [\"email\", \"profile\", \"openid\"],\n    prompt: \"consent\"\n  })\n);\n\nrouter.get(\n  \"/google/callback\",\n  passport.authenticate(\"google\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed\n    session: false\n  }),\n  googleOAuth\n);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/controllers/oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile as FacebookProfile } from \"passport-facebook\";\nimport { Profile as GoogleProfile } from \"passport-google-oauth20\";\n\nimport { ApiResponse } from \"../utils/api-response\";\nimport { AsyncHandler } from \"../utils/async-handler\";\nimport { ApiError } from \"../utils/api-error\";\n\n//? login with facebook\nexport const facebookOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as FacebookProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    ApiResponse.ok(res, \"Signin Successfull\", {\n      user: userInfo\n    });\n  }\n);\n\n//? login with google\nexport const googleOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GoogleProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: data?.emails && data?.emails[0]?.verified,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user: userInfo\n    });\n  }\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/passport.ts",
                      "content": "import passport from \"passport\";\n\nimport {\n  Strategy as GoogleStrategy,\n  Profile as GoogleProfile\n} from \"passport-google-oauth20\";\n\nimport {\n  Strategy as FacebookStrategy,\n  Profile as FacebookProfile\n} from \"passport-facebook\";\nimport env from \"./env\";\n\n//? FACEBOOK STRATEGY\npassport.use(\n  new FacebookStrategy(\n    {\n      clientID: env.FACEBOOK_APP_ID,\n      clientSecret: env.FACEBOOK_APP_SECRET,\n      callbackURL: env.FACEBOOK_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: FacebookProfile, cb) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n\n//? GOOGLE STRATEGY\npassport.use(\n  new GoogleStrategy(\n    {\n      clientID: env.GOOGLE_CLIENT_ID,\n      clientSecret: env.GOOGLE_CLIENT_SECRET,\n      callbackURL: env.GOOGLE_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: GoogleProfile, cb) {\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/env.ts",
                      "content": "/* eslint-disable no-console */\nimport \"dotenv-flow/config\";\nimport { z } from \"zod\";\n\nexport const envSchema = z.object({\n  NODE_ENV: z\n    .enum([\"development\", \"test\", \"production\"])\n    .default(\"development\"),\n  PORT: z.string().regex(/^\\d+$/, \"PORT must be a number\").transform(Number),\n  CORS_ORIGIN: z.string(),\n  LOG_LEVEL: z\n    .enum([\"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\"])\n    .default(\"info\"),\n\n  FACEBOOK_APP_ID: z.string(),\n  FACEBOOK_APP_SECRET: z.string(),\n  FACEBOOK_REDIRECT_URI: z.url(),\n\n  GOOGLE_CLIENT_ID: z.string(),\n  GOOGLE_CLIENT_SECRET: z.string(),\n  GOOGLE_REDIRECT_URI: z.url()\n});\n\nexport type Env = z.infer<typeof envSchema>;\n\nconst result = envSchema.safeParse(process.env);\n\nif (!result.success) {\n  console.error(\"❌ Invalid environment configuration\");\n  console.error(z.treeifyError(result.error));\n  process.exit(1);\n}\n\nexport const env: Readonly<Env> = Object.freeze(result.data);\n\nexport default env;\n"
                    }
                  ]
                },
                "feature": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./shared/middlewares/not-found-handler\";\nimport { errorHandler } from \"./shared/middlewares/error-handler\";\n\nimport AuthRoutes from \"./routes/index\";\n\nimport \"./shared/configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api/auth\", AuthRoutes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/index.ts",
                      "content": "import { Router } from \"express\";\r\nimport OAuthRoutes from \"../modules/oauth/oauth.routes\";\r\n\r\nconst router = Router();\r\n\r\nrouter.use(\"/auth\", OAuthRoutes);\r\n\r\nexport default router;\r\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../utils/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../utils/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/passport.ts",
                      "content": "import passport from \"passport\";\n\nimport {\n  Strategy as GoogleStrategy,\n  Profile as GoogleProfile\n} from \"passport-google-oauth20\";\n\nimport {\n  Strategy as FacebookStrategy,\n  Profile as FacebookProfile\n} from \"passport-facebook\";\nimport env from \"./env\";\n\n//? FACEBOOK STRATEGY\npassport.use(\n  new FacebookStrategy(\n    {\n      clientID: env.FACEBOOK_APP_ID,\n      clientSecret: env.FACEBOOK_APP_SECRET,\n      callbackURL: env.FACEBOOK_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: FacebookProfile, cb) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n\n//? GOOGLE STRATEGY\npassport.use(\n  new GoogleStrategy(\n    {\n      clientID: env.GOOGLE_CLIENT_ID,\n      clientSecret: env.GOOGLE_CLIENT_SECRET,\n      callbackURL: env.GOOGLE_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: GoogleProfile, cb) {\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/env.ts",
                      "content": "/* eslint-disable no-console */\nimport \"dotenv-flow/config\";\nimport { z } from \"zod\";\n\nexport const envSchema = z.object({\n  NODE_ENV: z\n    .enum([\"development\", \"test\", \"production\"])\n    .default(\"development\"),\n  PORT: z.string().regex(/^\\d+$/, \"PORT must be a number\").transform(Number),\n  CORS_ORIGIN: z.string(),\n  LOG_LEVEL: z\n    .enum([\"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\"])\n    .default(\"info\"),\n\n  FACEBOOK_APP_ID: z.string(),\n  FACEBOOK_APP_SECRET: z.string(),\n  FACEBOOK_REDIRECT_URI: z.url(),\n\n  GOOGLE_CLIENT_ID: z.string(),\n  GOOGLE_CLIENT_SECRET: z.string(),\n  GOOGLE_REDIRECT_URI: z.url()\n});\n\nexport type Env = z.infer<typeof envSchema>;\n\nconst result = envSchema.safeParse(process.env);\n\nif (!result.success) {\n  console.error(\"❌ Invalid environment configuration\");\n  console.error(z.treeifyError(result.error));\n  process.exit(1);\n}\n\nexport const env: Readonly<Env> = Object.freeze(result.data);\n\nexport default env;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/oauth/oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\nimport { facebookOAuth, googleOAuth } from \"./oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/facebook\",\n  passport.authenticate(\"facebook\", { scope: [\"email\", \"user_location\"] })\n);\n\nrouter.get(\n  \"/facebook/callback\",\n  passport.authenticate(\"facebook\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false,\n    failureMessage: true\n  }),\n  facebookOAuth\n);\n\nrouter.get(\n  \"/google\",\n  passport.authenticate(\"google\", {\n    scope: [\"email\", \"profile\", \"openid\"],\n    prompt: \"consent\"\n  })\n);\n\nrouter.get(\n  \"/google/callback\",\n  passport.authenticate(\"google\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed\n    session: false\n  }),\n  googleOAuth\n);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/oauth/oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile as FacebookProfile } from \"passport-facebook\";\nimport { Profile as GoogleProfile } from \"passport-google-oauth20\";\n\nimport { ApiResponse } from \"../../shared/utils/api-response\";\nimport { AsyncHandler } from \"../../shared/utils/async-handler\";\nimport { ApiError } from \"../../shared/utils/api-error\";\n\n//? login with facebook\nexport const facebookOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as FacebookProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    ApiResponse.ok(res, \"Signin Successfull\", {\n      user: userInfo\n    });\n  }\n);\n\n//? login with google\nexport const googleOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GoogleProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: data?.emails && data?.emails[0]?.verified,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user: userInfo\n    });\n  }\n);\n"
                    }
                  ]
                }
              }
            },
            "google-facebook-github": {
              "label": "Google + Facebook + GitHub",
              "dependencies": {
                "runtime": [
                  "dotenv-flow",
                  "cross-env",
                  "pino",
                  "pino-pretty",
                  "passport",
                  "passport-facebook",
                  "passport-github2",
                  "passport-google-oauth20",
                  "zod"
                ],
                "dev": [
                  "@types/passport-google-oauth20",
                  "@types/passport-github2",
                  "@types/passport-facebook",
                  "@types/passport"
                ]
              },
              "env": [
                "LOG_LEVEL",
                "FACEBOOK_APP_ID",
                "FACEBOOK_APP_SECRET",
                "FACEBOOK_REDIRECT_URI",
                "GOOGLE_CLIENT_ID",
                "GOOGLE_CLIENT_SECRET",
                "GOOGLE_REDIRECT_URI",
                "GITHUB_CLIENT_ID",
                "GITHUB_CLIENT_SECRET",
                "GITHUB_REDIRECT_URI"
              ],
              "architectures": {
                "mvc": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./middlewares/not-found-handler\";\nimport { errorHandler } from \"./middlewares/error-handler\";\n\nimport AuthRoutes from \"./routes/oauth.routes\";\n\nimport \"./configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api/auth\", AuthRoutes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/utils/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\nimport {\n  facebookOAuth,\n  githubOAuth,\n  googleOAuth\n} from \"../controllers/oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/github\",\n  passport.authenticate(\"github\", { scope: [\"user:email\"] })\n);\n\nrouter.get(\n  \"/github/callback\",\n  passport.authenticate(\"github\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false\n  }),\n  githubOAuth\n);\n\nrouter.get(\n  \"/google\",\n  passport.authenticate(\"google\", {\n    scope: [\"email\", \"profile\", \"openid\"],\n    prompt: \"consent\"\n  })\n);\n\nrouter.get(\n  \"/google/callback\",\n  passport.authenticate(\"google\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed\n    session: false\n  }),\n  googleOAuth\n);\n\nrouter.get(\n  \"/facebook\",\n  passport.authenticate(\"facebook\", { scope: [\"email\", \"user_location\"] })\n);\n\nrouter.get(\n  \"/facebook/callback\",\n  passport.authenticate(\"facebook\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false,\n    failureMessage: true\n  }),\n  facebookOAuth\n);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../utils/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../utils/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/controllers/oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile as GithubProfile } from \"passport-github2\";\nimport { Profile as FacebookProfile } from \"passport-facebook\";\nimport { Profile as GoogleProfile } from \"passport-google-oauth20\";\n\nimport { ApiResponse } from \"../utils/api-response\";\nimport { AsyncHandler } from \"../utils/async-handler\";\nimport { ApiError } from \"../utils/api-error\";\n\n//? login with github\nexport const githubOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GithubProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    // console.log(data);\n\n    const user = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.photos && data.photos[0].value\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user\n    });\n  }\n);\n\n//? login with google\nexport const googleOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GoogleProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: data?.emails && data?.emails[0]?.verified,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user: userInfo\n    });\n  }\n);\n\n//? login with facebook\nexport const facebookOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as FacebookProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    ApiResponse.ok(res, \"Signin Successfull\", {\n      user: userInfo\n    });\n  }\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport {\n  Strategy as GitHubStrategy,\n  Profile as GithubProfile\n} from \"passport-github2\";\n\nimport {\n  Strategy as GoogleStrategy,\n  Profile as GoogleProfile\n} from \"passport-google-oauth20\";\nimport {\n  Strategy as FacebookStrategy,\n  Profile as FacebookProfile\n} from \"passport-facebook\";\n\nimport env from \"./env\";\n\npassport.use(\n  new GitHubStrategy(\n    {\n      clientID: env.GITHUB_CLIENT_ID,\n      clientSecret: env.GITHUB_CLIENT_SECRET,\n      callbackURL: env.GITHUB_REDIRECT_URI\n    },\n    function (\n      accessToken: string,\n      refreshToken: string,\n      profile: GithubProfile,\n      cb: (error: Error | null, user?: any) => void\n    ) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n\npassport.use(\n  new GoogleStrategy(\n    {\n      clientID: env.GOOGLE_CLIENT_ID,\n      clientSecret: env.GOOGLE_CLIENT_SECRET,\n      callbackURL: env.GOOGLE_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: GoogleProfile, cb) {\n      return cb(null, profile);\n    }\n  )\n);\n\npassport.use(\n  new FacebookStrategy(\n    {\n      clientID: env.FACEBOOK_APP_ID,\n      clientSecret: env.FACEBOOK_APP_SECRET,\n      callbackURL: env.FACEBOOK_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: FacebookProfile, cb) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/configs/env.ts",
                      "content": "/* eslint-disable no-console */\nimport \"dotenv-flow/config\";\nimport { z } from \"zod\";\n\nexport const envSchema = z.object({\n  NODE_ENV: z\n    .enum([\"development\", \"test\", \"production\"])\n    .default(\"development\"),\n  PORT: z.string().regex(/^\\d+$/, \"PORT must be a number\").transform(Number),\n  CORS_ORIGIN: z.string(),\n  LOG_LEVEL: z\n    .enum([\"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\"])\n    .default(\"info\"),\n\n  FACEBOOK_APP_ID: z.string(),\n  FACEBOOK_APP_SECRET: z.string(),\n  FACEBOOK_REDIRECT_URI: z.url(),\n\n  GITHUB_CLIENT_ID: z.string(),\n  GITHUB_CLIENT_SECRET: z.string(),\n  GITHUB_REDIRECT_URI: z.url(),\n\n  GOOGLE_CLIENT_ID: z.string(),\n  GOOGLE_CLIENT_SECRET: z.string(),\n  GOOGLE_REDIRECT_URI: z.url()\n});\n\nexport type Env = z.infer<typeof envSchema>;\n\nconst result = envSchema.safeParse(process.env);\n\nif (!result.success) {\n  console.error(\"❌ Invalid environment configuration\");\n  console.error(z.treeifyError(result.error));\n  process.exit(1);\n}\n\nexport const env: Readonly<Env> = Object.freeze(result.data);\n\nexport default env;\n"
                    }
                  ]
                },
                "feature": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/app.oauth.ts",
                      "content": "import express, { Express } from \"express\";\n\nimport { notFoundHandler } from \"./shared/middlewares/not-found-handler\";\nimport { errorHandler } from \"./shared/middlewares/error-handler\";\n\nimport AuthRoutes from \"./routes/index\";\n\nimport \"./shared/configs/passport\";\n\nconst app: Express = express();\n\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\n\n// Routes\napp.use(\"/api/auth\", AuthRoutes);\n\n// Not found handler (should be after routes)\napp.use(notFoundHandler);\n\n// Global error handler (should be last)\napp.use(errorHandler);\n\nexport default app;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/routes/index.ts",
                      "content": "import { Router } from \"express\";\r\nimport OAuthRoutes from \"../modules/oauth/oauth.routes\";\r\n\r\nconst router = Router();\r\n\r\nrouter.use(\"/auth\", OAuthRoutes);\r\n\r\nexport default router;\r\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/logger.ts",
                      "content": "import pino from \"pino\";\nimport env from \"../configs/env\";\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  transport:\n    env.NODE_ENV !== \"production\"\n      ? {\n          target: \"pino-pretty\",\n          options: {\n            colorize: true,\n            translateTime: \"yyyy-mm-dd HH:MM:ss\",\n            ignore: \"pid,hostname\"\n          }\n        }\n      : undefined\n});\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/async-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\n\nexport type AsyncRouteHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => Promise<unknown>;\n\nexport function AsyncHandler(fn: AsyncRouteHandler) {\n  return function (req: Request, res: Response, next: NextFunction) {\n    Promise.resolve()\n      .then(() => fn(req, res, next))\n      .catch(next);\n  };\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/api-response.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\nimport type { Response } from \"express\";\n\ntype ApiResponseParams<T> = {\n  success: boolean;\n  message: string;\n  statusCode: StatusCode;\n  data?: T | null;\n  errors?: unknown;\n};\n\nexport class ApiResponse<T = unknown> {\n  public readonly success: boolean;\n  public readonly message: string;\n  public readonly statusCode: StatusCode;\n  public readonly data?: T | null;\n  public readonly errors?: unknown;\n\n  constructor({\n    success,\n    message,\n    statusCode,\n    data,\n    errors\n  }: ApiResponseParams<T>) {\n    this.success = success;\n    this.message = message;\n    this.statusCode = statusCode;\n    this.data = data;\n    this.errors = errors;\n  }\n\n  send(res: Response): Response {\n    return res.status(this.statusCode).json({\n      success: this.success,\n      message: this.message,\n      statusCode: this.statusCode,\n      ...(this.data !== undefined && { data: this.data }),\n      ...(this.errors !== undefined && { errors: this.errors })\n    });\n  }\n\n  static Success<T>(\n    res: Response,\n    message: string,\n    data?: T,\n    statusCode: StatusCode = STATUS_CODES.OK\n  ): Response {\n    return new ApiResponse<T>({\n      success: true,\n      message,\n      data,\n      statusCode\n    }).send(res);\n  }\n\n  static ok<T>(res: Response, message = \"OK\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.OK);\n  }\n\n  static created<T>(res: Response, message = \"Created\", data?: T) {\n    return ApiResponse.Success(res, message, data, STATUS_CODES.CREATED);\n  }\n}\n\n/*\n * Usage:\n * ApiResponse.ok(res, \"OK\", data);\n * ApiResponse.created(res, \"Created\", data);\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/utils/api-error.ts",
                      "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/not-found-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport { ApiError } from \"../utils/api-error\";\n\nexport const notFoundHandler = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  throw ApiError.notFound(`Route ${req.method} ${req.originalUrl} not found`);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/middlewares/error-handler.ts",
                      "content": "import { Request, Response, NextFunction } from \"express\";\nimport env from \"../configs/env\";\n\nimport { ApiError } from \"../utils/api-error\";\nimport { logger } from \"../utils/logger\";\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (res.headersSent) {\n    return next(err);\n  }\n  let statusCode = 500;\n  let message = \"Internal server error\";\n  let errors: unknown;\n\n  if (err instanceof ApiError) {\n    statusCode = err.statusCode;\n    message = err.message;\n    errors = err.errors;\n  }\n\n  logger.error(\n    err,\n    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`\n  );\n\n  const response = {\n    success: false,\n    message,\n    statusCode,\n    ...(errors !== undefined && { errors }),\n    ...(env.NODE_ENV === \"development\" && { stack: err.stack })\n  };\n\n  res.status(statusCode).json(response);\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/constants/status-codes.ts",
                      "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/passport.ts",
                      "content": "import passport from \"passport\";\nimport {\n  Strategy as GitHubStrategy,\n  Profile as GithubProfile\n} from \"passport-github2\";\n\nimport {\n  Strategy as GoogleStrategy,\n  Profile as GoogleProfile\n} from \"passport-google-oauth20\";\nimport {\n  Strategy as FacebookStrategy,\n  Profile as FacebookProfile\n} from \"passport-facebook\";\n\nimport env from \"./env\";\n\npassport.use(\n  new GitHubStrategy(\n    {\n      clientID: env.GITHUB_CLIENT_ID,\n      clientSecret: env.GITHUB_CLIENT_SECRET,\n      callbackURL: env.GITHUB_REDIRECT_URI\n    },\n    function (\n      accessToken: string,\n      refreshToken: string,\n      profile: GithubProfile,\n      cb: (error: Error | null, user?: any) => void\n    ) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n\npassport.use(\n  new GoogleStrategy(\n    {\n      clientID: env.GOOGLE_CLIENT_ID,\n      clientSecret: env.GOOGLE_CLIENT_SECRET,\n      callbackURL: env.GOOGLE_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: GoogleProfile, cb) {\n      return cb(null, profile);\n    }\n  )\n);\n\npassport.use(\n  new FacebookStrategy(\n    {\n      clientID: env.FACEBOOK_APP_ID,\n      clientSecret: env.FACEBOOK_APP_SECRET,\n      callbackURL: env.FACEBOOK_REDIRECT_URI\n    },\n    function (accessToken, refreshToken, profile: FacebookProfile, cb) {\n      // console.log({ profile });\n      return cb(null, profile);\n    }\n  )\n);\n"
                    },
                    {
                      "type": "file",
                      "path": "src/shared/configs/env.ts",
                      "content": "/* eslint-disable no-console */\nimport \"dotenv-flow/config\";\nimport { z } from \"zod\";\n\nexport const envSchema = z.object({\n  NODE_ENV: z\n    .enum([\"development\", \"test\", \"production\"])\n    .default(\"development\"),\n  PORT: z.string().regex(/^\\d+$/, \"PORT must be a number\").transform(Number),\n  CORS_ORIGIN: z.string(),\n  LOG_LEVEL: z\n    .enum([\"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\"])\n    .default(\"info\"),\n\n  FACEBOOK_APP_ID: z.string(),\n  FACEBOOK_APP_SECRET: z.string(),\n  FACEBOOK_REDIRECT_URI: z.url(),\n\n  GITHUB_CLIENT_ID: z.string(),\n  GITHUB_CLIENT_SECRET: z.string(),\n  GITHUB_REDIRECT_URI: z.url(),\n\n  GOOGLE_CLIENT_ID: z.string(),\n  GOOGLE_CLIENT_SECRET: z.string(),\n  GOOGLE_REDIRECT_URI: z.url()\n});\n\nexport type Env = z.infer<typeof envSchema>;\n\nconst result = envSchema.safeParse(process.env);\n\nif (!result.success) {\n  console.error(\"❌ Invalid environment configuration\");\n  console.error(z.treeifyError(result.error));\n  process.exit(1);\n}\n\nexport const env: Readonly<Env> = Object.freeze(result.data);\n\nexport default env;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/oauth/oauth.routes.ts",
                      "content": "import { Router } from \"express\";\nimport passport from \"passport\";\nimport {\n  facebookOAuth,\n  githubOAuth,\n  googleOAuth\n} from \"./oauth.controller\";\n\nconst router = Router();\n\nrouter.get(\n  \"/github\",\n  passport.authenticate(\"github\", { scope: [\"user:email\"] })\n);\n\nrouter.get(\n  \"/github/callback\",\n  passport.authenticate(\"github\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false\n  }),\n  githubOAuth\n);\n\nrouter.get(\n  \"/google\",\n  passport.authenticate(\"google\", {\n    scope: [\"email\", \"profile\", \"openid\"],\n    prompt: \"consent\"\n  })\n);\n\nrouter.get(\n  \"/google/callback\",\n  passport.authenticate(\"google\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed\n    session: false\n  }),\n  googleOAuth\n);\n\nrouter.get(\n  \"/facebook\",\n  passport.authenticate(\"facebook\", { scope: [\"email\", \"user_location\"] })\n);\n\nrouter.get(\n  \"/facebook/callback\",\n  passport.authenticate(\"facebook\", {\n    failureRedirect: \"/login\", //? redirect route if authenticated is failed,\n    session: false,\n    failureMessage: true\n  }),\n  facebookOAuth\n);\n\nexport default router;\n"
                    },
                    {
                      "type": "file",
                      "path": "src/modules/oauth/oauth.controller.ts",
                      "content": "import { NextFunction, Request, Response } from \"express\";\nimport { Profile as GithubProfile } from \"passport-github2\";\nimport { Profile as FacebookProfile } from \"passport-facebook\";\nimport { Profile as GoogleProfile } from \"passport-google-oauth20\";\n\nimport { ApiResponse } from \"../../shared/utils/api-response\";\nimport { AsyncHandler } from \"../../shared/utils/async-handler\";\nimport { ApiError } from \"../../shared/utils/api-error\";\n\n//? login with github\nexport const githubOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GithubProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    // console.log(data);\n\n    const user = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.photos && data.photos[0].value\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user\n    });\n  }\n);\n\n//? login with google\nexport const googleOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as GoogleProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: data?.emails && data?.emails[0]?.verified,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    //? save the data into your databases\n\n    ApiResponse.ok(res, \"Auth Successfull\", {\n      user: userInfo\n    });\n  }\n);\n\n//? login with facebook\nexport const facebookOAuth = AsyncHandler(\n  async (req: Request, res: Response, next: NextFunction) => {\n    const data = req.user as FacebookProfile | undefined;\n\n    if (!data) {\n      return next(ApiError.unauthorized(\"Authenticated failed!\"));\n    }\n\n    const userInfo = {\n      provider: data?.provider,\n      providerId: data.id,\n      name: data.displayName,\n      email: data?.emails && data?.emails[0]?.value,\n      isEmailVerified: true,\n      avatar: data.profileUrl || (data.photos && data.photos[0].value)\n    };\n\n    ApiResponse.ok(res, \"Signin Successfull\", {\n      user: userInfo\n    });\n  }\n);\n"
                    }
                  ]
                }
              }
            }
          }
        },
        "nextjs": {
          "prompt": "Select OAuth provider: ",
          "variants": {
            "google": {
              "label": "Google",
              "dependencies": {
                "runtime": [
                  "next-auth@^4.24.14"
                ],
                "dev": []
              },
              "env": [
                "NEXTAUTH_URL",
                "NEXTAUTH_SECRET",
                "GOOGLE_CLIENT_ID",
                "GOOGLE_CLIENT_SECRET"
              ],
              "message": "The `google-auth` component has been added. Remember to wrap your app with the `SessionProvider` component.\n\n```tsx title=\"app/layout.tsx\"\nimport { SessionProvider } from \"@/components/providers/session-provider\";\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\">\n      <body>\n        <SessionProvider>{children}</SessionProvider>\n      </body>\n    </html>\n  );\n}\n```",
              "architectures": {
                "file-api": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/lib/next-auth.ts",
                      "content": "import { Account, AuthOptions, Profile as NextAuthProfile } from \"next-auth\";\nimport Google from \"next-auth/providers/google\";\n\ninterface CustomProfile extends NextAuthProfile {\n  picture?: string;\n  avatar_url?: string;\n  email_verified?: boolean;\n}\n\nexport const authOptions: AuthOptions = {\n  callbacks: {\n    async signIn({\n      account,\n      profile\n    }: {\n      account: Account | null;\n      profile?: CustomProfile | undefined;\n    }) {\n      if (!account || !profile) return false;\n      if (account?.provider === \"google\") {\n        // console.log({ profile, account });\n\n        const userInfo = {\n          name: profile?.name as string,\n          email: profile?.email as string,\n          avatar: profile.picture || profile.avatar_url,\n          provider: account.provider,\n          providerAccountId: account.providerAccountId,\n          isEmailVerified: profile?.email_verified ?? false\n        };\n\n        console.log({ userInfo });\n\n        return true;\n      }\n      return true;\n    }\n  },\n  providers: [\n    Google({\n      clientId: process.env.GOOGLE_CLIENT_ID as string,\n      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,\n      authorization: {\n        params: {\n          prompt: \"consent\",\n          access_type: \"offline\",\n          response_type: \"code\"\n        }\n      }\n    }),\n    \n  ],\n\n  secret: process.env.NEXTAUTH_SECRET\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/page.tsx",
                      "content": "import { AuthStatus } from \"@/components/auth/auth-status\";\nimport { ProfileCard } from \"@/components/auth/profile-card\";\n\nimport Link from \"next/link\";\n\nexport default function Page() {\n  return (\n    <main className=\"min-h-screen bg-neutral-950 px-6\">\n      <div className=\"relative mx-auto max-w-3xl\">\n        <div className=\"mt-5 flex items-center justify-between rounded-full border border-neutral-500/30 p-4\">\n          <Link\n            href=\"https://github.com/akkaldhami/servercn\"\n            target=\"_blank\"\n            className=\"flex items-center gap-2 rounded-full bg-neutral-50 px-4 py-2 text-sm font-medium text-black\">\n            <svg\n              xmlns=\"http://www.w3.org/2000/svg\"\n              width=\"24\"\n              height=\"24\"\n              viewBox=\"0 0 24 24\">\n              <path\n                fill=\"currentColor\"\n                d=\"M12.026 2a9.975 9.975 0 0 0-3.153 19.439c.5.09.679-.217.679-.481c0-.237-.008-.865-.011-1.7c-2.775.6-3.361-1.338-3.361-1.338a2.635 2.635 0 0 0-1.107-1.459c-.9-.619.069-.605.069-.605c.64.088 1.205.467 1.527 1.028a2.124 2.124 0 0 0 2.9.829c.046-.506.272-.979.635-1.334c-2.214-.251-4.542-1.107-4.542-4.93a3.865 3.865 0 0 1 1.026-2.671a3.588 3.588 0 0 1 .1-2.64s.837-.269 2.742 1.021a9.439 9.439 0 0 1 4.992 0c1.906-1.291 2.742-1.021 2.742-1.021c.37.835.405 1.78.1 2.64a3.84 3.84 0 0 1 1.024 2.675c0 3.833-2.33 4.675-4.552 4.922c.48.49.725 1.162.675 1.846c0 1.334-.012 2.41-.012 2.737c0 .267.178.577.687.479A9.975 9.975 0 0 0 12.026 2Z\"\n              />\n            </svg>\n            Star on GitHub\n          </Link>\n          <div className=\"flex items-center justify-center gap-4\">\n            <AuthStatus />\n          </div>\n        </div>\n\n        <ProfileCard />\n      </div>\n    </main>\n  );\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/layout.tsx",
                      "content": "import type { Metadata } from \"next\";\nimport { Geist, Geist_Mono } from \"next/font/google\";\nimport \"./globals.css\";\nimport { SessionProvider } from \"@/components/providers/session-provider\";\n\nconst geistSans = Geist({\n  variable: \"--font-geist-sans\",\n  subsets: [\"latin\"]\n});\n\nconst geistMono = Geist_Mono({\n  variable: \"--font-geist-mono\",\n  subsets: [\"latin\"]\n});\n\nexport const metadata: Metadata = {\n  title: \"Servercn Next.js Google Oauth Component\",\n  description: \"Next.js Google Oauth Component\"\n};\n\nexport default function RootLayout({\n  children\n}: Readonly<{\n  children: React.ReactNode;\n}>) {\n  return (\n    <html\n      lang=\"en\"\n      suppressHydrationWarning\n      className={`${geistSans.variable} ${geistMono.variable} h-full font-sans antialiased`}>\n      <body className=\"flex min-h-full font-mono flex-col\">\n        <SessionProvider>{children}</SessionProvider>\n      </body>\n    </html>\n  );\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/globals.css",
                      "content": "@import \"tailwindcss\";\n\n@custom-variant dark (&:is(.dark *));\n"
                    },
                    {
                      "type": "file",
                      "path": "src/components/providers/session-provider.tsx",
                      "content": "\"use client\";\n\nimport { SessionProvider as NextSessionProvider } from \"next-auth/react\";\n\nexport function SessionProvider({ children }: { children: React.ReactNode }) {\n  return (\n    <NextSessionProvider>\n      {children}\n    </NextSessionProvider>\n  );\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/components/auth/profile-card.tsx",
                      "content": "\"use client\";\r\n\r\nimport { useSession } from \"next-auth/react\";\r\nimport { redirect } from \"next/navigation\";\r\n\r\n\r\nexport function ProfileCard() {\r\n  const session = useSession();\r\n\r\n  if (session.status === \"loading\") {\r\n    return (\r\n      <div className=\"flex flex-col items-center justify-center gap-4 py-8\">\r\n        <div className=\"flex items-center gap-2\">\r\n          <p className=\"text-muted-foreground\">Loading...</p>\r\n        </div>\r\n      </div>\r\n    );\r\n  }\r\n\r\n  if (session.status !== \"authenticated\") {\r\n    return redirect(\"/signin\");\r\n  }\r\n\r\n  if (!session.data?.user) {\r\n    return (\r\n      <div className=\"flex flex-col items-center justify-center gap-4 py-8\">\r\n        <div className=\"flex items-center gap-2\">\r\n          <p className=\"text-muted-foreground\">User not found</p>\r\n        </div>\r\n      </div>\r\n    );\r\n  }\r\n\r\n  const { user } = session.data;\r\n\r\n  return (\r\n    <div className=\"flex flex-col items-center justify-center gap-4 py-8 text-white\">\r\n      {user.image ? (\r\n        <img\r\n          src={user.image}\r\n          alt={user.name || \"\"}\r\n          className=\"h-24 w-24 rounded-full\"\r\n        />\r\n      ) : (\r\n        <div className=\"flex size-12 items-center justify-center rounded-full bg-neutral-50 text-2xl text-black\">\r\n          {user.name?.charAt(0).toUpperCase()}\r\n        </div>\r\n      )}\r\n\r\n      <h1 className=\"text-3xl font-bold\">{user.name}</h1>\r\n      <div className=\"flex items-center gap-2\">\r\n        <p className=\"text-neutral-400\">{user.email}</p>\r\n      </div>\r\n    </div>\r\n  );\r\n}\r\n"
                    },
                    {
                      "type": "file",
                      "path": "src/components/auth/oauth-signin.tsx",
                      "content": "\"use client\";\n\nimport { signIn, useSession } from \"next-auth/react\";\nimport { redirect } from \"next/navigation\";\n\nexport function OAuthSignin() {\n  const session = useSession();\n  if (session.status === \"authenticated\") {\n    return redirect(\"/\");\n  }\n\n  return (\n    <div\n      className={\n        \"justify-center mt-4 flex flex-col items-center\"\n      }>\n      <h3 className=\"text-xl font-medium text-white uppercase\">\n        Continue with\n      </h3>\n      <div className=\"mt-3 w-50\">\n        <button\n          className={\n            \"relative flex w-full cursor-pointer items-center justify-center gap-5 rounded-full bg-white px-4 py-3\"\n          }\n          onClick={() =>\n            signIn(\"google\", {\n              callbackUrl: \"/\"\n            })\n          }\n          type=\"button\">\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            width=\"24\"\n            height=\"24\"\n            viewBox=\"0 0 128 128\">\n            <path\n              fill=\"#fff\"\n              d=\"M44.59 4.21a63.28 63.28 0 0 0 4.33 120.9a67.6 67.6 0 0 0 32.36.35a57.13 57.13 0 0 0 25.9-13.46a57.44 57.44 0 0 0 16-26.26a74.3 74.3 0 0 0 1.61-33.58H65.27v24.69h34.47a29.72 29.72 0 0 1-12.66 19.52a36.2 36.2 0 0 1-13.93 5.5a41.3 41.3 0 0 1-15.1 0A37.2 37.2 0 0 1 44 95.74a39.3 39.3 0 0 1-14.5-19.42a38.3 38.3 0 0 1 0-24.63a39.25 39.25 0 0 1 9.18-14.91A37.17 37.17 0 0 1 76.13 27a34.3 34.3 0 0 1 13.64 8q5.83-5.8 11.64-11.63c2-2.09 4.18-4.08 6.15-6.22A61.2 61.2 0 0 0 87.2 4.59a64 64 0 0 0-42.61-.38\"\n            />\n            <path\n              fill=\"#e33629\"\n              d=\"M44.59 4.21a64 64 0 0 1 42.61.37a61.2 61.2 0 0 1 20.35 12.62c-2 2.14-4.11 4.14-6.15 6.22Q95.58 29.23 89.77 35a34.3 34.3 0 0 0-13.64-8a37.17 37.17 0 0 0-37.46 9.74a39.25 39.25 0 0 0-9.18 14.91L8.76 35.6A63.53 63.53 0 0 1 44.59 4.21\"\n            />\n            <path\n              fill=\"#f8bd00\"\n              d=\"M3.26 51.5a63 63 0 0 1 5.5-15.9l20.73 16.09a38.3 38.3 0 0 0 0 24.63q-10.36 8-20.73 16.08a63.33 63.33 0 0 1-5.5-40.9\"\n            />\n            <path\n              fill=\"#587dbd\"\n              d=\"M65.27 52.15h59.52a74.3 74.3 0 0 1-1.61 33.58a57.44 57.44 0 0 1-16 26.26c-6.69-5.22-13.41-10.4-20.1-15.62a29.72 29.72 0 0 0 12.66-19.54H65.27c-.01-8.22 0-16.45 0-24.68\"\n            />\n            <path\n              fill=\"#319f43\"\n              d=\"M8.75 92.4q10.37-8 20.73-16.08A39.3 39.3 0 0 0 44 95.74a37.2 37.2 0 0 0 14.08 6.08a41.3 41.3 0 0 0 15.1 0a36.2 36.2 0 0 0 13.93-5.5c6.69 5.22 13.41 10.4 20.1 15.62a57.13 57.13 0 0 1-25.9 13.47a67.6 67.6 0 0 1-32.36-.35a63 63 0 0 1-23-11.59A63.7 63.7 0 0 1 8.75 92.4\"\n            />\n          </svg>\n          Google\n        </button>\n\n      </div>\n    </div>\n  );\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/components/auth/auth-status.tsx",
                      "content": "\"use client\";\r\n\r\nimport { signOut, useSession } from \"next-auth/react\";\r\nimport Link from \"next/link\";\r\n\r\nexport function AuthStatus() {\r\n  const session = useSession();\r\n\r\n  if (session.status !== \"authenticated\") {\r\n    return (\r\n      <Link\r\n        href=\"/signin\"\r\n        className=\"rounded-full border border-neutral-400 bg-neutral-700 px-4 py-2 text-sm font-medium text-neutral-300\">\r\n        Sign In\r\n      </Link>\r\n    );\r\n  }\r\n\r\n  return (\r\n    <button\r\n      onClick={() => {\r\n        signOut();\r\n      }}\r\n      className=\"h-10 cursor-pointer rounded-full bg-neutral-50 px-4 font-medium text-black\">\r\n      Sign Out\r\n    </button>\r\n  );\r\n}\r\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/signin/page.tsx",
                      "content": "import { OAuthSignin } from \"@/components/auth/oauth-signin\";\r\n\r\nexport default function Page() {\r\n  return (\r\n    <div className={\"flex bg-neutral-950 h-screen w-full items-center justify-center\"}>\r\n      <OAuthSignin />\r\n    </div>\r\n  );\r\n}\r\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/profile/page.tsx",
                      "content": "import { ProfileCard } from \"@/components/auth/profile-card\";\r\n\r\nexport default function Page() {\r\n  return (\r\n    <div className={\"flex h-screen w-full items-center justify-center\"}>\r\n      <ProfileCard />\r\n    </div>\r\n  );\r\n}\r\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/api/auth/[...nextauth]/route.ts",
                      "content": "import { authOptions } from \"@/lib/next-auth\";\nimport NextAuth from \"next-auth\";\n\nconst handler = NextAuth(authOptions);\n\nexport { handler as GET, handler as POST };\n"
                    }
                  ]
                }
              }
            },
            "github": {
              "label": "GitHub",
              "dependencies": {
                "runtime": [
                  "next-auth@^4.24.14"
                ],
                "dev": []
              },
              "env": [
                "NEXTAUTH_URL",
                "NEXTAUTH_SECRET",
                "GITHUB_ID",
                "GITHUB_SECRET"
              ],
              "message": "The `google-auth` component has been added. Remember to wrap your app with the `SessionProvider` component.\n\n```tsx title=\"app/layout.tsx\"\nimport { SessionProvider } from \"@/components/providers/session-provider\";\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\">\n      <body>\n        <SessionProvider>{children}</SessionProvider>\n      </body>\n    </html>\n  );\n}\n```",
              "architectures": {
                "file-api": {
                  "files": [
                    {
                      "type": "file",
                      "path": "src/lib/next-auth.ts",
                      "content": "import { Account, AuthOptions, Profile as NextAuthProfile } from \"next-auth\";\nimport GitHubProvider from \"next-auth/providers/github\";\n\ninterface CustomProfile extends NextAuthProfile {\n  picture?: string;\n  avatar_url?: string;\n  email_verified?: boolean;\n}\n\nexport const authOptions: AuthOptions = {\n  callbacks: {\n    async signIn({\n      account,\n      profile\n    }: {\n      account: Account | null;\n      profile?: CustomProfile | undefined;\n    }) {\n      if (!account || !profile) return false;\n      if (account?.provider === \"github\") {\n        // console.log({ profile, account });\n\n        const userInfo = {\n          name: profile?.name as string,\n          email: profile?.email as string,\n          avatar: profile.picture || profile.avatar_url,\n          provider: account.provider,\n          providerAccountId: account.providerAccountId,\n          isEmailVerified: profile?.email_verified ?? false\n        };\n\n        console.log({ userInfo });\n\n        return true;\n      }\n      return true;\n    }\n  },\n  providers: [\n    GitHubProvider({\n      clientId: process.env.GITHUB_ID as string,\n      clientSecret: process.env.GITHUB_SECRET as string,\n      authorization: {\n        params: {\n          access_type: \"offline\",\n          response_type: \"code\"\n        }\n      }\n    })\n  ],\n\n  secret: process.env.NEXTAUTH_SECRET\n};\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/page.tsx",
                      "content": "import { AuthStatus } from \"@/components/auth/auth-status\";\nimport { ProfileCard } from \"@/components/auth/profile-card\";\n\nimport Link from \"next/link\";\n\nexport default function Page() {\n  return (\n    <main className=\"min-h-screen bg-neutral-950 px-6\">\n      <div className=\"relative mx-auto max-w-3xl\">\n        <div className=\"mt-5 flex items-center justify-between rounded-full border border-neutral-500/30 p-4\">\n          <Link\n            href=\"https://github.com/akkaldhami/servercn\"\n            target=\"_blank\"\n            className=\"flex items-center gap-2 rounded-full bg-neutral-50 px-4 py-2 text-sm font-medium text-black\">\n            <svg\n              xmlns=\"http://www.w3.org/2000/svg\"\n              width=\"24\"\n              height=\"24\"\n              viewBox=\"0 0 24 24\">\n              <path\n                fill=\"currentColor\"\n                d=\"M12.026 2a9.975 9.975 0 0 0-3.153 19.439c.5.09.679-.217.679-.481c0-.237-.008-.865-.011-1.7c-2.775.6-3.361-1.338-3.361-1.338a2.635 2.635 0 0 0-1.107-1.459c-.9-.619.069-.605.069-.605c.64.088 1.205.467 1.527 1.028a2.124 2.124 0 0 0 2.9.829c.046-.506.272-.979.635-1.334c-2.214-.251-4.542-1.107-4.542-4.93a3.865 3.865 0 0 1 1.026-2.671a3.588 3.588 0 0 1 .1-2.64s.837-.269 2.742 1.021a9.439 9.439 0 0 1 4.992 0c1.906-1.291 2.742-1.021 2.742-1.021c.37.835.405 1.78.1 2.64a3.84 3.84 0 0 1 1.024 2.675c0 3.833-2.33 4.675-4.552 4.922c.48.49.725 1.162.675 1.846c0 1.334-.012 2.41-.012 2.737c0 .267.178.577.687.479A9.975 9.975 0 0 0 12.026 2Z\"\n              />\n            </svg>\n            Star on GitHub\n          </Link>\n          <div className=\"flex items-center justify-center gap-4\">\n            <AuthStatus />\n          </div>\n        </div>\n\n        <ProfileCard />\n      </div>\n    </main>\n  );\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/layout.tsx",
                      "content": "import type { Metadata } from \"next\";\nimport { Geist, Geist_Mono } from \"next/font/google\";\nimport \"./globals.css\";\nimport { SessionProvider } from \"@/components/providers/session-provider\";\n\nconst geistSans = Geist({\n  variable: \"--font-geist-sans\",\n  subsets: [\"latin\"]\n});\n\nconst geistMono = Geist_Mono({\n  variable: \"--font-geist-mono\",\n  subsets: [\"latin\"]\n});\n\nexport const metadata: Metadata = {\n  title: \"Servercn Next.js GitHub Oauth Component\",\n  description: \"Next.js GitHub Oauth Component\"\n};\n\nexport default function RootLayout({\n  children\n}: Readonly<{\n  children: React.ReactNode;\n}>) {\n  return (\n    <html\n      lang=\"en\"\n      suppressHydrationWarning\n      className={`${geistSans.variable} ${geistMono.variable} h-full font-sans antialiased`}>\n      <body className=\"flex min-h-full font-mono flex-col\">\n        <SessionProvider>{children}</SessionProvider>\n      </body>\n    </html>\n  );\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/globals.css",
                      "content": "@import \"tailwindcss\";\n\n@custom-variant dark (&:is(.dark *));\n"
                    },
                    {
                      "type": "file",
                      "path": "src/components/providers/session-provider.tsx",
                      "content": "\"use client\";\n\nimport { SessionProvider as NextSessionProvider } from \"next-auth/react\";\n\nexport function SessionProvider({ children }: { children: React.ReactNode }) {\n  return (\n    <NextSessionProvider>\n      {children}\n    </NextSessionProvider>\n  );\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/components/auth/profile-card.tsx",
                      "content": "\"use client\";\r\n\r\n/* eslint-disable @next/next/no-img-element */\r\nimport { useSession } from \"next-auth/react\";\r\nimport { redirect } from \"next/navigation\";\r\n\r\nexport function ProfileCard() {\r\n  const session = useSession();\r\n\r\n  if (session.status === \"loading\") {\r\n    return (\r\n      <div className=\"flex flex-col items-center justify-center gap-4 py-8\">\r\n        <div className=\"flex items-center gap-2\">\r\n          <p className=\"text-white\">Loading...</p>\r\n        </div>\r\n      </div>\r\n    );\r\n  }\r\n\r\n  if (session.status !== \"authenticated\") {\r\n    return redirect(\"/signin\");\r\n  }\r\n\r\n  if (!session.data?.user) {\r\n    return (\r\n      <div className=\"flex flex-col items-center justify-center gap-4 py-8\">\r\n        <div className=\"flex items-center gap-2\">\r\n          <p className=\"text-white\">User not found</p>\r\n        </div>\r\n      </div>\r\n    );\r\n  }\r\n\r\n  const { user } = session.data;\r\n\r\n  return (\r\n    <div className=\"flex flex-col items-center justify-center gap-4 py-8 text-white\">\r\n      {user.image ? (\r\n        <img\r\n          src={user.image}\r\n          alt={user.name || \"\"}\r\n          className=\"h-24 w-24 rounded-full\"\r\n        />\r\n      ) : (\r\n        <div className=\"flex size-12 items-center justify-center rounded-full bg-neutral-50 text-2xl text-black\">\r\n          {user.name?.charAt(0).toUpperCase()}\r\n        </div>\r\n      )}\r\n\r\n      <h1 className=\"text-3xl font-bold\">{user.name}</h1>\r\n      <div className=\"flex items-center gap-2\">\r\n        <p className=\"text-neutral-400\">{user.email}</p>\r\n      </div>\r\n    </div>\r\n  );\r\n}\r\n"
                    },
                    {
                      "type": "file",
                      "path": "src/components/auth/github-signin.tsx",
                      "content": "\"use client\";\n\nimport { signIn, useSession } from \"next-auth/react\";\nimport { redirect } from \"next/navigation\";\n\nexport function GitHubSignin() {\n  const session = useSession();\n\n  if (session.status === \"authenticated\") {\n    return redirect(\"/\");\n  }\n\n  return (\n    <div className={\"mt-4 flex flex-col items-center justify-center\"}>\n      <h3 className=\"text-xl font-medium text-white uppercase\">\n        Continue with\n      </h3>\n      <div className=\"mt-3 w-50\">\n        <button\n          className={\n            \"relative flex w-full cursor-pointer items-center justify-center gap-5 rounded-full bg-white px-4 py-3\"\n          }\n          onClick={() =>\n            signIn(\"github\", {\n              callbackUrl: \"/\"\n            })\n          }\n          type=\"button\">\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            width=\"24\"\n            height=\"24\"\n            viewBox=\"0 0 64 64\">\n            <path\n              fill=\"currentColor\"\n              d=\"M32 0C14 0 0 14 0 32c0 21 19 30 22 30c2 0 2-1 2-2v-5c-7 2-10-2-11-5c0 0 0-1-2-3c-1-1-5-3-1-3c3 0 5 4 5 4c3 4 7 3 9 2c0-2 2-4 2-4c-8-1-14-4-14-15q0-6 3-9s-2-4 0-9c0 0 5 0 9 4c3-2 13-2 16 0c4-4 9-4 9-4c2 7 0 9 0 9q3 3 3 9c0 11-7 14-14 15c1 1 2 3 2 6v8c0 1 0 2 2 2c3 0 22-9 22-30C64 14 50 0 32 0\"\n            />\n          </svg>\n          GitHub\n        </button>\n      </div>\n    </div>\n  );\n}\n"
                    },
                    {
                      "type": "file",
                      "path": "src/components/auth/auth-status.tsx",
                      "content": "\"use client\";\r\n\r\nimport { signOut, useSession } from \"next-auth/react\";\r\nimport Link from \"next/link\";\r\n\r\nexport function AuthStatus() {\r\n  const session = useSession();\r\n\r\n  if (session.status !== \"authenticated\") {\r\n    return (\r\n      <Link\r\n        href=\"/signin\"\r\n        className=\"rounded-full border border-neutral-400 bg-neutral-700 px-4 py-2 text-sm font-medium text-neutral-300\">\r\n        Sign In\r\n      </Link>\r\n    );\r\n  }\r\n\r\n  return (\r\n    <button\r\n      onClick={() => {\r\n        signOut();\r\n      }}\r\n      className=\"h-10 cursor-pointer rounded-full bg-neutral-50 px-4 font-medium text-black\">\r\n      Sign Out\r\n    </button>\r\n  );\r\n}\r\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/signin/page.tsx",
                      "content": "import { GitHubSignin } from \"@/components/auth/github-signin\";\r\n\r\nexport default function Page() {\r\n  return (\r\n    <div\r\n      className={\r\n        \"flex h-screen w-full items-center justify-center bg-neutral-950\"\r\n      }>\r\n      <GitHubSignin />\r\n    </div>\r\n  );\r\n}\r\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/profile/page.tsx",
                      "content": "import { ProfileCard } from \"@/components/auth/profile-card\";\r\n\r\nexport default function Page() {\r\n  return (\r\n    <div className={\"flex h-screen w-full items-center justify-center\"}>\r\n      <ProfileCard />\r\n    </div>\r\n  );\r\n}\r\n"
                    },
                    {
                      "type": "file",
                      "path": "src/app/api/auth/[...nextauth]/route.ts",
                      "content": "import { authOptions } from \"@/lib/next-auth\";\nimport NextAuth from \"next-auth\";\n\nconst handler = NextAuth(authOptions);\n\nexport { handler as GET, handler as POST };\n"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
