diff --git a/package.json b/package.json index 7cac3ff..8df350e 100755 --- a/package.json +++ b/package.json @@ -13,13 +13,22 @@ "npm": false, "gittip": "bevry", "flattr": "344188/balupton-on-Flattr", - "paypal": "QB8GQPZAH84N6" + "bugs": { + "url": "https://github.com/Docport/inlinegui/issues" + }, + "dependencies": { + "docpad": "~6.78.0", + "express": "~3.4.0", + "mongodb": "~1.3.0", + "levelup": "~0.18.0", + "leveldown": "~0.10.0", + "bcrypt": "~0.7.0", + "validator": "~1.5.0" }, - "keywords": [ - "docpad-plugin", - "docpad", - "gui", - "cms", + "devDependencies": { + "docpad": "~6.78.0" + } +} "inline", "contenteditable" ], @@ -77,4 +86,15 @@ "scripts": { "start": "node ./node_modules/docpad/bin/docpad run --port 9779" } +{ + "name": "inlinegui", + "version": "0.1.0", + "description": "An Inline GUI/CMS for any backend", + "main": "docpad.coffee", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node docpad.coffee" + }, + "repository": { + "type": "git", } diff --git a/server.js b/server.js new file mode 100644 index 0000000..f0d9083 --- /dev/null +++ b/server.js @@ -0,0 +1,126 @@ +var express = require('express'); +var bodyParser = require('body-parser'); +var session = require('express-session'); +var cookieParser = require('cookie-parser'); +var path = require('path'); +var fs = require('fs'); + +// Database setup +var levelup = require('levelup'); +var dbPath = process.env.DB_PATH || './db'; +var db = levelup(dbPath); + +var app = express(); + +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); +app.use(cookieParser()); +app.use(session({ + secret: process.env.SESSION_SECRET || 'inlinegui-secret', + resave: false, + saveUninitialized: true +})); + +// Serve static files from src/files +app.use(express.static(path.join(__dirname, 'src', 'files'))); + +// API Routes + +// Get current user +app.get('/api/user', function(req, res) { + if (req.session.user) { + res.json({ success: true, user: req.session.user }); + } else { + res.json({ success: false, user: null }); + } +}); + +// Create or update user (from Persona login) +app.post('/api/user', function(req, res) { + var userData = req.body; + + if (!userData.email) { + return res.status(400).json({ success: false, error: 'Email is required' }); + } + + var user = { + email: userData.email, + name: userData.name || '', + createdAt: new Date().toISOString() + }; + + // Check if user exists + db.get('user:' + user.email, function(err, existing) { + if (existing) { + // Update existing user + var existingUser = JSON.parse(existing); + user.name = userData.name || existingUser.name || ''; + user.createdAt = existingUser.createdAt || user.createdAt; + } + + db.put('user:' + user.email, JSON.stringify(user), function(err) { + if (err) { + return res.status(500).json({ success: false, error: 'Failed to save user' }); + } + + req.session.user = user; + res.json({ success: true, user: user }); + }); + }); +}); + +// Login user +app.post('/api/login', function(req, res) { + var email = req.body.email; + var name = req.body.name || ''; + + if (!email) { + return res.status(400).json({ success: false, error: 'Email is required' }); + } + + var user = { + email: email, + name: name, + lastLoginAt: new Date().toISOString() + }; + + db.get('user:' + email, function(err, existing) { + if (existing) { + var existingUser = JSON.parse(existing); + user.name = name || existingUser.name || ''; + user.createdAt = existingUser.createdAt; + } + + db.put('user:' + email, JSON.stringify(user), function(err) { + if (err) { + return res.status(500).json({ success: false, error: 'Failed to save user' }); + } + + req.session.user = user; + res.json({ success: true, user: user }); + }); + }); +}); + +// Logout user +app.post('/api/logout', function(req, res) { + req.session.destroy(); + res.json({ success: true }); +}); + +// Serve login page +app.get('/login', function(req, res) { + res.sendFile(path.join(__dirname, 'src', 'files', 'login.html')); +}); + +// Default: serve index +app.get('*', function(req, res) { + res.sendFile(path.join(__dirname, 'src', 'files', 'index.html')); +}); + +var port = process.env.PORT || 3000; +app.listen(port, function() { + console.log('InlineGUI server listening on port ' + port); +}); + +module.exports = app; \ No newline at end of file diff --git a/shanaboo_solution.md b/shanaboo_solution.md new file mode 100644 index 0000000..6a43de8 --- /dev/null +++ b/shanaboo_solution.md @@ -0,0 +1,182 @@ + ```diff +--- a/package.json ++++ b/package.json +@@ -0,0 +0,0 +-{ +- "name": "inlinegui", +- "version": "0.1.0", +- "description": "An Inline GUI/CMS for any backend", +- "main": "docpad.coffee", +- "scripts": { +- "test": "echo \"Error: no test specified\" && exit 1" +- }, +- "repository": { +- "type": "git", +- "url": "https://github.com/Docport/inlinegui.git" +- }, +- "author": "", +- "license": "MIT" +-} ++{ ++ "name": "inlinegui", ++ "version": "0.1.0", ++ "description": "An Inline GUI/CMS for any backend", ++ "main": "docpad.coffee", ++ "scripts": { ++ "test": "echo \"Error: no test specified\" && exit 1" ++ }, ++ "repository": { ++ "type": "git", ++ "url": "https://github.com/Docport/inlinegui.git" ++ }, ++ "author": "", ++ "license": "MIT", ++ "dependencies": { ++ "levelup": "~0.18.0", ++ "leveldown": "~0.10.0", ++ "express": "~3.4.0", ++ "body-parser": "~1.0.0" ++ } ++} +--- a/docpad.coffee ++++ b/docpad.coffee +@@ -0,0 +0,0 +-# Docpad Configuration File +-# http://docpad.org/docs/config +- +-docpadConfig = { +- # ... +-} +- +-# Export our DocPad Configuration +-module.exports = docpadConfig ++# Docpad Configuration File ++# http://docpad.org/docs/config ++ ++levelup = require('levelup') ++path = require('path') ++ ++# Initialize LevelUP database ++dbPath = path.join(__dirname, 'data', 'users') ++db = levelup(dbPath) ++ ++docpadConfig = { ++ # Server configuration for Persona authentication and user management ++ serverExtend: ++ server: (server, express, docpadInstance) -> ++ # Parse JSON body ++ express.use(express.bodyParser()) ++ ++ # Serve static files for login page ++ express.use('/login', express.static(path.join(__dirname, 'src', 'files', 'login'))) ++ ++ # Persona verification endpoint ++ express.post('/auth/persona/verify', (req, res) -> ++ assertion = req.body.assertion ++ ++ # Verify assertion with Mozilla Persona ++ https = require('https') ++ querystring = require('querystring') ++ ++ data = querystring.stringify({ ++ assertion: assertion, ++ audience: req.headers.host ++ }) ++ ++ options = { ++ host: 'verifier.login.persona.org', ++ path: '/verify', ++ method: 'POST', ++ headers: { ++ 'Content-Type': 'application/x-www-form-urlencoded', ++ 'Content-Length': Buffer.byteLength(data) ++ } ++ } ++ ++ verifyReq = https.request(options, (verifyRes) -> ++ body = '' ++ verifyRes.on('data', (chunk) -> body += chunk) ++ verifyRes.on('end', () -> ++ try ++ response = JSON.parse(body) ++ ++ if response.status is 'okay' ++ email = response.email ++ ++ # Check if user exists, create if not ++ db.get(email, (err, userData) -> ++ if err and err.notFound ++ # Create new user ++ user = { ++ email: email, ++ name: email.split('@')[0], ++ createdAt: new Date().toISOString() ++ } ++ ++ db.put(email, JSON.stringify(user), (err) -> ++ if err ++ res.status(500).json({status: 'error', message: 'Failed to save user'}) ++ return ++ ++ req.session ?= {} ++ req.session.user = user ++ res.json({status: 'okay', email: email, name: user.name}) ++ ) ++ else if err ++ res.status(500).json({status: 'error', message: 'Database error'}) ++ else ++ # User exists, update session ++ user = JSON.parse(userData) ++ req.session ?= {} ++ req.session.user = user ++ res.json({status: 'okay', email: email, name: user.name}) ++ ) ++ else ++ res.status(403).json({status: 'failure', reason: response.reason}) ++ catch e ++ res.status(500).json({status: 'error', message: 'Invalid response from verifier'}) ++ ) ++ ) ++ ++ verifyReq.on('error', (err) -> ++ res.status(500).json({status: 'error', message: 'Verification request failed'}) ++ ) ++ ++ verifyReq.write(data) ++ verifyReq.end() ++ ) ++ ++ # Update user info endpoint ++ express.post('/api/user/update', (req, res) -> ++ email = req.body.email ++ name = req.body.name ++ ++ if not email or not name ++ res.status(400).json({status: 'error', message: 'Email and name required'}) ++ return ++ ++ db.get(email, (err, userData) -> ++ if err and err.notFound ++ res.status(404).json({status: 'error', message: 'User not found'}) ++ else if err ++ res.status(500).json({status: 'error', message: 'Database error'}) ++ else ++ user = JSON.parse(userData) ++ user.name = name ++ ++ db.put(email, JSON.stringify(user), (err) -> ++ if err ++ res.status(500).json({status: 'error', message: 'Failed to update user'}) ++ return ++ ++ req.session ?= {} ++ req.session.user = user ++ res.json({status: 'okay', user: user}) ++ ) ++ ) ++ ) ++ ++ # Get current user endpoint ++ express.get('/api/user/current', (req, res) -> ++ if req.session?.user ++ res.json({status: 'okay', user: req.session.user})