Skip to content

Commit 536a6a4

Browse files
committed
Completed Task 1
0 parents  commit 536a6a4

File tree

16 files changed

+397
-0
lines changed

16 files changed

+397
-0
lines changed

.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copy this file to .env and edit the settings
2+
3+
# Port to listen on (example: 3000)
4+
PORT=
5+
6+
# MongoDB database URL (example: mongodb://localhost/dbname)
7+
DATABASE_URL=
8+
9+
# Session secret string (must be unique to your server)
10+
SESSION_SECRET=

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
.gpt-pilot/
3+
.env
4+
package-lock.json

models/User.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const mongoose = require('mongoose');
2+
const bcrypt = require('bcrypt');
3+
const { v4: uuidv4 } = require('uuid');
4+
5+
const userSchema = new mongoose.Schema({
6+
username: { type: String, unique: true, required: true },
7+
password: { type: String, required: true },
8+
apiKey: { type: String, unique: true }
9+
});
10+
11+
// Pre-save middleware for generating the API key and hashing the password
12+
userSchema.pre('save', async function(next) {
13+
const user = this;
14+
15+
// Generating the API key
16+
if (user.isNew) {
17+
try {
18+
user.apiKey = uuidv4(); // Generate a unique API key
19+
console.log(`API Key generated for user: ${user.username}`);
20+
} catch (err) {
21+
console.error('Error generating API key:', err.message, err.stack);
22+
return next(err);
23+
}
24+
}
25+
26+
// Hashing the password
27+
if (user.isModified('password')) {
28+
try {
29+
const hash = await bcrypt.hash(user.password, 10);
30+
user.password = hash;
31+
console.log(`Password hashed for user: ${user.username}`);
32+
} catch (err) {
33+
console.error('Error hashing password:', err.message, err.stack);
34+
return next(err);
35+
}
36+
}
37+
38+
next();
39+
});
40+
41+
const User = mongoose.model('User', userSchema);
42+
43+
module.exports = User;

package.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "GPTOptimizely",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "server.js",
6+
"scripts": {
7+
"start": "node server.js",
8+
"test": "echo \"Error: no test specified\" && exit 1"
9+
},
10+
"keywords": [],
11+
"author": "",
12+
"license": "ISC",
13+
"dependencies": {
14+
"bcrypt": "^5.1.1",
15+
"body-parser": "^1.20.2",
16+
"chart.js": "^4.4.1",
17+
"connect-flash": "^0.1.1",
18+
"connect-mongo": "^5.1.0",
19+
"csv-writer": "^1.6.0",
20+
"dotenv": "^16.4.1",
21+
"ejs": "^3.1.9",
22+
"express": "^4.18.2",
23+
"express-session": "^1.18.0",
24+
"moment": "^2.30.1",
25+
"mongoose": "^8.1.1",
26+
"uuid": "^9.0.1"
27+
}
28+
}

public/css/style.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* Placeholder for custom styles */

public/js/main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Placeholder for future JavaScript code

routes/apiRoutes.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const express = require('express');
2+
const User = require('../models/User');
3+
4+
const router = express.Router();
5+
6+
router.get('/api/generate-snippet', async (req, res) => {
7+
const { apiKey } = req.query;
8+
if (!apiKey) {
9+
console.error('API key query parameter is missing.');
10+
return res.status(400).send('API key is required.');
11+
}
12+
13+
try {
14+
const user = await User.findOne({ apiKey });
15+
if (!user) {
16+
console.error(`Invalid API key: ${apiKey}`);
17+
return res.status(404).send('Invalid API key.');
18+
}
19+
20+
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}
21+
console.log(`Snippet generated for API key: ${apiKey}`);
22+
res.type('text/plain').send(snippet);
23+
} catch (error) {
24+
console.error('Error generating snippet:', error.message, error.stack);
25+
res.status(500).send('Internal server error while generating snippet.');
26+
}
27+
});
28+
29+
module.exports = router;

