add scripts to auto-install

This commit is contained in:
kilyabin
2025-11-18 03:57:22 +04:00
parent f7a8cc622f
commit b82d4e5085
12 changed files with 715 additions and 275 deletions

25
.env.production.example Normal file
View File

@@ -0,0 +1,25 @@
# Production environment variables for KSPGUTI Schedule
# Proxy URL for schedule parsing (optional, defaults to https://lk.ks.psuti.ru)
PROXY_URL=https://lk.ks.psuti.ru
# Telegram Bot API token for parsing failure notifications (optional)
# Get token from @BotFather on Telegram
PARSING_FAILURE_NOTIFICATIONS_TELEGRAM_BOTAPI_TOKEN=
# Telegram Chat ID for receiving notifications (optional)
# You can get your chat ID by messaging @userinfobot on Telegram
PARSING_FAILURE_NOTIFICATIONS_TELEGRAM_CHAT_ID=
# Node.js environment
NODE_ENV=production
# Disable Next.js telemetry
NEXT_TELEMETRY_DISABLED=1
# Server port (default: 3000)
PORT=3000
# Server hostname (default: 0.0.0.0)
HOSTNAME=0.0.0.0

View File

@@ -11,7 +11,7 @@ WORKDIR /app
COPY package.json package-lock.json* pnpm-lock.yaml* ./ COPY package.json package-lock.json* pnpm-lock.yaml* ./
RUN \ RUN \
if [ -f package-lock.json ]; then \ if [ -f package-lock.json ]; then \
npm ci; \ npm ci --legacy-peer-deps; \
elif [ -f pnpm-lock.yaml ]; then \ elif [ -f pnpm-lock.yaml ]; then \
corepack enable pnpm && corepack prepare pnpm@latest --activate && pnpm install --frozen-lockfile; \ corepack enable pnpm && corepack prepare pnpm@latest --activate && pnpm install --frozen-lockfile; \
else \ else \

View File

@@ -87,6 +87,98 @@ The Dockerfile uses Next.js standalone output for optimized production builds. T
- Health checks - Health checks
- Production optimizations - 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
**Installation:**
```bash
# Clone the repository
git clone <repository-url>
cd kspguti-schedule
# 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.production
```
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
```
Or use systemctl directly:
```bash
sudo systemctl start kspguti-schedule
sudo systemctl stop kspguti-schedule
sudo systemctl restart kspguti-schedule
sudo systemctl status kspguti-schedule
sudo journalctl -u kspguti-schedule -f
```
**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:
- `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)
#### Other platforms #### Other platforms
The project can be deployed to any platform supporting Node.js 20+: The project can be deployed to any platform supporting Node.js 20+:

View File

@@ -2,11 +2,6 @@
const nextConfig = { const nextConfig = {
reactStrictMode: true, reactStrictMode: true,
output: 'standalone', output: 'standalone',
redirects: async () => [{
permanent: false,
destination: '/ps7',
source: '/'
}],
generateEtags: false generateEtags: false
} }

573
package-lock.json generated
View File

