diff --git a/backend/custom/cloudflare/TurnStilleCaptcha.js b/backend/custom/cloudflare/TurnStilleCaptcha.js
deleted file mode 100644
index bb68c4b..0000000
--- a/backend/custom/cloudflare/TurnStilleCaptcha.js
+++ /dev/null
@@ -1,82 +0,0 @@
-export class TurnStilleCaptcha {
- sitekey = "{{.SiteKey}}";
- /**
- * Initializes the TurnStilleCaptcha instance.
- * Creates a container for the captcha and appends it to the target element.
- * If the Cloudflare Turnstile script is already loaded, it runs the captcha.
- * Otherwise, it loads the script and initializes the captcha with the given properties.
- * @param {HTMLElement} target - The element to attach the captcha container to.
- * @param {Object} [props={}] - Optional properties for captcha initialization, such as theme.
- */
-
- constructor(target, props = {}) {
- // create holder
- this.holder = document.createElement("div");
- this.holder.id = "turnstile-container";
- this.theme = props.theme || "auto";
- target.appendChild(this.holder);
-
-
- // execute code
- if (window.turnstile) {
- this.runCaptcha();
- } else {
- this.loadCloudflareScript();
- }
- }
- runCaptcha() {
- setTimeout(() => {
- if (globalThis.turnstileInstance) {
- window.turnstile.remove(globalThis.turnstileInstance);
- }
- globalThis.turnstileInstance = window.turnstile.render(this.holder, {
- sitekey: this.sitekey,
- theme: this.theme,
- callback: (token) => {
- if (token) {
- const event = new CustomEvent("token", {
- detail: token,
- bubbles: true,
- });
- this.holder.dispatchEvent(event);
- }
- },
- error: (error) => {
- const event = new CustomEvent("failure", {
- detail: error,
- bubbles: true,
- });
- this.holder.dispatchEvent(event);
- window.turnstile.reset(globalThis.turnstileInstance);
- },
- });
- }, 1000);
- }
-
- loadCloudflareScript() {
- const script = document.createElement("script");
- script.id = "turnstile-script";
- script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
- // script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
- script.async = true;
- script.defer = true;
-
- script.onload = () => {
- const event = new CustomEvent("loaded", {
- detail: "Turnstile script loaded",
- bubbles: true,
- });
- this.holder.dispatchEvent(event);
-
- this.runCaptcha();
- };
- script.onerror = () => {
- const event = new CustomEvent("failure", {
- detail: "Failed to load Turnstile script",
- bubbles: true,
- });
- this.holder.dispatchEvent(event);
- };
- document.head.appendChild(script);
- }
-}
diff --git a/backend/custom/cloudflare/cloudflare.go b/backend/custom/cloudflare/cloudflare.go
deleted file mode 100644
index 72cabf5..0000000
--- a/backend/custom/cloudflare/cloudflare.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package cloudflare
-
-import (
- "bytes"
- "encoding/json"
- "errors"
- "html/template"
- "io"
- "net/http"
- "strings"
-
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
- "github.com/pocketbase/pocketbase/tools/router"
-)
-
-var VerifyUrl = "https://challenges.cloudflare.com/turnstile/v0/siteverify"
-
-type TurnstileResponse struct {
- Success bool `json:"success"`
- ErrorCodes []string `json:"error-codes,omitempty"`
- ChallengeTS string `json:"challenge_ts,omitempty"`
- Hostname string `json:"hostname,omitempty"`
-}
-
-type TurnStilleCaptcha struct {
- SiteKey string
- SecretKey string
-}
-
-func ServeTurnstilleCaptchaJS(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
- return se.Router.GET("/api/email/script.js", func(e *core.RequestEvent) error {
- // www.abrasive.ma-al.pl
- // siteKey: "0x4AAAAAABdgeAdu4Pxxovj3"
- // secretKey: "0x4AAAAAABdgeHJDjMwmeX5aXaXGh6HWZbw"
-
- settings, err := GetSettings(app)
- if err != nil {
- return err
- }
-
- file, err := JS.ReadFile("TurnStilleCaptcha.js")
- if err != nil {
- return err
- }
- templ, err := template.New("test").Parse(string(file))
-
- buf := bytes.Buffer{}
- templ.Execute(&buf, map[string]interface{}{
- "SiteKey": settings.SiteKey,
- })
- if err != nil {
- return err
- }
-
- e.Response.Header().Set("Content-Type", "application/javascript")
- return e.String(http.StatusOK, buf.String())
- })
-}
-
-func GetSettings(app *pocketbase.PocketBase) (*TurnStilleCaptcha, error) {
- record, err := app.FindFirstRecordByFilter("settings", "key='turnstile'", nil)
-
- settings := TurnStilleCaptcha{}
- json.Unmarshal([]byte(record.GetString("value")), &settings)
-
- if err != nil {
- return nil, err
- }
-
- return &settings, nil
-}
-
-func VerifyTurnstile(app *pocketbase.PocketBase, token, ip string) error {
- conf, err := GetSettings(app)
- if err != nil {
- return err
- }
-
- data := map[string]string{"secret": conf.SecretKey, "response": token, "remoteip": ip}
- jsonData, _ := json.Marshal(data)
- resp, err := http.Post(VerifyUrl, "application/json", bytes.NewBuffer(jsonData))
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- var turnstileResp TurnstileResponse
- body, _ := io.ReadAll(resp.Body)
- json.Unmarshal(body, &turnstileResp)
-
- if !turnstileResp.Success {
- return errors.New(turnstileResp.ChallengeTS + ": " + strings.Join(turnstileResp.ErrorCodes, " "))
- }
- return nil
-}
diff --git a/backend/custom/cloudflare/js.go b/backend/custom/cloudflare/js.go
deleted file mode 100644
index 6a1e806..0000000
--- a/backend/custom/cloudflare/js.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package cloudflare
-
-import (
- "embed"
-)
-
-//go:embed TurnStilleCaptcha.js
-var JS embed.FS
diff --git a/backend/custom/custom.go b/backend/custom/custom.go
deleted file mode 100644
index e8c98c4..0000000
--- a/backend/custom/custom.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package custom
-
-import (
- "pocketbase/custom/cloudflare"
- "pocketbase/custom/gtm"
- "pocketbase/custom/mail"
- "pocketbase/custom/manifest"
- "pocketbase/custom/proxy"
- "pocketbase/custom/seo"
- "pocketbase/custom/supervise"
- "pocketbase/custom/version"
- webpconverter "pocketbase/custom/webpConverter"
-
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
-)
-
-func LoadCustomCode(app *pocketbase.PocketBase, se *core.ServeEvent) error {
-
- // include supervised subprocess if command provided
- supervise.ServeSubprocessSupervisor(app, se)
-
- // include serving js code from cloudflare
- cloudflare.ServeTurnstilleCaptchaJS(app, se)
-
- // include sending emails service
- mail.ServeMailSender(app, se)
-
- // include robots.txt endpoint
- seo.ServeRobotsTxt(app, se)
-
- // include feeds endpoint
- seo.ServeFeeds(app, se)
- seo.ServeFeedsIndex(app, se)
-
- // include proxy to serve nuxt app
- proxy.ServeProxyPassingToNuxt(app, se)
-
- // create endpoint to serve version information
- version.ServeVersionInfo(app, se)
-
- // include endpoint to server GTM script
- gtm.ServeTagMangerJS(app, se)
-
- // include endpoint serving manifest
- manifest.ServeManifst(app, se)
-
- return nil
-}
-
-func ExtendApp(app *pocketbase.PocketBase) *pocketbase.PocketBase {
- app.RootCmd.PersistentFlags().String("proxy", "", "inner proxy target")
- app.RootCmd.PersistentFlags().String("subcommand", "", "provide command with params like cli that will be executed and supervised by main process")
-
- // include webp converter
- app.OnRecordCreate().Bind(webpconverter.CreateEventHandler(app))
- app.OnRecordUpdate().Bind(webpconverter.CreateEventHandler(app))
- app.OnFileDownloadRequest().Bind(webpconverter.ThumbEventHandler(app))
-
- return app
-}
diff --git a/backend/custom/gtm/gtm.go b/backend/custom/gtm/gtm.go
deleted file mode 100644
index 4dfa642..0000000
--- a/backend/custom/gtm/gtm.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package gtm
-
-import (
- "bytes"
- "encoding/json"
- "net/http"
- "text/template"
-
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
- "github.com/pocketbase/pocketbase/tools/router"
-)
-
-func ServeTagMangerJS(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
- return se.Router.GET("/api/gtm/script.js", func(e *core.RequestEvent) error {
-
- settings, err := GetSettings(app)
- if err != nil {
- return err
- }
-
- file, err := JS.ReadFile("gtm.js")
- if err != nil {
- return err
- }
- templ, err := template.New("test").Parse(string(file))
-
- buf := bytes.Buffer{}
- templ.Execute(&buf, map[string]interface{}{
- "GtagID": settings.GtagID,
- })
- if err != nil {
- return err
- }
-
- e.Response.Header().Set("Content-Type", "application/javascript")
- return e.String(http.StatusOK, buf.String())
- })
-}
-
-type GTagSettings struct {
- GtagID string `json:"gtagID"`
-}
-
-func GetSettings(app *pocketbase.PocketBase) (*GTagSettings, error) {
- record, err := app.FindFirstRecordByFilter("settings", "key='gtagID'", nil)
-
- settings := GTagSettings{}
- json.Unmarshal([]byte(record.GetString("value")), &settings)
-
- if err != nil {
- return nil, err
- }
-
- return &settings, nil
-}
diff --git a/backend/custom/gtm/gtm.js b/backend/custom/gtm/gtm.js
deleted file mode 100644
index 561f9ae..0000000
--- a/backend/custom/gtm/gtm.js
+++ /dev/null
@@ -1,29 +0,0 @@
-export class GTM {
- gtagID = "{{.GtagID}}";
-
- constructor() {
- this.insertScript(window, document, 'script', 'dataLayer', this.gtagID);
- this.insertNoScript();
- }
-
-
- insertScript(w, d, s, l, i) {
- w[l] = w[l] || []; w[l].push({
- 'gtm.start':
- new Date().getTime(), event: 'gtm.js'
- }); var f = d.getElementsByTagName(s)[0],
- j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src =
- 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
- }
-
- insertNoScript() {
- const noscript = document.createElement("noscript");
- const iframe = document.createElement("iframe");
- iframe.src = "https://www.googletagmanager.com/ns.html?id=" + this.gtagID;
- iframe.height = 0;
- iframe.width = 0;
- iframe.style = "display:none;visibility:hidden";
- noscript.appendChild(iframe);
- document.body.appendChild(noscript);
- }
-}
diff --git a/backend/custom/gtm/js.go b/backend/custom/gtm/js.go
deleted file mode 100644
index fcf53fa..0000000
--- a/backend/custom/gtm/js.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package gtm
-
-import (
- "embed"
-)
-
-//go:embed gtm.js
-var JS embed.FS
diff --git a/backend/custom/mail/mail.go b/backend/custom/mail/mail.go
deleted file mode 100644
index 9592e92..0000000
--- a/backend/custom/mail/mail.go
+++ /dev/null
@@ -1,175 +0,0 @@
-package mail
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "net"
- "net/http"
- "net/mail"
- "pocketbase/custom/cloudflare"
- "strings"
- "text/template"
- "time"
-
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
- "github.com/pocketbase/pocketbase/tools/mailer"
- "github.com/pocketbase/pocketbase/tools/router"
-)
-
-type EmailData struct {
- Name string `json:"name"`
- Email string `json:"email"`
- Token string `json:"token"`
- Message string `json:"message"`
- Phone string `json:"phone"`
- LangIso string `json:"lang_iso"`
-}
-
-type MailsSettings struct {
- ReceiverMail string `json:"receiver_mail"`
- ReceiverName string `json:"receiver_name"`
- Subject string `json:"subject"`
-}
-
-func ServeMailSender(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
- return se.Router.POST("/api/email/send", func(e *core.RequestEvent) error {
- // name := e.Request.PathValue("name")
-
- data := EmailData{}
- if err := e.BindBody(&data); err != nil {
- e.Response.Header().Set("Content-Type", "application/json")
- app.Logger().Error(err.Error(), "type", "mail")
- return e.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
- }
-
- if pass, err := validateMX(data.Email); !pass && err != nil {
- e.Response.Header().Set("Content-Type", "application/json")
- app.Logger().Error("Invalid email address.", "type", "mail", "error", err.Error())
- return e.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid email address."})
- }
-
- // fmt.Printf("e.Request.Body: %v IP: %s\n", data, e.RealIP())
-
- err := cloudflare.VerifyTurnstile(app, data.Token, e.RealIP())
- if err != nil {
- e.Response.Header().Set("Content-Type", "application/json")
- app.Logger().Error("Captcha verification failed.", "type", "mail", "error", err.Error())
- return e.JSON(http.StatusBadRequest, map[string]string{"error": "Captcha verification failed."})
- }
-
- eamil_body, err := app.FindFirstRecordByFilter("email_template", fmt.Sprintf("name='contact_form'&&id_lang='%s'", data.LangIso), nil)
- if err != nil {
- e.Response.Header().Set("Content-Type", "application/json")
- app.Logger().Error("Template not available", "type", "mail", "error", err.Error())
- return e.JSON(http.StatusBadRequest, map[string]string{"error": "Template not available"})
- }
- templ, err := template.New("test").Parse(eamil_body.GetString("template"))
- if err != nil {
- app.Logger().Error("Template parsing error.", "type", "mail", "error", err.Error())
- e.Response.Header().Set("Content-Type", "application/json")
- return e.JSON(http.StatusBadRequest, map[string]string{"error": "Template parsing error."})
- }
-
- buf := bytes.Buffer{}
- templ.Execute(&buf, map[string]interface{}{
- "Data": time.Now().Local().Format("2006-01-02 15:04:05"),
- "Name": data.Name,
- "Email": data.Email,
- "Message": strings.ReplaceAll(data.Message, "\n", "
"),
- "Phone": data.Phone,
- })
-
- mailsSettings, err := getSettings(app)
- if err != nil {
- app.Logger().Error("Mails settings corrupted", "type", "mail", "error", err.Error())
- e.Response.Header().Set("Content-Type", "application/json")
- return e.JSON(http.StatusBadRequest, map[string]string{"error": "Mails settings corrupted"})
- }
-
- cc := []mail.Address{{Address: data.Email, Name: data.Name}}
- to := []mail.Address{{Address: mailsSettings.ReceiverMail, Name: mailsSettings.ReceiverName}}
-
- message := &mailer.Message{
- From: mail.Address{
- Address: e.App.Settings().Meta.SenderAddress,
- Name: e.App.Settings().Meta.SenderName,
- },
- Cc: cc,
- To: to,
- Subject: mailsSettings.Subject,
- HTML: buf.String(),
- }
-
- err = e.App.NewMailClient().Send(message)
- if err != nil {
- app.Logger().Error("Mails sending error", "type", "mail", "error", err.Error())
- e.Response.Header().Set("Content-Type", "application/json")
- return e.JSON(http.StatusBadRequest, map[string]string{"error": "Mails sending error"})
- }
-
- receivers := formReceiversList(to, cc, []mail.Address{})
- app.Logger().Info("Mail Sent", "type", "mail", "receivers", receivers)
- return e.JSON(http.StatusOK, map[string]string{"status": "success", "message": "Email sent successfully. to " + receivers})
- })
-}
-
-func formReceiversList(to []mail.Address, cc []mail.Address, bcc []mail.Address) string {
-
- res := ""
-
- for _, a := range to {
- res = fmt.Sprintf("%s %s<%s>", res, a.Name, a.Address)
- }
-
- for _, a := range cc {
- res = fmt.Sprintf("%s %s<%s>", res, a.Name, a.Address)
- }
-
- for _, a := range bcc {
- res = fmt.Sprintf("%s %s<%s>", res, a.Name, a.Address)
- }
- return res
-}
-func getSettings(app *pocketbase.PocketBase) (*MailsSettings, error) {
- record, err := app.FindFirstRecordByFilter("settings", "key='contact_page'", nil)
-
- settings := MailsSettings{}
- json.Unmarshal([]byte(record.GetString("value")), &settings)
-
- if err != nil {
- return nil, err
- }
-
- return &settings, nil
-}
-
-func extractDomain(email string) string {
- parts := strings.Split(email, "@")
- if len(parts) != 2 {
- return ""
- }
- return strings.TrimSpace(parts[1])
-}
-
-// validateMX checks if the domain has valid MX records or A records as a fallback
-func validateMX(email string) (bool, error) {
- // Check MX records
-
- domain := extractDomain(email)
- mxRecords, err := net.LookupMX(domain)
- if err != nil {
- return false, fmt.Errorf("'MX' records for %s not found", domain)
- } else if len(mxRecords) > 0 {
- // At least one MX record exists
- return true, nil
- }
-
- // Fallback: Check for A records (some domains accept mail via A records)
- aRecords, err := net.LookupHost(domain)
- if err != nil {
- return false, fmt.Errorf("'A' record for %s not found", domain)
- }
- return len(aRecords) > 0, nil
-}
diff --git a/backend/custom/manifest/manifest.go b/backend/custom/manifest/manifest.go
deleted file mode 100644
index 60bc24c..0000000
--- a/backend/custom/manifest/manifest.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package manifest
-
-import (
- "net/http"
-
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
- "github.com/pocketbase/pocketbase/tools/router"
-)
-
-type Manifest struct {
- Name string `json:"name"`
- ShortName string `json:"short_name"`
- Description string `json:"description"`
- Icons []Icon `json:"icons"`
- StartURL string `json:"start_url"`
- Display string `json:"display"`
- BackgroundColor string `json:"background_color"`
- ThemeColor string `json:"theme_color"`
- Lang string `json:"lang"`
- Author string `json:"author"`
- OgHost string `json:"ogHost"`
- Orientation string `json:"orientation"`
-}
-
-type Icon struct {
- Src string `json:"src"`
- Sizes string `json:"sizes"`
- Type string `json:"type"`
-}
-
-func ServeManifst(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
- return se.Router.GET("/api/manifest.json", func(e *core.RequestEvent) error {
- manifest, err := GetSettings(app)
- if err != nil {
- return err
- }
- e.Response.Header().Add("content-type", "application/json")
- return e.String(http.StatusOK, manifest)
- })
-}
-
-func GetSettings(app *pocketbase.PocketBase) (string, error) {
- record, err := app.FindFirstRecordByFilter("settings", "key='manifest'", nil)
- if err != nil {
- return "", err
- }
-
- return record.GetString("value"), nil
-}
diff --git a/backend/custom/proxy/proxy.go b/backend/custom/proxy/proxy.go
deleted file mode 100644
index e47a16d..0000000
--- a/backend/custom/proxy/proxy.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package proxy
-
-import (
- "context"
- "errors"
- "fmt"
- "log/slog"
- "net"
- "net/http"
- "net/http/httputil"
- "net/url"
- "time"
-
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
- "github.com/pocketbase/pocketbase/tools/router"
-)
-
-var Logger *slog.Logger
-
-func ServeProxyPassingToNuxt(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
- Logger = app.Logger()
-
- proxyUrl, _ := app.RootCmd.Flags().GetString("proxy")
-
- if len(proxyUrl) > 0 {
- target, _ := url.Parse(proxyUrl) // Node.js app
-
- proxy := httputil.NewSingleHostReverseProxy(target)
-
- originalDirector := proxy.Director
- proxy.Director = func(req *http.Request) {
- originalDirector(req)
- req.Host = target.Host
- }
- proxy.Transport = &loggingTransport{http.DefaultTransport}
-
- proxy.ErrorHandler = func(rw http.ResponseWriter, req *http.Request, err error) {
- if errors.Is(err, context.Canceled) {
- return
- }
- app.Logger().Error(fmt.Sprintf("Proxy error: %v for %s %s", err, req.Method, req.URL.Path), "type", "proxy")
- http.Error(rw, "Proxy error", http.StatusBadGateway)
- }
-
- return se.Router.Any("/", func(e *core.RequestEvent) error {
- // Ping the backend with a HEAD request (or TCP dial)
- backendUp := isBackendAlive(target)
- if !backendUp {
- app.Logger().Error(fmt.Sprintf("Backend %s is unreachable, sending 502", target), "type", "proxy", "path", e.Request.URL.Path, "remoteIP", e.Request.RemoteAddr)
- e.Response.WriteHeader(http.StatusBadGateway)
- e.Response.Write([]byte("502 Backend is unavailable"))
- return nil
- }
-
- // Forward to Node.js
- proxy.ServeHTTP(e.Response, e.Request)
- return nil
- })
- } else {
- return nil
- }
-}
-
-func isBackendAlive(target *url.URL) bool {
- conn, err := net.DialTimeout("tcp", target.Host, 500*time.Millisecond)
- if err != nil {
- return false
- }
- conn.Close()
- return true
-}
-
-type loggingTransport struct {
- rt http.RoundTripper
-}
-
-func (t *loggingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
- start := time.Now()
-
- // Do the actual request
- resp, err := t.rt.RoundTrip(req)
- duration := time.Since(start)
-
- // Prepare fields
- remoteAddr := req.RemoteAddr
- if ip := req.Header.Get("X-Real-IP"); len(ip) > 0 {
- remoteAddr = ip
- }
- method := req.Method
- uri := req.URL.RequestURI()
- // proto := req.Proto
- status := 0
- size := 0
-
- if resp != nil {
- status = resp.StatusCode
- if resp.ContentLength > 0 {
- size = int(resp.ContentLength)
- }
- }
-
- referer := req.Referer()
- ua := req.UserAgent()
- // timestamp := time.Now().Format("02/Jan/2006:15:04:05 -0700")
-
- Logger.Info(
- fmt.Sprintf("%s %s", method, uri),
- "type", "proxy",
- // "method", method,
- // "url", uri,
- "referer", referer,
- "remoteIP", remoteAddr,
- "userAgent", ua,
- "execTime", fmt.Sprintf("%.4fms", float64(duration.Milliseconds())),
- "status", status,
- "size", size,
- )
-
- return resp, err
-}
diff --git a/backend/custom/seo/feeds.go b/backend/custom/seo/feeds.go
deleted file mode 100644
index cce7998..0000000
--- a/backend/custom/seo/feeds.go
+++ /dev/null
@@ -1,194 +0,0 @@
-package seo
-
-import (
- "encoding/json"
- "encoding/xml"
- "fmt"
- "net/http"
- "path"
- "time"
-
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
- "github.com/pocketbase/pocketbase/tools/router"
-)
-
-type MenuRecord struct {
- Active bool `json:"active"`
- CollectionID string `json:"collectionId"`
- CollectionName string `json:"collectionName"`
- Created string `json:"created"`
- ID string `json:"id"`
- IDLang string `json:"id_lang"`
- IDPage string `json:"id_page"`
- IDParent string `json:"id_parent"`
- IsDefault bool `json:"is_default"`
- IsRoot bool `json:"is_root"`
- LinkRewrite string `json:"link_rewrite"`
- LinkTitle string `json:"link_title"`
- MetaDescription string `json:"meta_description"`
- MetaTitle string `json:"meta_title"`
- Name string `json:"name"`
- PageName string `json:"page_name"`
- PositionID int `json:"position_id"`
- Updated string `json:"updated"`
- Url string `json:"url"`
-}
-
-type UrlSet struct {
- XMLName xml.Name `xml:"urlset"`
- Xmlns string `xml:"xmlns,attr"`
- Urls []Url `xml:"url"`
-}
-
-type Url struct {
- Loc string `xml:"loc"`
- LastMod string `xml:"lastmod,omitempty"`
- ChangeFreq string `xml:"changefreq,omitempty"`
- Priority string `xml:"priority,omitempty"`
-}
-
-func ServeFeeds(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
- return se.Router.GET("/feeds/{lang}/sitemap.xml", func(e *core.RequestEvent) error {
-
- lang := e.Request.PathValue("lang")
-
- urls, err := getLocations(app, lang)
- if err != nil {
- return err
- }
-
- xx := UrlSet{
- Xmlns: "http://www.sitemaps.org/schemas/sitemap/0.9",
- Urls: urls,
- }
-
- bytes, err := xml.MarshalIndent(xx, "", " ")
- if err != nil {
- return err
- }
-
- e.Response.Header().Add("content-type", "text/xml")
- return e.String(http.StatusOK, xml.Header+string(bytes))
- })
-}
-
-func getLocations(app *pocketbase.PocketBase, lang string) ([]Url, error) {
- records, err := app.FindRecordsByFilter("menu_view", fmt.Sprintf("id_lang='%s'&&active=true", lang), "position_id", 200, 0)
- if err != nil {
- return nil, err
- }
-
- baseUrl, err := getBaseUrl(app)
- if err != nil {
- return nil, err
- }
-
- locations := []Url{}
- lastMod := time.Now().Add(time.Hour * 24 * 7 * -1).Format("2006-01-02")
-
- for _, r := range records {
- rec := MenuRecord{}
- x, _ := r.MarshalJSON()
- json.Unmarshal(x, &rec)
- if rec.IsRoot {
- continue
- }
- if len(rec.Url) > 0 {
- continue
- }
- if rec.IsDefault {
-
- locations = append(locations, Url{
- Loc: baseUrl + path.Join(lang),
- LastMod: lastMod,
- ChangeFreq: "weekly",
- Priority: "1.0",
- })
- }
- locations = append(locations, Url{
- Loc: baseUrl + path.Join(lang, rec.IDPage, rec.LinkRewrite),
- LastMod: lastMod,
- ChangeFreq: "weekly",
- Priority: "1.0",
- })
- }
-
- return locations, nil
-}
-
-type BaseUrl struct {
- BaseURL string `json:"baseUrl"`
-}
-
-func getBaseUrl(app *pocketbase.PocketBase) (string, error) {
- record, err := app.FindFirstRecordByFilter("settings", "key='baseUrl'", nil)
- if err != nil {
- return "", err
- }
-
- settings := BaseUrl{}
- json.Unmarshal([]byte(record.GetString("value")), &settings)
-
- if err != nil {
- return "", err
- }
-
- return settings.BaseURL, nil
-}
-
-type SitemapIndex struct {
- XMLName xml.Name `xml:"sitemapindex"`
- Xmlns string `xml:"xmlns,attr"`
- Sitemaps []Sitemap `xml:"sitemap"`
-}
-
-// Sitemap represents each entry
-type Sitemap struct {
- Loc string `xml:"loc"`
- LastMod string `xml:"lastmod"`
-}
-
-func ServeFeedsIndex(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
- return se.Router.GET("/feeds/index.xml", func(e *core.RequestEvent) error {
- index, err := makeSiteMapIndex(app)
- if err != nil {
- return err
- }
-
- bytes, err := xml.MarshalIndent(index, "", " ")
- if err != nil {
- return err
- }
-
- e.Response.Header().Add("content-type", "text/xml")
- return e.String(http.StatusOK, xml.Header+string(bytes))
- })
-}
-
-func makeSiteMapIndex(app *pocketbase.PocketBase) (*SitemapIndex, error) {
- index := &SitemapIndex{
- Xmlns: "http://www.sitemaps.org/schemas/sitemap/0.9",
- }
-
- bseUrl, err := getBaseUrl(app)
- if err != nil {
- return nil, err
- }
-
- langs, err := app.FindRecordsByFilter("lang", "active=true", "", 200, 0)
- if err != nil {
- return index, err
- }
-
- lastMod := time.Now().Add(time.Hour * 24 * 7 * -1).Format("2006-01-02")
-
- for _, l := range langs {
- index.Sitemaps = append(index.Sitemaps, Sitemap{
- Loc: bseUrl + path.Join("feeds/", l.Id, "sitemap.xml"),
- LastMod: lastMod,
- })
- }
-
- return index, nil
-}
diff --git a/backend/custom/seo/robots.go b/backend/custom/seo/robots.go
deleted file mode 100644
index 1db5c49..0000000
--- a/backend/custom/seo/robots.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package seo
-
-import (
- "encoding/json"
- "fmt"
- "net/http"
- "strings"
-
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
- "github.com/pocketbase/pocketbase/tools/router"
-)
-
-type Robots struct {
- Robots []string
-}
-
-func ServeRobotsTxt(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
- return se.Router.GET("/robots.txt", func(e *core.RequestEvent) error {
-
- text, err := getRobots(app)
- if err != nil {
- return err
- }
- return e.String(http.StatusOK, text)
- })
-}
-
-func getRobots(app *pocketbase.PocketBase) (string, error) {
- record, err := app.FindFirstRecordByFilter("settings", "key='robots_txt'", nil)
- if err != nil {
- return "", err
- }
-
- settings := Robots{}
- json.Unmarshal([]byte(record.GetString("value")), &settings)
-
- if err != nil {
- return "", err
- }
-
- baseUrl, err := getBaseUrl(app)
- if err != nil {
- return "", err
- }
-
- settings.Robots = append(settings.Robots, fmt.Sprintf("\n\nSitemap: %s%s", baseUrl, "feeds/index.xml"))
-
- return strings.Join(settings.Robots, "\n"), nil
-}
diff --git a/backend/custom/supervise/supervise.go b/backend/custom/supervise/supervise.go
deleted file mode 100644
index 3a6ad7d..0000000
--- a/backend/custom/supervise/supervise.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package supervise
-
-import (
- "context"
- "log"
- "os"
- "os/exec"
- "os/signal"
- "strings"
- "sync"
- "syscall"
- "time"
-
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
- "github.com/pocketbase/pocketbase/tools/router"
-)
-
-func ServeSubprocessSupervisor(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
-
- command, _ := app.RootCmd.PersistentFlags().GetString("subcommand")
-
- if len(command) > 0 {
- cmdsub := strings.Split(command, " ")
- if len(cmdsub) > 0 {
- startNodeProcessSupervised(cmdsub[0], strings.Join(cmdsub[1:], " "))
- }
- }
- return nil
-}
-
-func startNodeProcessSupervised(command string, args ...string) {
- const maxRetries = 3
- const retryDelay = 30 * time.Second
-
- var (
- cmdMu sync.Mutex
- cmd *exec.Cmd
- )
-
- stopChan := make(chan os.Signal, 1)
- signal.Notify(stopChan, os.Interrupt, syscall.SIGTERM)
-
- go func() {
- retries := 0
-
- for {
- select {
- case <-stopChan:
- log.Println("Received shutdown signal. Terminating subprocess...")
- cmdMu.Lock()
- if cmd != nil && cmd.Process != nil {
- _ = cmd.Process.Signal(syscall.SIGTERM)
- }
- cmdMu.Unlock()
- return
-
- default:
- ctx, cancel := context.WithCancel(context.Background())
- cmdMu.Lock()
- cmd = exec.CommandContext(ctx, command, args...)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- cmdMu.Unlock()
-
- log.Printf("Starting Process: %s %v\n", command, args)
- err := cmd.Run()
- cancel() // cancel the context when done
-
- if err != nil {
- log.Printf("Process exited with error: %v\n", err)
- retries++
- if retries >= maxRetries {
- log.Printf("Process failed %d times. Shutting down application...", retries)
- // _ = app.ResetBootstrapState()
- os.Exit(1)
- }
- log.Printf("Retrying in %s (%d/%d)...", retryDelay, retries, maxRetries)
- time.Sleep(retryDelay)
- } else {
- log.Printf("Process exited normally. Resetting retry count.")
- retries = 0
- }
- }
- }
- }()
-
-}
diff --git a/backend/custom/version/version.go b/backend/custom/version/version.go
deleted file mode 100644
index d976c11..0000000
--- a/backend/custom/version/version.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package version
-
-import (
- "net/http"
-
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
- "github.com/pocketbase/pocketbase/tools/router"
-)
-
-var Version string
-var BuildDate string
-var Company string
-var CompanyUrl string
-
-func ServeVersionInfo(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
- return se.Router.GET("/api/version/send", func(e *core.RequestEvent) error {
-
- return e.JSON(http.StatusOK, map[string]string{
- "version": Version,
- "build_date": BuildDate,
- "company": Company,
- "company_url": CompanyUrl,
- })
- })
-}
diff --git a/backend/custom/webpConverter/memFileReader.go b/backend/custom/webpConverter/memFileReader.go
deleted file mode 100644
index a3d0b5a..0000000
--- a/backend/custom/webpConverter/memFileReader.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package webpconverter
-
-import (
- "bytes"
- "io"
-)
-
-type memFileReader struct {
- data []byte
-}
-
-func (m *memFileReader) Open() (io.ReadSeekCloser, error) {
- return readSeekCloser{bytes.NewReader(m.data)}, nil
-}
diff --git a/backend/custom/webpConverter/readSeekCloser.go b/backend/custom/webpConverter/readSeekCloser.go
deleted file mode 100644
index 54444e1..0000000
--- a/backend/custom/webpConverter/readSeekCloser.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package webpconverter
-
-import "bytes"
-
-type readSeekCloser struct {
- *bytes.Reader
-}
-
-func (r readSeekCloser) Close() error { return nil }
diff --git a/backend/custom/webpConverter/webp_convert.go b/backend/custom/webpConverter/webp_convert.go
deleted file mode 100644
index d57381c..0000000
--- a/backend/custom/webpConverter/webp_convert.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package webpconverter
-
-import (
- "bytes"
- "image"
- "image/png"
- "io"
- "path/filepath"
- "strings"
-
- "github.com/chai2010/webp"
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
- "github.com/pocketbase/pocketbase/tools/filesystem"
- "github.com/pocketbase/pocketbase/tools/hook"
-)
-
-func CreateEventHandler(app *pocketbase.PocketBase) *hook.Handler[*core.RecordEvent] {
- return &hook.Handler[*core.RecordEvent]{
- Func: func(e *core.RecordEvent) error {
- fieldsData := e.Record.FieldsData()
- for _, v := range fieldsData {
- if files, ok := v.([]interface{}); ok {
- for _, f := range files {
- if file, ok := f.(*filesystem.File); ok {
- if shouldBeReplacedWithWebp(file.Name) {
- convertToWebp(file)
- }
- }
- }
- }
- if f, ok := v.(interface{}); ok {
- if file, ok := f.(*filesystem.File); ok {
- if shouldBeReplacedWithWebp(file.Name) {
- convertToWebp(file)
- }
- }
- }
- }
- return e.Next()
- },
- Priority: 100,
- }
-}
-
-func ThumbEventHandler(app *pocketbase.PocketBase) *hook.Handler[*core.FileDownloadRequestEvent] {
- return &hook.Handler[*core.FileDownloadRequestEvent]{
- Func: func(fdre *core.FileDownloadRequestEvent) error {
- if filepath.Ext(fdre.ServedName) == ".webp" {
-
- filename := fdre.Request.PathValue("filename")
- baseFilesPath := fdre.Record.BaseFilesPath()
-
- fs, err := app.App.NewFilesystem()
- if err != nil {
- return err
- }
- defer fs.Close()
-
- blob, err := fs.GetReader(baseFilesPath + "/thumbs_" + filename + "/" + fdre.ServedName)
- if err != nil {
- blob, err = fs.GetReader(baseFilesPath + "/" + fdre.ServedName)
- if err != nil {
- return err
- }
- }
- defer blob.Close()
-
- img, err := png.Decode(blob)
- if err != nil {
- return fdre.Next()
- }
-
- buf := bytes.Buffer{}
- err = webp.Encode(&buf, img, &webp.Options{Quality: 80})
- if err != nil {
- return err
- }
- err = fs.Upload(buf.Bytes(), baseFilesPath+"/thumbs_"+filename+"/"+fdre.ServedName)
- if err != nil {
- _, err := fdre.Response.Write(buf.Bytes())
- if err != nil {
- return err
- }
- return nil
- }
- return fdre.Next()
- }
- return fdre.Next()
- },
- Priority: 99,
- }
-}
-
-func convertToWebp(file *filesystem.File) error {
- file.Name = replaceExtWithWebp(file.Name)
- file.OriginalName = replaceExtWithWebp(file.OriginalName)
- ff, err := file.Reader.Open()
- if err != nil {
- return err
- }
- defer ff.Close()
- fff, err := io.ReadAll(ff)
- if err != nil {
- return err
- }
- img, _, err := image.Decode(bytes.NewReader(fff))
- if err != nil {
- return err
- }
-
- buf := bytes.Buffer{}
- err = webp.Encode(&buf, img, &webp.Options{Quality: 80})
- if err != nil {
- return err
- }
- file.Reader = &memFileReader{data: buf.Bytes()}
- file.Size = int64(buf.Len())
- return nil
-}
-
-func replaceExtWithWebp(path string) string {
- // List of image extensions to convert
- exts := []string{".png", ".jpg", ".jpeg", ".bmp", ".tiff"}
-
- for _, ext := range exts {
- if strings.HasSuffix(strings.ToLower(path), ext) {
- return strings.TrimSuffix(path, ext) + ".webp"
- }
- }
- return path
-}
-
-func shouldBeReplacedWithWebp(path string) bool {
- // List of image extensions to convert
- exts := []string{".png", ".jpg", ".jpeg", ".bmp", ".tiff"}
-
- for _, ext := range exts {
- if strings.HasSuffix(strings.ToLower(path), ext) {
- return true
- }
- }
- return false
-}
diff --git a/backend/go.mod b/backend/go.mod
deleted file mode 100644
index ecbdf9d..0000000
--- a/backend/go.mod
+++ /dev/null
@@ -1,42 +0,0 @@
-module pocketbase
-
-go 1.23.2
-
-require (
- github.com/chai2010/webp v1.4.0
- github.com/pocketbase/pocketbase v0.28.2
-)
-
-require (
- github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
- github.com/disintegration/imaging v1.6.2 // indirect
- github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
- github.com/dustin/go-humanize v1.0.1 // indirect
- github.com/fatih/color v1.18.0 // indirect
- github.com/gabriel-vasile/mimetype v1.4.9 // indirect
- github.com/ganigeorgiev/fexpr v0.5.0 // indirect
- github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
- github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
- github.com/google/uuid v1.6.0 // indirect
- github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/mattn/go-colorable v0.1.14 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/ncruces/go-strftime v0.1.9 // indirect
- github.com/pocketbase/dbx v1.11.0 // indirect
- github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
- github.com/spf13/cast v1.8.0 // indirect
- github.com/spf13/cobra v1.9.1 // indirect
- github.com/spf13/pflag v1.0.6 // indirect
- golang.org/x/crypto v0.38.0 // indirect
- golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
- golang.org/x/image v0.27.0 // indirect
- golang.org/x/net v0.40.0 // indirect
- golang.org/x/oauth2 v0.30.0 // indirect
- golang.org/x/sync v0.14.0 // indirect
- golang.org/x/sys v0.33.0 // indirect
- golang.org/x/text v0.25.0 // indirect
- modernc.org/libc v1.65.7 // indirect
- modernc.org/mathutil v1.7.1 // indirect
- modernc.org/memory v1.11.0 // indirect
- modernc.org/sqlite v1.37.1 // indirect
-)
diff --git a/backend/go.sum b/backend/go.sum
deleted file mode 100644
index 5c2bd85..0000000
--- a/backend/go.sum
+++ /dev/null
@@ -1,127 +0,0 @@
-github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
-github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
-github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
-github.com/chai2010/webp v1.4.0 h1:6DA2pkkRUPnbOHvvsmGI3He1hBKf/bkRlniAiSGuEko=
-github.com/chai2010/webp v1.4.0/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
-github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
-github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
-github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8=
-github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c=
-github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
-github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
-github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
-github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
-github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
-github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
-github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
-github.com/ganigeorgiev/fexpr v0.5.0 h1:XA9JxtTE/Xm+g/JFI6RfZEHSiQlk+1glLvRK1Lpv/Tk=
-github.com/ganigeorgiev/fexpr v0.5.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
-github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
-github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
-github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
-github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
-github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
-github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
-github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
-github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
-github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
-github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
-github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
-github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
-github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pocketbase/dbx v1.11.0 h1:LpZezioMfT3K4tLrqA55wWFw1EtH1pM4tzSVa7kgszU=
-github.com/pocketbase/dbx v1.11.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
-github.com/pocketbase/pocketbase v0.28.2 h1:b6cfUfr5d4whvUFGFhI8oHRzx/eB76GCUQGftqgv9lM=
-github.com/pocketbase/pocketbase v0.28.2/go.mod h1:ElwIYS1b5xS9w0U7AK7tsm6FuC0lzw57H8p/118Cu7g=
-github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
-github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
-github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
-github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
-github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
-github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
-golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
-golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
-golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
-golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
-golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
-golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
-golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
-golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
-golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
-golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
-golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
-golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
-golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
-golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
-golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
-google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
-modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
-modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
-modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
-modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
-modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
-modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
-modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
-modernc.org/libc v1.65.7 h1:Ia9Z4yzZtWNtUIuiPuQ7Qf7kxYrxP1/jeHZzG8bFu00=
-modernc.org/libc v1.65.7/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU=
-modernc.org/libc v1.65.8 h1:7PXRJai0TXZ8uNA3srsmYzmTyrLoHImV5QxHeni108Q=
-modernc.org/libc v1.65.8/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU=
-modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
-modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
-modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
-modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
-modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
-modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
-modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
-modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
-modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs=
-modernc.org/sqlite v1.37.1/go.mod h1:XwdRtsE1MpiBcL54+MbKcaDvcuej+IYSMfLN6gSKV8g=
-modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
-modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
-modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
-modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
diff --git a/backend/main.go b/backend/main.go
deleted file mode 100644
index 5868f2f..0000000
--- a/backend/main.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package main
-
-import (
- "log"
-
- "pocketbase/custom"
-
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/core"
-)
-
-func main() {
- app := pocketbase.New()
-
- app = custom.ExtendApp(app)
-
- app.OnServe().BindFunc(func(se *core.ServeEvent) error {
-
- custom.LoadCustomCode(app, se)
-
- return se.Next()
- })
-
- if err := app.Start(); err != nil {
- log.Fatal(err)
- }
-}