From 5014461a3ff8f7997df63097ebf037f580fb3f38 Mon Sep 17 00:00:00 2001 From: Frank Mayer Date: Sat, 9 Nov 2024 18:45:26 +0100 Subject: [PATCH] refactor: remove gzip --- internal/config/config.go | 20 +++ internal/net/net.go | 369 ++++++++++++++------------------------ internal/public/index.js | 57 ++++-- internal/setup/setup.go | 288 ----------------------------- main.go | 16 +- 5 files changed, 215 insertions(+), 535 deletions(-) create mode 100644 internal/config/config.go delete mode 100644 internal/setup/setup.go diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..205ee02 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,20 @@ +package config + +import ( + "flag" + "fmt" +) + +var ( + Addr string + Path string +) + +func init() { + port := flag.Int("port", 0, "port to listen on") + flag.StringVar(&Path, "path", ".", "path to serve") + + flag.Parse() + + Addr = fmt.Sprintf(":%d", *port) +} diff --git a/internal/net/net.go b/internal/net/net.go index 61ddc82..bbad970 100644 --- a/internal/net/net.go +++ b/internal/net/net.go @@ -1,26 +1,21 @@ package net import ( - "compress/gzip" + "context" "encoding/json" - "errors" "fmt" - "io" "net" "net/http" "os" + "os/signal" "path/filepath" - "sync" + "syscall" "time" "github.com/charmbracelet/log" "github.com/gorilla/websocket" + "github.com/tsukinoko-kun/portal/internal/config" "github.com/tsukinoko-kun/portal/internal/public" - "github.com/tsukinoko-kun/portal/internal/setup" -) - -var ( - upgrader = websocket.Upgrader{} ) type ( @@ -34,288 +29,196 @@ type ( // Mime contains the MIME type of the file Mime string `json:"mime"` } - - TransmissionReceiver struct { - conn *websocket.Conn - } - - TransmissionSignal string -) - -const ( - SignalNone TransmissionSignal = "" - SignalReady TransmissionSignal = "READY" - SignalEOF TransmissionSignal = "EOF" - SignalEOT TransmissionSignal = "EOT" -) - -func (t TransmissionSignal) Into() []byte { - return []byte(t) -} - -func (t *TransmissionReceiver) Error(message any) { - switch v := message.(type) { - case string: - _ = t.conn.WriteMessage(websocket.CloseProtocolError, websocket.FormatCloseMessage(websocket.CloseNormalClosure, v)) - case error: - _ = t.conn.WriteMessage(websocket.CloseProtocolError, websocket.FormatCloseMessage(websocket.CloseNormalClosure, v.Error())) - } -} - -var ( - EotError = errors.New("end of portal transmission") ) -func (t *TransmissionReceiver) End() error { - return EotError +var upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true // Allow all origins; customize this as needed + }, } -func (t *TransmissionReceiver) Read() ([]byte, TransmissionSignal, error) { - ty, b, err := t.conn.ReadMessage() +func UploadHandler(w http.ResponseWriter, r *http.Request) { + // Upgrade the connection to WebSocket + conn, err := upgrader.Upgrade(w, r, nil) if err != nil { - return nil, SignalNone, err - } - - switch ty { - case websocket.TextMessage: - str := string(b[:]) - return nil, TransmissionSignal(str), nil - case websocket.BinaryMessage: - return b, SignalNone, nil + log.Error("failed to upgrade connection", "err", err) + w.WriteHeader(http.StatusInternalServerError) + return } + defer conn.Close() - return nil, SignalNone, nil -} - -func (t *TransmissionReceiver) Signal(signal TransmissionSignal) error { - return t.conn.WriteMessage(websocket.TextMessage, signal.Into()) -} - -func (t *TransmissionReceiver) ReadHeader() (header Header, err error) { - _, b, err := t.conn.ReadMessage() + // Step 1: Receive and decode the file header + var header Header + _, message, err := conn.ReadMessage() if err != nil { - return header, errors.Join(errors.New("failed to read message expected header"), err) + log.Error("failed to read message", "err", err) + return } - if s := TransmissionSignal(b[:]); s == SignalEOT { - log.Debug("received EOT") - return header, EotError + if err := json.Unmarshal(message, &header); err != nil { + log.Error("failed to unmarshal header", "err", err) + return } - if err := json.Unmarshal(b, &header); err != nil { - log.Error("unmarshalling failed", "err", err) - return header, errors.Join(errors.New("failed to unmarshal header"), err) + // Normalize and verify the file path + filePath, err := normalizePath(header.Name) + if err != nil { + log.Error("failed to normalize file path", "err", err) + conn.WriteMessage(websocket.TextMessage, []byte("Invalid file path")) + return } - return header, nil -} - -func (t *TransmissionReceiver) CreateFileWriter(h Header) (*os.File, error) { - p := filepath.Join(setup.Wd, h.Name) - - // check if file is inside wd - if relPath, err := filepath.Rel(setup.Wd, p); err != nil || relPath == ".." || relPath[:2] == ".." { - log.Error("file is outside working directory", "path", p) - return nil, fmt.Errorf("file is outside working directory %s", p) + // Ensure the file path is within the server's working directory + if !isInWorkingDir(filePath) { + log.Error("file path outside working directory") + conn.WriteMessage(websocket.TextMessage, []byte("File path outside working directory")) + return } - // create parent directories - parentDir := filepath.Dir(p) - if err := os.MkdirAll(parentDir, 0777); err != nil { - return nil, errors.Join(fmt.Errorf("failed to create parent directory %s", p), err) + // Create the target directory if needed + if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { + log.Error("failed to create directory", "err", err) + conn.WriteMessage(websocket.TextMessage, []byte("Directory creation error")) + return } - // create file - f, err := os.Create(p) + // Open the target file for writing + file, err := os.Create(filePath) if err != nil { - return nil, errors.Join(fmt.Errorf("failed to create file %s", p), err) + log.Error("failed to create file", "err", err) + conn.WriteMessage(websocket.TextMessage, []byte("File creation error")) + return } - log.Debug("file created", "path", p) - return f, nil -} - -func (t *TransmissionReceiver) Copy(dst io.Writer) error { - pipeReader, pipeWriter := io.Pipe() - defer pipeReader.Close() - log.Debug("pipe created") + defer file.Close() - wg := sync.WaitGroup{} - wg.Add(1) + // Send "READY" message to the client + conn.WriteMessage(websocket.TextMessage, []byte("READY")) - go func() { - defer wg.Done() - log.Debug("creating Gzip reader") - gzipReader, err := gzip.NewReader(pipeReader) - if err != nil { - log.Error("Failed to create Gzip reader", "err", err) - return - } - defer gzipReader.Close() - log.Debug("Gzip reader created") - - if _, err := io.Copy(dst, gzipReader); err != nil { - t.Error(errors.Join(errors.New("failed to copy data"), err)) - } else { - if err := t.Signal(SignalEOF); err != nil { - log.Error("Failed to signal EOF", "err", err) - } - } - }() - - // Read and decompress chunks as they arrive + // Step 2: Receive and write file chunks for { - message, s, err := t.Read() + _, message, err := conn.ReadMessage() if err != nil { - if websocket.IsCloseError(err, websocket.CloseNormalClosure) { - log.Debug("WebSocket closed normally.") - return nil - } else { - return errors.Join(errors.New("failed to read data"), err) - } + log.Error("failed to read message", "err", err) + return } - if s == SignalEOF { - log.Debug("received EOF") + // Check for EOF + if string(message) == "EOF" { + log.Debug("end of file") break } - if s == SignalEOT { - log.Debug("received EOT") - return t.End() - } - - // Write the compressed chunk to the pipe, which the gzip reader will decompress - if _, err = pipeWriter.Write(message); err != nil { - return errors.Join(errors.New("failed to write data to compression pipe"), err) + // Write the chunk to the file + if _, err := file.Write(message); err != nil { + log.Error("failed to write file chunk", "err", err) + return } } - if err := pipeWriter.Close(); err != nil { - return errors.Join(errors.New("failed to close compression pipe"), err) + // Optionally, set the file's last modified time + modTime := time.Unix(header.LastModified, 0) + if err := os.Chtimes(filePath, modTime, modTime); err != nil { + log.Error("failed to set last modified time", "err", err) } - return nil + log.Info("file copy successful") } -func (t *TransmissionReceiver) Process() error { - h, err := t.ReadHeader() +// normalizePath cleans and returns the absolute path of the file. +func normalizePath(name string) (string, error) { + // Clean and make the path absolute + absPath, err := filepath.Abs(filepath.Clean(name)) if err != nil { - return errors.Join(errors.New("failed to read header"), err) - } - if len(h.Name) == 0 { - return errors.New("received invalid header") - } - log.Debug("received", "header", h) - - if err := t.Signal(SignalReady); err != nil { - return errors.Join(errors.New("failed to send READY signal"), err) + return "", err } + return absPath, nil +} - f, err := t.CreateFileWriter(h) +// isInWorkingDir checks if a path is within the server's current working directory. +func isInWorkingDir(path string) bool { + wd, err := os.Getwd() if err != nil { - return errors.Join(errors.New("failed to create file"), err) + log.Error("failed to get working directory", "err", err) + return false } - defer func() { - if err = f.Close(); err != nil { - log.Error("failed to close file", "err", err, "file", f.Name()) - } - - // set last modified time - lastModified := time.UnixMilli(h.LastModified) - log.Debug("set last modified time", "file", f.Name(), "last_modified", lastModified) - if err := os.Chtimes(f.Name(), lastModified, lastModified); err != nil { - log.Error("failed to set last modified time", "err", err) - } - }() - - copyErr := t.Copy(f) - - if copyErr != nil { - log.Error("failed to copy file", "err", copyErr) - } else { - log.Info("successfully copied file", "dst", f.Name()) + relPath, err := filepath.Rel(wd, path) + if err != nil || relPath == ".." || relPath == "." || relPath[0] == '/' || filepath.IsAbs(relPath) { + return false } - - return copyErr + return true } -func wsHandler(w http.ResponseWriter, r *http.Request) { - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - log.Error("failed to upgrade connection", "err", err) - return - } - defer conn.Close() +// StartServer starts the WebSocket server, prints IP/port, and handles graceful shutdown. +func StartServer() error { + mux := http.NewServeMux() + mux.HandleFunc("/ws", UploadHandler) + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.FileServerFS(public.Fs).ServeHTTP(w, r) + }) - t := TransmissionReceiver{ - conn: conn, + // Start the server on a random available port + server := &http.Server{ + Addr: config.Addr, + Handler: mux, } - for { - if err := t.Process(); err != nil { - if errors.Is(err, EotError) { - break - } - log.Error("portal protocol failed", "err", err) - t.Error(err) - <-time.After(time.Second) - break - } + // Listen on a random port and retrieve IP/port information + listener, err := net.Listen("tcp", server.Addr) + if err != nil { + return fmt.Errorf("failed to listen: %v", err) } -} + defer listener.Close() -func getPublicIP() (string, error) { - addrs, err := net.InterfaceAddrs() + // Print IP + port and hostname + port + ip, err := getLocalIP() if err != nil { - return "", err + return fmt.Errorf("failed to get local IP: %v", err) } + _, port, _ := net.SplitHostPort(listener.Addr().String()) + host, _ := os.Hostname() + fmt.Printf("http://%s:%s\nhttp://%s:%s\n", ip, port, host, port) - for _, addr := range addrs { - if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() { - if ip.IP.To4() != nil { - return ip.IP.String(), nil - } - } - } + // Signal handling for graceful shutdown + quit := make(chan os.Signal, 1) + signal.Notify(quit, os.Interrupt, syscall.SIGTERM) - for _, addr := range addrs { - if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() { - if ip.IP.To16() != nil { - return ip.IP.String(), nil - } + go func() { + <-quit + fmt.Println("Shutting down server...") + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := server.Shutdown(ctx); err != nil { + log.Fatalf("Server forced to shutdown: %v", err) } - } - - return "", fmt.Errorf("no public IP found") -} + }() -func setupHttpHandlers() { - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - http.FileServerFS(public.Fs).ServeHTTP(w, r) - }) - http.HandleFunc("/ws", wsHandler) + // Start the server + return server.Serve(listener) } -func Listen() error { - setupHttpHandlers() - - ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *setup.Port)) +// getLocalIP retrieves the local IP address of the computer. +func getLocalIP() (string, error) { + interfaces, err := net.Interfaces() if err != nil { - return errors.Join(errors.New("failed to listen"), err) - } - - log.Debug("server started", "addr", ln.Addr()) - - if publicIP, err := getPublicIP(); err == nil { - fmt.Printf("Portal available at http://%s:%d\n", publicIP, ln.Addr().(*net.TCPAddr).Port) - } - if hostname, err := os.Hostname(); err == nil { - fmt.Printf("Portal available at http://%s:%d\n", hostname, ln.Addr().(*net.TCPAddr).Port) + return "", err } - - if err := http.Serve(ln, nil); err != nil { - return errors.Join(errors.New("failed to serve"), err) + for _, iface := range interfaces { + addrs, err := iface.Addrs() + if err != nil { + continue + } + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + if ip != nil && !ip.IsLoopback() && ip.To4() != nil { + return ip.String(), nil + } + } } - - return nil + return "", fmt.Errorf("no connected network interface found") } diff --git a/internal/public/index.js b/internal/public/index.js index c98196d..2abf4e6 100644 --- a/internal/public/index.js +++ b/internal/public/index.js @@ -58,10 +58,10 @@ class Transmitter { url.hash = ""; url.search = ""; url.protocol = url.protocol === "https:" ? "wss:" : "ws:"; - this.ws = new WebSocket(url); + this.ws = new BlockingWebSocket(url); const expProm = explodedPromise(); this.available = expProm.promise; - this.ws.addEventListener("open", expProm.resolve, {once: true}) + this.ws.addEventListener("open", expProm.resolve, { once: true }) this.sentCount = 0; } @@ -87,7 +87,7 @@ class Transmitter { let lastTime = performance.now(); let lastData = 0; while (true) { - const {value, done} = await reader.read(); + const { value, done } = await reader.read(); if (done) { break; } @@ -123,7 +123,7 @@ class Transmitter { } else { reject(new ServerError(ev.data)); } - }, {once: true}); + }, { once: true }); }); } @@ -145,18 +145,17 @@ class Transmitter { console.debug("transmit", file, header); createFileVis(header); - const compressionStream = new CompressionStream("gzip"); const fileStream = file.stream(); - const compressedStream = fileStream.pipeThrough(compressionStream); - const reader = compressedStream.getReader(); + const reader = fileStream.getReader(); const serverReady = this.serverMessage((data) => data === "READY"); this.ws.send(JSON.stringify(header)); await serverReady; + await this.streamReaderToWs(reader, header); console.debug("EOF", header.name) - await this.ws.send("EOF"); + this.ws.send("EOF"); removeFileVis(header); this.setBusy(false); } @@ -167,6 +166,42 @@ class Transmitter { } } +class BlockingWebSocket { + constructor(url, bufferedThreshold = 1024 * 1024 * 2) { // Default threshold: 2MiB + this.ws = new WebSocket(url); + this.bufferedThreshold = bufferedThreshold; + } + + async send(data) { + // Wait until bufferedAmount is below the threshold + while (this.ws.bufferedAmount > this.bufferedThreshold) { + await this.waitForBuffer(); + } + + // Now it's safe to send + this.ws.send(data); + } + + waitForBuffer() { + return new Promise(resolve => { + // Use setTimeout to poll bufferedAmount after a short delay + const checkBuffer = () => { + if (this.ws.bufferedAmount <= this.bufferedThreshold) { + resolve(); + } else { + // Keep waiting if bufferedAmount is still high + setTimeout(checkBuffer, 50); + } + }; + checkBuffer(); + }); + } + + addEventListener(event, listener, options) { + this.ws.addEventListener(event, listener, options); + } +} + // input element fileInput.addEventListener("change", async function () { @@ -186,9 +221,9 @@ fileInput.addEventListener("change", async function () { // drag and drop -document.body.addEventListener("dragenter", handleDragEnter, {passive: true}); +document.body.addEventListener("dragenter", handleDragEnter, { passive: true }); document.body.addEventListener("dragover", handleDragOver); -document.body.addEventListener("dragleave", handleDragLeave, {passive: true}); +document.body.addEventListener("dragleave", handleDragLeave, { passive: true }); document.body.addEventListener("drop", handleDrop); function handleDragEnter() { @@ -271,7 +306,7 @@ async function* readDirectoryRecursively(directoryEntry) { if (entry.isFile) { try { const file = await getFileFromEntry(entry); - yield {file, name: entry.fullPath}; + yield { file, name: entry.fullPath }; } catch (err) { console.error("Failed to process file within directory:", entry, err); } diff --git a/internal/setup/setup.go b/internal/setup/setup.go deleted file mode 100644 index 86f8553..0000000 --- a/internal/setup/setup.go +++ /dev/null @@ -1,288 +0,0 @@ -package setup - -import ( - "compress/gzip" - "encoding/json" - "errors" - "flag" - "fmt" - "io" - "os" - "path/filepath" - "sync" - "time" - - "github.com/charmbracelet/log" - "github.com/gorilla/websocket" -) - -var ( - Wd string -) - -func init() { - var err error - if Wd, err = os.Getwd(); err != nil { - log.Fatal("failed to get working directory", "err", err) - } - - parseOptions() - applyPath() - applyLogger() -} - -type ( - Header struct { - // Name is the name of the file. - Name string `json:"name"` - // Size is the size of the file in bytes. - Size int `json:"size"` - // LastModified is the last modified time of the file. - LastModified int64 `json:"lastModified"` - // Mime contains the MIME type of the file - Mime string `json:"mime"` - } - - TransmissionReceiver struct { - conn *websocket.Conn - } - - TransmissionSignal string -) - -const ( - SignalNone TransmissionSignal = "" - SignalReady TransmissionSignal = "READY" - SignalEOF TransmissionSignal = "EOF" - SignalEOT TransmissionSignal = "EOT" -) - -func (t TransmissionSignal) Into() []byte { - return []byte(t) -} - -func (t *TransmissionReceiver) Error(message any) { - switch v := message.(type) { - case string: - _ = t.conn.WriteMessage(websocket.CloseProtocolError, websocket.FormatCloseMessage(websocket.CloseNormalClosure, v)) - case error: - _ = t.conn.WriteMessage(websocket.CloseProtocolError, websocket.FormatCloseMessage(websocket.CloseNormalClosure, v.Error())) - } -} - -var ( - EotError = errors.New("end of portal transmission") -) - -func (t *TransmissionReceiver) End() error { - return EotError -} - -func (t *TransmissionReceiver) Read() ([]byte, TransmissionSignal, error) { - ty, b, err := t.conn.ReadMessage() - if err != nil { - return nil, SignalNone, err - } - - switch ty { - case websocket.TextMessage: - str := string(b[:]) - return nil, TransmissionSignal(str), nil - case websocket.BinaryMessage: - return b, SignalNone, nil - } - - return nil, SignalNone, nil -} - -func (t *TransmissionReceiver) Signal(signal TransmissionSignal) error { - return t.conn.WriteMessage(websocket.TextMessage, signal.Into()) -} - -func (t *TransmissionReceiver) ReadHeader() (header Header, err error) { - _, b, err := t.conn.ReadMessage() - if err != nil { - return header, errors.Join(errors.New("failed to read message expected header"), err) - } - - if s := TransmissionSignal(b[:]); s == SignalEOT { - log.Debug("received EOT") - return header, EotError - } - - if err := json.Unmarshal(b, &header); err != nil { - log.Error("unmarshalling failed", "err", err) - return header, errors.Join(errors.New("failed to unmarshal header"), err) - } - - return header, nil -} - -func (t *TransmissionReceiver) CreateFileWriter(h Header) (*os.File, error) { - p := filepath.Join(Wd, h.Name) - - // check if file is inside wd - if relPath, err := filepath.Rel(Wd, p); err != nil || relPath == ".." || relPath[:2] == ".." { - log.Error("file is outside working directory", "path", p) - return nil, fmt.Errorf("file is outside working directory %s", p) - } - - // create parent directories - parentDir := filepath.Dir(p) - if err := os.MkdirAll(parentDir, 0777); err != nil { - return nil, errors.Join(fmt.Errorf("failed to create parent directory %s", p), err) - } - - // create file - f, err := os.Create(p) - if err != nil { - return nil, errors.Join(fmt.Errorf("failed to create file %s", p), err) - } - log.Debug("file created", "path", p) - return f, nil -} - -func (t *TransmissionReceiver) Copy(dst io.Writer) error { - pipeReader, pipeWriter := io.Pipe() - defer pipeReader.Close() - log.Debug("pipe created") - - wg := sync.WaitGroup{} - wg.Add(1) - - go func() { - defer wg.Done() - log.Debug("creating Gzip reader") - gzipReader, err := gzip.NewReader(pipeReader) - if err != nil { - log.Error("Failed to create Gzip reader", "err", err) - return - } - defer gzipReader.Close() - log.Debug("Gzip reader created") - - if _, err := io.Copy(dst, gzipReader); err != nil { - t.Error(errors.Join(errors.New("failed to copy data"), err)) - } else { - if err := t.Signal(SignalEOF); err != nil { - log.Error("Failed to signal EOF", "err", err) - } - } - }() - - // Read and decompress chunks as they arrive - for { - message, s, err := t.Read() - if err != nil { - if websocket.IsCloseError(err, websocket.CloseNormalClosure) { - log.Debug("WebSocket closed normally.") - return nil - } else { - return errors.Join(errors.New("failed to read data"), err) - } - } - - if s == SignalEOF { - log.Debug("received EOF") - break - } - - if s == SignalEOT { - log.Debug("received EOT") - return t.End() - } - - // Write the compressed chunk to the pipe, which the gzip reader will decompress - if _, err = pipeWriter.Write(message); err != nil { - return errors.Join(errors.New("failed to write data to compression pipe"), err) - } - } - - if err := pipeWriter.Close(); err != nil { - return errors.Join(errors.New("failed to close compression pipe"), err) - } - - return nil -} - -func (t *TransmissionReceiver) Process() error { - h, err := t.ReadHeader() - if err != nil { - return errors.Join(errors.New("failed to read header"), err) - } - if len(h.Name) == 0 { - return errors.New("received invalid header") - } - log.Debug("received", "header", h) - - if err := t.Signal(SignalReady); err != nil { - return errors.Join(errors.New("failed to send READY signal"), err) - } - - f, err := t.CreateFileWriter(h) - if err != nil { - return errors.Join(errors.New("failed to create file"), err) - } - defer func() { - if err = f.Close(); err != nil { - log.Error("failed to close file", "err", err, "file", f.Name()) - } - - // set last modified time - lastModified := time.UnixMilli(h.LastModified) - log.Debug("set last modified time", "file", f.Name(), "last_modified", lastModified) - if err := os.Chtimes(f.Name(), lastModified, lastModified); err != nil { - log.Error("failed to set last modified time", "err", err) - } - }() - - copyErr := t.Copy(f) - - if copyErr != nil { - log.Error("failed to copy file", "err", copyErr) - } else { - log.Info("successfully copied file", "dst", f.Name()) - } - - return copyErr -} - -// cli options -var ( - Port *int - Path *string - Debug *bool -) - -func parseOptions() { - Port = flag.Int("port", 0, "port to listen on") - Path = flag.String("path", ".", "path to save files") - Debug = flag.Bool("debug", false, "enable debug logging") - flag.Parse() -} - -func applyPath() { - if *Path != "." { - if err := os.MkdirAll(*Path, 0777); err != nil { - log.Fatal("failed to create directory", "path", Path, "err", err) - return - } - if err := os.Chdir(*Path); err != nil { - log.Fatal("failed to change directory", "err", err) - return - } - var err error - if Wd, err = os.Getwd(); err == nil { - log.Info("working directory", "path", Wd) - } - } -} - -func applyLogger() { - if *Debug { - log.SetLevel(log.DebugLevel) - log.Debug("debug logging enabled") - } else { - log.SetLevel(log.InfoLevel) - } -} diff --git a/main.go b/main.go index b419de1..3f5a545 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,19 @@ package main -import "github.com/tsukinoko-kun/portal/internal/net" +import ( + "log" + "os" + + "github.com/tsukinoko-kun/portal/internal/config" + "github.com/tsukinoko-kun/portal/internal/net" +) func main() { - if err := net.Listen(); err != nil { - panic(err) + if err := os.Chdir(config.Path); err != nil { + log.Fatal(err) + } + + if err := net.StartServer(); err != nil { + log.Fatal(err) } }