Compare commits

...

10 Commits

Author SHA1 Message Date
403d3db941 update 2025-07-15 17:16:48 +08:00
34b9822a3d update 2025-06-15 23:11:52 +08:00
313e326316 fix 2025-06-15 23:07:48 +08:00
30becb56bc update 2025-06-15 23:02:47 +08:00
ac3c7c4dec fix 2025-06-15 22:58:00 +08:00
452b4039fe test 2025-06-15 22:32:59 +08:00
3502ac0c42 test 2025-06-15 22:31:55 +08:00
2dc1c3b005 test 2025-06-15 22:28:48 +08:00
641d4a84b4 test 2025-06-15 22:23:03 +08:00
7edeea299e test 2025-06-15 22:13:35 +08:00
6 changed files with 44 additions and 57 deletions

View File

@@ -22,9 +22,6 @@ FROM python:3.12-slim
WORKDIR /app WORKDIR /app
# 创建非root用户
RUN useradd -m -u 1000 appuser
# 从builder阶段复制Python包 # 从builder阶段复制Python包
COPY --from=builder /usr/local/lib/python3.12/site-packages/ /usr/local/lib/python3.12/site-packages/ COPY --from=builder /usr/local/lib/python3.12/site-packages/ /usr/local/lib/python3.12/site-packages/
COPY --from=builder /usr/local/bin/ /usr/local/bin/ COPY --from=builder /usr/local/bin/ /usr/local/bin/
@@ -32,17 +29,7 @@ COPY --from=builder /usr/local/bin/ /usr/local/bin/
# 复制应用代码 # 复制应用代码
COPY . . COPY . .
# 设置权限 RUN chmod +x /app/entrypoint.sh
RUN chown -R appuser:appuser /app \
&& chmod +x /app/entrypoint.sh
# 在最终阶段,创建必要的目录并设置权限
RUN mkdir -p /app/media \
&& chown -R appuser:appuser /app/media \
&& chmod 755 /app/media
# 切换到非root用户
USER appuser
EXPOSE 8000 EXPOSE 8000

View File

@@ -27,9 +27,11 @@ BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.getenv("SECRET_KEY") SECRET_KEY = os.getenv("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.getenv("ENV") != "production" DEBUG = False
if DEBUG: DEV = os.getenv("ENV") != "production"
if DEV:
ALLOWED_HOSTS = ["localhost", "127.0.0.1"] ALLOWED_HOSTS = ["localhost", "127.0.0.1"]
CSRF_TRUSTED_ORIGINS = ["http://localhost:3000"] CSRF_TRUSTED_ORIGINS = ["http://localhost:3000"]
else: else:
@@ -116,7 +118,7 @@ PROD_CACHES = {
} }
} }
if DEBUG: if DEV:
DATABASES = DEV_DATABASES DATABASES = DEV_DATABASES
else: else:
# DATABASES = DEV_DATABASES # DATABASES = DEV_DATABASES
@@ -173,7 +175,7 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
AUTH_USER_MODEL = "account.User" AUTH_USER_MODEL = "account.User"
if DEBUG: if DEV:
CORS_ALLOWED_ORIGINS = ["http://localhost:3000"] CORS_ALLOWED_ORIGINS = ["http://localhost:3000"]
else: else:
CORS_ALLOWED_ORIGINS = ["https://web.xuyue.cc"] CORS_ALLOWED_ORIGINS = ["https://web.xuyue.cc"]

View File

@@ -15,12 +15,6 @@ python manage.py collectstatic --noinput
# 计算worker数量 (CPU核心数 * 2 + 1) # 计算worker数量 (CPU核心数 * 2 + 1)
WORKERS=$(python -c 'import multiprocessing; print(multiprocessing.cpu_count() * 2 + 1)') WORKERS=$(python -c 'import multiprocessing; print(multiprocessing.cpu_count() * 2 + 1)')
# 确保媒体目录存在并有正确的权限
echo "Setting up media directory..."
mkdir -p /app/media
chown -R appuser:appuser /app/media
chmod 755 /app/media
# 启动 Gunicorn # 启动 Gunicorn
echo "Starting Gunicorn with $WORKERS workers..." echo "Starting Gunicorn with $WORKERS workers..."
exec gunicorn api.asgi:application \ exec gunicorn api.asgi:application \

