1
1
package main
2
2
3
3
import (
4
+ "context"
4
5
"flag"
5
6
"fmt"
7
+ "net/http"
8
+ "os"
9
+ "sync"
10
+ "time"
11
+
6
12
"github.com/gorilla/mux"
13
+ "github.com/x1unix/foundation/app"
7
14
"github.com/x1unix/go-playground/pkg/analyzer"
15
+ "github.com/x1unix/go-playground/pkg/compiler"
16
+ "github.com/x1unix/go-playground/pkg/compiler/storage"
8
17
"github.com/x1unix/go-playground/pkg/langserver"
9
18
"go.uber.org/zap"
10
- "log"
11
- "net/http"
12
- "os"
13
19
)
14
20
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
+
15
33
func 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" )
22
40
23
41
goRoot , ok := os .LookupEnv ("GOROOT" )
24
42
if ! ok {
@@ -27,9 +45,9 @@ func main() {
27
45
}
28
46
29
47
flag .Parse ()
30
- l := getLogger (debug )
48
+ l := getLogger (args . debug )
31
49
defer l .Sync ()
32
- if err := start (packagesFile , addr , goRoot , debug ); err != nil {
50
+ if err := start (goRoot , args ); err != nil {
33
51
l .Sugar ().Fatal (err )
34
52
}
35
53
}
@@ -51,31 +69,78 @@ func getLogger(debug bool) (l *zap.Logger) {
51
69
return l
52
70
}
53
71
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
+
55
78
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 ())
57
81
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 )
59
88
if err != nil {
60
- return fmt . Errorf ( "failed to read packages file %q: %s" , packagesFile , err )
89
+ return err
61
90
}
62
91
92
+ ctx , _ := app .GetApplicationContext ()
93
+ wg := & sync.WaitGroup {}
94
+ go store .StartCleaner (ctx , cleanInterval , nil )
95
+
63
96
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 ())
65
99
r .PathPrefix ("/" ).Handler (langserver .SpaFileServer ("./public" ))
66
100
67
- zap .S ().Infof ("Listening on %q" , addr )
68
-
69
101
var handler http.Handler
70
- if debug {
102
+ if args . debug {
71
103
zap .S ().Info ("Debug mode enabled, CORS disabled" )
72
104
handler = langserver .NewCORSDisablerWrapper (r )
73
105
} else {
74
106
handler = r
75
107
}
76
108
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 )
79
144
}
80
145
81
146
return nil
0 commit comments