This repository has been archived by the owner on Oct 21, 2020. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1 parent
263b0a4
commit 5441b4c
Showing
11 changed files
with
10,588 additions
and
5,430 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,4 +58,11 @@ typings/ | |
.env | ||
|
||
# next.js build output | ||
.next | ||
.next | ||
|
||
# Webpack | ||
.webpack | ||
|
||
# Serverless | ||
.serverless | ||
.webpack |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
const mongoose = require("mongoose"); | ||
const bluebird = require("bluebird"); | ||
mongoose.Promise = bluebird; | ||
mongoose.Promise = global.Promise; | ||
|
||
// Only reconnect if needed. State is saved and outlives a handler invocation | ||
let isConnected; | ||
|
||
const connectToDatabase = () => { | ||
if (isConnected) { | ||
console.log("Re-using existing database connection"); | ||
return Promise.resolve(); | ||
} | ||
|
||
console.log("Creating new database connection"); | ||
return mongoose.connect(process.env.MONGODB_URL).then(db => { | ||
isConnected = db.connections[0].readyState; | ||
}); | ||
}; | ||
|
||
module.exports = connectToDatabase; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { graphqlLambda, graphiqlLambda } from "apollo-server-lambda"; | ||
import lambdaPlayground from "graphql-playground-middleware-lambda"; | ||
import { makeExecutableSchema } from "graphql-tools"; | ||
import { mergeResolvers, mergeTypes } from "merge-graphql-schemas"; | ||
import { userType } from "./types/user"; | ||
import { userResolver } from "./resolvers/user"; | ||
|
||
const types = mergeTypes([userType]); | ||
const solvers = mergeResolvers([userResolver]); | ||
const graphqlSchema = makeExecutableSchema({ | ||
typeDefs: types, | ||
resolvers: solvers, | ||
logger: console | ||
}); | ||
|
||
// Database connection logic lives outside of the handler for performance reasons | ||
const connectToDatabase = require("./db"); | ||
|
||
const server = require("apollo-server-lambda"); | ||
|
||
exports.graphqlHandler = function graphqlHandler(event, context, callback) { | ||
/* Cause Lambda to freeze the process and save state data after | ||
the callback is called the effect is that new handler invocations | ||
will be able to re-use the database connection. | ||
See https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html | ||
and https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs */ | ||
context.callbackWaitsForEmptyEventLoop = false; | ||
|
||
function callbackFilter(error, output) { | ||
if (!output.headers) { | ||
output.headers = {}; | ||
} | ||
// eslint-disable-next-line no-param-reassign | ||
output.headers["Access-Control-Allow-Origin"] = "*"; | ||
output.headers["Access-Control-Allow-Credentials"] = true; | ||
output.headers["Content-Type"] = "application/json"; | ||
|
||
callback(error, output); | ||
} | ||
|
||
const handler = server.graphqlLambda({ schema: graphqlSchema }); | ||
|
||
connectToDatabase() | ||
.then(() => { | ||
return handler(event, context, callbackFilter); | ||
}) | ||
.catch(err => { | ||
console.log("MongoDB connection error: ", err); | ||
// TODO: return 500? | ||
process.exit(); | ||
}); | ||
}; | ||
|
||
exports.apiHandler = lambdaPlayground({ | ||
endpoint: process.env.GRAPHQL_ENDPOINT_URL | ||
? process.env.GRAPHQL_ENDPOINT_URL | ||
: "/production/graphql" | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
const mongoose = require("mongoose"); | ||
const validator = require("validator"); | ||
|
||
const Schema = mongoose.Schema; | ||
const SchemaTypes = Schema.Types; | ||
|
||
const userSchema = new Schema({ | ||
email: { | ||
type: "string" | ||
}, | ||
newEmail: { | ||
type: "string" | ||
}, | ||
emailVerifyTTL: { | ||
type: "date" | ||
}, | ||
emailVerified: { | ||
type: "boolean", | ||
default: false | ||
}, | ||
emailAuthLinkTTL: { | ||
type: "date" | ||
}, | ||
password: { | ||
type: "string" | ||
}, | ||
progressTimestamps: { | ||
type: "array", | ||
default: [] | ||
}, | ||
isBanned: { | ||
type: "boolean", | ||
description: "User is banned from posting to camper news", | ||
default: false | ||
}, | ||
isCheater: { | ||
type: "boolean", | ||
description: | ||
"Users who are confirmed to have broken academic honesty policy are marked as cheaters", | ||
default: false | ||
}, | ||
isGithubCool: { | ||
type: "boolean", | ||
default: false | ||
}, | ||
githubId: { | ||
type: "string" | ||
}, | ||
githubURL: { | ||
type: "string" | ||
}, | ||
githubEmail: { | ||
type: "string" | ||
}, | ||
joinedGithubOn: { | ||
type: "date" | ||
}, | ||
website: { | ||
type: "string" | ||
}, | ||
githubProfile: { | ||
type: "string" | ||
}, | ||
_csrf: { | ||
type: "string" | ||
}, | ||
isMigrationGrandfathered: { | ||
type: "boolean", | ||
default: false | ||
}, | ||
username: { | ||
type: "string" | ||
}, | ||
bio: { | ||
type: "string", | ||
default: "" | ||
}, | ||
about: { | ||
type: "string", | ||
default: "" | ||
}, | ||
name: { | ||
type: "string", | ||
default: "" | ||
}, | ||
gender: { | ||
type: "string", | ||
default: "" | ||
}, | ||
location: { | ||
type: "string", | ||
default: "" | ||
}, | ||
picture: { | ||
type: "string", | ||
default: "" | ||
}, | ||
linkedin: { | ||
type: "string" | ||
}, | ||
codepen: { | ||
type: "string" | ||
}, | ||
twitter: { | ||
type: "string" | ||
}, | ||
currentStreak: { | ||
type: "number", | ||
default: 0 | ||
}, | ||
longestStreak: { | ||
type: "number", | ||
default: 0 | ||
}, | ||
sendMonthlyEmail: { | ||
type: "boolean", | ||
default: true | ||
}, | ||
sendNotificationEmail: { | ||
type: "boolean", | ||
default: true | ||
}, | ||
sendQuincyEmail: { | ||
type: "boolean", | ||
default: true | ||
}, | ||
isLocked: { | ||
type: "boolean", | ||
description: | ||
"Campers profile does not show challenges/certificates to the public", | ||
default: false | ||
}, | ||
currentChallengeId: { | ||
type: "string", | ||
description: "The challenge last visited by the user", | ||
default: "" | ||
}, | ||
currentChallenge: { | ||
type: {}, | ||
description: "deprecated" | ||
}, | ||
isUniqMigrated: { | ||
type: "boolean", | ||
description: "Campers completedChallenges array is free of duplicates", | ||
default: false | ||
}, | ||
isHonest: { | ||
type: "boolean", | ||
description: "Camper has signed academic honesty policy", | ||
default: false | ||
}, | ||
isFrontEndCert: { | ||
type: "boolean", | ||
description: "Camper is front end certified", | ||
default: false | ||
}, | ||
isDataVisCert: { | ||
type: "boolean", | ||
description: "Camper is data visualization certified", | ||
default: false | ||
}, | ||
isBackEndCert: { | ||
type: "boolean", | ||
description: "Campers is back end certified", | ||
default: false | ||
}, | ||
isFullStackCert: { | ||
type: "boolean", | ||
description: "Campers is full stack certified", | ||
default: false | ||
}, | ||
isRespWebDesignCert: { | ||
type: "boolean", | ||
description: "Camper is responsive web design certified", | ||
default: false | ||
}, | ||
is2018DataVisCert: { | ||
type: "boolean", | ||
description: "Camper is data visualization certified (2018)", | ||
default: false | ||
}, | ||
isFrontEndLibsCert: { | ||
type: "boolean", | ||
description: "Camper is front end libraries certified", | ||
default: false | ||
}, | ||
isJsAlgoDataStructCert: { | ||
type: "boolean", | ||
description: | ||
"Camper is javascript algorithms and data structures certified", | ||
default: false | ||
}, | ||
isApisMicroservicesCert: { | ||
type: "boolean", | ||
description: "Camper is apis and microservices certified", | ||
default: false | ||
}, | ||
isInfosecQaCert: { | ||
type: "boolean", | ||
description: | ||
"Camper is information security and quality assurance certified", | ||
default: false | ||
}, | ||
isChallengeMapMigrated: { | ||
type: "boolean", | ||
description: "Migrate completedChallenges array to challenge map", | ||
default: false | ||
}, | ||
challengeMap: { | ||
type: "object", | ||
description: "A map by ID of all the user completed challenges", | ||
default: {} | ||
}, | ||
completedChallenges: { | ||
type: [ | ||
{ | ||
completedDate: "number", | ||
lastUpdated: "number", | ||
numOfAttempts: "number", | ||
id: "string", | ||
name: "string", | ||
completedWith: "string", | ||
solution: "string", | ||
githubLink: "string", | ||
verified: "boolean", | ||
challengeType: { | ||
type: "number", | ||
default: 0 | ||
} | ||
} | ||
], | ||
default: [] | ||
}, | ||
portfolio: { | ||
type: "array", | ||
default: [] | ||
}, | ||
rand: { | ||
type: "number", | ||
index: true | ||
}, | ||
tshirtVote: { | ||
type: "number" | ||
}, | ||
timezone: { | ||
type: "string" | ||
}, | ||
theme: { | ||
type: "string", | ||
default: "default" | ||
}, | ||
languageTag: { | ||
type: "string", | ||
description: "An IETF language tag", | ||
default: "en" | ||
}, | ||
badges: { | ||
type: { | ||
coreTeam: { | ||
type: "array", | ||
default: [] | ||
} | ||
}, | ||
default: {} | ||
} | ||
}); | ||
|
||
module.exports = mongoose.model("User", userSchema, "user"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
const validator = require("validator"); | ||
const UserModel = require("../model/user.js"); | ||
const moment = require("moment"); | ||
const fs = require("fs"); | ||
|
||
const createErrorResponse = (statusCode, message) => ({ | ||
statusCode: statusCode || 501, | ||
headers: { "Content-Type": "text-plain" }, | ||
body: message || "Incorrect id" | ||
}); | ||
|
||
export function getUsers(args) { | ||
return new Promise((resolve, reject) => { | ||
UserModel.find(args) | ||
.then(users => { | ||
let userMap = []; | ||
users.forEach((user, index) => { | ||
userMap[index] = user; | ||
}); | ||
let res = {}; | ||
res.users = userMap; | ||
resolve(res.users); | ||
}) | ||
.catch(err => { | ||
reject(err); | ||
}); | ||
}); | ||
} | ||
|
||
export function createUser(args) { | ||
return new Promise((resolve, reject) => { | ||
// Needs rewriting / updating | ||
const userModel = new UserModel(args); // Would need validation | ||
const newUser = userModel.save(); | ||
|
||
if (!newUser) { | ||
reject("Failed to add user"); | ||
} | ||
|
||
resolve(newUser); | ||
}); | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import * as dbUsers from "../mongo/user"; | ||
|
||
export const userResolver = { | ||
Query: { | ||
users: (_, args) => dbUsers.getUsers(args) | ||
}, | ||
Mutation: { | ||
createUser: (_, args) => dbUsers.createUser(args) | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
service: serverless-graphql-apollo | ||
|
||
frameworkVersion: ">=1.21.0 <2.0.0" | ||
|
||
provider: | ||
name: aws | ||
runtime: nodejs8.10 | ||
stage: dev | ||
region: us-east-1 | ||
environment: | ||
MONGODB_URL: ${env:MONGODB_URL} | ||
GRAPHQL_ENDPOINT_URL: ${env:GRAPHQL_ENDPOINT_URL} | ||
iamRoleStatements: | ||
- Effect: Allow | ||
Action: | ||
- s3:* | ||
Resource: "*" | ||
|
||
plugins: | ||
- serverless-webpack | ||
- serverless-offline-scheduler | ||
- serverless-offline | ||
|
||
custom: | ||
serverless-offline: | ||
port: 4000 | ||
webpack: | ||
webpackConfig: ./webpack.config.js | ||
includeModules: true | ||
|
||
functions: | ||
graphql: | ||
handler: handler.graphqlHandler | ||
events: | ||
- http: | ||
path: graphql | ||
method: post | ||
cors: true | ||
|
||
api: | ||
handler: handler.apiHandler | ||
events: | ||
- http: | ||
path: api | ||
method: get | ||
cors: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
const userType = ` | ||
type Query { | ||
users( | ||
_id: ID | ||
name: String | ||
email: String | ||
): [User] | ||
} | ||
type Mutation { | ||
createUser( | ||
_id: ID | ||
name: String | ||
email: String | ||
): User | ||
} | ||
type User { | ||
_id: ID! | ||
email: String | ||
name: String | ||
} | ||
`; | ||
|
||
export { userType }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
const slsw = require("serverless-webpack"); | ||
const nodeExternals = require("webpack-node-externals"); | ||
|
||
module.exports = { | ||
entry: slsw.lib.entries, | ||
target: "node", | ||
// Generate sourcemaps for proper error messages | ||
devtool: "source-map", | ||
// Since 'aws-sdk' is not compatible with webpack, | ||
// we exclude all node dependencies | ||
externals: [nodeExternals()], | ||
mode: slsw.lib.webpack.isLocal ? "development" : "production", | ||
optimization: { | ||
// We no not want to minimize our code. | ||
minimize: false | ||
}, | ||
performance: { | ||
// Turn off size warnings for entry points | ||
hints: false | ||
}, | ||
// Run babel on all .js files and skip those in node_modules | ||
module: { | ||
rules: [ | ||
{ | ||
test: /\.js$/, | ||
loader: "babel-loader", | ||
include: __dirname, | ||
exclude: /node_modules/ | ||
} | ||
] | ||
} | ||
}; |