dockerized the project for dev
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
# The first FROM is now a stage called build-stage
|
||||||
|
|
||||||
|
FROM node:20 AS build-stage
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# This is a new stage, everything before this is gone, except for the files that we want to COPY
|
||||||
|
|
||||||
|
FROM nginx:1.25-alpine
|
||||||
|
|
||||||
|
# COPY the directory dist from the build-stage to /usr/share/nginx/html
|
||||||
|
# The target location here was found from the Docker hub page
|
||||||
|
|
||||||
|
COPY --from=build-stage /usr/src/app/build /usr/share/nginx/html
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"main.css": "/static/css/main.cb2b37d1.css",
|
||||||
|
"main.js": "/static/js/main.d19fb767.js",
|
||||||
|
"index.html": "/index.html",
|
||||||
|
"main.cb2b37d1.css.map": "/static/css/main.cb2b37d1.css.map",
|
||||||
|
"main.d19fb767.js.map": "/static/js/main.d19fb767.js.map"
|
||||||
|
},
|
||||||
|
"entrypoints": [
|
||||||
|
"static/css/main.cb2b37d1.css",
|
||||||
|
"static/js/main.d19fb767.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<!doctype html><html lang="en"><head><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><script defer="defer" src="/static/js/main.d19fb767.js"></script><link href="/static/css/main.cb2b37d1.css" rel="stylesheet"></head><body><div id="root"></div></body></html>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
.datePicker :hover{border-color:"#212121"}.datePicker :focus{border-color:"#1976d2";border-width:"2px"}.datePicker{background:"red";color:"red"}.colormebaby{background-color:"red";color:"red"}
|
||||||
|
/*# sourceMappingURL=main.cb2b37d1.css.map*/
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"static/css/main.cb2b37d1.css","mappings":"AAAA,mBACE,sBACF,CACA,mBACE,sBAAuB,CACvB,kBACF,CACA,YAEE,gBAAgB,CADhB,WAEF,CAEA,aACE,sBAAsB,CACtB,WACF","sources":["App2.css"],"sourcesContent":[".datePicker :hover {\n border-color: \"#212121\";\n}\n.datePicker :focus {\n border-color: \"#1976d2\";\n border-width: \"2px\";\n}\n.datePicker{\n color:\"red\";\n background:\"red\";\n}\n\n.colormebaby{\n background-color:\"red\";\n color:\"red\"\n}\n"],"names":[],"sourceRoot":""}
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,121 @@
|
|||||||
|
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* react-dom.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* react-is.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* react-jsx-runtime.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* react.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* scheduler.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* use-sync-external-store-shim.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @license React
|
||||||
|
* use-sync-external-store-shim/with-selector.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @mui/styled-engine v5.13.2
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @remix-run/router v1.7.2
|
||||||
|
*
|
||||||
|
* Copyright (c) Remix Software Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE.md file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React Router DOM v6.14.2
|
||||||
|
*
|
||||||
|
* Copyright (c) Remix Software Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE.md file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React Router v6.14.2
|
||||||
|
*
|
||||||
|
* Copyright (c) Remix Software Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE.md file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @license React v16.13.1
|
||||||
|
* react-is.production.min.js
|
||||||
|
*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
FROM node:20
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
CMD npm start
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: devcon-frontend-dev
|
||||||
|
build:
|
||||||
|
context: . # The context will pick this directory as the "build context"
|
||||||
|
dockerfile: dev.Dockerfile # This will simply tell which dockerfile to read
|
||||||
|
volumes:
|
||||||
|
- ./:/usr/src/app # The path can be relative, so ./ is enough to say "the same location as the docker-compose.yml"
|
||||||
|
ports:
|
||||||
|
- 5173:3000
|
||||||
|
container_name: devcon-front-dev
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: todo-front-dev
|
||||||
|
build:
|
||||||
|
context: . # The context will pick this directory as the "build context"
|
||||||
|
dockerfile: dev.Dockerfile # This will simply tell which dockerfile to read
|
||||||
|
volumes:
|
||||||
|
- ./:/usr/src/app # The path can be relative, so ./ is enough to say "the same location as the docker-compose.yml"
|
||||||
|
ports:
|
||||||
|
- 5173:5173
|
||||||
|
container_name: todo-front
|
||||||
+1
-1
@@ -53,5 +53,5 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/uuid": "^9.0.2"
|
"@types/uuid": "^9.0.2"
|
||||||
},
|
},
|
||||||
"proxy": "http://localhost:5000"
|
"proxy": "http://server:5000"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const Navbar = () => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(
|
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(
|
||||||
null
|
null,
|
||||||
);
|
);
|
||||||
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorElNav(event.currentTarget);
|
setAnchorElNav(event.currentTarget);
|
||||||
@@ -73,7 +73,7 @@ const Navbar = () => {
|
|||||||
<AdbIcon sx={{ display: { xs: "none", md: "flex" }, mr: 1 }} />
|
<AdbIcon sx={{ display: { xs: "none", md: "flex" }, mr: 1 }} />
|
||||||
{/*LOGO link for bi display*/}
|
{/*LOGO link for bi display*/}
|
||||||
<Typography
|
<Typography
|
||||||
variant="h6"
|
variant="h5"
|
||||||
noWrap
|
noWrap
|
||||||
sx={{
|
sx={{
|
||||||
mr: 2,
|
mr: 2,
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
const mongoose = require('mongoose');
|
|
||||||
const config = require('config');
|
|
||||||
const db = config.get('mongoURI');
|
|
||||||
|
|
||||||
const connectDB = async () => {
|
|
||||||
try {
|
|
||||||
await mongoose.connect(db, {
|
|
||||||
useNewUrlParser: true,
|
|
||||||
useUnifiedTopology: true
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('MongoDB Connected...');
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
// Exit process with failure
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = connectDB;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import mongoose from "mongoose";
|
|
||||||
import config from 'config'
|
|
||||||
|
|
||||||
const db = config.get('mongoURI');
|
|
||||||
|
|
||||||
const connectDB = async () => {
|
|
||||||
try {
|
|
||||||
if (typeof db === 'string')
|
|
||||||
await mongoose.connect(db);
|
|
||||||
|
|
||||||
console.log('MongoDB Connected...');
|
|
||||||
} catch (err: unknown) {
|
|
||||||
if (typeof err === 'string')
|
|
||||||
console.error(err)
|
|
||||||
else if (err instanceof Error)
|
|
||||||
console.error(err.message);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connectDB
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: devcon-frontend-dev
|
||||||
|
build:
|
||||||
|
context: ./client
|
||||||
|
dockerfile: ./dev.Dockerfile
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
volumes:
|
||||||
|
- ./client:/usr/src/app
|
||||||
|
|
||||||
|
server:
|
||||||
|
image: devcon-backend-dev
|
||||||
|
build:
|
||||||
|
context: ./server
|
||||||
|
dockerfile: ./dev.Dockerfile
|
||||||
|
ports:
|
||||||
|
- 5000:5000
|
||||||
|
volumes:
|
||||||
|
- ./server:/usr/src/app
|
||||||
|
environment:
|
||||||
|
MONGO_URL: "mongodb://the_username:the_password@mongo:27017/the_database"
|
||||||
|
|
||||||
|
mongo:
|
||||||
|
image: mongo
|
||||||
|
ports:
|
||||||
|
- 3456:27017
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: root
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: example
|
||||||
|
MONGO_INITDB_DATABASE: the_database
|
||||||
|
volumes:
|
||||||
|
- ./mongo/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js
|
||||||
|
- mongo_data:/data/db
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mongo_data:
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
services:
|
||||||
|
mongo:
|
||||||
|
image: mongo
|
||||||
|
ports:
|
||||||
|
- 3456:27017
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: root
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: example
|
||||||
|
MONGO_INITDB_DATABASE: the_database
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- ./mongo/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
const config = require('config');
|
|
||||||
const jwt = require('jsonwebtoken');
|
|
||||||
|
|
||||||
module.exports = function (req, res, next) {
|
|
||||||
|
|
||||||
// Get token from header
|
|
||||||
const token = req.header('x-auth-token');
|
|
||||||
// Check if not token
|
|
||||||
if (!token) {
|
|
||||||
return res.status(401).json({ msg: 'No token, authorization denied' });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify token
|
|
||||||
try {
|
|
||||||
jwt.verify(token, config.get('jwtSecret'), (error, decoded) => {
|
|
||||||
if (error) {
|
|
||||||
return res.status(401).json({ msg: 'Token is not valid' });
|
|
||||||
} else {
|
|
||||||
req.user = decoded.user;
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error('something wrong with auth middleware');
|
|
||||||
res.status(500).json({ msg: 'Server Error' });
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
const mongoose = require('mongoose');
|
|
||||||
// middleware to check for a valid object id
|
|
||||||
const checkObjectId = (idToCheck) => (req, res, next) => {
|
|
||||||
if (!mongoose.Types.ObjectId.isValid(req.params[idToCheck]))
|
|
||||||
return res.status(400).json({ msg: 'Invalid ID' });
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = checkObjectId;
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
const mongoose = require("mongoose");
|
|
||||||
const Schema = mongoose.Schema;
|
|
||||||
|
|
||||||
const PostSchema = new Schema({
|
|
||||||
user: {
|
|
||||||
type: Schema.Types.ObjectId,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
avatar: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
likes: [
|
|
||||||
{
|
|
||||||
user: {
|
|
||||||
type: Schema.Types.ObjectId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
comments: [
|
|
||||||
{
|
|
||||||
user: {
|
|
||||||
type: Schema.Types.ObjectId,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
avatar: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
date: {
|
|
||||||
type: Date,
|
|
||||||
default: Date.now,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
date: {
|
|
||||||
type: Date,
|
|
||||||
default: Date.now,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = mongoose.model("post", PostSchema);
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
const mongoose = require("mongoose");
|
|
||||||
|
|
||||||
const ProfileSchema = new mongoose.Schema({
|
|
||||||
user: {
|
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
|
||||||
ref: "user",
|
|
||||||
},
|
|
||||||
company: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
website: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
location: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
skills: {
|
|
||||||
type: [String],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
bio: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
githubusername: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
experience: [
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
company: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
location: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
from: {
|
|
||||||
type: Date,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
type: Date,
|
|
||||||
},
|
|
||||||
current: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
education: [
|
|
||||||
{
|
|
||||||
school: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
degree: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
fieldofstudy: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
from: {
|
|
||||||
type: Date,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
type: Date,
|
|
||||||
},
|
|
||||||
current: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
social: {
|
|
||||||
youtube: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
twitter: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
facebook: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
linkedin: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
instagram: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
date: {
|
|
||||||
type: Date,
|
|
||||||
default: Date.now,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = mongoose.model("profile", ProfileSchema);
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
const mongoose = require("mongoose");
|
|
||||||
|
|
||||||
const UserSchema = new mongoose.Schema({
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
email: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
unique: true,
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
avatar: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
date: {
|
|
||||||
type: Date,
|
|
||||||
default: Date.now,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = mongoose.model("user", UserSchema);
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
db.createUser({
|
||||||
|
user: "the_username",
|
||||||
|
pwd: "the_password",
|
||||||
|
roles: [
|
||||||
|
{
|
||||||
|
role: "dbOwner",
|
||||||
|
db: "the_database",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
db.createCollection("todos");
|
||||||
|
|
||||||
|
db.todos.insert({ text: "Write code", done: true });
|
||||||
|
db.todos.insert({ text: "Learn about containers", done: false });
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
const express = require('express');
|
|
||||||
const axios = require('axios');
|
|
||||||
const config = require('config');
|
|
||||||
const router = express.Router();
|
|
||||||
const auth = require('../../middleware/auth');
|
|
||||||
const { check, validationResult } = require('express-validator');
|
|
||||||
// bring in normalize to give us a proper url, regardless of what user entered
|
|
||||||
const normalize = require('normalize-url');
|
|
||||||
const checkObjectId = require('../../middleware/checkObjectId');
|
|
||||||
|
|
||||||
const Profile = require('../../models/Profile');
|
|
||||||
const User = require('../../models/User');
|
|
||||||
const Post = require('../../models/Post');
|
|
||||||
// @route GET api/profile/me
|
|
||||||
// @desc Get current users profile
|
|
||||||
// @access Private
|
|
||||||
router.get('/me', auth, async (req, res) => {
|
|
||||||
try {
|
|
||||||
const profile = await Profile.findOne({
|
|
||||||
user: req.user.id
|
|
||||||
}).populate('user', ['name', 'avatar']);
|
|
||||||
|
|
||||||
if (!profile) {
|
|
||||||
return res.status(400).json({ msg: 'There is no profile for this user' });
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json(profile);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
res.status(500).send('Server Error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// @route POST api/profile
|
|
||||||
// @desc Create or update user profile
|
|
||||||
// @access Private
|
|
||||||
router.post(
|
|
||||||
'/',
|
|
||||||
auth,
|
|
||||||
check('status', 'Status is required').notEmpty(),
|
|
||||||
check('skills', 'Skills is required').notEmpty(),
|
|
||||||
async (req, res) => {
|
|
||||||
const errors = validationResult(req);
|
|
||||||
if (!errors.isEmpty()) {
|
|
||||||
return res.status(400).json({ errors: errors.array() });
|
|
||||||
}
|
|
||||||
|
|
||||||
// destructure the request
|
|
||||||
const {
|
|
||||||
website,
|
|
||||||
skills,
|
|
||||||
youtube,
|
|
||||||
twitter,
|
|
||||||
instagram,
|
|
||||||
linkedin,
|
|
||||||
facebook,
|
|
||||||
// spread the rest of the fields we don't need to check
|
|
||||||
...rest
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
// build a profile
|
|
||||||
const profileFields = {
|
|
||||||
user: req.user.id,
|
|
||||||
website:
|
|
||||||
website && website !== ''
|
|
||||||
? normalize(website, { forceHttps: true })
|
|
||||||
: '',
|
|
||||||
skills: Array.isArray(skills)
|
|
||||||
? skills
|
|
||||||
: skills.split(',').map((skill) => ' ' + skill.trim()),
|
|
||||||
...rest
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build socialFields object
|
|
||||||
const socialFields = { youtube, twitter, instagram, linkedin, facebook };
|
|
||||||
|
|
||||||
// normalize social fields to ensure valid url
|
|
||||||
for (const [key, value] of Object.entries(socialFields)) {
|
|
||||||
if (value && value.length > 0)
|
|
||||||
socialFields[key] = normalize(value, { forceHttps: true });
|
|
||||||
}
|
|
||||||
// add to profileFields
|
|
||||||
profileFields.social = socialFields;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Using upsert option (creates new doc if no match is found):
|
|
||||||
let profile = await Profile.findOneAndUpdate(
|
|
||||||
{ user: req.user.id },
|
|
||||||
{ $set: profileFields },
|
|
||||||
{ new: true, upsert: true, setDefaultsOnInsert: true }
|
|
||||||
);
|
|
||||||
return res.json(profile);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
return res.status(500).send('Server Error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// @route GET api/profile
|
|
||||||
// @desc Get all profiles
|
|
||||||
// @access Public
|
|
||||||
router.get('/', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const profiles = await Profile.find().populate('user', ['name', 'avatar']);
|
|
||||||
res.json(profiles);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
res.status(500).send('Server Error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// @route GET api/profile/user/:user_id
|
|
||||||
// @desc Get profile by user ID
|
|
||||||
// @access Public
|
|
||||||
router.get(
|
|
||||||
'/user/:user_id',
|
|
||||||
checkObjectId('user_id'),
|
|
||||||
async ({ params: { user_id } }, res) => {
|
|
||||||
try {
|
|
||||||
const profile = await Profile.findOne({
|
|
||||||
user: user_id
|
|
||||||
}).populate('user', ['name', 'avatar']);
|
|
||||||
|
|
||||||
if (!profile) return res.status(400).json({ msg: 'Profile not found' });
|
|
||||||
|
|
||||||
return res.json(profile);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
return res.status(500).json({ msg: 'Server error' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// @route DELETE api/profile
|
|
||||||
// @desc Delete profile, user & posts
|
|
||||||
// @access Private
|
|
||||||
router.delete('/', auth, async (req, res) => {
|
|
||||||
try {
|
|
||||||
// Remove user posts
|
|
||||||
// Remove profile
|
|
||||||
// Remove user
|
|
||||||
await Promise.all([
|
|
||||||
Post.deleteMany({ user: req.user.id }),
|
|
||||||
Profile.findOneAndRemove({ user: req.user.id }),
|
|
||||||
User.findOneAndRemove({ _id: req.user.id })
|
|
||||||
]);
|
|
||||||
|
|
||||||
res.json({ msg: 'User deleted' });
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
res.status(500).send('Server Error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// @route PUT api/profile/experience
|
|
||||||
// @desc Add profile experience
|
|
||||||
// @access Private
|
|
||||||
router.put(
|
|
||||||
'/experience',
|
|
||||||
auth,
|
|
||||||
check('title', 'Title is required').notEmpty(),
|
|
||||||
check('company', 'Company is required').notEmpty(),
|
|
||||||
check('from', 'From date is required and needs to be from the past')
|
|
||||||
.notEmpty()
|
|
||||||
.custom((value, { req }) => (req.body.to ? value < req.body.to : true)),
|
|
||||||
async (req, res) => {
|
|
||||||
const errors = validationResult(req);
|
|
||||||
if (!errors.isEmpty()) {
|
|
||||||
return res.status(400).json({ errors: errors.array() });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const profile = await Profile.findOne({ user: req.user.id });
|
|
||||||
|
|
||||||
profile.experience.unshift(req.body);
|
|
||||||
|
|
||||||
await profile.save();
|
|
||||||
|
|
||||||
res.json(profile);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
res.status(500).send('Server Error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// @route DELETE api/profile/experience/:exp_id
|
|
||||||
// @desc Delete experience from profile
|
|
||||||
// @access Private
|
|
||||||
|
|
||||||
router.delete('/experience/:exp_id', auth, async (req, res) => {
|
|
||||||
try {
|
|
||||||
const foundProfile = await Profile.findOne({ user: req.user.id });
|
|
||||||
|
|
||||||
foundProfile.experience = foundProfile.experience.filter(
|
|
||||||
(exp) => exp._id.toString() !== req.params.exp_id
|
|
||||||
);
|
|
||||||
|
|
||||||
await foundProfile.save();
|
|
||||||
return res.status(200).json(foundProfile);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
return res.status(500).json({ msg: 'Server error' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// @route PUT api/profile/education
|
|
||||||
// @desc Add profile education
|
|
||||||
// @access Private
|
|
||||||
router.put(
|
|
||||||
'/education',
|
|
||||||
auth,
|
|
||||||
check('school', 'School is required').notEmpty(),
|
|
||||||
check('degree', 'Degree is required').notEmpty(),
|
|
||||||
check('fieldofstudy', 'Field of study is required').notEmpty(),
|
|
||||||
check('from', 'From date is required and needs to be from the past')
|
|
||||||
.notEmpty()
|
|
||||||
.custom((value, { req }) => (req.body.to ? value < req.body.to : true)),
|
|
||||||
async (req, res) => {
|
|
||||||
const errors = validationResult(req);
|
|
||||||
if (!errors.isEmpty()) {
|
|
||||||
return res.status(400).json({ errors: errors.array() });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const profile = await Profile.findOne({ user: req.user.id });
|
|
||||||
|
|
||||||
profile.education.unshift(req.body);
|
|
||||||
|
|
||||||
await profile.save();
|
|
||||||
|
|
||||||
res.json(profile);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
res.status(500).send('Server Error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// @route DELETE api/profile/education/:edu_id
|
|
||||||
// @desc Delete education from profile
|
|
||||||
// @access Private
|
|
||||||
|
|
||||||
router.delete('/education/:edu_id', auth, async (req, res) => {
|
|
||||||
try {
|
|
||||||
const foundProfile = await Profile.findOne({ user: req.user.id });
|
|
||||||
foundProfile.education = foundProfile.education.filter(
|
|
||||||
(edu) => edu._id.toString() !== req.params.edu_id
|
|
||||||
);
|
|
||||||
await foundProfile.save();
|
|
||||||
return res.status(200).json(foundProfile);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
return res.status(500).json({ msg: 'Server error' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// @route GET api/profile/github/:username
|
|
||||||
// @desc Get user repos from Github
|
|
||||||
// @access Public
|
|
||||||
router.get('/github/:username', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const uri = encodeURI(
|
|
||||||
`https://api.github.com/users/${req.params.username}/repos?per_page=5&sort=created:asc`
|
|
||||||
);
|
|
||||||
const headers = {
|
|
||||||
'user-agent': 'node.js',
|
|
||||||
Authorization: `token ${config.get('githubToken')}`
|
|
||||||
};
|
|
||||||
|
|
||||||
const gitHubResponse = await axios.get(uri, { headers });
|
|
||||||
return res.json(gitHubResponse.data);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
return res.status(404).json({ msg: 'No Github profile found' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
const express = require("express");
|
|
||||||
const router = express.Router();
|
|
||||||
const gravatar = require("gravatar");
|
|
||||||
const bcrypt = require("bcryptjs");
|
|
||||||
const jwt = require("jsonwebtoken");
|
|
||||||
const config = require("config");
|
|
||||||
const { check, validationResult } = require("express-validator");
|
|
||||||
const User = require("../../models/User");
|
|
||||||
const normalize = require('normalize-url');
|
|
||||||
|
|
||||||
// @route POST api/users
|
|
||||||
// @desc Register user
|
|
||||||
// @access Public
|
|
||||||
router.post(
|
|
||||||
"/",
|
|
||||||
check("name", "Name is required").notEmpty(),
|
|
||||||
check("email", "Please include a valid email").isEmail(),
|
|
||||||
check(
|
|
||||||
"password",
|
|
||||||
"Please enter a password with 6 or more characters"
|
|
||||||
).isLength({ min: 6 }),
|
|
||||||
async (req, res) => {
|
|
||||||
const errors = validationResult(req);
|
|
||||||
if (!errors.isEmpty()) {
|
|
||||||
return res.status(400).json({ errors: errors.array() });
|
|
||||||
}
|
|
||||||
|
|
||||||
const { name, email, password } = req.body;
|
|
||||||
|
|
||||||
try {
|
|
||||||
let user = await User.findOne({ email });
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
return res
|
|
||||||
.status(400)
|
|
||||||
.json({ errors: [{ msg: "User already exists" }] });
|
|
||||||
}
|
|
||||||
|
|
||||||
const avatar = normalize(
|
|
||||||
gravatar.url(email, {
|
|
||||||
s: "200",
|
|
||||||
r: "pg",
|
|
||||||
d: "mm",
|
|
||||||
}),
|
|
||||||
{ forceHttps: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
user = new User({
|
|
||||||
name,
|
|
||||||
email,
|
|
||||||
avatar,
|
|
||||||
password,
|
|
||||||
});
|
|
||||||
|
|
||||||
const salt = await bcrypt.genSalt(10);
|
|
||||||
|
|
||||||
user.password = await bcrypt.hash(password, salt);
|
|
||||||
|
|
||||||
await user.save();
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
user: {
|
|
||||||
id: user.id,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
jwt.sign(
|
|
||||||
payload,
|
|
||||||
config.get("jwtSecret"),
|
|
||||||
{ expiresIn: "5 days" },
|
|
||||||
(err, token) => {
|
|
||||||
if (err) throw err;
|
|
||||||
res.json({ token });
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message);
|
|
||||||
res.status(500).send("Server error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
FROM node:20
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
|
||||||
|
COPY --chown=node:node . .
|
||||||
|
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
ENV DEBUG=express:*
|
||||||
|
|
||||||
|
|
||||||
|
USER node
|
||||||
|
|
||||||
|
CMD npm start
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const MONGO_URL = process.env.MONGO_URL || undefined;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
MONGO_URL,
|
||||||
|
};
|
||||||
Executable
+22
@@ -0,0 +1,22 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
// import config from "config";
|
||||||
|
const { MONGO_URL } = require("./config");
|
||||||
|
|
||||||
|
// const db = config.get('mongoURI');
|
||||||
|
|
||||||
|
const connectDB = async () => {
|
||||||
|
try {
|
||||||
|
if (typeof MONGO_URL === "string") await mongoose.connect(MONGO_URL);
|
||||||
|
|
||||||
|
console.log("MongoDB Connected...");
|
||||||
|
} catch (err: unknown) {
|
||||||
|
if (typeof err === "string") console.error(err);
|
||||||
|
else if (err instanceof Error) {
|
||||||
|
console.error(err.message);
|
||||||
|
console.log(MONGO_URL);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connectDB;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
FROM node:20
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY --chown=node:node . .
|
||||||
|
|
||||||
|
RUN npm i
|
||||||
|
|
||||||
|
ENV DEBUG=express:*
|
||||||
|
|
||||||
|
USER node
|
||||||
|
|
||||||
|
CMD npm start server
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
services:
|
||||||
|
mongo:
|
||||||
|
image: mongo
|
||||||
|
ports:
|
||||||
|
- 3456:27017
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: root
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: example
|
||||||
|
MONGO_INITDB_DATABASE: the_database
|
||||||
|
volumes:
|
||||||
|
- ../mongo/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js
|
||||||
|
- mongo_data:/data/db
|
||||||
|
server: # The name of the service, can be anything
|
||||||
|
image: devcon-backend-dev # Declares which image to use
|
||||||
|
build: . # Declares where to build if image is not found
|
||||||
|
ports: # Declares the ports to publish
|
||||||
|
- 5000:5000
|
||||||
|
volumes:
|
||||||
|
- ./.:/usr/src/app
|
||||||
|
environment:
|
||||||
|
MONGO_URL: "mongodb://the_username:the_password@mongo:27017/the_database"
|
||||||
|
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mongo_data:
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
services:
|
||||||
|
app: # The name of the service, can be anything
|
||||||
|
image: devcon-backend # Declares which image to use
|
||||||
|
build: . # Declares where to build if image is not found
|
||||||
|
ports: # Declares the ports to publish
|
||||||
|
- 3000:3000
|
||||||
|
mongo:
|
||||||
|
image: mongo
|
||||||
|
ports:
|
||||||
|
- 3456:27017
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: root
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: example
|
||||||
|
MONGO_INITDB_DATABASE: the_database
|
||||||
|
volumes:
|
||||||
|
- ./mongo/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js
|
||||||
|
- mongo_data:/data/db
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mongo_data:
|
||||||
Reference in New Issue
Block a user