diff --git a/.air.toml b/.air.toml
index d17a93c..d8cedb9 100644
--- a/.air.toml
+++ b/.air.toml
@@ -4,8 +4,8 @@ tmp_dir = "tmp"
[build]
args_bin = []
- bin = "./tmp/main"
- cmd = "templ generate && go build -o ./tmp/main ."
+ bin = "./portkey"
+ cmd = "templ generate . && ./build.sh"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"]
exclude_file = []
diff --git a/Dockerfile b/Dockerfile
index a57f596..95d0fcb 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,7 @@
FROM node:20.11.1-alpine3.18 AS frontend
+ARG VERSION=dev
+
WORKDIR /usr/src/app
COPY package.json ./
@@ -27,7 +29,7 @@ COPY internal internal/
RUN apk add --no-cache git bash
RUN go install github.com/a-h/templ/cmd/templ@latest && templ generate
-RUN bash build.sh
+RUN sh build.sh $VERSION
FROM alpine:3.19.1
@@ -40,12 +42,15 @@ LABEL org.opencontainers.image.authors='dev@codehat.de' \
WORKDIR /opt
-COPY --from=backend /app/portkey /opt/app
+COPY --from=backend /app/portkey ./app
+# Provide a default config. Can be overwritten by mounting as volume by user.
+COPY config.yml .
-RUN adduser -D -H nonroot && \
- chmod +x /opt/app
+RUN apk add --no-cache tzdata && \
+ adduser -D -H nonroot && \
+ chmod +x ./app
-EXPOSE 3000
+EXPOSE 3000/tcp
USER nonroot:nonroot
diff --git a/build.sh b/build.sh
index 2dca958..3f512a5 100755
--- a/build.sh
+++ b/build.sh
@@ -1,34 +1,30 @@
#!/bin/sh
-clear
-
-TRG_PKG='main'
+TARGET_PACKAGE='github.com/kodehat/portkey/internal/build'
BUILD_TIME=$(date -u +"%Y.%m.%d_%H:%M:%S")
-CommitHash=N/A
-GoVersion=N/A
-if [[ $(go version) =~ [0-9]+\.[0-9]+\.[0-9]+ ]];
+commit_hash=
+version=${1:-dev}
+go_version=unknown
+
+latest_commit=$(git log -1 --pretty=format:%h || echo 'N/A')
+if [[ latest_commit =~ 'fatal' ]];
then
- GoVersion=${BASH_REMATCH[0]}
+ commit_hash=
+else
+ commit_hash=$latest_commit
fi
-GH=$(git log -1 --pretty=format:%h || echo 'N/A')
-if [[ GH =~ 'fatal' ]];
+if [[ $(go version) =~ [0-9]+\.[0-9]+\.[0-9]+ ]];
then
- CommitHash=N/A
-else
- CommitHash=$GH
+ go_version=${BASH_REMATCH[0]}
fi
-FLAG="-X $TRG_PKG.BuildTime=$BUILD_TIME"
-FLAG="$FLAG -X $TRG_PKG.CommitHash=$CommitHash"
-FLAG="$FLAG -X $TRG_PKG.GoVersion=$GoVersion"
+FLAG="-X $TARGET_PACKAGE.BuildTime=$BUILD_TIME"
+FLAG="$FLAG -X $TARGET_PACKAGE.CommitHash=$commit_hash"
+FLAG="$FLAG -X $TARGET_PACKAGE.Version=$version"
+FLAG="$FLAG -X $TARGET_PACKAGE.GoVersion=$go_version"
-if [[ $1 =~ '-i' ]];
-then
- echo 'go install'
- go install -v -ldflags "$FLAG"
-else
- echo 'go build'
- go build -v -ldflags "$FLAG"
-fi
\ No newline at end of file
+echo "[Go v${go_version}] Building portkey at ${BUILD_TIME} with commit ${commit_hash:-unknown} in version ${version}."
+
+CGO_ENABLED=0 go build -ldflags "${FLAG}"
\ No newline at end of file
diff --git a/internal/build/build.go b/internal/build/build.go
new file mode 100644
index 0000000..c939c61
--- /dev/null
+++ b/internal/build/build.go
@@ -0,0 +1,46 @@
+package build
+
+// Variables that can be injected during build.
+var (
+
+ // BuildTime time when the application was build.
+ BuildTime string = "unknown"
+
+ // CommitHash Git commit hash of the built application.
+ CommitHash string
+
+ // Version version of the built application.
+ Version string = "dev"
+
+ // GoVersion Go version the application was build with.
+ GoVersion string = "unknown"
+)
+
+// BuildDetails struct that contains information about the built application.
+type BuildDetails struct {
+
+ // BuildTime time when the application was build.
+ BuildTime string
+
+ // CommitHash Git commit hash of the built application.
+ CommitHash string
+
+ // Version version of the built application.
+ Version string
+
+ // GoVersion Go version the application was build with.
+ GoVersion string
+}
+
+// B contains the build details of the application.
+var B BuildDetails = newBuildDetails()
+
+// newBuildDetails loads the build details struct B during startup.
+func newBuildDetails() BuildDetails {
+ return BuildDetails{
+ BuildTime: BuildTime,
+ CommitHash: CommitHash,
+ Version: Version,
+ GoVersion: GoVersion,
+ }
+}
diff --git a/internal/components/utils.templ b/internal/components/utils.templ
index a592d64..9d05bf1 100644
--- a/internal/components/utils.templ
+++ b/internal/components/utils.templ
@@ -1,13 +1,14 @@
package components
-import "github.com/kodehat/portkey/internal/types"
+import "github.com/kodehat/portkey/internal/build"
-templ Version(buildDetails types.BuildDetails) {
+templ Version(buildDetails build.BuildDetails) {
- Build time: { buildDetails.BuildTime }
if buildDetails.CommitHash != "" {
- Commit hash: { buildDetails.CommitHash }
}
+ - Version: { buildDetails.Version }
- Go version: { buildDetails.GoVersion }
}
diff --git a/internal/config/config.go b/internal/config/config.go
new file mode 100644
index 0000000..0c7335b
--- /dev/null
+++ b/internal/config/config.go
@@ -0,0 +1,70 @@
+package config
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "github.com/kodehat/portkey/internal/models"
+ "github.com/spf13/viper"
+)
+
+type Config struct {
+ Host string
+ Port int
+ Title string
+ FooterText string
+ SortAlphabetically bool
+ Portals []models.Portal
+ Pages []models.Page
+}
+
+type Flags struct {
+ ConfigPath string
+}
+
+var C Config
+var F Flags
+
+func init() {
+ loadFlags()
+ configPath, err := filepath.Abs(F.ConfigPath)
+ if err != nil {
+ panic(fmt.Errorf("fatal error config file: %w", err))
+ }
+ log.Printf("Looking for config.y[a]ml in: %s\n", configPath)
+ loadConfig(F.ConfigPath)
+}
+
+func loadFlags() {
+ var configPath string
+ flag.StringVar(&configPath, "config-path", ".", "path where config.yml can be found")
+ flag.Parse()
+ F = Flags{
+ ConfigPath: configPath,
+ }
+}
+
+func loadConfig(configPath string) {
+ viper.SetConfigName("config")
+ viper.SetConfigType("yml")
+ viper.AddConfigPath(configPath)
+ viper.SetDefault("host", "localhost")
+ viper.SetDefault("port", "1414")
+ viper.SetDefault("title", "Your Portal")
+ viper.SetDefault("footerText", "Works like a portal.")
+ viper.AutomaticEnv()
+ err := viper.ReadInConfig()
+ if err != nil {
+ panic(fmt.Errorf("fatal error config file: %w", err))
+ }
+ viper.Unmarshal(&C)
+ if C.SortAlphabetically {
+ sort.Slice(C.Portals, func(i, j int) bool {
+ return strings.ToLower(C.Portals[i].Title) < strings.ToLower(C.Portals[j].Title)
+ })
+ }
+}
diff --git a/internal/models/models.go b/internal/models/models.go
new file mode 100644
index 0000000..3fe1920
--- /dev/null
+++ b/internal/models/models.go
@@ -0,0 +1,36 @@
+package models
+
+// Portal struct containing information about a portal.
+// This is used later as a link destination shown to the user.
+type Portal struct {
+
+ // Link destination link of a portal.
+ Link string `json:"link"`
+
+ // Title of a destination link.
+ Title string `json:"title"`
+
+ // Emoji used as a prefix of the title.
+ Emoji string `json:"emoji"`
+
+ // External defines if a destination link opens an external page or a custom page.
+ External bool `json:"external"`
+
+ // Keywords allows defining additional keywords used by the search.
+ // This can make getting reasonable search results a lot easier.
+ Keywords []string `json:"keywords"`
+}
+
+// Page struct defines a custom page that consists of a heading, content and a path,
+// where the page will be available at.
+type Page struct {
+
+ // Heading of the custom page.
+ Heading string `json:"heading"`
+
+ // Content of the custom page interpreted as HTML.
+ Content string `json:"content"`
+
+ // Path of the custom page.
+ Path string `json:"path"`
+}
diff --git a/internal/routes/home_handler.go b/internal/routes/home_handler.go
new file mode 100644
index 0000000..f15e393
--- /dev/null
+++ b/internal/routes/home_handler.go
@@ -0,0 +1,30 @@
+package routes
+
+import (
+ "net/http"
+ "sync"
+
+ "github.com/a-h/templ"
+ "github.com/kodehat/portkey/internal/components"
+ "github.com/kodehat/portkey/internal/config"
+ "github.com/kodehat/portkey/internal/utils"
+)
+
+func homeHandler() http.HandlerFunc {
+ var (
+ init sync.Once
+ allFooterPortals []templ.Component
+ )
+ init.Do(func() {
+ allFooterPortals = getAllFooterPortals()
+ })
+ home := components.HomePage()
+ return func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != "/" {
+ w.WriteHeader(http.StatusNotFound)
+ templ.Handler(components.ContentLayout(utils.PageTitle("404 Not Found", config.C.Title), "404 Not Found", components.NotFound(), allFooterPortals, config.C.FooterText)).ServeHTTP(w, r)
+ return
+ }
+ templ.Handler(components.HomeLayout(config.C.Title, home)).ServeHTTP(w, r)
+ }
+}
diff --git a/internal/routes/page_handler.go b/internal/routes/page_handler.go
new file mode 100644
index 0000000..2948e58
--- /dev/null
+++ b/internal/routes/page_handler.go
@@ -0,0 +1,35 @@
+package routes
+
+import (
+ "net/http"
+ "sync"
+
+ "github.com/a-h/templ"
+ "github.com/kodehat/portkey/internal/components"
+ "github.com/kodehat/portkey/internal/config"
+ "github.com/kodehat/portkey/internal/utils"
+)
+
+type pageHandlerInfo struct {
+ pagePath string
+ handlerFunc func(w http.ResponseWriter, r *http.Request)
+}
+
+func pageHandler() []pageHandlerInfo {
+ var (
+ init sync.Once
+ allFooterPortals []templ.Component
+ )
+ init.Do(func() {
+ allFooterPortals = getAllFooterPortals()
+ })
+
+ var pageHandlerInfos = make([]pageHandlerInfo, len(config.C.Pages))
+ for i, page := range config.C.Pages {
+ pageHandlerInfos[i] = pageHandlerInfo{
+ pagePath: page.Path,
+ handlerFunc: templ.Handler(components.ContentLayout(utils.PageTitle(page.Heading, config.C.Title), page.Heading, components.ContentPage(page.Content), allFooterPortals, config.C.FooterText)).ServeHTTP,
+ }
+ }
+ return pageHandlerInfos
+}
diff --git a/internal/routes/portals_handler.go b/internal/routes/portals_handler.go
new file mode 100644
index 0000000..cbd649c
--- /dev/null
+++ b/internal/routes/portals_handler.go
@@ -0,0 +1,29 @@
+package routes
+
+import (
+ "net/http"
+ "strings"
+
+ "github.com/a-h/templ"
+ "github.com/kodehat/portkey/internal/components"
+ "github.com/kodehat/portkey/internal/config"
+ "github.com/kodehat/portkey/internal/utils"
+)
+
+func portalsHandler() http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ query := r.URL.Query().Get("search")
+ var allHomePortals = make([]templ.Component, 0)
+ for _, configPortal := range config.C.Portals {
+ portal := components.HomePortal(configPortal.Link, configPortal.Emoji, configPortal.Title, configPortal.External)
+ if query != "" {
+ if strings.Contains(configPortal.Title, query) || utils.ArrSubStr(configPortal.Keywords, query) {
+ allHomePortals = append(allHomePortals, portal)
+ }
+ } else {
+ allHomePortals = append(allHomePortals, portal)
+ }
+ }
+ components.PortalPartial(allHomePortals).Render(r.Context(), w)
+ }
+}
diff --git a/internal/routes/rest_handler.go b/internal/routes/rest_handler.go
new file mode 100644
index 0000000..699cb7f
--- /dev/null
+++ b/internal/routes/rest_handler.go
@@ -0,0 +1,24 @@
+package routes
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/kodehat/portkey/internal/config"
+)
+
+func portalsRestHandler() http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(config.C.Portals)
+ }
+}
+
+func pagesRestHandler() http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(config.C.Pages)
+ }
+}
diff --git a/internal/routes/routes.go b/internal/routes/routes.go
new file mode 100644
index 0000000..07ae3fc
--- /dev/null
+++ b/internal/routes/routes.go
@@ -0,0 +1,41 @@
+package routes
+
+import (
+ "embed"
+ "net/http"
+
+ "github.com/a-h/templ"
+ "github.com/kodehat/portkey/internal/components"
+ "github.com/kodehat/portkey/internal/config"
+)
+
+func AddRoutes(mux *http.ServeMux, static embed.FS) {
+ // Home
+ mux.HandleFunc("/", homeHandler())
+
+ // Custom pages
+ for _, pageHandler := range pageHandler() {
+ mux.HandleFunc(pageHandler.pagePath, pageHandler.handlerFunc)
+ }
+
+ // Fix pages
+ mux.HandleFunc("/version", versionHandler())
+
+ // Static
+ mux.HandleFunc("/static/", staticHandler(static))
+
+ // htmx
+ mux.HandleFunc("/_/portals", portalsHandler())
+
+ // REST
+ mux.HandleFunc("/api/portals", portalsRestHandler())
+ mux.HandleFunc("/api/pages", pagesRestHandler())
+}
+
+func getAllFooterPortals() []templ.Component {
+ var allFooterPortals = make([]templ.Component, len(config.C.Portals))
+ for i, configPortal := range config.C.Portals {
+ allFooterPortals[i] = components.FooterPortal(configPortal.Link, configPortal.Emoji, configPortal.Title, configPortal.External)
+ }
+ return allFooterPortals
+}
diff --git a/internal/routes/static_handler.go b/internal/routes/static_handler.go
new file mode 100644
index 0000000..72a739c
--- /dev/null
+++ b/internal/routes/static_handler.go
@@ -0,0 +1,12 @@
+package routes
+
+import (
+ "embed"
+ "net/http"
+
+ "github.com/kodehat/portkey/internal/utils"
+)
+
+func staticHandler(static embed.FS) http.HandlerFunc {
+ return utils.StaticHandler(http.FileServer(http.FS(static)))
+}
diff --git a/internal/routes/version_handler.go b/internal/routes/version_handler.go
new file mode 100644
index 0000000..ef18608
--- /dev/null
+++ b/internal/routes/version_handler.go
@@ -0,0 +1,24 @@
+package routes
+
+import (
+ "net/http"
+ "sync"
+
+ "github.com/a-h/templ"
+ "github.com/kodehat/portkey/internal/build"
+ "github.com/kodehat/portkey/internal/components"
+ "github.com/kodehat/portkey/internal/config"
+ "github.com/kodehat/portkey/internal/utils"
+)
+
+func versionHandler() http.HandlerFunc {
+ var (
+ init sync.Once
+ allFooterPortals []templ.Component
+ )
+ init.Do(func() {
+ allFooterPortals = getAllFooterPortals()
+ })
+
+ return templ.Handler(components.ContentLayout(utils.PageTitle("Version", config.C.Title), "Version", components.Version(build.B), allFooterPortals, config.C.FooterText)).ServeHTTP
+}
diff --git a/internal/types/build.go b/internal/types/build.go
deleted file mode 100644
index 748e800..0000000
--- a/internal/types/build.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package types
-
-type BuildDetails struct {
- BuildTime string
- CommitHash string
- GoVersion string
-}
diff --git a/internal/types/settings.go b/internal/types/settings.go
deleted file mode 100644
index 748ade5..0000000
--- a/internal/types/settings.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package types
-
-type Portal struct {
- Link string `json:"link"`
- Title string `json:"title"`
- Emoji string `json:"emoji"`
- External bool `json:"external"`
- Keywords []string `json:"keywords"`
-}
-
-type Page struct {
- Heading string `json:"heading"`
- Path string `json:"path"`
- Content string `json:"content"`
-}
-
-type Config struct {
- Host string
- Port int
- Title string
- FooterText string
- SortAlphabetically bool
- Portals []Portal
- Pages []Page
-}
-
-type Flags struct {
- ConfigPath string
-}
diff --git a/static.go b/internal/utils/handler.go
similarity index 84%
rename from static.go
rename to internal/utils/handler.go
index 3e38149..c705301 100644
--- a/static.go
+++ b/internal/utils/handler.go
@@ -1,4 +1,4 @@
-package main
+package utils
import (
"net/http"
@@ -23,8 +23,8 @@ func (w *NotFoundRespWr) Write(p []byte) (int, error) {
return len(p), nil // Lie that we successfully written it
}
-// staticHandler Redirect to 404 page if static file not found https://stackoverflow.com/a/47286697
-func staticHandler(h http.Handler) http.HandlerFunc {
+// StaticHandler Redirect to 404 page if static file not found https://stackoverflow.com/a/47286697
+func StaticHandler(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
nfrw := &NotFoundRespWr{ResponseWriter: w}
h.ServeHTTP(nfrw, r)
diff --git a/internal/utils/http.go b/internal/utils/http.go
new file mode 100644
index 0000000..9d29c10
--- /dev/null
+++ b/internal/utils/http.go
@@ -0,0 +1,7 @@
+package utils
+
+import "fmt"
+
+func BuildAddr(host string, port int) string {
+ return fmt.Sprintf("%s:%d", host, port)
+}
diff --git a/main.go b/main.go
index c897572..24b8f55 100644
--- a/main.go
+++ b/main.go
@@ -1,139 +1,37 @@
package main
import (
+ "context"
"embed"
- "encoding/json"
- "flag"
"fmt"
+ "io"
"log"
"net/http"
- "path/filepath"
- "sort"
- "strings"
+ "os"
- "github.com/a-h/templ"
- "github.com/kodehat/portkey/internal/components"
- "github.com/kodehat/portkey/internal/types"
+ "github.com/kodehat/portkey/internal/config"
+ "github.com/kodehat/portkey/internal/routes"
"github.com/kodehat/portkey/internal/utils"
- "github.com/spf13/viper"
)
+const LOGGER_PREFIX string = "[portkey] "
+
//go:embed static
var static embed.FS
-// Injected during build.
-var (
- BuildTime string = "N/A"
- CommitHash string
- GoVersion string = "N/A"
-)
-
-var C types.Config
-var F types.Flags
-var B types.BuildDetails
-
func main() {
- loadBuildDetails()
- loadFlags()
- configPath, err := filepath.Abs(F.ConfigPath)
- if err != nil {
- panic(fmt.Errorf("fatal error config file: %w", err))
- }
- log.Printf("Looking for config.y[a]ml in: %s\n", configPath)
- loadConfig(configPath)
-
- if C.SortAlphabetically {
- sort.Slice(C.Portals, func(i, j int) bool {
- return strings.ToLower(C.Portals[i].Title) < strings.ToLower(C.Portals[j].Title)
- })
- }
- var allFooterPortals = make([]templ.Component, len(C.Portals))
- for i, configPortal := range C.Portals {
- allFooterPortals[i] = components.FooterPortal(configPortal.Link, configPortal.Emoji, configPortal.Title, configPortal.External)
+ ctx := context.Background()
+ if err := run(ctx, config.C, os.Stdin, os.Stdout, os.Stderr); err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ os.Exit(1)
}
- home := components.HomePage()
-
- http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/" {
- w.WriteHeader(http.StatusNotFound)
- templ.Handler(components.ContentLayout(utils.PageTitle("404 Not Found", C.Title), "404 Not Found", components.NotFound(), allFooterPortals, C.FooterText)).ServeHTTP(w, r)
- return
- }
- templ.Handler(components.HomeLayout(C.Title, home)).ServeHTTP(w, r)
- })
-
- for _, page := range C.Pages {
- http.Handle(page.Path, templ.Handler(components.ContentLayout(utils.PageTitle(page.Heading, C.Title), page.Heading, components.ContentPage(page.Content), allFooterPortals, C.FooterText)))
- }
- http.Handle("/version", templ.Handler(components.ContentLayout(utils.PageTitle("Version", C.Title), "Version", components.Version(B), allFooterPortals, C.FooterText)))
- http.Handle("/static/", staticHandler(http.FileServer(http.FS(static))))
-
- http.Handle("/_/portals", http.HandlerFunc(returnSearchedPortals))
-
- http.Handle("/api/v1/portals", http.HandlerFunc(returnPortalsAsJson))
- http.Handle("/api/v1/pages", http.HandlerFunc(returnPagessAsJson))
-
- log.Printf("Listening on %s:%d\n", C.Host, C.Port)
- http.ListenAndServe(fmt.Sprintf("%s:%d", C.Host, C.Port), nil)
}
-func returnSearchedPortals(w http.ResponseWriter, r *http.Request) {
- query := r.URL.Query().Get("search")
- var allHomePortals = make([]templ.Component, 0)
- for _, configPortal := range C.Portals {
- portal := components.HomePortal(configPortal.Link, configPortal.Emoji, configPortal.Title, configPortal.External)
- if query != "" {
- if strings.Contains(configPortal.Title, query) || utils.ArrSubStr(configPortal.Keywords, query) {
- allHomePortals = append(allHomePortals, portal)
- }
- } else {
- allHomePortals = append(allHomePortals, portal)
- }
- }
- components.PortalPartial(allHomePortals).Render(r.Context(), w)
-}
-
-func returnPortalsAsJson(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusOK)
- json.NewEncoder(w).Encode(C.Portals)
-}
-
-func returnPagessAsJson(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusOK)
- json.NewEncoder(w).Encode(C.Pages)
-}
-
-func loadBuildDetails() {
- B = types.BuildDetails{
- BuildTime: BuildTime,
- CommitHash: CommitHash,
- GoVersion: GoVersion,
- }
-}
-
-func loadConfig(configPath string) {
- viper.SetConfigName("config")
- viper.SetConfigType("yml")
- viper.AddConfigPath(configPath)
- viper.SetDefault("host", "localhost")
- viper.SetDefault("port", "1414")
- viper.SetDefault("title", "Your Portal")
- viper.SetDefault("footerText", "Works like a portal.")
- viper.SafeWriteConfig()
- err := viper.ReadInConfig()
- if err != nil {
- panic(fmt.Errorf("fatal error config file: %w", err))
- }
- viper.Unmarshal(&C)
-}
-
-func loadFlags() {
- var configPath string
- flag.StringVar(&configPath, "config-path", ".", "path where config.yml can be found")
- flag.Parse()
- F = types.Flags{
- ConfigPath: configPath,
- }
+func run(ctx context.Context, c config.Config, stdin io.Reader, stdout, stderr io.Writer) error {
+ logger := log.New(stdout, LOGGER_PREFIX, log.Lmsgprefix|log.Ldate|log.Ltime)
+ mux := http.NewServeMux()
+ routes.AddRoutes(mux, static)
+ addr := utils.BuildAddr(config.C.Host, config.C.Port)
+ logger.Printf("Listening on %s\n", addr)
+ return http.ListenAndServe(addr, mux)
}