Initialize Fonrey Django multi-tenant project skeleton

Set up the required directory layout, app scaffolding, core settings, templates, static assets, and Docker/Tailwind tooling to establish a standardized development baseline.
This commit is contained in:
2026-04-26 17:12:09 +08:00
commit 4aba6dfa77
170 changed files with 1220 additions and 0 deletions

29
.env.example Normal file
View File

@@ -0,0 +1,29 @@
# Django
SECRET_KEY=your-secret-key-here
DEBUG=True
DJANGO_SETTINGS_MODULE=config.settings.development
ALLOWED_HOSTS=localhost,127.0.0.1
# Database
DB_NAME=fonrey
DB_USER=fonrey
DB_PASSWORD=fonrey
DB_HOST=db
DB_PORT=5432
# Redis
REDIS_URL=redis://redis:6379/0
CELERY_BROKER_URL=redis://redis:6379/1
# Cloudflare R2
R2_ENDPOINT_URL=https://<account_id>.r2.cloudflarestorage.com
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=
R2_BUCKET_NAME=media
R2_CUSTOM_DOMAIN=
# Sentryproduction 填写)
SENTRY_DSN=
# PII 加密密钥AES-256生产环境必须替换
PHONE_ENCRYPTION_KEY=

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
.env
*.pyc
__pycache__/
.DS_Store
node_modules/
static/css/output.css
media/
dist/

8
Dockerfile Normal file
View File

