Improvements

This commit is contained in:
Andras Schmelczer 2025-08-31 14:24:48 +01:00
parent 5e74ab8322
commit 45175c031e
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
5 changed files with 225 additions and 95 deletions

View file

@ -1,10 +0,0 @@
# Server Configuration
PORT=3001
NODE_ENV=development
# CORS Configuration
FRONTEND_URL=*
# File Paths
DATA_PATH=../fizika.json
PICS_PATH=../pics

View file

@ -1,61 +1,26 @@
# Multi-stage Dockerfile for Fizika Admin Backend
FROM node:18-alpine AS base FROM node:18-alpine AS base
# Install dumb-init for proper signal handling # Install dumb-init for proper signal handling
RUN apk add --no-cache dumb-init RUN apk add --no-cache dumb-init
# Create app directory
WORKDIR /usr/src/app WORKDIR /usr/src/app
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S fizika -u 1001
# Copy package files
COPY package*.json ./ COPY package*.json ./
# Development stage
FROM base AS dependencies
# Install dependencies
RUN npm ci --only=production && npm cache clean --force RUN npm ci --only=production && npm cache clean --force
# Development dependencies for building
FROM base AS dev-dependencies
RUN npm ci
# Production stage
FROM base AS production FROM base AS production
COPY --from=base /usr/src/app/node_modules ./node_modules
COPY . .
# Copy production dependencies
COPY --from=dependencies /usr/src/app/node_modules ./node_modules
# Copy application code
COPY --chown=fizika:nodejs . .
# Create necessary directories and set permissions
RUN mkdir -p /usr/src/app/data /usr/src/app/pics && \
chown -R fizika:nodejs /usr/src/app/data /usr/src/app/pics
# Security: Remove package files and any other sensitive data
RUN rm -f package*.json RUN rm -f package*.json
# Set environment variables
ENV NODE_ENV=production ENV NODE_ENV=production
ENV PORT=3001 ENV PORT=3001
# Expose port
EXPOSE 3001 EXPOSE 3001
# Switch to non-root user
USER fizika
# Health check # Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3001/api/fizika', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))" CMD node -e "require('http').get('http://localhost:3001/api/fizika', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))"
# Use dumb-init for proper signal handling
ENTRYPOINT ["dumb-init", "--"] ENTRYPOINT ["dumb-init", "--"]
# Start the application
CMD ["node", "server.js"] CMD ["node", "server.js"]

View file

@ -257,37 +257,11 @@
<div class="buttonwrapper"> <div class="buttonwrapper">
<select id="evszam"> <select id="evszam">
<option value="all/">Összes év</option> <option value="all/">Összes év</option>
<option value="2024/">2024</option> <!-- Years will be populated dynamically from question data -->
<option value="2023/">2023</option>
<option value="2022/">2022</option>
<option value="2021/">2021</option>
<option value="2020/">2020</option>
<option value="2019/">2019</option>
<option value="2018/">2018</option>
<option value="2017/">2017</option>
<option value="2016/">2016</option>
<option value="2015/">2015</option>
<option value="2014/">2014</option>
<option value="2013/">2013</option>
<option value="2012/">2012</option>
<option value="2011/">2011</option>
<option value="2010/">2010</option>
<option value="2009/">2009</option>
<option value="2008/">2008</option>
<option value="2007/">2007</option>
<option value="2006/">2006</option>
<option value="2005/">2005</option>
</select> </select>
<select id="honap"> <select id="honap">
<option value="all">Összes feladatsora</option> <option value="all">Összes feladatsora</option>
<option value="1" class="f">Május-Június</option> <!-- Months will be populated dynamically based on selected year -->
<option value="2" class="f">Október-November</option>
<option value="1" class="f2006">Február-Március</option>
<option value="2" class="f2006">Május-Június</option>
<option value="3" class="f2006">Október-November</option>
<option value="m1" class="f2016">1. Mintafeladatsor</option>
<option value="m2" class="f2016">2. Mintafeladatsor</option>
<option value="m3" class="f2016">3. Mintafeladatsor</option>
</select> </select>
<select id="feladat"> <select id="feladat">
<option value="/all">Összes feladat</option> <option value="/all">Összes feladat</option>