@@ -21,14 +21,14 @@
"content-type": "^1.0.5", "content-type": "^1.0.5",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"jsdom": "^22.1.0", "jsdom": "^22.1.0",
"lucide-react": "^0.279.0", "lucide-react": "^0.554.0",
"next": "latest", "next": "16.0.3",
"next-sitemap": "^4.2.3", "next-sitemap": "^4.2.3",
"next-themes": "^0.2.1", "next-themes": "^0.2.1",
"node-html-parser": "^6.1.10", "node-html-parser": "^6.1.10",
"node-telegram-bot-api": "^0.63.0", "node-telegram-bot-api": "^0.63.0",
"react": "latest", "react": "19.2.0",
"react-dom": "latest", "react-dom": "19.2.0",
"react-icons": "^4.11.0", "react-icons": "^4.11.0",
"sass": "^1.69.3", "sass": "^1.69.3",
"sharp": "^0.32.6", "sharp": "^0.32.6",
@@ -38,17 +38,21 @@
}, },
"devDependencies": { "devDependencies": {
"@types/jsdom": "^21.1.3", "@types/jsdom": "^21.1.3",
"@types/node": "latest", "@types/node": "22.0.0",
"@types/node-telegram-bot-api": "^0.61.8", "@types/node-telegram-bot-api": "^0.61.8",
"@types/react": "latest", "@types/react": "19.2.0",
"@types/react-dom": "latest", "@types/react-dom": "19.2.0",
"@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/eslint-plugin": "^6.7.3",
"autoprefixer": "latest", "autoprefixer": "10.4.20",
"eslint": "latest", "eslint": "8.57.0",
"eslint-config-next": "latest", "eslint-config-next": "16.0.3",
"postcss": "latest", "postcss": "8.4.47",
"tailwindcss": "^3.4.18", "tailwindcss": "^3.4.18",
"typescript": "latest" "typescript": "5.9.3"
},
"engines": {
"node": ">=20.0.0",
"npm": ">=10.0.0"
} }
}, },
"node_modules/@alloc/quick-lru": { "node_modules/@alloc/quick-lru": {
@@ -401,82 +405,17 @@
"node": "^12.0.0 || ^14.0.0 || >=16.0.0" "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
} }
}, },
"node_modules/@eslint/config-array": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/config-array/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/@eslint/config-array/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/@eslint/config-helpers": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
"integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.17.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/eslintrc": { "node_modules/@eslint/eslintrc": {
"version": "3.3.1", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
"integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ajv": "^6.12.4", "ajv": "^6.12.4",
"debug": "^4.3.2", "debug": "^4.3.2",
"espree": "^10.0.1", "espree": "^9.6.0",
"globals": "^14.0.0", "globals": "^13.19.0",
"ignore": "^5.2.0", "ignore": "^5.2.0",
"import-fresh": "^3.2.1", "import-fresh": "^3.2.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
@@ -484,7 +423,7 @@
"strip-json-comments": "^3.1.1" "strip-json-comments": "^3.1.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
@@ -515,40 +454,13 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.39.1", "version": "8.57.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
"integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://eslint.org/donate"
}
},
"node_modules/@eslint/object-schema": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
"integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/plugin-kit": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
"integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.17.0",
"levn": "^0.4.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
} }
}, },
"node_modules/@floating-ui/core": { "node_modules/@floating-ui/core": {
@@ -589,28 +501,44 @@
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@humanfs/core": { "node_modules/@humanwhocodes/config-array": {
"version": "0.19.1", "version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
"dev": true, "deprecated": "Use @eslint/config-array instead",
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/node": {
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@humanfs/core": "^0.19.1", "@humanwhocodes/object-schema": "^2.0.2",
"@humanwhocodes/retry": "^0.4.0" "debug": "^4.3.1",
"minimatch": "^3.0.5"
}, },
"engines": { "engines": {
"node": ">=18.18.0" "node": ">=10.10.0"
}
},
"node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
} }
}, },
"node_modules/@humanwhocodes/module-importer": { "node_modules/@humanwhocodes/module-importer": {
@@ -627,19 +555,13 @@
"url": "https://github.com/sponsors/nzakas" "url": "https://github.com/sponsors/nzakas"
} }
}, },
"node_modules/@humanwhocodes/retry": { "node_modules/@humanwhocodes/object-schema": {
"version": "0.4.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
"deprecated": "Use @eslint/object-schema instead",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "BSD-3-Clause"
"engines": {
"node": ">=18.18"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/nzakas"
}
}, },
"node_modules/@img/colour": { "node_modules/@img/colour": {
"version": "1.0.0", "version": "1.0.0",
@@ -3054,13 +2976,6 @@
"integrity": "sha512-Hq9IMnfekuOCsEmYl4QX2HBrT+XsfXiupfrLLY8Dcf3Puf4BkBOxSbWYTITSOQAhJoYPBez+b4MJRpIYL65z8A==", "integrity": "sha512-Hq9IMnfekuOCsEmYl4QX2HBrT+XsfXiupfrLLY8Dcf3Puf4BkBOxSbWYTITSOQAhJoYPBez+b4MJRpIYL65z8A==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/jsdom": { "node_modules/@types/jsdom": {
"version": "21.1.7", "version": "21.1.7",
"resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz",
@@ -3088,13 +3003,13 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "24.10.1", "version": "22.0.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.0.0.tgz",
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "integrity": "sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~7.16.0" "undici-types": "~6.11.1"
} }
}, },
"node_modules/@types/node-telegram-bot-api": { "node_modules/@types/node-telegram-bot-api": {
@@ -3110,9 +3025,9 @@
} }
}, },
"node_modules/@types/react": { "node_modules/@types/react": {
"version": "19.2.5", "version": "19.2.0",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.5.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.0.tgz",
"integrity": "sha512-keKxkZMqnDicuvFoJbzrhbtdLSPhj/rZThDlKWCDbgXmUg0rEUFtRssDXKYmtXluZlIqiC5VqkCgRwzuyLHKHw==", "integrity": "sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -3120,9 +3035,9 @@
} }
}, },
"node_modules/@types/react-dom": { "node_modules/@types/react-dom": {
"version": "19.2.3", "version": "19.2.0",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.0.tgz",
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "integrity": "sha512-brtBs0MnE9SMx7px208g39lRmC5uHZs96caOJfTjFcYSLHNamvaSMfJNagChVNkup2SdtOxKX1FDBkRSJe1ZAg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
@@ -3554,6 +3469,13 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
} }
}, },
"node_modules/@ungap/structured-clone": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"dev": true,
"license": "ISC"
},
"node_modules/@unrs/resolver-binding-android-arm-eabi": { "node_modules/@unrs/resolver-binding-android-arm-eabi": {
"version": "1.11.1", "version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz",
@@ -4193,9 +4115,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/autoprefixer": { "node_modules/autoprefixer": {
"version": "10.4.22", "version": "10.4.20",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.22.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
"integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==", "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -4213,11 +4135,11 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"browserslist": "^4.27.0", "browserslist": "^4.23.3",
"caniuse-lite": "^1.0.30001754", "caniuse-lite": "^1.0.30001646",
"fraction.js": "^5.3.4", "fraction.js": "^4.3.7",
"normalize-range": "^0.1.2", "normalize-range": "^0.1.2",
"picocolors": "^1.1.1", "picocolors": "^1.0.1",
"postcss-value-parser": "^4.2.0" "postcss-value-parser": "^4.2.0"
}, },
"bin": { "bin": {
@@ -5461,63 +5383,60 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "9.39.1", "version": "8.57.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.6.1",
"@eslint/config-array": "^0.21.1", "@eslint/eslintrc": "^2.1.4",
"@eslint/config-helpers": "^0.4.2", "@eslint/js": "8.57.0",
"@eslint/core": "^0.17.0", "@humanwhocodes/config-array": "^0.11.14",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.39.1",
"@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2", "@nodelib/fs.walk": "^1.2.8",
"@types/estree": "^1.0.6", "@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4", "ajv": "^6.12.4",
"chalk": "^4.0.0", "chalk": "^4.0.0",
"cross-spawn": "^7.0.6", "cross-spawn": "^7.0.2",
"debug": "^4.3.2", "debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.4.0", "eslint-scope": "^7.2.2",
"eslint-visitor-keys": "^4.2.1", "eslint-visitor-keys": "^3.4.3",
"espree": "^10.4.0", "espree": "^9.6.1",
"esquery": "^1.5.0", "esquery": "^1.4.2",
"esutils": "^2.0.2", "esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"file-entry-cache": "^8.0.0", "file-entry-cache": "^6.0.1",
"find-up": "^5.0.0", "find-up": "^5.0.0",
"glob-parent": "^6.0.2", "glob-parent": "^6.0.2",
"globals": "^13.19.0",
"graphemer": "^1.4.0",
"ignore": "^5.2.0", "ignore": "^5.2.0",
"imurmurhash": "^0.1.4", "imurmurhash": "^0.1.4",
"is-glob": "^4.0.0", "is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1", "json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash.merge": "^4.6.2", "lodash.merge": "^4.6.2",
"minimatch": "^3.1.2", "minimatch": "^3.1.2",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
"optionator": "^0.9.3" "optionator": "^0.9.3",
"strip-ansi": "^6.0.1",
"text-table": "^0.2.0"
}, },
"bin": { "bin": {
"eslint": "bin/eslint.js" "eslint": "bin/eslint.js"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}, },
"funding": { "funding": {
"url": "https://eslint.org/donate" "url": "https://opencollective.com/eslint"
},
"peerDependencies": {
"jiti": "*"
},
"peerDependenciesMeta": {
"jiti": {
"optional": true
}
} }
}, },
"node_modules/eslint-config-next": { "node_modules/eslint-config-next": {
@@ -5883,9 +5802,9 @@
} }
}, },
"node_modules/eslint-scope": { "node_modules/eslint-scope": {
"version": "8.4.0", "version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
@@ -5893,7 +5812,7 @@
"estraverse": "^5.2.0" "estraverse": "^5.2.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
@@ -5912,6 +5831,16 @@
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
} }
}, },
"node_modules/eslint/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/eslint/node_modules/brace-expansion": { "node_modules/eslint/node_modules/brace-expansion": {
"version": "1.1.12", "version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@@ -5923,17 +5852,17 @@
"concat-map": "0.0.1" "concat-map": "0.0.1"
} }
}, },
"node_modules/eslint/node_modules/eslint-visitor-keys": { "node_modules/eslint/node_modules/doctrine": {
"version": "4.2.1", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "dependencies": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "esutils": "^2.0.2"
}, },
"funding": { "engines": {
"url": "https://opencollective.com/eslint" "node": ">=6.0.0"
} }
}, },
"node_modules/eslint/node_modules/minimatch": { "node_modules/eslint/node_modules/minimatch": {
@@ -5949,32 +5878,32 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/eslint/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/espree": { "node_modules/espree": {
"version": "10.4.0", "version": "9.6.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"acorn": "^8.15.0", "acorn": "^8.9.0",
"acorn-jsx": "^5.3.2", "acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.2.1" "eslint-visitor-keys": "^3.4.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/espree/node_modules/eslint-visitor-keys": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
@@ -6147,16 +6076,16 @@
} }
}, },
"node_modules/file-entry-cache": { "node_modules/file-entry-cache": {
"version": "8.0.0", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"flat-cache": "^4.0.0" "flat-cache": "^3.0.4"
}, },
"engines": { "engines": {
"node": ">=16.0.0" "node": "^10.12.0 || >=12.0.0"
} }
}, },
"node_modules/file-type": { "node_modules/file-type": {
@@ -6198,17 +6127,18 @@
} }
}, },
"node_modules/flat-cache": { "node_modules/flat-cache": {
"version": "4.0.1", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"flatted": "^3.2.9", "flatted": "^3.2.9",
"keyv": "^4.5.4" "keyv": "^4.5.3",
"rimraf": "^3.0.2"
}, },
"engines": { "engines": {
"node": ">=16" "node": "^10.12.0 || >=12.0.0"
} }
}, },
"node_modules/flatted": { "node_modules/flatted": {
@@ -6278,16 +6208,16 @@
} }
}, },
"node_modules/fraction.js": { "node_modules/fraction.js": {
"version": "5.3.4", "version": "4.3.7",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
"integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": "*" "node": "*"
}, },
"funding": { "funding": {
"type": "github", "type": "patreon",
"url": "https://github.com/sponsors/rawify" "url": "https://github.com/sponsors/rawify"
} }
}, },
@@ -6297,6 +6227,13 @@
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true,
"license": "ISC"
},
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -6511,13 +6448,16 @@
} }
}, },
"node_modules/globals": { "node_modules/globals": {
"version": "14.0.0", "version": "13.24.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": {
"type-fest": "^0.20.2"
},
"engines": { "engines": {
"node": ">=18" "node": ">=8"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
@@ -6845,6 +6785,18 @@
"node": ">=0.8.19" "node": ">=0.8.19"
} }
}, },
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
"license": "ISC",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": { "node_modules/inherits": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -7142,6 +7094,16 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-path-inside": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/is-potential-custom-element-name": { "node_modules/is-potential-custom-element-name": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@@ -7643,12 +7605,12 @@
} }
}, },
"node_modules/lucide-react": { "node_modules/lucide-react": {
"version": "0.279.0", "version": "0.554.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.279.0.tgz", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.554.0.tgz",
"integrity": "sha512-LJ8g66+Bxc3t3x9vKTeK3wn3xucrOQGfJ9ou9GsBwCt2offsrT2BB90XrTrIzE1noYYDe2O8jZaRHi6sAHXNxw==", "integrity": "sha512-St+z29uthEJVx0Is7ellNkgTEhaeSoA42I7JjOCBCrc5X6LYMGSv0P/2uS5HDLTExP5tpiqRD2PyUEOS6s9UXA==",
"license": "ISC", "license": "ISC",
"peerDependencies": { "peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0" "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
} }
}, },
"node_modules/math-intrinsics": { "node_modules/math-intrinsics": {
@@ -8390,6 +8352,16 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/path-key": { "node_modules/path-key": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -8495,9 +8467,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.6", "version": "8.4.47",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -8515,8 +8487,8 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.3.11", "nanoid": "^3.3.7",
"picocolors": "^1.1.1", "picocolors": "^1.1.0",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
}, },
"engines": { "engines": {
@@ -9228,6 +9200,69 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/rimraf/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/rimraf/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/rimraf/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/rrweb-cssom": { "node_modules/rrweb-cssom": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz",
@@ -10248,6 +10283,13 @@
"b4a": "^1.6.4" "b4a": "^1.6.4"
} }
}, },
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true,
"license": "MIT"
},
"node_modules/thenify": { "node_modules/thenify": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
@@ -10423,6 +10465,19 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typed-array-buffer": { "node_modules/typed-array-buffer": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
@@ -10794,9 +10849,9 @@
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "7.16.0", "version": "6.11.1",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },

