new supervisor step2
This commit is contained in:
@ -13,7 +13,7 @@
|
||||
<h1 class="text-2xl font-bold mb-6">スーパーバイザーパネル</h1>
|
||||
|
||||
<!-- 選択フォーム -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">イベントコード</label>
|
||||
<select id="eventCode" class="w-full border border-gray-300 rounded-md px-3 py-2">
|
||||
@ -26,10 +26,6 @@
|
||||
<option value="">ゼッケン番号を選択してください</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- チーム情報サマリー -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
<div class="bg-gray-50 p-4 rounded-lg">
|
||||
<div class="text-sm text-gray-500">チーム名</div>
|
||||
<div id="teamName" class="font-semibold"></div>
|
||||
@ -38,6 +34,10 @@
|
||||
<div class="text-sm text-gray-500">メンバー</div>
|
||||
<div id="members" class="font-semibold"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- チーム情報サマリー -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
<div class="bg-gray-50 p-4 rounded-lg">
|
||||
<div class="text-sm text-gray-500">スタート時刻</div>
|
||||
<div id="startTime" class="font-semibold"></div>
|
||||
@ -46,15 +46,23 @@
|
||||
<div class="text-sm text-gray-500">ゴール時刻</div>
|
||||
<div id="goalTime" class="font-semibold"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 p-4 rounded-lg">
|
||||
<div class="text-sm text-gray-500">ゴール時計</div>
|
||||
<div id="goalTime" class="font-semibold"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50 p-4 rounded-lg">
|
||||
<div class="text-sm text-gray-500">判定</div>
|
||||
<div id="validate" class="font-semibold text-blue-600"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
<div class="bg-gray-50 p-4 rounded-lg">
|
||||
<div class="text-sm text-gray-500">チェックインポイント合計</div>
|
||||
<div class="text-sm text-gray-500">CP合計</div>
|
||||
<div id="totalPoints" class="font-semibold"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50 p-4 rounded-lg">
|
||||
<div class="text-sm text-gray-500">買い物ポイント合計</div>
|
||||
<div class="text-sm text-gray-500">買物合計</div>
|
||||
<div id="buyPoints" class="font-semibold"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50 p-4 rounded-lg">
|
||||
@ -62,7 +70,7 @@
|
||||
<div id="latePoints" class="font-semibold text-red-600"></div>
|
||||
</div>
|
||||
<div class="bg-gray-50 p-4 rounded-lg">
|
||||
<div class="text-sm text-gray-500">総合計ポイント</div>
|
||||
<div class="text-sm text-gray-500">総合計</div>
|
||||
<div id="finalPoints" class="font-semibold text-blue-600"></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -72,11 +80,14 @@
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">順序</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">CP番号</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">チェックイン時刻</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">検証</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">ポイント</th>
|
||||
<th class="px-2 py-3 text-left text-xs font-medium text-gray-500 uppercase">走行順</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">規定写真</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">撮影写真</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">CP名称</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">通過時刻</th>
|
||||
<th class="px-2 py-3 text-left text-xs font-medium text-gray-500 uppercase">通過審査</th>
|
||||
<th class="px-2 py-3 text-left text-xs font-medium text-gray-500 uppercase">買物審査</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">獲得点数</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="checkinList" class="bg-white divide-y divide-gray-200">
|
||||
@ -87,6 +98,9 @@
|
||||
|
||||
<!-- アクションボタン -->
|
||||
<div class="mt-6 flex justify-end space-x-4">
|
||||
<button onclick="showAddCPModal()" class="px-4 py-2 bg-blue-500 text-white rounded">
|
||||
新規CP追加
|
||||
</button>
|
||||
<button id="saveButton" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
|
||||
保存
|
||||
</button>
|
||||
@ -98,6 +112,10 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// APIのベースURLを環境に応じて設定
|
||||
const API_BASE_URL = '/api';
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Sortable初期化
|
||||
const checkinList = document.getElementById('checkinList');
|
||||
@ -108,16 +126,25 @@
|
||||
}
|
||||
});
|
||||
|
||||
// イベントコードのドロップダウン要素を取得
|
||||
const eventCodeSelect = document.getElementById('eventCode');
|
||||
const zekkenNumberSelect = document.getElementById('zekkenNumber');
|
||||
|
||||
// ゼッケン番号変更時の処理
|
||||
zekkenNumberSelect.addEventListener('change', function(e) {
|
||||
const eventCode = eventCodeSelect.value;
|
||||
if (!eventCode) {
|
||||
alert('イベントコードを選択してください');
|
||||
return;
|
||||
}
|
||||
loadTeamData(e.target.value, eventCode);
|
||||
});
|
||||
|
||||
// イベントコード変更時の処理
|
||||
document.getElementById('eventCode').addEventListener('change', function(e) {
|
||||
loadZekkenNumbers(e.target.value);
|
||||
});
|
||||
|
||||
// ゼッケン番号変更時の処理
|
||||
document.getElementById('zekkenNumber').addEventListener('change', function(e) {
|
||||
loadTeamData(e.target.value);
|
||||
});
|
||||
|
||||
// チェックボックス変更時の処理
|
||||
checkinList.addEventListener('change', function(e) {
|
||||
if (e.target.type === 'checkbox') {
|
||||
@ -131,28 +158,46 @@
|
||||
// Excel出力ボタンの処理
|
||||
document.getElementById('exportButton').addEventListener('click', exportExcel);
|
||||
|
||||
console.log('Page loaded, attempting to load events...');
|
||||
// 初期データ読み込み
|
||||
loadEventCodes();
|
||||
});
|
||||
|
||||
function loadEventCodes() {
|
||||
// APIからイベントコードを取得して選択肢を設定
|
||||
fetch('/api/events')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const select = document.getElementById('eventCode');
|
||||
data.forEach(event => {
|
||||
const option = document.createElement('option');
|
||||
option.value = event.code;
|
||||
option.textContent = event.name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
async function loadEventCodes() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/events/`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (!contentType || !contentType.includes("application/json")) {
|
||||
throw new TypeError("Response is not JSON");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const select = document.getElementById('eventCode');
|
||||
// 既存のオプションをクリア
|
||||
select.innerHTML = '<option value="">イベントを選択してください</option>';
|
||||
|
||||
data.forEach(event => {
|
||||
const option = document.createElement('option');
|
||||
option.value = event.code;
|
||||
option.textContent = event.name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading events:', error);
|
||||
// ユーザーにエラーを表示
|
||||
const select = document.getElementById('eventCode');
|
||||
select.innerHTML = '<option value="">エラー: イベントの読み込みに失敗しました</option>';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function loadZekkenNumbers(eventCode) {
|
||||
// APIからゼッケン番号を取得して選択肢を設定
|
||||
fetch(`/api/zekken-numbers/${eventCode}`)
|
||||
fetch(`${API_BASE_URL}/zekken_numbers/${eventCode}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const select = document.getElementById('zekkenNumber');
|
||||
@ -166,11 +211,129 @@
|
||||
});
|
||||
}
|
||||
|
||||
function loadTeamData(zekkenNumber) {
|
||||
|
||||
async function loadTeamData(zekkenNumber,event_code) {
|
||||
try {
|
||||
const [teamResponse, checkinsResponse] = await Promise.all([
|
||||
fetch(`${API_BASE_URL}/team_info/${zekkenNumber}/`),
|
||||
fetch(`${API_BASE_URL}/checkins/${zekkenNumber}/${event_code}/`),
|
||||
]);
|
||||
|
||||
// 各レスポンスのステータスを個別にチェック
|
||||
if (!teamResponse.ok)
|
||||
throw new Error(`Team info fetch failed with status ${teamResponse.status}`);
|
||||
if (!checkinsResponse.ok)
|
||||
throw new Error(`Checkins fetch failed with status ${checkinsResponse.status}`);
|
||||
|
||||
|
||||
const teamData = await teamResponse.json();
|
||||
const checkinsData = await checkinsResponse.json();
|
||||
|
||||
// イベントコードに対応するイベントを検索
|
||||
//const event = eventData.find(e => e.code === document.getElementById('eventCode').value);
|
||||
|
||||
document.getElementById('teamName').textContent = teamData.team_name || '-';
|
||||
document.getElementById('members').textContent = teamData.members || '-';
|
||||
document.getElementById('startTime').textContent =
|
||||
teamData.start_datetime ? new Date(teamData.start_datetime).toLocaleString() : '-';
|
||||
document.getElementById('goalTime').textContent =
|
||||
teamData.end_datetime ? new Date(teamData.end_datetime).toLocaleString() : '-';
|
||||
//'(未ゴール)';
|
||||
|
||||
// チェックインリストの更新
|
||||
const tbody = document.getElementById('checkinList');
|
||||
tbody.innerHTML = ''; // 既存のデータをクリア
|
||||
|
||||
let totalPoints = 0;
|
||||
let buyPoints = 0;
|
||||
|
||||
checkinsData.forEach((checkin, index) => {
|
||||
const tr = document.createElement('tr');
|
||||
tr.dataset.id = checkin.id;
|
||||
tr.dataset.cpNumber = checkin.cp_number;
|
||||
|
||||
const bgColor = checkin.buy_point > 0 ? 'bg-blue-100' : 'bg-red-100';
|
||||
|
||||
tr.innerHTML = `
|
||||
<td class="px-6 py-4">${checkin.path_order}</td>
|
||||
<td class="px-6 py-4">
|
||||
${location.photos ?
|
||||
`<img src="${checkin.photos}" class="h-20 w-20 object-cover rounded">` : ''}
|
||||
<div class="text-sm">${checkin.photos}</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
${checkin.image_address ?
|
||||
`<img src="${checkin.image_address}" class="h-20 w-20 object-cover rounded">` : ''}
|
||||
${checkin.receipt_address && checkin.buy_flag ?
|
||||
`<img src="${checkin.receipt_address}" class="h-20 w-20 object-cover rounded ml-2">` : ''}
|
||||
<div class="text-sm">${checkin.image_address}</div>
|
||||
<div class="text-sm">${checkin.receipt_address}</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 ${bgColor}">
|
||||
<div class="font-bold">${checkin.sub_loc_id}</div>
|
||||
<div class="text-sm">${checkin.location_name}</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">${checkin.create_at || '不明'}</td>
|
||||
<td class="px-6 py-4">
|
||||
<input type="checkbox"
|
||||
${checkin.validate_location ? 'checked' : ''}
|
||||
class="h-4 w-4 text-blue-600 rounded validate-checkbox"
|
||||
onchange="updatePoints(this)">
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
${checkin.buy_point > 0 ? `
|
||||
<input type="checkbox"
|
||||
${checkin.buy_flag ? 'checked' : ''}
|
||||
class="h-4 w-4 text-green-600 rounded buy-checkbox"
|
||||
onchange="updateBuyPoints(this)">
|
||||
` : ''}
|
||||
</td>
|
||||
<td class="px-6 py-4 point-value">${checkin.points}</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
|
||||
if (checkin.points) {
|
||||
if (checkin.buy_flag) {
|
||||
buyPoints += checkin.points;
|
||||
} else {
|
||||
totalPoints += checkin.points;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 合計ポイントの更新
|
||||
document.getElementById('totalPoints').textContent = totalPoints;
|
||||
document.getElementById('buyPoints').textContent = buyPoints;
|
||||
document.getElementById('latePoints').textContent = teamData.late_points || 0;
|
||||
document.getElementById('finalPoints').textContent =
|
||||
totalPoints + buyPoints - (teamData.late_points || 0);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading team info:', error);
|
||||
|
||||
// エラーメッセージをユーザーに表示
|
||||
alert(`データの読み込みに失敗しました: ${error.message}`);
|
||||
|
||||
// UIをクリア
|
||||
document.getElementById('teamName').textContent = '-';
|
||||
document.getElementById('members').textContent = '-';
|
||||
document.getElementById('startTime').textContent = '-';
|
||||
document.getElementById('goalTime').textContent = '-';
|
||||
document.getElementById('checkinList').innerHTML = '';
|
||||
document.getElementById('totalPoints').textContent = '0';
|
||||
document.getElementById('buyPoints').textContent = '0';
|
||||
document.getElementById('latePoints').textContent = '0';
|
||||
document.getElementById('finalPoints').textContent = '0';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function loadTeamData_old(zekkenNumber) {
|
||||
// チーム情報とチェックインデータを取得
|
||||
Promise.all([
|
||||
fetch(`/api/team-info/${zekkenNumber}`),
|
||||
fetch(`/api/checkins/${zekkenNumber}`)
|
||||
fetch(`${API_BASE_URL}/team_info/${zekkenNumber}`),
|
||||
fetch(`${API_BASE_URL}/checkins/${zekkenNumber}`)
|
||||
]).then(responses => Promise.all(responses.map(r => r.json())))
|
||||
.then(([teamInfo, checkins]) => {
|
||||
updateTeamInfo(teamInfo);
|
||||
@ -194,20 +357,137 @@
|
||||
checkins.forEach((checkin, index) => {
|
||||
const tr = document.createElement('tr');
|
||||
tr.dataset.id = checkin.id;
|
||||
tr.dataset.cpNumber = checkin.cp_number;
|
||||
|
||||
const bgColor = checkin.buy_point > 0 ? 'bg-blue-100' : 'bg-red-100';
|
||||
|
||||
tr.innerHTML = `
|
||||
<td class="px-6 py-4">${index + 1}</td>
|
||||
<td class="px-6 py-4">${checkin.cp_number}</td>
|
||||
<td class="px-6 py-4">${formatDateTime(checkin.create_at)}</td>
|
||||
<td class="px-6 py-4">
|
||||
<input type="checkbox" ${checkin.validate_location ? 'checked' : ''}
|
||||
class="h-4 w-4 text-blue-600 rounded validate-checkbox">
|
||||
<td class="px-6 py-4">${checkin.path_order}</td>
|
||||
<td class="px-6 py-4 ${bgColor}">
|
||||
<div class="font-bold">${checkin.sub_loc_id}</div>
|
||||
<div class="text-sm">${checkin.location_name}</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">${checkin.points}</td>
|
||||
<td class="px-6 py-4">
|
||||
${checkin.image_address ?
|
||||
`<img src="${checkin.image_address}" class="h-20 w-20 object-cover rounded">` : ''}
|
||||
${checkin.receipt_address && checkin.buy_flag ?
|
||||
`<img src="${checkin.receipt_address}" class="h-20 w-20 object-cover rounded ml-2">` : ''}
|
||||
</td>
|
||||
<td class="px-6 py-4">${checkin.create_at || '不明'}</td>
|
||||
<td class="px-6 py-4">
|
||||
<input type="checkbox"
|
||||
${checkin.validate_location ? 'checked' : ''}
|
||||
class="h-4 w-4 text-blue-600 rounded validate-checkbox"
|
||||
onchange="updatePoints(this)">
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
${checkin.buy_point > 0 ? `
|
||||
<input type="checkbox"
|
||||
${checkin.buy_flag ? 'checked' : ''}
|
||||
class="h-4 w-4 text-green-600 rounded buy-checkbox"
|
||||
onchange="updateBuyPoints(this)">
|
||||
` : ''}
|
||||
</td>
|
||||
<td class="px-6 py-4 point-value">${checkin.points}</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
calculateTotalPoints();
|
||||
}
|
||||
|
||||
// ポイント更新関数
|
||||
function updatePoints(checkbox) {
|
||||
const tr = checkbox.closest('tr');
|
||||
const pointCell = tr.querySelector('.point-value');
|
||||
const cpNumber = tr.dataset.cpNumber;
|
||||
|
||||
fetch(`${API_BASE_URL}/location/${cpNumber}/`)
|
||||
.then(response => response.json())
|
||||
.then(location => {
|
||||
const points = checkbox.checked ? location.checkin_point : 0;
|
||||
pointCell.textContent = points;
|
||||
calculateTotalPoints();
|
||||
});
|
||||
}
|
||||
|
||||
// 買い物ポイント更新関数
|
||||
function updateBuyPoints(checkbox) {
|
||||
const tr = checkbox.closest('tr');
|
||||
const pointCell = tr.querySelector('.point-value');
|
||||
const cpNumber = tr.dataset.cpNumber;
|
||||
|
||||
fetch(`${API_BASE_URL}/location/${cpNumber}/`)
|
||||
.then(response => response.json())
|
||||
.then(location => {
|
||||
const points = checkbox.checked ? location.buy_point : 0;
|
||||
pointCell.textContent = points;
|
||||
calculateTotalPoints();
|
||||
});
|
||||
}
|
||||
|
||||
// 新規CP追加用のモーダル
|
||||
function showAddCPModal() {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center';
|
||||
modal.innerHTML = `
|
||||
<div class="bg-white p-6 rounded-lg w-96">
|
||||
<h3 class="text-lg font-bold mb-4">新規CPを追加</h3>
|
||||
<div class="space-y-4" id="cpInputs">
|
||||
<div class="flex gap-2">
|
||||
<input type="number" class="cp-input border rounded px-2 py-1 w-full" placeholder="CP番号">
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end mt-4 space-x-2">
|
||||
<button onclick="addCPInput()" class="px-4 py-2 bg-blue-500 text-white rounded">
|
||||
追加
|
||||
</button>
|
||||
<button onclick="saveCPs()" class="px-4 py-2 bg-green-500 text-white rounded">
|
||||
保存
|
||||
</button>
|
||||
<button onclick="closeModal()" class="px-4 py-2 bg-gray-500 text-white rounded">
|
||||
キャンセル
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
}
|
||||
|
||||
function saveCPs() {
|
||||
const inputs = document.querySelectorAll('.cp-input');
|
||||
const eventCode = eventCodeSelect.value;
|
||||
const newCheckins = Array.from(inputs)
|
||||
.map(input => ({
|
||||
cp_number: input.value,
|
||||
create_at: null,
|
||||
validate_location: false,
|
||||
buy_flag: false,
|
||||
points: 0
|
||||
}));
|
||||
|
||||
// APIを呼び出して保存
|
||||
fetch(`${API_BASE_URL}/checkins/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
zekken_number: currentZekkenNumber,
|
||||
checkins: newCheckins
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
loadTeamData(currentZekkenNumber,eventCode);
|
||||
closeModal();
|
||||
});
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.querySelector('.fixed').remove();
|
||||
}
|
||||
|
||||
|
||||
function updatePathOrders() {
|
||||
const rows = Array.from(document.getElementById('checkinList').children);
|
||||
rows.forEach((row, index) => {
|
||||
@ -250,7 +530,7 @@
|
||||
validate_location: row.querySelector('.validate-checkbox').checked
|
||||
}));
|
||||
|
||||
fetch('/api/update-checkins', {
|
||||
fetch('${API_BASE_URL}/update_checkins', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@ -1,33 +1,38 @@
|
||||
# HTTPS server
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name rogaining.sumasen.net;
|
||||
server_name rogaining.sumasen.net localhost;
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log debug;
|
||||
access_log /var/log/nginx/api_access.log;
|
||||
error_log /var/log/nginx/api_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/ {
|
||||
# Django admin
|
||||
location ~ ^/(admin|api)/ {
|
||||
proxy_pass http://api:8100;
|
||||
proxy_http_version 1.1;
|
||||
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';
|
||||
proxy_set_header X-CSRFToken $http_x_csrf_token;
|
||||
|
||||
# タイムアウト設定
|
||||
proxy_connect_timeout 300;
|
||||
proxy_send_timeout 300;
|
||||
proxy_read_timeout 300;
|
||||
send_timeout 300;
|
||||
}
|
||||
|
||||
# Supervisor webapp
|
||||
location /supervisor/ {
|
||||
alias /usr/share/nginx/html/supervisor/;
|
||||
try_files $uri $uri/ /supervisor/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 /static/ {
|
||||
@ -36,6 +41,10 @@ server {
|
||||
add_header Cache-Control "public, no-transform";
|
||||
}
|
||||
|
||||
location = / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
}
|
||||
|
||||
error_page 404 /404.html;
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
45
supervisor/nginx/default.conf.80
Normal file
45
supervisor/nginx/default.conf.80
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
@ -7,29 +7,38 @@ server {
|
||||
|
||||
# HTTPS server
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:80;
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
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;
|
||||
# Django admin
|
||||
location /admin/ {
|
||||
proxy_pass http://api:8100;
|
||||
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;
|
||||
}
|
||||
|
||||
# Supervisor webapp
|
||||
location /supervisor/ {
|
||||
alias /usr/share/nginx/html/supervisor/;
|
||||
try_files $uri $uri/ /supervisor/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';
|
||||
}
|
||||
|
||||
# API endpoints
|
||||
location /api/ {
|
||||
proxy_pass http://api:8100;
|
||||
proxy_http_version 1.1;
|
||||
@ -37,10 +46,15 @@ server {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user