Shinhati2023 commited on
Commit
7dff7ec
·
verified ·
1 Parent(s): 65306e8

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +57 -24
server.js CHANGED
@@ -9,9 +9,12 @@ const streamifier = require('streamifier');
9
  const app = express();
10
  const upload = multer({ storage: multer.memoryStorage() });
11
 
 
 
 
12
  app.set('trust proxy', 1);
13
 
14
- // Configure Cloudinary with your .env variables
15
  cloudinary.config({
16
  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
17
  api_key: process.env.CLOUDINARY_API_KEY,
@@ -21,9 +24,8 @@ cloudinary.config({
21
  const DB_FILE = 'megapin_master_db.json';
22
  const MAX_TRENDING = 100;
23
 
24
- // ADMIN CONFIGURATION
25
- const ADMIN_USER = process.env.ADMIN_USER; // Set this in your .env
26
- const ADMIN_PASS = process.env.ADMIN_PASS; // Set this in your .env
27
  const DEFAULT_AVATAR = "https://cdn-icons-png.flaticon.com/512/149/149071.png";
28
 
29
  let localDB = null;
@@ -32,14 +34,17 @@ app.use(express.urlencoded({ extended: true }));
32
  app.use(express.json());
33
  app.set('view engine', 'ejs');
34
 
 
35
  app.use(cookieSession({
36
  name: '__Secure-session',
37
- keys: [process.env.SESSION_SECRET || 'secret'],
38
  maxAge: 24 * 60 * 60 * 1000,
39
- secure: true,
40
- sameSite: 'none',
 
 
41
  httpOnly: true,
42
- partitioned: true
43
  }));
44
 
45
  // --- DB ENGINE ---
@@ -52,6 +57,7 @@ async function initDB() {
52
  if (!localDB.users) localDB.users = {};
53
  if (!localDB.pins) localDB.pins = [];
54
  } catch (e) {
 
55
  localDB = { users: {}, pins: [] };
56
  }
57
  return localDB;
@@ -84,8 +90,8 @@ function addNotification(toUser, fromUser, type, pinId = null, preview = "") {
84
 
85
  localDB.users[toUser].notifications.unshift({
86
  from: fromUser,
87
- fromAvatar: localDB.users[fromUser].avatar,
88
- type: type, // 'like', 'comment', 'follow'
89
  pinId: pinId,
90
  preview: preview,
91
  timestamp: Date.now(),
@@ -100,7 +106,12 @@ async function checkBan(req, res, next) {
100
  if (!req.session.user) return next();
101
  await initDB();
102
  const user = localDB.users[req.session.user];
103
- if (user && user.banned) {
 
 
 
 
 
104
  req.session = null;
105
  return res.send("Account Suspended.");
106
  }
@@ -114,15 +125,16 @@ function checkAdmin(req, res, next) {
114
 
115
  // --- ROUTES ---
116
 
117
- // Helper to format pins for view
118
  function formatPins(pins, username) {
119
  return pins.map(pin => {
120
- const authorData = localDB.users[pin.author] || { avatar: DEFAULT_AVATAR, displayName: pin.author };
 
121
  return {
122
  ...pin,
123
  authorDisplayName: authorData.displayName || pin.author,
124
- authorAvatar: authorData.avatar,
125
- badge: authorData.badge,
126
  likeCount: pin.likes ? pin.likes.length : 0,
127
  hasLiked: pin.likes && username ? pin.likes.includes(username) : false,
128
  comments: pin.comments || []
@@ -186,7 +198,8 @@ app.get('/u/:username', async (req, res) => {
186
  postCount: userPins.length,
187
  followerCount: targetUser.followers ? targetUser.followers.length : 0,
188
  followingCount: targetUser.following ? targetUser.following.length : 0,
189
- isFollowing: targetUser.followers && targetUser.followers.includes(username)
 
190
  };
191
 
192
  let currentUser = null;
@@ -208,6 +221,7 @@ app.get('/api/pin/:id', async (req, res) => {
208
  await initDB();
209
  const pin = localDB.pins.find(p => p.id === parseInt(req.params.id));
210
  if(!pin) return res.json({error: "Not found"});
 
211
  const formatted = formatPins([pin], req.session.user)[0];
212
  res.json(formatted);
213
  });
@@ -218,8 +232,8 @@ app.get('/api/search-users', async (req, res) => {
218
  const users = Object.keys(localDB.users).filter(u => u.toLowerCase().includes(q));
219
  const results = users.map(u => ({
220
  username: u,
221
- avatar: localDB.users[u].avatar,
222
- badge: localDB.users[u].badge
223
  }));
224
  res.json(results);
225
  });
@@ -276,8 +290,10 @@ app.post('/api/comment', checkBan, async (req, res) => {
276
  if (!req.session.user) return res.status(401).json({error: "Login required"});
277
  await initDB();
278
  const { pinId, text } = req.body;
 
 
279
  const pin = localDB.pins.find(p => p.id === parseInt(pinId));
280
- if (pin && text) {
281
  if (!pin.comments) pin.comments = [];
282
  const comment = {
283
  id: Date.now(),
@@ -334,7 +350,13 @@ app.post('/api/admin/delete-user', checkAdmin, async (req, res) => {
334
  const { targetUser } = req.body;
335
  if(localDB.users[targetUser] && targetUser !== ADMIN_USER) {
336
  delete localDB.users[targetUser];
 
337
  localDB.pins = localDB.pins.filter(p => p.author !== targetUser);
 
 
 
 
 
338
  await saveDB();
339
  res.json({success: true});
340
  } else {
@@ -347,9 +369,16 @@ app.post('/api/admin/delete-user', checkAdmin, async (req, res) => {
347
  app.post('/signup', async (req, res) => {
348
  const { username, password, email } = req.body;
349
  await initDB();
350
- if (localDB.users[username]) return res.send("Username taken.");
351
- localDB.users[username] = {
352
- displayName: username,
 
 
 
 
 
 
 
353
  hash: bcrypt.hashSync(password, 8),
354
  email: email,
355
  avatar: DEFAULT_AVATAR,
@@ -359,13 +388,17 @@ app.post('/signup', async (req, res) => {
359
  banned: false
360
  };
361
  await saveDB();
362
- req.session.user = username;
363
  res.redirect('/');
364
  });
365
 
366
  app.post('/login', async (req, res) => {
367
  const { username, password } = req.body;
368
  await initDB();
 
 
 
 
369
  // Admin Login Check
370
  if (username === ADMIN_USER && password === ADMIN_PASS) {
371
  req.session.user = username;
@@ -429,4 +462,4 @@ app.post('/delete', checkBan, async (req, res) => {
429
  app.get('/logout', (req, res) => { req.session = null; res.redirect('/'); });
430
 
431
  initDB();
432
- app.listen(7860, () => console.log("Megapin Complete Active"));
 
9
  const app = express();
10
  const upload = multer({ storage: multer.memoryStorage() });
11
 
12
+ // Detect if we are in production (HTTPS) or development
13
+ const IS_PROD = process.env.NODE_ENV === 'production';
14
+
15
  app.set('trust proxy', 1);
16
 
17
+ // Configure Cloudinary
18
  cloudinary.config({
19
  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
20
  api_key: process.env.CLOUDINARY_API_KEY,
 
24
  const DB_FILE = 'megapin_master_db.json';
25
  const MAX_TRENDING = 100;
26
 
27
+ const ADMIN_USER = process.env.ADMIN_USER;
28
+ const ADMIN_PASS = process.env.ADMIN_PASS;
 
29
  const DEFAULT_AVATAR = "https://cdn-icons-png.flaticon.com/512/149/149071.png";
30
 
31
  let localDB = null;
 
34
  app.use(express.json());
35
  app.set('view engine', 'ejs');
36
 
37
+ // --- FIXED SESSION CONFIG ---
38
  app.use(cookieSession({
39
  name: '__Secure-session',
40
+ keys: [process.env.SESSION_SECRET || 'secret_key_change_me'],
41
  maxAge: 24 * 60 * 60 * 1000,
42
+ // Only require secure (HTTPS) if we are definitely in production.
43
+ // This fixes "Signup broken" issues on localhost or non-https spaces.
44
+ secure: IS_PROD,
45
+ sameSite: IS_PROD ? 'none' : 'lax',
46
  httpOnly: true,
47
+ partitioned: IS_PROD
48
  }));
49
 
50
  // --- DB ENGINE ---
 
57
  if (!localDB.users) localDB.users = {};
58
  if (!localDB.pins) localDB.pins = [];
59
  } catch (e) {
60
+ // If file doesn't exist yet, create structure
61
  localDB = { users: {}, pins: [] };
62
  }
63
  return localDB;
 
90
 
91
  localDB.users[toUser].notifications.unshift({
92
  from: fromUser,
93
+ fromAvatar: localDB.users[fromUser].avatar || DEFAULT_AVATAR,
94
+ type: type,
95
  pinId: pinId,
96
  preview: preview,
97
  timestamp: Date.now(),
 
106
  if (!req.session.user) return next();
107
  await initDB();
108
  const user = localDB.users[req.session.user];
109
+ // If user deleted but session exists, clear session
110
+ if (!user) {
111
+ req.session = null;
112
+ return res.redirect('/');
113
+ }
114
+ if (user.banned) {
115
  req.session = null;
116
  return res.send("Account Suspended.");
117
  }
 
125
 
126
  // --- ROUTES ---
127
 
128
+ // Helper to format pins for view safely
129
  function formatPins(pins, username) {
130
  return pins.map(pin => {
131
+ // Handle case where user was deleted but pin exists
132
+ const authorData = localDB.users[pin.author] || { avatar: DEFAULT_AVATAR, displayName: pin.author + " (Deleted)", badge: '' };
133
  return {
134
  ...pin,
135
  authorDisplayName: authorData.displayName || pin.author,
136
+ authorAvatar: authorData.avatar || DEFAULT_AVATAR,
137
+ badge: authorData.badge || '',
138
  likeCount: pin.likes ? pin.likes.length : 0,
139
  hasLiked: pin.likes && username ? pin.likes.includes(username) : false,
140
  comments: pin.comments || []
 
198
  postCount: userPins.length,
199
  followerCount: targetUser.followers ? targetUser.followers.length : 0,
200
  followingCount: targetUser.following ? targetUser.following.length : 0,
201
+ isFollowing: targetUser.followers && targetUser.followers.includes(username),
202
+ badge: targetUser.badge || ''
203
  };
204
 
205
  let currentUser = null;
 
221
  await initDB();
222
  const pin = localDB.pins.find(p => p.id === parseInt(req.params.id));
223
  if(!pin) return res.json({error: "Not found"});
224
+ // Use req.session.user to determine "hasLiked" state in the modal
225
  const formatted = formatPins([pin], req.session.user)[0];
226
  res.json(formatted);
227
  });
 
232
  const users = Object.keys(localDB.users).filter(u => u.toLowerCase().includes(q));
233
  const results = users.map(u => ({
234
  username: u,
235
+ avatar: localDB.users[u].avatar || DEFAULT_AVATAR,
236
+ badge: localDB.users[u].badge || ''
237
  }));
238
  res.json(results);
239
  });
 
290
  if (!req.session.user) return res.status(401).json({error: "Login required"});
291
  await initDB();
292
  const { pinId, text } = req.body;
293
+ if(!text || !text.trim()) return res.json({success:false});
294
+
295
  const pin = localDB.pins.find(p => p.id === parseInt(pinId));
296
+ if (pin) {
297
  if (!pin.comments) pin.comments = [];
298
  const comment = {
299
  id: Date.now(),
 
350
  const { targetUser } = req.body;
351
  if(localDB.users[targetUser] && targetUser !== ADMIN_USER) {
352
  delete localDB.users[targetUser];
353
+ // Remove their pins
354
  localDB.pins = localDB.pins.filter(p => p.author !== targetUser);
355
+ // Clean up follows (optional but good)
356
+ Object.values(localDB.users).forEach(u => {
357
+ if(u.followers) u.followers = u.followers.filter(f => f !== targetUser);
358
+ if(u.following) u.following = u.following.filter(f => f !== targetUser);
359
+ });
360
  await saveDB();
361
  res.json({success: true});
362
  } else {
 
369
  app.post('/signup', async (req, res) => {
370
  const { username, password, email } = req.body;
371
  await initDB();
372
+
373
+ // VALIDATION FIX: Check empty fields
374
+ if(!username || !password) return res.send("Username and Password are required.");
375
+
376
+ // Sanitize username
377
+ const safeUser = username.trim();
378
+ if (localDB.users[safeUser]) return res.send("Username taken.");
379
+
380
+ localDB.users[safeUser] = {
381
+ displayName: safeUser,
382
  hash: bcrypt.hashSync(password, 8),
383
  email: email,
384
  avatar: DEFAULT_AVATAR,
 
388
  banned: false
389
  };
390
  await saveDB();
391
+ req.session.user = safeUser;
392
  res.redirect('/');
393
  });
394
 
395
  app.post('/login', async (req, res) => {
396
  const { username, password } = req.body;
397
  await initDB();
398
+
399
+ // VALIDATION FIX
400
+ if(!username || !password) return res.send("Missing credentials.");
401
+
402
  // Admin Login Check
403
  if (username === ADMIN_USER && password === ADMIN_PASS) {
404
  req.session.user = username;
 
462
  app.get('/logout', (req, res) => { req.session = null; res.redirect('/'); });
463
 
464
  initDB();
465
+ app.listen(7860, () => console.log("Megapin Complete Active"));