Compare commits

..

No commits in common. "main" and "asch/backend" have entirely different histories.

23 changed files with 388 additions and 415 deletions

View file

@ -1,31 +0,0 @@
name: Deploy to Pages
on:
push:
branches: ['main']
pull_request:
branches: ['main']
workflow_dispatch:
concurrency:
group: 'pages'
cancel-in-progress: false
jobs:
deploy:
runs-on: docker
steps:
- uses: actions/checkout@v4
- name: Validate static frontend
run: |
test -f frontend/index.html
test -f frontend/fizika.json
- name: Copy frontend to host pages mount
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
apt update && apt install -y rsync
mkdir -p /pages
rsync -a --delete frontend/ /pages/fizika

View file

@ -1,63 +0,0 @@
name: Build and Publish Docker Image
on:
push:
branches: ['main']
tags: ['v*']
pull_request:
branches: ['main']
workflow_dispatch:
env:
IMAGE_NAME: ${{ forgejo.repository }}/fizika-admin
jobs:
build-and-push:
runs-on: ubuntu-docker
steps:
- name: Checkout repository
uses: https://code.forgejo.org/actions/checkout@v4
- name: Extract registry host
id: registry
run: echo "host=$(echo '${{ forgejo.server_url }}' | sed 's|https\?://||')" >> "$GITHUB_OUTPUT"
- name: Log into Forgejo registry
if: forgejo.event_name != 'pull_request'
run: echo "${{ secrets.FORGEJO_TOKEN }}" | docker login "${{ steps.registry.outputs.host }}" -u "${{ forgejo.actor }}" --password-stdin
- name: Build Docker image
run: |
IMAGE="${{ steps.registry.outputs.host }}/$(echo "${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]')"
SHA_SHORT="$(echo "${{ forgejo.sha }}" | cut -c1-12)"
TAG_ARGS="-t ${IMAGE}:sha-${SHA_SHORT}"
if [ "${{ forgejo.ref }}" = "refs/heads/main" ]; then
TAG_ARGS="${TAG_ARGS} -t ${IMAGE}:main -t ${IMAGE}:latest"
fi
if [ "${{ forgejo.ref_type }}" = "tag" ]; then
REF_NAME="${{ forgejo.ref_name }}"
TAG_ARGS="${TAG_ARGS} -t ${IMAGE}:${REF_NAME}"
if echo "$REF_NAME" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
VERSION="${REF_NAME#v}"
MAJOR_MINOR="$(echo "$VERSION" | cut -d. -f1,2)"
MAJOR="$(echo "$VERSION" | cut -d. -f1)"
TAG_ARGS="${TAG_ARGS} -t ${IMAGE}:${VERSION} -t ${IMAGE}:${MAJOR_MINOR} -t ${IMAGE}:${MAJOR}"
fi
fi
docker build \
--label "org.opencontainers.image.source=${{ forgejo.server_url }}/${{ forgejo.repository }}" \
--label "org.opencontainers.image.revision=${{ forgejo.sha }}" \
--label "org.opencontainers.image.created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
${TAG_ARGS} \
./backend
echo "IMAGE=${IMAGE}" >> "$GITHUB_ENV"
- name: Push Docker image
if: forgejo.event_name != 'pull_request'
run: docker push --all-tags "$IMAGE"

6
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

36
.github/workflows/deploy.yaml vendored Normal file
View file

@ -0,0 +1,36 @@
name: Deploy to Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: "frontend"
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

86
.github/workflows/docker-publish.yml vendored Normal file
View file

@ -0,0 +1,86 @@
name: Build and Publish Docker Image
on:
push:
branches: ["main"]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/fizika-admin
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix={{branch}}-
# set latest tag for default branch
type=raw,value=latest,enable={{is_default_branch}}
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v5
with:
context: ./backend
file: ./backend/Dockerfile
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Security scanning
sbom: true
provenance: true
# Sign the resulting Docker image digest.
# This will only write to the public Rekor transparency log when the Docker
# repository is public to avoid leaking data. If you would like to publish
# transparency data even for private images, pass --force to cosign below.
# https://github.com/sigstore/cosign
- name: Sign the published Docker image
if: ${{ github.ref_type == 'tag' }}
env:
# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
TAGS: ${{ steps.meta.outputs.tags }}
DIGEST: ${{ steps.build-and-push.outputs.digest }}
# This step uses the identity token to provision an ephemeral certificate
# against the sigstore community Fulcio instance.
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}

