2024-01-16 08:10:14 +00:00
|
|
|
import debug from 'debug';
|
2024-01-16 09:36:35 +00:00
|
|
|
import express from 'express';
|
2024-02-22 11:56:26 +00:00
|
|
|
import cors from 'cors';
|
2024-01-16 09:36:35 +00:00
|
|
|
import { ApolloServer } from 'apollo-server-express';
|
|
|
|
import { createServer } from 'http';
|
|
|
|
import {
|
|
|
|
ApolloServerPluginDrainHttpServer,
|
2024-02-22 11:56:26 +00:00
|
|
|
ApolloServerPluginLandingPageLocalDefault,
|
2024-04-21 23:02:42 +00:00
|
|
|
AuthenticationError,
|
2024-01-16 09:36:35 +00:00
|
|
|
} from 'apollo-server-core';
|
2024-04-21 23:02:42 +00:00
|
|
|
import cookieSession from 'cookie-session';
|
2024-01-16 08:10:14 +00:00
|
|
|
|
2024-01-16 09:36:35 +00:00
|
|
|
import { TypeSource } from '@graphql-tools/utils';
|
|
|
|
import { makeExecutableSchema } from '@graphql-tools/schema';
|
|
|
|
|
2024-01-17 05:23:01 +00:00
|
|
|
import { ServerConfig } from './config';
|
2024-02-22 11:56:26 +00:00
|
|
|
import { DEFAULT_GQL_PATH } from './constants';
|
2024-02-15 11:54:57 +00:00
|
|
|
import githubRouter from './routes/github';
|
2024-02-22 11:56:26 +00:00
|
|
|
import authRouter from './routes/auth';
|
2024-06-05 16:38:19 +00:00
|
|
|
import stagingRouter from './routes/staging';
|
2024-02-15 11:54:57 +00:00
|
|
|
import { Service } from './service';
|
2024-01-16 08:10:14 +00:00
|
|
|
|
|
|
|
const log = debug('snowball:server');
|
|
|
|
|
2024-02-22 11:56:26 +00:00
|
|
|
declare module 'express-session' {
|
|
|
|
interface SessionData {
|
2024-04-24 02:10:20 +00:00
|
|
|
userId: string;
|
2024-02-22 11:56:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-16 09:36:35 +00:00
|
|
|
export const createAndStartServer = async (
|
2024-02-15 11:54:57 +00:00
|
|
|
serverConfig: ServerConfig,
|
2024-01-16 09:36:35 +00:00
|
|
|
typeDefs: TypeSource,
|
|
|
|
resolvers: any,
|
2024-04-21 23:02:42 +00:00
|
|
|
service: Service,
|
2024-01-16 09:36:35 +00:00
|
|
|
): Promise<ApolloServer> => {
|
|
|
|
const { host, port, gqlPath = DEFAULT_GQL_PATH } = serverConfig;
|
2024-02-22 11:56:26 +00:00
|
|
|
const { appOriginUrl, secret, domain, trustProxy } = serverConfig.session;
|
2024-01-16 08:10:14 +00:00
|
|
|
|
|
|
|
const app = express();
|
|
|
|
|
2024-01-16 09:36:35 +00:00
|
|
|
// Create HTTP server
|
|
|
|
const httpServer = createServer(app);
|
|
|
|
|
|
|
|
// Create the schema
|
|
|
|
const schema = makeExecutableSchema({
|
|
|
|
typeDefs,
|
2024-04-21 23:02:42 +00:00
|
|
|
resolvers,
|
2024-01-16 08:10:14 +00:00
|
|
|
});
|
|
|
|
|
2024-01-16 09:36:35 +00:00
|
|
|
const server = new ApolloServer({
|
|
|
|
schema,
|
|
|
|
csrfPrevention: true,
|
2024-02-22 11:56:26 +00:00
|
|
|
context: async ({ req }) => {
|
|
|
|
// https://www.apollographql.com/docs/apollo-server/v3/security/authentication#api-wide-authorization
|
|
|
|
|
2024-05-06 19:36:33 +00:00
|
|
|
const { userId } = req.session;
|
2024-02-22 11:56:26 +00:00
|
|
|
|
2024-05-06 19:36:33 +00:00
|
|
|
if (!userId) {
|
2024-02-22 11:56:26 +00:00
|
|
|
throw new AuthenticationError('Unauthorized: No active session');
|
|
|
|
}
|
|
|
|
|
2024-05-06 19:36:33 +00:00
|
|
|
const user = await service.getUser(userId);
|
2024-02-22 11:56:26 +00:00
|
|
|
|
|
|
|
return { user };
|
2024-01-17 05:23:01 +00:00
|
|
|
},
|
2024-01-16 09:36:35 +00:00
|
|
|
plugins: [
|
|
|
|
// Proper shutdown for the HTTP server
|
|
|
|
ApolloServerPluginDrainHttpServer({ httpServer }),
|
2024-04-21 23:02:42 +00:00
|
|
|
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
|
|
|
|
],
|
2024-01-16 08:10:14 +00:00
|
|
|
});
|
|
|
|
|
2024-01-16 09:36:35 +00:00
|
|
|
await server.start();
|
|
|
|
|
2024-04-21 23:02:42 +00:00
|
|
|
app.use(
|
|
|
|
cors({
|
|
|
|
origin: appOriginUrl,
|
|
|
|
credentials: true,
|
|
|
|
}),
|
|
|
|
);
|
2024-02-22 11:56:26 +00:00
|
|
|
|
|
|
|
if (trustProxy) {
|
|
|
|
// trust first proxy
|
|
|
|
app.set('trust proxy', 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
app.use(
|
2024-04-21 23:02:42 +00:00
|
|
|
cookieSession({
|
|
|
|
secret: secret,
|
|
|
|
secure: new URL(appOriginUrl).protocol === 'https:',
|
|
|
|
// 23 hours (less than 24 hours to avoid sessionSigs expiration issues)
|
|
|
|
maxAge: 23 * 60 * 60 * 1000,
|
|
|
|
sameSite: new URL(appOriginUrl).protocol === 'https:' ? 'none' : 'lax',
|
|
|
|
domain: domain || undefined,
|
|
|
|
}),
|
2024-02-22 11:56:26 +00:00
|
|
|
);
|
|
|
|
|
2024-01-16 09:36:35 +00:00
|
|
|
server.applyMiddleware({
|
|
|
|
app,
|
2024-02-22 11:56:26 +00:00
|
|
|
path: gqlPath,
|
|
|
|
cors: {
|
|
|
|
origin: [appOriginUrl],
|
2024-04-21 23:02:42 +00:00
|
|
|
credentials: true,
|
|
|
|
},
|
2024-01-16 08:10:14 +00:00
|
|
|
});
|
2024-01-16 09:36:35 +00:00
|
|
|
|
2024-02-15 11:54:57 +00:00
|
|
|
app.use(express.json());
|
2024-02-22 11:56:26 +00:00
|
|
|
|
|
|
|
app.set('service', service);
|
|
|
|
app.use('/auth', authRouter);
|
2024-02-15 11:54:57 +00:00
|
|
|
app.use('/api/github', githubRouter);
|
2024-06-05 16:38:19 +00:00
|
|
|
app.use('/staging', stagingRouter);
|
2024-02-15 11:54:57 +00:00
|
|
|
|
2024-01-16 09:36:35 +00:00
|
|
|
httpServer.listen(port, host, () => {
|
|
|
|
log(`Server is listening on ${host}:${port}${server.graphqlPath}`);
|
|
|
|
});
|
|
|
|
|
|
|
|
return server;
|
|
|
|
};
|