command line arguments

This commit is contained in:
Robert Janetzko 2022-05-09 15:20:31 +00:00
parent aed21a1e95
commit 375eab8e22
21 changed files with 183 additions and 111 deletions

1
backend/cmd/root.go Normal file
View File

@ -0,0 +1 @@
package cmd

View File

@ -9,6 +9,7 @@ require (
github.com/iancoleman/strcase v0.2.0 github.com/iancoleman/strcase v0.2.0
github.com/pkg/profile v1.6.0 github.com/pkg/profile v1.6.0
github.com/shirou/gopsutil v3.21.11+incompatible github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/cobra v1.4.0
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 golang.org/x/exp v0.0.0-20220428152302-39d4317da171
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9
) )
@ -17,10 +18,12 @@ require (
github.com/VividCortex/ewma v1.1.1 // indirect github.com/VividCortex/ewma v1.1.1 // indirect
github.com/fatih/color v1.10.0 // indirect github.com/fatih/color v1.10.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mattn/go-runewidth v0.0.12 // indirect github.com/mattn/go-runewidth v0.0.12 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
) )

View File

@ -2,6 +2,7 @@ github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdc
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA=
github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
@ -12,6 +13,8 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
@ -23,8 +26,13 @@ github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdL
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4= golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4=
@ -39,3 +47,5 @@ golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16C
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View File

@ -2,62 +2,89 @@ package main
import ( import (
"embed" "embed"
"flag"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
_ "net/http/pprof" _ "net/http/pprof"
"os"
"runtime" "runtime"
"github.com/pkg/profile" "github.com/pkg/profile"
"github.com/robertjanetzko/LegendsBrowser2/backend/model" "github.com/robertjanetzko/LegendsBrowser2/backend/model"
"github.com/robertjanetzko/LegendsBrowser2/backend/server" "github.com/robertjanetzko/LegendsBrowser2/backend/server"
"github.com/robertjanetzko/LegendsBrowser2/backend/templates" "github.com/robertjanetzko/LegendsBrowser2/backend/templates"
"github.com/spf13/cobra"
) )
//go:embed static //go:embed static
var static embed.FS var static embed.FS
func main() { var (
f := flag.String("f", "", "open a file") f, c, subUri string
p := flag.Bool("p", false, "start profiling") l, p, d, s *bool
c := flag.String("c", "", "config file") port *int
l := flag.Bool("l", false, "open last file") )
flag.Parse()
if *p { var rootCmd = &cobra.Command{
defer profile.Start(profile.ProfilePath(".")).Stop() Use: "legendsbrowser",
go func() { Short: "A Legends Browser for Dwarf Fortress",
http.ListenAndServe(":8081", nil) Run: func(cmd *cobra.Command, args []string) {
}() if *p {
} defer profile.Start(profile.ProfilePath(".")).Stop()
go func() {
config, err := server.LoadConfig(*c) http.ListenAndServe(":8081", nil)
if err != nil { }()
log.Fatal(err)
}
server.DebugJSON = config.DebugJSON
templates.DebugTemplates = config.DebugTemplates
var world *model.DfWorld
if *l {
*f = config.LastFile
}
if len(*f) > 0 {
w, err := model.Parse(*f, nil)
if err != nil {
fmt.Println(err)
} else {
runtime.GC()
world = w
} }
}
err = server.StartServer(config, world, static) config, err := server.LoadConfig(c)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
}
templates.DebugTemplates = config.DebugTemplates
server.DebugJSON = *d
config.Port = *port
config.ServerMode = *s
config.SubUri = subUri
var world *model.DfWorld
if *l {
f = config.LastFile
}
if len(f) > 0 {
w, err := model.Parse(f, nil)
if err != nil {
fmt.Println(err)
} else {
runtime.GC()
world = w
}
}
err = server.StartServer(config, world, static)
if err != nil {
log.Fatal(err)
}
},
}
func main() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
} }
} }
func init() {
rootCmd.PersistentFlags().StringVarP(&f, "world", "w", "", "path to legends.xml")
rootCmd.PersistentFlags().StringVarP(&c, "config", "c", "", "config file")
rootCmd.PersistentFlags().StringVarP(&subUri, "subUri", "u", "", "run on /<subUri>")
l = rootCmd.PersistentFlags().BoolP("last", "l", false, "open last file")
p = rootCmd.PersistentFlags().BoolP("profile", "P", false, "start profiling")
d = rootCmd.PersistentFlags().BoolP("debug", "d", false, "show debug data")
s = rootCmd.PersistentFlags().BoolP("serverMode", "s", false, "run in server mode (disables file chooser)")
port = rootCmd.PersistentFlags().IntP("port", "p", 58881, "use specific port")
}