View file

@ -1 +1,23 @@
# Fizika
# Fizika - Physics Quiz Application
A comprehensive physics quiz application for Hungarian students preparing for their physics exams (érettségi). The application consists of a frontend quiz interface and an admin backend for content management.
## 🚀 Features
### Student Interface (Frontend)
- Interactive physics quiz questions
- Multiple choice questions with immediate feedback
- Category-based filtering (dynamics, mechanics, fluids, etc.)
- Search functionality by year, month, and question number
- Responsive design for desktop and mobile
- Progress tracking and scoring
- Timer functionality
### Admin Interface (Backend)
- 📝 Full CRUD operations for quiz questions
- 🖼️ Image management (upload, view, delete)
- 📊 RESTful API for frontend integration
- 🛡️ Basic security features (input validation)
- 🐳 Docker containerization ready

134
backend/CLAUDE.md Normal file
View file

@ -0,0 +1,134 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Common Development Commands
### Running the Application
```bash
npm run dev # Development mode with nodemon hot reload
npm start # Production mode
```
### Docker Commands
```bash
# Production deployment
docker-compose up -d
# Development with hot reload
docker-compose --profile dev up fizika-admin-dev
# Build image manually
docker build -t fizika-admin ./backend
```
### Environment Setup
```bash
cp .env.example .env
# Edit .env with your configuration
```
## Architecture Overview
This is a single-file Node.js/Express backend (`server.js`) that serves as both:
1. **API Server**: Provides REST endpoints for question and image management
2. **Static File Server**: Serves the admin interface from `/public/` directory
### Key Components
**Data Storage**:
- Questions stored in JSON file at `../frontend/fizika.json` (configurable via `DATA_PATH`)
- Images stored in `../frontend/pics/` directory (configurable via `PICS_PATH`)
**Admin Interface**:
- Built-in web UI served from `/public/index.html`
- JavaScript client in `/public/admin.js`
- No authentication required (simplified for admin use)
**API Structure**:
- Public endpoints: `/api/fizika`, `/api/images`, `/api/pics/:filename`
- Admin endpoints: `/api/admin/questions`, `/api/admin/images`
- No JWT authentication implemented despite README documentation
### Question Data Format
```json
{
"id": 1,
"source": "2016/m1/1",
"description": "Question text...",
"a": "Option A",
"b": "Option B",
"c": "Option C",
"d": "Option D",
"correct": 2,
"type": "md",
"image": "optional-image.jpg"
}
```
### Complete Question Types (17 Categories)
**IMPORTANT**: The README only mentions 3 types, but the frontend supports all 17:
**Mechanics (Mechanika)**:
- `mec` - Mechanika (general mechanics)
- `mk` - Kinematika (kinematics)
- `md` - Dinamika (dynamics)
- `me` - Munka és energia (work and energy)
- `mf` - Folyadékok és gázok mechanikája (fluid mechanics)
- `mr` - Rezgések és hullámok (oscillations and waves)
**Thermodynamics**:
- `h` - Hőtan (thermodynamics)
**Electricity**:
- `ele` - Elektromosság (general electricity)
- `es` - Elektrosztatika (electrostatics)
- `ee` - Egyenáram (direct current)
- `ev` - Váltakozó áram (alternating current)
**Other Physics**:
- `m` - Mágnesesség (magnetism)
- `o` - Fénytan (optics)
- `atm` - Atomfizika (general atomic physics)
- `ah` - Atomhéj (electron shells)
- `am` - Atommag (atomic nucleus)
- `cs` - Égi mechanika, csillagászat (celestial mechanics, astronomy)
- `v` - Vegyes (mixed/various)
### Security Considerations
- Helmet.js for security headers
- CORS configuration via `FRONTEND_URL` environment variable
- File upload restricted to images only (5MB limit)
- Input validation minimal - add validation when modifying endpoints
## File Structure
```
backend/
├── server.js # Main application file
├── public/
│ ├── index.html # Admin interface HTML
│ └── admin.js # Admin interface JavaScript
├── package.json
├── Dockerfile
├── docker-compose.yml
└── .env.example
```
## Important Notes
- **Single File Architecture**: All server logic is in `server.js` - no separate route files or modules
- **File-based Data**: Uses JSON file for persistence, not a database
- **No Authentication**: Despite README documentation mentioning JWT, no auth is implemented
- **Path Dependencies**: Assumes frontend directory structure (`../frontend/fizika.json`, `../frontend/pics/`)
- **Admin UI Included**: Built-in web interface accessible at root path `/`
- **Question Types**: Support all 17 physics categories listed above, not just the 3 in README
## Making Changes
When modifying the API:
1. All changes go in `server.js`
2. Test both API endpoints and admin UI functionality
3. Ensure question type validation supports all 17 categories if adding validation
4. Consider impact on file paths and data format
5. Update environment variables in `.env.example` if needed