View File

@@ -45,14 +45,14 @@
"@types/jsdom": "^21.1.3", "@types/jsdom": "^21.1.3",
"@types/node": "22.0.0", "@types/node": "22.0.0",
"@types/node-telegram-bot-api": "^0.61.8", "@types/node-telegram-bot-api": "^0.61.8",
"@types/react": "19.0.0", "@types/react": "19.2.0",
"@types/react-dom": "19.0.0", "@types/react-dom": "19.2.0",
"@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/eslint-plugin": "^6.7.3",
"autoprefixer": "10.4.20", "autoprefixer": "10.4.20",
"eslint": "9.0.0", "eslint": "8.57.0",
"eslint-config-next": "16.0.3", "eslint-config-next": "16.0.3",
"postcss": "8.4.47", "postcss": "8.4.47",
"tailwindcss": "^3.4.18", "tailwindcss": "^3.4.18",
"typescript": "5.6.0" "typescript": "5.9.3"
} }
} }

108
scripts/install.sh Executable file
View File

@@ -0,0 +1,108 @@
#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
INSTALL_DIR="/opt/kspguti-schedule"
SERVICE_USER="www-data"
SERVICE_GROUP="www-data"
SERVICE_NAME="kspguti-schedule"
NODE_VERSION="20"
echo -e "${GREEN}=== KSPGUTI Schedule Installation Script ===${NC}\n"
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Please run as root (use sudo)${NC}"
exit 1
fi
# Check Node.js version
echo -e "${YELLOW}Checking Node.js...${NC}"
if ! command -v node &> /dev/null; then
echo -e "${RED}Node.js is not installed. Please install Node.js ${NODE_VERSION}+ first.${NC}"
exit 1
fi
NODE_VER=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
if [ "$NODE_VER" -lt "$NODE_VERSION" ]; then
echo -e "${RED}Node.js version ${NODE_VERSION}+ is required. Current version: $(node -v)${NC}"
exit 1
fi
echo -e "${GREEN}Node.js version: $(node -v)${NC}"
# Check npm
if ! command -v npm &> /dev/null; then
echo -e "${RED}npm is not installed.${NC}"
exit 1
fi
echo -e "${GREEN}npm version: $(npm -v)${NC}\n"
# Get the directory where the script is located
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_DIR="$( cd "$SCRIPT_DIR/.." && pwd )"
echo -e "${YELLOW}Project directory: $PROJECT_DIR${NC}"
echo -e "${YELLOW}Installation directory: $INSTALL_DIR${NC}\n"
# Create installation directory
echo -e "${YELLOW}Creating installation directory...${NC}"
mkdir -p "$INSTALL_DIR"
# Copy project files
echo -e "${YELLOW}Copying project files...${NC}"
rsync -av --exclude='node_modules' \
--exclude='.next' \
--exclude='.git' \
--exclude='*.log' \
--exclude='.env*' \
--exclude='*.md' \
"$PROJECT_DIR/" "$INSTALL_DIR/"
# Create .env.production if it doesn't exist
if [ ! -f "$INSTALL_DIR/.env.production" ]; then
echo -e "${YELLOW}Creating .env.production from example...${NC}"
if [ -f "$INSTALL_DIR/.env.production.example" ]; then
cp "$INSTALL_DIR/.env.production.example" "$INSTALL_DIR/.env.production"
echo -e "${YELLOW}Please edit $INSTALL_DIR/.env.production with your configuration${NC}"
fi
fi
# Install dependencies
echo -e "${YELLOW}Installing dependencies...${NC}"
cd "$INSTALL_DIR"
npm ci --legacy-peer-deps --production=false
# Build the application
echo -e "${YELLOW}Building the application...${NC}"
npm run build
# Set ownership
echo -e "${YELLOW}Setting ownership...${NC}"
chown -R "$SERVICE_USER:$SERVICE_GROUP" "$INSTALL_DIR"
# Install systemd service
echo -e "${YELLOW}Installing systemd service...${NC}"
cp "$INSTALL_DIR/systemd/$SERVICE_NAME.service" "/etc/systemd/system/"
systemctl daemon-reload
# Enable service
echo -e "${YELLOW}Enabling service...${NC}"
systemctl enable "$SERVICE_NAME.service"
echo -e "\n${GREEN}=== Installation completed successfully! ===${NC}\n"
echo -e "${YELLOW}Next steps:${NC}"
echo -e "1. Edit environment variables: ${GREEN}$INSTALL_DIR/.env.production${NC}"
echo -e "2. Update systemd service if needed: ${GREEN}/etc/systemd/system/$SERVICE_NAME.service${NC}"
echo -e "3. Start the service: ${GREEN}systemctl start $SERVICE_NAME${NC}"
echo -e "4. Check status: ${GREEN}systemctl status $SERVICE_NAME${NC}"
echo -e "5. View logs: ${GREEN}journalctl -u $SERVICE_NAME -f${NC}\n"