View File

@ -9,7 +9,7 @@ import (
) )
func (x *HistoricalEventCollection) Link(s string) string { func (x *HistoricalEventCollection) Link(s string) string {
return fmt.Sprintf(`<a class="collection %s" href="/collection/%d">%s</a>`, strcase.ToKebab(x.Details.Type()), x.Id_, util.Title(s)) return fmt.Sprintf(`<a class="collection %s" href="./collection/%d">%s</a>`, strcase.ToKebab(x.Details.Type()), x.Id_, util.Title(s))
} }
func (x *HistoricalEventCollection) ParentId() int { func (x *HistoricalEventCollection) ParentId() int {

View File

@ -57,7 +57,7 @@ func (c *Context) hfUnrelated(id int) string {
func (c *Context) hfShort(id int) string { func (c *Context) hfShort(id int) string {
if x, ok := c.World.HistoricalFigures[id]; ok { if x, ok := c.World.HistoricalFigures[id]; ok {
return fmt.Sprintf(`<a class="hf" href="/hf/%d">%s%s</a>`, x.Id(), hfIcon(x), util.Title(x.FirstName())) return fmt.Sprintf(`<a class="hf" href="./hf/%d">%s%s</a>`, x.Id(), hfIcon(x), util.Title(x.FirstName()))
} }
return "UNKNOWN HISTORICAL FIGURE" return "UNKNOWN HISTORICAL FIGURE"
} }
@ -73,7 +73,7 @@ func (c *Context) hfRelated(id, to int) string {
if x, ok := c.World.HistoricalFigures[id]; ok { if x, ok := c.World.HistoricalFigures[id]; ok {
if t, ok := c.World.HistoricalFigures[to]; ok { if t, ok := c.World.HistoricalFigures[to]; ok {
if y, ok := util.Find(t.HfLink, func(l *HfLink) bool { return l.Hfid == id }); ok { if y, ok := util.Find(t.HfLink, func(l *HfLink) bool { return l.Hfid == id }); ok {
return fmt.Sprintf(`%s %s <a class="hf" href="/hf/%d">%s%s</a>`, t.PossesivePronoun(), y.LinkType, x.Id(), hfIcon(x), util.Title(x.Name())) return fmt.Sprintf(`%s %s <a class="hf" href="./hf/%d">%s%s</a>`, t.PossesivePronoun(), y.LinkType, x.Id(), hfIcon(x), util.Title(x.Name()))
} }
} }
return hf(x) return hf(x)
@ -121,7 +121,7 @@ func hf(x *HistoricalFigure) string {
if x.Vampire { if x.Vampire {
r += " vampire" r += " vampire"
} }
return fmt.Sprintf(`the %s <a class="hf" href="/hf/%d">%s%s</a>`, r, x.Id(), hfIcon(x), util.Title(x.Name())) return fmt.Sprintf(`the %s <a class="hf" href="./hf/%d">%s%s</a>`, r, x.Id(), hfIcon(x), util.Title(x.Name()))
} }
func (c *Context) hfList(ids []int) string { func (c *Context) hfList(ids []int) string {
@ -134,7 +134,7 @@ func (c *Context) hfListRelated(ids []int, to int) string {
func (c *Context) artifact(id int) string { func (c *Context) artifact(id int) string {
if x, ok := c.World.Artifacts[id]; ok { if x, ok := c.World.Artifacts[id]; ok {
return fmt.Sprintf(`<a class="artifact" href="/artifact/%d"><i class="%s fa-xs"></i> %s</a>`, x.Id(), x.Icon(), util.Title(x.Name())) return fmt.Sprintf(`<a class="artifact" href="./artifact/%d"><i class="%s fa-xs"></i> %s</a>`, x.Id(), x.Icon(), util.Title(x.Name()))
} }
return "UNKNOWN ARTIFACT" return "UNKNOWN ARTIFACT"
} }
@ -145,7 +145,7 @@ func (c *Context) entity(id int) string {
if c != "" { if c != "" {
c = fmt.Sprintf(` style="color:%s"`, c) c = fmt.Sprintf(` style="color:%s"`, c)
} }
return fmt.Sprintf(`<a class="entity" href="/entity/%d"><i class="%s fa-xs" %s></i> %s</a>`, x.Id(), x.Icon(), c, util.Title(x.Name())) return fmt.Sprintf(`<a class="entity" href="./entity/%d"><i class="%s fa-xs" %s></i> %s</a>`, x.Id(), x.Icon(), c, util.Title(x.Name()))
} }
return "UNKNOWN ENTITY" return "UNKNOWN ENTITY"
} }
@ -179,7 +179,7 @@ func (c *Context) siteStructure(siteId, structureId int, prefix string) string {
func (c *Context) site(id int, prefix string) string { func (c *Context) site(id int, prefix string) string {
if x, ok := c.World.Sites[id]; ok { if x, ok := c.World.Sites[id]; ok {
return fmt.Sprintf(`%s <a class="site" href="/site/%d"><i class="%s fa-xs%s"></i> %s</a>`, prefix, x.Id(), x.Icon(), util.If(x.Ruin, " ruin", ""), util.Title(x.Name())) return fmt.Sprintf(`%s <a class="site" href="./site/%d"><i class="%s fa-xs%s"></i> %s</a>`, prefix, x.Id(), x.Icon(), util.If(x.Ruin, " ruin", ""), util.Title(x.Name()))
} }
return "UNKNOWN SITE" return "UNKNOWN SITE"
} }
@ -187,7 +187,7 @@ func (c *Context) site(id int, prefix string) string {
func (c *Context) structure(siteId, structureId int) string { func (c *Context) structure(siteId, structureId int) string {
if x, ok := c.World.Sites[siteId]; ok { if x, ok := c.World.Sites[siteId]; ok {
if y, ok := x.Structures[structureId]; ok { if y, ok := x.Structures[structureId]; ok {
return fmt.Sprintf(`<a class="structure" href="/site/%d/structure/%d"><i class="%s fa-xs%s"></i> %s</a>`, siteId, structureId, y.Icon(), util.If(y.Ruin, " ruin", ""), util.Title(y.Name())) return fmt.Sprintf(`<a class="structure" href="./site/%d/structure/%d"><i class="%s fa-xs%s"></i> %s</a>`, siteId, structureId, y.Icon(), util.If(y.Ruin, " ruin", ""), util.Title(y.Name()))
} }
} }
return "UNKNOWN STRUCTURE" return "UNKNOWN STRUCTURE"
@ -207,7 +207,7 @@ func (c *Context) property(siteId, propertyId int) string {
func (c *Context) region(id int) string { func (c *Context) region(id int) string {
if x, ok := c.World.Regions[id]; ok { if x, ok := c.World.Regions[id]; ok {
return fmt.Sprintf(`<a class="region" href="/region/%d">%s</a>`, x.Id(), util.Title(x.Name())) return fmt.Sprintf(`<a class="region" href="./region/%d">%s</a>`, x.Id(), util.Title(x.Name()))
} }
return "UNKNOWN REGION" return "UNKNOWN REGION"
} }
@ -234,7 +234,7 @@ func (c *Context) place(structureId, siteId int, sitePrefix string, regionId int
func (c *Context) mountain(id int) string { func (c *Context) mountain(id int) string {
if x, ok := c.World.MountainPeaks[id]; ok { if x, ok := c.World.MountainPeaks[id]; ok {
return fmt.Sprintf(`<a class="mountain" href="/mountain/%d"><i class="fa-solid %s"></i> %s</a>`, return fmt.Sprintf(`<a class="mountain" href="./mountain/%d"><i class="fa-solid %s"></i> %s</a>`,
x.Id(), util.If(x.IsVolcano, "fa-volcano", "fa-mountain"), util.Title(x.Name())) x.Id(), util.If(x.IsVolcano, "fa-volcano", "fa-mountain"), util.Title(x.Name()))
} }
return "UNKNOWN MOUNTAIN" return "UNKNOWN MOUNTAIN"
@ -242,61 +242,61 @@ func (c *Context) mountain(id int) string {
func (c *Context) landmass(id int) string { func (c *Context) landmass(id int) string {
if x, ok := c.World.Landmasses[id]; ok { if x, ok := c.World.Landmasses[id]; ok {
return fmt.Sprintf(`<a class="landmass" href="/landmass/%d">%s</a>`, x.Id(), util.Title(x.Name())) return fmt.Sprintf(`<a class="landmass" href="./landmass/%d">%s</a>`, x.Id(), util.Title(x.Name()))
} }
return "UNKNOWN LANDMASS" return "UNKNOWN LANDMASS"
} }
func (c *Context) river(id int) string { func (c *Context) river(id int) string {
x := c.World.Rivers[id] x := c.World.Rivers[id]
return fmt.Sprintf(`<a class="river" href="/river/%d">%s</a>`, id, util.Title(x.Name())) return fmt.Sprintf(`<a class="river" href="./river/%d">%s</a>`, id, util.Title(x.Name()))
} }
func (c *Context) identity(id int) string { func (c *Context) identity(id int) string {
if x, ok := c.World.Identities[id]; ok { if x, ok := c.World.Identities[id]; ok {
return fmt.Sprintf(`<a class="identity" href="/identity/%d">%s</a>`, x.Id(), util.Title(x.Name())) return fmt.Sprintf(`<a class="identity" href="./identity/%d">%s</a>`, x.Id(), util.Title(x.Name()))
} }
return "UNKNOWN IDENTITY" return "UNKNOWN IDENTITY"
} }
func (c *Context) fullIdentity(id int) string { func (c *Context) fullIdentity(id int) string {
if x, ok := c.World.Identities[id]; ok { if x, ok := c.World.Identities[id]; ok {
return fmt.Sprintf(`&quot;the %s <a class="identity" href="/identity/%d">%s</a> of %s&quot;`, x.Profession.String(), x.Id(), util.Title(x.Name()), c.entity(x.EntityId)) return fmt.Sprintf(`&quot;the %s <a class="identity" href="./identity/%d">%s</a> of %s&quot;`, x.Profession.String(), x.Id(), util.Title(x.Name()), c.entity(x.EntityId))
} }
return "UNKNOWN IDENTITY" return "UNKNOWN IDENTITY"
} }
func (c *Context) danceForm(id int) string { func (c *Context) danceForm(id int) string {
if x, ok := c.World.DanceForms[id]; ok { if x, ok := c.World.DanceForms[id]; ok {
return fmt.Sprintf(`<a class="artform" href="/danceform/%d"><i class="fa-solid fa-shoe-prints fa-xs"></i> %s</a>`, id, util.Title(x.Name())) return fmt.Sprintf(`<a class="artform" href="./danceform/%d"><i class="fa-solid fa-shoe-prints fa-xs"></i> %s</a>`, id, util.Title(x.Name()))
} }
return "UNKNOWN DANCE FORM" return "UNKNOWN DANCE FORM"
} }
func (c *Context) musicalForm(id int) string { func (c *Context) musicalForm(id int) string {
if x, ok := c.World.MusicalForms[id]; ok { if x, ok := c.World.MusicalForms[id]; ok {
return fmt.Sprintf(`<a class="artform" href="/musicalform/%d"><i class="fa-solid fa-music fa-xs"></i> %s</a>`, id, util.Title(x.Name())) return fmt.Sprintf(`<a class="artform" href="./musicalform/%d"><i class="fa-solid fa-music fa-xs"></i> %s</a>`, id, util.Title(x.Name()))
} }
return "UNKNOWN MUSICAL FORM" return "UNKNOWN MUSICAL FORM"
} }
func (c *Context) poeticForm(id int) string { func (c *Context) poeticForm(id int) string {
if x, ok := c.World.PoeticForms[id]; ok { if x, ok := c.World.PoeticForms[id]; ok {
return fmt.Sprintf(`<a class="artform" href="/poeticform/%d"><i class="fa-solid fa-comment-dots fa-xs"></i> %s</a>`, id, util.Title(x.Name())) return fmt.Sprintf(`<a class="artform" href="./poeticform/%d"><i class="fa-solid fa-comment-dots fa-xs"></i> %s</a>`, id, util.Title(x.Name()))
} }
return "UNKNOWN POETIC FORM" return "UNKNOWN POETIC FORM"
} }
func (c *Context) worldConstruction(id int) string { func (c *Context) worldConstruction(id int) string {
if x, ok := c.World.WorldConstructions[id]; ok { if x, ok := c.World.WorldConstructions[id]; ok {
return fmt.Sprintf(`<a class="worldconstruction" href="/worldconstruction/%d"><i class="%s fa-xs"></i> %s</a>`, id, x.Icon(), util.Title(x.Name())) return fmt.Sprintf(`<a class="worldconstruction" href="./worldconstruction/%d"><i class="%s fa-xs"></i> %s</a>`, id, x.Icon(), util.Title(x.Name()))
} }
return "UNKNOWN WORLD CONSTRUCTION" return "UNKNOWN WORLD CONSTRUCTION"
} }
func (c *Context) writtenContent(id int) string { func (c *Context) writtenContent(id int) string {
if x, ok := c.World.WrittenContents[id]; ok { if x, ok := c.World.WrittenContents[id]; ok {
return fmt.Sprintf(`<a class="writtencontent" href="/writtencontent/%d">%s</a>`, id, util.Title(x.Name())) return fmt.Sprintf(`<a class="writtencontent" href="./writtencontent/%d">%s</a>`, id, util.Title(x.Name()))
} }
return "UNKNOWN WORLD CONSTRUCTION" return "UNKNOWN WORLD CONSTRUCTION"
} }

View File

@ -12,8 +12,11 @@ type Config struct {
path string path string
LastPath string LastPath string
LastFile string LastFile string
DebugTemplates bool `json:"DebugTemplates,omitempty"` Port int `json:"-"`
DebugJSON bool `json:"DebugJSON,omitempty"` ServerMode bool `json:"-"`
SubUri string `json:"-"`
DebugTemplates bool `json:"DebugTemplates,omitempty"`
DebugJSON bool `json:"DebugJSON,omitempty"`
} }
func LoadConfig(path string) (*Config, error) { func LoadConfig(path string) (*Config, error) {

View File

@ -48,6 +48,14 @@ func (h loadHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
if h.server.context.config.ServerMode {
err := h.server.templates.Render(w, "serverMode.html", nil)
if err != nil {
httpError(w, err)
}
return
}
var partitions []string var partitions []string
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
ps, _ := disk.Partitions(false) ps, _ := disk.Partitions(false)
@ -95,11 +103,11 @@ func (h loadHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.server.context.isLoading = true h.server.context.isLoading = true
h.server.context.world = nil h.server.context.world = nil
go loadWorld(h.server, p.Current) go loadWorld(h.server, p.Current)
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, h.server.context.config.SubUri+"/", http.StatusSeeOther)
return return
} }
} }
http.Redirect(w, r, "/load", http.StatusSeeOther) http.Redirect(w, r, h.server.context.config.SubUri+"/load", http.StatusSeeOther)
} }
func isLegendsXml(f fs.FileInfo) bool { func isLegendsXml(f fs.FileInfo) bool {
@ -126,6 +134,6 @@ func (srv *DfServer) renderLoading(w http.ResponseWriter, r *http.Request) {
httpError(w, err) httpError(w, err)
} }
} else { } else {
http.Redirect(w, r, "/load", http.StatusSeeOther) http.Redirect(w, r, srv.context.config.SubUri+"/load", http.StatusSeeOther)
} }
} }

View File

@ -39,6 +39,12 @@ func StartServer(config *Config, world *model.DfWorld, static embed.FS) error {
progress: &model.LoadProgress{}, progress: &model.LoadProgress{},
}, },
} }
root := srv.router
if srv.context.config.SubUri != "" {
srv.router = srv.router.PathPrefix("/legends").Subrouter()
}
srv.loader = &loadHandler{server: srv} srv.loader = &loadHandler{server: srv}
srv.LoadTemplates() srv.LoadTemplates()
@ -196,10 +202,8 @@ func StartServer(config *Config, world *model.DfWorld, static embed.FS) error {
} }
srv.router.PathPrefix("/").Handler(spa) srv.router.PathPrefix("/").Handler(spa)
OpenBrowser("http://localhost:8080") OpenBrowser(fmt.Sprintf("http://localhost:%d", config.Port))
http.ListenAndServe(fmt.Sprintf(":%d", config.Port), root)
fmt.Println("Serving at :8080")
http.ListenAndServe(":8080", srv.router)
return nil return nil
} }

View File

@ -6,6 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
"strings"
) )
type spaHandler struct { type spaHandler struct {
@ -18,6 +19,7 @@ type spaHandler struct {
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// get the absolute path to prevent directory traversal // get the absolute path to prevent directory traversal
path := r.URL.Path path := r.URL.Path
path = strings.TrimPrefix(path, h.server.context.config.SubUri)
// if err != nil { // if err != nil {
// // if we failed to get the absolute path respond with a 400 bad request and stop // // if we failed to get the absolute path respond with a 400 bad request and stop
// http.Error(w, err.Error(), http.StatusBadRequest) // http.Error(w, err.Error(), http.StatusBadRequest)
@ -26,8 +28,11 @@ func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// prepend the path with the path to the static directory // prepend the path with the path to the static directory
path = h.staticPath + path path = h.staticPath + path
fmt.Println(path)
_, err := h.staticFS.Open(path) _, err := h.staticFS.Open(path)
if os.IsNotExist(err) { if os.IsNotExist(err) {
fmt.Println("ERR")
// file does not exist, serve index.html // file does not exist, serve index.html
fmt.Println(path) fmt.Println(path)
file, err := h.staticFS.Open(h.staticPath + "/" + h.indexPath) file, err := h.staticFS.Open(h.staticPath + "/" + h.indexPath)
@ -57,5 +62,5 @@ func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
// otherwise, use http.FileServer to serve the static dir // otherwise, use http.FileServer to serve the static dir
http.FileServer(http.FS(statics)).ServeHTTP(w, r) http.StripPrefix(h.server.context.config.SubUri, http.FileServer(http.FS(statics))).ServeHTTP(w, r)
} }

View File

@ -32,10 +32,11 @@ func (srv *DfServer) LoadTemplates() {
"title": util.Title, "title": util.Title,
"kebab": func(s string) string { return strcase.ToKebab(s) }, "kebab": func(s string) string { return strcase.ToKebab(s) },
"andList": model.AndList, "andList": model.AndList,
"suburi": func() string { return srv.context.config.SubUri },
"world": func() *model.DfWorld { return srv.context.world }, "world": func() *model.DfWorld { return srv.context.world },
"context": func(r any) *model.Context { return model.NewContext(srv.context.world, r) }, "context": func(r any) *model.Context { return model.NewContext(srv.context.world, r) },
"initMap": func() template.HTML { "initMap": func() template.HTML {
return template.HTML(fmt.Sprintf(`<script>var worldWidth = %d, worldHeight = %d;</script><script src="/js/map.js"></script>`, return template.HTML(fmt.Sprintf(`<script>var worldWidth = %d, worldHeight = %d;</script><script src="./js/map.js"></script>`,
srv.context.world.Width, srv.context.world.Height)) srv.context.world.Width, srv.context.world.Height))
}, },
"hf": func(id int) template.HTML { return model.LinkHf(srv.context.world, id) }, "hf": func(id int) template.HTML { return model.LinkHf(srv.context.world, id) },

View File

@ -21,7 +21,7 @@ func OpenBrowser(url string) {
} }
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
fmt.Println("navigate to http://localhost:8080 in your browser") fmt.Println("navigate to " + url + " in your browser")
} }
} }

