# Mthimkhulu Welanga Organisation, NPO A complete website and message inbox for Mthimkhulu Welanga Organisation, NPO — substance abuse and mental health prevention and support for youth in Bloemfontein. The site carries the organisation’s **“It’s okay to not be okay”** message, all programme and contact details, a printable QR code, and three ways for visitors to reach the team: **appointment request**, **comment**, and **anonymous message**. A floating **WhatsApp** button is available on every screen. --- ## Start the app 1. Install Python 3 from (tick **Add Python to PATH**). 2. Double-click **`start-app.bat`**. Or from a terminal in this folder: ```bat python server.py --port 8090 ``` The browser opens automatically. | What | Address | | --- | --- | | Website | | | Phone on the same Wi-Fi | `http://:8090` — printed when the server starts | | Private inbox | | The server prints the LAN address (for example `http://172.20.10.9:8090`) and the inbox key each time it starts. The key is also stored in `data/admin-key.txt`. If a phone cannot open the LAN address, allow Python through Windows Firewall for **private networks** (Windows usually asks the first time the server starts). ### Opening the inbox Go to `/inbox.html`, paste the key, and press **Unlock**. You can also bookmark: ```text http://localhost:8090/inbox.html?key=YOUR-KEY ``` The inbox shows appointments, comments and anonymous messages, with filters, search, refresh and **Export CSV**. Keep the key private: anyone with it can read the messages. To choose your own key, set an environment variable before starting: ```bat set MW_ADMIN_KEY=my-long-private-key python server.py --port 8090 ``` --- ## How messages are stored Everything stays on this computer. Messages are appended to `data/submissions.json`; nothing is sent to a third party. - **Appointments** store name, phone, optional email, date, time, topic and notes. - **Comments** store optional name/email and the comment. - **Anonymous messages** store **only the message** — no name, email, phone, IP address or device details are saved. The form also rate-limits submissions (20 per 10 minutes per device) and caps message sizes. The server never serves the `data/` folder, hidden files, source files or directory listings; requests for them return 404. Messages and the inbox key are only readable through the inbox API with the correct key. If the page is opened as a plain file (`file://`) or the server is unavailable, the form falls back to **WhatsApp / email / copy message** so the visitor can still send it. Anonymous messages never use WhatsApp or email automatically, because that would reveal the sender — they offer “Copy my message” instead. --- ## QR code The **QR code** section builds a code in the browser. It defaults to the address the page is currently open on. - On `localhost` the code is only useful on this computer. The page warns you. - To print a poster, **publish the site first**, then paste the public address into the QR field and press **Generate**. - **Download PNG** saves a high-resolution code for print; **Print poster** prints a clean A4 card with the crest. The site is already published, so `assets/js/config.js` pre-fills the QR code with the live address: ```js window.MW_CONFIG = { apiBase: "", shareLink: "https://its-okay-to-not-be-okay.pages.dev", whatsapp: "27825550282", phone: "+27825550282", email: "mthiweorg@gmail.com", address: "Roth Avenue, Willows, Bloemfontein, 9301" }; ``` ### Publishing the site **Short answer:** it is already published, free, on Cloudflare Pages — . No card, permanent, and it keeps the private inbox and the anonymous promise. To change the site later, redeploy with `wrangler` as described below. Setup, database and key details are in `deploy\cloudflare\README.md`. The page and QR code work on any static host. The three forms need a small backend to receive messages; on the live site that is a Cloudflare Function writing to D1. Running `server.py` locally still works too, and `apiBase` in `config.js` can point at a different backend if the site ever moves. Phones scanning a QR code must be able to reach the address in it. A `localhost` address will not work from a phone; use the LAN address on the same Wi-Fi, or the live address below. #### Live site | What | Address | | --- | --- | | Public site (phrase in the address) | | | Private inbox | `https://its-okay-to-not-be-okay.pages.dev/inbox.html?key=YOUR-KEY` | | Admin key file | `data/inbox-key.txt` (private, gitignored) | This is **permanent hosting**, not a tunnel. It stays up when this computer is off, and the QR code on the page already points at it. Hosting is Cloudflare Pages with a free D1 database; the address is free because it uses the `pages.dev` domain, so nothing needs renewing and no card is on file. The inbox key is the only way into the inbox. It is not in the website code; it lives in the Cloudflare secret `MW_ADMIN_KEY` and in `data/inbox-key.txt`. Keep a copy of that file somewhere safe — if it is lost, the inbox can still be reached by setting a new key in the dashboard and redeploying. ##### Redeploying changes The project was created by Direct Upload, so Cloudflare's Git integration cannot be attached to it. Push changes with `wrangler` instead: ```powershell cd "C:\Users\bceem\OneDrive\Documents\Default Project\deploy\cloudflare" npx wrangler pages deploy public --project-name its-okay-to-not-be-okay --branch main ``` Copy any edited files from the root site into `deploy\cloudflare\public\` first (the published copy is the one in that folder). Wrangler writes progress to stderr, so PowerShell may report exit code 1 even when the deploy succeeded — check for `Deployment complete!` in the output. #### Fallback option: Netlify (not used) `deploy\netlify` is a static copy that needs no server. Nothing was published there, and it is kept only as a fallback. It has no `inbox.html` or `server.py`; the three forms go to Netlify Forms, which allows 100 submissions a month. Full steps are in `deploy\README.md`. #### Why Cloudflare Pages was chosen Cloudflare's free plan hosts the site **and** a small database, so messages stay in your own inbox page with your own key, and the sender's IP is never stored with a message. The live project is `its-okay-to-not-be-okay`, database `mw-messages`, and the address is set in the QR code and the page metadata. Full click-by-click setup, privacy details and free-plan limits are in `deploy\cloudflare\README.md`. Both free options cost R0 and need no card: | | Netlify (fallback) | Cloudflare Pages (live) | | --- | --- | --- | | Publish effort | drag and drop | `wrangler pages deploy` | | Inbox | Netlify dashboard | `inbox.html` with your key | | Anonymous promise | message only, host logs IP | message only, IP hashed for 10 min in a separate table | | Monthly limit | 100 submissions | 100,000 requests/day | --- ## Editing the site | File | What it controls | | --- | --- | | `index.html` | All page content: about, programmes, vision, contact details | | `inbox.html` | Private message inbox | | `assets/css/styles.css` | Colours, type, spacing, print layout | | `assets/js/app.js` | Navigation, tabs, validation, sending, QR tool | | `assets/js/config.js` | Phone, WhatsApp number, email, address, fixed QR link | | `assets/js/qr.js` | Self-contained QR encoder (no internet needed) | | `assets/brand/logo.svg` | Designed crest used in the header, hero, QR card and footer | | `assets/brand/social-card.png` | 1200 × 630 sharing image | | `favicon.png` | Browser tab icon | | `server.py` | Website server, message API and inbox data | | `deploy/netlify/` | Free-hosted copy: same site, Netlify Forms instead of `server.py` | | `deploy/cloudflare/` | The **live** site plus `functions/` and D1, keeps the private inbox | Brand colours: ink `#11211d`, teal `#2b5f55`, gold `#b98a34`, cream `#f7f2e7`. Display type is a Georgia/Palatino serif stack. ### Contact details used on the site - WhatsApp / phone: **082 555 0282** (`+27 82 555 0282`) - Email: **mthiweorg@gmail.com** - Address: **Roth Avenue, Willows, Bloemfontein, 9301** (at National Hospital) - Registration: **610-1021** · **Reg. No.: 290-537 NPO** - Office hours: Mon–Thu 8:30am–4pm, Fri 8:30am–2pm, weekends and public holidays closed --- ## Files and housekeeping - `data/` — private messages and inbox key. **Do not publish this folder.** It is already listed in `.gitignore`. - Back up `data/submissions.json` regularly. It is a plain JSON list. - To clear all messages, stop the server, replace the file contents with `[]`, and start the server again. - `start-app.bat` is the double-click launcher; `server.py` is the server. ### Crisis support shown on the page SADAG national helpline **0800 567 567** (24 hours) and emergency **10111**. Check these numbers before publishing, as helpline details can change.