// Package main m2.go
package main
import (
"bufio"
"bytes"
"embed"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
htmpl "html/template"
//htmpl "github.com/gofiber/template/html/v2"
"log"
"net/http"
"os"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"sync"
ttmpl "text/template"
"time"
"unicode"
"reflect"
"github.com/alecthomas/chroma/quick"
"github.com/bitfield/script"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/0magnet/calvin"
cc "github.com/ivanpirog/coloredcobra"
"github.com/spf13/cobra"
"github.com/stripe/stripe-go/v81"
"github.com/stripe/stripe-go/v81/paymentintent"
)
//Files: head.html header.html header1.html main.html category.html product.html front.html font.html test.html sitemap.xml
//go:embed m2.go
var quine string
//go:embed htmpl/*
var templatesFS embed.FS
//go:embed content/*
var contentFS embed.FS
func mustReadFileToString(path string, fs embed.FS) (string) {
data, _ := fs.ReadFile(path)
return string(data)
}
func mustReadFileToBytes(path string, fs embed.FS) (res []byte) {
res, _ = fs.ReadFile(path)
return res
}
type htmlTemplate struct {
Head string
Header string
Categories string
CatSubcats string
Footer string
MainPage string
FrontPage string
CategoryPage string
ProductPage string
Schema string
Cart string
XmlSitemap string
Wasm string
Clock string
AboutPage string
PolicyPage string
LinksPage string
CheckoutPage string
CompletePage string
CheckoutCSS string
StyleCSS string
FaviconICO []byte
}
var h = htmlTemplate{
Head: mustReadFileToString("htmpl/head.html", templatesFS),
Header: mustReadFileToString("htmpl/header.html", templatesFS),
Categories: mustReadFileToString("htmpl/categories.html", templatesFS),
CatSubcats: mustReadFileToString("htmpl/catsubcats.html", templatesFS),
Footer: mustReadFileToString("htmpl/footer.html", templatesFS),
MainPage: mustReadFileToString("htmpl/main.html", templatesFS),
FrontPage: mustReadFileToString("htmpl/front.html", templatesFS),
CategoryPage: mustReadFileToString("htmpl/category.html", templatesFS),
ProductPage: mustReadFileToString("htmpl/product.html", templatesFS),
Schema: mustReadFileToString("htmpl/schema.html", templatesFS),
Cart: mustReadFileToString("htmpl/cart.html", templatesFS),
XmlSitemap: mustReadFileToString("htmpl/sitemap.xml", templatesFS),
Wasm: mustReadFileToString("htmpl/wasm.html", templatesFS),
Clock: mustReadFileToString("htmpl/clock.html", templatesFS),
CompletePage: mustReadFileToString("htmpl/complete.html", templatesFS),
AboutPage: mustReadFileToString("content/about.html", contentFS),
PolicyPage: mustReadFileToString("content/policy.html", contentFS),
LinksPage: mustReadFileToString("content/links.html", contentFS),
CheckoutPage: mustReadFileToString("content/checkout.html", contentFS),
CheckoutCSS: mustReadFileToString("content/checkout.css", contentFS),
StyleCSS: mustReadFileToString("content/style.css", contentFS),
FaviconICO: mustReadFileToBytes("content/favicon.ico", contentFS),
}
func main() {
Execute()
}
func init() {
stripe.EnableTelemetry = false
rootCmd.CompletionOptions.DisableDefaultCmd = true
rootCmd.AddCommand(
runCmd,
genCmd,
)
var helpflag bool
rootCmd.SetUsageTemplate(help)
rootCmd.PersistentFlags().BoolVarP(&helpflag, "help", "h", false, "help for "+rootCmd.Use)
rootCmd.SetHelpCommand(&cobra.Command{Hidden: true})
rootCmd.PersistentFlags().MarkHidden("help") //nolint
}
var rootCmd = &cobra.Command{
Use: "m2",
Short: "web store server",
Long: "web store server",
}
var genCmd = &cobra.Command{
Use: "gen",
Short: "generate conf template",
Long: "generate conf template",
Run: func(_ *cobra.Command, _ []string) {
fmt.Println(envfiletemplate)
},
}
// Execute executes the root cli command
func Execute() {
cc.Init(&cc.Config{
RootCmd: rootCmd,
Headings: cc.HiBlue + cc.Bold,
Commands: cc.HiBlue + cc.Bold,
CmdShortDescr: cc.HiBlue,
Example: cc.HiBlue + cc.Italic,
ExecName: cc.HiBlue + cc.Bold,
Flags: cc.HiBlue + cc.Bold,
FlagsDescr: cc.HiBlue,
NoExtraNewlines: true,
NoBottomNewline: true,
})
if err := rootCmd.Execute(); err != nil {
log.Fatal("Failed to execute command: ", err)
}
}
var menvfile = os.Getenv("MENV")
type FlagVars struct {
Teststripekey bool
ProductsCSV string
WebPort int
StripelivePK string
StripeliveSK string
StripetestPK string
StripetestSK string
StripeSK string
StripePK string
Siteimagesrc string
Siteordersurl string
Sitename string
Siteext string
Sitedomain string
Sitelongname string
Sitetagline string
Sitemeta string
Siteprettyname string
Siteprettynamecap string
Siteprettynamecaps string
Siteasciilogo string
Tgcontact string
Tgchannel string
UseTinygo bool
StaticWasm bool
WasmSRC []string
WasmExecPath string
WasmExecPathGo string
WasmExecPathTinyGo string
Gobuild string
Tinygobuild string
Buildwasmwith string
LDFlagsX string
}
var f = FlagVars{
// WasmSRC: []string{"wasm/stl2.go","wasm/checkout_wasm.go"},
ProductsCSV: "products.csv",
WasmExecPath: runtime.GOROOT() + "/misc/wasm/wasm_exec.js",
WasmExecPathGo: runtime.GOROOT() + "/misc/wasm/wasm_exec.js",
WasmExecPathTinyGo: strings.TrimSuffix(runtime.GOROOT(), "go") + "tinygo" + "/targets/wasm_exec.js",
Gobuild: "go build",
Tinygobuild: "tinygo build -target=wasm --no-debug",
Buildwasmwith: "go build",
LDFlagsX: "stripePK",
}
var (
// Hardcoded array of valid shorthand characters, excluding "h"
shorthandChars = []rune("abcdefgijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
nextShortIndex = 0 // Index for the next shorthand flag
)
// Get the next available shorthand flag
func getNextShortFlag() string {
if nextShortIndex >= len(shorthandChars) {
return ""
}
short := shorthandChars[nextShortIndex]
nextShortIndex++
return string(short)
}
var a = true
var b = false
func addStringFlag(cmd *cobra.Command, f interface{}, fieldPtr *string, description string) {
cmd.Flags().StringVarP(fieldPtr,ccc(fieldPtr, f, b),getNextShortFlag(),scriptExecString(fmt.Sprintf("${%s%s}", ccc(fieldPtr, f, a), func(s string) string {
if s != "" {s= "-"+s}
return s
}(*fieldPtr))), fmt.Sprintf("%s env: %s\033[0m\n\r", description, ccc(fieldPtr, f, a)))
}
func addStringSliceFlag(cmd *cobra.Command, f interface{}, fieldPtr *[]string, description string) {
cmd.Flags().StringSliceVarP(
fieldPtr,
ccc(fieldPtr, f, b),
getNextShortFlag(),
scriptExecStringSlice(fmt.Sprintf("${%s[@]}", ccc(fieldPtr, f, a))),
fmt.Sprintf("%s env: %s\033[0m\n\r", description, ccc(fieldPtr, f, a)),
)
}
func addBoolFlag(cmd *cobra.Command, f interface{}, fieldPtr *bool, description string) {
cmd.Flags().BoolVarP(fieldPtr,ccc(fieldPtr, f, b),getNextShortFlag(),scriptExecBool(fmt.Sprintf("${%s%s}", ccc(fieldPtr, f, a), func(b bool) string {
return "-"+strconv.FormatBool(b)
}(*fieldPtr))), fmt.Sprintf("%s env: %s\033[0m\n\r", description, ccc(fieldPtr, f, a)))
}
func addIntFlag(cmd *cobra.Command, f interface{}, fieldPtr *int, description string) {
cmd.Flags().IntVarP(fieldPtr,ccc(fieldPtr, f, b),getNextShortFlag(),scriptExecInt(fmt.Sprintf("${%s%s}", ccc(fieldPtr, f, a), func(i int) string {
return fmt.Sprintf("-%d", i)
}(*fieldPtr))), fmt.Sprintf("%s env: %s\033[0m\n\r", description, ccc(fieldPtr, f, a)))
}
func init() {
runCmd.Flags().SortFlags = false
addStringFlag(runCmd, &f, &f.ProductsCSV, "products csv file")
addBoolFlag(runCmd, &f, &f.Teststripekey, "use stripe test api keys instead of live key")
addStringFlag(runCmd, &f, &f.StripeliveSK, "stripe live api sk")
addStringFlag(runCmd, &f, &f.StripelivePK, "stripe live api pk")
addStringFlag(runCmd, &f, &f.StripetestSK, "stripe test api sk")
addStringFlag(runCmd, &f, &f.StripetestPK, "stripe test api pk")
addIntFlag(runCmd, &f, &f.WebPort, "port to serve on")
addStringFlag(runCmd, &f, &f.Siteimagesrc, "domain for images - leave blank to serve images")
addStringFlag(runCmd, &f, &f.Siteordersurl, "domain for orders - leave blank for same domain")
addStringFlag(runCmd, &f, &f.Sitename, "site name")
addStringFlag(runCmd, &f, &f.Siteext, "site domain extension")
addStringFlag(runCmd, &f, &f.Sitelongname, "site long name")
addStringFlag(runCmd, &f, &f.Sitetagline, "site domain extension")
addStringFlag(runCmd, &f, &f.Sitemeta, "site meta")
addStringFlag(runCmd, &f, &f.Tgcontact, "telegram contact")
addStringFlag(runCmd, &f, &f.Tgchannel, "telegram channel")
addBoolFlag(runCmd, &f, &f.UseTinygo, "use tinygo instead of go to compile wasm")
addBoolFlag(runCmd, &f, &f.StaticWasm, "compile wasm once and serve instead of per request")
addStringSliceFlag(runCmd, &f, &f.WasmSRC, "wasm source code files RELATIVE PATHS without '..'")
}
// change case
func ccc(val interface{}, strct interface{}, upper bool) string {
v := reflect.ValueOf(strct)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
panic("uc: second argument must be a pointer to a struct")
}
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
if field.CanAddr() && field.Addr().Interface() == val {
if upper {
return strings.ToUpper(v.Type().Field(i).Name)
}
return strings.ToLower(v.Type().Field(i).Name)
}
}
return ""
}
var runCmd = &cobra.Command{
Use: "run",
Short: "run the web application",
Long: func() string {
helptext := `Run the web application
Generate a config file first
Config defaults file may also be specified with:
MENV=m2.conf m2 run
OR
MENV=/path/to/m2.conf m2 run
print the MENV file template with:
m2 gen`
if menvfile == "" {
return helptext
}
if _, err := os.Stat(menvfile); err == nil {
return `Run the web application
menv file detected: ` + menvfile
}
return helptext
}(),
Run: func(_ *cobra.Command, _ []string) {
f.Sitedomain = f.Sitename + f.Siteext
f.StripeSK = f.StripeliveSK
f.StripePK = f.StripelivePK
if f.Teststripekey {
f.StripeSK = f.StripetestSK
f.StripePK = f.StripetestPK
}
stripe.Key = f.StripeSK
// awkward way to do this
f.LDFlagsX += "="+f.StripePK
f.Siteprettyname = calvin.BlackboardBold(f.Sitedomain) //"ππππππ₯π π€π‘πππ£π.πππ₯"
f.Siteprettynamecap = calvin.BlackboardBold(strings.Title(f.Sitedomain)) //"ππππππ₯π π€π‘πππ£π.πππ₯"
f.Siteprettynamecaps = calvin.BlackboardBold(strings.ToUpper(f.Sitedomain)) //"ππΈπΎβπΌπππββπΌβπΌ.βπΌπ"
f.Siteasciilogo = strings.Replace(strings.Replace(calvin.AsciiFont(f.Sitedomain), " ", " ", -1), "\n", "<br>\n", -1)
if f.UseTinygo {
f.WasmExecPath = f.WasmExecPathTinyGo
f.Buildwasmwith = f.Tinygobuild
}
if len(f.WasmSRC) == 0 {
f.WasmExecPath = ""
f.Buildwasmwith = ""
}
fileInfo, err := os.Stat("products.csv")
if err != nil {
log.Fatal("Error getting file info:", err)
return
}
lastModTime = fileInfo.ModTime()
allproducts = readCSV("products.csv")
go func() {
for {
fileInfo, err := os.Stat("products.csv")
if err != nil {
log.Println("Error getting file info:", err)
}
currentModTime := fileInfo.ModTime()
if currentModTime != lastModTime {
log.Println("CSV file has been modified!")
allproducts = readCSV("products.csv")
lastModTime = currentModTime
}
time.Sleep(1 * time.Second)
}
}()
server()
},
}
var cWasm []byte
var lastModTime time.Time
var htmlPageTemplateData htmlTemplateData
var tmpl *htmpl.Template
func ldflags(s string) (ss string) {
if f.LDFlagsX != "" {
res, _ := script.File(s).Match(strings.Split(f.LDFlagsX, "=")[0]).String()
if res != "" {
ss += fmt.Sprintf(` -X 'main.%s' `,f.LDFlagsX)
}
}
res, _ := script.File(s).Match("wasmName").String()
if res != "" {
ss += fmt.Sprintf(` -X 'main.wasmName=%s' `, strings.TrimSuffix(filepath.Base(s), filepath.Ext(s)) + ".wasm")
}
if ss != "" {
ss = `-ldflags="` + ss + `"`
}
return ss
}
func server() {
wg := new(sync.WaitGroup)
wg.Add(1)
htmlPageTemplateData = htmlTemplateData{
TestMode: f.Teststripekey,
Title: f.Sitelongname,
StripePK: f.StripePK,
SiteName: f.Sitedomain,
SiteTagLine: f.Sitetagline,
SiteName1: htmpl.HTML(checkerBoard(f.Sitedomain)),
SiteLongName: f.Sitelongname,
SiteAsciiLogo: htmpl.HTML(f.Siteasciilogo),
SitePrettyName: f.Siteprettyname,
SitePrettyNameCap: f.Siteprettynamecap,
SitePrettyNameCaps: f.Siteprettynamecaps,
TelegramContact: f.Tgcontact,
TelegramChannel: f.Tgchannel,
WasmBinary: func() (ret []string) {
if len(f.WasmSRC) == 0 {
return ret
}
// if f.StaticWasm {
for _, wasmSRC := range f.WasmSRC {
outputFile := strings.TrimSuffix(filepath.Base(wasmSRC), filepath.Ext(wasmSRC)) + ".wasm"
compilecmd := fmt.Sprintf("bash -c 'GOOS=js GOARCH=wasm %s %s -o %s %s'", f.Gobuild, ldflags(wasmSRC), outputFile, wasmSRC)
log.Println("Compiling wasm with:")
log.Println(compilecmd)
startTime := time.Now()
data, err := script.Exec(compilecmd).Bytes()
if err != nil {
log.Println(string(data))
log.Fatal(err)
}
log.Println("Compiled wasm! Compile time:", time.Since(startTime))
log.Println("size: ")
_, _ = script.Exec(fmt.Sprintf("du -h %s", outputFile)).Stdout()
}
for _, wasmSRC := range f.WasmSRC {
outputFile := strings.TrimSuffix(filepath.Base(wasmSRC), filepath.Ext(wasmSRC)) + "-tiny.wasm"
compilecmd := fmt.Sprintf("bash -c 'GOOS=js GOARCH=wasm %s %s -o %s %s'",f.Tinygobuild, ldflags(wasmSRC), outputFile, wasmSRC)
log.Println("compiling wasm with:")
log.Println(compilecmd)
startTime := time.Now()
data, err := script.Exec(compilecmd).Bytes()
if err != nil {
log.Println(string(data))
log.Fatal(err)
}
log.Println("Compiled wasm! Compile time:", time.Since(startTime))
log.Println("size: ")
_, _ = script.Exec(fmt.Sprintf("du -h %s", outputFile)).Stdout()
}
if f.UseTinygo {
for _, wasmSRC := range f.WasmSRC {
outputFile := strings.TrimSuffix(filepath.Base(wasmSRC), filepath.Ext(wasmSRC)) + "-tiny.wasm"
ret = append(ret, outputFile)
}
return ret
}
for _, wasmSRC := range f.WasmSRC {
outputFile := strings.TrimSuffix(filepath.Base(wasmSRC), filepath.Ext(wasmSRC)) + ".wasm"
ret = append(ret, outputFile)
}
return ret
// }
return ret
}(),
WasmExecPath: f.WasmExecPath,
WasmExecRel: "/wasm_exec.js",
Cats: getcats(),
LenAllProducts: len(allproducts),
ImgSRC: func() (ret string) {
ret = f.Siteimagesrc
if ret == "" {
ret = "/img"
}
return ret
}(),
Page: "front",
Time: time.Now().Format(time.RFC3339Nano),
Year: fmt.Sprintf("%v", time.Now().Year()),
}
htmlPageTemplateData.CatsCounts, htmlPageTemplateData.Cats, htmlPageTemplateData.SubCatsCounts, htmlPageTemplateData.SubCatsByCat = getcategories(allproducts)
var err1 error
completetmpl, err1 := htmpl.New("index").Parse(h.CompletePage)
if err1 != nil {
log.Println("Error parsing complete page template:", err1)
}
_, err1 = completetmpl.New("wasm").Parse(h.Wasm)
if err1 != nil {
log.Println("Error parsing wasm template:", err1)
}
tmpl, err1 = htmpl.New("index").Funcs(htmpl.FuncMap{"replace": replace, "mul": mul, "div": div, "safeHTML": safeHTML, "safeJS": safeJS, "stripProtocol": stripProtocol, "add": add, "sub": sub, "toFloat": toFloat, "equalsIgnoreCase": equalsIgnoreCase, "getsubcats": getsubcats, "escapesubcat": escapesubcat, "sortsubcats": sortsubcats, "repeat": repeat}).Parse(h.MainPage)
if err1 != nil {
log.Println("Error parsing index template:", err1)
}
_, err1 = tmpl.New("head").Parse(h.Head)
if err1 != nil {
log.Println("Error parsing head template:", err1)
}
_, err1 = tmpl.New("schema").Parse(h.Schema)
if err1 != nil {
log.Println("Error parsing schema template:", err1)
}
_, err1 = tmpl.New("header").Parse(h.Header)
if err1 != nil {
log.Println("Error parsing header template:", err1)
}
_, err1 = tmpl.New("catsubcats").Parse(h.CatSubcats)
if err1 != nil {
log.Println("Error parsing header template:", err1)
}
_, err1 = tmpl.New("categories").Parse(h.Categories)
if err1 != nil {
log.Println("Error parsing categories template:", err1)
}
_, err1 = tmpl.New("footer").Parse(h.Footer)
if err1 != nil {
log.Println("Error parsing footer template:", err1)
}
_, err1 = tmpl.New("cart").Parse(h.Cart)
if err1 != nil {
log.Println("Error parsing cart template:", err1)
}
_, err1 = tmpl.New("wasm").Parse(h.Wasm)
if err1 != nil {
log.Println("Error parsing wasm template:", err1)
}
r := fiber.New(fiber.Config{
ErrorHandler: func(c *fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
var e *fiber.Error
if errors.As(err, &e) {
code = e.Code
}
c.SendStatus(code)
return nil
},
})
r.Use(logger.New(logger.Config{
Done: nil,
Format: "${time} | ${status} | ${latency} | ${ip} | ${ips} | ${method} | ${path}\n",
TimeFormat: "2006-01-02 15:04:05",
}))
r.Get("/m2.go", func(c *fiber.Ctx) error {
c.Set("Content-Type", "text/html;charset=utf-8")
lang := "go"
var buf bytes.Buffer
err := quick.Highlight(&buf, quine, lang, "html", "monokai")
if err != nil {
log.Println("error in function serveSyntaxHighlighted ; error on quick.Highlight: ", err)
c.SendStatus(fiber.StatusInternalServerError)
return err
}
c.Status(fiber.StatusOK).Write(buf.Bytes())
return nil
})
if f.WasmExecPath != "" {
_, err := script.File(f.WasmExecPath).Bytes()
if err != nil {
log.Printf("Error reading %s: %v\n", f.WasmExecPath, err)
} else { //the wasm exec must be present or none of the webassembly stuff will work ; provided by the golang installaton
r.Get("/wasm_exec.js", func(c *fiber.Ctx) error {
wasmExecData, err := script.File(f.WasmExecPath).Bytes()
if err != nil {
log.Printf("Error reading %s: %v\n", f.WasmExecPath, err)
c.SendStatus(fiber.StatusNotFound)
return err
}
c.Status(fiber.StatusOK).Write(wasmExecData)
return nil
})
if f.UseTinygo {
for _, wasmSRC := range f.WasmSRC {
outputFile := strings.TrimSuffix(filepath.Base(wasmSRC), filepath.Ext(wasmSRC)) + "-tiny.wasm"
r.Get("/"+outputFile, func(c *fiber.Ctx) error {
data, err := script.File(outputFile).Bytes()
if err != nil {
script.File(outputFile).Stdout()
c.SendStatus(fiber.StatusInternalServerError)
return err
}
c.Set("Content-Type", "application/wasm")
c.Status(fiber.StatusOK).Send(data)
return nil
})
}
} else {
for _, wasmSRC := range f.WasmSRC {
outputFile := strings.TrimSuffix(filepath.Base(wasmSRC), filepath.Ext(wasmSRC)) + ".wasm"
r.Get("/"+outputFile, func(c *fiber.Ctx) error {
data, err := script.File(outputFile).Bytes()
if err != nil {
script.File(outputFile).Stdout()
c.SendStatus(fiber.StatusInternalServerError)
return err
}
c.Set("Content-Type", "application/wasm")
c.Status(fiber.StatusOK).Send(data)
return nil
})
}
}
for _, wasmSRC := range f.WasmSRC {
r.Get("/"+wasmSRC, func(c *fiber.Ctx) error {
return serveSyntaxHighlighted(c)
})
}
/*
r.Get("/b.wasm", func(c *fiber.Ctx) error {
compilecmd := fmt.Sprintf("bash -c 'GOOS=js GOARCH=wasm %s %s -o /dev/stdout %s'", f.Buildwasmwith, ldflags(), f.WasmSRC)
log.Println("Compiling wasm with:")
log.Println(compilecmd)
startTime := time.Now()
data, err := script.Exec(compilecmd).Bytes()
if err != nil {
script.Exec(compilecmd).Stdout()
c.SendStatus(fiber.StatusNotFound)
return err
}
log.Println("Compiled wasm! Compile time:", time.Since(startTime))
c.Set("Content-Type", "application/wasm")
c.Status(fiber.StatusOK).Send(data)
return nil
})
r.Get("/c.wasm", func(c *fiber.Ctx) error {
c.Set("Content-Type", "application/wasm")
c.Status(fiber.StatusOK).Send(cWasm)
return nil
})
r.Get("/d.wasm", func(c *fiber.Ctx) error {
c.Set("Content-Type", "application/wasm")
dWasm, err := script.File("d.wasm").Bytes()
if err != nil {
c.SendStatus(fiber.StatusNotFound)
return err
}
c.Status(fiber.StatusOK).Send(dWasm)
return nil
})
r.Get("/e.wasm", func(c *fiber.Ctx) error {
c.Set("Content-Type", "application/wasm")
dWasm, err := script.File("e.wasm").Bytes()
if err != nil {
c.SendStatus(fiber.StatusNotFound)
return err
}
c.Status(fiber.StatusOK).Send(dWasm)
return nil
})
*/
}
}
r.Static("/logo.png", "./logo.png")
r.Static("/logo.html", "./logo.html")
r.Static("/mobilelogo.html", "./mobilelogo.html")
r.Static("/logolarge.html", "./logolarge.html")
r.Get("/favicon.ico", func(c *fiber.Ctx) error {
c.Set("Content-Type", "image/jpeg")
c.Status(fiber.StatusOK).Write(h.FaviconICO)
return nil
})
r.Get("/robots.txt", func(c *fiber.Ctx) error {
c.Set("Content-Type", "text/html;charset=utf-8")
c.Status(fiber.StatusOK).Write([]byte(fmt.Sprintf("User-Agent: *\n\nSitemap: https://%s/sitemap", c.OriginalURL())))
return nil
})
if f.Siteimagesrc == "" {
r.Static("/img", "./img")
}
if len(f.WasmSRC) == 0 {
r.Get("/stl/:filename", func(c *fiber.Ctx) error {
stlfile, err := script.File("img/stl/" + c.Params("filename")).Bytes()
if err != nil {
c.SendStatus(fiber.StatusNotFound)
return err
}
c.Write(stlfile)
return nil
})
r.Get("/stl/base64/:filename", func(c *fiber.Ctx) error {
stlfile, err := script.File("img/stl/" + c.Params("filename")).Bytes()
if err != nil {
c.SendStatus(fiber.StatusNotFound)
return err
}
base64Data := base64.StdEncoding.EncodeToString(stlfile)
c.Status(fiber.StatusOK).Write([]byte("data:model/stl;base64," + base64Data))
return nil
})
}
r.Get("/site.webmanifest", func(c *fiber.Ctx) error {
c.JSON([]byte(`{"name":"","short_name":"","icons":[{"src":"` + f.Siteimagesrc + `/img/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"` + f.Siteimagesrc + `/img/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}`))
return nil
})
r.Get("/sitemap", func(c *fiber.Ctx) error {
c.XML(generateSitemapXML())
return nil
})
r.Get("/sitemap.xml", func(c *fiber.Ctx) error {
c.XML(generateSitemapXML())
return nil
})
r.Get("/coffee", func(c *fiber.Ctx) error {
c.SendStatus(fiber.StatusTeapot)
return nil
})
r.Get("/clock", func(c *fiber.Ctx) error {
c.Set("Content-Type", "text/html;charset=utf-8")
c.Status(fiber.StatusOK).Write([]byte(h.Clock))
return nil
})
r.Get("/", func(c *fiber.Ctx) error {
tmpl0, err1 := tmpl.Clone()
if err1 != nil {
log.Println("Error cloning template:", err1)
}
_, err1 = tmpl0.New("main").Parse(h.FrontPage)
if err1 != nil {
log.Println("Error parsing Front Page template:", err1)
}
_, err1 = tmpl0.New("about").Parse(h.AboutPage)
if err1 != nil {
log.Println("Error parsing about template:", err1)
}
_, err1 = tmpl0.New("policy").Parse(h.PolicyPage)
if err1 != nil {
log.Println("Error parsing about template:", err1)
}
_, err1 = tmpl0.New("links").Parse(h.LinksPage)
if err1 != nil {
log.Println("Error parsing about template:", err1)
}
tmpl := tmpl0
log.Println(c.Get("User-Agent"))
c.Set("Content-Type", "text/html;charset=utf-8")
htmlPageTemplateData1 := htmlPageTemplateData
htmlPageTemplateData1.Canonical = c.Protocol() + `://` + string(c.Request().Host()) + c.OriginalURL()
htmlPageTemplateData1.BaseURL = c.Protocol() + `://` + string(c.Request().Host())
htmlPageTemplateData1.RequestHost = string(c.Request().Host())
htmlPageTemplateData1.Protocol = c.Protocol()
// htmlPageTemplateData1.Mobile = strings.Contains(strings.ToLower(c.Get("User-Agent")), "mobile")
htmlPageTemplateData1.CatsCounts, htmlPageTemplateData1.Cats, htmlPageTemplateData1.SubCatsCounts, htmlPageTemplateData1.SubCatsByCat = getcategories(allproducts)
htmlPageTemplateData1.LenAllProducts = len(allproducts)
htmlPageTemplateData1.Time = time.Now().Format(time.RFC3339Nano)
htmlPageTemplateData1.Year = fmt.Sprintf("%v", time.Now().Year())
htmlPageTemplateData1.MetaDesc = f.Sitemeta
htmlPageTemplateData1.KeyWords = strings.Replace(f.Sitelongname, " ", ", ", -1)
tmplData := map[string]interface{}{
"Page": htmlPageTemplateData1,
"Prods": allproducts,
}
var result bytes.Buffer
err := tmpl.Execute(&result, tmplData)
if err != nil {
log.Println("error: ", err)
c.SendStatus(fiber.StatusInternalServerError)
return err
}
c.Status(fiber.StatusOK).Write(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(result.Bytes(), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1))
return nil
})
r.Get("/p/:partno", func(c *fiber.Ctx) error {
tmpl0, err1 := tmpl.Clone()
if err1 != nil {
log.Println("Error cloning template:", err1)
}
_, err1 = tmpl0.New("main").Parse(h.ProductPage)
if err1 != nil {
log.Println("Error parsing product page template:", err1)
}
tmpl := tmpl0
c.Set("Content-Type", "text/html;charset=utf-8")
for _, prod := range allproducts {
if prod.Partno == c.Params("partno") {
var result bytes.Buffer
htmlPageTemplateData1 := htmlPageTemplateData
htmlPageTemplateData1.Canonical = c.Protocol() + `://` + string(c.Request().Host()) + c.OriginalURL()
htmlPageTemplateData1.BaseURL = c.Protocol() + `://` + string(c.Request().Host())
htmlPageTemplateData1.RequestHost = string(c.Request().Host())
htmlPageTemplateData1.Protocol = c.Protocol()
htmlPageTemplateData1.Title = fmt.Sprintf("%s | %s", prod.Name, htmlPageTemplateData1.Title)
// htmlPageTemplateData1.Mobile = strings.Contains(strings.ToLower(c.Get("User-Agent")), "mobile")
htmlPageTemplateData1.Page = "product"
htmlPageTemplateData1.Time = time.Now().Format(time.RFC3339Nano)
htmlPageTemplateData1.Year = fmt.Sprintf("%v", time.Now().Year())
tmplData := map[string]interface{}{
"Prod": prod,
"Page": htmlPageTemplateData1,
"Prods": allproducts,
}
err := tmpl.Execute(&result, tmplData)
if err != nil {
log.Println("error: ", err)
c.Status(fiber.StatusInternalServerError).Write(result.Bytes())
return err
}
c.Status(fiber.StatusOK).Write(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(result.Bytes(), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1))
return nil
}
}
log.Printf("product %s does not match any existing product\n", c.Params("partno"))
c.Redirect("/cat", fiber.StatusMovedPermanently)
return nil
})
r.Get("/post/:partno", handlecat)
r.Get("/p", handlecat)
r.Get("/cat", handlecat)
r.Get("/cat/:cat", handlecat)
r.Get("/cat/:cat/:subcat", handlecat)
r.Get("/checkout.css", func(c *fiber.Ctx) error {
c.Set("Content-Type", "text/css;charset=utf-8")
c.Status(fiber.StatusOK).Write([]byte(h.CheckoutCSS))
return nil
})
r.Get("/style.css", func(c *fiber.Ctx) error {
c.Set("Content-Type", "text/css;charset=utf-8")
c.Status(fiber.StatusOK).Write([]byte(h.StyleCSS))
return nil
})
r.Get("/complete", func(c *fiber.Ctx) error {
htmlPageTemplateData1 := htmlPageTemplateData
htmlPageTemplateData1.Canonical = c.Protocol() + `://` + c.Hostname() + c.OriginalURL()
htmlPageTemplateData1.BaseURL = c.Protocol() + `://` + c.Hostname()
htmlPageTemplateData1.RequestHost = c.Hostname()
htmlPageTemplateData1.Protocol = c.Protocol()
htmlPageTemplateData1.Time = time.Now().Format(time.RFC3339Nano)
htmlPageTemplateData1.Year = fmt.Sprintf("%v", time.Now().Year())
tmplData := map[string]interface{}{
"Page": htmlPageTemplateData1,
}
var result bytes.Buffer
err := completetmpl.Execute(&result, tmplData)
if err != nil {
msg := fmt.Sprintf("Could not execute html template %v\n", err)
log.Println(msg)
return c.Status(fiber.StatusInternalServerError).SendString(msg)
}
c.Set("Content-Type", "text/html;charset=utf-8")
return c.Status(fiber.StatusOK).Send(result.Bytes())
})
r.Get("/order/:piid", func(c *fiber.Ctx) error {
piid := c.Params("piid")
order, err := script.File("orders/" + piid + ".json").Bytes()
if err != nil {
return c.Status(fiber.StatusNotFound).SendString("Order not found")
}
return c.Status(fiber.StatusOK).Send(order)
})
r.Post("/create-payment-intent", func(c *fiber.Ctx) error {
rawBody := c.Body()
if rawBody == nil {
log.Printf("Failed to read raw request body")
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to read request body"})
}
log.Printf("Raw request body: %s", string(rawBody))
var req struct {
Items []item `json:"items"`
}
if err := json.Unmarshal(rawBody, &req); err != nil {
log.Printf("Failed to parse JSON: %v", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
}
total := int64(0)
for _, item := range req.Items {
total += item.Amount
}
params := &stripe.PaymentIntentParams{
Amount: stripe.Int64(total),
Currency: stripe.String(string(stripe.CurrencyUSD)),
}
pi, err := paymentintent.New(params)
if err != nil {
log.Printf("Failed to create PaymentIntent: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
log.Printf("Created PaymentIntent with ClientSecret: %v", pi.ClientSecret)
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"clientSecret": pi.ClientSecret,
"dpmCheckerLink": fmt.Sprintf("https://dashboard.stripe.com/settings/payment_methods/review?transaction_id=%s", pi.ID),
})
})
r.Post("/submit-order", func(c *fiber.Ctx) error {
var requestData struct {
LocalStorageData map[string]interface{} `json:"localStorageData"`
PaymentIntentId string `json:"paymentIntentId"`
}
if err := c.BodyParser(&requestData); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request data"})
}
log.Printf("Received order data: %+v", requestData.LocalStorageData)
log.Printf("Received payment intent ID: %s", requestData.PaymentIntentId)
paymentIntent, err := paymentintent.Get(requestData.PaymentIntentId, nil)
if err != nil {
log.Printf("Error retrieving payment intent: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Unable to verify payment"})
}
if paymentIntent.Status != stripe.PaymentIntentStatusSucceeded {
log.Printf("Payment was not successful, status: %s", paymentIntent.Status)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Payment not successful"})
}
ordersDir := "./orders"
if err := os.MkdirAll(ordersDir, os.ModePerm); err != nil {
log.Printf("Error creating orders directory: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Unable to save order"})
}
filePath := filepath.Join(ordersDir, fmt.Sprintf("%s.json", requestData.PaymentIntentId))
data, err := json.MarshalIndent(requestData.LocalStorageData, "", " ")
if err != nil {
log.Printf("Error marshalling data: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Unable to save order"})
}
if err := os.WriteFile(filePath, data, 0644); err != nil {
log.Printf("Error writing data to file: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Unable to save order"})
}
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Order submitted successfully"})
})
go func() {
err := r.Listen(fmt.Sprintf(":%d", f.WebPort))
if err != nil {
log.Println("Error serving http: ", err)
}
wg.Done()
}()
wg.Wait()
}
type item struct {
Id string
Amount int64
}
func htmlErr(msg string) []byte {
return []byte(fmt.Sprintf(`<!DOCTYPE html><html><head><meta charset="utf-8"><title>Error</title></head><body style='background-color: black; color: white;'><div>%s</div></body></html>`, strings.ReplaceAll(msg, "\n", "<br>")))
}
func handlecomplete(c *fiber.Ctx) error {
tmpl2, err2 := htmpl.New("index").Parse(h.CompletePage)
if err2 != nil {
log.Println("Error parsing h.CompletePage template:", err2)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err2.Error()})
}
_, err2 = tmpl2.New("css").Parse(h.CheckoutCSS)
if err2 != nil {
log.Println("Error parsing checkoutCSS template:", err2)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err2.Error()})
}
htmlPageTemplateData2 := htmlTemplateData{
StripePK: f.StripePK,
OrdersURL: f.Siteordersurl,
}
var result bytes.Buffer
tmplData := map[string]interface{}{
"Page": htmlPageTemplateData2,
}
err2 = tmpl2.Execute(&result, tmplData)
if err2 != nil {
log.Println("error: ", err2)
c.Status(fiber.StatusInternalServerError).Write(result.Bytes())
return err2
}
c.Status(fiber.StatusOK).Type("html").Write(result.Bytes())
return nil
}
func scriptFile(a string) string {
re, err := script.File(a).String()
if err != nil {
log.Printf("Error on script.File(%s).String(): %v", a, err)
}
return re
}
func isAllUpperCase(s string) bool {
for _, char := range s {
if unicode.IsLetter(char) && !unicode.IsUpper(char) {
return false
}
}
return true
}
func serveSyntaxHighlighted(c *fiber.Ctx) error {
c.Set("Content-Type", "text/html;charset=utf-8")
data, err := script.File(strings.TrimLeft(c.OriginalURL(), "/")).String()
if err != nil {
log.Println("error in function serveSyntaxHighlighted ; error on script.File: ", err)
c.SendStatus(fiber.StatusNotFound)
return err
}
lang := strings.TrimLeft(filepath.Ext(strings.TrimLeft(c.OriginalURL(), "/")), ".")
if lang == "sh" {
lang = "bash"
}
var buf bytes.Buffer
err = quick.Highlight(&buf, data, lang, "html", "monokai")
if err != nil {
log.Println("error in function serveSyntaxHighlighted ; error on quick.Highlight: ", err)
c.SendStatus(fiber.StatusInternalServerError)
return err
}
if c.Context().IsHead() {
c.SendStatus(fiber.StatusOK)
return nil
}
c.Status(fiber.StatusOK).Write(buf.Bytes())
return nil
}
func cathtmlfunc(c *fiber.Ctx) error {
tmpl0, err1 := tmpl.Clone()
if err1 != nil {
log.Println("Error cloning template:", err1)
}
_, err1 = tmpl0.New("main").Parse(h.CategoryPage)
if err1 != nil {
log.Println("Error parsing Category page template:", err1)
}
tmpl := tmpl0
var tmplData map[string]interface{}
var result bytes.Buffer
var categoryproducts Products
c.Set("Content-Type", "text/html;charset=utf-8")
htmlPageTemplateData1 := htmlPageTemplateData
htmlPageTemplateData1.Title = fmt.Sprintf("%s | %s", func() string {
var str string
if c.Params("partno") != "" {
return "No product matching partno.: " + c.Params("partno") + " | Showing All Products"
}
if c.Params("cat") == "" {
return "All Products"
} else {
str = fmt.Sprintf("Category: %s", c.Params("cat"))
}
if c.Params("subcat") != "" {
str += fmt.Sprintf("; Subcategory: %s", c.Params("subcat"))
}
return str
}(), htmlPageTemplateData1.Title)
htmlPageTemplateData1.Canonical = c.Protocol() + `://` + string(c.Request().Host()) + c.OriginalURL()
htmlPageTemplateData1.BaseURL = c.Protocol() + `://` + string(c.Request().Host())
htmlPageTemplateData1.RequestHost = string(c.Request().Host())
htmlPageTemplateData1.Protocol = c.Protocol()
// htmlPageTemplateData1.Mobile = strings.Contains(strings.ToLower(c.Get("User-Agent")), "mobile")
htmlPageTemplateData1.Page = "category"
// htmlPageTemplateData1.WasmBinary = ""
htmlPageTemplateData1.CatsCounts, htmlPageTemplateData1.Cats, htmlPageTemplateData1.SubCatsCounts, htmlPageTemplateData1.SubCatsByCat = getcategories(allproducts)
if c.Params("cat") == "" && c.Params("subcat") == "" {
tmplData = map[string]interface{}{
"Products": allproducts,
"Page": htmlPageTemplateData1,
"Category": c.Params("cat"),
"Subcategory": c.Params("subcat"),
"Prods": allproducts,
"Product": c.Params("partno"),
}
} else {
for _, prod := range allproducts {
if prod.Category == c.Params("cat") && (c.Params("subcat") == "" || escapesubcat(prod.Subcategory) == c.Params("subcat")) {
categoryproducts = append(categoryproducts, prod)
}
}
tmplData = map[string]interface{}{
"Products": categoryproducts,
"Page": htmlPageTemplateData1,
"Category": c.Params("cat"),
"Subcategory": c.Params("subcat"),
"Prods": allproducts,
}
}
err := tmpl.Execute(&result, tmplData)
if err != nil {
log.Println("error: ", err)
c.SendStatus(fiber.StatusInternalServerError)
return err
}
c.Status(fiber.StatusOK).Write(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(bytes.Replace(result.Bytes(), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1), []byte("\n\n"), []byte("\n"), -1))
return nil
}
func parseFloat(s string) float64 {
if s == "" {
return 0.0
}
value, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
if err != nil {
log.Printf(`Error on strconv.ParseFloat(strings.TrimSpace(%s), 64): %v`, s, err)
}
return value
}
const shcmd = `/usr/bin/bash -c`
func getcats() (cats []string) {
var catsMap = make(map[string]int)
for _, prod := range allproducts {
catsMap[prod.Category]++
}
for cat := range catsMap {
cats = append(cats, cat)
}
return cats
}
func contains(slice []string, str string) bool {
for _, s := range slice {
if s == str {
return true
}
}
return false
}
func getcategories(allproducts Products) (map[string]int, []string, map[string]map[string]int, map[string][]string) {
categoryCounts := make(map[string]int)
subcategoryCounts := make(map[string]map[string]int)
subcategoriesByCategory := make(map[string][]string)
for _, prod := range allproducts {
if prod.Category != "" {
categoryCounts[prod.Category]++
if prod.Subcategory != "" {
if subcategoryCounts[prod.Category] == nil {
subcategoryCounts[prod.Category] = make(map[string]int)
}
subcategoryCounts[prod.Category][prod.Subcategory]++
if !contains(subcategoriesByCategory[prod.Category], prod.Subcategory) {
subcategoriesByCategory[prod.Category] = append(subcategoriesByCategory[prod.Category], prod.Subcategory)
}
}
}
}
var sortableCategories []struct {
Name string
Count int
}
for cat, count := range categoryCounts {
sortableCategories = append(sortableCategories, struct {
Name string
Count int
}{Name: cat, Count: count})
}
sort.Slice(sortableCategories, func(i, j int) bool {
return sortableCategories[i].Count > sortableCategories[j].Count
})
var sortedCategories []string
for _, cat := range sortableCategories {
sortedCategories = append(sortedCategories, cat.Name)
var sortableSubcategories []struct {
Name string
Count int
}
for subcat, count := range subcategoryCounts[cat.Name] {
sortableSubcategories = append(sortableSubcategories, struct {
Name string
Count int
}{Name: subcat, Count: count})
}
sort.Slice(sortableSubcategories, func(i, j int) bool {
return sortableSubcategories[i].Count > sortableSubcategories[j].Count
})
var sortedSubcategories []string
for _, subcat := range sortableSubcategories {
sortedSubcategories = append(sortedSubcategories, subcat.Name)
}
subcategoriesByCategory[cat.Name] = sortedSubcategories
}
return categoryCounts, sortedCategories, subcategoryCounts, subcategoriesByCategory
}
var subcats []string
func getsubcats(cat string) (subcats []string) {
var subcatsMap = make(map[string]int)
for _, prod := range allproducts {
if cat == "" || cat == prod.Category {
if prod.Subcategory != "" {
subcat := strings.Replace(prod.Subcategory, "ΒΌ", "quarter-", -1)
subcat = strings.Replace(subcat, "Β½", "half-", -1)
subcat = strings.Replace(subcat, "1/16", "sixteenth-", -1)
subcat = strings.Replace(subcat, "%", "-pct", -1)
subcat = strings.Replace(subcat, " ", " ", -1)
subcat = strings.Replace(subcat, " ", "-", -1)
subcat = strings.Replace(subcat, "--", "-", -1)
subcat = strings.Replace(subcat, "watt1", "watt-1", -1)
subcat = strings.Replace(subcat, "watt5", "watt-5", -1)
subcatsMap[subcat]++
}
}
}
for subcat := range subcatsMap {
subcats = append(subcats, subcat)
}
return subcats
}
func escapesubcat(subcat string) (escapedsubcat string) {
escapedsubcat = strings.Replace(subcat, "ΒΌ", "quarter-", -1)
escapedsubcat = strings.Replace(escapedsubcat, "Β½", "half-", -1)
escapedsubcat = strings.Replace(escapedsubcat, "1/16", "sixteenth-", -1)
escapedsubcat = strings.Replace(escapedsubcat, "%", "-pct", -1)
escapedsubcat = strings.Replace(escapedsubcat, " ", " ", -1)
escapedsubcat = strings.Replace(escapedsubcat, " ", "-", -1)
escapedsubcat = strings.Replace(escapedsubcat, "--", "-", -1)
escapedsubcat = strings.Replace(escapedsubcat, "watt1", "watt-1", -1)
escapedsubcat = strings.Replace(escapedsubcat, "watt5", "watt-5", -1)
return escapedsubcat
}
func handlecat(c *fiber.Ctx) error {
if c.Params("cat") == "" && c.Params("subcat") == "" {
cathtmlfunc(c)
return nil
}
var catexists bool
var subcatexists bool
catexists = false
for _, cat := range getcats() {
if cat == c.Params("cat") {
catexists = true
break
}
}
subcatexists = false
if c.Params("subcat") != "" {
for _, subcat := range getsubcats("") {
if escapesubcat(subcat) == c.Params("subcat") {
subcatexists = true
break
}
}
}
if c.Params("subcat") != "" && !subcatexists {
log.Printf("subcategory %s does not match any existing subcategory\n", c.Params("subcat"))
c.Redirect("/cat/"+c.Params("cat"), http.StatusMovedPermanently)
return nil
}
if !catexists {
log.Printf("category %s does not match any existing category\n", c.Params("cat"))
c.Redirect("/cat", http.StatusMovedPermanently)
return nil
}
if catexists || (catexists && subcatexists) {
cathtmlfunc(c)
return nil
}
c.SendStatus(fiber.StatusNotFound)
return nil
}
func getCatsAndSubcats(data []byte) ([]string, map[string][]string) {
lines := strings.Split(string(data), "\n")
categoryCounts := make(map[string]int)
subcategoryMap := make(map[string][]string)
for _, line := range lines {
fields := strings.Split(line, ",")
if len(fields) >= 14 && fields[3] == "TRUE" {
category := fields[13]
categoryCounts[category]++
subcategory := fields[14]
if len(subcategory) > 0 {
subcategoryMap[category] = append(subcategoryMap[category], subcategory)
}
}
}
var cats []string
for cat := range categoryCounts {
cats = append(cats, cat)
}
sort.Strings(cats)
return cats, subcategoryMap
}
type xmlTemplateData struct {
Cats []string
SubCatsByCat map[string][]string
Products Products
Update string
}
func generateSitemapXML() string {
xmlSitemapTemplateData := xmlTemplateData{
Products: allproducts,
Update: time.Now().Format("2006-01-02"),
}
_, xmlSitemapTemplateData.Cats, _, xmlSitemapTemplateData.SubCatsByCat = getcategories(allproducts)
var err1 error
xtmpl, err1 := ttmpl.New("index").Funcs(ttmpl.FuncMap{"getsubcats": getsubcats}).Parse(h.XmlSitemap)
if err1 != nil {
log.Println("Error parsing index template:", err1)
}
var result bytes.Buffer
err1 = xtmpl.Execute(&result, xmlSitemapTemplateData)
if err1 != nil {
log.Println("error: ", err1)
}
return result.String()
}
func toFloat(s string) float64 {
if s == "" {
return 0.0
}
f, err := strconv.ParseFloat(s, 64)
if err != nil {
return 0.0
}
return f
}
func checkerBoard(input string) string {
var result strings.Builder
for i, char := range input {
// Wrap every other letter with the specified HTML
if i%2 == 0 {
result.WriteString(fmt.Sprintf("<span class='nv'>%c</span>", char))
} else {
result.WriteRune(char)
}
}
return result.String()
}
const help = "\r\n" +
" {{if .HasAvailableSubCommands}}{{end}} {{if gt (len .Aliases) 0}}\r\n\r\n" +
"{{.NameAndAliases}}{{end}}{{if .HasAvailableSubCommands}}\r\n\r\n" +
"Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand)}}\r\n " +
"{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\r\n\r\n" +
"Flags:\r\n" +
"{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\r\n\r\n" +
"Global Flags:\r\n" +
"{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\r\n\r\n"
type htmlTemplateData struct {
Title string
MetaDesc string
Canonical string
BaseURL string
ImgSRC string // url where images are hosted
OrdersURL string // url where checkout is served from
SiteName string
SiteTagLine string
SiteName1 htmpl.HTML //checkerboard - alternate swap text & bg color
SiteLongName string
SitePrettyName string //ππππππ₯π π€π‘πππ£π.πππ₯
SitePrettyNameCap string //ππππππ₯π π€π‘πππ£π.πππ₯
SitePrettyNameCaps string //ππΈπΎβπΌπππββπΌβπΌ.βπΌπ
SiteAsciiLogo htmpl.HTML
TelegramContact string
TelegramChannel string
Protocol string
RequestHost string
KeyWords string
Style htmpl.HTML
Heading htmpl.HTML
StripePK string
Cats []string
CatsCounts map[string]int
SubCatsCounts map[string]map[string]int
SubCatsByCat map[string][]string
LenAllProducts int
Mobile bool
Gocanvas htmpl.HTML
WasmBinary []string
WasmExecPath string
WasmExecRel string
StyleFontFace htmpl.CSS
Message htmpl.HTML
Page string
Year string
Time string
AboutHTML htmpl.HTML
LinksHTML htmpl.HTML
PolicyHTML htmpl.HTML
TestMode bool
}
func equalsIgnoreCase(a, b string) bool {
return strings.EqualFold(strings.Join(strings.Fields(a), ""), strings.Join(strings.Fields(b), ""))
}
func replace(s, old, new string) string {
return strings.ReplaceAll(s, old, new)
}
func mul(a, b float64) float64 {
return a * b
}
func div(a, b float64) float64 {
return a / b
}
func add(a, b int) int {
return a + b
}
func sub(a, b int) int {
return a - b
}
func safeHTML(s string) htmpl.HTML {
return htmpl.HTML(s)
}
func safeJS(s string) htmpl.JS {
return htmpl.JS(s)
}
func stripProtocol(s string) string {
return strings.Replace(strings.Replace(s, "https://", "", -1), "http://", "", -1)
}
func repeat(s string, count int) string {
var result string
for i := 0; i < count; i++ {
result += s
}
return result
}
func sortsubcats(subcats []string, counts map[string]map[string]int) []string {
sort.Slice(subcats, func(i, j int) bool {
catI, catJ := subcats[i], subcats[j]
countI, countJ := counts[catI]["count"], counts[catJ]["count"]
return countI > countJ
})
return subcats
}
// Product represents a product record line in the products csv
type Product struct {
Enable string
Partno string
Name string
Image1 string
Price string
Quantity string
Shippable string
Minorder string
Maxorder string
Defaultquantity string
Stepquantity string
Mfgpartno string
Mfgname string
Category string
Subcategory string
Location string
Msrp string
Cost string
Typ string
Packagetype string
Technology string
Materials string
Value string
ValUnit string
Resistance string
ResUnit string
Tolerance string
VoltsRating string
AmpsRating string
WattsRating string
TempRating string
TempUnit string
Description1 string
Description2 string
Color1 string
Color2 string
Sourceinfo string
Datasheet string
Docs string
Reference string
Attributes string
Year string
Condition string
Note string
Warning string
CableLengthInches string
LengthInches string
WidthInches string
HeightInches string
WeightLb string
WeightOz string
}
// Products is an array of Product
type Products []Product
var allproducts Products
func readproductscsv(csvFile string) (data []byte) {
data, err := os.ReadFile(csvFile)
if err != nil {
log.Printf(`Error on os.ReadFile("products.csv"): %v`, err)
}
return data
}
func readCSV(csvFile string) (prods Products) {
scanner := bufio.NewScanner(bytes.NewReader(readproductscsv(csvFile)))
for scanner.Scan() {
line := scanner.Text()
fields := strings.Split(line, ",")
if len(fields) < 3 {
continue
}
if fields[3] == "TRUE" {
p := Product{
Image1: fields[0],
Partno: fields[1],
Name: fields[2],
Enable: fields[3],
Price: fields[4],
Quantity: fields[5],
Shippable: fields[6],
Minorder: fields[7],
Maxorder: fields[8],
Defaultquantity: fields[9],
Stepquantity: fields[10],
Mfgpartno: fields[11],
Mfgname: fields[12],
Category: fields[13],
Subcategory: fields[14],
Location: fields[15],
Msrp: fields[16],
Cost: fields[17],
Typ: fields[18],
Packagetype: fields[19],
Technology: fields[20],
Materials: fields[21],
Value: fields[22],
ValUnit: fields[23],
Resistance: fields[24],
ResUnit: fields[25],
Tolerance: fields[26],
VoltsRating: fields[27],
AmpsRating: fields[28],
WattsRating: fields[29],
TempRating: fields[30],
TempUnit: fields[31],
Description1: fields[32],
Description2: fields[33],
Color1: fields[34],
Color2: fields[35],
Sourceinfo: fields[36],
Datasheet: fields[37],
Docs: fields[38],
Reference: fields[39],
Attributes: fields[40],
Year: fields[41],
Condition: fields[42],
Note: fields[43],
Warning: fields[44],
CableLengthInches: fields[45],
LengthInches: fields[46],
WidthInches: fields[47],
HeightInches: fields[48],
WeightLb: fields[49],
WeightOz: fields[50],
}
prods = append(prods, p)
}
}
return prods
}
func scriptExecString(s string) string {
z, err := script.Exec(fmt.Sprintf(`bash -c 'MENV=%s ; if [[ $MENV != "" ]] && [[ -f $MENV ]] ; then source $MENV ; fi ; printf "%s"'`, menvfile, s)).String()
if err == nil {
return strings.TrimSpace(z)
}
return ""
}
func scriptExecStringSlice(s string) []string {
z, err := script.Exec(fmt.Sprintf(`bash -c 'MENV=%s ; if [[ $MENV != "" ]] && [[ -f $MENV ]] ; then source $MENV ; fi ; printf "%s" "%s"'`, menvfile,"%s\n", s)).Slice()
if err == nil {
return z
}
return []string{""}
}
func scriptExecBool(s string) bool {
z, err := script.Exec(fmt.Sprintf(`bash -c 'MENV=%s ; if [[ $MENV != "" ]] && [[ -f $MENV ]] ; then source $MENV ; fi ; printf "%s"'`, menvfile, s)).String()
if err == nil {
b, err := strconv.ParseBool(z)
if err == nil {
return b
}
}
return false
}
func scriptExecArray(s string) string {
y, err := script.Exec(fmt.Sprintf(`bash -c 'MENV=%s ; if [[ $MENV != "" ]] && [[ -f $MENV ]] ; then source $MENV ; fi ; for _i in %s ; do echo "$_i" ; done'`, menvfile, s)).Slice()
if err == nil {
return strings.Join(y, ",")
}
return ""
}
func scriptExecInt(s string) int {
z, err := script.Exec(fmt.Sprintf(`bash -c 'MENV=%s ; if [[ $MENV != "" ]] && [[ -f $MENV ]] ; then source $MENV ; fi ; printf "%s"'`, menvfile, s)).String()
if err == nil {
if z == "" {
return 0
}
i, err := strconv.Atoi(z)
if err == nil {
return i
}
}
return 0
}
const envfiletemplate = `#
# /etc/m2.conf
#
#########################################################################
# M2 CONFIG TEMPLATE
# change config defaults
# or comment values with # to exclude
#########################################################################
### Stripe Configuration ################################################
#-- Live and test API keys - REQUIRED
STRIPELIVEPK='pk_live_...'
STRIPELIVESK='sk_live_...'
STRIPETESTPK='pk_test_...'
STRIPETESTSK='sk_test_...'
#-- Use Test Keys
USETESTKEY=true
### Site Product Data Configuration #####################################
#-- Products CSV path (ex. 'products.csv')
PRODUCTSCVS=''
#-- Image subdomain (ex. 'https://img.magnetosphere.net')
# no trailing slash '/' !
IMGSRC=''
#-- Orders subdomain (ex. 'https://pay.magnetosphere.net')
# no trailing slash '/' !
ORDERSURL=''
### Site Configuration ##################################################
#-- Website (Host) Name (domain minus extension - ex. 'magnetosphere')
SITENAME=''
#-- Website Domain Extension (ex. '.com' '.net')
SITEEXT=''
#-- Site Long Name (ex. 'magnetosphere electronic surplus')
SITELONGNAME=''
#-- Site Tag Line (ex. 'we have the technology')
SITETAGLINE=''
#-- Site Meta Description (ex. 'we have the technology (ββΏβ) electronic surplus for sale')
SITEMETA=''
#-- Site Telegram Contact
# DO NOT INCLUDE 'https://t.me/'
# ex. 'magnetosphere' will display on-site as 'https://t.me/magnetosphere'
TGCONTACT=''
#-- Site Telegram Channel
# DO NOT INCLUDE 'https://t.me/'
# ex. 'magnetospheredotnet' will display on-site as 'https://t.me/magnetospheredotnet'
TGCHANNEL=''
### Web Server Configuration ############################################
#-- Port to serve http on (ex. '9883')
WEBPORT='9883'
`