From 2913a435c1fffa5a814f727afb997d9da0c536d9 Mon Sep 17 00:00:00 2001 From: hayano Date: Mon, 28 Oct 2024 02:20:28 +0000 Subject: [PATCH] initialize supervisor...still has bugs --- Dockerfile.gdal | 4 +- Dockerfile.supervisor | 26 +++ docker-compose.yaml | 30 ++++ nginx.conf | 32 ++-- rog/urls.py | 12 +- rog/views.py | 110 ++++++++++++ supervisor-html.html | 274 ++++++++++++++++++++++++++++++ supervisor/html/index.html | 274 ++++++++++++++++++++++++++++++ supervisor/nginx/default.conf | 45 +++++ supervisor/nginx/default.conf.ssl | 51 ++++++ 10 files changed, 842 insertions(+), 16 deletions(-) create mode 100644 Dockerfile.supervisor create mode 100644 supervisor-html.html create mode 100755 supervisor/html/index.html create mode 100644 supervisor/nginx/default.conf create mode 100644 supervisor/nginx/default.conf.ssl diff --git a/Dockerfile.gdal b/Dockerfile.gdal index 77fb835..98097a6 100644 --- a/Dockerfile.gdal +++ b/Dockerfile.gdal @@ -51,7 +51,9 @@ RUN pip install gunicorn #RUN ["chmod", "+x", "wait-for.sh"] -RUN pip install -r requirements.txt +# xlsxwriterを追加 +RUN pip install -r requirements.txt \ + && pip install xlsxwriter gunicorn COPY . /app diff --git a/Dockerfile.supervisor b/Dockerfile.supervisor new file mode 100644 index 0000000..a1c088b --- /dev/null +++ b/Dockerfile.supervisor @@ -0,0 +1,26 @@ +FROM nginx:alpine + +# Create necessary directories and set permissions +RUN mkdir -p /usr/share/nginx/html \ + && mkdir -p /var/log/nginx \ + && mkdir -p /var/cache/nginx \ + && chown -R nginx:nginx /usr/share/nginx/html \ + && chown -R nginx:nginx /var/log/nginx \ + && chown -R nginx:nginx /var/cache/nginx \ + && chmod -R 755 /usr/share/nginx/html + +# Copy files - notice the change in the source path +COPY supervisor/html/* /usr/share/nginx/html/ +COPY supervisor/nginx/default.conf /etc/nginx/conf.d/default.conf + +# Set final permissions +RUN chown -R nginx:nginx /usr/share/nginx/html \ + && chmod -R 755 /usr/share/nginx/html \ + && touch /var/log/nginx/access.log \ + && touch /var/log/nginx/error.log \ + && chown -R nginx:nginx /var/log/nginx \ + && chown -R nginx:nginx /etc/nginx/conf.d + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker-compose.yaml b/docker-compose.yaml index e091c8f..3b3247e 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -37,6 +37,34 @@ services: #entrypoint: ["/app/wait-for.sh", "postgres-db:5432", "--", ""] #command: python3 manage.py runserver 0.0.0.0:8100 + supervisor-web: + build: + context: . + dockerfile: Dockerfile.supervisor + volumes: + - type: bind + source: ./supervisor/html + target: /usr/share/nginx/html + read_only: true + - type: bind + source: ./supervisor/nginx/default.conf + target: /etc/nginx/conf.d/default.conf + read_only: true + - type: volume + source: static_volume + target: /app/static + read_only: true + - type: volume + source: nginx_logs + target: /var/log/nginx + ports: + - "80:80" + depends_on: + - api + networks: + - rog-api + restart: always + networks: rog-api: @@ -45,3 +73,5 @@ networks: volumes: postgres_data: geoserver-data: + static_volume: + nginx_logs: diff --git a/nginx.conf b/nginx.conf index a4fd15c..ace435c 100644 --- a/nginx.conf +++ b/nginx.conf @@ -26,29 +26,33 @@ http { #gzip on; server { - listen 80; - server_name localhost; - + listen 80; + server_name localhost; + + # 静的ファイルの提供 location /static/ { - alias /app/static/; + alias /app/static/; } - - location /media/ { - alias /app/media/; - } - - + + # スーパーバイザー Web アプリケーション location / { - proxy_pass http://app:8000; + root /usr/share/nginx/html; + index index.html; + try_files $uri $uri/ /index.html; + } + + # Django API プロキシ + location /api/ { + proxy_pass http://api:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } - - error_page 500 502 503 504 /50x.html; + + error_page 500 502 503 504 /50x.html; location = /50x.html { - root /usr/share/nginx/html; + root /usr/share/nginx/html; } } } diff --git a/rog/urls.py b/rog/urls.py index 2e7862d..a455bec 100644 --- a/rog/urls.py +++ b/rog/urls.py @@ -1,7 +1,8 @@ from sys import prefix from rest_framework import urlpatterns from rest_framework.routers import DefaultRouter -from .views import LocationViewSet, Location_lineViewSet, Location_polygonViewSet, Jpn_Main_PerfViewSet, LocationsInPerf, ExtentForSubPerf, SubPerfInMainPerf, ExtentForMainPerf, LocationsInSubPerf, CatView, RegistrationAPI, LoginAPI, UserAPI, UserActionViewset, UserMakeActionViewset, UserDestinations, UpdateOrder, LocationInBound, DeleteDestination, CustomAreaLocations, GetAllGifuAreas, CustomAreaNames, userDetials, UserTracksViewSet, CatByCity, ChangePasswordView, GoalImageViewSet, CheckinImageViewSet, ExtentForLocations, DeleteAccount, PrivacyView, RegistrationView, TeamViewSet,MemberViewSet,EntryViewSet,RegisterView, VerifyEmailView, NewEventListView,NewEvent2ListView,NewCategoryListView,CategoryListView, MemberUserDetailView, TeamMembersWithUserView,MemberAddView,UserActivationView,RegistrationView,TempUserRegistrationView,ResendInvitationEmailView,update_user_info,update_user_detail,ActivateMemberView, ActivateNewMemberView, PasswordResetRequestView, PasswordResetConfirmView, NewCategoryViewSet,LocationInBound2,UserLastGoalTimeView,TeamEntriesView,update_entry_status +from .views import LocationViewSet, Location_lineViewSet, Location_polygonViewSet, Jpn_Main_PerfViewSet, LocationsInPerf, ExtentForSubPerf, SubPerfInMainPerf, ExtentForMainPerf, LocationsInSubPerf, CatView, RegistrationAPI, LoginAPI, UserAPI, UserActionViewset, UserMakeActionViewset, UserDestinations, UpdateOrder, LocationInBound, DeleteDestination, CustomAreaLocations, GetAllGifuAreas, CustomAreaNames, userDetials, UserTracksViewSet, CatByCity, ChangePasswordView, GoalImageViewSet, CheckinImageViewSet, ExtentForLocations, DeleteAccount, PrivacyView, RegistrationView, TeamViewSet,MemberViewSet,EntryViewSet,RegisterView, VerifyEmailView, NewEventListView,NewEvent2ListView,NewCategoryListView,CategoryListView, MemberUserDetailView, TeamMembersWithUserView,MemberAddView,UserActivationView,RegistrationView,TempUserRegistrationView,ResendInvitationEmailView,update_user_info,update_user_detail,ActivateMemberView, ActivateNewMemberView, PasswordResetRequestView, PasswordResetConfirmView, NewCategoryViewSet,LocationInBound2,UserLastGoalTimeView,TeamEntriesView,update_entry_status,get_events,get_zekken_numbers,get_team_info,get_checkins,update_checkins,export_excel + from django.urls import path, include from knox import views as knox_views @@ -110,4 +111,13 @@ urlpatterns += [ path('entries//update-status/', update_entry_status, name='update-entry-status'), + + # for Supervisor Web app + path('api/events/', get_events, name='get_events'), + path('api/zekken_numbers//', get_zekken_numbers, name='get_zekken_numbers'), + path('api/team_info//', get_team_info, name='get_team_info'), + path('api/checkins//', get_checkins, name='get_checkins'), + path('api/update_checkins/', update_checkins, name='update_checkins'), + path('api/export_excel//', export_excel, name='export_excel'), + # for Supervisor Web app ] diff --git a/rog/views.py b/rog/views.py index 0b74c0d..b25d609 100644 --- a/rog/views.py +++ b/rog/views.py @@ -85,6 +85,9 @@ from django.conf import settings from django.db import transaction from django.core.exceptions import ValidationError +import xlsxwriter +from io import BytesIO + logger = logging.getLogger(__name__) @api_view(['PATCH']) @@ -2309,3 +2312,110 @@ class UserLastGoalTimeView(APIView): 'message': 'User not found' }, status=status.HTTP_404_NOT_FOUND) + +# ----- for Supervisor ----- + +@api_view(['GET']) +def get_events(request): + events = NewEvent2.objects.filter(public=True) + return Response([{ + 'code': event.event_name, + 'name': event.event_name + } for event in events]) + +@api_view(['GET']) +def get_zekken_numbers(request, event_code): + entries = Entry.objects.filter( + event__event_name=event_code, + is_active=True + ).order_by('zekken_number') + return Response([entry.zekken_number for entry in entries]) + +@api_view(['GET']) +def get_team_info(request, zekken_number): + entry = Entry.objects.select_related('team').get(zekken_number=zekken_number) + members = Member.objects.filter(team=entry.team) + + return Response({ + 'team_name': entry.team.team_name, + 'members': ', '.join([f"{m.lastname} {m.firstname}" for m in members]), + 'start_time': entry.start_time.strftime('%Y-%m-%d %H:%M:%S') if entry.start_time else None, + 'goal_time': entry.goal_time.strftime('%Y-%m-%d %H:%M:%S') if entry.goal_time else None, + 'late_points': entry.late_point or 0 + }) + +@api_view(['GET']) +def get_checkins(request, zekken_number): + checkins = GpsCheckin.objects.filter( + zekken_number=zekken_number + ).order_by('path_order') + return Response([{ + 'id': c.id, + 'path_order': c.path_order, + 'cp_number': c.cp_number, + 'create_at': c.create_at, + 'validate_location': c.validate_location, + 'points': c.points, + 'buy_flag': c.buy_flag + } for c in checkins]) + +@api_view(['POST']) +def update_checkins(request): + with transaction.atomic(): + for update in request.data: + checkin = GpsCheckin.objects.get(id=update['id']) + checkin.path_order = update['path_order'] + checkin.validate_location = update['validate_location'] + checkin.save() + return Response({'status': 'success'}) + +@api_view(['GET']) +def export_excel(request, zekken_number): + # エントリー情報の取得 + entry = Entry.objects.select_related('team').get(zekken_number=zekken_number) + checkins = GpsCheckin.objects.filter(zekken_number=zekken_number).order_by('path_order') + + # Excelファイルの生成 + output = BytesIO() + workbook = xlsxwriter.Workbook(output) + worksheet = workbook.add_worksheet('通過証明書') + + # スタイルの定義 + header_format = workbook.add_format({ + 'bold': True, + 'bg_color': '#CCCCCC', + 'border': 1 + }) + + # ヘッダー情報の書き込み + worksheet.write('A1', 'チーム名', header_format) + worksheet.write('B1', entry.team.team_name) + worksheet.write('A2', 'ゼッケン番号', header_format) + worksheet.write('B2', zekken_number) + + # チェックインデータの書き込み + headers = ['順序', 'CP番号', 'チェックイン時刻', '検証', 'ポイント'] + for col, header in enumerate(headers): + worksheet.write(3, col, header, header_format) + + for row, checkin in enumerate(checkins, start=4): + worksheet.write(row, 0, checkin.path_order) + worksheet.write(row, 1, checkin.cp_number) + worksheet.write(row, 2, checkin.create_at.strftime('%Y-%m-%d %H:%M:%S')) + worksheet.write(row, 3, '✓' if checkin.validate_location else '') + worksheet.write(row, 4, checkin.points) + + workbook.close() + + # レスポンスの生成 + output.seek(0) + response = HttpResponse( + output.read(), + content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + ) + response['Content-Disposition'] = f'attachment; filename=通過証明書_{zekken_number}.xlsx' + return response + +# ----- for Supervisor ----- + + diff --git a/supervisor-html.html b/supervisor-html.html new file mode 100644 index 0000000..cfa6ac3 --- /dev/null +++ b/supervisor-html.html @@ -0,0 +1,274 @@ + + + + + + スーパーバイザーパネル + + + + +
+
+