routes/authRoutes.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const express = require('express');
2+
const User = require('../models/User');
3+
const bcrypt = require('bcrypt');
4+
const router = express.Router();
5+
6+
router.get('/auth/register', (req, res) => {
7+
res.render('register', { error: '' });
8+
});
9+
10+
router.post('/auth/register', async (req, res) => {
11+
try {
12+
const { username, password } = req.body;
13+
const existingUser = await User.findOne({ username });
14+
if (existingUser) {
15+
console.log('Username already exists:', username);
16+
return res.render('register', { error: 'Username already exists.' });
17+
}
18+
await User.create({ username, password });
19+
console.log('User registered successfully:', username);
20+
res.redirect('/auth/login');
21+
} catch (error) {
22+
console.error('Registration error:', error.message, error.stack);
23+
res.render('register', { error: error.message });
24+
}
25+
});
26+
27+
router.get('/auth/login', (req, res) => {
28+
res.render('login', { error: '' });
29+
});
30+
31+
router.post('/auth/login', async (req, res) => {
32+
try {
33+
const { username, password } = req.body;
34+
const user = await User.findOne({ username });
35+
if (!user) {
36+
console.log('User not found:', username);
37+
return res.render('login', { error: 'Invalid username or password.' });
38+
}
39+
const isMatch = await bcrypt.compare(password, user.password);
40+
if (isMatch) {
41+
req.session.userId = user._id;
42+
console.log('User logged in successfully:', username);
43+
return res.redirect('/');
44+
} else {
45+
console.log('Password is incorrect for user:', username);
46+
return res.render('login', { error: 'Invalid username or password.' });
47+
}
48+
} catch (error) {
49+
console.error('Login error:', error.message, error.stack);
50+
res.render('login', { error: error.message });
51+
}
52+
});
53+
54+
router.get('/auth/logout', (req, res) => {
55+
req.session.destroy(err => {
56+
if (err) {
57+
console.error('Error during session destruction:', err.message, err.stack);
58+
return res.status(500).send('Error logging out');
59+
}
60+
console.log('User logged out successfully');
61+
res.redirect('/auth/login');
62+
});
63+
});
64+
65+
module.exports = router;

routes/middleware/authMiddleware.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const isAuthenticated = (req, res, next) => {
2+
if (req.session && req.session.userId) {
3+
return next(); // User is authenticated, proceed to the next middleware/route handler
4+
} else {
5+
return res.status(401).send('You are not authenticated'); // User is not authenticated
6+
}
7+
};
8+
9+
module.exports = {
10+
isAuthenticated
11+
};

server.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Load environment variables
2+
require("dotenv").config();
3+
const mongoose = require("mongoose");
4+
const express = require("express");
5+
const session = require("express-session");
6+
const MongoStore = require('connect-mongo');
7+
const authRoutes = require("./routes/authRoutes");
8+
const apiRoutes = require('./routes/apiRoutes'); // Include API routes
9+
10+
if (!process.env.DATABASE_URL || !process.env.SESSION_SECRET) {
11+
console.error("Error: config environment variables not set. Please create/edit .env configuration file.");
12+
process.exit(-1);
13+
}
14+
15+
const app = express();
16+
const port = process.env.PORT || 3000;
17+
18+
// Middleware to parse request bodies
19+
app.use(express.urlencoded({ extended: true }));
20+
app.use(express.json());
21+
22+
// Setting the templating engine to EJS
23+
app.set("view engine", "ejs");
24+
25+
// Serve static files
26+
app.use(express.static("public"));
27+
28+
// Database connection
29+
mongoose
30+
.connect(process.env.DATABASE_URL)
31+
.then(() => {
32+
console.log("Database connected successfully");
33+
})
34+
.catch((err) => {
35+
console.error(`Database connection error: ${err.message}`);
36+
console.error(err.stack);
37+
process.exit(1);
38+
});
39+
40+
// Session configuration with connect-mongo
41+
app.use(
42+
session({
43+
secret: process.env.SESSION_SECRET,
44+
resave: false,
45+
saveUninitialized: false,
46+
store: MongoStore.create({ mongoUrl: process.env.DATABASE_URL }),
47+
}),
48+
);
49+
50+
app.on("error", (error) => {
51+
console.error(`Server error: ${error.message}`);
52+
console.error(error.stack);
53+
});
54+
55+
// Logging session creation and destruction
56+
app.use((req, res, next) => {
57+
const sess = req.session;
58+
// Make session available to all views
59+
res.locals.session = sess;
60+
if (!sess.views) {
61+
sess.views = 1;
62+
console.log("Session created at: ", new Date().toISOString());
63+
} else {
64+
sess.views++;
65+
console.log(
66+
`Session accessed again at: ${new Date().toISOString()}, Views: ${sess.views}, User ID: ${sess.userId || '(unauthenticated)'}`,
67+
);
68+
}
69+
next();
70+
});
71+
72+
// Authentication Routes
73+
app.use(authRoutes);
74+
75+
// API Routes
76+
app.use(apiRoutes); // Use API routes in the application
77+
78+
// Root path response
79+
app.get("/", (req, res) => {
80+
res.render("index");
81+
});
82+
83+
// If no routes handled the request, it's a 404
84+
app.use((req, res, next) => {
85+
res.status(404).send("Page not found.");
86+
});
87+
88+
// Error handling
89+
app.use((err, req, res, next) => {
90+
console.error(`Unhandled application error: ${err.message}`);
91+
console.error(err.stack);
92+
res.status(500).send("There was an error serving your request.");
93+
});
94+
95+
app.listen(port, () => {
96+
console.log(`Server running at http://localhost:${port}`);
97+
});