124
scripts/manage.sh Executable file
View File

@@ -0,0 +1,124 @@
#!/bin/bash
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
SERVICE_NAME="kspguti-schedule"
INSTALL_DIR="/opt/kspguti-schedule"
show_usage() {
echo -e "${BLUE}Usage: $0 {start|stop|restart|status|logs|update|enable|disable}${NC}"
echo ""
echo "Commands:"
echo " start - Start the service"
echo " stop - Stop the service"
echo " restart - Restart the service"
echo " status - Show service status"
echo " logs - Show service logs (use -f for follow)"
echo " update - Update the application (pull, install, build, restart)"
echo " enable - Enable service to start on boot"
echo " disable - Disable service from starting on boot"
exit 1
}
check_root() {
if [ "$1" != "status" ] && [ "$1" != "logs" ] && [ "$EUID" -ne 0 ]; then
echo -e "${RED}This command requires root privileges (use sudo)${NC}"
exit 1
fi
}
case "$1" in
start)
check_root "$1"
echo -e "${YELLOW}Starting $SERVICE_NAME...${NC}"
systemctl start "$SERVICE_NAME"
systemctl status "$SERVICE_NAME" --no-pager
;;
stop)
check_root "$1"
echo -e "${YELLOW}Stopping $SERVICE_NAME...${NC}"
systemctl stop "$SERVICE_NAME"
echo -e "${GREEN}Service stopped${NC}"
;;
restart)
check_root "$1"
echo -e "${YELLOW}Restarting $SERVICE_NAME...${NC}"
systemctl restart "$SERVICE_NAME"
systemctl status "$SERVICE_NAME" --no-pager
;;
status)
systemctl status "$SERVICE_NAME" --no-pager
;;
logs)
if [ "$2" == "-f" ]; then
journalctl -u "$SERVICE_NAME" -f
else
journalctl -u "$SERVICE_NAME" -n 50 --no-pager
fi
;;
update)
check_root "$1"
echo -e "${YELLOW}Updating $SERVICE_NAME...${NC}"
if [ ! -d "$INSTALL_DIR" ]; then
echo -e "${RED}Installation directory not found: $INSTALL_DIR${NC}"
exit 1
fi
cd "$INSTALL_DIR"
# Stop service
echo -e "${YELLOW}Stopping service...${NC}"
systemctl stop "$SERVICE_NAME"
# Pull latest changes (if using git)
if [ -d ".git" ]; then
echo -e "${YELLOW}Pulling latest changes...${NC}"
git pull
else
echo -e "${YELLOW}Not a git repository, skipping pull${NC}"
fi
# Install dependencies
echo -e "${YELLOW}Installing dependencies...${NC}"
npm ci --legacy-peer-deps --production=false
# Build
echo -e "${YELLOW}Building application...${NC}"
npm run build
# Set ownership
chown -R www-data:www-data "$INSTALL_DIR"
# Reload systemd and restart
systemctl daemon-reload
echo -e "${YELLOW}Starting service...${NC}"
systemctl start "$SERVICE_NAME"
echo -e "${GREEN}Update completed!${NC}"
systemctl status "$SERVICE_NAME" --no-pager
;;
enable)
check_root "$1"
echo -e "${YELLOW}Enabling $SERVICE_NAME to start on boot...${NC}"
systemctl enable "$SERVICE_NAME"
echo -e "${GREEN}Service enabled${NC}"
;;
disable)
check_root "$1"
echo -e "${YELLOW}Disabling $SERVICE_NAME from starting on boot...${NC}"
systemctl disable "$SERVICE_NAME"
echo -e "${GREEN}Service disabled${NC}"
;;
*)
show_usage
;;
esac
exit 0

View File

@@ -43,15 +43,13 @@ function Popup({ open, onClose }: {
Если вы хотите добавить свою группу на сайт, скиньтесь всей группой и задонатьте мне 500 Если вы хотите добавить свою группу на сайт, скиньтесь всей группой и задонатьте мне 500
</DialogDescription> </DialogDescription>
<DialogDescription> <DialogDescription>
На сайте не только появится ваше расписание, но оно будет обновляться каждую неделю. А если во время парсинга произойдет ошибка я сразу получу об этом уведомление в телеграм, потому что у меня настроен бот. Для меня это будет очень хорошая поддержка🥺🥺🥺
</DialogDescription> </DialogDescription>
<DialogDescription> <DialogDescription>
{/* eslint-disable-next-line @next/next/no-img-element */}
Для меня добавить вашу группу это даже не одна строка кода, а одно нажатие клавиши, но я хочу чтобы этот сайт был доступен только самым лучшим и избранным, достойных престижа <img src={'/1925.png'} width={14} height={14} alt='' className='inline align-text-bottom' />
</DialogDescription> </DialogDescription>
<DialogFooter className='!justify-start !flex-row mt-3 gap-3'> <DialogFooter className='!justify-start !flex-row mt-3 gap-3'>
<Link href='https://t.me/ilyakm'> <Link href='https://t.me/ilyakm'>
<Button tabIndex={-1} className='gap-3'><BsTelegram /> Мой телеграм</Button> <Button tabIndex={-1} className='gap-3'><BsTelegram /> Мой Telegram</Button>
</Link> </Link>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>

View File

@@ -1,11 +1,15 @@
import { GetServerSidePropsResult } from 'next' import { GetServerSidePropsResult } from 'next'
import { groups } from '@/shared/data/groups'
export default function HomePage() { } export default function HomePage() { }
export async function getServerSideProps(): Promise<GetServerSidePropsResult<Record<string, never>>> { export async function getServerSideProps(): Promise<GetServerSidePropsResult<Record<string, never>>> {
// Получаем первую группу из списка
const firstGroupId = Object.keys(groups)[0]
return { return {
redirect: { redirect: {
destination: '/ps7', destination: `/${firstGroupId}`,
permanent: true permanent: true
} }
} }

View File

@@ -51,7 +51,7 @@ export function NavBar({ cacheAvailableFor }: {
<AddGroupButton /> <AddGroupButton />
</ul> </ul>
<div className='flex gap-1 min-[500px]:gap-2'> <div className='flex gap-1 min-[500px]:gap-2'>
<Link href='https://github.com/VityaSchel/kspguti-schedule' target='_blank' rel='nofollower noreferrer'> <Link href='https://github.com/kilyabin/kspguti-schedule' target='_blank' rel='nofollower noreferrer'>
<Button variant='outline' size='icon' tabIndex={-1}> <Button variant='outline' size='icon' tabIndex={-1}>
<FaGithub /> <FaGithub />
</Button> </Button>

View File

@@ -0,0 +1,39 @@
[Unit]
Description=KSPGUTI Schedule - Next.js Application
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/kspguti-schedule
Environment=NODE_ENV=production
Environment=NEXT_TELEMETRY_DISABLED=1
Environment=PORT=3000
Environment=HOSTNAME=0.0.0.0
# Uncomment and set your environment variables:
# Environment=PROXY_URL=https://lk.ks.psuti.ru
# Environment=PARSING_FAILURE_NOTIFICATIONS_TELEGRAM_BOTAPI_TOKEN=your_token_here
# Environment=PARSING_FAILURE_NOTIFICATIONS_TELEGRAM_CHAT_ID=your_chat_id_here
# Load environment variables from file (optional)
# EnvironmentFile=/opt/kspguti-schedule/.env.production
# Use standalone server from Next.js build
ExecStart=/usr/bin/node /opt/kspguti-schedule/.next/standalone/server.js
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=kspguti-schedule
# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/kspguti-schedule
[Install]
WantedBy=multi-user.target