View file

@ -259,6 +259,17 @@ setInterval(function () {
$(document).ready(function () { $(document).ready(function () {
eredmeny(); eredmeny();
// Initialize year dropdown with dynamic years from question data
if (typeof initializeYearDropdown === 'function') {
initializeYearDropdown().then(() => {
// Initialize month dropdown for default "all" year selection
if (typeof initializeMonthDropdown === 'function') {
initializeMonthDropdown('all/');
}
});
}
$(window).on("mousewheel", function () { $(window).on("mousewheel", function () {
$("body").stop(); $("body").stop();
}); });
@ -316,17 +327,24 @@ $(document).ready(function () {
isSearch = false; isSearch = false;
}); });
$("#evszam").change(function () { $("#evszam").change(function () {
if ($("#evszam").val() == "2006/") { const selectedYear = $("#evszam").val();
// Initialize month dropdown dynamically based on selected year
if (typeof initializeMonthDropdown === 'function') {
initializeMonthDropdown(selectedYear);
} else {
// Fallback to original logic if dynamic function not available
if (selectedYear == "2006/") {
$(".f2006").show(); $(".f2006").show();
$(".f2016").hide(); $(".f2016").hide();
$(".f").hide(); $(".f").hide();
$(".fnem17").hide(); $(".fnem17").hide();
} else if ($("#evszam").val() == "2016/") { } else if (selectedYear == "2016/") {
$(".f2006").hide(); $(".f2006").hide();
$(".f2016").show(); $(".f2016").show();
$(".f").show(); $(".f").show();
$(".fnem17").show(); $(".fnem17").show();
} else if ($("#evszam").val() == "2017/") { } else if (selectedYear == "2017/") {
$(".f2006").hide(); $(".f2006").hide();
$(".f2016").hide(); $(".f2016").hide();
$(".f").show(); $(".f").show();
@ -337,6 +355,8 @@ $(document).ready(function () {
$(".f").show(); $(".f").show();
$(".fnem17").show(); $(".fnem17").show();
} }
}
$("#honap").val("all"); $("#honap").val("all");
}); });

View file

@ -122,3 +122,184 @@ function shuffleArray(array) {
[array[i], array[j]] = [array[j], array[i]]; [array[i], array[j]] = [array[j], array[i]];
} }
} }
// Initialize year dropdown with dynamic years from question data
const initializeYearDropdown = async () => {
try {
// Load questions if not already loaded
if (questions === null) {
try {
const response = await fetch(`${API_BASE}/api/fizika`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
questions = await response.json();
console.log('Questions loaded for year dropdown initialization');
} catch (error) {
console.warn('Failed to load questions from API, falling back to local file:', error);
try {
const fallbackResponse = await fetch("fizika.json");
if (!fallbackResponse.ok) {
throw new Error(`Local file not available: ${fallbackResponse.status}`);
}
questions = await fallbackResponse.json();
console.log('Questions loaded from local fallback file for year dropdown');
} catch (fallbackError) {
console.error('Both API and local file failed:', fallbackError);
return; // Don't update dropdown if data unavailable
}
}
}
// Extract unique years from question sources
const yearSet = new Set();
questions.forEach(q => {
const yearMatch = q.source.match(/^(\d{4})\//);
if (yearMatch) {
yearSet.add(parseInt(yearMatch[1]));
}
});
// Convert to sorted array (newest first)
const uniqueYears = Array.from(yearSet).sort((a, b) => b - a);
// Get existing dropdown
const yearDropdown = document.getElementById('evszam');
if (!yearDropdown) return;
// Preserve the "Összes év" option and add dynamic years
const allYearsOption = '<option value="all/">Összes év</option>';
const yearOptions = uniqueYears.map(year =>
`<option value="${year}/">${year}</option>`
).join('');
yearDropdown.innerHTML = allYearsOption + yearOptions;
console.log('Year dropdown initialized with years:', uniqueYears);
} catch (error) {
console.error('Failed to initialize year dropdown:', error);
}
};
// Initialize month dropdown dynamically based on selected year
const initializeMonthDropdown = (selectedYear) => {
if (!questions) return;
const monthDropdown = document.getElementById('honap');
if (!monthDropdown) return;
// Always include "Összes feladatsora" option
let monthOptions = '<option value="all">Összes feladatsora</option>';
if (selectedYear === 'all/') {
// Show all possible month patterns
const monthSet = new Set();
questions.forEach(q => {
const sourceMatch = q.source.match(/^(\d{4})\/(.+?)\//);
if (sourceMatch) {
monthSet.add(sourceMatch[2]);
}
});
// Convert to sorted array and create options
const uniqueMonths = Array.from(monthSet).sort();
uniqueMonths.forEach(month => {
monthOptions += `<option value="${month}" class="fdynamic">${getMonthLabel(month)}</option>`;
});
} else {
// Extract year from selected value (e.g., "2024/" -> "2024")
const year = selectedYear.replace('/', '');
// Get unique months for this specific year
const monthSet = new Set();
questions.forEach(q => {
const sourceMatch = q.source.match(`^${year}\/(.+?)\/`);
if (sourceMatch) {
monthSet.add(sourceMatch[1]);
}
});
const uniqueMonths = Array.from(monthSet).sort();
// Special handling for known year patterns
if (year === '2006') {
// Preserve existing 2006 logic but add any new months found
monthOptions += '<option value="1" class="f2006">Február-Március</option>';
monthOptions += '<option value="2" class="f2006">Május-Június</option>';
monthOptions += '<option value="3" class="f2006">Október-November</option>';
// Add any dynamic months not covered by the standard ones
uniqueMonths.forEach(month => {
if (!['1', '2', '3'].includes(month)) {
monthOptions += `<option value="${month}" class="f2006">${getMonthLabel(month)}</option>`;
}
});
} else if (year === '2016') {
// Preserve existing 2016 logic but add any new months found
monthOptions += '<option value="1" class="f">Május-Június</option>';
monthOptions += '<option value="2" class="f">Október-November</option>';
monthOptions += '<option value="m1" class="f2016">1. Mintafeladatsor</option>';
monthOptions += '<option value="m2" class="f2016">2. Mintafeladatsor</option>';
monthOptions += '<option value="m3" class="f2016">3. Mintafeladatsor</option>';
// Add any dynamic months not covered
uniqueMonths.forEach(month => {
if (!['1', '2', 'm1', 'm2', 'm3'].includes(month)) {
monthOptions += `<option value="${month}" class="f">${getMonthLabel(month)}</option>`;
}
});
} else if (year === '2017') {
// Preserve existing 2017 logic but add any new months found
monthOptions += '<option value="1" class="f">Május-Június</option>';
monthOptions += '<option value="2" class="f">Október-November</option>';
// Add any dynamic months
uniqueMonths.forEach(month => {
if (!['1', '2'].includes(month)) {
monthOptions += `<option value="${month}" class="f">${getMonthLabel(month)}</option>`;
}
});
} else {
// For other years, use standard logic plus dynamic months
const hasStandard1 = uniqueMonths.includes('1');
const hasStandard2 = uniqueMonths.includes('2');
if (hasStandard1) {
monthOptions += '<option value="1" class="f">Május-Június</option>';
}
if (hasStandard2) {
monthOptions += '<option value="2" class="f">Október-November</option>';
}
// Add any non-standard months
uniqueMonths.forEach(month => {
if (!['1', '2'].includes(month)) {
monthOptions += `<option value="${month}" class="f">${getMonthLabel(month)}</option>`;
}
});
}
}
monthDropdown.innerHTML = monthOptions;
console.log(`Month dropdown initialized for year: ${selectedYear}`);
};
// Helper function to get readable month labels
const getMonthLabel = (monthValue) => {
// Handle known patterns
const knownLabels = {
'1': 'Május-Június',
'2': 'Október-November',
'3': 'Harmadik időszak',
'm1': '1. Mintafeladatsor',
'm2': '2. Mintafeladatsor',
'm3': '3. Mintafeladatsor'
};
return knownLabels[monthValue] || monthValue;
};