11package main
22
33import (
4+ "bytes"
45 "errors"
56 "flag"
67 "fmt"
@@ -11,17 +12,18 @@ import (
1112 "os"
1213 "path/filepath"
1314 "strings"
15+ "time"
1416
17+ "github.com/barelyhuman/go/env"
1518 "github.com/barelyhuman/goblin/build"
1619 "github.com/barelyhuman/goblin/resolver"
20+ "github.com/barelyhuman/goblin/storage"
1721 "github.com/joho/godotenv"
1822)
1923
2024var shTemplates * template.Template
2125var serverURL string
22-
23- // FIXME: Disabled storage and caching for initial version
24- // var storageClient *storage.Storage
26+ var storageClient storage.Storage
2527
2628func HandleRequest (rw http.ResponseWriter , req * http.Request ) {
2729 path := req .URL .Path
@@ -40,6 +42,7 @@ func HandleRequest(rw http.ResponseWriter, req *http.Request) {
4042 }
4143
4244 if strings .HasPrefix (path , "/binary" ) {
45+ log .Print ("handle binary" )
4346 fetchBinary (rw , req )
4447 return
4548 }
@@ -63,20 +66,12 @@ func StartServer(port string) {
6366 }
6467}
6568
66- func envDefault (key string , def string ) string {
67- if s := os .Getenv (key ); len (strings .TrimSpace (s )) == 0 {
68- return def
69- } else {
70- return s
71- }
72- }
73-
7469// TODO: cleanup code
7570// TODO: move everything into their own interface/structs
7671func main () {
7772
7873 envFile := flag .String ("env" , ".env" , "path to read the env config from" )
79- portFlag := envDefault ("PORT" , "3000" )
74+ portFlag := env . Get ("PORT" , "3000" )
8075
8176 flag .Parse ()
8277
@@ -88,19 +83,64 @@ func main() {
8883 }
8984
9085 shTemplates = template .Must (template .ParseGlob ("templates/*" ))
91- serverURL = envDefault ("ORIGIN_URL" , "http://localhost:3000" )
86+ serverURL = env . Get ("ORIGIN_URL" , "http://localhost:" + portFlag )
9287
93- // FIXME: Disabled storage and caching for initial version
94- // storageClient = & storage.Storage{}
95- // storageClient.BucketName = os.Getenv("BUCKET_NAME" )
96- // err := storageClient.Connect()
97- // if err != nil {
98- // log.Fatal(err)
99- // }
88+ if isStorageEnabled () {
89+ storageClient = storage .NewAWSStorage ( env . Get ( "STORAGE_BUCKET" , "goblin-cache" ))
90+ err := storageClient . Connect ( )
91+ if err != nil {
92+ log . Fatal ( err )
93+ }
94+ }
10095
96+ clearStorageBackgroundJob ()
10197 StartServer (portFlag )
10298}
10399
100+ func clearStorageBackgroundJob () {
101+ cacheHoldEnv := env .Get ("CLEAR_CACHE_TIME" , "" )
102+ if len (cacheHoldEnv ) == 0 {
103+ return
104+ }
105+
106+ cacheHoldDuration , _ := time .ParseDuration (cacheHoldEnv )
107+
108+ cleaner := func (storageClient storage.Storage ) {
109+ log .Println ("Cleaning Cached Storage Object" )
110+ objects := storageClient .ListObjects ()
111+ for _ , obj := range objects {
112+ objExpiry := obj .LastModified .Add (cacheHoldDuration )
113+ if time .Now ().Equal (objExpiry ) || time .Now ().After (objExpiry ) {
114+ storageClient .RemoveObject (obj .Key )
115+ }
116+ }
117+ }
118+
119+ ticker := time .NewTicker (cacheHoldDuration )
120+ quit := make (chan struct {})
121+
122+ go func () {
123+ for {
124+ select {
125+ case <- ticker .C :
126+ cleaner (storageClient )
127+ case <- quit :
128+ ticker .Stop ()
129+ return
130+ }
131+ }
132+ }()
133+ }
134+
135+ func isStorageEnabled () bool {
136+ useStorageEnv := env .Get ("STORAGE_ENABLED" , "false" )
137+ useStorage := false
138+ if useStorageEnv == "true" {
139+ useStorage = true
140+ }
141+ return useStorage
142+ }
143+
104144func normalizePackage (pkg string ) string {
105145 // strip leading protocol
106146 pkg = strings .Replace (pkg , "https://" , "" , 1 )
@@ -228,38 +268,49 @@ func fetchBinary(rw http.ResponseWriter, req *http.Request) {
228268 Module : mod ,
229269 }
230270
231- // TODO: check the storage for existing binary for the module
232- // and return from the storage instead
233-
234271 immutable (rw )
235272
236- // FIXME: Disabled storage and caching for initial version
237- // var buf bytes.Buffer
238- // err := bin.WriteBuild(io.MultiWriter(rw, &buf))
273+ artifactName := constructArtifactName (bin )
239274
240- err := bin .WriteBuild (io .MultiWriter (rw ))
275+ if isStorageEnabled () && storageClient .HasObject (artifactName ) {
276+ url , _ := storageClient .GetSignedURL (artifactName )
277+ log .Println ("From cache" )
278+ http .Redirect (rw , req , url , http .StatusSeeOther )
279+ return
280+ }
281+
282+ var buf bytes.Buffer
283+ err := bin .WriteBuild (io .MultiWriter (rw , & buf ))
241284
242285 if err != nil {
243286 rw .WriteHeader (http .StatusInternalServerError )
244287 fmt .Fprint (rw , err .Error ())
245288 return
246289 }
247290
291+ if isStorageEnabled () {
292+ err = storageClient .Upload (
293+ artifactName ,
294+ buf ,
295+ )
296+
297+ if err != nil {
298+ log .Println ("Failed to upload" , err )
299+ }
300+ }
301+
248302 err = bin .Cleanup ()
249303 if err != nil {
250304 log .Println ("cleaning binary build" , err )
251305 }
306+ }
252307
253- // FIXME: Disabled storage and caching for initial version
254- // err = storageClient.Upload(bin.Module, bin.Dest)
255- // if err != nil {
256- // fmt.Fprint(rw, err.Error())
257- // return
258- // }
259-
260- // url, err := storageClient.GetSignedURL(bin.Module, bin.Name)
261- // if err != nil {
262- // fmt.Fprint(rw, err.Error())
263- // return
264- // }
308+ func constructArtifactName (bin * build.Binary ) string {
309+ var artifactName strings.Builder
310+ artifactName .Write ([]byte (bin .Name ))
311+ artifactName .Write ([]byte ("-" ))
312+ artifactName .Write ([]byte (bin .OS ))
313+ artifactName .Write ([]byte ("-" ))
314+ artifactName .Write ([]byte (bin .Arch ))
315+ return artifactName .String ()
265316}
0 commit comments