View file

@ -17,6 +17,7 @@ ENV NODE_ENV=production
ENV PORT=3001
EXPOSE 3001
# Health check
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))"

View file

@ -0,0 +1,70 @@
version: '3.8'
services:
fizika-admin:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "3001:3001"
environment:
- NODE_ENV=production
- PORT=3001
- FRONTEND_URL=${FRONTEND_URL:-*}
- DATA_PATH=${DATA_PATH:-/usr/src/app/fizika.json}
- PICS_PATH=${PICS_PATH:-/usr/src/app/pics}
volumes:
# Mount data files
- ./fizika.json:/usr/src/app/fizika.json:ro
- ./pics:/usr/src/app/pics
# Optional: mount for development
# - ./backend:/usr/src/app
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3001/api/fizika', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# Optional: Development service with hot reload
fizika-admin-dev:
build:
context: ./backend
dockerfile: Dockerfile
target: dev-dependencies
ports:
- "3001:3001"
environment:
- NODE_ENV=development
- PORT=3001
- FRONTEND_URL=*
- DATA_PATH=/usr/src/app/fizika.json
- PICS_PATH=/usr/src/app/pics
volumes:
- ./backend:/usr/src/app
- ./fizika.json:/usr/src/app/fizika.json
- ./pics:/usr/src/app/pics
- /usr/src/app/node_modules
command: npm run dev
profiles: ["dev"]
# Optional: Nginx reverse proxy for production
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- .:/usr/share/nginx/html:ro
# SSL certificates (if using HTTPS)
# - ./ssl:/etc/ssl/certs
depends_on:
- fizika-admin
restart: unless-stopped
profiles: ["nginx"]
networks:
default:
name: fizika-network

View file

