Skip to content

Commit c091392

Browse files
sysoclaude
andcommitted
Merge v2-redesign: admin UI overhaul, API tokens, branding, Swagger docs
Merges the v2-redesign branch with major improvements: - Complete admin UI rewrite with Preact SPA - Bearer token auth for API access - Full branding system (logo, colors, markdown landing page) - Swagger/OpenAPI docs at /api/docs - WebRTC Go Live, AutoDJ studio, transcoding, relay management - Multi-tenant, HLS, RTMP/SRT ingest support Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2 parents 9e73dc6 + 56365f0 commit c091392

167 files changed

Lines changed: 26620 additions & 502 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ tmp_gomodcache/
1717
bin/
1818
.gemini/
1919
.antigravity/
20+
.worktrees/

Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.PHONY: build generate dev clean
2+
3+
# Full build: rebuild frontend + compile Go binary
4+
build: generate
5+
go build -o tinyice .
6+
7+
# Rebuild frontend assets via go generate
8+
generate:
9+
go generate ./server/...
10+
11+
# Quick build: Go only (skip frontend rebuild)
12+
quick:
13+
go build -o tinyice .
14+
15+
# Dev: run frontend dev server
16+
dev:
17+
cd server/frontend && npm run dev
18+
19+
clean:
20+
rm -f tinyice
21+
rm -rf server/frontend/dist

README.md

Lines changed: 191 additions & 244 deletions
Large diffs are not rendered by default.
36.1 KB
Loading
68.3 KB
Loading

assets/screenshots/developers.png

111 KB
Loading

assets/screenshots/landing.png

131 KB
Loading

config/config.go

Lines changed: 130 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,70 @@ import (
1010
const (
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+
1558
type 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

2279
type 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+
67138
type 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

124218
func (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

241342
func (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

280400
func (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

Comments
 (0)