views/index.ejs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<%- include('partials/_head.ejs') %>
4+
<body>
5+
<%- include('partials/_header.ejs') %>
6+
<main role="main" class="container mt-4">
7+
<div class="text-center">
8+
<h1>GPTOptimizely</h1>
9+
</div>
10+
</main>
11+
</body>
12+
<%- include('partials/_footer.ejs') %>
13+
<script src="/js/main.js"></script>
14+
</html>

views/login.ejs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<%- include('partials/_head.ejs') %>
4+
<body>
5+
<%- include('partials/_header.ejs') %>
6+
<main role="main">
7+
<div class="container mt-5">
8+
<h2>Login</h2>
9+
<% if (typeof error !== 'undefined' && error) { %>
10+
<div class="alert alert-danger" role="alert">
11+
<%= error %>
12+
</div>
13+
<% } %>
14+
<form action="/auth/login" method="POST">
15+
<div class="mb-3">
16+
<input type="text" name="username" placeholder="Username" required class="form-control">
17+
</div>
18+
<div class="mb-3">
19+
<input type="password" name="password" placeholder="Password" required class="form-control">
20+
</div>
21+
<div class="mb-3">
22+
<button type="submit" class="btn btn-primary">Login</button>
23+
Don't have an account? <a href="/auth/register">Register</a>
24+
</div>
25+
</form>
26+
</div>
27+
</main>
28+
</body>
29+
<%- include('partials/_footer.ejs') %>
30+
</html>

views/partials/_footer.ejs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<footer class="footer fixed-bottom bg-light">
2+
<div class="container text-center my-2">
3+
<span>Copyright &copy; <%= 1900 + new Date().getYear() %> GPTOptimizely</span>
4+
</div>
5+
</footer>
6+
<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+

views/partials/_head.ejs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<head>
2+
<meta charset="UTF-8">
3+
<title>GPTOptimizely</title>
4+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
5+
<link rel="stylesheet" href="/css/style.css">
6+
</head>
7+

views/partials/_header.ejs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
2+
<a class="navbar-brand" href="/">GPTOptimizely</a>
3+
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
4+
<span class="navbar-toggler-icon"></span>
5+
</button>
6+
<div class="collapse navbar-collapse" id="navbarNav">
7+
<ul class="navbar-nav">
8+
<li class="nav-item">
9+
<a class="nav-link" href="/">Home</a>
10+
</li>
11+
<li class="nav-item">
12+
<% if (session && session.userId) { %>
13+
<a class="nav-link" href="/auth/logout">Logout</a>
14+
<% } else { %>
15+
<a class="nav-link" href="/auth/login">Login</a>
16+
<% } %>
17+
</li>
18+
</ul>
19+
</div>
20+
</nav>

0 commit comments

Comments
 (0)