View File

@@ -6,7 +6,7 @@ from ninja.pagination import paginate
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from account.decorators import admin_required
from .schemas import ( from .schemas import (
SubmissionFilter, SubmissionFilter,
SubmissionIn, SubmissionIn,
@@ -17,7 +17,6 @@ from .schemas import (
from .models import Rating, Submission from .models import Rating, Submission
from task.models import Task from task.models import Task
from account.models import User
router = Router() router = Router()
@@ -46,23 +45,29 @@ def list_submissions(request, filters: SubmissionFilter = Query(...)):
""" """
获取提交列表,支持按任务和用户过滤 获取提交列表,支持按任务和用户过滤
""" """
submissions = Submission.objects.all() submissions = Submission.objects.select_related("task", "user")
if filters.task_id: if filters.task_id:
task = get_object_or_404(Task, id=filters.task_id) task = get_object_or_404(Task, id=filters.task_id)
submissions = submissions.select_related("task").filter(task=task) submissions = submissions.filter(task=task)
if filters.task_type: elif filters.task_type:
tasks = Task.objects.filter(task_type=filters.task_type) submissions = submissions.filter(task__task_type=filters.task_type)
submissions = submissions.select_related("task").filter(task__in=tasks)
if filters.username: if filters.username:
users = User.objects.filter(username__icontains=filters.username) submissions = submissions.filter(user__username__icontains=filters.username)
submissions = submissions.select_related("user").filter(user__in=users)
ratings = Rating.objects.select_related("user", "submission").filter( # 获取所有提交
user=request.user, submission__in=submissions submissions = submissions.prefetch_related("ratings")
)
rating_dict = {rating.submission_id: rating.score for rating in ratings} # 获取当前用户的评分
return [SubmissionOut.list(submission, rating_dict) for submission in submissions] user_ratings = {
rating.submission_id: rating.score
for rating in Rating.objects.filter(
user=request.user,
submission__in=submissions
)
}
return [SubmissionOut.list(submission, user_ratings) for submission in submissions]
@router.get("/{submission_id}", response=SubmissionOut) @router.get("/{submission_id}", response=SubmissionOut)

View File

@@ -1,6 +1,5 @@
from typing import List from typing import List
from ninja import Router from ninja import Router
from ninja.errors import HttpError
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from account.decorators import super_required from account.decorators import super_required
from .schemas import TutorialAll, TutorialIn, TutorialSlim from .schemas import TutorialAll, TutorialIn, TutorialSlim

36
uv.lock generated
View File

@@ -13,11 +13,11 @@ wheels = [
[[package]] [[package]]
name = "asgiref" name = "asgiref"
version = "3.8.1" version = "3.9.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/29/38/b3395cc9ad1b56d2ddac9970bc8f4141312dbaec28bc7c218b0dfafd0f42/asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590", size = 35186, upload-time = "2024-03-22T14:39:36.863Z" } sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload-time = "2025-07-08T09:07:43.344Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828, upload-time = "2024-03-22T14:39:34.521Z" }, { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" },
] ]
[[package]] [[package]]
@@ -43,16 +43,16 @@ wheels = [
[[package]] [[package]]
name = "django" name = "django"
version = "5.2.3" version = "5.2.4"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "asgiref" }, { name = "asgiref" },
{ name = "sqlparse" }, { name = "sqlparse" },
{ name = "tzdata", marker = "sys_platform == 'win32'" }, { name = "tzdata", marker = "sys_platform == 'win32'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/c6/af/77b403926025dc6f7fd7b31256394d643469418965eb528eab45d0505358/django-5.2.3.tar.gz", hash = "sha256:335213277666ab2c5cac44a792a6d2f3d58eb79a80c14b6b160cd4afc3b75684", size = 10850303, upload-time = "2025-06-10T10:14:05.174Z" } sdist = { url = "https://files.pythonhosted.org/packages/9c/7e/034f0f9fb10c029a02daaf44d364d6bf2eced8c73f0d38c69da359d26b01/django-5.2.4.tar.gz", hash = "sha256:a1228c384f8fa13eebc015196db7b3e08722c5058d4758d20cb287503a540d8f", size = 10831909, upload-time = "2025-07-02T18:47:39.19Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/1b/11/7aff961db37e1ea501a2bb663d27a8ce97f3683b9e5b83d3bfead8b86fa4/django-5.2.3-py3-none-any.whl", hash = "sha256:c517a6334e0fd940066aa9467b29401b93c37cec2e61365d663b80922542069d", size = 8301935, upload-time = "2025-06-10T10:13:58.993Z" }, { url = "https://files.pythonhosted.org/packages/14/ae/706965237a672434c8b520e89a818e8b047af94e9beb342d0bee405c26c7/django-5.2.4-py3-none-any.whl", hash = "sha256:60c35bd96201b10c6e7a78121bd0da51084733efa303cc19ead021ab179cef5e", size = 8302187, upload-time = "2025-07-02T18:47:35.373Z" },
] ]
[[package]] [[package]]
@@ -95,15 +95,15 @@ wheels = [
[[package]] [[package]]
name = "django-redis" name = "django-redis"
version = "5.4.0" version = "6.0.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "django" }, { name = "django" },
{ name = "redis" }, { name = "redis" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/83/9d/2272742fdd9d0a9f0b28cd995b0539430c9467a2192e4de2cea9ea6ad38c/django-redis-5.4.0.tar.gz", hash = "sha256:6a02abaa34b0fea8bf9b707d2c363ab6adc7409950b2db93602e6cb292818c42", size = 52567, upload-time = "2023-10-01T20:22:01.221Z" } sdist = { url = "https://files.pythonhosted.org/packages/08/53/dbcfa1e528e0d6c39947092625b2c89274b5d88f14d357cee53c4d6dbbd4/django_redis-6.0.0.tar.gz", hash = "sha256:2d9cb12a20424a4c4dde082c6122f486628bae2d9c2bee4c0126a4de7fda00dd", size = 56904, upload-time = "2025-06-17T18:15:46.376Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/f1/63caad7c9222c26a62082f4f777de26389233b7574629996098bf6d25a4d/django_redis-5.4.0-py3-none-any.whl", hash = "sha256:ebc88df7da810732e2af9987f7f426c96204bf89319df4c6da6ca9a2942edd5b", size = 31119, upload-time = "2023-10-01T20:21:33.009Z" }, { url = "https://files.pythonhosted.org/packages/7e/79/055dfcc508cfe9f439d9f453741188d633efa9eab90fc78a67b0ab50b137/django_redis-6.0.0-py3-none-any.whl", hash = "sha256:20bf0063a8abee567eb5f77f375143c32810c8700c0674ced34737f8de4e36c0", size = 33687, upload-time = "2025-06-17T18:15:34.165Z" },
] ]
[[package]] [[package]]
@@ -252,11 +252,11 @@ wheels = [
[[package]] [[package]]
name = "python-dotenv" name = "python-dotenv"
version = "1.1.0" version = "1.1.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920, upload-time = "2025-03-25T10:14:56.835Z" } sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" }, { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" },
] ]
[[package]] [[package]]
@@ -279,11 +279,11 @@ wheels = [
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.14.0" version = "4.14.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" },
] ]
[[package]] [[package]]
@@ -309,15 +309,15 @@ wheels = [
[[package]] [[package]]
name = "uvicorn" name = "uvicorn"
version = "0.34.3" version = "0.35.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click" },
{ name = "h11" }, { name = "h11" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" } sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" }, { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" },
] ]
[[package]] [[package]]