Live site: https://www.fcjamison.com
Developer-focused Flask + Jinja2 portfolio site. The homepage is server-rendered, static assets live under static/, and the Contact / Blog Reply forms submit via AJAX and send notification email via SMTP.
-
Ensure you have a local venv with deps installed (see Manual setup below).
-
Run the VS Code task
Dev: Flask + Open Browser
- Starts the dev server (
Flask: Run (dev server)) and opens the browser
- Open:
Note: *.localhost typically resolves to 127.0.0.1 without editing hosts files.
- Python 3.x
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -U pip
.\.venv\Scripts\python.exe -m pip install -r requirements.txt$env:FLASK_DEBUG='1'
$env:HOST='127.0.0.1'
$env:PORT='5000'
& .\.venv\Scripts\python.exe .\app.pyThen open http://127.0.0.1:5000/ (or http://fcjamison.comv2026.localhost:5000/).
Local development supports a repo-root .env file (loaded by python-dotenv) as long as you are not in production mode (ENV/FLASK_ENV != production).
HOST(default:127.0.0.1)PORT(default:5000)FLASK_DEBUG(1/0)
These settings let you reuse the same codebase across multiple sites by configuring branding/metadata via env vars (no template edits required).
SITE_URL(recommended in production) — absolute public root URL likehttps://example.com(used for sitemap/robots when behind a proxy).SITE_NAME— site name (used for OpenGraph + JSON-LD).SITE_OWNER_NAME— author/owner name.SITE_OWNER_EMAIL— optional (used in JSON-LD asmailto:).SITE_DEFAULT_TITLE— default<title>when a page doesn’t overrideheadTitle.SITE_TITLE_SUFFIX— appended to the title (set to empty to disable).SITE_DEFAULT_DESCRIPTION— default meta description when a page doesn’t overrideheadDescription.SITE_LOGO_PATH— static path for logo (relative tostatic/), defaultimages/logo/logo.png.SITE_OG_IMAGE_URL— optional absolute URL to use forog:image/twitter:image.SITE_TWITTER_HANDLE— optional,@handle.
Sitemap controls:
SITEMAP_INCLUDE_PROJECTS—1/0(default:1).SITEMAP_PATHS— extra on-site paths to include (comma or newline separated), e.g./about,/contact.SITEMAP_URLS— extra absolute URLs to include (comma or newline separated).
Forms:
POST /contactPOST /leave-reply
SMTP env vars are documented in SMTP_SETUP.md. The important ones are:
SMTP_HOST,SMTP_PORTSMTP_USER,SMTP_PASSWORDSMTP_FROM,SMTP_TO(defaults toSMTP_USER)SMTP_USE_SSL(default:1) /SMTP_USE_TLS(default:0)
__init__.py— creates the Flask app and loads.envin devapp.py— dev entrypoint (app.run(...))homeViews.py— routes, validation, CSV persistence, SMTP sendwsgi.py— production WSGI entrypoint (application)templates/— Jinja templates (pages + partials)static/— CSS/JS/images/fonts + portfolio archives understatic/portfolio/
GET /renderstemplates/home/index.html
GET /projects/<project_slug>
Resolution order:
- If
static/portfolio/<project_slug>/index.htmlexists, the app redirects to that static file. - Otherwise, if the slug is a known hosted project (see the
hosted = {...}map inhomeViews.py), it redirects to the corresponding*.fcjamison.comURL. - Otherwise it redirects to
https://github.com/<GITHUB_ORG>/<project_slug>(defaultGITHUB_ORG=FrankJamison).
- Both endpoints persist submissions to
data/*.csvand attempt to send a notification email. - Both endpoints include a simple honeypot field (
hp) for bot reduction.
- Install production deps from
requirements-prod.txt. - Prefer running behind a reverse proxy (Nginx/Apache) with a real WSGI server (Gunicorn/etc).
- This repo includes a Linux-oriented walkthrough in PRODUCTION_SETUP.md.
Example (Gunicorn):
gunicorn --workers 2 --bind 127.0.0.1:8000 wsgi:applicationNote: /contact and /leave-reply write CSV files under data/, so ensure the process user can write to that directory in production.
- Forms return
{ ok: false }: SMTP env vars are missing or failing; see SMTP_SETUP.md and check server logs. - TLS/cert errors locally: some antivirus tools intercept SMTP TLS;
SMTP_ALLOW_INVALID_CERT=1is available for dev only.
This project is intended to be WCAG-aligned (target: WCAG 2.2 AA). For a repeatable checklist (automated + manual verification), see ACCESSIBILITY.md.
Quick regression check:
python3 tools/a11y_sanity_check.py