THU.JUN.18
2026
23:40:51
← back to modules MODULE · 04 · FINISH THE SITE
0 / 10 chapters complete · 0%

Ship It — Polish, Backups & What's Next

Everything works — now let's make it finished. By the end you'll have one-shot "saved!" messages, a real database backup, a security checklist you've actually run, a graceful no-JS story, and a clear path to unlocking the Python/JavaScript/Linux/DevOps tracks. This is the chapter where the site stops being a project and becomes done.
Flash messages (the Part 2 one-shot pattern). Verify the page still works with JS off. A mysqldump backup script + cron (filling in the empty tools/db-backup.php stub). A security pass: delete leftover scripts, keep secrets out of git, lock down cookies, get on HTTPS. Then unlock the next module.

Flash messages — the finishing UX touch

You've got forms everywhere now, and good apps confirm actions: "Task added," "Track saved." The clean way pairs perfectly with the POST-Redirect-GET you've used all module — stash a one-shot message in the session before redirecting, show-and-delete it on the next page:

// after a successful action, before the redirect:
$_SESSION['flash'] = 'Saved.';
header('Location: ' . tabUrl('work'));
exit;

// near the top of the page render:
if (!empty($_SESSION['flash'])) {
    echo '<div class="flash">' . e($_SESSION['flash']) . '</div>';
    unset($_SESSION['flash']);          // one-shot: gone after one view
}

It appears exactly once and survives a refresh-free moment, then vanishes — because we unset it right after showing it. Tiny feature, big "this feels finished" payoff. Style .flash to match the dashboard's neon and you're done.

Honor the no-JS foundation

Back in chapter 2 we promised progressive enhancement: the site works without JavaScript, and gets nicer with it. Let's actually verify that promise instead of assuming it. Open your browser dev tools, disable JavaScript, and reload:

  • The clock shows the server time (frozen, but correct at load) — good.
  • The timer shows a static display — degraded but not broken.
  • Tasks, login, editing — all still work, because they're plain forms and redirects.
  • The audio player can't play (it needs JS), but nothing throws and the page is intact.

If anything is actually broken (not just degraded) with JS off, that's a bug worth fixing — it means you leaned on JavaScript somewhere you didn't need to. This is the test that proves your architecture is sound.

Backups — the empty stub finally gets filled

There's a tools/db-backup.php file in the project that's been empty this whole time. Now that you have a database worth protecting, fill it in. The job is a mysqldump on a schedule:

# a daily backup, kept for a week — run from cron on the Lubuntu server
mysqldump --single-transaction dashboard \
  | gzip > /home/erictey/backups/dashboard-$(date +%F).sql.gz
A backup you've never restored is not a backup — it's a hope. Actually test it: load the dump into a scratch database (gunzip < backup.sql.gz | mariadb dashboard_test) and confirm your data's there. The day you need a backup is the worst possible day to discover it was empty or corrupt. Test restores now, while it's calm.

Wire a cron entry (crontab -e) to run it nightly, and add a line to prune backups older than a week so the disk doesn't fill. The same machine you set up in Part 1 is doing the work — it all comes full circle.

The security checklist — run it, don't just read it

Before you call anything done, walk this list end to end. Each item is a real foot-gun this module could have left behind:

  • Delete register.php if it's still there. (Chapter 8 told you to. Double-check.)
  • Keep secrets out of git. Your DB password should come from getenv('DB_PASS'), not a committed file. Add any config-with-secrets to .gitignore and rotate the password if it ever got committed.
  • Session cookies: httponly + samesite=Lax on, and secure on once you're on HTTPS.
  • HTTPS: finish what Part 1 started — a login over plain HTTP sends passwords in the clear on your network.
  • Least-privilege DB user: dashboard_user has only SELECT/INSERT/UPDATE/DELETE, never root, never DROP. Confirm it.
  • Escape on output everywhere user content renders — the e() helper. One unescaped echo is an XSS hole.

What's next — unlock the locked tracks

Look at the Programming module grid: PYTHON, JAVASCRIPT, LINUX, and DEVOPS are sitting there locked. Unlocking one is genuinely just flipping 'locked' to 'unlocked' in the $modules array in index.php and giving it a chapters file — the exact same five-step wiring that added this module (require + $MODULE_SUBS + $MODULE_CHAPTERS + grid card + $MODULE_META). You've now seen how a module is built from the inside, so you can author your own.

That's the quiet victory of finishing this module: you didn't just complete the dashboard, you learned the machine that makes the dashboard. Adding the next track is no longer a mystery — it's a checklist you've already run.

Flash messages appear once and vanish; the site survives JS being off; tools/db-backup.php (or a cron mysqldump) produces a gzipped dump you've test-restored; every box on the security checklist is ticked; and you know exactly how to unlock the next module. There are no // FAKE comments left in index.php. That last one means the site is, genuinely, finished.

▣ Mini Project: The Ship Checklist

One last pass to cross the finish line. This isn't new features — it's the professional polish that separates "it works on my machine" from "it's done." Work the list top to bottom and enjoy ticking the final boxes on a site you took from mockup to real.

  1. Add the flash-message helper and use it after every successful action.
  2. Do the JS-off test; fix anything that's broken (not just degraded).
  3. Fill in tools/db-backup.php (or a cron mysqldump), run it, and test-restore the dump into a scratch DB.
  4. Run the entire security checklist and fix every miss.
  5. Unlock one locked module: flip it to 'unlocked', add a chapters file, and confirm it appears with working progress.

Stretch goals:

  • Wire the last two fake Home stats (MOON, WEATHER) to a free public API.
  • Add log rotation and a tiny uptime check that pings the site and alerts you if it's down.
  • Write your own first chapter for one of the unlocked tracks — you know the format cold now.

What you flexed: one-shot flash messages, verifying progressive enhancement instead of assuming it, real backups with tested restores, a security checklist run end to end, and the full module-wiring pattern. You started with a beautiful mockup and you finished the site. That's the whole game — go build the next track.