this repo has no description

move back crons and private/public fields to the app config

Changed files
+89 -78
app
cmd
example
.smallweb
ls
schemas
+9 -4
app/app.go
··· 20 ) 21 22 type AppConfig struct { 23 - Entrypoint string `json:"entrypoint,omitempty"` 24 - Root string `json:"root,omitempty"` 25 } 26 27 type CronJob struct { 28 - Schedule string `json:"schedule"` 29 - Args []string `json:"args"` 30 } 31 32 type App struct {
··· 20 ) 21 22 type AppConfig struct { 23 + Entrypoint string `json:"entrypoint,omitempty"` 24 + Root string `json:"root,omitempty"` 25 + Crons []CronJob `json:"cron"` 26 + Private bool `json:"private"` 27 + PrivateRoutes []string `json:"privateRoutes"` 28 + PublicRoutes []string `json:"publicRoutes"` 29 } 30 31 type CronJob struct { 32 + Description string `json:"description"` 33 + Schedule string `json:"schedule"` 34 + Args []string `json:"args"` 35 } 36 37 type App struct {
+14 -4
cmd/crons.go
··· 47 Short: "List cron jobs", 48 RunE: func(cmd *cobra.Command, args []string) error { 49 var crons []CronItem 50 - for _, appname := range k.MapKeys("apps") { 51 if len(args) > 0 && appname != args[0] { 52 continue 53 } else if len(args) == 0 && !flags.all { ··· 69 } 70 } 71 72 - for _, job := range k.Slices(fmt.Sprintf("apps.%s.crons", appname)) { 73 crons = append(crons, CronItem{ 74 App: appname, 75 - Args: job.Strings("args"), 76 - Schedule: job.String("schedule"), 77 }) 78 } 79 }
··· 47 Short: "List cron jobs", 48 RunE: func(cmd *cobra.Command, args []string) error { 49 var crons []CronItem 50 + apps, err := app.ListApps(k.String("dir")) 51 + if err != nil { 52 + return fmt.Errorf("failed to list apps: %w", err) 53 + } 54 + 55 + for _, appname := range apps { 56 if len(args) > 0 && appname != args[0] { 57 continue 58 } else if len(args) == 0 && !flags.all { ··· 74 } 75 } 76 77 + a, err := app.LoadApp(appname, k.String("dir"), k.String("domain"), k.Bool(fmt.Sprintf("apps.%s.admin", appname))) 78 + if err != nil { 79 + return fmt.Errorf("failed to load app %s: %w", appname, err) 80 + } 81 + 82 + for _, job := range a.Config.Crons { 83 crons = append(crons, CronItem{ 84 App: appname, 85 + Args: job.Args, 86 + Schedule: job.Schedule, 87 }) 88 } 89 }
+19 -19
cmd/up.go
··· 694 return 695 } 696 697 claims, err := me.extractClaims(r) 698 - if err != nil && isRoutePrivate(appname, r.URL.Path) { 699 if me.oidcIssuerUrl == nil { 700 http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) 701 return ··· 775 } 776 } 777 778 - if isRoutePrivate(appname, r.URL.Path) && !isAuthorized(appname, claims.Email, claims.Group) { 779 if claims.Email == "" { 780 http.Redirect(w, r, fmt.Sprintf("https://%s/_smallweb/signin", r.Host), http.StatusTemporaryRedirect) 781 return ··· 790 r.Header.Set("Remote-Group", claims.Group) 791 r.Header.Set("Remote-Name", claims.Name) 792 793 - wk, err := me.GetWorker(appname, k.String("dir"), k.String("domain")) 794 - if err != nil { 795 - if errors.Is(err, app.ErrAppNotFound) { 796 - w.WriteHeader(http.StatusNotFound) 797 - w.Write([]byte(fmt.Sprintf("No app found for host %s", r.Host))) 798 - return 799 - } 800 - 801 - w.WriteHeader(http.StatusInternalServerError) 802 - fmt.Fprintf(w, "failed to get worker: %v", err) 803 - return 804 - } 805 - 806 wk.ServeHTTP(w, r) 807 } 808 809 - func isRoutePrivate(appname string, route string) bool { 810 - isPrivate := k.Bool(fmt.Sprintf("apps.%s.private", appname)) 811 812 - for _, publicRoute := range k.Strings(fmt.Sprintf("apps.%s.publicRoutes", appname)) { 813 if ok, _ := doublestar.Match(publicRoute, route); ok { 814 isPrivate = false 815 } 816 } 817 818 - for _, privateRoute := range k.Strings(fmt.Sprintf("apps.%s.privateRoutes", appname)) { 819 if ok, _ := doublestar.Match(privateRoute, route); ok { 820 isPrivate = true 821 }
··· 694 return 695 } 696 697 + wk, err := me.GetWorker(appname, k.String("dir"), k.String("domain")) 698 + if err != nil { 699 + if errors.Is(err, app.ErrAppNotFound) { 700 + w.WriteHeader(http.StatusNotFound) 701 + w.Write([]byte(fmt.Sprintf("No app found for host %s", r.Host))) 702 + return 703 + } 704 + 705 + w.WriteHeader(http.StatusInternalServerError) 706 + fmt.Fprintf(w, "failed to get worker: %v", err) 707 + return 708 + } 709 + 710 claims, err := me.extractClaims(r) 711 + if err != nil && isRoutePrivate(wk.App, r.URL.Path) { 712 if me.oidcIssuerUrl == nil { 713 http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) 714 return ··· 788 } 789 } 790 791 + if isRoutePrivate(wk.App, r.URL.Path) && !isAuthorized(appname, claims.Email, claims.Group) { 792 if claims.Email == "" { 793 http.Redirect(w, r, fmt.Sprintf("https://%s/_smallweb/signin", r.Host), http.StatusTemporaryRedirect) 794 return ··· 803 r.Header.Set("Remote-Group", claims.Group) 804 r.Header.Set("Remote-Name", claims.Name) 805 806 wk.ServeHTTP(w, r) 807 } 808 809 + func isRoutePrivate(a app.App, route string) bool { 810 + isPrivate := a.Config.Private 811 812 + for _, publicRoute := range a.Config.PublicRoutes { 813 if ok, _ := doublestar.Match(publicRoute, route); ok { 814 isPrivate = false 815 } 816 } 817 818 + for _, privateRoute := range a.Config.PrivateRoutes { 819 if ok, _ := doublestar.Match(privateRoute, route); ok { 820 isPrivate = true 821 }
-1
example/.smallweb/config.json
··· 8 "apps": { 9 "ls": { 10 "admin": true, 11 - "private": true, 12 "additionalDomains": [ 13 "custom-domain.localhost" 14 ]
··· 8 "apps": { 9 "ls": { 10 "admin": true, 11 "additionalDomains": [ 12 "custom-domain.localhost" 13 ]
+1 -4
example/ls/smallweb.json
··· 1 { 2 - "private": true, 3 - "publicRoutes": [ 4 - "/public/*" 5 - ] 6 }
··· 1 { 2 + "private": true 3 }
-46
schemas/config.schema.json
··· 65 "description": "Give the app admin privileges", 66 "type": "boolean" 67 }, 68 - "private": { 69 - "description": "Protect all routes behind authentication", 70 - "type": "boolean" 71 - }, 72 - "privateRoutes": { 73 - "description": "Make specific routes private", 74 - "type": "array", 75 - "items": { 76 - "type": "string" 77 - } 78 - }, 79 - "publicRoutes": { 80 - "description": "Make specific routes public", 81 - "type": "array", 82 - "items": { 83 - "type": "string" 84 - } 85 - }, 86 - "crons": { 87 - "description": "Cron jobs", 88 - "type": "array", 89 - "items": { 90 - "type": "object", 91 - "required": [ 92 - "schedule", 93 - "args" 94 - ], 95 - "properties": { 96 - "schedule": { 97 - "description": "Cron schedule", 98 - "type": "string" 99 - }, 100 - "description": { 101 - "type": "string", 102 - "description": "An optional description for the task" 103 - }, 104 - "args": { 105 - "description": "Cron arguments", 106 - "type": "array", 107 - "items": { 108 - "type": "string" 109 - } 110 - } 111 - } 112 - } 113 - }, 114 "additionalDomains": { 115 "description": "Additional app domains", 116 "type": "array",
··· 65 "description": "Give the app admin privileges", 66 "type": "boolean" 67 }, 68 "additionalDomains": { 69 "description": "Additional app domains", 70 "type": "array",
+46
schemas/manifest.schema.json
··· 10 "description": "The root directory of the project", 11 "type": "string" 12 }, 13 "labels": { 14 "description": "Labels for the project", 15 "type": "object",
··· 10 "description": "The root directory of the project", 11 "type": "string" 12 }, 13 + "private": { 14 + "description": "Protect all routes behind authentication", 15 + "type": "boolean" 16 + }, 17 + "privateRoutes": { 18 + "description": "Make specific routes private", 19 + "type": "array", 20 + "items": { 21 + "type": "string" 22 + } 23 + }, 24 + "publicRoutes": { 25 + "description": "Make specific routes public", 26 + "type": "array", 27 + "items": { 28 + "type": "string" 29 + } 30 + }, 31 + "crons": { 32 + "description": "Cron jobs", 33 + "type": "array", 34 + "items": { 35 + "type": "object", 36 + "required": [ 37 + "schedule", 38 + "args" 39 + ], 40 + "properties": { 41 + "schedule": { 42 + "description": "Cron schedule", 43 + "type": "string" 44 + }, 45 + "description": { 46 + "type": "string", 47 + "description": "An optional description for the task" 48 + }, 49 + "args": { 50 + "description": "Cron arguments", 51 + "type": "array", 52 + "items": { 53 + "type": "string" 54 + } 55 + } 56 + } 57 + } 58 + }, 59 "labels": { 60 "description": "Labels for the project", 61 "type": "object",