Update requirements and minor features
This commit is contained in:
+5
-4
@@ -7,13 +7,14 @@
|
||||
"dev": "nodemon server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"ws": "^8.16.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"helmet": "^8.2.0",
|
||||
"sql.js": "^1.10.3",
|
||||
"cors": "^2.8.5"
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-27
@@ -9,6 +9,7 @@ import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
import cors from 'cors';
|
||||
import helmet from 'helmet';
|
||||
|
||||
const app = express();
|
||||
const server = createServer(app);
|
||||
@@ -98,6 +99,7 @@ function saveDB() {
|
||||
}
|
||||
|
||||
// Middleware
|
||||
app.use(helmet());
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.set('trust proxy', 1);
|
||||
@@ -178,13 +180,18 @@ wss.on('connection', (ws) => {
|
||||
|
||||
// Submit phrase
|
||||
app.post('/api/submit', (req, res) => {
|
||||
const { phrase, timestamps } = req.body;
|
||||
const { timestamps } = req.body;
|
||||
const ip = getClientIP(req);
|
||||
|
||||
if (!phrase || !timestamps || !Array.isArray(timestamps)) {
|
||||
if (!timestamps || !Array.isArray(timestamps)) {
|
||||
return res.status(400).json({ error: 'Invalid request' });
|
||||
}
|
||||
|
||||
// Validate timestamp count
|
||||
if (timestamps.length > 256) {
|
||||
return res.status(400).json({ error: 'Phrase too long (max 256 characters)' });
|
||||
}
|
||||
|
||||
// Check rate limit
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const oneHourAgo = now - 3600;
|
||||
@@ -200,22 +207,7 @@ app.post('/api/submit', (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Validate phrase
|
||||
if (phrase.length !== timestamps.length || phrase.length === 0 || phrase.length > 200) {
|
||||
return res.status(400).json({ error: 'Invalid phrase length' });
|
||||
}
|
||||
|
||||
// Check for duplicate phrase
|
||||
const dupeResult = db.exec(
|
||||
'SELECT COUNT(*) as count FROM submissions WHERE LOWER(TRIM(phrase)) = LOWER(TRIM(?))',
|
||||
[phrase.trim()]
|
||||
);
|
||||
const dupeCount = dupeResult[0]?.values[0]?.[0] || 0;
|
||||
if (dupeCount > 0) {
|
||||
return res.status(400).json({ error: 'This phrase already exists on the leaderboard' });
|
||||
}
|
||||
|
||||
// Validate all letters in one query
|
||||
// Resolve phrase from server-side letter records
|
||||
const placeholders = timestamps.map(() => '?').join(',');
|
||||
const letterResult = db.exec(
|
||||
`SELECT timestamp, letter FROM letters WHERE timestamp IN (${placeholders})`,
|
||||
@@ -227,17 +219,25 @@ app.post('/api/submit', (req, res) => {
|
||||
validLetters.set(ts, lt);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < timestamps.length; i++) {
|
||||
if (validLetters.get(timestamps[i]) !== phrase[i]) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid submission - letters do not match server records'
|
||||
});
|
||||
}
|
||||
const chars = timestamps.map(ts => validLetters.get(ts));
|
||||
if (chars.some(c => c === undefined)) {
|
||||
return res.status(400).json({ error: 'Invalid submission - unknown timestamps' });
|
||||
}
|
||||
const phrase = chars.join('');
|
||||
|
||||
// Check for duplicate phrase
|
||||
const dupeResult = db.exec(
|
||||
'SELECT COUNT(*) as count FROM submissions WHERE LOWER(TRIM(phrase)) = LOWER(TRIM(?))',
|
||||
[phrase]
|
||||
);
|
||||
const dupeCount = dupeResult[0]?.values[0]?.[0] || 0;
|
||||
if (dupeCount > 0) {
|
||||
return res.status(400).json({ error: 'This phrase already exists on the leaderboard' });
|
||||
}
|
||||
|
||||
// Insert submission
|
||||
db.run('INSERT INTO submissions (phrase, timestamps) VALUES (?, ?)',
|
||||
[phrase.trim(), JSON.stringify(timestamps)]);
|
||||
[phrase, JSON.stringify(timestamps)]);
|
||||
|
||||
const lastIdResult = db.exec('SELECT last_insert_rowid() as id');
|
||||
const submissionId = lastIdResult[0].values[0][0];
|
||||
@@ -257,11 +257,12 @@ app.post('/api/submit', (req, res) => {
|
||||
|
||||
// Get leaderboard
|
||||
app.get('/api/leaderboard', (req, res) => {
|
||||
const sort = req.query.sort === 'new' ? 'created_at DESC' : 'votes DESC, created_at DESC';
|
||||
const result = db.exec(`
|
||||
SELECT id, phrase, votes, created_at
|
||||
FROM submissions
|
||||
ORDER BY votes DESC, created_at DESC
|
||||
LIMIT 100
|
||||
ORDER BY ${sort}
|
||||
LIMIT 100
|
||||
`);
|
||||
|
||||
let submissions = [];
|
||||
|
||||
Reference in New Issue
Block a user