-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a custom assets server instead of using nginx.
- Loading branch information
1 parent
22305b4
commit a99f4cb
Showing
10 changed files
with
360 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
FROM golang:1.19-buster as builder-image | ||
|
||
WORKDIR /app | ||
|
||
COPY . /app/ | ||
|
||
RUN go mod download && \ | ||
go build -o assets-server main.go | ||
|
||
FROM fedora:36 | ||
|
||
RUN useradd -u 1001 -g 0 -M -d /opt/app-root/src default && \ | ||
mkdir -p /opt/app-root/src && \ | ||
chown -R 1001:0 /opt/app-root | ||
|
||
WORKDIR /opt/app-root | ||
|
||
COPY --from=builder-image /app/assets-server /opt/app-root/bin/ | ||
|
||
USER 1001 | ||
|
||
EXPOSE 8080 | ||
|
||
VOLUME ["/opt/app-root/data"] | ||
|
||
ENTRYPOINT ["/opt/app-root/bin/assets-server"] | ||
|
||
CMD ["--dir", "/opt/app-root/data", "--host", "0.0.0.0"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module github.com/vmware-tanzu-labs/educates-training-platform/assets-server | ||
|
||
go 1.20 | ||
|
||
require ( | ||
github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||
github.com/spf13/cobra v1.7.0 // indirect | ||
github.com/spf13/pflag v1.0.5 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= | ||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | ||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= | ||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= | ||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
/* | ||
* This is a Golang application that serves static files from a specified | ||
* directory and can also create and serve tar and zip archives of directories | ||
* from the same directory. | ||
* | ||
* The application uses the cobra package for command-line argument handling. It | ||
* allows the user to specify the directory path from which static files are | ||
* served, the port the server listens on, and the host interface the listener | ||
* socket is bound to. | ||
* | ||
* The server can handle the following types of requests: | ||
* - Requests for regular static files (e.g., http://localhost:8080/file.txt) | ||
* - Requests for tar archives of directories (e.g., http://localhost:8080/subdir/.tar) | ||
* - Requests for tar.gz or .tgz archives of directories (e.g., http://localhost:8080/subdir/.tar.gz) | ||
* - Requests for zip archives of directories (e.g., http://localhost:8080/subdir/.zip) | ||
*/ | ||
|
||
package main | ||
|
||
import ( | ||
"archive/tar" | ||
"archive/zip" | ||
"compress/gzip" | ||
"fmt" | ||
"io" | ||
"log" | ||
"net/http" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
func createTarArchive(dirPath string, writer io.Writer, compress bool) error { | ||
var tarWriter *tar.Writer | ||
if compress { | ||
gzipWriter := gzip.NewWriter(writer) | ||
defer gzipWriter.Close() | ||
tarWriter = tar.NewWriter(gzipWriter) | ||
} else { | ||
tarWriter = tar.NewWriter(writer) | ||
} | ||
defer tarWriter.Close() | ||
|
||
return filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
|
||
relPath, err := filepath.Rel(dirPath, path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Create a new tar header | ||
header, err := tar.FileInfoHeader(info, "") | ||
if err != nil { | ||
return err | ||
} | ||
header.Name = filepath.ToSlash(relPath) | ||
|
||
// Write the header to the tar archive | ||
if err := tarWriter.WriteHeader(header); err != nil { | ||
return err | ||
} | ||
|
||
// If the file is not a directory, write its content to the tar archive | ||
if !info.IsDir() { | ||
file, err := os.Open(path) | ||
if err != nil { | ||
return err | ||
} | ||
defer file.Close() | ||
|
||
_, err = io.Copy(tarWriter, file) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
}) | ||
} | ||
|
||
func createZipArchive(dirPath string, writer io.Writer) error { | ||
zipWriter := zip.NewWriter(writer) | ||
defer zipWriter.Close() | ||
|
||
return filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
|
||
relPath, err := filepath.Rel(dirPath, path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Create a new zip header | ||
header, err := zip.FileInfoHeader(info) | ||
if err != nil { | ||
return err | ||
} | ||
header.Name = filepath.ToSlash(relPath) | ||
|
||
// Write the header to the zip archive | ||
writer, err := zipWriter.CreateHeader(header) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// If the file is not a directory, write its content to the zip archive | ||
if !info.IsDir() { | ||
file, err := os.Open(path) | ||
if err != nil { | ||
return err | ||
} | ||
defer file.Close() | ||
|
||
_, err = io.Copy(writer, file) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
}) | ||
} | ||
|
||
func main() { | ||
var rootCmd = &cobra.Command{ | ||
Use: "static-server", | ||
Short: "Serve static files from a directory", | ||
Run: startServer, | ||
} | ||
|
||
var dataDir string | ||
var port string | ||
var host string | ||
|
||
rootCmd.Flags().StringVarP(&dataDir, "dir", "d", "data", "Directory path containing static files") | ||
rootCmd.Flags().StringVarP(&port, "port", "p", "8080", "Port number to listen on") | ||
rootCmd.Flags().StringVarP(&host, "host", "H", "localhost", "Host interface to bind the listener socket") | ||
|
||
if err := rootCmd.Execute(); err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func startServer(cmd *cobra.Command, args []string) { | ||
dataDir, _ := cmd.Flags().GetString("dir") | ||
port, _ := cmd.Flags().GetString("port") | ||
host, _ := cmd.Flags().GetString("host") | ||
|
||
// Check if the data directory exists | ||
_, err := os.Stat(dataDir) | ||
if err != nil { | ||
if os.IsNotExist(err) { | ||
fmt.Println("Directory", dataDir, "does not exist. Please create the directory and put your static files in it.") | ||
return | ||
} | ||
fmt.Println("Error:", err) | ||
return | ||
} | ||
|
||
// Middleware for logging HTTP requests | ||
loggingMiddleware := func(next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
log.Printf("Incoming request: %s %s", r.Method, r.URL.Path) | ||
next.ServeHTTP(w, r) | ||
}) | ||
} | ||
|
||
// Create a file server handler to serve static files from the data directory | ||
fileServer := http.FileServer(http.Dir(dataDir)) | ||
|
||
// Handle requests for tar, tar.gz, or zip archives of directories | ||
http.Handle("/", loggingMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
requestedPath := r.URL.Path | ||
|
||
// Check if the requested path ends with ".tar", ".tar.gz" or ".tgz" | ||
if strings.HasSuffix(requestedPath, "/.tar") { | ||
// Remove the ".tar" suffix from the path | ||
requestedPath = strings.TrimSuffix(requestedPath, ".tar") | ||
|
||
// Check if the path maps to a directory | ||
fileInfo, err := os.Stat(filepath.Join(dataDir, requestedPath)) | ||
if err != nil || !fileInfo.IsDir() { | ||
// Serve static files as the path does not map to a directory | ||
fileServer.ServeHTTP(w, r) | ||
return | ||
} | ||
|
||
// Serve the tar archive for the requested directory | ||
w.Header().Set("Content-Type", "application/x-tar") | ||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.tar\"", requestedPath)) | ||
|
||
err = createTarArchive(filepath.Join(dataDir, requestedPath), w, false) | ||
if err != nil { | ||
http.Error(w, "Error creating tar archive", http.StatusInternalServerError) | ||
return | ||
} | ||
return | ||
} else if strings.HasSuffix(requestedPath, "/.tar.gz") || strings.HasSuffix(requestedPath, "/.tgz") { | ||
// Remove the ".tar.gz" or ".tgz" suffix from the path | ||
requestedPath = strings.TrimSuffix(requestedPath, ".tar.gz") | ||
requestedPath = strings.TrimSuffix(requestedPath, ".tgz") | ||
|
||
// Check if the path maps to a directory | ||
fileInfo, err := os.Stat(filepath.Join(dataDir, requestedPath)) | ||
if err != nil || !fileInfo.IsDir() { | ||
// Serve static files as the path does not map to a directory | ||
fileServer.ServeHTTP(w, r) | ||
return | ||
} | ||
|
||
// Serve the tar.gz archive for the requested directory | ||
w.Header().Set("Content-Type", "application/gzip") | ||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.tar.gz\"", requestedPath)) | ||
|
||
err = createTarArchive(filepath.Join(dataDir, requestedPath), w, true) | ||
if err != nil { | ||
http.Error(w, "Error creating tar.gz archive", http.StatusInternalServerError) | ||
return | ||
} | ||
return | ||
} else if strings.HasSuffix(requestedPath, "/.zip") { | ||
// Remove the ".zip" suffix from the path | ||
requestedPath = strings.TrimSuffix(requestedPath, ".zip") | ||
|
||
// Check if the path maps to a directory | ||
fileInfo, err := os.Stat(filepath.Join(dataDir, requestedPath)) | ||
if err != nil || !fileInfo.IsDir() { | ||
// Serve static files as the path does not map to a directory | ||
fileServer.ServeHTTP(w, r) | ||
return | ||
} | ||
|
||
// Serve the zip archive for the requested directory | ||
w.Header().Set("Content-Type", "application/zip") | ||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.zip\"", requestedPath)) | ||
|
||
err = createZipArchive(filepath.Join(dataDir, requestedPath), w) | ||
if err != nil { | ||
http.Error(w, "Error creating zip archive", http.StatusInternalServerError) | ||
return | ||
} | ||
return | ||
} | ||
|
||
// Serve static files | ||
fileServer.ServeHTTP(w, r) | ||
}))) | ||
|
||
// Start the server on the specified host and port | ||
addr := host + ":" + port | ||
fmt.Println("Server is running on http://" + addr) | ||
err = http.ListenAndServe(addr, nil) | ||
if err != nil { | ||
fmt.Println("Error:", err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.