Skip to content

Commit 1d5911c

Browse files
committed
start of server reintegration
1 parent 6fc3649 commit 1d5911c

14 files changed

+736
-0
lines changed

server/.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
node_modules/
2+
domains.ext
3+
localhost.crt
4+
localhost.csr
5+
localhost.key
6+
package-lock.json
7+
rootCA.key
8+
rootCA.pem
9+
RootCA.crt
10+
RootCA.srl
11+
.env
12+
/*.code-workspace

server/Procfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: npm run start

server/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<p align="center">
2+
<img width="300" src="reactypeserverlogo.png">
3+
<h1 align="center">ReacType Server</h1>
4+
</p>
5+
6+
**ReacType Server** is the backend complement to the visual React prototyping tool **ReacType**. It is built in **Node.js** with the **Express** framework linked to **MongoDB** to handle user authentication (personal accounts on our own database as well as through Github Oauth), sessions, and user project management. The server itself is officially deployed through Heroku, but you can host your own local environment to communicate with the database with this repo.
7+
8+
If `npm` is your package manager, you just need to run the script `npm run dev` and it will start the server on `http://localhost:5000` for your development environment.
9+
10+
Endpoint testing is currently integrated with Jest and Supertest as well and can be run by `npm run test` or `npm run test:watch` for watch mode.

server/__tests__/projects.test.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
const request = require('supertest');
2+
let server = 'https://reactype.herokuapp.com';
3+
const isDev = process.env.NODE_ENV === 'development';
4+
if (isDev) {
5+
server = 'http://localhost:5000';
6+
}
7+
8+
// save and get projects endpoint testing
9+
describe('Project endpoints tests', () => {
10+
// initializes the project to be sent to server/DB
11+
const state = {
12+
name: 'test',
13+
isLoggedIn: false,
14+
components: [
15+
{
16+
id: 1,
17+
name: 'index',
18+
style: {},
19+
code: '<div>Drag in a component or HTML element into the canvas!</div>',
20+
children: [],
21+
},
22+
],
23+
projectType: 'Next.js',
24+
rootComponents: [1],
25+
canvasFocus: { componentId: 1, childId: null },
26+
nextComponentId: 2,
27+
nextChildId: 1,
28+
};
29+
const projectToSave = {
30+
name: 'test',
31+
project: state,
32+
userId: '5f0df0636678ba002ba43b88',
33+
};
34+
35+
// test saveProject endpoint
36+
describe('/saveProject', () => {
37+
describe('/POST', () => {
38+
it('responds with a status of 200 and json object equal to project sent', () => {
39+
return request(server)
40+
.post('/saveProject')
41+
.send(projectToSave)
42+
.expect('Content-Type', /json/)
43+
.expect(200)
44+
.then((res) => expect(res.body.project.name).toBe('test'));
45+
});
46+
});
47+
});
48+
49+
// test getProjects endpoint
50+
describe('/getProjects', () => {
51+
describe('POST', () => {
52+
it('responds with status of 200 and json object equal to an array of user projects', () => {
53+
return request(server)
54+
.post('/getProjects')
55+
.send({ userId: projectToSave.userId })
56+
.expect(200)
57+
.expect('Content-Type', /json/)
58+
.then((res) => {
59+
expect(Array.isArray(res.body)).toBeTruthy;
60+
expect(res.body[0].name).toBe('test');
61+
});
62+
});
63+
});
64+
});
65+
66+
// test deleteProject endpoint
67+
describe('/deleteProject', () => {
68+
describe('DELETE', () => {
69+
const { name, userId } = projectToSave;
70+
it('responds with status of 200 and json object equal to deleted project', () => {
71+
return request(server)
72+
.delete('/deleteProject')
73+
.send({ name, userId })
74+
.expect(200)
75+
.expect('Content-Type', /json/)
76+
.then((res) => expect(res.body.name).toBe('test'));
77+
});
78+
});
79+
});
80+
});

server/__tests__/users.test.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
const request = require('supertest');
2+
let server = 'https://reactype.herokuapp.com';
3+
const isDev = process.env.NODE_ENV === 'development';
4+
if (isDev) {
5+
server = 'http://localhost:5000';
6+
}
7+
8+
// tests user signup and login routes
9+
describe('User authentication tests', () => {
10+
let num = Math.floor(Math.random() * 1000);
11+
12+
describe('/signup', () => {
13+
describe('POST', () => {
14+
it('responds with status 200 and json object on valid new user signup', () => {
15+
return request(server)
16+
.post('/signup')
17+
.send({
18+
username: `supertest${num}`,
19+
email: `test${num}@test.com`,
20+
password: `${num}`,
21+
})
22+
.expect('Content-Type', /json/)
23+
.expect(200)
24+
.then((res) => expect(typeof res.body).toBe('object'));
25+
});
26+
it('responds with status 400 and json string on invalid new user signup', () => {
27+
return request(server)
28+
.post('/signup')
29+
.send({
30+
username: 'reactyp3test',
31+
32+
password: 'password',
33+
})
34+
.expect('Content-Type', /json/)
35+
.expect(400)
36+
.then((res) => expect(typeof res.body).toBe('string'));
37+
});
38+
});
39+
});
40+
describe('/login', () => {
41+
describe('POST', () => {
42+
it('responds with status 200 and json object on verified user login', () => {
43+
return request(server)
44+
.post('/login')
45+
.send({ username: 'reactyp3test', password: 'codesmith1!' })
46+
.expect(200)
47+
.expect('Content-Type', /json/)
48+
.then((res) =>
49+
expect(res.body.sessionId).toEqual('5f0df0636678ba002ba43b88')
50+
);
51+
});
52+
it('responds with status 400 and json string on invalid user login', () => {
53+
return request(server)
54+
.post('/login')
55+
.send({ username: 'wrongusername', password: 'wrongpassword' })
56+
.expect(400)
57+
.expect('Content-Type', /json/)
58+
.then((res) => expect(typeof res.body).toBe('string'));
59+
});
60+
});
61+
});
62+
});
63+
64+
describe('User Login Tests', () => {});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const cookieController = {};
2+
3+
// setSSIDCookie - store the user id from database in cookie
4+
cookieController.setSSIDCookie = (req, res, next) => {
5+
// set cookie with key 'ssid' and value to user's id
6+
res.cookie('ssid', res.locals.id);
7+
return next();
8+
};
9+
10+
module.exports = cookieController;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
const { Projects } = require('../models/reactypeModels');
2+
const projectController = {};
3+
4+
// saveProject saves current workspace to database
5+
6+
projectController.saveProject = (req, res, next) => {
7+
// pull project name and project itself from body
8+
const { name, project, userId } = req.body;
9+
// pull ssid from cookies for user id
10+
Projects.findOneAndUpdate(
11+
// looks in projects collection for project by user and name
12+
{ name, userId },
13+
// update or insert the project
14+
{ project },
15+
// Options:
16+
// upsert: true - if none found, inserts new project, if found, updates project
17+
// new: true - returns updated document not the original one
18+
{ upsert: true, new: true },
19+
(err, result) => {
20+
if (err) {
21+
return next({
22+
log: `Error in projectController.saveProject: ${err}`,
23+
message: {
24+
err: `Error in projectController.saveProject, check server logs for details`,
25+
},
26+
});
27+
} else {
28+
res.locals.savedProject = result;
29+
return next();
30+
}
31+
}
32+
);
33+
};
34+
35+
// gets all of current user's projects
36+
37+
projectController.getProjects = (req, res, next) => {
38+
const { userId } = req.body;
39+
Projects.find({ userId }, (err, projects) => {
40+
if (err) {
41+
return next({
42+
log: `Error in projectController.getProjects: ${err}`,
43+
message: {
44+
err: `Error in projectController.getProjects, check server logs for details`,
45+
},
46+
});
47+
} else {
48+
// so it returns each project like it is in state, not the whole object in DB
49+
res.locals.projects = projects.map((elem) => elem.project);
50+
return next();
51+
}
52+
});
53+
};
54+
55+
// delete project from database **currently not integrated into app**
56+
57+
projectController.deleteProject = (req, res, next) => {
58+
console.log('In delete projects controller');
59+
// pull project name and userId from req.body
60+
const { name, userId } = req.body;
61+
console.log('name and userId in delete', name, userId);
62+
Projects.findOneAndDelete({ name, userId }, (err, deleted) => {
63+
if (err) {
64+
return next({
65+
log: `Error in projectController.deleteProject: ${err}`,
66+
message: {
67+
err: `Error in projectController.deleteProject, check server logs for details`,
68+
},
69+
});
70+
} else {
71+
res.locals.deleted = deleted;
72+
console.log('Successful deleteProjects, deleted', deleted);
73+
return next();
74+
}
75+
});
76+
};
77+
78+
module.exports = projectController;
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
const { Sessions } = require('../models/reactypeModels');
2+
const sessionController = {};
3+
4+
// isLoggedIn finds appropriate session for this request in database, then verifies whether or not the session is still valid
5+
sessionController.isLoggedIn = (req, res, next) => {
6+
let cookieId;
7+
if (req.cookies.ssid) {
8+
cookieId = req.cookies.ssid;
9+
} else {
10+
cookieId = req.body.userId;
11+
}
12+
13+
// find session from request session ID in mongodb
14+
Sessions.findOne({ cookieId }, (err, session) => {
15+
if (err) {
16+
return next({
17+
log: `Error in sessionController.isLoggedIn: ${err}`,
18+
message: {
19+
err: `Error in sessionController.isLoggedIn, check server logs for details`,
20+
},
21+
});
22+
// no session found, redirect to signup page
23+
} else if (!session) {
24+
return res.redirect('/');
25+
} else {
26+
// session found, move onto next middleware
27+
return next();
28+
}
29+
});
30+
};
31+
32+
// startSession - create and save a new session into the database
33+
sessionController.startSession = (req, res, next) => {
34+
// first check if user is logged in already
35+
Sessions.findOne({ cookieId: res.locals.id }, (err, session) => {
36+
if (err) {
37+
return next({
38+
log: `Error in sessionController.startSession find session: ${err}`,
39+
message: {
40+
err: `Error in sessionController.startSession find session, check server logs for details`,
41+
},
42+
});
43+
// if session doesn't exist, create a session
44+
// if valid user logged in/signed up, res.locals.id should be user's id generated from mongodb, which we will set as this session's cookieId
45+
} else if (!session) {
46+
Sessions.create({ cookieId: res.locals.id }, (err, session) => {
47+
if (err) {
48+
return next({
49+
log: `Error in sessionController.startSession create session: ${err}`,
50+
message: {
51+
err: `Error in sessionController.startSession create session, check server logs for details`,
52+
},
53+
});
54+
} else {
55+
res.locals.ssid = session.cookieId;
56+
return next();
57+
}
58+
});
59+
// if session exists, move onto next middleware
60+
} else {
61+
res.locals.ssid = session.cookieId;
62+
return next();
63+
}
64+
});
65+
};
66+
67+
// creates a session when logging in with github
68+
sessionController.githubSession = (req, res, next) => {
69+
// req.user is passed in from passport js -> serializeuser/deserializeuser
70+
const cookieId = req.user._id;
71+
Sessions.findOne({ cookieId }, (err, session) => {
72+
if (err) {
73+
return next({
74+
log: `Error in sessionController.githubSession find session: ${err}`,
75+
message: {
76+
err: `Error in sessionController.githubSession find session, check server logs for details`,
77+
},
78+
});
79+
} else if (!session) {
80+
Sessions.create({ cookieId }, (err, session) => {
81+
if (err) {
82+
return next({
83+
log: `Error in sessionController.githubSession create session: ${err}`,
84+
message: {
85+
err: `Error in sessionController.githubSession create session, check server logs for details`,
86+
},
87+
});
88+
} else {
89+
res.locals.id = session.cookieId;
90+
return next();
91+
}
92+
});
93+
} else {
94+
res.locals.id = session.cookieId;
95+
return next();
96+
}
97+
});
98+
};
99+
100+
module.exports = sessionController;

0 commit comments

Comments
 (0)