diff --git a/.gitignore b/.gitignore
index d241c34..c3f62c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,4 +10,7 @@ package-lock.json
npm-debug.log*
# output from webpack
-dist/
\ No newline at end of file
+dist/
+
+#environment file
+.env
\ No newline at end of file
diff --git a/client/src/components/AddSnippet/AddSnippet.jsx b/client/src/components/AddSnippet/AddSnippet.jsx
index efebf01..7171ba7 100644
--- a/client/src/components/AddSnippet/AddSnippet.jsx
+++ b/client/src/components/AddSnippet/AddSnippet.jsx
@@ -32,15 +32,15 @@ const AddSnippet = ({ closeModal }) => {
fetch('/snippets', {
method: 'POST',
headers: {
- 'Content-Type': 'application/json',
+ 'Content-Type': 'application/json'
},
body: JSON.stringify({
title: title,
language: language,
comments: comments,
tags: tagList,
- storedCode: storedCode,
- }),
+ storedCode: storedCode
+ })
})
.then((data) => data.json())
.catch((err) => {
@@ -48,7 +48,6 @@ const AddSnippet = ({ closeModal }) => {
console.log('failed saving snippet');
});
-
// setTitle('');
// setLanguage('');
// setComments('');
@@ -72,12 +71,13 @@ const AddSnippet = ({ closeModal }) => {
centered
>
- Add a snippet
+
+ Add a snippet
+
-
{
setTitle(e.target.value);
}}
>
- {error && Title is required!}
+ {error && Title is required!}
-
- {openModal && }
+ {openModal && }
@@ -154,7 +154,6 @@ const AddSnippet = ({ closeModal }) => {
Save
-
diff --git a/client/src/components/SnippetDisplay/SnippetDisplay.jsx b/client/src/components/SnippetDisplay/SnippetDisplay.jsx
index 2bba1ca..6d0b593 100644
--- a/client/src/components/SnippetDisplay/SnippetDisplay.jsx
+++ b/client/src/components/SnippetDisplay/SnippetDisplay.jsx
@@ -5,14 +5,20 @@ import CodeMirror from '@uiw/react-codemirror';
import styles from './SnippetDisplay.module.scss';
import { langs } from '@uiw/codemirror-extensions-langs';
import TagInput from '../../components/ui/TagInput/TagInput';
-import {Card, Button} from 'react-bootstrap';
+import { Card, Button } from 'react-bootstrap';
const SnippetDisplay = ({ selectedSnippet, getSnippet }) => {
// indSnippet = this.props
// create delete method using fetch request
let snippetTitle = selectedSnippet.title ? selectedSnippet.title : '';
- let snippetLanguage = selectedSnippet.language ? selectedSnippet.language : '';
- let snippetComments = selectedSnippet.comments ? selectedSnippet.comments : '';
- let snippetStoredCode = selectedSnippet.storedCode ? selectedSnippet.storedCode : '';
+ let snippetLanguage = selectedSnippet.language
+ ? selectedSnippet.language
+ : '';
+ let snippetComments = selectedSnippet.comments
+ ? selectedSnippet.comments
+ : '';
+ let snippetStoredCode = selectedSnippet.storedCode
+ ? selectedSnippet.storedCode
+ : '';
let snippetTagList = selectedSnippet.tags ? selectedSnippet.tags : [];
// create a state variable for each passed down state and the its setState function
@@ -24,8 +30,8 @@ const SnippetDisplay = ({ selectedSnippet, getSnippet }) => {
const [editButtonState, setEditButtonState] = useState(false);
const deleteSnippet = (id) => {
- fetch (`http://localhost:3000/snippets?id=${id}`, {
- method: 'DELETE',
+ fetch(`http://localhost:3000/snippets?id=${id}`, {
+ method: 'DELETE'
})
.then((response) => {
if (response.ok) {
@@ -33,13 +39,13 @@ const SnippetDisplay = ({ selectedSnippet, getSnippet }) => {
}
})
.catch((err) => {
- return ({
+ return {
log: `SnippetDisiplay.deleteSnippet: Error: ${err}`,
status: err.status || 500,
message: 'There was an error deleting snippet.'
- })
- })
- }
+ };
+ });
+ };
const editSnippet = (id) => {
// const [oldState, setOldState] = React.useState([]);
@@ -54,7 +60,7 @@ const SnippetDisplay = ({ selectedSnippet, getSnippet }) => {
};
// within fetch request (post)
// body: JSON.stringify(created object)
- fetch (`/snippets?id=${id}`, {
+ fetch(`/snippets?id=${id}`, {
method: 'PUT',
body: JSON.stringify(updatedSnippet)
})
@@ -63,65 +69,64 @@ const SnippetDisplay = ({ selectedSnippet, getSnippet }) => {
getSnippet();
})
.catch((err) => {
- return ({
+ return {
log: `SnippetDisplay.editSnippet: Error: ${err}`,
status: err.status || 500,
message: 'There was an error editing code snippet.'
- });
+ };
});
};
- // copy code state
+ // copy code state
const [copied, setCopied] = useState(false);
-
const checkEdit = () => {
if (editButtonState === true) {
- return(
+ return (
-
);
}
@@ -148,60 +152,71 @@ const SnippetDisplay = ({ selectedSnippet, getSnippet }) => {
if (editButtonState === false) {
return (
-
-
-
Title: {snippetTitle}
-
Language: {snippetLanguage}
-
Comments: {snippetComments}
-
+
+
+ {' '}
+ Title: {snippetTitle}
+
+
+ {' '}
+ Language: {snippetLanguage}
+
+
+ {' '}
+ Comments: {snippetComments}
+
+
{/*
{renderTags()}
*/}
{\n console.log(\'Hello World!)\n}'}
options={{
- readOnly: true,
+ readOnly: true
}}
- onChange={(e) => snippetStoredCode = (e)}
+ onChange={(e) => (snippetStoredCode = e)}
>
setCopied(true)}
>
-
+
-
);
}
};
-
+
return (
- <>
-
- {checkEdit()}
+ <>
+
+ {checkEdit()}
-
-
-
-
+
+
+
+
>
);
diff --git a/package.json b/package.json
index f13e347..a287341 100644
--- a/package.json
+++ b/package.json
@@ -17,12 +17,13 @@
"bootstrap": "^5.2.3",
"codemirror": "^6.0.1",
"cors": "^2.8.5",
+ "dotenv": "^16.0.3",
"express": "^4.18.2",
"mongodb": "^5.5.0",
"mongoose": "^7.1.1",
"react": "^18.2.0",
- "react-copy-to-clipboard": "^5.1.0",
"react-bootstrap": "^2.7.4",
+ "react-copy-to-clipboard": "^5.1.0",
"react-dnd": "^14.0.5",
"react-dnd-html5-backend": "^14.1.0",
"react-dom": "^18.2.0",
diff --git a/server/controllers/authenticationController.js b/server/controllers/authenticationController.js
new file mode 100644
index 0000000..4e200ab
--- /dev/null
+++ b/server/controllers/authenticationController.js
@@ -0,0 +1,51 @@
+const User = require('../models/userModel.js');
+const authenticationController = {};
+
+//Error creator method specific to this controller
+const createError = (method, log, status, message = log) => {
+ return {
+ log: `Error occurred in authenticationController.${method}: ${log}`,
+ status,
+ message: { err: message }
+ };
+};
+
+//Authentication and user creation methods go here.
+//Feel free to change these as you see fit -- I just have them in here for testing purposes.
+authenticationController.createUser = (req, res, next) => {
+ const { username, password } = req.body;
+ User.create(
+ //Information goes here...
+ { username, password }
+ )
+ .then((user) => {
+ res.locals.newUserInfo = user;
+ return next();
+ })
+ .catch((err) => {
+ return next(
+ createError(
+ 'createUser',
+ `Error creating user with provided credentials: ${err}`,
+ 500
+ )
+ );
+ });
+};
+
+authenticationController.getUserData = (req, res, next) => {
+ const { _id } = req.query;
+ User.findById(_id)
+ .exec()
+ .then((user) => {
+ res.locals.userData = user;
+ return next();
+ })
+ .catch((err) => {
+ return next(
+ createError('getUserData', `Error retrieving user data: ${err}`, 500)
+ );
+ });
+};
+
+module.exports = authenticationController;
diff --git a/server/controllers/snippetsController.js b/server/controllers/snippetsController.js
index 864cf42..52f3efd 100644
--- a/server/controllers/snippetsController.js
+++ b/server/controllers/snippetsController.js
@@ -1,13 +1,26 @@
const User = require('../models/userModel.js');
-
+const Snippet = require('../models/snippetModel.js');
const snippetsController = {};
-snippetsController.getSnippets = (req, res, next) => {
- const userId = '645fee9104d1f0acef95a002';
+//Error creator method specific to this controller
+const createError = (method, log, status, message = log) => {
+ return {
+ log: `Error occurred in snippetsController.${method}: ${log}`,
+ status,
+ message: { err: message }
+ };
+};
+
+//Retrieves all snippets associated with a user by looking up user (by ID) and referencing all snippets in the associated list
+snippetsController.getSnippetsByUser = (req, res, next) => {
+ const { _id } = req.query;
+ //const userId = '645fee9104d1f0acef95a002';
- User.findOne({ _id: userId })
+ User.findById(_id)
+ .populate('snippets')
+ .exec()
.then((user) => {
- res.locals.allSnippets = user;
+ res.locals.allSnippets = user.snippets;
return next();
})
.catch((err) => {
@@ -16,100 +29,140 @@ snippetsController.getSnippets = (req, res, next) => {
});
};
+//Creates snippet under Snippets collection
snippetsController.createSnippet = (req, res, next) => {
const { title, comments, storedCode, tags, language } = req.body;
const snippet = { title, comments, storedCode, tags, language };
- const userId = '645fee9104d1f0acef95a002';
+ Snippet.create(snippet)
+ .then((doc) => {
+ res.locals.newSnippet = doc;
+ return next();
+ })
+ .catch((err) => {
+ return next(
+ createError('createSnippet', `Error creating new snippet: ${err}`, 500)
+ );
+ });
+};
+//Associates snippet with a particular user
+snippetsController.saveSnippetToUser = (req, res, next) => {
+ const userId = req.body.userId;
User.findById(userId)
.then((user) => {
- // Increment the lastId and assign it to the new snippet
- const newSnippetId = user.lastId + 1;
- user.lastId = newSnippetId;
-
- // Create the new snippet object with the assigned ID
- const newSnippet = {
- id: newSnippetId,
- ...snippet,
- };
-
- // Push the new snippet to the snippets array
- user.snippets.push(newSnippet);
-
- const [tags, languages] = recalcTagsAndLang(user);
- user.tags = tags;
- user.languages = languages;
-
- // Save the updated user document
- return user.save().then((updatedUser) => {
- res.locals.createdSnippet = newSnippet;
- next();
- });
+ user.snippets.push(res.locals.newSnippet._id);
+ user
+ .save()
+ .then((r) => {
+ res.locals.updatedUserRecord = r;
+ return next();
+ })
+ .catch((err) => {
+ return next(
+ createError(
+ 'saveSnippetToUser',
+ `Encountered error when saving new snippet to user: ${err}`,
+ 500
+ )
+ );
+ });
})
- .catch((error) => {
- console.error('Creating a snippet has failed:', error);
- next(error);
+ .catch((err) => {
+ return next(
+ createError(
+ 'saveSnippetToUser',
+ `Encountered error when retrieving user record: ${err}`,
+ 500
+ )
+ );
});
};
-
+
+//Updates snippet with provided properties
snippetsController.updateSnippet = (req, res, next) => {
- const { id, title, comments, storedCode, tags, language } = req.body;
- const updatedSnippet = { id, title, comments, storedCode, tags, language };
- const userId = '645fee9104d1f0acef95a002';
-
- User.findOneAndUpdate(
- { _id: userId, 'snippets.id': updatedSnippet.id },
- {
- $set: { 'snippets.$': updatedSnippet },
- },
- { new: true }
+ const { _id, title, comments, storedCode, tags, language } = req.body;
+ const updatedSnippet = { title, comments, storedCode, tags, language };
+
+ for (const key in updatedSnippet) {
+ if (!updatedSnippet[key]) {
+ delete updatedSnippet[key];
+ }
+ }
+
+ //Need to work out how best to update user tags, languages under this new approach
+
+ Snippet.findByIdAndUpdate(
+ _id,
+ { ...updatedSnippet },
+ { new: true, upsert: true }
)
- .then((updatedUser) => {
- const [tags, languages] = recalcTagsAndLang(updatedUser);
- updatedUser.tags = tags;
- updatedUser.languages = languages;
- return updatedUser.save();
- })
- .then((savedUser) => {
- res.locals.updatedSnippet = updatedSnippet;
- next();
+ .exec()
+ .then((result) => {
+ res.locals.updatedSnippet = result;
+ return next();
})
.catch((err) => {
- console.log('Updating the snippet has failed:', err);
- next(err);
+ return next(
+ createError(
+ '.updateSnippet',
+ `Encountered error while updating snippet: ${err}`,
+ 500
+ )
+ );
});
};
+//Deletes snippet with provided ID and removes from users with associated ID
snippetsController.deleteSnippet = (req, res, next) => {
- const { id } = req.query;
- const userId = '645fee9104d1f0acef95a002';
-
- User.findOne({ _id: userId })
- .then((user) => {
- const deletedSnippet = user.snippets.find((snippet) => {
- return `${snippet.id}` === id;
- });
-
- // Remove the snippet from the user's snippets array
- user.snippets = user.snippets.filter((snippet) => `${snippet.id}` !== id);
-
- //recalculate the tags and languages.
- const [tags, languages] = recalcTagsAndLang(user);
- user.tags = tags;
- user.languages = languages;
-
- // Save the updated user document
- return user.save().then(() => {
- res.locals.deletedSnippet = deletedSnippet;
- next();
- });
+ const { uid, sid } = req.query;
+ Snippet.findByIdAndDelete(sid)
+ .exec()
+ .then((result) => {
+ res.locals.deletedSnippet = result;
+ })
+ .then(() => {
+ User.findById(uid)
+ .exec()
+ .then((user) => {
+ user.snippets = user.snippets.filter((el) => el != uid);
+ user
+ .save()
+ .then((updatedUser) => {
+ res.locals.updatedUserRecord = updatedUser;
+ return next();
+ })
+ .catch((err) => {
+ return next(
+ createError(
+ 'deleteSnippet',
+ `Encountered error while saving updated user snippets list: ${err}`,
+ 500
+ )
+ );
+ });
+ })
+ .catch((err) => {
+ return next(
+ createError(
+ 'deleteSnippet',
+ `Encountered error while retrieving user with provided ID: ${err}`,
+ 500
+ )
+ );
+ });
})
- .catch((error) => {
- console.error('Error deleting snippet:', error);
- next(error);
+ .catch((err) => {
+ return next(
+ createError(
+ 'deleteSnippet',
+ `Encountered error while attempting to delete snippet with provided ID: ${err}`,
+ 500
+ )
+ );
});
};
-// helper function to re-calculate taglist/language counts?
+
+//Not using this at present...
const recalcTagsAndLang = function (user) {
const tagList = {};
const languageList = {};
@@ -134,3 +187,98 @@ const recalcTagsAndLang = function (user) {
};
module.exports = snippetsController;
+
+// snippetsController.createSnippet = (req, res, next) => {
+// const { title, comments, storedCode, tags, language } = req.body;
+// const snippet = { title, comments, storedCode, tags, language };
+// const userId = '645fee9104d1f0acef95a002';
+
+// User.findById(userId)
+// .then((user) => {
+// // Increment the lastId and assign it to the new snippet
+// const newSnippetId = user.lastId + 1;
+// user.lastId = newSnippetId;
+
+// // Create the new snippet object with the assigned ID
+// const newSnippet = {
+// id: newSnippetId,
+// ...snippet
+// };
+
+// // Push the new snippet to the snippets array
+// user.snippets.push(newSnippet);
+
+// const [tags, languages] = recalcTagsAndLang(user);
+// user.tags = tags;
+// user.languages = languages;
+
+// // Save the updated user document
+// return user.save().then((updatedUser) => {
+// res.locals.createdSnippet = newSnippet;
+// next();
+// });
+// })
+// .catch((error) => {
+// console.error('Creating a snippet has failed:', error);
+// next(error);
+// });
+// };
+
+// snippetsController.updateSnippet = (req, res, next) => {
+// const { id, title, comments, storedCode, tags, language } = req.body;
+// const updatedSnippet = { id, title, comments, storedCode, tags, language };
+// const userId = '645fee9104d1f0acef95a002';
+
+// User.findOneAndUpdate(
+// { _id: userId, 'snippets.id': updatedSnippet.id },
+// {
+// $set: { 'snippets.$': updatedSnippet }
+// },
+// { new: true }
+// )
+// .then((updatedUser) => {
+// const [tags, languages] = recalcTagsAndLang(updatedUser);
+// updatedUser.tags = tags;
+// updatedUser.languages = languages;
+// return updatedUser.save();
+// })
+// .then((savedUser) => {
+// res.locals.updatedSnippet = updatedSnippet;
+// next();
+// })
+// .catch((err) => {
+// console.log('Updating the snippet has failed:', err);
+// next(err);
+// });
+// };
+
+// snippetsController.deleteSnippet = (req, res, next) => {
+// const { id } = req.query;
+// const userId = '645fee9104d1f0acef95a002';
+
+// User.findOne({ _id: userId })
+// .then((user) => {
+// const deletedSnippet = user.snippets.find((snippet) => {
+// return `${snippet.id}` === id;
+// });
+
+// // Remove the snippet from the user's snippets array
+// user.snippets = user.snippets.filter((snippet) => `${snippet.id}` !== id);
+
+// //recalculate the tags and languages.
+// const [tags, languages] = recalcTagsAndLang(user);
+// user.tags = tags;
+// user.languages = languages;
+
+// // Save the updated user document
+// return user.save().then(() => {
+// res.locals.deletedSnippet = deletedSnippet;
+// next();
+// });
+// })
+// .catch((error) => {
+// console.error('Error deleting snippet:', error);
+// next(error);
+// });
+// };
+// helper function to re-calculate taglist/language counts?
diff --git a/server/models/snippetModel.js b/server/models/snippetModel.js
new file mode 100644
index 0000000..a3a1a39
--- /dev/null
+++ b/server/models/snippetModel.js
@@ -0,0 +1,12 @@
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+
+const snippetSchema = new Schema({
+ title: { type: String, required: true },
+ comments: { type: String },
+ storedCode: { type: String },
+ tags: [String],
+ language: { type: String }
+});
+
+module.exports = mongoose.model('Snippet', snippetSchema);
diff --git a/server/models/tagModel.js b/server/models/tagModel.js
new file mode 100644
index 0000000..e69de29
diff --git a/server/models/userModel.js b/server/models/userModel.js
index a29070c..68cbf2c 100644
--- a/server/models/userModel.js
+++ b/server/models/userModel.js
@@ -4,32 +4,17 @@ const Schema = mongoose.Schema;
const userSchema = new Schema({
username: { type: String, required: true },
password: { type: String, required: true },
- tags: {
- type: Object,
- default: {},
- },
- languages: {
- type: Object,
- default: {},
- },
-
- lastId: { type: Number, default: 0 },
-
+ tags: { type: [String], default: [] },
+ languages: { type: [String], default: [] },
snippets: {
type: [
{
- id: { type: Number, required: true },
- type: Object,
- title: { type: String, required: true },
- comments: { type: String },
- storedCode: { type: String },
-
- tags: [String],
- language: { type: String },
- },
+ type: Schema.Types.ObjectId,
+ ref: 'Snippet'
+ }
],
- default: [],
- },
+ default: []
+ }
});
module.exports = mongoose.model('User', userSchema);
diff --git a/server/routes/authenticationRouter.js b/server/routes/authenticationRouter.js
new file mode 100644
index 0000000..fb92d4e
--- /dev/null
+++ b/server/routes/authenticationRouter.js
@@ -0,0 +1,13 @@
+const express = require('express');
+const router = express.Router();
+
+const authenticationController = require('../controllers/authenticationController');
+
+router.post('/', authenticationController.createUser, (req, res) => {
+ res.status(200).json(res.locals.newUserInfo);
+});
+
+router.get('/', authenticationController.getUserData, (req, res) => {
+ res.status(200).json(res.locals.userData);
+});
+module.exports = router;
diff --git a/server/routes/snippets.js b/server/routes/snippetsRouter.js
similarity index 70%
rename from server/routes/snippets.js
rename to server/routes/snippetsRouter.js
index 4e1b5c2..3b5571c 100644
--- a/server/routes/snippets.js
+++ b/server/routes/snippetsRouter.js
@@ -4,12 +4,15 @@ const snippetsController = require('../controllers/snippetsController');
const router = express.Router();
-router.get('/', snippetsController.getSnippets, (req, res) =>
+router.get('/', snippetsController.getSnippetsByUser, (req, res) =>
res.status(200).json(res.locals.allSnippets)
);
-router.post('/', snippetsController.createSnippet, (req, res) =>
- res.status(200).json(res.locals.createdSnippet)
+router.post(
+ '/',
+ snippetsController.createSnippet,
+ snippetsController.saveSnippetToUser,
+ (req, res) => res.status(200).json(res.locals.newSnippet)
);
router.put('/', snippetsController.updateSnippet, (req, res) =>
diff --git a/server/server.js b/server/server.js
index 855cf32..528aa96 100644
--- a/server/server.js
+++ b/server/server.js
@@ -1,37 +1,43 @@
const path = require('path');
const express = require('express');
-const app = express();
const mongoose = require('mongoose');
const cors = require('cors');
+const snippetsRouter = require('./routes/snippetsRouter');
+const authenticationRouter = require('./routes/authenticationRouter');
-const port = process.env.PORT || 3000;
+require('dotenv').config();
-const mongoURI =
- 'mongodb+srv://paaoul:Melikeit1@scratchcluster.igf2bag.mongodb.net/';
+//Create express app and set constants
+const app = express();
+const port = process.env.PORT || 3000;
+//Get mongoURI and connect to DB
+const mongoURI = process.env.MONGO_URI;
mongoose.connect(mongoURI);
-const snippetsRouter = require('./routes/snippets');
-
+//Call default middleware
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
+//Point relevant requests to snippet and authentication routers
app.use('/snippets', snippetsRouter);
+app.use('/authentication', authenticationRouter);
+//Handle requests to invalid endpoints and middleware errors
app.use((req, res) => res.status(404).send('Invalid endpoint'));
-
app.use((err, req, res, next) => {
const defaultErr = {
log: 'Express error handler caught unknown middleware error',
status: 500,
- message: { err: 'An error occurred' },
+ message: { err: 'An error occurred' }
};
const errorObj = Object.assign({}, defaultErr, err);
console.log(errorObj.log);
return res.status(errorObj.status).json(errorObj.message);
});
+//Get 'er goin'
app.listen(port, () => {
console.log(`Server listening on port ${port}...`);
});