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:
29
.env.example
Normal file
29
.env.example
Normal 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=
|
||||||
|
|
||||||
|
# Sentry(production 填写)
|
||||||
|
SENTRY_DSN=
|
||||||
|
|
||||||
|
# PII 加密密钥(AES-256,生产环境必须替换)
|
||||||
|
PHONE_ENCRYPTION_KEY=
|
||||||
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.env
|
||||||
|
*.pyc
|
||||||
|
__pycache__/
|
||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
|
static/css/output.css
|
||||||
|
media/
|
||||||
|
dist/
|
||||||
8
Dockerfile
Normal file
8
Dockerfile
Normal 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
23
Makefile
Normal 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
0
apps/__init__.py
Normal file
0
apps/account/__init__.py
Normal file
0
apps/account/__init__.py
Normal file
1
apps/account/admin.py
Normal file
1
apps/account/admin.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from django.contrib import admin
|
||||||
6
apps/account/apps.py
Normal file
6
apps/account/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AccountConfig(AppConfig):
|
||||||
|
name = "apps.account"
|
||||||
|
verbose_name = "登录认证"
|
||||||
0
apps/account/models/.gitkeep
Normal file
0
apps/account/models/.gitkeep
Normal file
0
apps/account/models/__init__.py
Normal file
0
apps/account/models/__init__.py
Normal file
0
apps/account/services/.gitkeep
Normal file
0
apps/account/services/.gitkeep
Normal file
0
apps/account/services/__init__.py
Normal file
0
apps/account/services/__init__.py
Normal file
6
apps/account/tasks.py
Normal file
6
apps/account/tasks.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def sample_task() -> str:
|
||||||
|
return "account task placeholder"
|
||||||
0
apps/account/tests/.gitkeep
Normal file
0
apps/account/tests/.gitkeep
Normal file
0
apps/account/tests/__init__.py
Normal file
0
apps/account/tests/__init__.py
Normal file
9
apps/account/urls.py
Normal file
9
apps/account/urls.py
Normal 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
5
apps/account/views.py
Normal 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
0
apps/client/__init__.py
Normal file
1
apps/client/admin.py
Normal file
1
apps/client/admin.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from django.contrib import admin
|
||||||
6
apps/client/apps.py
Normal file
6
apps/client/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ClientConfig(AppConfig):
|
||||||
|
name = "apps.client"
|
||||||
|
verbose_name = "客源管理"
|
||||||
0
apps/client/models/.gitkeep
Normal file
0
apps/client/models/.gitkeep
Normal file
0
apps/client/models/__init__.py
Normal file
0
apps/client/models/__init__.py
Normal file
0
apps/client/services/.gitkeep
Normal file
0
apps/client/services/.gitkeep
Normal file
0
apps/client/services/__init__.py
Normal file
0
apps/client/services/__init__.py
Normal file
6
apps/client/tasks.py
Normal file
6
apps/client/tasks.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def sample_task() -> str:
|
||||||
|
return "client task placeholder"
|
||||||
0
apps/client/tests/.gitkeep
Normal file
0
apps/client/tests/.gitkeep
Normal file
0
apps/client/tests/__init__.py
Normal file
0
apps/client/tests/__init__.py
Normal file
9
apps/client/urls.py
Normal file
9
apps/client/urls.py
Normal 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
5
apps/client/views.py
Normal 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
0
apps/complex/__init__.py
Normal file
1
apps/complex/admin.py
Normal file
1
apps/complex/admin.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from django.contrib import admin
|
||||||
6
apps/complex/apps.py
Normal file
6
apps/complex/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ComplexConfig(AppConfig):
|
||||||
|
name = "apps.complex"
|
||||||
|
verbose_name = "楼盘管理"
|
||||||
0
apps/complex/models/.gitkeep
Normal file
0
apps/complex/models/.gitkeep
Normal file
0
apps/complex/models/__init__.py
Normal file
0
apps/complex/models/__init__.py
Normal file
0
apps/complex/services/.gitkeep
Normal file
0
apps/complex/services/.gitkeep
Normal file
0
apps/complex/services/__init__.py
Normal file
0
apps/complex/services/__init__.py
Normal file
6
apps/complex/tasks.py
Normal file
6
apps/complex/tasks.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def sample_task() -> str:
|
||||||
|
return "complex task placeholder"
|
||||||
0
apps/complex/tests/.gitkeep
Normal file
0
apps/complex/tests/.gitkeep
Normal file
0
apps/complex/tests/__init__.py
Normal file
0
apps/complex/tests/__init__.py
Normal file
9
apps/complex/urls.py
Normal file
9
apps/complex/urls.py
Normal 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
5
apps/complex/views.py
Normal 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
0
apps/org/__init__.py
Normal file
1
apps/org/admin.py
Normal file
1
apps/org/admin.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from django.contrib import admin
|
||||||
6
apps/org/apps.py
Normal file
6
apps/org/apps.py
Normal 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
0
apps/org/models/.gitkeep
Normal file
0
apps/org/models/__init__.py
Normal file
0
apps/org/models/__init__.py
Normal file
0
apps/org/services/.gitkeep
Normal file
0
apps/org/services/.gitkeep
Normal file
0
apps/org/services/__init__.py
Normal file
0
apps/org/services/__init__.py
Normal file
6
apps/org/tasks.py
Normal file
6
apps/org/tasks.py
Normal 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
0
apps/org/tests/.gitkeep
Normal file
0
apps/org/tests/__init__.py
Normal file
0
apps/org/tests/__init__.py
Normal file
9
apps/org/urls.py
Normal file
9
apps/org/urls.py
Normal 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
5
apps/org/views.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
|
||||||
|
|
||||||
|
def index(request: HttpRequest) -> HttpResponse:
|
||||||
|
return HttpResponse("org app placeholder")
|
||||||
0
apps/permission/__init__.py
Normal file
0
apps/permission/__init__.py
Normal file
1
apps/permission/admin.py
Normal file
1
apps/permission/admin.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from django.contrib import admin
|
||||||
6
apps/permission/apps.py
Normal file
6
apps/permission/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionConfig(AppConfig):
|
||||||
|
name = "apps.permission"
|
||||||
|
verbose_name = "权限管理"
|
||||||
0
apps/permission/models/.gitkeep
Normal file
0
apps/permission/models/.gitkeep
Normal file
0
apps/permission/models/__init__.py
Normal file
0
apps/permission/models/__init__.py
Normal file
0
apps/permission/services/.gitkeep
Normal file
0
apps/permission/services/.gitkeep
Normal file
0
apps/permission/services/__init__.py
Normal file
0
apps/permission/services/__init__.py
Normal file
6
apps/permission/tasks.py
Normal file
6
apps/permission/tasks.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def sample_task() -> str:
|
||||||
|
return "permission task placeholder"
|
||||||
0
apps/permission/tests/.gitkeep
Normal file
0
apps/permission/tests/.gitkeep
Normal file
0
apps/permission/tests/__init__.py
Normal file
0
apps/permission/tests/__init__.py
Normal file
9
apps/permission/urls.py
Normal file
9
apps/permission/urls.py
Normal 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
5
apps/permission/views.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
|
||||||
|
|
||||||
|
def index(request: HttpRequest) -> HttpResponse:
|
||||||
|
return HttpResponse("permission app placeholder")
|
||||||
0
apps/property/__init__.py
Normal file
0
apps/property/__init__.py
Normal file
1
apps/property/admin.py
Normal file
1
apps/property/admin.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from django.contrib import admin
|
||||||
6
apps/property/apps.py
Normal file
6
apps/property/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PropertyConfig(AppConfig):
|
||||||
|
name = "apps.property"
|
||||||
|
verbose_name = "房源核心"
|
||||||
0
apps/property/models/.gitkeep
Normal file
0
apps/property/models/.gitkeep
Normal file
0
apps/property/models/__init__.py
Normal file
0
apps/property/models/__init__.py
Normal file
0
apps/property/services/.gitkeep
Normal file
0
apps/property/services/.gitkeep
Normal file
0
apps/property/services/__init__.py
Normal file
0
apps/property/services/__init__.py
Normal file
6
apps/property/tasks.py
Normal file
6
apps/property/tasks.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def sample_task() -> str:
|
||||||
|
return "property task placeholder"
|
||||||
0
apps/property/tests/.gitkeep
Normal file
0
apps/property/tests/.gitkeep
Normal file
0
apps/property/tests/__init__.py
Normal file
0
apps/property/tests/__init__.py
Normal file
9
apps/property/urls.py
Normal file
9
apps/property/urls.py
Normal 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
5
apps/property/views.py
Normal 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
0
apps/region/__init__.py
Normal file
1
apps/region/admin.py
Normal file
1
apps/region/admin.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from django.contrib import admin
|
||||||
6
apps/region/apps.py
Normal file
6
apps/region/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class RegionConfig(AppConfig):
|
||||||
|
name = "apps.region"
|
||||||
|
verbose_name = "区域管理"
|
||||||
0
apps/region/models/.gitkeep
Normal file
0
apps/region/models/.gitkeep
Normal file
0
apps/region/models/__init__.py
Normal file
0
apps/region/models/__init__.py
Normal file
0
apps/region/services/.gitkeep
Normal file
0
apps/region/services/.gitkeep
Normal file
0
apps/region/services/__init__.py
Normal file
0
apps/region/services/__init__.py
Normal file
6
apps/region/tasks.py
Normal file
6
apps/region/tasks.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def sample_task() -> str:
|
||||||
|
return "region task placeholder"
|
||||||
0
apps/region/tests/.gitkeep
Normal file
0
apps/region/tests/.gitkeep
Normal file
0
apps/region/tests/__init__.py
Normal file
0
apps/region/tests/__init__.py
Normal file
9
apps/region/urls.py
Normal file
9
apps/region/urls.py
Normal 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
5
apps/region/views.py
Normal 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
0
apps/release/__init__.py
Normal file
1
apps/release/admin.py
Normal file
1
apps/release/admin.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from django.contrib import admin
|
||||||
6
apps/release/apps.py
Normal file
6
apps/release/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ReleaseConfig(AppConfig):
|
||||||
|
name = "apps.release"
|
||||||
|
verbose_name = "客户端发布管理"
|
||||||
0
apps/release/models/.gitkeep
Normal file
0
apps/release/models/.gitkeep
Normal file
0
apps/release/models/__init__.py
Normal file
0
apps/release/models/__init__.py
Normal file
0
apps/release/services/.gitkeep
Normal file
0
apps/release/services/.gitkeep
Normal file
0
apps/release/services/__init__.py
Normal file
0
apps/release/services/__init__.py
Normal file
6
apps/release/tasks.py
Normal file
6
apps/release/tasks.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def sample_task() -> str:
|
||||||
|
return "release task placeholder"
|
||||||
0
apps/release/tests/.gitkeep
Normal file
0
apps/release/tests/.gitkeep
Normal file
0
apps/release/tests/__init__.py
Normal file
0
apps/release/tests/__init__.py
Normal file
9
apps/release/urls.py
Normal file
9
apps/release/urls.py
Normal 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
Reference in New Issue
Block a user