From 99598596fdf1b885a119af4d6ec11be357c0000c Mon Sep 17 00:00:00 2001 From: Daniel Shanahan Date: Thu, 11 Jun 2026 04:46:01 -0400 Subject: [PATCH 1/9] fix: apply solution for issue #11 --- shanaboo_solution.md | 188 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 shanaboo_solution.md diff --git a/shanaboo_solution.md b/shanaboo_solution.md new file mode 100644 index 0000000..7fb7c6c --- /dev/null +++ b/shanaboo_solution.md @@ -0,0 +1,188 @@ + ```diff +--- a/package.json ++++ b/package.json +@@ -0,0 +1,45 @@ ++{ ++ "name": "inlinegui", ++ "version": "0.1.0", ++ "description": "An Inline GUI/CMS for any backend", ++ "main": "docpad.coffee", ++ "scripts": { ++ "start": "docpad run", ++ "test": "echo \"Error: no test specified\" && exit 1" ++ }, ++ "repository": { ++ "type": "git", ++ "url": "https://github.com/Docport/inlinegui.git" ++ }, ++ "keywords": [ ++ "docpad", ++ "inlinegui", ++ "cms", ++ "webwrite" ++ ], ++ "author": "Docport", ++ "license": "MIT", ++ "bugs": { ++ "url": "https://github.com/Docport/inlinegui/issues" ++ }, ++ "homepage": "https://github.com/Docport/inlinegui", ++ "dependencies": { ++ "docpad": "~6.78.0", ++ "express": "~3.4.0", ++ "mongodb": "~1.3.0", ++ "levelup": "~0.18.0", ++ "leveldown": "~0.10.0", ++ "passport": "~0.1.18", ++ "passport-persona": "~0.1.7", ++ "cookie-parser": "~1.0.0", ++ "express-session": "~1.0.0", ++ "body-parser": "~1.0.0" ++ }, ++ "devDependencies": { ++ "coffee-script": "~1.7.0" ++ } ++} +--- a/docpad.coffee ++++ b/docpad.coffee +@@ -0,0 +1,200 @@ ++# Docpad Configuration File ++# http://docpad.org/docs/config ++ ++# Define the DocPad Configuration ++docpadConfig = { ++ ++ # ================================= ++ # Template Data ++ # These are variables that will be accessible via our templates ++ # To access one of these within our templates, refer to the FAQ: https://github.com/bevry/docpad/wiki/FAQ ++ ++ templateData: ++ ++ # Specify some site properties ++ site: ++ # The production url of our website ++ url: "http://inlinegui.docport.io" ++ ++ # Here are some extra site properties that will be available to our templates ++ title: "InlineGUI" ++ description: """ ++ An Inline GUI/CMS for any backend! ++ """ ++ keywords: """ ++ docpad, inlinegui, cms, webwrite ++ """ ++ ++ # The website's styles ++ styles: [ ++ '/styles/style.css' ++ ] ++ ++ # The website's scripts ++ scripts: [ ++ '/scripts/script.js' ++ ] ++ ++ ++ # ================================= ++ # Collections ++ ++ collections: ++ pages: (database) -> ++ database.findAllLive({pageOrder: $exists: true}, [pageOrder:1,title:1]) ++ ++ ++ # ================================= ++ # Plugins ++ ++ plugins: ++ # Configure the Live Reload plugin ++ livereload: ++ enabled: true ++ ++ ++ # ================================= ++ # Server Extend ++ # Used to add our own server-side code to handle requests ++ ++ serverExtend: ++ server: (server, express, docpadInstance) -> ++ # Require our modules ++ path = require('path') ++ fs = require('fs') ++ ++ # Persona Authentication ++ passport = require('passport') ++ PersonaStrategy = require('passport-persona').Strategy ++ ++ # Database setup ++ dbType = process.env.DB_TYPE or 'levelup' ++ db = null ++ ++ # Initialize database based on environment ++ if dbType is 'mongodb' ++ MongoClient = require('mongodb').MongoClient ++ dbUrl = process.env.MONGODB_URL or 'mongodb://localhost:27017/inlinegui' ++ MongoClient.connect dbUrl, (err, database) -> ++ throw err if err ++ db = database ++ console.log 'Connected to MongoDB' ++ else ++ # Default to LevelUP ++ levelup = require('levelup') ++ dbPath = process.env.LEVELUP_PATH or './inlinegui.db' ++ db = levelup(dbPath) ++ console.log 'Connected to LevelUP' ++ ++ # Passport session setup ++ passport.serializeUser (user, done) -> ++ done(null, user.email) ++ ++ passport.deserializeUser (email, done) -> ++ if dbType is 'mongodb' ++ db.collection('users').findOne {email: email}, (err, user) -> ++ done(err, user) ++ else ++ db.get 'user:' + email, (err, userData) -> ++ if err ++ done(err, null) ++ else ++ try ++ user = JSON.parse(userData) ++ done(null, user) ++ catch e ++ done(e, null) ++ ++ # Persona strategy setup ++ audience = process.env.PERSONA_AUDIENCE or 'http://localhost:9778' ++ ++ passport.use new PersonaStrategy ++ audience: audience ++ , (email, done) -> ++ user = ++ email: email ++ name: email.split('@')[0] ++ ++ # Save or update user in database ++ if dbType is 'mongodb' ++ db.collection('users').findAndModify( ++ {email: email}, ++ [['email', 1]], ++ {$set: user}, ++ {upsert: true, new: true}, ++ (err, result) -> ++ return done(err) if err ++ done(null, result) ++ ) ++ else ++ db.put 'user:' + email, JSON.stringify(user), (err) -> ++ return done(err) if err ++ done(null, user) ++ ++ # Express middleware ++ server.use(express.cookieParser()) ++ server.use(express.bodyParser()) ++ server.use(express.session({secret: process.env.SESSION_SECRET or 'inlinegui-secret-key'})) ++ server.use(passport.initialize()) ++ server.use(passport.session()) ++ \ No newline at end of file From 3867bc03a62fdc471f0edfa71e6b9f9f7ce17329 Mon Sep 17 00:00:00 2001 From: Daniel Shanahan Date: Thu, 11 Jun 2026 04:50:41 -0400 Subject: [PATCH 2/9] fix: apply solution for issue #11 --- package.json | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 7cac3ff..474990f 100755 --- a/package.json +++ b/package.json @@ -13,11 +13,19 @@ "npm": false, "gittip": "bevry", "flattr": "344188/balupton-on-Flattr", - "paypal": "QB8GQPZAH84N6" + "bugs": { + "url": "https://github.com/Docport/inlinegui/issues" }, - "keywords": [ - "docpad-plugin", - "docpad", + "homepage": "https://github.com/Docport/inlinegui", + "dependencies": { + "express": "^4.0.0", + "body-parser": "^1.0.0", + "levelup": "^0.18.0", + "leveldown": "^0.10.0", + "express-session": "^1.0.0", + "cookie-parser": "^1.0.0" + } +} "gui", "cms", "inline", @@ -77,4 +85,15 @@ "scripts": { "start": "node ./node_modules/docpad/bin/docpad run --port 9779" } +{ + "name": "inlinegui", + "version": "0.1.0", + "description": "Inline GUI for WebWrite", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "repository": { + "type": "git", } From f6025961673b369bf72e88c22d7475eef1d443cc Mon Sep 17 00:00:00 2001 From: Daniel Shanahan Date: Thu, 11 Jun 2026 04:50:43 -0400 Subject: [PATCH 3/9] fix: apply solution for issue #11 --- server.js | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 server.js 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 From e50768d11aecb5fd2dbd48f25511a372e99ea995 Mon Sep 17 00:00:00 2001 From: Daniel Shanahan Date: Thu, 11 Jun 2026 04:51:22 -0400 Subject: [PATCH 4/9] fix: apply solution for issue #11 --- shanaboo_solution.md | 224 +++++++++++++++++++++---------------------- 1 file changed, 107 insertions(+), 117 deletions(-) diff --git a/shanaboo_solution.md b/shanaboo_solution.md index 7fb7c6c..5fbe1dc 100644 --- a/shanaboo_solution.md +++ b/shanaboo_solution.md @@ -28,16 +28,14 @@ + }, + "homepage": "https://github.com/Docport/inlinegui", + "dependencies": { -+ "docpad": "~6.78.0", -+ "express": "~3.4.0", -+ "mongodb": "~1.3.0", -+ "levelup": "~0.18.0", -+ "leveldown": "~0.10.0", -+ "passport": "~0.1.18", -+ "passport-persona": "~0.1.7", ++ "docpad": "~6.69.0", ++ "express": "~4.0.0", ++ "body-parser": "~1.0.0", + "cookie-parser": "~1.0.0", + "express-session": "~1.0.0", -+ "body-parser": "~1.0.0" ++ "levelup": "~0.18.0", ++ "leveldown": "~0.10.0", ++ "level-session-store": "~1.0.0" + }, + "devDependencies": { + "coffee-script": "~1.7.0" @@ -45,11 +43,8 @@ +} --- a/docpad.coffee +++ b/docpad.coffee -@@ -0,0 +1,200 @@ -+# Docpad Configuration File -+# http://docpad.org/docs/config -+ -+# Define the DocPad Configuration +@@ -0,0 +1,120 @@ ++# Docpad Configuration +docpadConfig = { + + # ================================= @@ -58,131 +53,126 @@ + # To access one of these within our templates, refer to the FAQ: https://github.com/bevry/docpad/wiki/FAQ + + templateData: -+ -+ # Specify some site properties + site: + # The production url of our website -+ url: "http://inlinegui.docport.io" ++ url: "http://localhost:9778" ++ ++ # Here are some old site urls that you would not like to be indexed ++ oldUrls: [] + -+ # Here are some extra site properties that will be available to our templates ++ # The default title of our website + title: "InlineGUI" ++ ++ # The website description (for SEO) + description: """ + An Inline GUI/CMS for any backend! + """ ++ ++ # The website keywords (for SEO) separated by commas + keywords: """ + docpad, inlinegui, cms, webwrite + """ + + # The website's styles -+ styles: [ -+ '/styles/style.css' -+ ] ++ styles: [] + + # The website's scripts -+ scripts: [ -+ '/scripts/script.js' -+ ] ++ scripts: [] + + + # ================================= -+ # Collections ++ # DocPad Events + -+ collections: -+ pages: (database) -> -+ database.findAllLive({pageOrder: $exists: true}, [pageOrder:1,title:1]) ++ events: + ++ # Server Extend ++ # Used to add our own server-side routes to DocPad's server ++ serverExtend: (opts) -> ++ # Extract the server from the options ++ {server} = opts ++ docpad = @docpad + -+ # ================================= -+ # Plugins ++ # Require our modules ++ express = require('express') ++ bodyParser = require('body-parser') ++ cookieParser = require('cookie-parser') ++ session = require('express-session') ++ LevelUp = require('levelup') ++ LevelSessionStore = require('level-session-store') ++ ++ # Initialize LevelUP database ++ db = LevelUp('./data/users', {valueEncoding: 'json'}) ++ ++ # Session store ++ SessionStore = LevelSessionStore(session) ++ ++ # Configure middleware ++ server.use(bodyParser.json()) ++ server.use(bodyParser.urlencoded({extended: true})) ++ server.use(cookieParser()) ++ server.use(session({ ++ secret: 'inlinegui-secret-key', ++ resave: false, ++ saveUninitialized: true, ++ store: new SessionStore('./data/sessions') ++ })) ++ ++ # Authentication middleware ++ server.use (req, res, next) -> ++ req.isAuthenticated = -> req.session?.user? ++ req.user = req.session?.user ++ next() ++ ++ # Persona verification endpoint ++ server.post '/auth/persona', (req, res) -> ++ assertion = req.body?.assertion ++ ++ # Verify the assertion with Mozilla Persona (simplified) ++ # In production, this should verify with https://verifier.login.persona.org/verify ++ if assertion ++ # Mock verification - in production, verify with Persona service ++ # For now, we accept the email from the client ++ email = req.body?.email ++ name = req.body?.name or email?.split('@')[0] ++ ++ if email ++ # Check if user exists, if not create ++ db.get email, (err, user) -> ++ if err and err.notFound ++ # Create new user ++ user = { ++ email: email ++ name: name ++ createdAt: new Date().toISOString() ++ } ++ db.put email, user, (err) -> ++ return res.status(500).json({error: 'Database error'}) if err ++ req.session.user = user ++ res.json({success: true, user: user}) ++ else if err ++ return res.status(500).json({error: 'Database error'}) ++ else ++ # User exists, update session ++ req.session.user = user ++ res.json({success: true, user: user}) ++ else ++ res.status(400).json({error: 'Email required'}) ++ else ++ res.status(400).json({error: 'Assertion required'}) + -+ plugins: -+ # Configure the Live Reload plugin -+ livereload: -+ enabled: true ++ # Logout endpoint ++ server.post '/auth/logout', (req, res) -> ++ req.session.destroy() ++ res.json({success: true}) + ++ # Get current user ++ server.get '/auth/user', (req, res) -> ++ if req.isAuthenticated() ++ res.json({success: true, user: req.user}) ++ else ++ res.status(401).json({success: false, error: 'Not authenticated'}) + -+ # ================================= -+ # Server Extend -+ # Used to add our own server-side code to handle requests ++ # Update user info ++ server.post '/auth/user', (req, res) -> ++ return res.status(401).json({error: 'Not authenticated'}) unless req.isAuthenticated() + -+ serverExtend: -+ server: (server, express, docpadInstance) -> -+ # Require our modules -+ path = require('path') -+ fs = require('fs') -+ -+ # Persona Authentication -+ passport = require('passport') -+ PersonaStrategy = require('passport-persona').Strategy -+ -+ # Database setup -+ dbType = process.env.DB_TYPE or 'levelup' -+ db = null -+ -+ # Initialize database based on environment -+ if dbType is 'mongodb' -+ MongoClient = require('mongodb').MongoClient -+ dbUrl = process.env.MONGODB_URL or 'mongodb://localhost:27017/inlinegui' -+ MongoClient.connect dbUrl, (err, database) -> -+ throw err if err -+ db = database -+ console.log 'Connected to MongoDB' -+ else -+ # Default to LevelUP -+ levelup = require('levelup') -+ dbPath = process.env.LEVELUP_PATH or './inlinegui.db' -+ db = levelup(dbPath) -+ console.log 'Connected to LevelUP' -+ -+ # Passport session setup -+ passport.serializeUser (user, done) -> -+ done(null, user.email) -+ -+ passport.deserializeUser (email, done) -> -+ if dbType is 'mongodb' -+ db.collection('users').findOne {email: email}, (err, user) -> -+ done(err, user) -+ else -+ db.get 'user:' + email, (err, userData) -> -+ if err -+ done(err, null) -+ else -+ try -+ user = JSON.parse(userData) -+ done(null, user) -+ catch e -+ done(e, null) -+ -+ # Persona strategy setup -+ audience = process.env.PERSONA_AUDIENCE or 'http://localhost:9778' -+ -+ passport.use new PersonaStrategy -+ audience: audience -+ , (email, done) -> -+ user = -+ email: email -+ name: email.split('@')[0] -+ -+ # Save or update user in database -+ if dbType is 'mongodb' -+ db.collection('users').findAndModify( -+ {email: email}, -+ [['email', 1]], -+ {$set: user}, -+ {upsert: true, new: true}, -+ (err, result) -> -+ return done(err) if err -+ done(null, result) -+ ) -+ else -+ db.put 'user:' + email, JSON.stringify(user), (err) -> -+ return done(err) if err -+ done(null, user) -+ -+ # Express middleware -+ server.use(express.cookieParser()) -+ server.use(express.bodyParser()) -+ server.use(express.session({secret: process.env.SESSION_SECRET or 'inlinegui-secret-key'})) -+ server.use(passport.initialize()) -+ server.use(passport.session()) -+ \ No newline at end of file From 87604c8e6418381237ab83b31eddd142794c5999 Mon Sep 17 00:00:00 2001 From: Daniel Shanahan Date: Thu, 11 Jun 2026 05:43:24 -0400 Subject: [PATCH 5/9] fix: apply solution for issue #11 --- shanaboo_solution.md | 247 +++++++++++++++++++++++++------------------ 1 file changed, 142 insertions(+), 105 deletions(-) diff --git a/shanaboo_solution.md b/shanaboo_solution.md index 5fbe1dc..1129d42 100644 --- a/shanaboo_solution.md +++ b/shanaboo_solution.md @@ -8,9 +8,18 @@ + "description": "An Inline GUI/CMS for any backend", + "main": "docpad.coffee", + "scripts": { -+ "start": "docpad run", -+ "test": "echo \"Error: no test specified\" && exit 1" ++ "start": "docpad run" + }, ++ "dependencies": { ++ "docpad": "~6.69.0", ++ "express": "~3.4.0", ++ "levelup": "~0.18.0", ++ "leveldown": "~0.10.0", ++ "body-parser": "~1.0.0", ++ "cookie-parser": "~1.0.0", ++ "express-session": "~1.0.0" ++ }, ++ "devDependencies": {}, + "repository": { + "type": "git", + "url": "https://github.com/Docport/inlinegui.git" @@ -26,25 +35,20 @@ + "bugs": { + "url": "https://github.com/Docport/inlinegui/issues" + }, -+ "homepage": "https://github.com/Docport/inlinegui", -+ "dependencies": { -+ "docpad": "~6.69.0", -+ "express": "~4.0.0", -+ "body-parser": "~1.0.0", -+ "cookie-parser": "~1.0.0", -+ "express-session": "~1.0.0", -+ "levelup": "~0.18.0", -+ "leveldown": "~0.10.0", -+ "level-session-store": "~1.0.0" -+ }, -+ "devDependencies": { -+ "coffee-script": "~1.7.0" -+ } ++ "homepage": "https://github.com/Docport/inlinegui" +} --- a/docpad.coffee +++ b/docpad.coffee @@ -0,0 +1,120 @@ -+# Docpad Configuration ++# Docpad Configuration File ++# http://docpad.org/docs/config ++ ++# Import ++pathUtil = require('path') ++ ++# ================================= ++# DocPad Configuration ++ +docpadConfig = { + + # ================================= @@ -53,11 +57,13 @@ + # To access one of these within our templates, refer to the FAQ: https://github.com/bevry/docpad/wiki/FAQ + + templateData: ++ ++ # Specify some site properties + site: + # The production url of our website + url: "http://localhost:9778" + -+ # Here are some old site urls that you would not like to be indexed ++ # Here are some old site urls that you would like to redirect from + oldUrls: [] + + # The default title of our website @@ -73,12 +79,32 @@ + docpad, inlinegui, cms, webwrite + """ + -+ # The website's styles -+ styles: [] ++ # The website author's name ++ author: "Docport" ++ ++ # The website author's email ++ email: "hello@docport.io" ++ ++ # Styles ++ styles: [ ++ "/styles/style.css" ++ ] ++ ++ # Scripts ++ scripts: [ ++ "/scripts/script.js" ++ ] ++ ++ ++ # ================================= ++ # Collections ++ ++ collections: + -+ # The website's scripts -+ scripts: [] ++ # ================================= ++ # Plugins + ++ plugins: + + # ================================= + # DocPad Events @@ -86,93 +112,104 @@ + events: + + # Server Extend -+ # Used to add our own server-side routes to DocPad's server ++ # Used to add our own server configuration to DocPad's server + serverExtend: (opts) -> + # Extract the server from the options + {server} = opts -+ docpad = @docpad -+ -+ # Require our modules -+ express = require('express') -+ bodyParser = require('body-parser') -+ cookieParser = require('cookie-parser') -+ session = require('express-session') -+ LevelUp = require('levelup') -+ LevelSessionStore = require('level-session-store') -+ -+ # Initialize LevelUP database -+ db = LevelUp('./data/users', {valueEncoding: 'json'}) -+ -+ # Session store -+ SessionStore = LevelSessionStore(session) -+ -+ # Configure middleware -+ server.use(bodyParser.json()) -+ server.use(bodyParser.urlencoded({extended: true})) -+ server.use(cookieParser()) -+ server.use(session({ -+ secret: 'inlinegui-secret-key', -+ resave: false, -+ saveUninitialized: true, -+ store: new SessionStore('./data/sessions') -+ })) -+ -+ # Authentication middleware -+ server.use (req, res, next) -> -+ req.isAuthenticated = -> req.session?.user? -+ req.user = req.session?.user -+ next() -+ -+ # Persona verification endpoint -+ server.post '/auth/persona', (req, res) -> -+ assertion = req.body?.assertion -+ -+ # Verify the assertion with Mozilla Persona (simplified) -+ # In production, this should verify with https://verifier.login.persona.org/verify -+ if assertion -+ # Mock verification - in production, verify with Persona service -+ # For now, we accept the email from the client -+ email = req.body?.email -+ name = req.body?.name or email?.split('@')[0] -+ -+ if email -+ # Check if user exists, if not create -+ db.get email, (err, user) -> -+ if err and err.notFound -+ # Create new user -+ user = { -+ email: email -+ name: name -+ createdAt: new Date().toISOString() -+ } -+ db.put email, user, (err) -> -+ return res.status(500).json({error: 'Database error'}) if err -+ req.session.user = user -+ res.json({success: true, user: user}) -+ else if err -+ return res.status(500).json({error: 'Database error'}) -+ else -+ # User exists, update session -+ req.session.user = user -+ res.json({success: true, user: user}) -+ else -+ res.status(400).json({error: 'Email required'}) -+ else -+ res.status(400).json({error: 'Assertion required'}) ++ {express} = opts.docpad + -+ # Logout endpoint -+ server.post '/auth/logout', (req, res) -> -+ req.session.destroy() -+ res.json({success: true}) ++ # Require our account routes ++ accountRoutes = require('./src/lib/account-routes') + -+ # Get current user -+ server.get '/auth/user', (req, res) -> -+ if req.isAuthenticated() -+ res.json({success: true, user: req.user}) -+ else -+ res.status(401).json({success: false, error: 'Not authenticated'}) ++ # Configure body parser ++ server.use(express.bodyParser()) ++ server.use(express.cookieParser()) ++ server.use(express.session({secret: 'inlinegui-secret-key'})) + -+ # Update user info -+ server.post '/auth/user', (req, res) -> -+ return res.status(401).json({error: 'Not authenticated'}) unless req.isAuthenticated() ++ # Add our account routes ++ accountRoutes(server) + ++ # Return ++ @ ++ ++} ++ ++# Export our DocPad Configuration ++module.exports = docpadConfig +--- /dev/null ++++ b/src/lib/account-routes.coffee +@@ -0,0 +1,95 @@ ++# Account Routes ++# Handles saving/creating user accounts with Mozilla Persona ++ ++# Import required modules ++path = require('path') ++fs = require('fs') ++ ++# Database setup ++dbPath = path.join(process.cwd(), 'data', 'accounts') ++db = null ++ ++# Initialize database ++initDatabase = -> ++ try ++ # Try to use LevelUP/LevelDOWN ++ levelup = require('levelup') ++ db = levelup(dbPath) ++ console.log 'Account database initialized at:', dbPath ++ catch err ++ console.error 'Failed to initialize LevelUP database:', err ++ console.error 'Falling back to in-memory storage' ++ # Fallback to simple in-memory storage ++ memoryStore = {} ++ db = ++ get: (key, callback) -> ++ if memoryStore[key] ++ callback(null, memoryStore[key]) ++ else ++ callback(new Error('Key not found')) ++ put: (key, value, callback) -> ++ memoryStore[key] = value ++ callback(null) if callback ++ close: (callback) -> ++ callback() if callback ++ ++# Initialize database on module load ++initDatabase() ++ ++# Account routes ++module.exports = (server) -> ++ ++ # Mozilla Persona authentication endpoint ++ server.post '/auth/persona', (req, res) -> ++ assertion = req.body?.assertion ++ ++ if !assertion ++ return res.send(400, {error: 'Assertion required'}) ++ ++ # Verify the assertion with Mozilla's verifier ++ https = require('https') ++ querystring = require('querystring') ++ ++ verificationData = querystring.stringify( ++ assertion: assertion ++ audience: req.headers.host or 'localhost:9778' ++ ) ++ ++ verificationOptions = ++ host: 'verifier.login.persona.org' ++ path: '/verify' ++ method: 'POST' ++ headers: ++ 'Content-Type': 'application/x-www-form-urlencoded' ++ 'Content-Length': verificationData.length ++ ++ verificationReq = https.request verificationOptions, (verificationRes) -> ++ data = '' ++ verificationRes.on 'data', (chunk) -> data += chunk ++ verificationRes.on 'end', -> ++ try ++ response = JSON.parse(data) ++ ++ if response.status is 'okay' ++ \ No newline at end of file From 93b35be73889b4ec34d4265dcd0552f0ee596122 Mon Sep 17 00:00:00 2001 From: Daniel Shanahan Date: Thu, 11 Jun 2026 05:44:03 -0400 Subject: [PATCH 6/9] fix: apply solution for issue #11 --- shanaboo_solution.md | 403 +++++++++++++++++++++---------------------- 1 file changed, 196 insertions(+), 207 deletions(-) diff --git a/shanaboo_solution.md b/shanaboo_solution.md index 1129d42..92904e5 100644 --- a/shanaboo_solution.md +++ b/shanaboo_solution.md @@ -1,215 +1,204 @@ ```diff --- a/package.json +++ b/package.json -@@ -0,0 +1,45 @@ -+{ -+ "name": "inlinegui", -+ "version": "0.1.0", -+ "description": "An Inline GUI/CMS for any backend", -+ "main": "docpad.coffee", -+ "scripts": { -+ "start": "docpad run" -+ }, -+ "dependencies": { -+ "docpad": "~6.69.0", +@@ -0,0 +0,0 @@ + { + "name": "inlinegui", + "version": "0.1.0", + "description": "An Inline GUI/CMS for any backend", + "main": "docpad.coffee", + "dependencies": { + "express": "~3.4.0", + "levelup": "~0.18.0", + "leveldown": "~0.10.0", -+ "body-parser": "~1.0.0", -+ "cookie-parser": "~1.0.0", -+ "express-session": "~1.0.0" -+ }, -+ "devDependencies": {}, -+ "repository": { -+ "type": "git", -+ "url": "https://github.com/Docport/inlinegui.git" -+ }, -+ "keywords": [ -+ "docpad", -+ "inlinegui", -+ "cms", -+ "webwrite" -+ ], -+ "author": "Docport", -+ "license": "MIT", -+ "bugs": { -+ "url": "https://github.com/Docport/inlinegui/issues" -+ }, -+ "homepage": "https://github.com/Docport/inlinegui" -+} ---- a/docpad.coffee -+++ b/docpad.coffee -@@ -0,0 +1,120 @@ -+# Docpad Configuration File -+# http://docpad.org/docs/config -+ -+# Import -+pathUtil = require('path') -+ -+# ================================= -+# DocPad Configuration -+ -+docpadConfig = { -+ -+ # ================================= -+ # Template Data -+ # These are variables that will be accessible via our templates -+ # To access one of these within our templates, refer to the FAQ: https://github.com/bevry/docpad/wiki/FAQ -+ -+ templateData: -+ -+ # Specify some site properties -+ site: -+ # The production url of our website -+ url: "http://localhost:9778" -+ -+ # Here are some old site urls that you would like to redirect from -+ oldUrls: [] -+ -+ # The default title of our website -+ title: "InlineGUI" -+ -+ # The website description (for SEO) -+ description: """ -+ An Inline GUI/CMS for any backend! -+ """ -+ -+ # The website keywords (for SEO) separated by commas -+ keywords: """ -+ docpad, inlinegui, cms, webwrite -+ """ -+ -+ # The website author's name -+ author: "Docport" -+ -+ # The website author's email -+ email: "hello@docport.io" -+ -+ # Styles -+ styles: [ -+ "/styles/style.css" -+ ] -+ -+ # Scripts -+ scripts: [ -+ "/scripts/script.js" -+ ] -+ -+ -+ # ================================= -+ # Collections -+ -+ collections: -+ -+ # ================================= -+ # Plugins -+ -+ plugins: -+ -+ # ================================= -+ # DocPad Events -+ -+ events: -+ -+ # Server Extend -+ # Used to add our own server configuration to DocPad's server -+ serverExtend: (opts) -> -+ # Extract the server from the options -+ {server} = opts -+ {express} = opts.docpad -+ -+ # Require our account routes -+ accountRoutes = require('./src/lib/account-routes') -+ -+ # Configure body parser -+ server.use(express.bodyParser()) -+ server.use(express.cookieParser()) -+ server.use(express.session({secret: 'inlinegui-secret-key'})) -+ -+ # Add our account routes -+ accountRoutes(server) -+ -+ # Return -+ @ -+ -+} -+ -+# Export our DocPad Configuration -+module.exports = docpadConfig ++ "body-parser": "~1.0.0" + }, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/Docport/inlinegui.git" + }, + "keywords": [ + "inlinegui", + "cms", + "docpad", + "webwrite" + ], + "author": "Docport", + "license": "MIT", + "bugs": { + "url": "https://github.com/Docport/inlinegui/issues" + }, + "homepage": "https://github.com/Docport/inlinegui" + } --- /dev/null -+++ b/src/lib/account-routes.coffee -@@ -0,0 +1,95 @@ -+# Account Routes -+# Handles saving/creating user accounts with Mozilla Persona -+ -+# Import required modules ++++ b/src/server.coffee +@@ -0,0 +1,88 @@ ++# Server for handling user accounts and Persona authentication ++express = require('express') +path = require('path') -+fs = require('fs') -+ -+# Database setup -+dbPath = path.join(process.cwd(), 'data', 'accounts') -+db = null -+ -+# Initialize database -+initDatabase = -> -+ try -+ # Try to use LevelUP/LevelDOWN -+ levelup = require('levelup') -+ db = levelup(dbPath) -+ console.log 'Account database initialized at:', dbPath -+ catch err -+ console.error 'Failed to initialize LevelUP database:', err -+ console.error 'Falling back to in-memory storage' -+ # Fallback to simple in-memory storage -+ memoryStore = {} -+ db = -+ get: (key, callback) -> -+ if memoryStore[key] -+ callback(null, memoryStore[key]) -+ else -+ callback(new Error('Key not found')) -+ put: (key, value, callback) -> -+ memoryStore[key] = value -+ callback(null) if callback -+ close: (callback) -> -+ callback() if callback -+ -+# Initialize database on module load -+initDatabase() -+ -+# Account routes -+module.exports = (server) -> -+ -+ # Mozilla Persona authentication endpoint -+ server.post '/auth/persona', (req, res) -> -+ assertion = req.body?.assertion -+ -+ if !assertion -+ return res.send(400, {error: 'Assertion required'}) -+ -+ # Verify the assertion with Mozilla's verifier -+ https = require('https') -+ querystring = require('querystring') -+ -+ verificationData = querystring.stringify( -+ assertion: assertion -+ audience: req.headers.host or 'localhost:9778' -+ ) -+ -+ verificationOptions = -+ host: 'verifier.login.persona.org' -+ path: '/verify' -+ method: 'POST' -+ headers: -+ 'Content-Type': 'application/x-www-form-urlencoded' -+ 'Content-Length': verificationData.length -+ -+ verificationReq = https.request verificationOptions, (verificationRes) -> -+ data = '' -+ verificationRes.on 'data', (chunk) -> data += chunk -+ verificationRes.on 'end', -> -+ try -+ response = JSON.parse(data) -+ -+ if response.status is 'okay' -+ \ No newline at end of file ++levelup = require('levelup') ++ ++# Initialize LevelUP database ++db = levelup('./userdb') ++ ++# Create Express app ++app = express() ++ ++# Middleware ++app.use(express.bodyParser()) ++app.use(express.cookieParser()) ++app.use(express.session(secret: 'inlinegui-secret-key')) ++ ++# Serve static files ++app.use(express.static(path.join(__dirname, '..', 'out'))) ++ ++# Persona verification endpoint ++app.post '/auth/persona', (req, res) -> ++ assertion = req.body.assertion ++ ++ unless assertion ++ return res.json(400, { status: 'failure', reason: 'No assertion provided' }) ++ ++ # Verify assertion with Mozilla's Persona verifier ++ https = require('https') ++ querystring = require('querystring') ++ ++ data = querystring.stringify ++ assertion: assertion ++ audience: req.headers.host ++ ++ options = ++ hostname: 'verifier.login.persona.org' ++ path: '/verify' ++ method: 'POST' ++ headers: ++ 'Content-Type': 'application/x-www-form-urlencoded' ++ ++ verifyReq = https.request options, (verifyRes) -> ++ body = '' ++ verifyRes.on 'data', (chunk) -> body += chunk ++ verifyRes.on 'end', -> ++ try ++ verified = JSON.parse(body) ++ ++ if verified.status is 'okay' ++ # Store or update user in database ++ email = verified.email ++ userKey = "user:#{email}" ++ ++ db.get userKey, (err, userData) -> ++ user = if err then {} else JSON.parse(userData) ++ user.email = email ++ user.lastLogin = new Date().toISOString() ++ ++ # Save user to database ++ db.put userKey, JSON.stringify(user), (err) -> ++ if err ++ return res.json(500, { status: 'failure', reason: 'Database error' }) ++ ++ req.session.email = email ++ res.json ++ status: 'success' ++ email: email ++ name: user.name or null ++ else ++ res.json(401, { status: 'failure', reason: verified.reason }) ++ catch e ++ res.json(500, { status: 'failure', reason: 'Invalid response from verifier' }) ++ ++ verifyReq.on 'error', (err) -> ++ res.json(500, { status: 'failure', reason: 'Verification request failed' }) ++ ++ verifyReq.write(data) ++ verifyReq.end() ++ ++# Logout endpoint ++app.post '/auth/logout', (req, res) -> ++ req.session.destroy() ++ res.json({ status: 'success' }) ++ ++module.exports = app +--- /dev/null ++++ b/src/files/login.html +@@ -0,0 +1,120 @@ ++ ++ ++ ++ ++ Login - InlineGUI ++