{"id":50824895,"url":"https://github.com/belda/webapptemplate","last_synced_at":"2026-06-13T17:33:22.153Z","repository":{"id":347527996,"uuid":"1194368338","full_name":"belda/webapptemplate","owner":"belda","description":"Reusable Django starter with Google OAuth, workspaces w/ invitations, HTMX and tailwind CSS.","archived":false,"fork":false,"pushed_at":"2026-03-28T12:15:14.000Z","size":69,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-28T13:40:26.068Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/belda.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-28T09:00:29.000Z","updated_at":"2026-03-28T12:15:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/belda/webapptemplate","commit_stats":null,"previous_names":["belda/webapptemplate"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/belda/webapptemplate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/belda%2Fwebapptemplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/belda%2Fwebapptemplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/belda%2Fwebapptemplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/belda%2Fwebapptemplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/belda","download_url":"https://codeload.github.com/belda/webapptemplate/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/belda%2Fwebapptemplate/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34294411,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-13T17:33:19.851Z","updated_at":"2026-06-13T17:33:22.138Z","avatar_url":"https://github.com/belda.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WebApp Template\n\nA reusable Django starter kit distributed as an installable Python package. Scaffold a new\nproduction-ready Django project in under a minute with:\n\n- **Google OAuth + email/password auth** via `django-allauth`\n- **Workspaces** — multi-tenant, with roles, invitations, and API keys\n- **HTMX + Alpine.js + Tailwind CSS** — no build step required\n- **Django Ninja** REST API at `/api/v1/`\n- **Docker** ready — Postgres + Redis, separate dev and prod configs\n\n---\n\n## Creating a new project\n\nInstall the CLI (requires Python 3.11+):\n\n```bash\npip install git+https://github.com/belda/webapptemplate.git\n```\n\nScaffold a new project:\n\n```bash\ncd ~/Projects\nwebapptemplate init\n```\n\nThe wizard prompts for project name, database (PostgreSQL or SQLite), Redis, production domain,\nadmin email, and whether to generate Docker files. A `SECRET_KEY` is auto-generated and written\nto `.env`.\n\nThen set up the generated project:\n\n```bash\ncd myproject\npython -m venv .venv \u0026\u0026 source .venv/bin/activate\npip install -r requirements.txt\npython manage.py migrate\npython manage.py createsuperuser\npython manage.py runserver\n```\n\nVisit http://localhost:8000 — you'll be redirected to the login page.\n\n### Run with Docker\n\n```bash\ncp .env.example .env     # set DB_PASSWORD and any other secrets\n\n# Production-like (Postgres + Gunicorn):\ndocker compose up --build\n\n# Development (hot-reload, Postgres):\ndocker compose -f docker-compose.yml -f docker-compose.dev.yml up\n```\n\n---\n\n## How it works\n\nThe `webapptemplate` package contains both the core Django framework and the `webapptemplate init` CLI wizard.\n\nA scaffolded project depends only on `webapptemplate`. Its settings inherit via:\n\n```\nconfig/settings/production.py  (or development.py)\n    └── config/settings/base.py\n            └── from webapptemplate.default_settings import *\n```\n\nFramework updates are applied by bumping the `webapptemplate` version in `requirements.txt` and\nrunning `pip install -r requirements.txt` — no need to touch project config.\n\n---\n\n## Extending a scaffolded project\n\n### Add a Django app\n\n```bash\npython manage.py startapp blog apps/blog\n```\n\nRegister it in `config/settings/base.py`:\n\n```python\nINSTALLED_APPS += [\"apps.blog\"]\n```\n\n### Add a nav item\n\nEdit `templates/components/sidebar.html`:\n\n```html\n{% include \"components/nav_item.html\" with url=\"blog:index\" label=\"Blog\" icon=\"home\" %}\n```\n\nSupported icons: `home`, `building`, `cog`. Add more by editing `components/nav_item.html`\nwith any [Font Awesome 6 Free](https://fontawesome.com/icons) icon name.\n\n### Add an API endpoint\n\n1. Create `apps/blog/api.py` with a `Router()`\n2. Add schemas to `apps/blog/schemas.py` (or `apps/api/v1/schemas.py`)\n3. Register in `config/urls.py` (or `apps/api/v1/router.py` if using the shared router):\n   ```python\n   from apps.blog.api import router as blog_router\n   api.add_router(\"/blog/\", blog_router)\n   ```\n\n### Override a template\n\nDrop a file in your project's `templates/` directory with the same path as the framework\ntemplate. Project templates take precedence:\n\n```\ntemplates/dashboard.html          # overrides the default dashboard\ntemplates/components/sidebar.html # overrides the sidebar\n```\n\n### Override the app logo icon\n\nThe sidebar header and mobile topbar both include `components/app_logo.html`. To swap the\ndefault bolt icon for your own, create this file in your project's `templates/` directory:\n\n```\ntemplates/components/app_logo.html\n```\n\nExample — replace bolt with a custom SVG:\n\n```html\n\u003cdiv class=\"h-8 w-8 rounded-lg bg-indigo-600 flex items-center justify-center shrink-0\"\u003e\n  \u003cimg src=\"{% static 'img/logo.png' %}\" alt=\"Logo\" class=\"h-6 w-6\"\u003e\n\u003c/div\u003e\n```\n\nOr with a different Font Awesome icon:\n\n```html\n\u003cdiv class=\"h-8 w-8 rounded-lg bg-emerald-600 flex items-center justify-center shrink-0\"\u003e\n  \u003ci class=\"fa-solid fa-leaf text-white\"\u003e\u003c/i\u003e\n\u003c/div\u003e\n```\n\nProject templates take precedence over framework templates, so no other changes are needed.\n\n### Add a settings panel\n\nSettings panels let any app inject a card into the workspace settings page\n(`/workspaces/settings/`) or the user profile page (`/accounts/settings/profile/`)\nwithout touching framework templates or views.\n\n#### Simple case — ModelForm panel\n\nDeclare the panel on your `WebAppConfig` and provide a template. The framework\ngenerates the view for you.\n\n```python\n# apps/billing/apps.py\nfrom webapptemplate.app_config import WebAppConfig\nfrom webapptemplate.settings_panels import WorkspaceSettingsPanel, UserSettingsPanel\n\nclass BillingConfig(WebAppConfig):\n    name = \"apps.billing\"\n\n    workspace_settings_panels = [\n        WorkspaceSettingsPanel(\n            id=\"billing\",                          # must be unique across all apps\n            title=\"Billing\",\n            description=\"Manage your subscription plan.\",\n            template=\"billing/panels/workspace_billing.html\",\n            form_class=BillingSettingsForm,        # must be a ModelForm(instance=workspace)\n            admin_only=True,                       # hide from non-admins (optional)\n            order=50,                              # controls position (default 100)\n        ),\n    ]\n\n    user_settings_panels = [\n        UserSettingsPanel(\n            id=\"notifications\",\n            title=\"Notifications\",\n            template=\"billing/panels/user_notifications.html\",\n            form_class=NotificationPrefsForm,      # ModelForm(instance=user)\n        ),\n    ]\n```\n\n`form_class` must be a `ModelForm`. It is instantiated with `instance=workspace`\n(workspace panels) or `instance=request.user` (user panels).\n\nWrite the panel template — it receives `panel`, `form`, and `saved` in context:\n\n```html\n{# apps/billing/templates/billing/panels/workspace_billing.html #}\n{% if saved %}\n\u003cp class=\"mb-3 text-sm text-green-600 font-medium\"\u003eSaved.\u003c/p\u003e\n{% endif %}\n\u003cform hx-post=\"{% url panel.url_name %}\"\n      hx-target=\"#panel-{{ panel.id }}\"\n      hx-swap=\"innerHTML\"\u003e\n  {% csrf_token %}\n  \u003cdiv class=\"space-y-4\"\u003e\n    {{ form.as_p }}\n  \u003c/div\u003e\n  \u003cdiv class=\"mt-4 flex justify-end\"\u003e\n    \u003cbutton type=\"submit\" class=\"btn-primary\"\u003eSave\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/form\u003e\n```\n\nThe `hx-target=\"#panel-{{ panel.id }}\"` / `hx-swap=\"innerHTML\"` pair keeps the\ncard header in place and swaps only the form area after submission.\n\nThat's it — no URL registration, no template overrides, no view edits.\n\n#### Custom view panel\n\nFor panels that need more than a simple form save, provide `view_func` instead\nof `form_class`. Your view handles both GET (returns the panel body HTML) and POST:\n\n```python\nfrom webapptemplate.settings_panels import WorkspaceSettingsPanel\n\ndef my_billing_panel_view(request):\n    workspace = request.workspace\n    # ... custom logic ...\n    return render(request, \"billing/panels/workspace_billing.html\", {\"panel\": panel, ...})\n\nclass BillingConfig(WebAppConfig):\n    name = \"apps.billing\"\n    workspace_settings_panels = [\n        WorkspaceSettingsPanel(\n            id=\"billing\",\n            title=\"Billing\",\n            template=\"billing/panels/workspace_billing.html\",\n            view_func=my_billing_panel_view,\n        ),\n    ]\n```\n\n`view_func` panels are loaded via HTMX (`hx-trigger=\"load\"`) on the settings\npage — the framework renders a placeholder and your view provides the content.\n\n#### URL names\n\nEach panel gets an auto-registered URL:\n\n| Panel type | URL | `{% url %}` name |\n|---|---|---|\n| `WorkspaceSettingsPanel` | `/workspaces/settings/panel/\u003cid\u003e/` | `settings_panel_\u003cid\u003e` |\n| `UserSettingsPanel` | `/accounts/settings/panel/\u003cid\u003e/` | `user_settings_panel_\u003cid\u003e` |\n\nUse these in templates as shown above: `{% url panel.url_name %}`.\n\n### Workspace-aware views\n\n```python\nfrom webapptemplate.apps.workspaces.decorators import workspace_member_required, workspace_admin_required\n\n@workspace_member_required\ndef my_view(request):\n    workspace = request.workspace   # guaranteed non-None\n```\n\n---\n\n## Project structure (scaffolded)\n\n```\nmyproject/\n  manage.py\n  config/\n    settings/\n      base.py          Shared settings — extend webapptemplate defaults here\n      development.py   DEBUG=True, console email backend\n      production.py    SSL/HSTS/secure cookies\n    urls.py            Extends webapptemplate.urls; add project-specific routes here\n    wsgi.py / asgi.py\n  apps/                Your project-specific Django apps go here\n  templates/           Project templates (take precedence over framework templates)\n  static/              Project static files\n  .env                 Secrets — never commit this\n  .env.example         Committed placeholder for .env\n  requirements.txt     Pinned to webapptemplate==\u003cversion\u003e\n  Dockerfile\n  docker-compose.yml\n  docker-compose.dev.yml\n```\n\nFramework apps (accounts, workspaces, dashboard, API) live inside the installed\n`webapptemplate` package at `webapptemplate/apps/` and are imported as\n`webapptemplate.apps.accounts`, etc.\n\n---\n\n## Configuration reference\n\nSet these in `config/settings/base.py` (or via `.env` where noted).\n\n| Setting | Default | Description |\n|---|---|---|\n| `REQUIRE_EMAIL_VERIFICATION` | `True` | Blocks email/password users until they confirm their address. Social logins and invitation acceptors are exempt. |\n| `WORKSPACE_MEMBERS_CAN_INVITE` | `False` | When `True`, any workspace member can send invitations. Default: admins and owners only. |\n| `USE_API` | `True` | Enables the REST API at `/api/v1/` and API key management in workspace settings. |\n\n### Google OAuth\n\n```bash\n# .env\nGOOGLE_CLIENT_ID=your-client-id\nGOOGLE_CLIENT_SECRET=your-client-secret\n```\n\nThen in Django admin:\n1. **Sites** → change `example.com` to your domain (e.g. `localhost:8000` in dev)\n2. **Social Applications** → add a Google app with your credentials and assign it to the site\n\nAdd authorized redirect URI in [Google Cloud Console](https://console.cloud.google.com/):\n`https://yourdomain.com/accounts/google/login/callback/`\n\n### Email\n\nDevelopment uses the `console` backend — emails print to the terminal. For production:\n\n```bash\n# .env\nEMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend\nEMAIL_HOST=smtp.example.com\nEMAIL_PORT=587\nEMAIL_USE_TLS=True\nEMAIL_HOST_USER=you@example.com\nEMAIL_HOST_PASSWORD=secret\nDEFAULT_FROM_EMAIL=noreply@example.com\n```\n\n---\n\n## Developing the template itself\n\nTo work on `webapptemplate` directly (not on a scaffolded project):\n\n```bash\ngit clone https://github.com/belda/webapptemplate.git\ncd webapptemplate\n\npyenv exec python -m venv .venv\nsource .venv/bin/activate.fish   # or activate for bash/zsh\npip install -r requirements.txt\n\nDJANGO_SETTINGS_MODULE=config.settings.development python manage.py runserver\n```\n\nRun checks and tests:\n\n```bash\nDJANGO_SETTINGS_MODULE=config.settings.development python manage.py check\nDJANGO_SETTINGS_MODULE=config.settings.development python manage.py test\n```\n\nTo test the installer locally:\n\n```bash\npip install installer/\nwebapptemplate init\n```\n\n---\n\n## Tech stack\n\n| Layer | Library |\n|---|---|\n| Framework | Django 6.0.4 |\n| Auth | django-allauth 65.15 |\n| API | django-ninja 1.6.2 |\n| Frontend | HTMX 2 + Alpine.js 3 + Tailwind CSS (CDN) |\n| Static files | Whitenoise 6 |\n| Database | PostgreSQL (prod) / SQLite (dev) |\n| Cache / sessions | Redis + django-redis (optional) |\n| Container | Docker + Compose |\n| Build | Hatchling |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbelda%2Fwebapptemplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbelda%2Fwebapptemplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbelda%2Fwebapptemplate/lists"}