Add admin backend #4
3 changed files with 4 additions and 497 deletions
69
.gitignore
vendored
69
.gitignore
vendored
|
|
@ -1,68 +1 @@
|
||||||
# Logs
|
node_modules
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
firebase-debug.log*
|
|
||||||
firebase-debug.*.log*
|
|
||||||
|
|
||||||
# Firebase cache
|
|
||||||
.firebase/
|
|
||||||
|
|
||||||
# Firebase config
|
|
||||||
|
|
||||||
# Uncomment this if you'd like others to create their own Firebase project.
|
|
||||||
# For a team working on the same Firebase project(s), it is recommended to leave
|
|
||||||
# it commented so all members can deploy to the same project(s) in .firebaserc.
|
|
||||||
# .firebaserc
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
|
|
||||||
.DS_Store
|
|
||||||
|
|
|
||||||
193
README.md
193
README.md
|
|
@ -5,8 +5,9 @@ A comprehensive physics quiz application for Hungarian students preparing for th
|
||||||
## 🚀 Features
|
## 🚀 Features
|
||||||
|
|
||||||
### Student Interface (Frontend)
|
### Student Interface (Frontend)
|
||||||
|
|
||||||
- Interactive physics quiz questions
|
- Interactive physics quiz questions
|
||||||
- Multiple choice questions with immediate feedback
|
- Multiple choice questions with immediate feedback
|
||||||
- Category-based filtering (dynamics, mechanics, fluids, etc.)
|
- Category-based filtering (dynamics, mechanics, fluids, etc.)
|
||||||
- Search functionality by year, month, and question number
|
- Search functionality by year, month, and question number
|
||||||
- Responsive design for desktop and mobile
|
- Responsive design for desktop and mobile
|
||||||
|
|
@ -14,197 +15,9 @@ A comprehensive physics quiz application for Hungarian students preparing for th
|
||||||
- Timer functionality
|
- Timer functionality
|
||||||
|
|
||||||
### Admin Interface (Backend)
|
### Admin Interface (Backend)
|
||||||
|
|
||||||
- 📝 Full CRUD operations for quiz questions
|
- 📝 Full CRUD operations for quiz questions
|
||||||
- 🖼️ Image management (upload, view, delete)
|
- 🖼️ Image management (upload, view, delete)
|
||||||
- 📊 RESTful API for frontend integration
|
- 📊 RESTful API for frontend integration
|
||||||
- 🛡️ Basic security features (input validation)
|
- 🛡️ Basic security features (input validation)
|
||||||
- 🐳 Docker containerization ready
|
- 🐳 Docker containerization ready
|
||||||
|
|
||||||
## 📁 Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
fizika/
|
|
||||||
├── index.html # Main quiz interface
|
|
||||||
├── fizika.json # Quiz questions database
|
|
||||||
├── pics/ # Question images
|
|
||||||
├── js/ # Frontend JavaScript
|
|
||||||
│ ├── fizika.js # Main quiz logic
|
|
||||||
│ └── load.js # Data loading (updated for API)
|
|
||||||
├── css/ # Stylesheets
|
|
||||||
├── backend/ # Admin backend service
|
|
||||||
│ ├── server.js # Express server
|
|
||||||
│ ├── public/admin.html # Admin interface
|
|
||||||
│ ├── package.json # Dependencies
|
|
||||||
│ ├── Dockerfile # Container configuration
|
|
||||||
│ └── README.md # Backend documentation
|
|
||||||
├── docker-compose.yml # Multi-service setup
|
|
||||||
└── .github/workflows/ # CI/CD pipelines
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🛠️ Quick Start
|
|
||||||
|
|
||||||
### Option 1: Docker (Recommended)
|
|
||||||
|
|
||||||
1. **Clone the repository:**
|
|
||||||
```bash
|
|
||||||
git clone <repository-url>
|
|
||||||
cd fizika
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Start the services:**
|
|
||||||
```bash
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Access the applications:**
|
|
||||||
- **Student Quiz**: http://localhost (or your domain)
|
|
||||||
- **Admin Panel**: http://localhost:3001/
|
|
||||||
|
|
||||||
### Option 2: Local Development
|
|
||||||
|
|
||||||
1. **Frontend** (Static files):
|
|
||||||
- Serve the root directory with any web server
|
|
||||||
- Update `API_BASE` in `js/load.js` to point to your backend
|
|
||||||
|
|
||||||
2. **Backend** (Admin API):
|
|
||||||
```bash
|
|
||||||
cd backend
|
|
||||||
npm install
|
|
||||||
cp .env.example .env # Configure your environment
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Configuration
|
|
||||||
|
|
||||||
### Backend Environment Variables
|
|
||||||
|
|
||||||
Create `backend/.env` from the example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Server settings
|
|
||||||
PORT=3001
|
|
||||||
NODE_ENV=production
|
|
||||||
|
|
||||||
# Frontend integration
|
|
||||||
FRONTEND_URL=*
|
|
||||||
|
|
||||||
# File paths (relative to backend directory)
|
|
||||||
DATA_PATH=../fizika.json
|
|
||||||
PICS_PATH=../pics
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔌 API Integration
|
|
||||||
|
|
||||||
The frontend **prioritizes backend API** with local fallback:
|
|
||||||
- **Primary**: Auto-detects backend (`localhost:3001` for dev, same origin for production)
|
|
||||||
- **Fallback**: Local `fizika.json` and `pics/` directory when backend unavailable
|
|
||||||
- **Images**: Try backend API first, fallback to local `pics/` directory
|
|
||||||
|
|
||||||
**Graceful degradation** - Quiz works even when backend is down, using local files.
|
|
||||||
|
|
||||||
## 📊 API Endpoints
|
|
||||||
|
|
||||||
### Public (Used by Frontend)
|
|
||||||
- `GET /api/fizika` - Get all questions
|
|
||||||
- `GET /api/images` - List images
|
|
||||||
- `GET /api/pics/:filename` - Serve images
|
|
||||||
|
|
||||||
### Admin (Open Access)
|
|
||||||
- `GET|POST|PUT|DELETE /api/admin/questions` - Question management
|
|
||||||
- `POST|DELETE /api/admin/images` - Image management
|
|
||||||
|
|
||||||
## 🐳 Docker Deployment
|
|
||||||
|
|
||||||
### Production with Docker Compose
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Deploy
|
|
||||||
docker-compose up -d
|
|
||||||
|
|
||||||
# With nginx reverse proxy
|
|
||||||
docker-compose --profile nginx up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual Docker Build
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker build -t fizika-admin ./backend
|
|
||||||
docker run -d -p 3001:3001 \
|
|
||||||
-v $(pwd)/fizika.json:/usr/src/app/fizika.json:ro \
|
|
||||||
-v $(pwd)/pics:/usr/src/app/pics \
|
|
||||||
fizika-admin
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔄 CI/CD Pipeline
|
|
||||||
|
|
||||||
GitHub Actions automatically:
|
|
||||||
- Builds Docker images on pushes to main
|
|
||||||
- Publishes to GitHub Container Registry
|
|
||||||
- Signs images with Cosign for security
|
|
||||||
- Performs vulnerability scanning
|
|
||||||
- Supports multi-architecture builds (AMD64, ARM64)
|
|
||||||
|
|
||||||
## 🧪 Data Structure
|
|
||||||
|
|
||||||
Questions in `fizika.json` follow this 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"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Question Types
|
|
||||||
- `md` - Dinamika (Dynamics)
|
|
||||||
- `me` - Mechanika (Mechanics)
|
|
||||||
- `mf` - Folyadékok (Fluids)
|
|
||||||
|
|
||||||
## 🛡️ Security Features
|
|
||||||
|
|
||||||
- Input validation and sanitization
|
|
||||||
- CORS protection with configurable origins
|
|
||||||
- Secure file upload with type/size restrictions
|
|
||||||
- Security headers via Helmet.js
|
|
||||||
- Container security with non-root user
|
|
||||||
|
|
||||||
## 🎯 Usage Scenarios
|
|
||||||
|
|
||||||
1. **Student Practice**: Access main site, select categories, take quizzes
|
|
||||||
2. **Content Management**: Login to admin panel, add/edit questions and images
|
|
||||||
3. **Deployment**: Use Docker Compose for easy production deployment
|
|
||||||
4. **Development**: Use dev profile for hot-reload development
|
|
||||||
|
|
||||||
## 📚 Documentation
|
|
||||||
|
|
||||||
- **Backend API**: See `backend/README.md` for detailed API documentation
|
|
||||||
- **Frontend**: Static HTML/JS application with jQuery
|
|
||||||
- **Docker**: Multi-stage builds with security best practices
|
|
||||||
- **CI/CD**: Automated builds and deployments via GitHub Actions
|
|
||||||
|
|
||||||
## 🤝 Contributing
|
|
||||||
|
|
||||||
1. Fork the repository
|
|
||||||
2. Create a feature branch
|
|
||||||
3. Make your changes
|
|
||||||
4. Test thoroughly
|
|
||||||
5. Submit a pull request
|
|
||||||
|
|
||||||
## 📄 License
|
|
||||||
|
|
||||||
Educational use for Hungarian physics students.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Open Admin Access**
|
|
||||||
- No authentication required
|
|
||||||
- Direct access to admin panel
|
|
||||||
- ⚠️ **Secure admin access with reverse proxy in production!**
|
|
||||||
|
|
|
||||||
|
|
@ -1,239 +0,0 @@
|
||||||
# Fizika Admin Backend
|
|
||||||
|
|
||||||
A secure Node.js/Express backend for managing physics quiz questions and images for the Fizika website.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- 🔒 **JWT-based Authentication** - Secure admin access with token-based auth
|
|
||||||
- 📝 **Question Management** - Full CRUD operations for quiz questions
|
|
||||||
- 🖼️ **Image Management** - Upload, view, and delete image files
|
|
||||||
- 🛡️ **Security First** - Rate limiting, input validation, CORS protection
|
|
||||||
- 📊 **Public API** - Serves data to the frontend application
|
|
||||||
- 🐳 **Docker Ready** - Containerized with multi-stage build
|
|
||||||
- 🚀 **Production Ready** - Health checks, proper error handling, logging
|
|
||||||
|
|
||||||
## API Endpoints
|
|
||||||
|
|
||||||
### Public Endpoints
|
|
||||||
- `GET /api/fizika` - Get all questions (for frontend)
|
|
||||||
- `GET /api/images` - List all available images
|
|
||||||
- `GET /api/pics/:filename` - Serve image files
|
|
||||||
|
|
||||||
### Authentication
|
|
||||||
- `POST /api/auth/login` - Admin login (returns JWT token)
|
|
||||||
|
|
||||||
### Admin Endpoints (Require Authentication)
|
|
||||||
- `GET /api/admin/questions` - Get all questions
|
|
||||||
- `POST /api/admin/questions` - Create new question
|
|
||||||
- `PUT /api/admin/questions/:id` - Update question
|
|
||||||
- `DELETE /api/admin/questions/:id` - Delete question
|
|
||||||
- `POST /api/admin/images/upload` - Upload image
|
|
||||||
- `DELETE /api/admin/images/:filename` - Delete image
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### Using Docker (Recommended)
|
|
||||||
|
|
||||||
1. **Clone and navigate to the project:**
|
|
||||||
```bash
|
|
||||||
git clone <repository-url>
|
|
||||||
cd fizika
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Set up environment variables:**
|
|
||||||
```bash
|
|
||||||
cp backend/.env.example backend/.env
|
|
||||||
# Edit backend/.env with your configuration
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Run with Docker Compose:**
|
|
||||||
```bash
|
|
||||||
# Production mode
|
|
||||||
docker-compose up -d
|
|
||||||
|
|
||||||
# Development mode with hot reload
|
|
||||||
docker-compose --profile dev up fizika-admin-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Access the admin interface:**
|
|
||||||
- Open http://localhost:3001/
|
|
||||||
- No authentication required
|
|
||||||
|
|
||||||
### Local Development
|
|
||||||
|
|
||||||
1. **Install dependencies:**
|
|
||||||
```bash
|
|
||||||
cd backend
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Set up environment:**
|
|
||||||
```bash
|
|
||||||
cp .env.example .env
|
|
||||||
# Edit .env file with your settings
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Run the server:**
|
|
||||||
```bash
|
|
||||||
npm run dev # Development with nodemon
|
|
||||||
npm start # Production mode
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
| Variable | Description | Default |
|
|
||||||
|----------|-------------|---------|
|
|
||||||
| `PORT` | Server port | `3001` |
|
|
||||||
| `NODE_ENV` | Environment | `development` |
|
|
||||||
| `JWT_SECRET` | JWT signing secret | *Required in production* |
|
|
||||||
| `ADMIN_PASSWORD_HASH` | Bcrypt hash of admin password | `admin123` |
|
|
||||||
| `FRONTEND_URL` | CORS origin for frontend | `http://localhost:8080` |
|
|
||||||
|
|
||||||
### Generating Password Hash
|
|
||||||
|
|
||||||
```bash
|
|
||||||
node -e "console.log(require('bcrypt').hashSync('your-password', 10))"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security Features
|
|
||||||
|
|
||||||
- **Rate Limiting**: 100 requests per 15 minutes, 5 auth attempts per 15 minutes
|
|
||||||
- **Input Validation**: All inputs validated and sanitized
|
|
||||||
- **File Upload Security**: Image-only uploads with size limits (5MB)
|
|
||||||
- **JWT Authentication**: Secure token-based admin authentication
|
|
||||||
- **CORS Protection**: Configurable cross-origin request handling
|
|
||||||
- **Helmet.js**: Security headers and protection middleware
|
|
||||||
|
|
||||||
## Data Structure
|
|
||||||
|
|
||||||
### Question Object
|
|
||||||
```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": "image.jpg"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Question Types
|
|
||||||
- `md` - Dinamika (Dynamics)
|
|
||||||
- `me` - Mechanika (Mechanics)
|
|
||||||
- `mf` - Folyadékok (Fluids)
|
|
||||||
|
|
||||||
## Docker Deployment
|
|
||||||
|
|
||||||
### Building the Image
|
|
||||||
```bash
|
|
||||||
docker build -t fizika-admin ./backend
|
|
||||||
```
|
|
||||||
|
|
||||||
### Running with Docker
|
|
||||||
```bash
|
|
||||||
docker run -d \
|
|
||||||
-p 3001:3001 \
|
|
||||||
-e JWT_SECRET=your-secret-key \
|
|
||||||
-e ADMIN_PASSWORD_HASH='$2b$10$...' \
|
|
||||||
-v $(pwd)/fizika.json:/usr/src/app/fizika.json:ro \
|
|
||||||
-v $(pwd)/pics:/usr/src/app/pics \
|
|
||||||
fizika-admin
|
|
||||||
```
|
|
||||||
|
|
||||||
### Docker Compose Production
|
|
||||||
```bash
|
|
||||||
# Set environment variables
|
|
||||||
export JWT_SECRET="your-super-secret-jwt-key"
|
|
||||||
export ADMIN_PASSWORD_HASH="$2b$10$your-bcrypt-hash"
|
|
||||||
export FRONTEND_URL="https://your-domain.com"
|
|
||||||
|
|
||||||
# Run
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
## GitHub Actions
|
|
||||||
|
|
||||||
The project includes a GitHub Actions workflow that:
|
|
||||||
- Builds multi-architecture Docker images (AMD64, ARM64)
|
|
||||||
- Pushes to GitHub Container Registry
|
|
||||||
- Signs images with Cosign
|
|
||||||
- Performs security scanning with Trivy
|
|
||||||
- Runs on pushes to main branch and releases
|
|
||||||
|
|
||||||
## Admin Interface
|
|
||||||
|
|
||||||
Access the admin interface at `/`:
|
|
||||||
- **Questions Tab**: Add, edit, delete quiz questions
|
|
||||||
- **Images Tab**: Upload and manage image files
|
|
||||||
- **Responsive Design**: Works on desktop and mobile
|
|
||||||
- **Real-time Updates**: Changes reflect immediately
|
|
||||||
|
|
||||||
## API Client Example
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Login
|
|
||||||
const response = await fetch('/api/auth/login', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ password: 'your-password' })
|
|
||||||
});
|
|
||||||
const { token } = await response.json();
|
|
||||||
|
|
||||||
// Create question
|
|
||||||
await fetch('/api/admin/questions', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${token}`
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
source: '2024/test/1',
|
|
||||||
description: 'What is physics?',
|
|
||||||
a: 'Science',
|
|
||||||
b: 'Art',
|
|
||||||
c: 'Math',
|
|
||||||
d: 'Biology',
|
|
||||||
correct: 1,
|
|
||||||
type: 'md'
|
|
||||||
})
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Common Issues
|
|
||||||
|
|
||||||
1. **CORS Errors**: Check `FRONTEND_URL` environment variable
|
|
||||||
2. **Authentication Fails**: Verify `JWT_SECRET` and `ADMIN_PASSWORD_HASH`
|
|
||||||
3. **File Upload Errors**: Check write permissions on pics directory
|
|
||||||
4. **Health Check Fails**: Ensure fizika.json exists and is readable
|
|
||||||
|
|
||||||
### Logs
|
|
||||||
```bash
|
|
||||||
# Docker logs
|
|
||||||
docker logs fizika-admin
|
|
||||||
|
|
||||||
# Docker Compose logs
|
|
||||||
docker-compose logs fizika-admin
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is part of the Fizika educational platform.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
1. Fork the repository
|
|
||||||
2. Create a feature branch
|
|
||||||
3. Make your changes
|
|
||||||
4. Add tests if applicable
|
|
||||||
5. Submit a pull request
|
|
||||||
|
|
||||||
For issues or questions, please open a GitHub issue.
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue