11package main
22
33import (
4+ "context"
45 "flag"
56 "fmt"
7+ "net/http"
8+ "os"
9+ "sync"
10+ "time"
11+
612 "github.com/gorilla/mux"
13+ "github.com/x1unix/foundation/app"
714 "github.com/x1unix/go-playground/pkg/analyzer"
15+ "github.com/x1unix/go-playground/pkg/compiler"
16+ "github.com/x1unix/go-playground/pkg/compiler/storage"
817 "github.com/x1unix/go-playground/pkg/langserver"
918 "go.uber.org/zap"
10- "log"
11- "net/http"
12- "os"
1319)
1420
21+ type appArgs struct {
22+ packagesFile string
23+ addr string
24+ debug bool
25+ buildDir string
26+ cleanupInterval string
27+ }
28+
29+ func (a appArgs ) getCleanDuration () (time.Duration , error ) {
30+ return time .ParseDuration (a .cleanupInterval )
31+ }
32+
1533func main () {
16- var packagesFile string
17- var addr string
18- var debug bool
19- flag .StringVar (& packagesFile , "f " , "packages.json" , "Path to packages index JSON file " )
20- flag .StringVar (& addr , "addr " , ":8080 " , "TCP Listen address " )
21- flag .BoolVar (& debug , "debug" , false , "Enable debug mode" )
34+ args := appArgs {}
35+ flag . StringVar ( & args . packagesFile , "f" , "packages.json" , "Path to packages index JSON file" )
36+ flag . StringVar ( & args . addr , "addr" , ":8080" , "TCP Listen address" )
37+ flag .StringVar (& args . buildDir , "wasm-build-dir " , os . TempDir () , "Directory for WASM builds " )
38+ flag .StringVar (& args . cleanupInterval , "clean-interval " , "10m " , "Build directory cleanup interval " )
39+ flag .BoolVar (& args . debug , "debug" , false , "Enable debug mode" )
2240
2341 goRoot , ok := os .LookupEnv ("GOROOT" )
2442 if ! ok {
@@ -27,9 +45,9 @@ func main() {
2745 }
2846
2947 flag .Parse ()
30- l := getLogger (debug )
48+ l := getLogger (args . debug )
3149 defer l .Sync ()
32- if err := start (packagesFile , addr , goRoot , debug ); err != nil {
50+ if err := start (goRoot , args ); err != nil {
3351 l .Sugar ().Fatal (err )
3452 }
3553}
@@ -51,31 +69,78 @@ func getLogger(debug bool) (l *zap.Logger) {
5169 return l
5270}
5371
54- func start (packagesFile , addr , goRoot string , debug bool ) error {
72+ func start (goRoot string , args appArgs ) error {
73+ cleanInterval , err := args .getCleanDuration ()
74+ if err != nil {
75+ return fmt .Errorf ("invalid cleanup interval parameter: %s" , err )
76+ }
77+
5578 zap .S ().Infof ("GOROOT is %q" , goRoot )
56- zap .S ().Infof ("Packages file is %q" , packagesFile )
79+ zap .S ().Infof ("Packages file is %q" , args .packagesFile )
80+ zap .S ().Infof ("Cleanup interval is %s" , cleanInterval .String ())
5781 analyzer .SetRoot (goRoot )
58- packages , err := analyzer .ReadPackagesFile (packagesFile )
82+ packages , err := analyzer .ReadPackagesFile (args .packagesFile )
83+ if err != nil {
84+ return fmt .Errorf ("failed to read packages file %q: %s" , args .packagesFile , err )
85+ }
86+
87+ store , err := storage .NewLocalStorage (zap .S (), args .buildDir )
5988 if err != nil {
60- return fmt . Errorf ( "failed to read packages file %q: %s" , packagesFile , err )
89+ return err
6190 }
6291
92+ ctx , _ := app .GetApplicationContext ()
93+ wg := & sync.WaitGroup {}
94+ go store .StartCleaner (ctx , cleanInterval , nil )
95+
6396 r := mux .NewRouter ()
64- langserver .New (packages ).Mount (r .PathPrefix ("/api" ).Subrouter ())
97+ langserver .New (packages , compiler .NewBuildService (zap .S (), store )).
98+ Mount (r .PathPrefix ("/api" ).Subrouter ())
6599 r .PathPrefix ("/" ).Handler (langserver .SpaFileServer ("./public" ))
66100
67- zap .S ().Infof ("Listening on %q" , addr )
68-
69101 var handler http.Handler
70- if debug {
102+ if args . debug {
71103 zap .S ().Info ("Debug mode enabled, CORS disabled" )
72104 handler = langserver .NewCORSDisablerWrapper (r )
73105 } else {
74106 handler = r
75107 }
76108
77- if err := http .ListenAndServe (addr , handler ); err != nil {
78- log .Fatal (err )
109+ server := & http.Server {
110+ Addr : args .addr ,
111+ Handler : handler ,
112+ ReadTimeout : 5 * time .Second ,
113+ WriteTimeout : 10 * time .Second ,
114+ IdleTimeout : 15 * time .Second ,
115+ }
116+
117+ if err := startHttpServer (ctx , wg , server ); err != nil {
118+ return err
119+ }
120+
121+ wg .Wait ()
122+ return nil
123+ }
124+
125+ func startHttpServer (ctx context.Context , wg * sync.WaitGroup , server * http.Server ) error {
126+ logger := zap .S ()
127+ go func () {
128+ <- ctx .Done ()
129+ logger .Info ("Shutting down server..." )
130+ shutdownCtx , cancel := context .WithTimeout (ctx , 30 * time .Second )
131+ defer cancel ()
132+ defer wg .Done ()
133+ server .SetKeepAlivesEnabled (false )
134+ if err := server .Shutdown (shutdownCtx ); err != nil {
135+ logger .Errorf ("Could not gracefully shutdown the server: %v\n " , err )
136+ }
137+ return
138+ }()
139+
140+ wg .Add (1 )
141+ logger .Infof ("Listening on %q" , server .Addr )
142+ if err := server .ListenAndServe (); err != nil && err != http .ErrServerClosed {
143+ return fmt .Errorf ("cannot start server on %q: %s" , server .Addr , err )
79144 }
80145
81146 return nil
0 commit comments