View File

@ -49,7 +49,7 @@ map.fitBounds(bounds);
map.options.minZoom = map.getZoom(); map.options.minZoom = map.getZoom();
var imageUrl = '/map' var imageUrl = './map'
var imageBounds = [[0, 0], var imageBounds = [[0, 0],
[worldWidth, worldHeight]]; [worldWidth, worldHeight]];
@ -106,7 +106,7 @@ function attachTooltip(layer, tip) {
function urlToolTip(type, id) { function urlToolTip(type, id) {
return function (layer) { return function (layer) {
return $.ajax({ return $.ajax({
url: "/popover/" + type + "/" + id, url: "./popover/" + type + "/" + id,
async: false async: false
}).responseText; }).responseText;
} }

View File

@ -5,7 +5,7 @@
{{define "content"}} {{define "content"}}
<ul> <ul>
{{- range $t := . }} {{- range $t := . }}
<li><a href="/events/{{ $t }}">{{ $t }}</a></li> <li><a href="./events/{{ $t }}">{{ $t }}</a></li>
{{- end }} {{- end }}
</ul> </ul>
{{- end }} {{- end }}

View File

@ -3,7 +3,7 @@
<li data-event-id="{{ $event.Id }}"> <li data-event-id="{{ $event.Id }}">
In {{ time $event.Year $event.Seconds72 }}, In {{ time $event.Year $event.Seconds72 }},
{{ html ($event.Details.Html ($.Context.WithEvent $event)) }} {{ html ($event.Details.Html ($.Context.WithEvent $event)) }}
{{ if ne .Collection -1 }} <a class="collection" href="/collection/{{.Collection}}"><i {{ if ne .Collection -1 }} <a class="collection" href="./collection/{{.Collection}}"><i
class="fa-solid fa-magnifying-glass fa-xs"></i></a>{{end}} class="fa-solid fa-magnifying-glass fa-xs"></i></a>{{end}}
{{ json $event.Details }} {{ json $event.Details }}
</li> </li>

View File

@ -18,7 +18,7 @@
</tr> </tr>
{{- range .Hfs }}{{- if not (eq .Name "") }} {{- range .Hfs }}{{- if not (eq .Name "") }}
<tr> <tr>
<td><a class="hf" href="/hf/{{.Id}}">{{ title .Name }}</a></td> <td><a class="hf" href="./hf/{{.Id}}">{{ title .Name }}</a></td>
<td>{{ .Race }}</td> <td>{{ .Race }}</td>
<td> <td>
{{- if eq .DeathYear -1 }} {{- if eq .DeathYear -1 }}
@ -34,7 +34,7 @@
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<h5>Filter</h5> <h5>Filter</h5>
<form action="/hfs" method="GET"> <form action="./hfs" method="GET">
<div class="checkbox"><label><input class="filter" type="checkbox" name="leader" value="1" {{if eq .Params.leader "1" <div class="checkbox"><label><input class="filter" type="checkbox" name="leader" value="1" {{if eq .Params.leader "1"
}}checked{{end}}> Leader</label></div> }}checked{{end}}> Leader</label></div>
<div class="checkbox"><label><input class="filter" type="checkbox" name="deity" value="1" {{if eq .Params.deity "1" <div class="checkbox"><label><input class="filter" type="checkbox" name="deity" value="1" {{if eq .Params.deity "1"

View File

@ -2,21 +2,23 @@
<html lang="en"> <html lang="en">
<head> <head>
<base href="{{suburi}}/">
<link rel="icon" type="image/x-icon" href="./favicon.ico">
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{block "title" .}}{{end}}</title> <title>{{block "title" .}}{{end}}</title>
<link href="/css/bootstrap.min.css" rel="stylesheet"> <link href="./css/bootstrap.min.css" rel="stylesheet">
<link href="/css/bootstrap-dark.css" rel="stylesheet"> <link href="./css/bootstrap-dark.css" rel="stylesheet">
<link href="/css/bootstrap-icons.css" rel="stylesheet"> <link href="./css/bootstrap-icons.css" rel="stylesheet">
<link href="/css/all.min.css" rel="stylesheet"> <link href="./css/all.min.css" rel="stylesheet">
<link href="/css/legends.css" rel="stylesheet"> <link href="./css/legends.css" rel="stylesheet">
<script src="/js/jquery-3.6.0.min.js"></script> <script src="./js/jquery-3.6.0.min.js"></script>
<script src="/js/popper.min.js"></script> <script src="./js/popper.min.js"></script>
<script src="/js/bootstrap.min.js"></script> <script src="./js/bootstrap.min.js"></script>
<script src="/js/autocomplete.js"></script> <script src="./js/autocomplete.js"></script>
<link href="/leaflet/leaflet.css" rel="stylesheet"> <link href="./leaflet/leaflet.css" rel="stylesheet">
<script src="/leaflet/leaflet.js"></script> <script src="./leaflet/leaflet.js"></script>
</head> </head>
<body> <body>
@ -30,10 +32,10 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/">Civilizations</a> <a class="nav-link" href="./">Civilizations</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/worldmap">World Map</a> <a class="nav-link" href="./worldmap">World Map</a>
</li> </li>
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown"
@ -41,26 +43,26 @@
Objects Objects
</a> </a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown"> <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="/geography">Geography</a></li> <li><a class="dropdown-item" href="./geography">Geography</a></li>
<li><a class="dropdown-item" href="/entities">Entities</a></li> <li><a class="dropdown-item" href="./entities">Entities</a></li>
<li><a class="dropdown-item" href="/sites">Sites</a></li> <li><a class="dropdown-item" href="./sites">Sites</a></li>
<li><a class="dropdown-item" href="/structures">Structures</a></li> <li><a class="dropdown-item" href="./structures">Structures</a></li>
<li><a class="dropdown-item" href="/hfs">Historical Figures</a></li> <li><a class="dropdown-item" href="./hfs">Historical Figures</a></li>
<li><a class="dropdown-item" href="/identities">Identities</a></li> <li><a class="dropdown-item" href="./identities">Identities</a></li>
<li><a class="dropdown-item" href="/worldconstructions">World Constructions</a></li> <li><a class="dropdown-item" href="./worldconstructions">World Constructions</a></li>
<li><a class="dropdown-item" href="/artifacts">Artifacts</a></li> <li><a class="dropdown-item" href="./artifacts">Artifacts</a></li>
<li><a class="dropdown-item" href="/artforms">Art Forms</a></li> <li><a class="dropdown-item" href="./artforms">Art Forms</a></li>
<li><a class="dropdown-item" href="/writtencontents">Written Contents</a></li> <li><a class="dropdown-item" href="./writtencontents">Written Contents</a></li>
</ul> </ul>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/years">Years</a> <a class="nav-link" href="./years">Years</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/collections">Collections</a> <a class="nav-link" href="./collections">Collections</a>
</li> </li>
</ul> </ul>
<form class="d-flex" action="/search" method="get"> <form class="d-flex" action="./search" method="get">
<div class="input-group"> <div class="input-group">
<input id="search" class="form-control" name="search" type="search" placeholder="Search" aria-label="Search" <input id="search" class="form-control" name="search" type="search" placeholder="Search" aria-label="Search"
autocomplete="off"> autocomplete="off">
@ -71,8 +73,8 @@
const ac = new Autocomplete(document.getElementById("search"), { const ac = new Autocomplete(document.getElementById("search"), {
data: [{ label: "I'm a label", value: 42 }], data: [{ label: "I'm a label", value: 42 }],
maximumItems: 50, maximumItems: 50,
onInput: value => $.get("/search?term=" + value, data => ac.setData(data)), onInput: value => $.get("./search?term=" + value, data => ac.setData(data)),
onSelectItem: ({ label, value }) => window.location = value onSelectItem: ({ label, value }) => window.location = "." + value
}); });
</script> </script>
</div> </div>
@ -96,7 +98,7 @@
function loadLinkPopoverData() { function loadLinkPopoverData() {
return $.ajax({ return $.ajax({
url: "/popover" + this.getAttribute("href"), url: "./popover" + this.getAttribute("href").substring(1),
async: false async: false
}).responseText; }).responseText;
} }

View File

@ -14,14 +14,14 @@
{{- range .Partitions }} {{- range .Partitions }}
<tr> <tr>
<td><a href="/load?p={{ . }}"><i class="bi bi-hdd"></i> {{ . }}</a></td> <td><a href="./load?p={{ . }}"><i class="bi bi-hdd"></i> {{ . }}</a></td>
<td></td> <td></td>
<td></td> <td></td>
</tr> </tr>
{{- end }} {{- end }}
<tr> <tr>
<td><a href="/load?p={{ (printf `%s/..` $.Current) }}&x=1"><i class="bi bi-folder2"></i> ..</a></td> <td><a href="./load?p={{ (printf `%s/..` $.Current) }}&x=1"><i class="bi bi-folder2"></i> ..</a></td>
<td></td> <td></td>
<td></td> <td></td>
</tr> </tr>
@ -29,7 +29,7 @@
{{- range $f := .List }}{{- if $f.IsDir }} {{- range $f := .List }}{{- if $f.IsDir }}
<tr> <tr>
<td> <td>
<a href="/load?p={{ (printf `%s/%s` $.Current $f.Name) }}"><i class="bi bi-folder2"></i> {{$f.Name}}</a> <a href="./load?p={{ (printf `%s/%s` $.Current $f.Name) }}"><i class="bi bi-folder2"></i> {{$f.Name}}</a>
</td> </td>
<td></td> <td></td>
<td></td> <td></td>
@ -39,7 +39,7 @@
{{- range $f := .List }}{{- if isLegendsXml $f }} {{- range $f := .List }}{{- if isLegendsXml $f }}
<tr> <tr>
<td> <td>
<a class="loadable" href="/load?p={{ (printf `%s/%s` $.Current $f.Name) }}"><i class="bi bi-filetype-xml"></i> {{$f.Name}}</a> <a class="loadable" href="./load?p={{ (printf `%s/%s` $.Current $f.Name) }}"><i class="bi bi-filetype-xml"></i> {{$f.Name}}</a>
</td> </td>
<td>{{ bytes $f.Size }}</td> <td>{{ bytes $f.Size }}</td>
<td>{{ $f.ModTime.Format "02 Jan 06 15:04" }}</td> <td>{{ $f.ModTime.Format "02 Jan 06 15:04" }}</td>

View File

@ -14,7 +14,7 @@
<script> <script>
setInterval(() => { setInterval(() => {
$.ajax({ $.ajax({
url: "/load/progress", url: "./load/progress",
context: document.body, context: document.body,
success: function (data) { success: function (data) {
$("#msg").text(data.msg); $("#msg").text(data.msg);

View File

@ -0,0 +1,8 @@
{{template "layout.html" .}}
{{define "title"}}Not available{{end}}
{{define "content"}}
Not available in server mode
{{- end }}

View File

@ -16,7 +16,7 @@
<div class="col-md-3"> <div class="col-md-3">
<ul> <ul>
{{end}} {{end}}
<li><a href="/year/{{ $y }}">Year {{ $y }}</a> ({{len $e}} events)</li> <li><a href="./year/{{ $y }}">Year {{ $y }}</a> ({{len $e}} events)</li>
{{ end }} {{ end }}
</ul> </ul>
</div> </div>