スーパーバイザーパネル

+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
チーム名
+
+
+
+
メンバー
+
+
+
+
スタート時刻
+
+
+
+
ゴール時刻
+
+
+
+ +
+
+
チェックインポイント合計
+
+
+
+
買い物ポイント合計
+
+
+
+
遅刻減点
+
+
+
+
総合計ポイント
+
+
+
+ + +
+ + + + + + + + + + + + + +
順序CP番号チェックイン時刻検証ポイント
+
+ + +
+ + +
+
+
+ + + + diff --git a/supervisor/html/index.html b/supervisor/html/index.html new file mode 100755 index 0000000..cfa6ac3 --- /dev/null +++ b/supervisor/html/index.html @@ -0,0 +1,274 @@ + + + + + + スーパーバイザーパネル + + + + +
+
+

スーパーバイザーパネル

+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
チーム名
+
+
+
+
メンバー
+
+
+
+
スタート時刻
+
+
+
+
ゴール時刻
+
+
+
+ +
+
+
チェックインポイント合計
+
+
+
+
買い物ポイント合計
+
+
+
+
遅刻減点
+
+
+
+
総合計ポイント
+
+
+
+ + +
+ + + + + + + + + + + + + +
順序CP番号チェックイン時刻検証ポイント
+
+ + +
+ + +
+
+
+ + + + diff --git a/supervisor/nginx/default.conf b/supervisor/nginx/default.conf new file mode 100644 index 0000000..cc19d4b --- /dev/null +++ b/supervisor/nginx/default.conf @@ -0,0 +1,45 @@ +# HTTPS server +server { + listen 80; + listen [::]:80; + server_name rogaining.sumasen.net; + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log debug; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization'; + + } + + location /api/ { + proxy_pass http://api:8100; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + + } + + location /static/ { + alias /app/static/; + expires 1h; + add_header Cache-Control "public, no-transform"; + } + + + error_page 404 /404.html; + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/supervisor/nginx/default.conf.ssl b/supervisor/nginx/default.conf.ssl new file mode 100644 index 0000000..feaa69b --- /dev/null +++ b/supervisor/nginx/default.conf.ssl @@ -0,0 +1,51 @@ +# HTTP to HTTPS redirect +server { + listen 80; + server_name rogaining.sumasen.net; + return 301 https://$server_name$request_uri; +} + +# HTTPS server +server { + listen 443 ssl; + listen [::]:80; + server_name rogaining.sumasen.net; + + ssl_certificate /etc/letsencrypt/live/rogaining.sumasen.net/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/rogaining.sumasen.net/privkey.pem; + + # SSL configuration + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384; + + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log debug; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api/ { + proxy_pass http://api:8100; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /static/ { + alias /app/static/; + } + + error_page 404 /404.html; + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +}