Skip to content

Commit

Permalink
Completed Task 1
Browse files Browse the repository at this point in the history
  • Loading branch information
jeeshumittal7 committed Feb 22, 2024
0 parents commit 536a6a4
Show file tree
Hide file tree
Showing 16 changed files with 397 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copy this file to .env and edit the settings

# Port to listen on (example: 3000)
PORT=

# MongoDB database URL (example: mongodb://localhost/dbname)
DATABASE_URL=

# Session secret string (must be unique to your server)
SESSION_SECRET=
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
.gpt-pilot/
.env
package-lock.json
43 changes: 43 additions & 0 deletions models/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const { v4: uuidv4 } = require('uuid');

const userSchema = new mongoose.Schema({
username: { type: String, unique: true, required: true },
password: { type: String, required: true },
apiKey: { type: String, unique: true }
});

// Pre-save middleware for generating the API key and hashing the password
userSchema.pre('save', async function(next) {
const user = this;

// Generating the API key
if (user.isNew) {
try {
user.apiKey = uuidv4(); // Generate a unique API key
console.log(`API Key generated for user: ${user.username}`);
} catch (err) {
console.error('Error generating API key:', err.message, err.stack);
return next(err);
}
}

// Hashing the password
if (user.isModified('password')) {
try {
const hash = await bcrypt.hash(user.password, 10);
user.password = hash;
console.log(`Password hashed for user: ${user.username}`);
} catch (err) {
console.error('Error hashing password:', err.message, err.stack);
return next(err);
}
}

next();
});

const User = mongoose.model('User', userSchema);

module.exports = User;
28 changes: 28 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "GPTOptimizely",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"chart.js": "^4.4.1",
"connect-flash": "^0.1.1",
"connect-mongo": "^5.1.0",
"csv-writer": "^1.6.0",
"dotenv": "^16.4.1",
"ejs": "^3.1.9",
"express": "^4.18.2",
"express-session": "^1.18.0",
"moment": "^2.30.1",
"mongoose": "^8.1.1",
"uuid": "^9.0.1"
}
}
1 change: 1 addition & 0 deletions public/css/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Placeholder for custom styles */
1 change: 1 addition & 0 deletions public/js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Placeholder for future JavaScript code
29 changes: 29 additions & 0 deletions routes/apiRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const express = require('express');
const User = require('../models/User');

const router = express.Router();

router.get('/api/generate-snippet', async (req, res) => {
const { apiKey } = req.query;
if (!apiKey) {
console.error('API key query parameter is missing.');
return res.status(400).send('API key is required.');
}

try {
const user = await User.findOne({ apiKey });
if (!user) {
console.error(`Invalid API key: ${apiKey}`);
return res.status(404).send('Invalid API key.');
}

const snippet = `<script src="http://yourdomain.com/loader.js" data-api-key="${apiKey}"></script>`; // INPUT_REQUIRED {replace http://yourdomain.com/loader.js with your actual domain and path to the loader.js}
console.log(`Snippet generated for API key: ${apiKey}`);
res.type('text/plain').send(snippet);
} catch (error) {
console.error('Error generating snippet:', error.message, error.stack);
res.status(500).send('Internal server error while generating snippet.');
}
});

module.exports = router;
65 changes: 65 additions & 0 deletions routes/authRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const express = require('express');
const User = require('../models/User');
const bcrypt = require('bcrypt');
const router = express.Router();

router.get('/auth/register', (req, res) => {
res.render('register', { error: '' });
});

router.post('/auth/register', async (req, res) => {
try {
const { username, password } = req.body;
const existingUser = await User.findOne({ username });
if (existingUser) {
console.log('Username already exists:', username);
return res.render('register', { error: 'Username already exists.' });
}
await User.create({ username, password });
console.log('User registered successfully:', username);
res.redirect('/auth/login');
} catch (error) {
console.error('Registration error:', error.message, error.stack);
res.render('register', { error: error.message });
}
});

router.get('/auth/login', (req, res) => {
res.render('login', { error: '' });
});

router.post('/auth/login', async (req, res) => {
try {
const { username, password } = req.body;
const user = await User.findOne({ username });
if (!user) {
console.log('User not found:', username);
return res.render('login', { error: 'Invalid username or password.' });
}
const isMatch = await bcrypt.compare(password, user.password);
if (isMatch) {
req.session.userId = user._id;
console.log('User logged in successfully:', username);
return res.redirect('/');
} else {
console.log('Password is incorrect for user:', username);
return res.render('login', { error: 'Invalid username or password.' });
}
} catch (error) {
console.error('Login error:', error.message, error.stack);
res.render('login', { error: error.message });
}
});

router.get('/auth/logout', (req, res) => {
req.session.destroy(err => {
if (err) {
console.error('Error during session destruction:', err.message, err.stack);
return res.status(500).send('Error logging out');
}
console.log('User logged out successfully');
res.redirect('/auth/login');
});
});

module.exports = router;
11 changes: 11 additions & 0 deletions routes/middleware/authMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const isAuthenticated = (req, res, next) => {
if (req.session && req.session.userId) {
return next(); // User is authenticated, proceed to the next middleware/route handler
} else {
return res.status(401).send('You are not authenticated'); // User is not authenticated
}
};

module.exports = {
isAuthenticated
};
97 changes: 97 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Load environment variables
require("dotenv").config();
const mongoose = require("mongoose");
const express = require("express");
const session = require("express-session");
const MongoStore = require('connect-mongo');
const authRoutes = require("./routes/authRoutes");
const apiRoutes = require('./routes/apiRoutes'); // Include API routes

if (!process.env.DATABASE_URL || !process.env.SESSION_SECRET) {
console.error("Error: config environment variables not set. Please create/edit .env configuration file.");
process.exit(-1);
}

const app = express();
const port = process.env.PORT || 3000;

// Middleware to parse request bodies
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

// Setting the templating engine to EJS
app.set("view engine", "ejs");

// Serve static files
app.use(express.static("public"));

// Database connection
mongoose
.connect(process.env.DATABASE_URL)
.then(() => {
console.log("Database connected successfully");
})
.catch((err) => {
console.error(`Database connection error: ${err.message}`);
console.error(err.stack);
process.exit(1);
});

// Session configuration with connect-mongo
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: MongoStore.create({ mongoUrl: process.env.DATABASE_URL }),
}),
);

app.on("error", (error) => {
console.error(`Server error: ${error.message}`);
console.error(error.stack);
});

// Logging session creation and destruction
app.use((req, res, next) => {
const sess = req.session;
// Make session available to all views
res.locals.session = sess;
if (!sess.views) {
sess.views = 1;
console.log("Session created at: ", new Date().toISOString());
} else {
sess.views++;
console.log(
`Session accessed again at: ${new Date().toISOString()}, Views: ${sess.views}, User ID: ${sess.userId || '(unauthenticated)'}`,
);
}
next();
});

// Authentication Routes
app.use(authRoutes);

// API Routes
app.use(apiRoutes); // Use API routes in the application

// Root path response
app.get("/", (req, res) => {
res.render("index");
});

// If no routes handled the request, it's a 404
app.use((req, res, next) => {
res.status(404).send("Page not found.");
});

// Error handling
app.use((err, req, res, next) => {
console.error(`Unhandled application error: ${err.message}`);
console.error(err.stack);
res.status(500).send("There was an error serving your request.");
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
14 changes: 14 additions & 0 deletions views/index.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<%- include('partials/_head.ejs') %>
<body>
<%- include('partials/_header.ejs') %>
<main role="main" class="container mt-4">
<div class="text-center">
<h1>GPTOptimizely</h1>
</div>
</main>
</body>
<%- include('partials/_footer.ejs') %>
<script src="/js/main.js"></script>
</html>
30 changes: 30 additions & 0 deletions views/login.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<%- include('partials/_head.ejs') %>
<body>
<%- include('partials/_header.ejs') %>
<main role="main">
<div class="container mt-5">
<h2>Login</h2>
<% if (typeof error !== 'undefined' && error) { %>
<div class="alert alert-danger" role="alert">
<%= error %>
</div>
<% } %>
<form action="/auth/login" method="POST">
<div class="mb-3">
<input type="text" name="username" placeholder="Username" required class="form-control">
</div>
<div class="mb-3">
<input type="password" name="password" placeholder="Password" required class="form-control">
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Login</button>
Don't have an account? <a href="/auth/register">Register</a>
</div>
</form>
</div>
</main>
</body>
<%- include('partials/_footer.ejs') %>
</html>
7 changes: 7 additions & 0 deletions views/partials/_footer.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<footer class="footer fixed-bottom bg-light">
<div class="container text-center my-2">
<span>Copyright &copy; <%= 1900 + new Date().getYear() %> GPTOptimizely</span>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-BBtl+eGJRgqQAUMxJ7pMwbEyER4l1g+O15P+16Ep7Q9Q+zqX6gSbd85u4mG4QzX+" crossorigin="anonymous"></script>

7 changes: 7 additions & 0 deletions views/partials/_head.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<head>
<meta charset="UTF-8">
<title>GPTOptimizely</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<link rel="stylesheet" href="/css/style.css">
</head>

20 changes: 20 additions & 0 deletions views/partials/_header.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<a class="navbar-brand" href="/">GPTOptimizely</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<% if (session && session.userId) { %>
<a class="nav-link" href="/auth/logout">Logout</a>
<% } else { %>
<a class="nav-link" href="/auth/login">Login</a>
<% } %>
</li>
</ul>
</div>
</nav>
Loading

0 comments on commit 536a6a4

Please sign in to comment.