initialize next generation ui

This commit is contained in:
ゆめ 2022-12-07 09:47:37 -06:00
parent 0c994944aa
commit ed599fef3f
19 changed files with 648 additions and 46 deletions

View file

@ -2,9 +2,13 @@ PROJECT_NAME := yoake
MODULE_PATH := github.com/eternal-flame-AD/${PROJECT_NAME}
CMD_DIR := cmd
WASM_DIR := wasm
COMMANDS := $(patsubst ${CMD_DIR}/%,%,$(shell find ${CMD_DIR}/ -mindepth 1 -maxdepth 1 -type d))
WASM_APPS := $(patsubst ${WASM_DIR}/%,%.wasm,$(shell find ${WASM_DIR}/ -mindepth 1 -maxdepth 1 -type d))
COMMANDSDIST = $(addprefix dist/,${COMMANDS})
WASM_APPSDIST = $(addprefix dist/web/,${WASM_APPS})
ifeq ($(INSTALLDEST),)
INSTALLDEST := /opt/${PROJECT_NAME}
endif
@ -16,8 +20,8 @@ install:
mkdir -p $(INSTALLDEST)
cp -r dist/* $(INSTALLDEST)
build: webroot $(COMMANDSDIST)
chmod -R 755 $(COMMANDSDIST)
build: webroot $(COMMANDSDIST) $(WASM_APPSDIST)
chmod -R 755 $(COMMANDSDIST) $(WASM_APPSDIST)
dev:
while true; do \
@ -30,6 +34,7 @@ dev:
webroot: $(wildcard webroot/**) FORCE
mkdir -p dist
mkdir -p dist/web
cp -r assets dist
cp -r webroot dist
(cd dist/webroot; ../../scripts/webroot-build.fish)
@ -42,6 +47,13 @@ clean:
rm -rf dist/webroot
rm -rf dist
dist/web/%.wasm: ${WASM_DIR}/% FORCE
GOOS=js GOARCH=wasm CGO_ENABLED=0 go build -buildvcs\
-ldflags "-X ${MODULE_PATH}/internal/version.tagVersion=$(VERSION) \
-X ${MODULE_PATH}/internal/version.buildDate=$(BUILDDATE) \
-s -w" \
-o $@ ${MODULE_PATH}/$<
dist/%: ${CMD_DIR}/% FORCE
go build -buildvcs\
-ldflags "-X ${MODULE_PATH}/internal/version.tagVersion=$(VERSION) \
@ -49,5 +61,6 @@ dist/%: ${CMD_DIR}/% FORCE
-gcflags "$(GOGCFLAGS)" \
-o $@ ${MODULE_PATH}/$<
.PHONY: build clean
FORCE:

4
go.mod
View file

@ -7,7 +7,7 @@ require (
github.com/PuerkitoBio/goquery v1.8.0
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
github.com/dgraph-io/badger/v3 v3.2103.4
github.com/eternal-flame-AD/go-apparmor v0.0.3
github.com/eternal-flame-AD/go-apparmor v0.0.4
github.com/eternal-flame-AD/yubigo v0.0.0-20221005082707-ce0c8989e8b1
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c
@ -16,10 +16,12 @@ require (
github.com/gorilla/sessions v1.2.1
github.com/jinzhu/configor v1.2.1
github.com/labstack/echo/v4 v4.9.1
github.com/maxence-charriere/go-app/v9 v9.6.7
github.com/spf13/afero v1.9.3
github.com/stretchr/testify v1.8.1
github.com/twilio/twilio-go v1.1.1
github.com/vanng822/go-premailer v1.20.1
golang.org/x/mod v0.7.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
)

8
go.sum
View file

@ -88,8 +88,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/eternal-flame-AD/go-apparmor v0.0.3 h1:nFOxi6mbE8wpd5aHbSGvjbnaEjYC156IICWTteSgEIo=
github.com/eternal-flame-AD/go-apparmor v0.0.3/go.mod h1:OpqESxf/LXsssooWBPzAoIAC2PtloCT1CmA+glQKYV8=
github.com/eternal-flame-AD/go-apparmor v0.0.4 h1:MSHdwn+lCby8HWm3q4NZRWUejCNlJF86RbX+YWim6/A=
github.com/eternal-flame-AD/go-apparmor v0.0.4/go.mod h1:OpqESxf/LXsssooWBPzAoIAC2PtloCT1CmA+glQKYV8=
github.com/eternal-flame-AD/yubigo v0.0.0-20221005082707-ce0c8989e8b1 h1:B+ad4UMWwNAUsZhLLQCCrEx+cfLsbf0+AbbcfG7RIv0=
github.com/eternal-flame-AD/yubigo v0.0.0-20221005082707-ce0c8989e8b1/go.mod h1:kRnqsWaIjqWNPoCV14+cxs/B9eClc0hKL/I2a3LKOQ4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@ -217,6 +217,8 @@ github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZb
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/maxence-charriere/go-app/v9 v9.6.7 h1:t+wofnLjVsptBB7MNevsFymMYaMIX2hGjLdWgsIFgq4=
github.com/maxence-charriere/go-app/v9 v9.6.7/go.mod h1:UlniES44R5JoD4HsjMNrAqWXSzyw0smM0Ox+QwnO/IE=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@ -323,6 +325,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=

View file

@ -0,0 +1,4 @@
# uinext
Next generation UI based on go-app

View file

@ -0,0 +1,48 @@
package apicall
import (
"encoding/json"
"net/http"
"time"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
func GET(ctx app.Context, path string, dispatch func(app.Context, *http.Response, error)) {
resp, err := http.Get(path)
ctx.Dispatch(func(ctx app.Context) {
dispatch(ctx, resp, err)
})
}
func GetJSON(ctx app.Context, path string, result interface{}, dispatch func(app.Context, error)) {
resp, err := http.Get(path)
if err != nil {
ctx.Dispatch(func(ctx app.Context) {
dispatch(ctx, err)
})
return
}
ctx.Defer(func(app.Context) { resp.Body.Close() })
dec := json.NewDecoder(resp.Body)
ctx.Dispatch(func(ctx app.Context) {
err = dec.Decode(result)
dispatch(ctx, err)
})
}
type RequestAuth struct {
Present bool
Valid bool
Roles []string
Expire time.Time
Ident UserIdent
}
type UserIdent struct {
Username string `json:"username"`
PhotoURL string `json:"photo_url"`
DisplayName string `json:"display_name"`
}

View file

@ -0,0 +1,55 @@
package compo
import (
"fmt"
"github.com/eternal-flame-AD/yoake/internal/uinext/webapp"
"github.com/eternal-flame-AD/yoake/internal/version"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
// source: includes/navbar.tpl.html
type Navbar struct {
app.Compo
LoginUsername string
}
func (n *Navbar) renderBrand() app.UI {
return app.A().Class("navbar-brand", "col-md-3", "col-lg-2", "me-0", "px-3", "fs-6").
Href(webapp.Singleton.BasePath+"/").
Body(
app.Text("夜明け"),
app.Small().Class("fw-lighter", "text-muted", "px-2").Text(fmt.Sprintf("%s - %s", version.Version, version.Date)),
)
}
func (n *Navbar) renderNavBtn() app.UI {
return app.Button().Class("navbar-toggler", "position-absolute", "d-md-none", "collapsed").
Type("button").
Aria("aria-label", "Toggle navigation").
Attr("data-bs-toggle", "collapse").
Attr("data-bs-target", "#sidebar").
Attr("aria-controls", "sidebarMenu").
Attr("aria-expanded", "false").
Body(
app.Span().Class("navbar-toggler-icon"),
)
}
func (n *Navbar) renderAuthUsername() app.UI {
return app.Div().Class("navbar-nav").Body(
app.Div().Class("nav-item", "text-nowrap", "px-3").Body(
app.Text(n.LoginUsername),
),
)
}
func (n *Navbar) Render() app.UI {
return app.Nav().Class("navbar", "sticky-top", "flex-md-nowrap", "p-0").Body(
n.renderBrand(),
n.renderNavBtn(),
n.renderAuthUsername(),
)
}

View file

@ -0,0 +1,137 @@
package compo
import (
"log"
"github.com/eternal-flame-AD/yoake/internal/uinext/apicall"
"github.com/eternal-flame-AD/yoake/internal/uinext/webapp"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type Sidebar struct {
app.Compo
routePath string
Auth apicall.RequestAuth
}
func (c *Sidebar) OnMount(ctx app.Context) {
ctx.Async(func() {
apicall.GetJSON(ctx, "/api/auth/auth.json", &c.Auth, func(ctx app.Context, err error) {
if err != nil {
log.Printf("Failed to get auth info: %v", err)
return
}
log.Printf("Got auth info: %+v", c.Auth)
})
})
}
func (c *Sidebar) OnNav(ctx app.Context) {
c.routePath = ctx.Page().URL().Path
c.Update()
}
func (c *Sidebar) Render() app.UI {
return app.Nav().ID("sidebar").
Class("col-md-3", "col-lg-2", "d-md-block", "bg-light", "sidebar", "collapse").
Body(
app.Div().Class("position-sticky", "pt-3", "sidebar-sticky").Body(
&SidebarItem{
Name: "Dashboard",
Link: webapp.Singleton.BasePath + "/",
CurPath: c.routePath,
Auth: c.Auth,
},
SidebarHeading("Entertainment"),
&SidebarItem{
Name: "YouTube Playlist",
Link: webapp.Singleton.BasePath + "/entertainment/youtube",
CurPath: c.routePath,
Auth: c.Auth,
},
),
)
}
type SidebarItem struct {
app.Compo
Name string
Link string
CurPath string
state SidebarItemState
checkAccess func(auth apicall.RequestAuth) bool
Auth apicall.RequestAuth
HasAccess bool
}
func (c *SidebarItem) OnMount(ctx app.Context) {
c.Update()
}
func (c *SidebarItem) OnUpdate(ctx app.Context) {
if c.checkAccess != nil {
c.HasAccess = c.checkAccess(c.Auth)
} else {
c.HasAccess = true
}
if c.Link == c.CurPath {
if c.HasAccess {
c.state = SidebarItemStateActive
} else {
c.state = SidebarItemStateAccessDenied
}
} else {
if c.HasAccess {
c.state = SidebarItemStateAvailable
} else {
c.state = SidebaritemStateAuthRequired
}
}
}
type SidebarItemState int
const (
SidebarItemStateUnknown SidebarItemState = 0
SidebarItemStateActive SidebarItemState = 1
SidebarItemStateAvailable SidebarItemState = 2
SidebaritemStateAuthRequired SidebarItemState = 3
SidebarItemStateAccessDenied SidebarItemState = 4
)
func (c *SidebarItem) Render() app.UI {
// TODO: allow theming
var img app.HTMLImg
switch c.state {
case SidebaritemStateAuthRequired:
img = app.Img().Src(webapp.Singleton.TrimaImgBase + "icon_t_vista_procedure_ineligible.gif")
case SidebarItemStateAccessDenied:
img = app.Img().Src(webapp.Singleton.TrimaImgBase + "icon_t_vista_procedure_invalid.gif")
case SidebarItemStateActive:
img = app.Img().Src(webapp.Singleton.TrimaImgBase + "icon_t_vista_procedure_optimal.gif")
case SidebarItemStateAvailable:
img = app.Img().Src(webapp.Singleton.TrimaImgBase + "icon_t_vista_procedure_valid.gif")
default:
img = app.Img().Src(webapp.Singleton.TrimaImgBase + "icon_t_vista_procedure_questionable.gif")
}
return app.Ul().Class("nav", "flex-column").Body(
app.Li().Class("nav-item").Body(
app.A().Class("nav-link").Href(c.Link).Body(
app.Span().Class("px-1").Body(img.Style("height", "2.5rem").Style("width", "2.5rem")),
app.Text(c.Name),
),
),
)
}
func SidebarHeading(title string) app.UI {
return app.H6().Class("sidebar-heading", "d-flex", "justify-content-between", "align-items-center", "px-3", "mt-4", "mb-1", "text-muted").Body(
app.Span().Text(title),
)
}

108
internal/uinext/ui/app.go Normal file
View file

@ -0,0 +1,108 @@
package ui
import (
"os"
"regexp"
"strings"
"time"
"github.com/eternal-flame-AD/yoake/internal/uinext/compo"
"github.com/eternal-flame-AD/yoake/internal/uinext/ui/page"
"github.com/eternal-flame-AD/yoake/internal/uinext/webapp"
"github.com/eternal-flame-AD/yoake/internal/util"
"github.com/eternal-flame-AD/yoake/internal/version"
"github.com/labstack/echo/v4"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type App struct {
app.Compo
navbar *compo.Navbar
sidebar *compo.Sidebar
}
func (a *App) Render() app.UI {
if a.navbar == nil {
a.navbar = &compo.Navbar{}
}
if a.sidebar == nil {
a.sidebar = &compo.Sidebar{}
}
return app.Div().ID("app").Body(
a.navbar,
app.Div().Class("row").Body(
a.sidebar,
&page.Router{
Rules: []page.RouterRule{
{
CheckNav: page.RouterMatchPathRegexp(regexp.MustCompile(`^/$`)),
Page: &page.Dashboard{},
},
},
},
),
)
}
const BasePath = "/uinext"
func Register(g *echo.Group) {
webapp.Singleton.BasePath = BasePath
webapp.Singleton.TrimaImgBase = "https://yumechi.jp/img/trima/"
handler := &app.Handler{
Name: "Yoake PMS",
ShortName: "夜明け",
Description: "Yoake PMS - 夜明け",
RawHeaders: util.Join(headTagBootstrap, headTagDayjs, headTagCustom),
Lang: "en",
AutoUpdateInterval: 10 * time.Second,
BackgroundColor: "#FEDFE1",
ThemeColor: "#FEDFE1",
LoadingLabel: "Loading...",
Version: version.Version + "-" + version.Date,
Icon: app.Icon{
Default: webapp.Singleton.TrimaImgBase + "icon_squeeze.gif",
},
}
app.RouteWithRegexp("^"+BasePath+"/.*", new(App))
g.Group("/web").GET("*", func(c echo.Context) error {
handler.ServeHTTP(c.Response(), c.Request())
return nil
})
g.GET("/web/app.wasm", func(c echo.Context) error {
tryPaths := []string{
"web/app.wasm",
"dist/web/app.wasm",
"app.wasm",
}
for _, path := range tryPaths {
if stat, err := os.Stat(path); err == nil && !stat.IsDir() {
return c.File(path)
}
}
return c.NoContent(404)
})
g.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if util.Contain(staticFiles, strings.ToLower(c.Request().URL.Path)) {
handler.ServeHTTP(c.Response(), c.Request())
return nil
}
return next(c)
}
})
g.Group(BasePath).Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
next(c)
if !c.Response().Committed {
handler.ServeHTTP(c.Response(), c.Request())
return nil
}
return nil
}
})
}

View file

@ -0,0 +1,45 @@
package page
import "github.com/maxence-charriere/go-app/v9/pkg/app"
type Dashboard struct {
app.Compo
}
func (d *Dashboard) Render() app.UI {
return BasePage(BasicHeading("Dashboard"),
app.Div().Class("container").Body(
app.Div().Class("row").Body(
app.Div().Class("col").Body(
&Card{
Header: app.Text("Welcome"),
BodyClass: []string{"text-center"},
Body: []app.UI{
app.Blockquote().Class("blockquote").Body(
app.P().Text("夜明け前が一番暗い"),
app.P().Text("The night is darkest just before the dawn."),
),
app.Hr(),
app.Div().ID("welcome").Body(
app.P().Body(
app.Text("Welcome to yoake.yumechi.jp, Yumechi's "),
Abbr("PIM", "Personal Information Manager", "initialism", "https://en.wikipedia.org/wiki/Personal_information_manager"),
app.Text("."),
),
app.P().Body(
app.Text("Built with "),
Abbr("Echo", "Echo HTTP Framework", "", "https://echo.labstack.com/"),
app.Text(", "),
Abbr("Bootstrap", "Bootstrap CSS Framework", "", "https://getbootstrap.com/"),
app.Text(", and "),
Abbr("go-app", "Go PWA Framework", "", "https://go-app.dev/"),
app.Text("."),
),
),
},
},
),
),
),
)
}

View file

@ -0,0 +1,45 @@
package page
import "github.com/maxence-charriere/go-app/v9/pkg/app"
func BasePage(elems ...app.UI) app.UI {
return app.Main().Class("col-md-9", "ms-sm-auto", "col-lg-10", "px-md-4").Body(elems...)
}
func BasicHeading(name string) app.UI {
return app.Div().Body(
app.H1().Class("page-header").Text(name),
app.Hr(),
)
}
type Card struct {
app.Compo
HeaderClass []string
Header app.UI
BodyClass []string
Body []app.UI
}
func (c *Card) Render() app.UI {
return app.Div().Class("card", "border").Body(
app.Div().Class(append(c.HeaderClass, "card-header")...).Body(c.Header),
app.Div().Class(append(c.BodyClass, "card-body")...).Body(c.Body...),
)
}
func Abbr(abbr string, title string, class string, href string) app.UI {
abbrEle := app.Abbr()
if title != "" {
abbrEle = abbrEle.Title(title)
}
if class != "" {
abbrEle = abbrEle.Class(class)
}
if href != "" {
return app.A().Target("_blank").Rel("noopener noreferrer").Href(href).Body(abbrEle.Text(abbr))
}
return abbrEle.Text(abbr)
}

View file

@ -0,0 +1,59 @@
package page
import (
"net/url"
"regexp"
"strings"
"github.com/eternal-flame-AD/yoake/internal/uinext/webapp"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
type Router struct {
app.Compo
matchedPage app.UI
Rules []RouterRule
NotFound app.UI
}
func (r *Router) OnNav(ctx app.Context) {
defer r.Update()
for _, rule := range r.Rules {
urlCopy := *ctx.Page().URL()
if !rule.NoStripPrefix {
bp := webapp.Singleton.BasePath
if bp != "" {
urlCopy.Path = strings.TrimPrefix(urlCopy.Path, bp)
urlCopy.RawPath = strings.TrimPrefix(urlCopy.RawPath, bp)
}
}
if rule.CheckNav(&urlCopy) {
r.matchedPage = rule.Page
return
}
}
r.matchedPage = nil
}
func (r *Router) Render() app.UI {
if r.matchedPage != nil {
return r.matchedPage
}
if r.NotFound != nil {
return r.NotFound
}
return BasePage(BasicHeading("404 Not Found"), app.P().Text("The page you are looking for does not exist."))
}
type RouterRule struct {
NoStripPrefix bool
CheckNav func(*url.URL) bool
Page app.UI
}
func RouterMatchPathRegexp(regex *regexp.Regexp) func(*url.URL) bool {
return func(u *url.URL) bool {
return regex.MatchString(u.Path)
}
}

View file

@ -0,0 +1,36 @@
package ui
var headTagDayjs = []string{
`<script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.6/dayjs.min.js"
integrity="sha256-EfJOqCcshFS/2TxhArURu3Wn8b/XDA4fbPWKSwZ+1B8=" crossorigin="anonymous"></script>`,
` <script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.6/plugin/relativeTime.js"
integrity="sha256-muryXOPFkVJcJO1YFmhuKyXYmGDT2TYVxivG0MCgRzg=" crossorigin="anonymous"></script>`,
` <script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.6/plugin/localizedFormat.js"
integrity="sha256-g+gxm1xmRq4IecSRujv2eKyUCo/i1b5kRnWNcSbYEO0=" crossorigin="anonymous"></script>`,
`<script>
dayjs.extend(window.dayjs_plugin_relativeTime);
dayjs.extend(window.dayjs_plugin_localizedFormat);</script>`,
}
var headTagBootstrap = []string{
`<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.1/dist/jquery.min.js"
integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script>`,
`<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">`,
`<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3"
crossorigin="anonymous"></script>`,
}
var headTagCustom = []string{
`<link rel="stylesheet" href="/style.css">`,
`<link rel="stylesheet" href="/dashboard.css">`,
}
var staticFiles = []string{
"/app-worker.js",
"/app.js",
"/app.css",
"/manifest.webmanifest",
"/wasm_exec.js",
}

View file

@ -0,0 +1,9 @@
package webapp
type IWebApp struct {
BasePath string
TrimaImgBase string
}
var Singleton IWebApp

View file

@ -1,3 +1,5 @@
//go:build linux
package util
import (

View file

@ -32,6 +32,14 @@ func AntiJoin[T comparable](a []T, b []T) []T {
return result
}
func Join[T any](vals ...[]T) []T {
result := make([]T, 0)
for _, val := range vals {
result = append(result, val...)
}
return result
}
func Reverse[T any](a []T) []T {
var result []T
for i := len(a) - 1; i >= 0; i-- {

View file

@ -1,48 +1,9 @@
package version
import (
"runtime/debug"
"time"
)
var (
tagVersion = ""
buildDate = "unknown"
Date = "unknown"
Version = func() string {
info, ok := debug.ReadBuildInfo()
if !ok {
return "unknown"
}
var vcsRevision string
var vcsTime time.Time
var vcsModified bool
for _, setting := range info.Settings {
switch setting.Key {
case "vcs.revision":
vcsRevision = setting.Value
case "vcs.time":
vcsTime, _ = time.Parse(time.RFC3339, setting.Value)
case "vcs.modified":
vcsModified = setting.Value != "false"
}
}
if tagVersion != "" {
vcsRevision = tagVersion
}
if vcsModified {
Date = buildDate
return vcsRevision + "+devel"
} else {
Date = buildDate
if !vcsTime.IsZero() {
Date = vcsTime.Format("2006-01-02T15:04Z07:00")
}
return vcsRevision
}
}()
Date = buildDate
Version = tagVersion
)

View file

@ -0,0 +1,44 @@
//go:build !tinygo
package version
import (
"runtime/debug"
"time"
)
func init() {
info, ok := debug.ReadBuildInfo()
if !ok {
Version = "unknown"
return
}
var vcsRevision string
var vcsTime time.Time
var vcsModified bool
for _, setting := range info.Settings {
switch setting.Key {
case "vcs.revision":
vcsRevision = setting.Value
case "vcs.time":
vcsTime, _ = time.Parse(time.RFC3339, setting.Value)
case "vcs.modified":
vcsModified = setting.Value != "false"
}
}
if tagVersion != "" {
vcsRevision = tagVersion
}
if vcsModified {
Date = buildDate
Version = vcsRevision + "+devel"
} else {
Date = buildDate
if !vcsTime.IsZero() {
Date = vcsTime.Format("2006-01-02T15:04Z07:00")
}
Version = vcsRevision
}
}

View file

@ -14,10 +14,12 @@ import (
"github.com/eternal-flame-AD/yoake/internal/echoerror"
"github.com/eternal-flame-AD/yoake/internal/entertainment"
"github.com/eternal-flame-AD/yoake/internal/filestore"
"github.com/eternal-flame-AD/yoake/internal/gomod"
"github.com/eternal-flame-AD/yoake/internal/health"
"github.com/eternal-flame-AD/yoake/internal/servetpl"
"github.com/eternal-flame-AD/yoake/internal/session"
"github.com/eternal-flame-AD/yoake/internal/twilio"
"github.com/eternal-flame-AD/yoake/internal/uinext/ui"
"github.com/eternal-flame-AD/yoake/internal/utilapi"
"github.com/eternal-flame-AD/yoake/server"
"github.com/gorilla/context"
@ -66,6 +68,11 @@ func Init(hostname string, comm *comm.Communicator, database db.DB, fs filestore
e.Use(middleware.RequestLoggerWithConfig(lc))
}
{
goproxy := e.Group("/goproxy")
e.Use(gomod.Register("/goproxy", goproxy))
}
api := e.Group("/api", echoerror.Middleware(echoerror.JSONWriter))
{
canvaslms.Register(api.Group("/canvas", logMiddleware("api_canvas", nil)), comm)
@ -112,5 +119,6 @@ func Init(hostname string, comm *comm.Communicator, database db.DB, fs filestore
logMiddleware("template", servetpl.ServeTemplateDir(webroot.Root)),
logMiddleware("static", middleware.Static(webroot.Root)))
ui.Register(e.Group(""))
server.RegisterHostname(hostname, &server.Host{Echo: e})
}

14
wasm/app/app.go Normal file
View file

@ -0,0 +1,14 @@
//go:build wasm
package main
import (
"github.com/eternal-flame-AD/yoake/internal/uinext/ui"
"github.com/labstack/echo/v4"
"github.com/maxence-charriere/go-app/v9/pkg/app"
)
func main() {
ui.Register(echo.New().Group(""))
app.RunWhenOnBrowser()
}