| @@ -0,0 +1,20 @@ | |||||
| { | |||||
| "permissions": { | |||||
| "allow": [ | |||||
| "Bash(docker compose *)", | |||||
| "Bash(grep -E '^\\\\.env$|^\\\\.env' .gitignore)", | |||||
| "Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" http://127.0.0.1:8090/)", | |||||
| "Bash(docker rm *)", | |||||
| "Bash(powershell.exe -NoProfile -Command \"netsh interface ipv4 show excludedportrange protocol=tcp\")", | |||||
| "Bash(curl -s -o /dev/null -w \"app :8080 -> %{http_code}\\\\n\" http://127.0.0.1:8080/)", | |||||
| "Bash(curl -s -o /dev/null -w \"phpliteadmin :8091 -> %{http_code}\\\\n\" http://127.0.0.1:8091/)", | |||||
| "Bash(curl -s http://127.0.0.1:8080/)", | |||||
| "Bash(curl -s -o /dev/null -w \"app :9092 -> %{http_code}\\\\n\" http://127.0.0.1:9092/)", | |||||
| "Bash(curl -s -o /dev/null -w \"phpliteadmin :9091 -> %{http_code}\\\\n\" http://127.0.0.1:9091/)", | |||||
| "Bash(curl -s http://127.0.0.1:9092/)", | |||||
| "Bash(curl -s http://127.0.0.1:9092/locations)", | |||||
| "Bash(curl -s http://127.0.0.1:9092/items)", | |||||
| "Bash(grep -E '\\\\.env$|\\\\.sqlite$|storage/')" | |||||
| ] | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| # Default ignored files | |||||
| /shelf/ | |||||
| /workspace.xml | |||||
| # Editor-based HTTP Client requests | |||||
| /httpRequests/ | |||||
| # Ignored default folder with query files | |||||
| /queries/ | |||||
| # Datasource local storage ignored files | |||||
| /dataSources/ | |||||
| /dataSources.local.xml | |||||
| @@ -0,0 +1,8 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project version="4"> | |||||
| <component name="InertiaPackage"> | |||||
| <option name="directoryPaths"> | |||||
| <list /> | |||||
| </option> | |||||
| </component> | |||||
| </project> | |||||
| @@ -0,0 +1,8 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project version="4"> | |||||
| <component name="ProjectModuleManager"> | |||||
| <modules> | |||||
| <module fileurl="file://$PROJECT_DIR$/.idea/warehouse.iml" filepath="$PROJECT_DIR$/.idea/warehouse.iml" /> | |||||
| </modules> | |||||
| </component> | |||||
| </project> | |||||
| @@ -0,0 +1,27 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project version="4"> | |||||
| <component name="MessDetectorOptionsConfiguration"> | |||||
| <option name="transferred" value="true" /> | |||||
| </component> | |||||
| <component name="PHPCSFixerOptionsConfiguration"> | |||||
| <option name="transferred" value="true" /> | |||||
| </component> | |||||
| <component name="PHPCodeSnifferOptionsConfiguration"> | |||||
| <option name="highlightLevel" value="WARNING" /> | |||||
| <option name="transferred" value="true" /> | |||||
| </component> | |||||
| <component name="PhpProjectSharedConfiguration" php_language_level="8.0"> | |||||
| <option name="suggestChangeDefaultLanguageLevel" value="false" /> | |||||
| </component> | |||||
| <component name="PhpStanOptionsConfiguration"> | |||||
| <option name="transferred" value="true" /> | |||||
| </component> | |||||
| <component name="PhpUnit"> | |||||
| <phpunit_settings> | |||||
| <PhpUnitSettings configuration_file_path="$PROJECT_DIR$/phpunit.xml" custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" use_configuration_file="true" /> | |||||
| </phpunit_settings> | |||||
| </component> | |||||
| <component name="PsalmOptionsConfiguration"> | |||||
| <option name="transferred" value="true" /> | |||||
| </component> | |||||
| </project> | |||||
| @@ -0,0 +1,10 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project version="4"> | |||||
| <component name="PHPUnit"> | |||||
| <option name="directories"> | |||||
| <list> | |||||
| <option value="$PROJECT_DIR$/tests" /> | |||||
| </list> | |||||
| </option> | |||||
| </component> | |||||
| </project> | |||||
| @@ -0,0 +1,6 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project version="4"> | |||||
| <component name="VcsDirectoryMappings"> | |||||
| <mapping directory="" vcs="Git" /> | |||||
| </component> | |||||
| </project> | |||||
| @@ -0,0 +1,12 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <module type="WEB_MODULE" version="4"> | |||||
| <component name="NewModuleRootManager"> | |||||
| <content url="file://$MODULE_DIR$"> | |||||
| <sourceFolder url="file://$MODULE_DIR$/app" isTestSource="false" packagePrefix="App\" /> | |||||
| <sourceFolder url="file://$MODULE_DIR$/framework" isTestSource="false" packagePrefix="Framework\" /> | |||||
| <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" /> | |||||
| </content> | |||||
| <orderEntry type="inheritedJdk" /> | |||||
| <orderEntry type="sourceFolder" forTests="false" /> | |||||
| </component> | |||||
| </module> | |||||
| @@ -0,0 +1,62 @@ | |||||
| # Raspberry Pi deployment | |||||
| This app can run directly on a Raspberry Pi with **nginx + php-fpm** instead of Docker. | |||||
| ## What the script does | |||||
| `scripts/deploy-raspi.sh` runs from your local machine and connects over SSH to the Pi. On the Pi it will: | |||||
| - install `nginx`, `git`, `composer`, and PHP 8.2 packages | |||||
| - clone or update the repository | |||||
| - create/update `.env` | |||||
| - install Composer dependencies | |||||
| - prepare the default SQLite database | |||||
| - run migrations | |||||
| - create an nginx vhost that serves `public/` | |||||
| - create a `systemd` service for `php command.php queue:work` | |||||
| ## Assumptions | |||||
| - the Raspberry Pi is running a Debian/Raspberry Pi OS style distro with `apt` | |||||
| - your SSH user can run `sudo` | |||||
| - the repository is reachable from the Pi over Git/SSH or HTTPS | |||||
| - you want the app at `/var/www/warehouse` | |||||
| - you are fine using the app's default **SQLite** setup | |||||
| ## Basic usage | |||||
| From this repo on your local machine: | |||||
| ```bash | |||||
| chmod +x scripts/deploy-raspi.sh | |||||
| scripts/deploy-raspi.sh pi@raspberrypi.local git@github.com:YOUR_USER/warehouse.git DietPi.ntp.kentcommunications.com | |||||
| ``` | |||||
| Arguments: | |||||
| - argument 1: SSH target, for example `pi@192.168.1.50` | |||||
| - argument 2: Git clone URL the Pi can access | |||||
| - argument 3: nginx `server_name` value, optional, defaults to `DietPi.ntp.kentcommunications.com` | |||||
| ## Useful environment overrides | |||||
| ```bash | |||||
| DEPLOY_REF=main \ | |||||
| DEPLOY_PATH=/var/www/warehouse \ | |||||
| PHP_VERSION=8.2 \ | |||||
| scripts/deploy-raspi.sh pi@raspberrypi.local git@github.com:YOUR_USER/warehouse.git DietPi.ntp.kentcommunications.com | |||||
| ``` | |||||
| ## After deploy | |||||
| - browse to `http://DietPi.ntp.kentcommunications.com` | |||||
| - check nginx with `sudo systemctl status nginx` | |||||
| - check php-fpm with `sudo systemctl status php8.2-fpm` | |||||
| - check the queue worker with `sudo systemctl status warehouse-queue.service` | |||||
| ## Notes | |||||
| - the app serves from `public/`, so the nginx root must stay pointed there | |||||
| - low-stock emails are always queued in this app, so the queue worker service matters in production | |||||
| - this script sets up **HTTP only**; add TLS separately with `certbot` or another reverse-proxy setup if you want HTTPS | |||||
| - if you prefer MySQL instead of SQLite, update `.env` and the script before rerunning it | |||||
| @@ -37,3 +37,7 @@ Note: host ports `9092`/`9091` map to the app's internal `80`/`8081` — the hos | |||||
| ## Framework guide | ## Framework guide | ||||
| This app is built on a small custom MVC framework in `framework/`. See [docs/framework.md](docs/framework.md) for how routing, controllers, views, models/migrations, validation, sessions, caching, queues, and the CLI commands all work. | This app is built on a small custom MVC framework in `framework/`. See [docs/framework.md](docs/framework.md) for how routing, controllers, views, models/migrations, validation, sessions, caching, queues, and the CLI commands all work. | ||||
| ## Raspberry Pi deployment | |||||
| If you want to run this app directly on a Raspberry Pi with `nginx` and `php-fpm`, see [docs/deploy-raspberry-pi.md](docs/deploy-raspberry-pi.md) and use `scripts/deploy-raspi.sh`. | |||||
| @@ -0,0 +1,125 @@ | |||||
| #!/usr/bin/env bash | |||||
| set -euo pipefail | |||||
| if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then | |||||
| echo "Usage: scripts/deploy-raspi.sh <ssh-host> <git-repo> [domain]" | |||||
| exit 1 | |||||
| fi | |||||
| ssh_host="$1" | |||||
| repo_url="$2" | |||||
| domain="${3:-DietPi.ntp.kentcommunications.com}" | |||||
| deploy_path="${DEPLOY_PATH:-/var/www/warehouse}" | |||||
| deploy_ref="${DEPLOY_REF:-main}" | |||||
| php_version="${PHP_VERSION:-8.2}" | |||||
| ssh "$ssh_host" bash -s -- "$repo_url" "$domain" "$deploy_path" "$deploy_ref" "$php_version" <<'REMOTE' | |||||
| set -euo pipefail | |||||
| repo_url="$1" | |||||
| domain="$2" | |||||
| deploy_path="$3" | |||||
| deploy_ref="$4" | |||||
| php_version="$5" | |||||
| owner="$(id -un)" | |||||
| group="$(id -gn)" | |||||
| php_fpm_service="php${php_version}-fpm" | |||||
| php_fpm_socket="/run/php/${php_fpm_service}.sock" | |||||
| set_env() { | |||||
| file="$1" | |||||
| key="$2" | |||||
| value="$3" | |||||
| if grep -q "^${key}=" "$file"; then | |||||
| sed -i "s|^${key}=.*|${key}=${value}|" "$file" | |||||
| else | |||||
| printf '%s=%s\n' "$key" "$value" >> "$file" | |||||
| fi | |||||
| } | |||||
| sudo apt-get update | |||||
| sudo apt-get install -y git nginx composer \ | |||||
| "php${php_version}" "php${php_version}-cli" "php${php_version}-fpm" \ | |||||
| "php${php_version}-mbstring" "php${php_version}-xml" \ | |||||
| "php${php_version}-curl" "php${php_version}-zip" \ | |||||
| "php${php_version}-sqlite3" "php${php_version}-mysql" | |||||
| sudo mkdir -p "$(dirname "$deploy_path")" | |||||
| if [ ! -d "$deploy_path/.git" ]; then | |||||
| sudo git clone --branch "$deploy_ref" --single-branch "$repo_url" "$deploy_path" | |||||
| sudo chown -R "$owner:$group" "$deploy_path" | |||||
| else | |||||
| git -C "$deploy_path" fetch --prune origin | |||||
| git -C "$deploy_path" checkout "$deploy_ref" | |||||
| git -C "$deploy_path" reset --hard "origin/$deploy_ref" | |||||
| fi | |||||
| cd "$deploy_path" | |||||
| if [ ! -f .env ] && [ -f .env.example ]; then cp .env.example .env; fi | |||||
| set_env .env APP_ENV prod | |||||
| set_env .env DB_CONNECTION sqlite | |||||
| set_env .env DB_SQLITE_PATH "$deploy_path/database/database.sqlite" | |||||
| composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-reqs | |||||
| mkdir -p storage/framework/views database | |||||
| touch database/database.sqlite | |||||
| sudo chgrp -R www-data storage database | |||||
| sudo chmod -R g+rwX storage database | |||||
| sudo find storage database -type d -exec chmod 2775 {} \; | |||||
| sudo chmod 664 database/database.sqlite | |||||
| php command.php migrate | |||||
| sudo tee /etc/systemd/system/warehouse-queue.service >/dev/null <<EOF | |||||
| [Unit] | |||||
| Description=Warehouse queue worker | |||||
| After=network.target | |||||
| [Service] | |||||
| Type=simple | |||||
| User=${owner} | |||||
| Group=www-data | |||||
| WorkingDirectory=${deploy_path} | |||||
| ExecStart=/usr/bin/php ${deploy_path}/command.php queue:work | |||||
| Restart=always | |||||
| RestartSec=3 | |||||
| [Install] | |||||
| WantedBy=multi-user.target | |||||
| EOF | |||||
| sudo tee /etc/nginx/sites-available/warehouse >/dev/null <<EOF | |||||
| server { | |||||
| listen 80; | |||||
| listen [::]:80; | |||||
| server_name ${domain}; | |||||
| root ${deploy_path}/public; | |||||
| index index.php; | |||||
| location / { | |||||
| try_files \$uri \$uri/ /index.php?\$query_string; | |||||
| } | |||||
| location ~ \.php$ { | |||||
| include snippets/fastcgi-php.conf; | |||||
| fastcgi_pass unix:${php_fpm_socket}; | |||||
| } | |||||
| location ~ /\. { | |||||
| deny all; | |||||
| } | |||||
| } | |||||
| EOF | |||||
| sudo ln -sfn /etc/nginx/sites-available/warehouse /etc/nginx/sites-enabled/warehouse | |||||
| sudo rm -f /etc/nginx/sites-enabled/default | |||||
| sudo systemctl daemon-reload | |||||
| sudo systemctl enable "$php_fpm_service" nginx warehouse-queue.service | |||||
| sudo systemctl restart "$php_fpm_service" | |||||
| sudo nginx -t | |||||
| sudo systemctl restart nginx | |||||
| sudo systemctl restart warehouse-queue.service | |||||
| echo "Deployment complete: http://${domain}" | |||||
| REMOTE | |||||
Powered by TurnKey Linux.