UNPKG

@ideal-photography/shared

Version:

Shared GraphQL and Mongoose logic for Ideal Photography PWAs - Complete photography business models with authentication, bookings, galleries, reviews, and admin management. Fixed ES module imports and GraphQL schema issues.

270 lines (246 loc) 8.87 kB
import { mergeTypeDefs } from '@graphql-tools/merge'; import { mergeResolvers } from '@graphql-tools/merge'; import { ApolloServer } from '@apollo/server'; import { expressMiddleware } from '@apollo/server/express4'; // Import all typeDefs import userTypeDefs from './typeDefs/user.type.js'; import productTypeDefs from './typeDefs/product.type.js'; import bookingTypeDefs from './typeDefs/booking.type.js'; import serviceTypeDefs from './typeDefs/service.type.js'; import galleryTypeDefs from './typeDefs/gallery.type.js'; import reviewTypeDefs from './typeDefs/review.type.js'; import orderTypeDefs from './typeDefs/order.type.js'; import campaignTypeDefs from './typeDefs/campaign.type.js'; import notificationTypeDefs from './typeDefs/notification.type.js'; import settingsTypeDefs from './typeDefs/settings.type.js'; import auditLogTypeDefs from './typeDefs/auditLog.type.js'; // Import all resolvers import userResolvers from './resolvers/user.resolver.js'; import productResolvers from './resolvers/product.resolver.js'; import bookingResolvers from './resolvers/booking.resolver.js'; import serviceResolvers from './resolvers/service.resolver.js'; import galleryResolvers from './resolvers/gallery.resolver.js'; import reviewResolvers from './resolvers/review.resolver.js'; import orderResolvers from './resolvers/order.resolver.js'; import campaignResolvers from './resolvers/campaign.resolver.js'; import notificationResolvers from './resolvers/notification.resolver.js'; import settingsResolvers from './resolvers/settings.resolver.js'; import auditLogResolvers from './resolvers/auditLog.resolver.js'; // Base GraphQL schema for common scalars and directives const baseTypeDefs = ` scalar JSON scalar Upload scalar Date directive @auth(requires: [String!]) on FIELD_DEFINITION directive @admin(requires: [String!]) on FIELD_DEFINITION directive @owner on FIELD_DEFINITION directive @rateLimit(max: Int!, window: String!) on FIELD_DEFINITION # Common shared types type Query { _empty: String } type Mutation { _empty: String } type Subscription { _empty: String } `; // Merge all typeDefs and resolvers const typeDefs = mergeTypeDefs([ baseTypeDefs, userTypeDefs, productTypeDefs, bookingTypeDefs, serviceTypeDefs, galleryTypeDefs, reviewTypeDefs, orderTypeDefs, campaignTypeDefs, notificationTypeDefs, settingsTypeDefs, auditLogTypeDefs, ]); const resolvers = mergeResolvers([ userResolvers, productResolvers, bookingResolvers, serviceResolvers, galleryResolvers, reviewResolvers, orderResolvers, campaignResolvers, notificationResolvers, settingsResolvers, auditLogResolvers, ]); // Authentication middleware const authMiddleware = (req) => { const token = req.headers.authorization?.replace('Bearer ', ''); // TODO: Implement JWT verification return { token, user: req.user }; }; // Audit logging middleware const auditMiddleware = async (req, resolverName, args, result, error) => { // TODO: Implement audit logging for mutations if (resolverName.includes('Mutation') && req.user) { const { createAuditLog } = await import('../mongoDB/index.js'); try { await createAuditLog.utils.createAuditLog( resolverName, { user: req.user._id, userInfo: { name: req.user.name, email: req.user.email, role: req.user.role } }, { resourceType: 'graphql', resourceName: resolverName }, { status: error ? 'failure' : 'success', message: error?.message }, { request: { method: 'POST', url: '/graphql' } } ); } catch (auditError) { console.error('Audit logging failed:', auditError); } } }; // Enhanced context function const createContext = ({ req, res }) => { const auth = authMiddleware(req); return { req, res, user: auth.user, token: auth.token, isAuthenticated: !!auth.user, isAdmin: auth.user?.role === 'admin' || auth.user?.role === 'super_admin', isSuperAdmin: auth.user?.role === 'super_admin', // Helper functions for common operations requireAuth: () => { if (!auth.user) throw new Error('Authentication required'); }, requireAdmin: () => { if (!auth.user || !['admin', 'super_admin', 'manager'].includes(auth.user.role)) { throw new Error('Admin access required'); } }, requireSuperAdmin: () => { if (!auth.user || auth.user.role !== 'super_admin') { throw new Error('Super admin access required'); } }, checkPermission: (permission) => { if (!auth.user?.permissions?.includes(permission)) { throw new Error(`Permission ${permission} required`); } }, // Audit logging helper audit: (action, target, result, details = {}) => { return auditMiddleware(req, action, {}, result, null); } }; }; // Plugin for authentication and authorization const authPlugin = { requestDidStart() { return { didResolveOperation(requestContext) { // TODO: Check rate limiting here }, willSendResponse(requestContext) { // TODO: Log response metrics } }; } }; // Plugin for audit logging const auditPlugin = { requestDidStart() { return { didEncounterErrors(requestContext) { // Log GraphQL errors console.error('GraphQL Error:', requestContext.errors); } }; } }; // Helper function to create Apollo Server v4 instance const createApolloServer = (options = {}) => { return new ApolloServer({ typeDefs, resolvers, plugins: [ authPlugin, auditPlugin, ...(options.plugins || []) ], formatError: (error) => { // Log errors but don't expose internal details in production console.error('GraphQL Error:', error); if (process.env.NODE_ENV === 'production') { // Hide internal error details in production if (error.message.includes('ValidationError') || error.message.includes('CastError') || error.message.includes('MongoError')) { return new Error('An internal error occurred'); } } return error; }, introspection: process.env.NODE_ENV !== 'production', ...options, }); }; // Helper function to apply Apollo Server middleware to Express app const applyApolloMiddleware = async (app, server, options = {}) => { await server.start(); const defaultOptions = { context: createContext, cors: { origin: process.env.CLIENT_URL || 'http://localhost:3000', credentials: true } }; app.use( '/graphql', expressMiddleware(server, { ...defaultOptions, ...options, context: async ({ req, res }) => { const baseContext = await createContext({ req, res }); const customContext = options.context ? await options.context({ req, res }) : {}; return { ...baseContext, ...customContext }; } }) ); }; // Helper function for subscription server setup const createSubscriptionServer = (httpServer, options = {}) => { // TODO: Implement WebSocket subscriptions using graphql-ws return { start: () => console.log('Subscription server started'), stop: () => console.log('Subscription server stopped') }; }; export { typeDefs, resolvers, createApolloServer, applyApolloMiddleware, createSubscriptionServer, createContext, authMiddleware, auditMiddleware, // Legacy exports for backward compatibility ApolloServer, expressMiddleware }; // Additional utilities export const utils = { formatError: (error) => { console.error('GraphQL Error:', error); return error; }, authenticate: authMiddleware, authorize: (roles = []) => (user) => { if (!user) throw new Error('Authentication required'); if (roles.length > 0 && !roles.includes(user.role)) { throw new Error('Insufficient permissions'); } return true; } };