Skip to content

Commit 8777149

Browse files
committed
Initial project setup with authentication, session management, and basic views
0 parents  commit 8777149

File tree

17 files changed

+440
-0
lines changed

17 files changed

+440
-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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
package-lock.json
3+
.env

models/Article.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const mongoose = require('mongoose');
2+
3+
const articleSchema = new mongoose.Schema({
4+
title: { type: String, required: true },
5+
content: { type: String, required: true },
6+
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
7+
category: { type: String, required: true },
8+
tags: [{ type: String }],
9+
status: { type: String, enum: ['draft', 'published'], default: 'draft' },
10+
}, { timestamps: true });
11+
12+
const Article = mongoose.model('Article', articleSchema);
13+
14+
module.exports = Article;

models/User.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const mongoose = require('mongoose');
2+
const bcrypt = require('bcrypt');
3+
4+
const userSchema = new mongoose.Schema({
5+
username: { type: String, unique: true, required: true },
6+
password: { type: String, required: true }
7+
});
8+
9+
userSchema.pre('save', function(next) {
10+
const user = this;
11+
if (!user.isModified('password')) return next();
12+
bcrypt.hash(user.password, 10, (err, hash) => {
13+
if (err) {
14+
console.error('Error hashing password:', err);
15+
return next(err);
16+
}
17+
user.password = hash;
18+
next();
19+
});
20+
});
21+
22+
const User = mongoose.model('User', userSchema);
23+
24+
module.exports = User;

package.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "PythaSpace",
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+
"@anthropic-ai/sdk": "^0.27.3",
15+
"axios": "^1.7.7",
16+
"bcrypt": "^5.1.1",
17+
"body-parser": "^1.20.2",
18+
"chart.js": "^4.4.1",
19+
"connect-flash": "^0.1.1",
20+
"connect-mongo": "^5.1.0",
21+
"csv-writer": "^1.6.0",
22+
"dotenv": "^16.4.5",
23+
"ejs": "^3.1.9",
24+
"express": "^4.18.2",
25+
"express-session": "^1.18.0",
26+
"moment": "^2.30.1",
27+
"mongoose": "^8.1.1",
28+
"openai": "^4.63.0"
29+
}
30+
}

public/css/style.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* Placeholder for custom styles */
2+
body {
3+
padding-bottom: 60px;
4+
}
5+
6+
footer {
7+
height: 40px;
8+
}
9+
10+
nav.navbar {
11+
padding: 10px 20px;
12+
}
13+
14+
.pythagora-logo {
15+
height: 20px;
16+
margin-left: 5px;
17+
}

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/authRoutes.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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');
8+
});
9+
10+
router.post('/auth/register', async (req, res) => {
11+
try {
12+
const { username, password } = req.body;
13+
// User model will automatically hash the password using bcrypt
14+
await User.create({ username, password });
15+
res.redirect('/auth/login');
16+
} catch (error) {
17+
console.error('Registration error:', error);
18+
res.status(500).send(error.message);
19+
}
20+
});
21+
22+
router.get('/auth/login', (req, res) => {
23+
res.render('login');
24+
});
25+
26+
router.post('/auth/login', async (req, res) => {
27+
try {
28+
const { username, password } = req.body;
29+
const user = await User.findOne({ username });
30+
if (!user) {
31+
return res.status(400).send('User not found');
32+
}
33+
const isMatch = await bcrypt.compare(password, user.password);
34+
if (isMatch) {
35+
req.session.userId = user._id;
36+
return res.redirect('/');
37+
} else {
38+
return res.status(400).send('Password is incorrect');
39+
}
40+
} catch (error) {
41+
console.error('Login error:', error);
42+
return res.status(500).send(error.message);
43+
}
44+
});
45+
46+
router.get('/auth/logout', (req, res) => {
47+
req.session.destroy(err => {
48+
if (err) {
49+
console.error('Error during session destruction:', err); // gpt_pilot_debugging_log
50+
return res.status(500).send('Error logging out');
51+
}
52+
res.redirect('/auth/login');
53+
});
54+
});
55+
56+
module.exports = router;

routes/middleware/authMiddleware.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const User = require('../../models/User');
2+
const isAuthenticated = async (req, res, next) => {
3+
if (req.session && req.session.userId) {
4+
try {
5+
const user = await User.findById(req.session.userId);
6+
if (user) {
7+
req.user = user;
8+
return next();
9+
}
10+
} catch (error) {
11+
console.error('Error in authentication middleware:', error);
12+
res.status(500).send('Error during authentication process');
13+
}
14+
}
15+
return res.status(401).send('You are not authenticated');
16+
};
17+
18+
module.exports = {
19+
isAuthenticated
20+
};

server.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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 Article = require('./models/Article'); // Importing the Article model
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+
// Root path response
76+
app.get("/", (req, res) => {
77+
res.render("index");
78+
});
79+
80+
// If no routes handled the request, it's a 404
81+
app.use((req, res, next) => {
82+
res.status(404).send("Page not found.");
83+
});
84+
85+
// Error handling
86+
app.use((err, req, res, next) => {
87+
console.error(`Unhandled application error: ${err.message}`);
88+
console.error(err.stack);
89+
res.status(500).send("There was an error serving your request.");
90+
});
91+
92+
app.listen(port, () => {
93+
console.log(`Server running at http://localhost:${port}`);
94+
});

services/llm.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
const axios = require('axios');
2+
const OpenAI = require('openai');
3+
const Anthropic = require('@anthropic-ai/sdk');
4+
const dotenv = require('dotenv');
5+
6+
dotenv.config();
7+
8+
const openai = new OpenAI({
9+
apiKey: process.env.OPENAI_API_KEY,
10+
});
11+
12+
const anthropic = new Anthropic({
13+
apiKey: process.env.ANTHROPIC_API_KEY,
14+
});
15+
16+
const MAX_RETRIES = 3;
17+
const RETRY_DELAY = 1000;
18+
19+
async function sleep(ms) {
20+
return new Promise(resolve => setTimeout(resolve, ms));
21+
}
22+
23+
async function sendRequestToOpenAI(model, message) {
24+
for (let i = 0; i < MAX_RETRIES; i++) {
25+
try {
26+
const response = await openai.chat.completions.create({
27+
model: model,
28+
messages: [{ role: 'user', content: message }],
29+
max_tokens: 1024,
30+
});
31+
return response.choices[0].message.content;
32+
} catch (error) {
33+
console.error(`Error sending request to OpenAI (attempt ${i + 1}):`, error.message, error.stack);
34+
if (i === MAX_RETRIES - 1) throw error;
35+
await sleep(RETRY_DELAY);
36+
}
37+
}
38+
}
39+
40+
async function sendRequestToAnthropic(model, message) {
41+
for (let i = 0; i < MAX_RETRIES; i++) {
42+
try {
43+
console.log(`Sending request to Anthropic with model: ${model} and message: ${message}`);
44+
const response = await anthropic.messages.create({
45+
model: model,
46+
messages: [{ role: 'user', content: message }],
47+
max_tokens: 1024,
48+
});
49+
console.log(`Received response from Anthropic: ${JSON.stringify(response.content)}`);
50+
return response.content[0].text;
51+
} catch (error) {
52+
console.error(`Error sending request to Anthropic (attempt ${i + 1}):`, error.message, error.stack);
53+
if (i === MAX_RETRIES - 1) throw error;
54+
await sleep(RETRY_DELAY);
55+
}
56+
}
57+
}
58+
59+
async function sendLLMRequest(provider, model, message) {
60+
switch (provider.toLowerCase()) {
61+
case 'openai':
62+
return sendRequestToOpenAI(model, message);
63+
case 'anthropic':
64+
return sendRequestToAnthropic(model, message);
65+
default:
66+
throw new Error(`Unsupported LLM provider: ${provider}`);
67+
}
68+
}
69+
70+
module.exports = {
71+
sendLLMRequest
72+
};

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>PythaSpace</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: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
<form action="/auth/login" method="POST">
10+
<div class="mb-3">
11+
<input type="text" name="username" placeholder="Username" required class="form-control">
12+
</div>
13+
<div class="mb-3">
14+
<input type="password" name="password" placeholder="Password" required class="form-control">
15+
</div>
16+
<div class="mb-3">
17+
<button type="submit" class="btn btn-primary">Login</button>
18+
Don't have an account? <a href="/auth/register">Register</a>
19+
</div>
20+
</form>
21+
</div>
22+
</main>
23+
</body>
24+
<%- include('partials/_footer.ejs') %>
25+
</html>

0 commit comments

Comments
 (0)