CE5info

Get your personalized map

Back to home
*Subject to weather/surf and space. Adults 21+ advocating peace and love.

Reviews + Photos

Start a CE‑5.cam in my hometown

Starter pack (code + instructions) is $3,330. Hosting on our Apache is $50/mo. Until payment is enabled, this form just sends us your interest.

We’ll reply with setup instructions and (optionally) a payment link when you’re ready. We never collect card numbers here.
``` --- ## Tiny server — **Express** (Node.js) *Pick either Express or Flask; you don’t need both.* This version keeps everything free: emails/logs go to local files, image uploads go to `./uploads/`. **server.js** ```js // Minimal CE-5.cam server (Express) // Free path: no paid services. Optional Stripe/Nodemailer are behind env flags. const express = require('express'); const fs = require('fs'); const path = require('path'); const multer = require('multer'); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(express.static(path.join(__dirname, 'public'))); // put index.html + info.html in ./public // --- uploads (free, local disk) --- const uploadDir = path.join(__dirname, 'uploads'); fs.mkdirSync(uploadDir, { recursive: true }); const upload = multer({ dest: uploadDir, limits: { fileSize: 8 * 1024 * 1024 }, // 8 MB fileFilter: (req, file, cb) => { if (!file.mimetype.startsWith('image/')) return cb(new Error('Images only')); cb(null, true); } }); // --- /api/map-request --- app.post('/api/map-request', async (req, res) => { const { display_name, email, notes } = req.body || {}; if (!display_name || !email) return res.status(400).json({ ok:false, error:'name+email required' }); const record = { ts: new Date().toISOString(), display_name, email, notes }; fs.appendFileSync(path.join(__dirname, 'requests.ndjson'), JSON.stringify(record) + '\n'); // OPTIONAL email (dev): set SMTP_URL and uncomment below to use a local MailHog/SMTP server // const nodemailer = require('nodemailer'); // const transporter = nodemailer.createTransport(process.env.SMTP_URL); // await transporter.sendMail({ from:'ce5@localhost', to: email, subject:'Your CE-5 map', text:`Aloha ${display_name}! We received your request and will send your personalized map shortly.` }); res.json({ ok:true, message:'Request received. We will email your map shortly.' }); }); // --- /api/reviews --- app.post('/api/reviews', upload.single('photo'), (req, res) => { const { name, location, text } = req.body || {}; const img = req.file ? path.basename(req.file.path) : null; const rec = { ts:new Date().toISOString(), name, location, text, photo: img }; fs.appendFileSync(path.join(__dirname, 'reviews.ndjson'), JSON.stringify(rec)+'\n'); res.json({ ok:true, message:'Mahalo for your review!', photo: img }); }); // --- /api/checkout --- app.post('/api/checkout', async (req, res) => { // Free path: just log interest and reply OK. const { name, email, hometown } = req.body || {}; fs.appendFileSync(path.join(__dirname, 'checkout_interest.ndjson'), JSON.stringify({ ts:new Date().toISOString(), name, email, hometown })+'\n'); // If STRIPE_SECRET is set, create a real Checkout session if (process.env.STRIPE_SECRET) { const stripe = require('stripe')(process.env.STRIPE_SECRET); const price = process.env.STRIPE_PRICE_ID; // define a Price in Stripe dashboard const session = await stripe.checkout.sessions.create({ mode: 'payment', line_items: [{ price, quantity: 1 }], success_url: process.env.SUCCESS_URL || 'http://localhost:3000/?paid=1', cancel_url: process.env.CANCEL_URL || 'http://localhost:3000/info.html?cancel=1', customer_email: email, }); return res.json({ ok:true, url: session.url }); } res.json({ ok:true, message:'Interest recorded. We\'ll email you with next steps.' }); }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => console.log('CE-5.cam server on http://localhost:'+PORT)); ``` **How to run (Express)** ```bash mkdir ce5 && cd ce5 mkdir public uploads # put the two html files into ./public as index.html and info.html npm init -y npm i express multer # (optional) npm i stripe nodemailer node server.js ``` --- ## Tiny server — **Flask** (Python) **app.py** ```python from flask import Flask, request, send_from_directory, jsonify from werkzeug.utils import secure_filename import os, json, time app = Flask(__name__, static_folder='public', static_url_path='') UPLOAD_DIR = 'uploads' os.makedirs(UPLOAD_DIR, exist_ok=True) @app.route('/') def root(): return send_from_directory('public', 'index.html') @app.post('/api/map-request') def map_request(): display_name = request.form.get('display_name') email = request.form.get('email') notes = request.form.get('notes') if not display_name or not email: return jsonify(ok=False, error='name+email required'), 400 with open('requests.ndjson','a') as f: f.write(json.dumps({ 'ts': time.time(), 'display_name':display_name, 'email':email, 'notes':notes })+'\n') return jsonify(ok=True, message='Request received. We will email your map shortly.') @app.post('/api/reviews') def reviews(): name = request.form.get('name') location = request.form.get('location') text = request.form.get('text') photo = request.files.get('photo') img_name = None if photo and photo.mimetype.startswith('image/'): img_name = secure_filename(photo.filename) photo.save(os.path.join(UPLOAD_DIR, img_name)) with open('reviews.ndjson','a') as f: f.write(json.dumps({ 'ts': time.time(), 'name':name, 'location':location, 'text':text, 'photo':img_name })+'\n') return jsonify(ok=True, message='Mahalo!', photo=img_name) @app.post('/api/checkout') def checkout(): # Free path: just record interest. name = request.form.get('name') email = request.form.get('email') hometown = request.form.get('hometown') with open('checkout_interest.ndjson','a') as f: f.write(json.dumps({ 'ts': time.time(), 'name':name, 'email':email, 'hometown':hometown })+'\n') return jsonify(ok=True, message='Interest recorded. We\'ll email steps soon.') if __name__ == '__main__': os.makedirs('public', exist_ok=True) app.run(host='0.0.0.0', port=5000, debug=True) ``` **How to run (Flask)** ```bash mkdir ce5 && cd ce5 mkdir public uploads # put the two html files into ./public as index.html and info.html python3 -m venv venv && source venv/bin/activate pip install flask python app.py ``` --- ## Notes / next steps * Put both HTML files into `./public/` so either server can serve them. * File uploads land in `./uploads/`. Keep it small and prune occasionally. * When you’re ready, replace the `/api/checkout` stub with live **Stripe Checkout**; it’s pay‑per‑transaction, no monthly fee. * Never collect raw card data yourself. --- ## (Optional) `.gitignore` ``` node_modules venv uploads *.ndjson .env ```