@ -1,11 +1,5 @@
const API_BASE = window.location.origin;
window.plausible =
window.plausible ||
function () {
(window.plausible.q = window.plausible.q || []).push(arguments);
};
document.addEventListener("DOMContentLoaded", function () {
loadQuestions();
loadImages();

View file

@ -10,6 +10,13 @@
data-api="https://stats.schmelczer.dev/status"
src="https://stats.schmelczer.dev/js/script.file-downloads.hash.outbound-links.js"
></script>
<script>
window.plausible =
window.plausible ||
function () {
(window.plausible.q = window.plausible.q || []).push(arguments);
};
</script>
<style>
* {
margin: 0;
@ -331,17 +338,20 @@
<div class="form-group">
<label for="questionType">Típus:</label>
<select id="questionType" required>
<option value="mec">Mechanika</option>
<option value="mk">Kinematika</option>
<option value="md">Dinamika</option>
<option value="me">Munka és energia</option>
<option value="mf">Folyadékok és gázok mechanikája</option>
<option value="mr">Rezgések és hullámok</option>
<option value="h">Hőtan</option>
<option value="ele">Elektromosság</option>
<option value="es">Elektrosztatika</option>
<option value="ee">Egyenáram</option>
<option value="ev">Váltakozó áram</option>
<option value="m">Mágnesesség</option>
<option value="o">Fénytan</option>
<option value="atm">Atomfizika</option>
<option value="ah">Atomhéj</option>
<option value="am">Atommag</option>
<option value="cs">Égi mechanika, csillagászat</option>

View file

@ -9,22 +9,7 @@ const app = express();
const PORT = process.env.PORT || 3001;
// Security middleware
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: [
"'self'",
"https://stats.schmelczer.dev",
"'unsafe-inline'",
],
scriptSrc: [
"'self'",
"https://stats.schmelczer.dev",
"'unsafe-inline'",
],
},
},
}));
app.use(helmet());
app.use(cors({
origin: process.env.FRONTEND_URL || '*',
credentials: true
@ -49,7 +34,7 @@ const storage = multer.diskStorage({
const upload = multer({
storage: storage,
limits: { fileSize: 50 * 1024 * 1024 }, // 5MB
limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
fileFilter: (req, file, cb) => {
if (file.mimetype.startsWith('image/')) {
cb(null, true);

View file

@ -33,7 +33,6 @@ img {
-ms-user-select: none;
user-select: none;
margin: 1px;
max-width: 500px;
}
@media only screen and (max-width: 1000px){
h2 {

View file

@ -231,7 +231,7 @@ h4 {
font-size: 0.6em;
}
img {
max-width: 400px;
max-width: 500px;
}
select {
font-size: 0.8em;

View file

@ -7897,290 +7897,14 @@
},
{
"id": 659,
"source": "2024/2/15",
"source": "2024/1/15",
"description": "Az alábbi égitestek közül melyik válhat szupernóvává?",
"a": "Egy neutroncsillag.",
"b": "Egy fekete lyuk.",
"c": "Egy, a Napnál kilencszer nagyobb tömegű csillag.",
"d": "Egy óriásbolygó.",
"correct": 3,
"type": "cs",
"image": null
},
{
"id": 660,
"source": "2025/1/1",
"description": "Két diák egy-egy gördeszkán áll, mindketten nyugalomban vannak. Kezükben egy \nőket összekötő kötél végeit tartják. Egy adott pillanatban húzni kezdik egymást a kötél \nsegítségével. A gördeszkák súrlódása elhanyagolható. A húzás hatására a diákok \negymás felé gurulnak. Melyikük mozdul el kevesebbet? ",
"a": "Mindenképpen az, aki erősebben húz. ",
"b": "Mindenképpen az, aki nagyobb tömegű.",
"c": "Mindenképpen egyforma a két diák elmozdulása.",
"d": "",
"correct": 2,
"type": "md",
"image": null
},
{
"id": 661,
"source": "2025/1/2",
"description": "Egy vastag fába egy lövedék 1 cm mélyre fúródik be. Milyen mélyen fúródna be a lövedék a fába, ha az kétszeres sebességgel érkezik a fához, és a fa anyaga által a lövedékre kifejtett fékezőerő nem változik a fékeződés során?",
"a": "2 cm ",
"b": "3 cm ",
"c": "4 cm ",
"d": "",
"correct": 3,
"type": "mk",
"image": null
},
{
"id": 662,
"source": "2025/1/3",
"description": "Váltóáramú körbe kondenzátort kapcsolunk. Mekkora a kondenzátor elektromos energiája abban a pillanatban, amikor az áramerősség nulla? ",
"a": "A kondenzátor energiája ebben a pillanatban nulla. ",
"b": "A kondenzátor energiája ebben a pillanatban maximális.",
"c": "A kondenzátor energiája ebben a pillanatban a maximális érték négyzetgyök ketted része. ",
"d": "",
"correct": 2,
"type": "ev",
"image": null
},
{
"id": 663,
"source": "2025/1/4",
"description": "Egy G súlyú ládát az ábrán látható elrendezés tart \negyensúlyban. A csigát tartó kötél 45°-os szöget zár be a mennyezettel. A kötelek és a csiga súlytalannak tekinthetők. Mekkora erő hat az A pontban a falra, illetve a B pontban a mennyezetre? ",
"a": "Mindkét pontban G-nél nagyobb erő hat. ",
"b": "Az A pontban G, a B pontban G-nél nagyobb erő hat.",
"c": "Az A pontban G, a B pontban G-nél kisebb erő hat.",
"d": "Mindkét pontban G erő hat.",
"correct": 2,
"type": "md",
"image": "202514.JPG"
},
{
"id": 664,
"source": "2025/1/5",
"description": "Egy tartályban azonos tömegű hélium- és argongáz keveréke található 20°C hőmérsékleten. Melyik állítás helyes? ",
"a": "A héliumatomok átlagos sebessége megegyezik az argonatomokéval. ",
"b": "A tartályban a hélium- és az argonatomok száma azonos.",
"c": "A két gáz parciális nyomása azaz külön-külön az egyik, illetve a másik gáz nyomása az edényben egyenlő. ",
"d": "Az előző állítások egyike sem igaz. ",
"correct": 4,
"type": "h",
"image": null
},
{
"id": 665,
"source": "2025/1/6",
"description": "Az alábbi elrendezésben a tekercs áramkörét a kapcsolóval (K) zárjuk. Milyen irányú áram jön létre az R ellenálláson?",
"a": "a-ból b-be irányuló áramlökés.",
"b": "b-ből a-ba irányuló áramlökés.",
"c": "Nem jön létre áramlökés.",
"d": "",
"correct": 1,
"type": "m",
"image": "202516.JPG"
},
{
"id": 666,
"source": "2025/1/7",
"description": "Egy ejtőernyős kiugrik egy vízszintesen haladó repülőgépből, majd egy percnyi zuhanás után kinyitja az ejtőernyőt. A sebesség függőleges komponensének \nnagyságát a mozgás során a mellékelt grafikon mutatja. A mozgásra vonatkozó alábbi állítások közül melyik a helyes? ",
"a": "Az ejtőernyős az t_A időpillanatban nyitja ki az ejtőernyőt, ezt követően nem gyorsul tovább.",
"b": "Az ejtőernyős a t_B időpillanatban nyitja ki az ejtőernyőt, ezt követően a sebessége gyorsan csökkeni kezd. ",
"c": "Az ejtőernyős a t_C időpillanatban földet ér.",
"d": "",
"correct": 2,
"type": "mk",
"image": "202517.JPG"
},
{
"id": 667,
"source": "2025/1/8",
"description": "Egy adott fémet 1,4 eV energiájú fotonokat tartalmazó monokromatikus fény világít meg. Ennek hatására a fémből elektronok lépnek ki. A kilépő elektronok maximális mozgási energiája 0,3 eV lesz. Mekkora lesz a kilépő elektronok maximális energiája, ha 2,1 eV energiájú fotonokat tartalmazó fénnyel világítjuk meg a fémet? ",
"a": "Kb. 0,3 eV. ",
"b": "Kb. 0,6 eV. ",
"c": "Kb. 1 eV.",
"d": "A fémből ekkor nem lépnek ki elektronok. ",
"correct": 3,
"type": "ah",
"image": null
},
{
"id": 668,
"source": "2025/1/9",
"description": "Egy kör alakú lencséket befogadó keretbe az \nolvasószemüveget készítő optikus fordítva \nszerelte be a lencséket. Így a domború oldalukkal most befelé állnak a szem felé (ld. ábra). Hogyan használható most a szemüveg? ",
"a": "A szemüveg most is olvasószemüvegként használható. ",
"b": "A szemüveg most már rövidlátást korrigál. ",
"c": "A szemüveg így egyáltalán nem használható látásjavításra. ",
"d": "",
"correct": 1,
"type": "o",
"image": "202519.JPG"
},
{
"id": 669,
"source": "2025/1/10",
"description": "Egy merev rúdra szerelt golyót függőleges síkban, \negyenletesen forgatunk az ábrának megfelelően. Mely \nhelyzetekben lesz a golyó gyorsulásának abszolút értéke \nazonos? ",
"a": "Az 1. és a 3. helyzetben.",
"b": "A 2. és a 4. helyzetben.",
"c": "A gyorsulás nagysága mind a négy helyzetben azonos. ",
"d": "A gyorsulás nagysága mind a négy helyzetben különböző .",
"correct": 3,
"type": "md",
"image": "2025110.JPG"
},
{
"id": 670,
"source": "2025/1/11",
"description": "Egy tiszta éjszakán teleholdat látunk az égen. Milyen földfázist lát ekkor egy, a Holdon álló űrhajós, ha feltekint az égre?",
"a": ",,Újföldet”.",
"b": ",,Teleföldet”.",
"c": "A kérdésnek nincs értelme, hiszen a Hold keringése során mindig ugyanazt az oldalát mutatja a Föld felé. ",
"d": "Ennyi információból nem lehet eldönteni, mert a Föld naponta megfordul a tengelye körül. ",
"correct": 1,
"type": "cs",
"image": null
},
{
"id": 671,
"source": "2025/1/12",
"description": "Egy tengerszinten lévő kútból vizet szeretnénk felszívni egy talajon álló szivattyúval. \nHa a szivattyú csövét leengedjük a kútba, elvileg legfeljebb 10 m mélyről, a veszteségek miatt a gyakorlatban csak 78 m mélyről hozható fel a víz. Milyen mélyről tudná ugyanez a szivattyú felszivattyúzni a vizet egy 3000 m magas fennsíkon \nlévő kútból? ",
"a": "Kisebb mélységből, mert a magas fennsíkon kisebb a légnyomás. ",
"b": "Valamivel nagyobb mélységből, mert a magas fennsíkon kissé kisebb a gravitációs állandó, így a víz súlya is csökken egy kicsit. ",
"c": "A magas fennsíkon is ugyanolyan mélyről tudná felszivattyúzni, mert ez az adat csak a motor teljesítményétől függ.",
"d": "",
"correct": 1,
"type": "mf",
"image": null
},
{
"id": 672,
"source": "2025/1/13",
"description": "Két rézhuzal tömege és hőmérséklete megegyezik, azonban az A huzal hossza ötszöröse a B huzal hosszának. Mekkora a két huzal elektromos ellenállásának aránya? ",
"a": "R_a/R_b=25",
"b": "R_a/R_b=5",
"c": "R_a/R_b=1/5",
"d": "R_a/R_b=1/25",
"correct": 1,
"type": "ee",
"image": null
},
{
"id": 673,
"source": "2025/1/14",
"description": "A pozitron az elektron antirészecskéje, tehát a két részecske tömege és töltésének nagysága is megegyezik, azonban elektromos töltésük ellenkező előjelű. Tegyük fel, hogy az A tömegszámú és Z rendszámú izotóp pozitív béta bomló, vagyis radioaktív bomlásakor pozitront bocsát ki. Mekkora lesz a bomlás után létrejövő izotóp tömegszáma és rendszáma? ",
"a": "A, (Z 1)",
"b": "A, (Z + 1) ",
"c": "(A 1), Z ",
"d": "(A + 1), Z ",
"correct": 1,
"type": "am",
"image": null
},
{
"id": 674,
"source": "2025/1/15",
"description": "A képen két rezgő pontforrással felületi hullámokat keltünk egy vízzel teli kádban. Milyen jelenség látható a képen? ",
"a": "Elhajlás ",
"b": "Interferencia ",
"c": "Diffrakció",
"d": "",
"correct": 2,
"type": "mr",
"image": "2025115.JPG"
},
{
"id": 675,
"source": "2025/1/16",
"description": "Egy test két egymást követő egyenes útszakaszon egyenletesen mozog, ámde az egyes szakaszokon különböző sebességgel. Milyen feltétel esetén lesz a teljes útra vonatkozó átlagsebesség a két útszakaszra vonatkozó sebességek számtani közepe? ",
"a": "Csak akkor, ha a két útszakasz azonos hosszúságú. ",
"b": "Csak akkor, ha a két útszakaszt azonos idő alatt teszi meg a test. ",
"c": "Mindkét fenti esetben. ",
"d": "A két fenti eset egyikében sem.",
"correct": 2,
"type": "mk",
"image": null
},
{
"id": 676,
"source": "2025/1/17",
"description": "Az alábbi grafikonok közül melyik mutatja helyesen egy fonálinga T lengésidejének függését az inga l hosszától? ",
"a": "Az a) ábra. ",
"b": "A b) ábra. ",
"c": "A c) ábra.",
"d": "A d) ábra. ",
"correct": 4,
"type": "mr",
"image": "2025117.JPG"
},
{
"id": 677,
"source": "2025/1/18",
"description": "Merre mozdul el a patkómágnes két vége közé belógatott \nvezeték a kapcsoló zárása után? ",
"a": "A vezeték a mágnesek közül kifelé mozdul (az ábrán balra). ",
"b": "A vezeték a mágnesek közé befelé mozdul (az ábrán jobbra).",
"c": "Az ábra alapján nem lehet eldönteni, hogy merre mozdul a vezeték.",
"d": "",
"correct": 1,
"type": "m",
"image": "2025118.JPG"
},
{
"id": 678,
"source": "2025/1/19",
"description": "Három, egy egyenesbe eső, rögzítetlen, töltött test egy súrlódásmentes asztalon egyensúlyban van. A két szélső töltése pozitív, a középsőé negatív. Mi történik akkor, ha a jobb szélső pozitív töltésű testet a töltések egyenese mentén jobbra elmozdítjuk? ",
"a": "A középső és bal szélső töltött test is elindul jobbra. ",
"b": "A középső jobbra indul el, a bal szélső pedig balra. ",
"c": "A középső balra indul el, a bal szélső pedig jobbra. ",
"d": "Mindkét töltött test balra indul el. ",
"correct": 3,
"type": "es",
"image": null
},
{
"id": 679,
"source": "2025/1/20",
"description": "Egy súlyos gömbhéj a talaj felé zuhan. Belsejében egy apró, súlyos golyó van. Hol helyezkedik el a golyó a gömbhéj belsejében esés közben, ha a közegellenállás nem hanyagolható el? ",
"a": "A gömbhéjban alul helyezkedik el. ",
"b": "A gömbhéjban bárhol elhelyezkedhet, hiszen lebeg. ",
"c": "A gömbhéjban felül helyezkedik el.",
"d": "",
"correct": 1,
"type": "md",
"image": null
},
{
"id": 680,
"source": "2025/1/21",
"description": "Hétfőn a harmatpont 12 °C volt, kedden 14 °C olvastuk egy amatőr észlelő naplójában. Lehetséges ez?",
"a": "Nem, mert a harmatpont egy fizikai állandó, ami minden folyadékra más. ",
"b": "Igen, ha a nappali maximum-hőmérsékletek között is 2 °C volt a különbség. ",
"c": "Nem, mert a víz harmatpontja mindig 10 °C alatt van. ",
"d": "Igen, ha eltérő volt az abszolút páratartalom a hét két különböző napján.",
"correct": 4,
"type": "h",
"image": null
},
{
"id": 681,
"source": "2025/1/22",
"description": "Üvegben egy ultraibolya és egy infravörös sugár halad egymás mellett egymással párhuzamosan. Amikor elérik az üveg és levegő közötti sík közeghatárt, az ultraibolya sugár teljes visszaverődést szenved, míg az infravörös részlegesen kilép a levegőre. \nMelyik színű fényre nagyobb az üveg törésmutatója?",
"a": "Az ultraibolya sugárra. ",
"b": "Az infravörös sugárra. ",
"c": "Egyforma a törésmutató a két fényre. ",
"d": "",
"correct": 1,
"type": "o",
"image": null
},
{
"id": 682,
"source": "2025/1/23",
"description": "A háztartásokban régebben használt hagyományos izzók izzószálán bekapcsoláskor nagyobb áram folyik, mint a folyamatos működés közben. Mi lehet ennek az oka?",
"a": "Mert bekapcsoláskor még alacsonyabb az izzószál hőmérséklete, így kisebb az ellenállása. ",
"b": "Mert bekapcsoláskor még alacsonyabb az izzószál hőmérséklete, így nagyobb az ellenállása. ",
"c": "Mert bekapcsoláskor még alacsonyabb az izzószál hőmérséklete, így kisebb feszültség esik az izzószálon, mint üzemelés közben.",
"d": "",
"correct": 1,
"type": "ee",
"type": "g",
"image": null
}
]

View file

@ -68,7 +68,7 @@ const loadQuestions = async (
<div class="feladat card" id="feladat${id}">
<h2 style="float: left;">${i + 1}.</h2><h2>${source}</h2>
<pre>${description}</pre>
${image ? `<img src="${API_BASE}/api/pics/${image}" crossorigin="anonymous" onerror="this.src='pics/${image}'"><br>` : ""}
${image ? `<img src="${API_BASE}/api/pics/${image}" onerror="this.src='pics/${image}'"><br>` : ""}
<form id="form${id}"">
<input type="radio" id="rad1" name="group">
<label id="label${id}" class="rad1">${a}</label>
@ -169,14 +169,14 @@ const initializeYearDropdown = async () => {
// Preserve the "Összes év" option and add dynamic years
const allYearsOption = '<option value="all/">Összes év</option>';
const yearOptions = uniqueYears.map(year =>
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);
}
@ -211,7 +211,7 @@ const initializeMonthDropdown = (selectedYear) => {
} 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 => {
@ -222,21 +222,21 @@ const initializeMonthDropdown = (selectedYear) => {
});
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>';
@ -244,38 +244,38 @@ const initializeMonthDropdown = (selectedYear) => {
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)) {
@ -294,12 +294,12 @@ const getMonthLabel = (monthValue) => {
// Handle known patterns
const knownLabels = {
'1': 'Május-Június',
'2': 'Október-November',
'2': 'Október-November',
'3': 'Harmadik időszak',
'm1': '1. Mintafeladatsor',
'm2': '2. Mintafeladatsor',
'm2': '2. Mintafeladatsor',
'm3': '3. Mintafeladatsor'
};
return knownLabels[monthValue] || monthValue;
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB