Каркас Laravel-приложения: Breeze+Sanctum, схема БД, модели, Docker

Laravel 13 в laravel-app/, Breeze (Blade) для сессионного логина + Sanctum
для API-токенов (задел под Vue/Flutter на этапе 3). Схема Postgres
перенесена из сырого SQL в Laravel-миграции (владелец схемы теперь Laravel,
Go-сервисы как читали/писали эти таблицы, так и продолжают). Роли/категории/
операторы условий — закрытые PHP-enum'ы (UserRole, DeviceCategory,
ConditionOperator); action_type правил намеренно остаётся строкой — открытый,
управляемый данными список, а не код. Сидер device_types с growbox-типами
как пример данных, не бизнес-логика.

Docker: один контейнер (php-fpm + nginx через supervisord, multi-stage
сборка vendor/assets). Образ пока не --no-dev — сидер использует fake()
для демо-аккаунта.

Проверено: миграции применяются на реальном Postgres (в докере и локально),
дефолтные тесты Breeze проходят (25/25), Breeze-логин подтверждён вживую
через браузер, Sanctum корректно отдаёт 401 на защищённый /api/user без
токена.
This commit is contained in:
2026-07-28 08:15:26 +05:00
parent abc83faf05
commit bb492785a4
132 changed files with 15894 additions and 72 deletions
-59
View File
@@ -1,59 +0,0 @@
-- Core platform schema: users, zones, device types, devices, automation rules.
-- Deliberately generic — no growbox-specific tables or columns. A "growbox"
-- is just a row in zones with device_types like light/pump/fan attached to it.
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'owner' CHECK (role IN ('owner', 'viewer')),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE zones (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE device_types (
id BIGSERIAL PRIMARY KEY,
code TEXT NOT NULL UNIQUE, -- light, pump, fan, sensor_temp_humidity, vacuum, ...
category TEXT NOT NULL CHECK (category IN ('actuator', 'sensor')),
capabilities JSONB NOT NULL DEFAULT '[]' -- e.g. ["turn_on","turn_off","set_level"]
);
CREATE TABLE devices (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
zone_id BIGINT NOT NULL REFERENCES zones(id) ON DELETE CASCADE,
device_type_id BIGINT NOT NULL REFERENCES device_types(id),
name TEXT NOT NULL,
external_id TEXT NOT NULL UNIQUE, -- physical device id (ESP32 chip id, etc.)
protocol TEXT NOT NULL DEFAULT 'mqtt',
status TEXT NOT NULL DEFAULT 'offline' CHECK (status IN ('online', 'offline')),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX devices_zone_id_idx ON devices(zone_id);
CREATE INDEX devices_external_id_idx ON devices(external_id);
CREATE TABLE automation_rules (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
zone_id BIGINT NOT NULL REFERENCES zones(id) ON DELETE CASCADE,
target_device_id BIGINT NOT NULL REFERENCES devices(id) ON DELETE CASCADE,
condition_source_device_id BIGINT NOT NULL REFERENCES devices(id) ON DELETE CASCADE,
condition_sensor_type TEXT NOT NULL,
condition_operator TEXT NOT NULL CHECK (condition_operator IN ('>', '<', '>=', '<=', '=', '!=')),
condition_value DOUBLE PRECISION NOT NULL,
action_type TEXT NOT NULL, -- e.g. turn_on, turn_off, set_level
action_params JSONB NOT NULL DEFAULT '{}',
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX automation_rules_zone_id_idx ON automation_rules(zone_id);
CREATE INDEX automation_rules_active_idx ON automation_rules(is_active) WHERE is_active;