Refactor MQTT course and bound backend message processing

Move lesson source into the teaching structure, align the course path with the learner's Go and DevOps experience, and add the interactive lesson guides. Add advanced DevOps lifecycle study as a separate section.

Bound MQTT message work and drain accepted jobs during shutdown; align the backend implementation with the revised course through Section 6.
This commit is contained in:
2026-10-22 10:19:56 +03:30
parent f9ab91ed60
commit fb30e8e884
70 changed files with 7492 additions and 312 deletions
+16 -2
View File
@@ -130,8 +130,8 @@ func (c Config) Validate() error {
if strings.TrimSpace(c.MQTT.Password) == "" {
return fmt.Errorf("mqtt.password is required in the TOML configuration")
}
if c.MQTT.DeviceID == "" || strings.ContainsAny(c.MQTT.DeviceID, "/+# \t\r\n") {
return fmt.Errorf("device ID must be a nonempty single topic segment")
if !validDeviceID(c.MQTT.DeviceID) {
return fmt.Errorf("device ID must be 1-128 ASCII letters, digits, dots, underscores, or hyphens")
}
d, err := time.ParseDuration(c.App.ConnectTimeout)
if err != nil || d <= 0 || d > time.Minute {
@@ -139,3 +139,17 @@ func (c Config) Validate() error {
}
return nil
}
// validDeviceID mirrors the topic contract's single-segment ID rules so
// configuration fails before the process tries to build MQTT topics.
func validDeviceID(value string) bool {
if value == "" || len(value) > 128 {
return false
}
for _, r := range value {
if !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || strings.ContainsRune("._-", r)) {
return false
}
}
return true
}
+17
View File
@@ -0,0 +1,17 @@
package config
import "testing"
func TestValidateRejectsDeviceIDThatCannotFormTopic(t *testing.T) {
cfg := Config{}
cfg.MQTT.URL = "mqtt://127.0.0.1:18883"
cfg.MQTT.ClientID = "lab-client"
cfg.MQTT.Username = "backend"
cfg.MQTT.Password = "lab-password"
cfg.MQTT.DeviceID = "device!001"
cfg.App.ConnectTimeout = "10s"
if err := cfg.Validate(); err == nil {
t.Fatal("Validate accepted a device ID rejected by the topic contract")
}
}