Initial commit

This commit is contained in:
Allen
2026-04-30 02:14:25 +00:00
commit 7090e6f01d
243 changed files with 115122 additions and 0 deletions
+287
View File
@@ -0,0 +1,287 @@
const express = require('express');
const { body } = require('express-validator');
const Games = require('../models/games');
const { pool } = require('../models/db');
const { authenticate } = require('../middleware/auth');
const validate = require('../middleware/validate');
const { generateGame, regenerateGame } = require('../utils/claude');
const router = express.Router();
// Create game with AI
router.post('/create',
authenticate,
validate([
body('description')
.trim()
.isLength({ min: 10, max: 1000 })
.withMessage('Game description must be 10-1000 characters'),
body('title')
.optional()
.trim()
.isLength({ min: 1, max: 100 })
]),
async (req, res, next) => {
try {
const { description, title } = req.body;
// Check if user is a guest
if (req.user.is_guest) {
return res.status(403).json({
error: 'Guests cannot create games. Please create an account first.'
});
}
// Generate game with Claude
const result = await generateGame(description);
if (!result.success) {
return res.status(500).json({ error: 'Failed to generate game. Please try again.' });
}
// Create game in database
const gameTitle = title || `Game by ${req.user.username}`;
const game = await Games.create({
creatorId: req.user.id,
title: gameTitle,
description: description,
gameCode: result.code,
prompt: description
});
res.status(201).json({
message: 'Game created successfully',
game: {
id: game.id,
title: game.title,
description: game.description,
status: game.status,
createdAt: game.created_at
},
previewCode: result.code,
tokensUsed: result.tokensUsed || 0
});
} catch (error) {
next(error);
}
}
);
// Get game by ID
router.get('/:gameId',
authenticate,
async (req, res, next) => {
try {
const { gameId } = req.params;
const game = await Games.findById(gameId);
if (!game) {
return res.status(404).json({ error: 'Game not found' });
}
// Only show code if owner or game is published
const isOwner = game.creator_id === req.user.id;
const isPublished = game.status === 'published';
if (!isOwner && !isPublished) {
return res.status(403).json({ error: 'Game not available' });
}
// Prevent caching of game code responses
res.set({
'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate',
'Pragma': 'no-cache',
'Expires': '0'
});
res.json({
game: {
id: game.id,
title: game.title,
description: game.description,
gameCode: game.game_code,
codeVersion: game.code_version || 1,
prompt: game.prompt,
creationPrompt: game.creation_prompt,
originalPrompt: game.original_prompt,
promptWasEdited: game.prompt_was_edited || false,
promptEditCount: game.prompt_edit_count || 0,
promptAnswers: game.prompt_answers,
status: game.status,
playCount: game.play_count,
totalRatings: game.total_ratings,
ratingSum: game.rating_sum,
creatorId: game.creator_id,
creatorUsername: game.creator_username,
creatorDisplayName: game.creator_display_name,
createdAt: game.created_at,
publishedAt: game.published_at,
creatorPlayed: game.creator_played,
creatorPlayDuration: game.creator_play_duration || 0,
isOwner
}
});
} catch (error) {
next(error);
}
}
);
// Update game
router.put('/:gameId',
authenticate,
validate([
body('title').optional().trim().isLength({ min: 1, max: 100 }),
body('description').optional().trim().isLength({ min: 10, max: 1000 }),
body('feedback').optional().trim().isLength({ min: 5, max: 1000 })
]),
async (req, res, next) => {
try {
const { gameId } = req.params;
const { title, description, feedback } = req.body;
const game = await Games.findById(gameId);
if (!game) {
return res.status(404).json({ error: 'Game not found' });
}
if (game.creator_id !== req.user.id) {
return res.status(403).json({ error: 'Not authorized' });
}
if (game.status === 'published') {
return res.status(400).json({ error: 'Cannot edit published games' });
}
let updatedCode = game.game_code;
let tokensUsed = 0;
// If feedback provided, regenerate with Claude
if (feedback) {
const result = await regenerateGame(game.game_code, feedback);
if (result.success) {
updatedCode = result.code;
tokensUsed = result.tokensUsed || 0;
} else {
return res.status(500).json({ error: 'Failed to regenerate game. Please try again.' });
}
}
const updated = await Games.update(gameId, {
title,
description,
gameCode: feedback ? updatedCode : undefined
});
res.json({
message: 'Game updated',
game: {
id: updated.id,
title: updated.title,
description: updated.description,
status: updated.status,
gameCode: updated.game_code
},
tokensUsed
});
} catch (error) {
next(error);
}
}
);
// Submit for review
router.post('/:gameId/submit',
authenticate,
async (req, res, next) => {
try {
const { gameId } = req.params;
const game = await Games.findById(gameId);
if (!game) {
return res.status(404).json({ error: 'Game not found' });
}
if (game.creator_id !== req.user.id) {
return res.status(403).json({ error: 'Not authorized' });
}
if (game.status !== 'draft') {
return res.status(400).json({ error: 'Game has already been submitted' });
}
const updated = await Games.submit(gameId);
res.json({
message: 'Game submitted for review',
game: { id: updated.id, status: updated.status }
});
} catch (error) {
next(error);
}
}
);
// Unpublish game (creator only)
router.post('/:gameId/unpublish',
authenticate,
async (req, res, next) => {
try {
const { gameId } = req.params;
const game = await Games.findById(gameId);
if (!game) {
return res.status(404).json({ error: 'Game not found' });
}
if (game.creator_id !== req.user.id) {
return res.status(403).json({ error: 'Not authorized' });
}
if (game.status !== 'published') {
return res.status(400).json({ error: 'Game is not published' });
}
// Unpublish - set status back to testing so they can re-publish
await pool.query(
`UPDATE games SET status = 'testing', published_at = NULL WHERE id = $1`,
[gameId]
);
res.json({
message: 'Game unpublished. You can edit and re-publish it.',
game: { id: parseInt(gameId), status: 'testing' }
});
} catch (error) {
next(error);
}
}
);
// Delete game
router.delete('/:gameId',
authenticate,
async (req, res, next) => {
try {
const { gameId } = req.params;
const game = await Games.findById(gameId);
if (!game) {
return res.status(404).json({ error: 'Game not found' });
}
if (game.creator_id !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Not authorized' });
}
await Games.delete(gameId);
res.json({ message: 'Game deleted' });
} catch (error) {
next(error);
}
}
);
module.exports = router;