@@ -10,13 +10,70 @@ import (
1010const (
1111 RoleSuperAdmin = "superadmin"
1212 RoleAdmin = "admin"
13+ RoleDJ = "dj"
1314)
1415
16+ type PasskeyCredential struct {
17+ ID string `json:"id"`
18+ RawCredential string `json:"raw_credential"`
19+ Name string `json:"name"`
20+ CreatedAt string `json:"created_at"`
21+ LastUsed string `json:"last_used"`
22+ }
23+
24+ type OIDCProvider struct {
25+ ID string `json:"id"`
26+ Name string `json:"name"`
27+ ClientID string `json:"client_id"`
28+ ClientSecret string `json:"client_secret"`
29+ DiscoveryURL string `json:"discovery_url"`
30+ Icon string `json:"icon"`
31+ Enabled bool `json:"enabled"`
32+ }
33+
34+ type PendingUser struct {
35+ ID string `json:"id"`
36+ Email string `json:"email"`
37+ Name string `json:"name"`
38+ Provider string `json:"provider"`
39+ RequestedAt string `json:"requested_at"`
40+ DeniedAt string `json:"denied_at,omitempty"`
41+ }
42+
43+ type SMTPConfig struct {
44+ Enabled bool `json:"enabled"`
45+ Host string `json:"host"`
46+ Port int `json:"port"`
47+ From string `json:"from"`
48+ Username string `json:"username"`
49+ Password string `json:"password"`
50+ }
51+
52+ type WebAuthnConfig struct {
53+ RPID string `json:"rp_id"`
54+ RPName string `json:"rp_name"`
55+ RPOrigins []string `json:"rp_origins"`
56+ }
57+
1558type User struct {
16- Username string `json:"username"`
17- Password string `json:"password"` // Hashed
18- Role string `json:"role"`
19- Mounts map [string ]string `json:"mounts"` // Backward compatibility
59+ Username string `json:"username"`
60+ Password string `json:"password"`
61+ Role string `json:"role"`
62+ Mounts map [string ]string `json:"mounts"`
63+ Passkeys []* PasskeyCredential `json:"passkeys,omitempty"`
64+ LinkedEmails []string `json:"linked_emails,omitempty"`
65+ }
66+
67+ type APIToken struct {
68+ ID string `json:"id"`
69+ Name string `json:"name"`
70+ TokenHash string `json:"token_hash"`
71+ Username string `json:"username"`
72+ Role string `json:"role"`
73+ CreatedAt string `json:"created_at"`
74+ LastUsedAt string `json:"last_used_at"`
75+ LastUsedIP string `json:"last_used_ip"`
76+ ExpiresAt string `json:"expires_at"`
2077}
2178
2279type RelayConfig struct {
@@ -64,6 +121,20 @@ type AutoDJConfig struct {
64121 Visible bool `json:"visible"`
65122}
66123
124+ type IngestConfig struct {
125+ RTMPEnabled bool `json:"rtmp_enabled"`
126+ RTMPPort string `json:"rtmp_port"`
127+ SRTEnabled bool `json:"srt_enabled"`
128+ SRTPort string `json:"srt_port"`
129+ SRTLatency int `json:"srt_latency"` // ms
130+ }
131+
132+ type MultiTenantConfig struct {
133+ Enabled bool `json:"enabled"`
134+ DefaultTenant string `json:"default_tenant"`
135+ TenantStore string `json:"tenant_store"` // "config", "database"
136+ }
137+
67138type Config struct {
68139 BindHost string `json:"bind_host"`
69140 Port string `json:"port"`
@@ -95,6 +166,11 @@ type Config struct {
95166 PageTitle string `json:"page_title"`
96167 PageSubtitle string `json:"page_subtitle"`
97168
169+ // Branding
170+ AccentColor string `json:"accent_color"`
171+ LogoPath string `json:"logo_path"`
172+ LandingMarkdown string `json:"landing_markdown"`
173+
98174 // HTTPS Configuration
99175 UseHTTPS bool `json:"use_https"`
100176 AutoHTTPS bool `json:"auto_https"` // ACME
@@ -117,8 +193,26 @@ type Config struct {
117193 // Internal Streamer (AutoDJ)
118194 AutoDJs []* AutoDJConfig `json:"autodjs"`
119195
196+ // Ingest (RTMP/SRT)
197+ Ingest * IngestConfig `json:"ingest"`
198+
120199 // Multi-tenant
121- Users map [string ]* User `json:"users"`
200+ MultiTenant * MultiTenantConfig `json:"multi_tenant"`
201+ Users map [string ]* User `json:"users"`
202+ APITokens []* APIToken `json:"api_tokens,omitempty"`
203+
204+ // Auth: Setup & Onboarding
205+ SetupComplete bool `json:"setup_complete"`
206+
207+ // Auth: OIDC
208+ OIDCProviders []* OIDCProvider `json:"oidc_providers,omitempty"`
209+ PendingUsers []* PendingUser `json:"pending_users,omitempty"`
210+
211+ // Auth: WebAuthn
212+ WebAuthn * WebAuthnConfig `json:"webauthn,omitempty"`
213+
214+ // Auth: Email Notifications
215+ SMTP * SMTPConfig `json:"smtp,omitempty"`
122216}
123217
124218func (c * Config ) IsWhitelisted (ip string ) bool {
@@ -224,6 +318,9 @@ func (config *Config) setBasicDefaults() {
224318 if config .PageSubtitle == "" {
225319 config .PageSubtitle = "Live Streaming Server powered by Go"
226320 }
321+ if config .AccentColor == "" {
322+ config .AccentColor = "#ff6600"
323+ }
227324 if config .HTTPSPort == "" {
228325 config .HTTPSPort = "443"
229326 }
@@ -236,6 +333,10 @@ func (config *Config) setBasicDefaults() {
236333 if config .ChecksumURL == "" {
237334 config .ChecksumURL = "https://github.com/DatanoiseTV/tinyice/releases/latest/download/checksums.txt"
238335 }
336+ // Existing configs are already set up
337+ if config .AdminPassword != "" && ! config .SetupComplete {
338+ config .SetupComplete = true
339+ }
239340}
240341
241342func (config * Config ) initMapsAndArrays () {
@@ -275,6 +376,25 @@ func (config *Config) initMapsAndArrays() {
275376 if config .AutoDJs == nil {
276377 config .AutoDJs = make ([]* AutoDJConfig , 0 )
277378 }
379+ if config .OIDCProviders == nil {
380+ config .OIDCProviders = make ([]* OIDCProvider , 0 )
381+ }
382+ if config .PendingUsers == nil {
383+ config .PendingUsers = make ([]* PendingUser , 0 )
384+ }
385+ if config .Ingest == nil {
386+ config .Ingest = & IngestConfig {
387+ RTMPPort : "1935" ,
388+ SRTPort : "9000" ,
389+ SRTLatency : 120 ,
390+ }
391+ }
392+ if config .MultiTenant == nil {
393+ config .MultiTenant = & MultiTenantConfig {
394+ DefaultTenant : "default" ,
395+ TenantStore : "config" ,
396+ }
397+ }
278398}
279399
280400func (config * Config ) handleMigrations () {
@@ -298,5 +418,9 @@ func (c *Config) SaveConfig() error {
298418 if err != nil {
299419 return err
300420 }
301- return os .WriteFile (c .ConfigPath , data , 0600 )
421+ tmpPath := c .ConfigPath + ".tmp"
422+ if err := os .WriteFile (tmpPath , data , 0600 ); err != nil {
423+ return err
424+ }
425+ return os .Rename (tmpPath , c .ConfigPath )
302426}
0 commit comments