# Schedule for College of Communication Volga State Goverment University of ICT (КС ПГУТИ) Reskin of https://lk.ks.psuti.ru/ since it lacks mobile support. [![Next.js](https://img.shields.io/badge/Next.js-16-black?logo=next.js)](https://nextjs.org/) [![React](https://img.shields.io/badge/React-19-61DAFB?logo=react&logoColor=white)](https://react.dev/) [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/) [![Tailwind CSS](https://img.shields.io/badge/Tailwind-CSS-06B6D4?logo=tailwindcss&logoColor=white)](https://tailwindcss.com/) [![shadcn/ui](https://img.shields.io/badge/shadcn%2Fui-components-000000?logo=shadcnui)](https://ui.shadcn.com/) [![SQLite](https://img.shields.io/badge/SQLite-003B57?logo=sqlite&logoColor=white)](https://www.sqlite.org/) [![Docker](https://img.shields.io/badge/Docker-ready-2496ED?logo=docker&logoColor=white)](https://www.docker.com/) [![Telegram Bot](https://img.shields.io/badge/Telegram-Bot_API-26A5E4?logo=telegram&logoColor=white)](https://core.telegram.org/bots/api) [![Website](https://img.shields.io/website?url=https%3A%2F%2Fschedule.itlxrd.space&label=schedule.itlxrd.space)](https://schedule.itlxrd.space/) [![License](https://img.shields.io/github/license/kilyabin/kspguti-schedule)](https://github.com/kilyabin/kspguti-schedule/blob/master/LICENSE) Screenshots: ![1](misc/screen1.png) ![3](misc/screen3.png)
Old screenshots (2023) [![Screenshot](https://github.com/VityaSchel/kspguti-schedule/assets/59040542/07cc1f67-ccb0-4522-a59d-16387fa11987#gh-dark-mode-only)](https://schedule.itlxrd.space/) [![Screenshot](https://github.com/VityaSchel/kspguti-schedule/assets/59040542/7bd26798-5ec1-4033-a9ca-84ffa0c44f52#gh-light-mode-only)](https://schedule.itlxrd.space/)
## Tech stack & features - React 19.2.0 with Next.js 16.1.6 (pages router) - Tailwind CSS - @shadcn/ui components (built with Radix UI) - JSDOM for parsing scraped pages, rehydration strategy for cache - TypeScript 5.9.3 with types for each package - SQLite database (better-sqlite3) for storing groups and settings - bcrypt for secure password hashing - Telegram Bot API (native `fetch`) for parsing failure notifications - Custom [js parser for teachers' photos](https://gist.github.com/VityaSchel/28f1a360ee7798511765910b39c6086c) - Accessibility & tab navigation support - Dark theme with automatic switching based on system settings - Admin panel for managing groups and settings - Optimized code structure with reusable utilities and components - Optional auto-sync of groups from `lk.ks.psuti.ru` controlled by env ## Architecture & Code Organization The project follows a feature-sliced design pattern with clear separation of concerns: **Code Structure:** - **Shared utilities** (`src/shared/utils/`): - `auth.ts` - Authentication and session management - `api-wrapper.ts` - Reusable API route wrappers with auth and error handling - `validation.ts` - Centralized validation functions - **Data layer** (`src/shared/data/`): - SQLite database for persistent storage - Data loaders with caching (1-minute TTL) - Automatic cache invalidation on updates - **API routes** (`src/pages/api/admin/`): - Unified authentication via `withAuth` wrapper - Consistent error handling - Method validation - **Components**: - Reusable UI components in `src/shared/ui/` - Feature-specific components in `src/features/` - Complex widgets in `src/widgets/` **Optimizations:** - Toggle switch component reused across admin panel - Unified data loading functions - Centralized validation logic - Consistent API error handling - Optimized cache management ## Known issues - Previous week cannot be accessed if you enter from main "/" Workaround: Locate to next week, then enter previous twice. ## Schedule modes The behaviour of group management and data source is controlled by the `SCHED_MODE` environment variable: - `hobby` (default): - Groups are managed manually via the admin panel (add/edit/delete). - The app uses whatever is stored in the local SQLite `groups` table. - `kspsuti`: - On the server, the app periodically fetches `https://lk.ks.psuti.ru/?mn=2`, parses the group list (day + distance), and synchronises it into the local database. - The admin panel shows the current groups but disables manual editing/removal — groups are treated as read‑only and must be changed on the college site. - All pages that call `loadGroups()` automatically work with the synced list. Set `SCHED_MODE=kspsuti` during deployment to enable automatic group syncing; omit it or set `SCHED_MODE=hobby` to keep the previous manual workflow. ## Project structure ``` kspguti-schedule/ ├── src/ # Source code │ ├── app/ # App-level code │ │ ├── agregator/ # Schedule fetching logic │ │ ├── parser/ # HTML parsing for schedule │ │ └── utils/ # App-level utilities │ ├── pages/ # Pages router (Next.js) │ │ ├── api/ # API routes │ │ │ └── admin/ # Admin panel API endpoints │ │ │ ├── groups.ts # Groups CRUD operations │ │ │ ├── settings.ts # Settings management │ │ │ ├── login.ts # Authentication │ │ │ ├── check-auth.ts # Auth verification │ │ │ ├── change-password.ts # Password change │ │ │ └── logs.ts # Error logs viewer │ │ ├── [group].tsx # Dynamic group schedule page │ │ ├── admin.tsx # Admin panel page │ │ └── index.tsx # Home page │ ├── entities/ # Business entities │ ├── features/ # Feature modules │ │ ├── add-group/ # Add group feature │ │ └── theme-switch/ # Theme switcher │ ├── shared/ # Shared code │ │ ├── constants/ # App constants │ │ ├── context/ # React contexts │ │ ├── data/ # Data layer │ │ │ ├── database.ts # SQLite database operations │ │ │ ├── groups-loader.ts # Groups data loader │ │ │ └── settings-loader.ts # Settings data loader │ │ ├── model/ # Data models │ │ ├── providers/ # React providers │ │ ├── ui/ # Shared UI components │ │ └── utils/ # Utility functions │ │ ├── auth.ts # Authentication utilities │ │ ├── api-wrapper.ts # API route wrappers │ │ └── validation.ts # Validation utilities │ ├── shadcn/ # shadcn/ui components │ └── widgets/ # Complex UI widgets │ ├── navbar/ # Navigation bar │ └── schedule/ # Schedule display components ├── public/ # Static assets │ └── teachers/ # Teacher photos ├── old/ # Deprecated/archived files │ ├── data/ # Old data files (groups.ts, groups.json) │ └── README.md # Documentation for old files ├── scripts/ # Deployment scripts ├── systemd/ # Systemd service file ├── db/ # SQLite database files ├── components.json # shadcn/ui config ├── docker-compose.yml # Docker Compose config ├── Dockerfile # Docker image definition ├── next.config.js # Next.js configuration ├── tailwind.config.js # Tailwind CSS config └── tsconfig.json # TypeScript config ``` ## Development ### Prerequisites - Node.js 20+ (see `.nvmrc`) - npm 10+ or pnpm ### Local development ```bash # Install dependencies npm install # Run development server npm run dev ``` ### Admin Panel The application includes an admin panel for managing groups and application settings. Access it at `/admin` route. **Features:** - Manage groups (add, edit, delete) - Configure application settings (e.g., week navigation) - Password-protected access with session management (24-hour sessions) - Change admin password from the admin panel - View error logs - Debug options (development mode only) **Security:** - Passwords are hashed using bcrypt - Session-based authentication with secure cookies - Rate limiting on login attempts (5 attempts per 15 minutes) - Default password warning if not changed **Default password:** - On first launch, the default password is `ksadmin` - ⚠️ **Important:** Change the default password immediately after first login for security! - The admin panel will show a warning if the default password is still in use **Password recovery:** - If you forgot your admin password, you can reset it using the provided scripts - See `scripts/RESET_PASSWORD.md` for detailed instructions - Quick reset: `node scripts/reset-admin-password.js "new_password"` **Environment variables for admin panel:** - `ADMIN_SESSION_SECRET` - Secret key for session tokens (optional, defaults to 'change-me-in-production') - `ADMIN_PASSWORD` - Initial admin password (optional, defaults to 'ksadmin') ⚠️ **Important:** Always set a strong `ADMIN_SESSION_SECRET` in production! **API Endpoints:** - `GET /api/admin/groups` - Get all groups - `POST /api/admin/groups` - Create new group - `PUT /api/admin/groups/[id]` - Update group - `DELETE /api/admin/groups/[id]` - Delete group - `GET /api/admin/settings` - Get settings - `PUT /api/admin/settings` - Update settings - `POST /api/admin/login` - Authenticate - `GET /api/admin/check-auth` - Check authentication status - `POST /api/admin/logout` - Logout - `POST /api/admin/change-password` - Change password - `GET /api/admin/logs` - Get error logs ### Docker deployment #### Build and run with Docker ```bash # Build the image docker build -t kspguti-schedule . # Run the container docker run -p 3000:3000 kspguti-schedule ``` #### Using Docker Compose ```bash # Build and start docker-compose up -d # View logs docker-compose logs -f # Stop docker-compose down ``` **Environment variables:** Edit `docker-compose.yml` to add your environment variables: - `PROXY_URL` - URL for schedule parsing (optional) - `PARSING_FAILURE_NOTIFICATIONS_TELEGRAM_BOTAPI_TOKEN` - Telegram bot token (optional) - `PARSING_FAILURE_NOTIFICATIONS_TELEGRAM_CHAT_ID` - Telegram chat ID (optional) - `ADMIN_SESSION_SECRET` - Secret key for session tokens (optional, but recommended in production) - `NEXT_PUBLIC_SITE_URL` - Site URL for canonical links and sitemap (optional) **Database:** - The application uses SQLite database (`db/schedule-app.db`) for storing: - Groups configuration - Application settings - Admin password (hashed with bcrypt) - Database is automatically created on first run in the `db/` directory at the project root - Database directory is excluded from deployment scripts to preserve data - No additional database setup required **Note:** Admin password is stored in SQLite database. Default password is `ksadmin` - change it after first login! ### Production deployment #### Netlify The project includes `netlify.toml` for automatic deployment configuration. #### Docker The Dockerfile uses Next.js standalone output for optimized production builds. The image includes: - Multi-stage build for smaller image size - Non-root user for security - Health checks - Production optimizations #### System installation (Linux systemd) Install the application directly on a Linux system as a systemd service: **Prerequisites:** - Linux system with systemd - Node.js 20+ installed - Root/sudo access - ICU library (for Node.js): - Arch Linux: `sudo pacman -S icu` - Ubuntu/Debian: `sudo apt-get install libicu-dev` - Fedora/RHEL/CentOS: `sudo dnf install libicu` or `sudo yum install libicu` **Installation:** ```bash # Clone the repository git clone cd kspguti-schedule # Copy example and edit .env cp .env.production.example .env nano .env # Run the installation script sudo ./scripts/install.sh ``` The installation script will: - Check Node.js and npm versions - Copy files to `/opt/kspguti-schedule` - Install dependencies - Build the production version - Install and enable systemd service **Configuration:** 1. Edit environment variables: ```bash sudo nano /opt/kspguti-schedule/.env ``` The installation script will: - Copy `.env` file from source directory if it exists - Preserve existing `.env` in installation directory if it already exists - Create `.env` from `.env.production.example` if no `.env` file is found 2. Update systemd service if needed: ```bash sudo nano /etc/systemd/system/kspguti-schedule.service ``` **Managing the service:** Use the management script for easy service control: ```bash # Start the service sudo ./scripts/manage.sh start # Stop the service sudo ./scripts/manage.sh stop # Restart the service sudo ./scripts/manage.sh restart # Check status ./scripts/manage.sh status # View logs ./scripts/manage.sh logs ./scripts/manage.sh logs -f # Follow logs # Update application sudo ./scripts/manage.sh update # Enable/disable autostart sudo ./scripts/manage.sh enable sudo ./scripts/manage.sh disable ``` **Service configuration:** - Installation directory: `/opt/kspguti-schedule` - Service user: `www-data` - Port: `3000` (configurable via environment variables) - Logs: `journalctl -u kspguti-schedule` **Environment variables:** See `.env.production.example` for available options. The application uses `.env` file in production: **Schedule parsing:** - `PROXY_URL` - URL for schedule parsing (optional, defaults to https://lk.ks.psuti.ru) - `PARSING_FAILURE_NOTIFICATIONS_TELEGRAM_BOTAPI_TOKEN` - Telegram bot token for parsing failure notifications (optional) - `PARSING_FAILURE_NOTIFICATIONS_TELEGRAM_CHAT_ID` - Telegram chat ID for receiving notifications (optional) **Admin panel:** - `ADMIN_PASSWORD` - Initial password for admin panel access (optional, defaults to 'ksadmin') - `ADMIN_SESSION_SECRET` - Secret key for session tokens (optional, defaults to 'change-me-in-production', but should be changed in production) **Note:** The admin password is stored in SQLite database and can be changed from the admin panel. The `ADMIN_PASSWORD` environment variable is only used for initial setup. **Site configuration:** - `NEXT_PUBLIC_SITE_URL` - Site URL for canonical links and sitemap (optional, defaults to https://schedule.itlxrd.space) #### Other platforms The project can be deployed to any platform supporting Node.js 20+: - Vercel - Railway - DigitalOcean App Platform - AWS App Runner - Any Docker-compatible platform