package store import ( "context" "fmt" "golang.org/x/crypto/bcrypt" ) // bridgeNamespace — служебный namespace строки бриджа в iot_devices. // EMQX (postgres auth/ACL) узнаёт бридж по условию // namespace = '__bridge' AND username = device_id. const bridgeNamespace = "__bridge" // EnsureBridgeDevice создаёт (или обновляет) служебную строку бриджа в // iot_devices. EMQX авторизует MQTT-подключение бриджа по этой строке, // поэтому device_id = MQTT username (iot-bridge), пароль = MQTT_PASSWORD. // // Нужно для PG-auth EMQX (хвост 3): EMQX зависит только от PostgreSQL, // монолит при старте гарантирует наличие строки бриджа. func (s *DeviceStore) EnsureBridgeDevice(ctx context.Context, username, password string) error { // Пароль бриджа храним bcrypt-хэшем (EMQX проверяет bcrypt), как и пароли // устройств. hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return fmt.Errorf("store: hash bridge password: %w", err) } _, err = s.db.ExecContext(ctx, ` INSERT INTO iot_devices (namespace, name, device_id, enabled, mqtt_password, metadata, phase) VALUES ($1, $2, $3, true, $4, '{}'::jsonb, 'Active') ON CONFLICT (namespace, name) DO UPDATE SET device_id = EXCLUDED.device_id, enabled = true, mqtt_password = EXCLUDED.mqtt_password`, bridgeNamespace, "iot-bridge", username, string(hash)) if err != nil { return fmt.Errorf("store: ensure bridge device: %w", err) } return nil }