d44a058fd8
* Converts import script to use public API endpoints The endpoints don't exist yet, but this is a good way to see how the implementation of the data structures differ. * Exposes public API endpoint to fetch user's sketches * Implements public API delete endpoint * Adds helper to create custom ApplicationError classes * Adds create project endpoint that understand API's data structure This transforms the nested tree of file data into a mongoose Project model * Returns '201 Created' to match API spec * Removes 'CustomError' variable assignment as it shows up in test output * transformFiles will return file validation errors * Tests API project controller * Tests toModel() * Creates default files if no root-level .html file is provided * Do not auto-generate a slug if it is provided Fixes a bug where the slug was auto-generated using the sketch name, even if a slug property had been provided. * Validates uniqueness of slugs for projects created by the public API * Adds tests for slug uniqueness * Configures node's Promise implementation for mongoose (fixes warnings) * Moves createProject tests to match controller location * Adds support for code to ApplicationErrors * deleteProject controller tests * getProjectsForUser controller tests - implements tests - update apiKey tests to use new User mocks * Ensure error objects have consistent property names `message` is used as a high-level description of the errors `detail` is optional and has an plain language explanation of the individual errors `errors` is an array of each individual problem from `detail` in a machine-readable format * Assert environment variables are provided at script start * Version public API * Expect "files" property to always be provided * Fixes linting error * Converts import script to use public API endpoints The endpoints don't exist yet, but this is a good way to see how the implementation of the data structures differ. * Exposes public API endpoint to fetch user's sketches * Implements public API delete endpoint * Adds helper to create custom ApplicationError classes * Adds create project endpoint that understand API's data structure This transforms the nested tree of file data into a mongoose Project model * Returns '201 Created' to match API spec * Removes 'CustomError' variable assignment as it shows up in test output * transformFiles will return file validation errors * Tests API project controller * Tests toModel() * Creates default files if no root-level .html file is provided * Do not auto-generate a slug if it is provided Fixes a bug where the slug was auto-generated using the sketch name, even if a slug property had been provided. * Validates uniqueness of slugs for projects created by the public API * Adds tests for slug uniqueness * Configures node's Promise implementation for mongoose (fixes warnings) * Moves createProject tests to match controller location * deleteProject controller tests * Adds support for code to ApplicationErrors * getProjectsForUser controller tests - implements tests - update apiKey tests to use new User mocks * Ensure error objects have consistent property names `message` is used as a high-level description of the errors `detail` is optional and has an plain language explanation of the individual errors `errors` is an array of each individual problem from `detail` in a machine-readable format * Assert environment variables are provided at script start * Version public API * Expect "files" property to always be provided * Fixes linting error * Checks that authenticated user has permission to create under this namespace Previously, the project was always created under the authenticated user's namespace, but this not obvious behaviour.
175 lines
5.1 KiB
JavaScript
175 lines
5.1 KiB
JavaScript
import Express from 'express';
|
|
import mongoose from 'mongoose';
|
|
import bodyParser from 'body-parser';
|
|
import cookieParser from 'cookie-parser';
|
|
import cors from 'cors';
|
|
import session from 'express-session';
|
|
import connectMongo from 'connect-mongo';
|
|
import passport from 'passport';
|
|
import path from 'path';
|
|
import basicAuth from 'express-basic-auth';
|
|
|
|
// Webpack Requirements
|
|
import webpack from 'webpack';
|
|
import webpackDevMiddleware from 'webpack-dev-middleware';
|
|
import webpackHotMiddleware from 'webpack-hot-middleware';
|
|
import config from '../webpack/config.dev';
|
|
|
|
// Import all required modules
|
|
import api from './routes/api.routes';
|
|
import users from './routes/user.routes';
|
|
import sessions from './routes/session.routes';
|
|
import projects from './routes/project.routes';
|
|
import files from './routes/file.routes';
|
|
import aws from './routes/aws.routes';
|
|
import serverRoutes from './routes/server.routes';
|
|
import embedRoutes from './routes/embed.routes';
|
|
import assetRoutes from './routes/asset.routes';
|
|
import { requestsOfTypeJSON } from './utils/requestsOfType';
|
|
|
|
import { renderIndex } from './views/index';
|
|
import { get404Sketch } from './views/404Page';
|
|
|
|
const app = new Express();
|
|
const MongoStore = connectMongo(session);
|
|
|
|
app.get('/health', (req, res) => res.json({ success: true }));
|
|
|
|
// For basic auth, in setting up beta editor
|
|
if (process.env.BASIC_USERNAME && process.env.BASIC_PASSWORD) {
|
|
app.use(basicAuth({
|
|
users: {
|
|
[process.env.BASIC_USERNAME]: process.env.BASIC_PASSWORD
|
|
},
|
|
challenge: true
|
|
}));
|
|
}
|
|
|
|
const corsOriginsWhitelist = [
|
|
/p5js\.org$/,
|
|
];
|
|
|
|
// Run Webpack dev server in development mode
|
|
if (process.env.NODE_ENV === 'development') {
|
|
const compiler = webpack(config);
|
|
app.use(webpackDevMiddleware(compiler, { lazy: false, noInfo: true, publicPath: config[0].output.publicPath }));
|
|
app.use(webpackHotMiddleware(compiler));
|
|
|
|
corsOriginsWhitelist.push(/localhost/);
|
|
}
|
|
|
|
const mongoConnectionString = process.env.MONGO_URL;
|
|
app.set('trust proxy', true);
|
|
|
|
// Enable Cross-Origin Resource Sharing (CORS) for all origins
|
|
const corsMiddleware = cors({
|
|
credentials: true,
|
|
origin: corsOriginsWhitelist,
|
|
});
|
|
app.use(corsMiddleware);
|
|
// Enable pre-flight OPTIONS route for all end-points
|
|
app.options('*', corsMiddleware);
|
|
|
|
// Body parser, cookie parser, sessions, serve public assets
|
|
|
|
app.use(Express.static(path.resolve(__dirname, '../dist/static'), {
|
|
maxAge: process.env.STATIC_MAX_AGE || (process.env.NODE_ENV === 'production' ? '1d' : '0')
|
|
}));
|
|
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
|
|
app.use(bodyParser.json({ limit: '50mb' }));
|
|
app.use(cookieParser());
|
|
app.use(session({
|
|
resave: true,
|
|
saveUninitialized: false,
|
|
secret: process.env.SESSION_SECRET,
|
|
proxy: true,
|
|
name: 'sessionId',
|
|
cookie: {
|
|
httpOnly: true,
|
|
secure: false,
|
|
},
|
|
store: new MongoStore({
|
|
url: mongoConnectionString,
|
|
autoReconnect: true
|
|
})
|
|
}));
|
|
|
|
app.use(passport.initialize());
|
|
app.use(passport.session());
|
|
app.use('/api/v1', requestsOfTypeJSON(), api);
|
|
app.use('/api', requestsOfTypeJSON(), users);
|
|
app.use('/api', requestsOfTypeJSON(), sessions);
|
|
app.use('/api', requestsOfTypeJSON(), files);
|
|
app.use('/api', requestsOfTypeJSON(), projects);
|
|
app.use('/api', requestsOfTypeJSON(), aws);
|
|
|
|
// This is a temporary way to test access via Personal Access Tokens
|
|
// Sending a valid username:<personal-access-token> combination will
|
|
// return the user's information.
|
|
app.get('/api/auth/access-check', passport.authenticate('basic', { session: false }), (req, res) => res.json(req.user));
|
|
|
|
app.use(assetRoutes);
|
|
// this is supposed to be TEMPORARY -- until i figure out
|
|
// isomorphic rendering
|
|
app.use('/', serverRoutes);
|
|
|
|
app.use('/', embedRoutes);
|
|
app.get('/auth/github', passport.authenticate('github'));
|
|
app.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/login' }), (req, res) => {
|
|
res.redirect('/');
|
|
});
|
|
|
|
app.get('/auth/google', passport.authenticate('google'));
|
|
app.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/login' }), (req, res) => {
|
|
res.redirect('/');
|
|
});
|
|
|
|
// configure passport
|
|
require('./config/passport');
|
|
// const passportConfig = require('./config/passport');
|
|
|
|
// Connect to MongoDB
|
|
mongoose.Promise = global.Promise;
|
|
mongoose.connect(mongoConnectionString, { useMongoClient: true });
|
|
mongoose.connection.on('error', () => {
|
|
console.error('MongoDB Connection Error. Please make sure that MongoDB is running.');
|
|
process.exit(1);
|
|
});
|
|
|
|
app.get('/', (req, res) => {
|
|
res.sendFile(renderIndex());
|
|
});
|
|
|
|
// Handle API errors
|
|
app.use('/api', (error, req, res, next) => {
|
|
if (error && error.code && !res.headersSent) {
|
|
res.status(error.code).json({ error: error.message });
|
|
return;
|
|
}
|
|
|
|
next(error);
|
|
});
|
|
|
|
|
|
// Handle missing routes.
|
|
app.get('*', (req, res) => {
|
|
res.status(404);
|
|
if (req.accepts('html')) {
|
|
get404Sketch(html => res.send(html));
|
|
return;
|
|
}
|
|
if (req.accepts('json')) {
|
|
res.send({ error: 'Not found.' });
|
|
return;
|
|
}
|
|
res.type('txt').send('Not found.');
|
|
});
|
|
|
|
// start app
|
|
app.listen(process.env.PORT, (error) => {
|
|
if (!error) {
|
|
console.log(`p5js web editor is running on port: ${process.env.PORT}!`); // eslint-disable-line
|
|
}
|
|
});
|
|
|
|
export default app;
|