@@ -0,0 +1,8 @@
FROM python:3.12-slim
WORKDIR /app
RUN apt-get update && apt-get install -y libpq-dev gcc && rm -rf /var/lib/apt/lists/*
COPY requirements/base.txt requirements/base.txt
RUN pip install --no-cache-dir -r requirements/base.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "config.asgi:application", "--host", "0.0.0.0", "--port", "8000"]

23
Makefile Normal file
View File

@@ -0,0 +1,23 @@
.PHONY: dev migrate shell createsuperuser test lint
dev:
docker compose up
migrate:
docker compose exec web python manage.py migrate_schemas --shared
docker compose exec web python manage.py migrate_schemas
shell:
docker compose exec web python manage.py shell_plus
test:
docker compose exec web pytest apps/ -v
lint:
ruff check . && black --check .
tailwind-build:
npm run build
createsuperuser:
docker compose exec web python manage.py create_tenant_superuser

0
apps/__init__.py Normal file
View File

0
apps/account/__init__.py Normal file
View File

1
apps/account/admin.py Normal file
View File

@@ -0,0 +1 @@
from django.contrib import admin

6
apps/account/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class AccountConfig(AppConfig):
name = "apps.account"
verbose_name = "登录认证"

View File

View File

View File

View File

6
apps/account/tasks.py Normal file
View File

@@ -0,0 +1,6 @@
from celery import shared_task
@shared_task
def sample_task() -> str:
return "account task placeholder"

View File

View File

9
apps/account/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
app_name = "account"
urlpatterns = [
path("", views.index, name="index"),
]

5
apps/account/views.py Normal file
View File

@@ -0,0 +1,5 @@
from django.http import HttpRequest, HttpResponse
def index(request: HttpRequest) -> HttpResponse:
return HttpResponse("account app placeholder")

0
apps/client/__init__.py Normal file
View File

1
apps/client/admin.py Normal file
View File

@@ -0,0 +1 @@
from django.contrib import admin

6
apps/client/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ClientConfig(AppConfig):
name = "apps.client"
verbose_name = "客源管理"

View File

View File

View File

View File

6
apps/client/tasks.py Normal file
View File

@@ -0,0 +1,6 @@
from celery import shared_task
@shared_task
def sample_task() -> str:
return "client task placeholder"

View File

View File

9
apps/client/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
app_name = "client"
urlpatterns = [
path("", views.index, name="index"),
]

5
apps/client/views.py Normal file
View File

@@ -0,0 +1,5 @@
from django.http import HttpRequest, HttpResponse
def index(request: HttpRequest) -> HttpResponse:
return HttpResponse("client app placeholder")

0
apps/complex/__init__.py Normal file
View File

1
apps/complex/admin.py Normal file
View File

@@ -0,0 +1 @@
from django.contrib import admin

6
apps/complex/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ComplexConfig(AppConfig):
name = "apps.complex"
verbose_name = "楼盘管理"

View File

View File

View File

View File

6
apps/complex/tasks.py Normal file
View File

@@ -0,0 +1,6 @@
from celery import shared_task
@shared_task
def sample_task() -> str:
return "complex task placeholder"

View File

View File

9
apps/complex/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
app_name = "complex"
urlpatterns = [
path("", views.index, name="index"),
]

5
apps/complex/views.py Normal file
View File

@@ -0,0 +1,5 @@
from django.http import HttpRequest, HttpResponse
def index(request: HttpRequest) -> HttpResponse:
return HttpResponse("complex app placeholder")

0
apps/org/__init__.py Normal file
View File

1
apps/org/admin.py Normal file
View File

@@ -0,0 +1 @@
from django.contrib import admin

6
apps/org/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class OrgConfig(AppConfig):
name = "apps.org"
verbose_name = "组织人事"

0
apps/org/models/.gitkeep Normal file
View File

View File

View File

View File

6
apps/org/tasks.py Normal file
View File

@@ -0,0 +1,6 @@
from celery import shared_task
@shared_task
def sample_task() -> str:
return "org task placeholder"

0
apps/org/tests/.gitkeep Normal file
View File

View File

9
apps/org/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
app_name = "org"
urlpatterns = [
path("", views.index, name="index"),
]

5
apps/org/views.py Normal file
View File

@@ -0,0 +1,5 @@
from django.http import HttpRequest, HttpResponse
def index(request: HttpRequest) -> HttpResponse:
return HttpResponse("org app placeholder")

View File

1
apps/permission/admin.py Normal file
View File

@@ -0,0 +1 @@
from django.contrib import admin

6
apps/permission/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class PermissionConfig(AppConfig):
name = "apps.permission"
verbose_name = "权限管理"

View File

View File

View File

View File

6
apps/permission/tasks.py Normal file
View File

@@ -0,0 +1,6 @@
from celery import shared_task
@shared_task
def sample_task() -> str:
return "permission task placeholder"

View File

View File

9
apps/permission/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
app_name = "permission"
urlpatterns = [
path("", views.index, name="index"),
]

5
apps/permission/views.py Normal file
View File

@@ -0,0 +1,5 @@
from django.http import HttpRequest, HttpResponse
def index(request: HttpRequest) -> HttpResponse:
return HttpResponse("permission app placeholder")

View File

1
apps/property/admin.py Normal file
View File

@@ -0,0 +1 @@
from django.contrib import admin

6
apps/property/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class PropertyConfig(AppConfig):
name = "apps.property"
verbose_name = "房源核心"

View File

View File

View File

View File

6
apps/property/tasks.py Normal file
View File

@@ -0,0 +1,6 @@
from celery import shared_task
@shared_task
def sample_task() -> str:
return "property task placeholder"

View File

View File

9
apps/property/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
app_name = "property"
urlpatterns = [
path("", views.index, name="index"),
]

5
apps/property/views.py Normal file
View File

@@ -0,0 +1,5 @@
from django.http import HttpRequest, HttpResponse
def index(request: HttpRequest) -> HttpResponse:
return HttpResponse("property app placeholder")

0
apps/region/__init__.py Normal file
View File

1
apps/region/admin.py Normal file
View File

@@ -0,0 +1 @@
from django.contrib import admin

6
apps/region/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class RegionConfig(AppConfig):
name = "apps.region"
verbose_name = "区域管理"

View File

View File

View File

View File

6
apps/region/tasks.py Normal file
View File

@@ -0,0 +1,6 @@
from celery import shared_task
@shared_task
def sample_task() -> str:
return "region task placeholder"

View File

View File

9
apps/region/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
app_name = "region"
urlpatterns = [
path("", views.index, name="index"),
]

5
apps/region/views.py Normal file
View File

@@ -0,0 +1,5 @@
from django.http import HttpRequest, HttpResponse
def index(request: HttpRequest) -> HttpResponse:
return HttpResponse("region app placeholder")

0
apps/release/__init__.py Normal file
View File

1
apps/release/admin.py Normal file
View File

@@ -0,0 +1 @@
from django.contrib import admin

6
apps/release/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ReleaseConfig(AppConfig):
name = "apps.release"
verbose_name = "客户端发布管理"

View File

View File

View File

View File

6
apps/release/tasks.py Normal file
View File

@@ -0,0 +1,6 @@
from celery import shared_task
@shared_task
def sample_task() -> str:
return "release task placeholder"

View File

View File

9
apps/release/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
app_name = "release"
urlpatterns = [
path("", views.index, name="index"),
]

Some files were not shown because too many files have changed in this diff Show More