Compare commits
11 commits
d6e846e940
...
c7fe67c65b
| Author | SHA1 | Date | |
|---|---|---|---|
| c7fe67c65b | |||
| 8a65bf1af2 | |||
| 196fd1a061 | |||
| 29c1d2502e | |||
| c2225ddc5e | |||
| 48455218d5 | |||
| 263df02e4c | |||
| 7a51b2e540 | |||
| 195509f48a | |||
| 92df4a58b4 | |||
| 6fd9d08195 |
43 changed files with 357 additions and 695 deletions
|
|
@ -1,14 +0,0 @@
|
|||
happyDomain
|
||||
===========
|
||||
|
||||
This product bundles third-party components under the following licenses.
|
||||
The original license texts are reproduced in full below.
|
||||
|
||||
{{ range . }}
|
||||
--------------------------------------------------------------------------------
|
||||
Module: {{ .Name }}
|
||||
License: {{ .LicenseName }}
|
||||
{{ with .LicenseURL }}Source: {{ . }}
|
||||
{{ end }}
|
||||
{{ .LicenseText }}
|
||||
{{ end }}
|
||||
57
.drone.yml
57
.drone.yml
|
|
@ -28,8 +28,8 @@ steps:
|
|||
commands:
|
||||
- apk --no-cache add tar
|
||||
- yarn config set network-timeout 100000
|
||||
- yarn install
|
||||
- tar --transform="s@.@./happydomain-${DRONE_COMMIT}@" --exclude-vcs --exclude=./node_modules/.cache --exclude=./web/node_modules/.cache --exclude=./web-admin/node_modules/.cache -czf /dev/shm/happydomain-src.tar.gz ./package.json ./package-lock.json ./.npmrc ./node_modules/ ./web/ ./web-admin/
|
||||
- yarn --cwd web install
|
||||
- tar --transform="s@.@./happydomain-${DRONE_COMMIT}@" --exclude-vcs --exclude=./web/node_modules/.cache -czf /dev/shm/happydomain-src.tar.gz .
|
||||
- mkdir deploy
|
||||
- mv /dev/shm/happydomain-src.tar.gz deploy
|
||||
- yarn --cwd web --offline run svelte-kit sync && yarn --cwd web --offline generate:api && sed -i "s/hey-api\.ts';/hey-api';/" web/src/lib/api-base/client.gen.ts
|
||||
|
|
@ -37,25 +37,14 @@ steps:
|
|||
- yarn --cwd web-admin --offline run svelte-kit sync && yarn --cwd web-admin --offline generate:api && sed -i "s/hey-api\.ts';/hey-api';/" web/src/lib/api-admin/client.gen.ts
|
||||
- yarn --cwd web-admin --offline build
|
||||
|
||||
- name: frontend NOTICE
|
||||
image: node:24-alpine
|
||||
commands:
|
||||
- npx --yes generate-license-file@3 --input web/package.json --output deploy/NOTICE.web --ci
|
||||
- npx --yes generate-license-file@3 --input web-admin/package.json --output deploy/NOTICE.web-admin --ci
|
||||
- for f in deploy/NOTICE.web deploy/NOTICE.web-admin; do { echo '------'; tail -n +3 "$f"; } > "$f.tmp" && mv "$f.tmp" "$f"; done
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
||||
- name: backend-commit
|
||||
image: golang:1-alpine
|
||||
commands:
|
||||
- apk add --no-cache git
|
||||
- go build -ldflags '-w -X "main.Version=${DRONE_BRANCH}-${DRONE_COMMIT}" -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/
|
||||
- go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_BRANCH}-${DRONE_COMMIT}" -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/
|
||||
- ln deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydomain
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
GOFLAGS: "-tags=netgo,swagger,web"
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
|
|
@ -65,16 +54,10 @@ steps:
|
|||
image: golang:1-alpine
|
||||
commands:
|
||||
- apk add --no-cache git
|
||||
- go build -ldflags '-w -X main.Version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/
|
||||
- go build -tags netgo,swagger,web -ldflags '-w -X main.Version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/
|
||||
- ln deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydomain
|
||||
# Generate NOTICE file (merging Go + frontend)
|
||||
- go install github.com/google/go-licenses@v1.6.0
|
||||
- go-licenses report ./cmd/happyDomain --template .drone-notice.tpl > deploy/NOTICE.go
|
||||
- cat deploy/NOTICE.go deploy/NOTICE.web deploy/NOTICE.web-admin > deploy/NOTICE
|
||||
- rm deploy/NOTICE.go deploy/NOTICE.web deploy/NOTICE.web-admin
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
GOFLAGS: "-tags=netgo,swagger,web"
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
|
@ -171,7 +154,11 @@ steps:
|
|||
from_secret: git_nemunaire_token
|
||||
base_url: https://git.nemunai.re
|
||||
draft: true
|
||||
files: deploy/*
|
||||
files:
|
||||
- happydomain-src.tar.gz
|
||||
- happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
- happydomain-darwin-${DRONE_STAGE_ARCH}
|
||||
- happydomain-sbom.spdx.json
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
|
@ -183,7 +170,11 @@ steps:
|
|||
from_secret: codeberg_token
|
||||
base_url: https://codeberg.org
|
||||
draft: true
|
||||
files: deploy/*
|
||||
files:
|
||||
- happydomain-src.tar.gz
|
||||
- happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
- happydomain-darwin-${DRONE_STAGE_ARCH}
|
||||
- happydomain-sbom.spdx.json
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
|
@ -195,7 +186,11 @@ steps:
|
|||
from_secret: github_release_token
|
||||
draft: true
|
||||
github_url: https://github.com
|
||||
files: deploy/*
|
||||
files:
|
||||
- happydomain-src.tar.gz
|
||||
- happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
- happydomain-darwin-${DRONE_STAGE_ARCH}
|
||||
- happydomain-sbom.spdx.json
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
|
@ -237,8 +232,8 @@ steps:
|
|||
- name: frontend
|
||||
image: node:24-alpine
|
||||
commands:
|
||||
- npm install --network-timeout=100000
|
||||
- cd web
|
||||
- npm install --network-timeout=100000
|
||||
- npx svelte-kit sync && npm run generate:api
|
||||
- npm test
|
||||
- npm run build
|
||||
|
|
@ -367,7 +362,9 @@ steps:
|
|||
base_url: https://git.nemunai.re
|
||||
draft: true
|
||||
prerelease: true
|
||||
files: deploy/*
|
||||
files:
|
||||
- happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
- happydomain-darwin-${DRONE_STAGE_ARCH}
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
|
@ -380,7 +377,9 @@ steps:
|
|||
base_url: https://codeberg.org
|
||||
draft: true
|
||||
prerelease: true
|
||||
files: deploy/*
|
||||
files:
|
||||
- happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
- happydomain-darwin-${DRONE_STAGE_ARCH}
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
|
@ -393,7 +392,9 @@ steps:
|
|||
draft: true
|
||||
prerelease: true
|
||||
github_url: https://github.com
|
||||
files: deploy/*
|
||||
files:
|
||||
- happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||
- happydomain-darwin-${DRONE_STAGE_ARCH}
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,2 @@
|
|||
./happydomain
|
||||
./happyDomain
|
||||
node_modules
|
||||
2
go.mod
2
go.mod
|
|
@ -27,7 +27,7 @@ require (
|
|||
github.com/libdns/libdns v1.1.1
|
||||
github.com/miekg/dns v1.1.72
|
||||
github.com/mileusna/useragent v1.3.5
|
||||
github.com/oracle/nosql-go-sdk v1.4.8
|
||||
github.com/oracle/nosql-go-sdk v1.4.7
|
||||
github.com/ovh/go-ovh v1.9.0
|
||||
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d
|
||||
github.com/swaggo/files v1.0.1
|
||||
|
|
|
|||
11
go.sum
11
go.sum
|
|
@ -135,10 +135,6 @@ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvF
|
|||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
|
||||
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
|
||||
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
|
||||
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||
|
|
@ -476,8 +472,8 @@ github.com/openrdap/rdap v0.9.1 h1:Rv6YbanbiVPsKRvOLdUmlU1AL5+2OFuEFLjFN+mQsCM=
|
|||
github.com/openrdap/rdap v0.9.1/go.mod h1:vKSiotbsENrjM/vaHXLddXbW8iQkBfa+ldEuYEjyLTQ=
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
||||
github.com/oracle/nosql-go-sdk v1.4.8 h1:eMzz+yNLHvB0GCPAWxe0qYttBJF7Fh0Aup+zectpgc4=
|
||||
github.com/oracle/nosql-go-sdk v1.4.8/go.mod h1:xgJE9wxADDbk7vR4FGA4NOt4RNAaIsQOj4sCATmCVXM=
|
||||
github.com/oracle/nosql-go-sdk v1.4.7 h1:dqVBSMulObDj0JHm1mAncTHrQg8wIiQJNC0JRNKPACg=
|
||||
github.com/oracle/nosql-go-sdk v1.4.7/go.mod h1:xgJE9wxADDbk7vR4FGA4NOt4RNAaIsQOj4sCATmCVXM=
|
||||
github.com/oracle/oci-go-sdk/v65 v65.111.0 h1:eDkWg6ZN0uKwWzSekoFcQJhR+C+F/aVdTwr+lGHU9Qk=
|
||||
github.com/oracle/oci-go-sdk/v65 v65.111.0/go.mod h1:8ZzvzuEG/cFLFZhxg/Mg1w19KqyXBKO3c17QIc5PkGs=
|
||||
github.com/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE=
|
||||
|
|
@ -633,8 +629,6 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
|||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE=
|
||||
github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
|
||||
go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU=
|
||||
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
|
||||
|
|
@ -654,6 +648,7 @@ go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfC
|
|||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
|
|
|
|||
|
|
@ -120,46 +120,6 @@ func (bc *BackupController) DoBackup() (ret happydns.Backup) {
|
|||
}
|
||||
}
|
||||
|
||||
// Checker configurations (positional, one entry per (checker, user?, domain?, service?)).
|
||||
if cfgIter, err := bc.store.ListAllCheckerConfigurations(); err != nil {
|
||||
ret.Errors = append(ret.Errors, fmt.Sprintf("unable to retrieve CheckerConfigurations: %s", err.Error()))
|
||||
} else {
|
||||
defer cfgIter.Close()
|
||||
for cfgIter.Next() {
|
||||
ret.CheckerConfigurations = append(ret.CheckerConfigurations, cfgIter.Item())
|
||||
}
|
||||
}
|
||||
|
||||
// Check plans.
|
||||
if planIter, err := bc.store.ListAllCheckPlans(); err != nil {
|
||||
ret.Errors = append(ret.Errors, fmt.Sprintf("unable to retrieve CheckPlans: %s", err.Error()))
|
||||
} else {
|
||||
defer planIter.Close()
|
||||
for planIter.Next() {
|
||||
ret.CheckPlans = append(ret.CheckPlans, planIter.Item())
|
||||
}
|
||||
}
|
||||
|
||||
// Check evaluations.
|
||||
if evalIter, err := bc.store.ListAllEvaluations(); err != nil {
|
||||
ret.Errors = append(ret.Errors, fmt.Sprintf("unable to retrieve CheckEvaluations: %s", err.Error()))
|
||||
} else {
|
||||
defer evalIter.Close()
|
||||
for evalIter.Next() {
|
||||
ret.CheckEvaluations = append(ret.CheckEvaluations, evalIter.Item())
|
||||
}
|
||||
}
|
||||
|
||||
// Executions.
|
||||
if execIter, err := bc.store.ListAllExecutions(); err != nil {
|
||||
ret.Errors = append(ret.Errors, fmt.Sprintf("unable to retrieve Executions: %s", err.Error()))
|
||||
} else {
|
||||
defer execIter.Close()
|
||||
for execIter.Next() {
|
||||
ret.Executions = append(ret.Executions, execIter.Item())
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -215,29 +175,6 @@ func (bc *BackupController) DoRestore(backup *happydns.Backup) (errs error) {
|
|||
errs = errors.Join(errs, bc.store.UpdateSession(session))
|
||||
}
|
||||
|
||||
// Checker configurations.
|
||||
for _, cfg := range backup.CheckerConfigurations {
|
||||
if cfg == nil {
|
||||
continue
|
||||
}
|
||||
errs = errors.Join(errs, bc.store.UpdateCheckerConfiguration(cfg.CheckName, cfg.UserId, cfg.DomainId, cfg.ServiceId, cfg.Options))
|
||||
}
|
||||
|
||||
// Check plans.
|
||||
for _, plan := range backup.CheckPlans {
|
||||
errs = errors.Join(errs, bc.store.RestoreCheckPlan(plan))
|
||||
}
|
||||
|
||||
// Check evaluations (reference plans, restored above).
|
||||
for _, eval := range backup.CheckEvaluations {
|
||||
errs = errors.Join(errs, bc.store.RestoreEvaluation(eval))
|
||||
}
|
||||
|
||||
// Executions.
|
||||
for _, exec := range backup.Executions {
|
||||
errs = errors.Join(errs, bc.store.RestoreExecution(exec))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,6 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
|
|
@ -44,25 +41,14 @@ func NewTidyController(tidyUpService happydns.TidyUpUseCase) *TidyController {
|
|||
//
|
||||
// @Summary Tidy up the database
|
||||
// @Schemes
|
||||
// @Description Performs cleanup and maintenance operations on the database, removing orphaned records and optimizing storage. When drop_invalid is true (default), records that fail to decode (e.g. after a schema change) are deleted; set it to false to only log them.
|
||||
// @Description Performs cleanup and maintenance operations on the database, removing orphaned records and optimizing storage.
|
||||
// @Tags admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param drop_invalid query bool false "Delete records that fail to decode instead of only logging (default true)"
|
||||
// @Security securitydefinitions.basic
|
||||
// @Success 200 {boolean} bool
|
||||
// @Failure 500 {object} happydns.ErrorResponse "Internal server error"
|
||||
// @Router /tidy [post]
|
||||
func (tc *TidyController) TidyDB(c *gin.Context) {
|
||||
dropInvalid := true
|
||||
if v := c.Query("drop_invalid"); v != "" {
|
||||
parsed, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
happydns.ApiResponse(c, nil, fmt.Errorf("drop_invalid must be a boolean: %w", err))
|
||||
return
|
||||
}
|
||||
dropInvalid = parsed
|
||||
}
|
||||
|
||||
happydns.ApiResponse(c, true, tc.tidyUpService.TidyAll(dropInvalid))
|
||||
happydns.ApiResponse(c, true, tc.tidyUpService.TidyAll())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ func (cc *CheckerController) TriggerCheck(c *gin.Context) {
|
|||
} else {
|
||||
go func() {
|
||||
if _, err := cc.engine.RunExecution(context.WithoutCancel(c.Request.Context()), exec, plan, req.Options); err != nil {
|
||||
log.Printf("async RunExecution error for checker %q execution %s: %v", cname, exec.Id.String(), err)
|
||||
log.Printf("async RunExecution error for checker %q execution %v: %v", cname, exec.Id, err)
|
||||
}
|
||||
}()
|
||||
c.JSON(http.StatusAccepted, exec)
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ func SameUserHandler(c *gin.Context) {
|
|||
user := c.MustGet("user").(*happydns.User)
|
||||
|
||||
if !bytes.Equal(user.Id, myuser.Id) {
|
||||
log.Printf("%s: tries to do action as %s (logged %s)", c.ClientIP(), myuser.Id.String(), user.Id.String())
|
||||
log.Printf("%s: tries to do action as %s (logged %s)", c.ClientIP(), myuser.Id, user.Id)
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, happydns.ErrorResponse{Message: "Not authorized"})
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -559,21 +559,6 @@ func (s *instrumentedStorage) PutCachedObservation(target happydns.CheckTarget,
|
|||
return s.inner.PutCachedObservation(target, key, entry)
|
||||
}
|
||||
|
||||
func (s *instrumentedStorage) RestoreCheckPlan(plan *happydns.CheckPlan) (err error) {
|
||||
defer observe("restore", "check_plan")(&err)
|
||||
return s.inner.RestoreCheckPlan(plan)
|
||||
}
|
||||
|
||||
func (s *instrumentedStorage) RestoreEvaluation(eval *happydns.CheckEvaluation) (err error) {
|
||||
defer observe("restore", "check_evaluation")(&err)
|
||||
return s.inner.RestoreEvaluation(eval)
|
||||
}
|
||||
|
||||
func (s *instrumentedStorage) RestoreExecution(exec *happydns.Execution) (err error) {
|
||||
defer observe("restore", "execution")(&err)
|
||||
return s.inner.RestoreExecution(exec)
|
||||
}
|
||||
|
||||
func (s *instrumentedStorage) PutState(state *happydns.NotificationState) (err error) {
|
||||
defer observe("put", "notification_state")(&err)
|
||||
return s.inner.PutState(state)
|
||||
|
|
|
|||
|
|
@ -106,24 +106,6 @@ func (s *KVStorage) CreateEvaluation(eval *happydns.CheckEvaluation) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RestoreEvaluation writes an evaluation at its existing Id and rebuilds
|
||||
// its secondary indexes. Used by the backup restore path.
|
||||
func (s *KVStorage) RestoreEvaluation(eval *happydns.CheckEvaluation) error {
|
||||
if err := s.db.Put(fmt.Sprintf("chckeval|%s", eval.Id.String()), eval); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if eval.PlanID != nil {
|
||||
indexKey := fmt.Sprintf("chckeval-plan|%s|%s", eval.PlanID.String(), eval.Id.String())
|
||||
if err := s.db.Put(indexKey, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
checkerIndexKey := fmt.Sprintf("chckeval-chkr|%s|%s|%s", eval.CheckerID, eval.Target.String(), eval.Id.String())
|
||||
return s.db.Put(checkerIndexKey, true)
|
||||
}
|
||||
|
||||
func (s *KVStorage) DeleteEvaluation(evalID happydns.Identifier) error {
|
||||
// Load first to find plan ID for index cleanup.
|
||||
eval, err := s.GetEvaluation(evalID)
|
||||
|
|
|
|||
|
|
@ -138,16 +138,6 @@ func (s *KVStorage) putCheckPlanIndexes(plan *happydns.CheckPlan) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RestoreCheckPlan writes a plan at its existing Id and (re)builds its
|
||||
// secondary indexes. Used by the backup restore path, which must preserve
|
||||
// the original identifier instead of generating a new one.
|
||||
func (s *KVStorage) RestoreCheckPlan(plan *happydns.CheckPlan) error {
|
||||
if err := s.db.Put(fmt.Sprintf("chckpln|%s", plan.Id.String()), plan); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.putCheckPlanIndexes(plan)
|
||||
}
|
||||
|
||||
func (s *KVStorage) DeleteCheckPlan(planID happydns.Identifier) error {
|
||||
plan, err := s.GetCheckPlan(planID)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -123,40 +123,6 @@ func (s *KVStorage) CreateExecution(exec *happydns.Execution) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RestoreExecution writes an execution at its existing Id and rebuilds
|
||||
// its secondary indexes. Used by the backup restore path.
|
||||
func (s *KVStorage) RestoreExecution(exec *happydns.Execution) error {
|
||||
if err := s.db.Put(fmt.Sprintf("chckexec|%s", exec.Id.String()), exec); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exec.PlanID != nil {
|
||||
indexKey := fmt.Sprintf("chckexec-plan|%s|%s", exec.PlanID.String(), exec.Id.String())
|
||||
if err := s.db.Put(indexKey, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
checkerIndexKey := fmt.Sprintf("chckexec-chkr|%s|%s|%s", exec.CheckerID, exec.Target.String(), exec.Id.String())
|
||||
if err := s.db.Put(checkerIndexKey, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exec.Target.UserId != "" {
|
||||
if err := s.db.Put(executionUserIndexKey(exec.Target.UserId, exec.Id.String()), true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if exec.Target.DomainId != "" {
|
||||
if err := s.db.Put(executionDomainIndexKey(exec.Target.DomainId, exec.Id.String()), true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *KVStorage) UpdateExecution(exec *happydns.Execution) error {
|
||||
// Load the old record so we can detect changed index keys.
|
||||
old, err := s.GetExecution(exec.Id)
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ func (it *KVIterator[T]) NextWithError() bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
it.err = nil
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,12 +38,12 @@ import (
|
|||
// abstract.EMail
|
||||
func explodeAbstractEMail(dn happydns.Subdomain, in *happydns.ServiceMessage) ([]*happydns.ServiceMessage, error) {
|
||||
var val struct {
|
||||
MX []map[string]any `json:"mx,omitempty"`
|
||||
SPF map[string]any `json:"spf,omitempty"`
|
||||
DKIM map[string]*svcs.DKIM `json:"dkim,omitempty"`
|
||||
DMARC *svcs.DMARCFields `json:"dmarc,omitempty"`
|
||||
MTA_STS *svcs.MTASTSFields `json:"mta_sts,omitempty"`
|
||||
TLS_RPT *svcs.TLS_RPTField `json:"tls_rpt,omitempty"`
|
||||
MX []map[string]any `json:"mx,omitempty"`
|
||||
SPF map[string]any `json:"spf,omitempty"`
|
||||
DKIM map[string]*svcs.DKIM `json:"dkim,omitempty"`
|
||||
DMARC *svcs.DMARCFields `json:"dmarc,omitempty"`
|
||||
MTA_STS *svcs.MTASTSFields `json:"mta_sts,omitempty"`
|
||||
TLS_RPT *svcs.TLS_RPTField `json:"tls_rpt,omitempty"`
|
||||
}
|
||||
|
||||
err := json.Unmarshal(in.Service, &val)
|
||||
|
|
@ -815,12 +815,7 @@ func migrateFrom7(s *KVStorage) error {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var rr dns.RR
|
||||
if strings.Contains(val["Target"], "IN\tCNAME") {
|
||||
rr, err = dns.NewRR(val["Target"])
|
||||
} else {
|
||||
rr, err = dns.NewRR(fmt.Sprintf("%s.zZzZ. 0 IN CNAME %s", val["SubDomain"], helpers.DomainFQDN(val["Target"], "zZzZ.")))
|
||||
}
|
||||
rr, err := dns.NewRR(fmt.Sprintf("%s.zZzZ. 0 IN CNAME %s", val["SubDomain"], helpers.DomainFQDN(val["Target"], "zZzZ.")))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
|
|
@ -34,15 +33,6 @@ func migrateFrom9(s *KVStorage) (err error) {
|
|||
|
||||
for sessions.Next() {
|
||||
session := sessions.Item()
|
||||
if len(session.Id) != 103 {
|
||||
err = sessions.DropItem()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to drop invalid session: %s: %w", session.Id, err)
|
||||
}
|
||||
log.Printf("Drop invalid session identifier: %s", session.Id)
|
||||
continue
|
||||
}
|
||||
|
||||
err := s.UpdateSession(session)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -374,11 +374,6 @@ func (s *planStore) UpdateCheckPlan(plan *happydns.CheckPlan) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *planStore) RestoreCheckPlan(plan *happydns.CheckPlan) error {
|
||||
s.plans[plan.Id.String()] = plan
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *planStore) DeleteCheckPlan(planID happydns.Identifier) error {
|
||||
delete(s.plans, planID.String())
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -102,7 +102,6 @@ func (s *mockExecStore) GetExecution(happydns.Identifier) (*happydns.Execution,
|
|||
}
|
||||
func (s *mockExecStore) CreateExecution(*happydns.Execution) error { return nil }
|
||||
func (s *mockExecStore) UpdateExecution(*happydns.Execution) error { return nil }
|
||||
func (s *mockExecStore) RestoreExecution(*happydns.Execution) error { return nil }
|
||||
func (s *mockExecStore) DeleteExecutionsByChecker(string, happydns.CheckTarget) error { return nil }
|
||||
func (s *mockExecStore) TidyExecutionIndexes() error { return nil }
|
||||
func (s *mockExecStore) ClearExecutions() error { return nil }
|
||||
|
|
@ -493,7 +492,6 @@ func (s *mockEvalStore) GetLatestEvaluation(happydns.Identifier) (*happydns.Chec
|
|||
return nil, nil
|
||||
}
|
||||
func (s *mockEvalStore) CreateEvaluation(*happydns.CheckEvaluation) error { return nil }
|
||||
func (s *mockEvalStore) RestoreEvaluation(*happydns.CheckEvaluation) error { return nil }
|
||||
func (s *mockEvalStore) DeleteEvaluationsByChecker(string, happydns.CheckTarget) error { return nil }
|
||||
func (s *mockEvalStore) TidyEvaluationIndexes() error { return nil }
|
||||
func (s *mockEvalStore) ClearEvaluations() error { return nil }
|
||||
|
|
|
|||
|
|
@ -492,7 +492,7 @@ func (s *Scheduler) buildQueue() {
|
|||
svcTarget := happydns.CheckTarget{UserId: uid.String(), DomainId: did.String(), ServiceId: sid.String(), ServiceType: svc.Type}
|
||||
|
||||
for _, c := range serviceCheckers {
|
||||
if !serviceCheckerApplies(c.def, svc.Type) {
|
||||
if len(c.def.Availability.LimitToServices) > 0 && !slices.Contains(c.def.Availability.LimitToServices, svc.Type) {
|
||||
continue
|
||||
}
|
||||
s.enqueueJob(c.id, c.def, svcTarget, disabledSet, planMap, lastRun)
|
||||
|
|
@ -534,7 +534,7 @@ func (s *Scheduler) NotifyDomainChange(domain *happydns.Domain) {
|
|||
}
|
||||
if def.Availability.ApplyToService {
|
||||
for _, svc := range services {
|
||||
if !serviceCheckerApplies(def, svc.Type) {
|
||||
if len(def.Availability.LimitToServices) > 0 && !slices.Contains(def.Availability.LimitToServices, svc.Type) {
|
||||
continue
|
||||
}
|
||||
svcTarget := happydns.CheckTarget{UserId: uid.String(), DomainId: didStr, ServiceId: svc.Id.String(), ServiceType: svc.Type}
|
||||
|
|
@ -579,7 +579,7 @@ func (s *Scheduler) NotifyDomainChange(domain *happydns.Domain) {
|
|||
|
||||
if def.Availability.ApplyToService {
|
||||
for _, svc := range services {
|
||||
if !serviceCheckerApplies(def, svc.Type) {
|
||||
if len(def.Availability.LimitToServices) > 0 && !slices.Contains(def.Availability.LimitToServices, svc.Type) {
|
||||
continue
|
||||
}
|
||||
sid := svc.Id
|
||||
|
|
@ -627,20 +627,10 @@ func (s *Scheduler) NotifyDomainRemoved(domainID happydns.Identifier) {
|
|||
s.mu.Unlock()
|
||||
|
||||
if n > 0 {
|
||||
log.Printf("Scheduler: NotifyDomainRemoved(%s): removed %d jobs", domainID.String(), n)
|
||||
log.Printf("Scheduler: NotifyDomainRemoved(%s): removed %d jobs", domainID, n)
|
||||
}
|
||||
}
|
||||
|
||||
// serviceCheckerApplies reports whether a service-scoped checker should be
|
||||
// auto-scheduled for the given service type. Auto-scheduling is restricted to
|
||||
// checkers that explicitly declare the service type in LimitToServices; a
|
||||
// checker with an empty LimitToServices must be activated manually via a
|
||||
// CheckPlan.
|
||||
func serviceCheckerApplies(def *happydns.CheckerDefinition, serviceType string) bool {
|
||||
return len(def.Availability.LimitToServices) > 0 &&
|
||||
slices.Contains(def.Availability.LimitToServices, serviceType)
|
||||
}
|
||||
|
||||
// buildPlanIndex builds disabled and plan lookup maps from a slice of plans.
|
||||
func buildPlanIndex(plans []*happydns.CheckPlan) (disabledSet map[string]bool, planMap map[string]*happydns.CheckPlan) {
|
||||
disabledSet = make(map[string]bool)
|
||||
|
|
@ -741,7 +731,7 @@ func (s *Scheduler) loadDomainServices(domain *happydns.Domain) []*happydns.Serv
|
|||
}
|
||||
zone, err := s.zoneStore.GetZone(domain.ZoneHistory[idx])
|
||||
if err != nil {
|
||||
log.Printf("Scheduler: failed to load zone %s for domain %s: %v", domain.ZoneHistory[idx].String(), domain.DomainName, err)
|
||||
log.Printf("Scheduler: failed to load zone %s for domain %s: %v", domain.ZoneHistory[idx], domain.DomainName, err)
|
||||
continue
|
||||
}
|
||||
for _, svcs := range zone.Services {
|
||||
|
|
|
|||
|
|
@ -121,9 +121,8 @@ func (s *mockPlanStore) CreateCheckPlan(plan *happydns.CheckPlan) error {
|
|||
s.plans = append(s.plans, plan)
|
||||
return nil
|
||||
}
|
||||
func (s *mockPlanStore) UpdateCheckPlan(plan *happydns.CheckPlan) error { return nil }
|
||||
func (s *mockPlanStore) RestoreCheckPlan(plan *happydns.CheckPlan) error { return nil }
|
||||
func (s *mockPlanStore) DeleteCheckPlan(happydns.Identifier) error { return nil }
|
||||
func (s *mockPlanStore) UpdateCheckPlan(plan *happydns.CheckPlan) error { return nil }
|
||||
func (s *mockPlanStore) DeleteCheckPlan(happydns.Identifier) error { return nil }
|
||||
func (s *mockPlanStore) TidyCheckPlanIndexes() error { return nil }
|
||||
func (s *mockPlanStore) ClearCheckPlans() error { return nil }
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ type CheckPlanStorage interface {
|
|||
GetCheckPlan(planID happydns.Identifier) (*happydns.CheckPlan, error)
|
||||
CreateCheckPlan(plan *happydns.CheckPlan) error
|
||||
UpdateCheckPlan(plan *happydns.CheckPlan) error
|
||||
RestoreCheckPlan(plan *happydns.CheckPlan) error
|
||||
DeleteCheckPlan(planID happydns.Identifier) error
|
||||
TidyCheckPlanIndexes() error
|
||||
ClearCheckPlans() error
|
||||
|
|
@ -85,7 +84,6 @@ type CheckEvaluationStorage interface {
|
|||
GetEvaluation(evalID happydns.Identifier) (*happydns.CheckEvaluation, error)
|
||||
GetLatestEvaluation(planID happydns.Identifier) (*happydns.CheckEvaluation, error)
|
||||
CreateEvaluation(eval *happydns.CheckEvaluation) error
|
||||
RestoreEvaluation(eval *happydns.CheckEvaluation) error
|
||||
DeleteEvaluation(evalID happydns.Identifier) error
|
||||
DeleteEvaluationsByChecker(checkerID string, target happydns.CheckTarget) error
|
||||
TidyEvaluationIndexes() error
|
||||
|
|
@ -102,7 +100,6 @@ type ExecutionStorage interface {
|
|||
GetExecution(execID happydns.Identifier) (*happydns.Execution, error)
|
||||
CreateExecution(exec *happydns.Execution) error
|
||||
UpdateExecution(exec *happydns.Execution) error
|
||||
RestoreExecution(exec *happydns.Execution) error
|
||||
DeleteExecution(execID happydns.Identifier) error
|
||||
DeleteExecutionsByChecker(checkerID string, target happydns.CheckTarget) error
|
||||
TidyExecutionIndexes() error
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ func (uc *ZoneCorrectionApplierUsecase) Apply(
|
|||
if len(domain.ZoneHistory) > 1 {
|
||||
prevZone, prevErr := uc.zoneGetter.Get(domain.ZoneHistory[1])
|
||||
if prevErr != nil {
|
||||
log.Printf("ReassociateMetadata: unable to load previous zone %s: %s (metadata will not be transferred)", domain.ZoneHistory[1].String(), prevErr)
|
||||
log.Printf("ReassociateMetadata: unable to load previous zone %s: %s (metadata will not be transferred)", domain.ZoneHistory[1], prevErr)
|
||||
} else {
|
||||
zoneUC.ReassociateMetadata(prevZone.Services, services, domain.DomainName, defaultTTL)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ func (uc *ZoneImporterUsecase) Import(user *happydns.User, domain *happydns.Doma
|
|||
if len(domain.ZoneHistory) > 0 {
|
||||
prevZone, err := uc.zoneGetter.Get(domain.ZoneHistory[0])
|
||||
if err != nil {
|
||||
log.Printf("ReassociateMetadata: unable to load previous zone %s: %s (metadata will not be transferred)", domain.ZoneHistory[0].String(), err)
|
||||
log.Printf("ReassociateMetadata: unable to load previous zone %s: %s (metadata will not be transferred)", domain.ZoneHistory[0], err)
|
||||
} else {
|
||||
zoneUC.ReassociateMetadata(prevZone.Services, services, domain.DomainName, defaultTTL)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,33 +40,8 @@ func NewTidyUpUsecase(store storage.Storage) happydns.TidyUpUseCase {
|
|||
}
|
||||
}
|
||||
|
||||
// iterateTidy drives an iterator using NextWithError so Tidy can decide
|
||||
// whether to delete undecodable records (via DropItem) or just log them.
|
||||
// handle is only invoked for successfully decoded items.
|
||||
func iterateTidy[T any](iter happydns.Iterator[T], dropInvalid bool, handle func(*T) error) error {
|
||||
for iter.NextWithError() {
|
||||
item := iter.Item()
|
||||
if item == nil {
|
||||
key := iter.Key()
|
||||
log.Printf("KVIterator: error decoding item at key %q: %s", key, iter.Err())
|
||||
if dropInvalid {
|
||||
if err := iter.DropItem(); err != nil {
|
||||
log.Printf("KVIterator: failed to delete invalid item at key %q: %s", key, err)
|
||||
} else {
|
||||
log.Printf("KVIterator: dropped invalid item at key %q", key)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := handle(item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return iter.Err()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyAll(dropInvalid bool) error {
|
||||
for _, tidy := range []func(bool) error{
|
||||
func (tu *tidyUpUsecase) TidyAll() error {
|
||||
for _, tidy := range []func() error{
|
||||
tu.TidySessions,
|
||||
tu.TidyAuthUsers,
|
||||
tu.TidyUsers,
|
||||
|
|
@ -81,22 +56,24 @@ func (tu *tidyUpUsecase) TidyAll(dropInvalid bool) error {
|
|||
tu.TidySnapshots,
|
||||
tu.TidyObservationCache,
|
||||
} {
|
||||
if err := tidy(dropInvalid); err != nil {
|
||||
if err := tidy(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyAuthUsers(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidyAuthUsers() error {
|
||||
iter, err := tu.store.ListAllAuthUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
return iterateTidy(iter, dropInvalid, func(userAuth *happydns.UserAuth) error {
|
||||
_, err := tu.store.GetUser(userAuth.Id)
|
||||
for iter.Next() {
|
||||
userAuth := iter.Item()
|
||||
|
||||
_, err = tu.store.GetUser(userAuth.Id)
|
||||
if errors.Is(err, happydns.ErrUserNotFound) && time.Since(userAuth.CreatedAt) > 24*time.Hour {
|
||||
// Drop providers of unexistant users
|
||||
log.Printf("Deleting orphan authuser (user %s not found): %v\n", userAuth.Id.String(), userAuth)
|
||||
|
|
@ -104,18 +81,21 @@ func (tu *tidyUpUsecase) TidyAuthUsers(dropInvalid bool) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return iter.Err()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyCheckEvaluations(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidyCheckEvaluations() error {
|
||||
iter, err := tu.store.ListAllEvaluations()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
err = iterateTidy(iter, dropInvalid, func(eval *happydns.CheckEvaluation) error {
|
||||
for iter.Next() {
|
||||
eval := iter.Item()
|
||||
|
||||
drop := false
|
||||
|
||||
if eval.Target.UserId != "" {
|
||||
|
|
@ -139,34 +119,36 @@ func (tu *tidyUpUsecase) TidyCheckEvaluations(dropInvalid bool) error {
|
|||
}
|
||||
|
||||
if !drop && eval.PlanID != nil {
|
||||
if _, err := tu.store.GetCheckPlan(*eval.PlanID); errors.Is(err, happydns.ErrCheckPlanNotFound) {
|
||||
if _, err = tu.store.GetCheckPlan(*eval.PlanID); errors.Is(err, happydns.ErrCheckPlanNotFound) {
|
||||
log.Printf("Deleting orphan check evaluation (plan %s not found): %s\n", eval.PlanID.String(), eval.Id.String())
|
||||
drop = true
|
||||
}
|
||||
}
|
||||
|
||||
if drop {
|
||||
if err := tu.store.DeleteEvaluation(eval.Id); err != nil {
|
||||
if err = tu.store.DeleteEvaluation(eval.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
}
|
||||
|
||||
if err = iter.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tu.store.TidyEvaluationIndexes()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyCheckPlans(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidyCheckPlans() error {
|
||||
iter, err := tu.store.ListAllCheckPlans()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
err = iterateTidy(iter, dropInvalid, func(plan *happydns.CheckPlan) error {
|
||||
for iter.Next() {
|
||||
plan := iter.Item()
|
||||
|
||||
if plan.Target.UserId != "" {
|
||||
userId, err := happydns.NewIdentifierFromString(plan.Target.UserId)
|
||||
if err == nil {
|
||||
|
|
@ -175,7 +157,10 @@ func (tu *tidyUpUsecase) TidyCheckPlans(dropInvalid bool) error {
|
|||
log.Printf("Deleting orphan check plan (user %s not found): %s\n", plan.Target.UserId, plan.Id.String())
|
||||
_ = tu.store.DeleteEvaluationsByChecker(plan.CheckerID, plan.Target)
|
||||
_ = tu.store.DeleteExecutionsByChecker(plan.CheckerID, plan.Target)
|
||||
return iter.DropItem()
|
||||
if err = iter.DropItem(); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -188,31 +173,39 @@ func (tu *tidyUpUsecase) TidyCheckPlans(dropInvalid bool) error {
|
|||
log.Printf("Deleting orphan check plan (domain %s not found): %s\n", plan.Target.DomainId, plan.Id.String())
|
||||
_ = tu.store.DeleteEvaluationsByChecker(plan.CheckerID, plan.Target)
|
||||
_ = tu.store.DeleteExecutionsByChecker(plan.CheckerID, plan.Target)
|
||||
return iter.DropItem()
|
||||
if err = iter.DropItem(); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
}
|
||||
|
||||
if err := iter.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tu.store.TidyCheckPlanIndexes()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyCheckerConfigurations(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidyCheckerConfigurations() error {
|
||||
iter, err := tu.store.ListAllCheckerConfigurations()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
return iterateTidy(iter, dropInvalid, func(cfg *happydns.CheckerOptionsPositional) error {
|
||||
for iter.Next() {
|
||||
cfg := iter.Item()
|
||||
|
||||
if cfg.UserId != nil {
|
||||
if _, err := tu.store.GetUser(*cfg.UserId); errors.Is(err, happydns.ErrUserNotFound) {
|
||||
if _, err = tu.store.GetUser(*cfg.UserId); errors.Is(err, happydns.ErrUserNotFound) {
|
||||
log.Printf("Deleting orphan checker configuration (user %s not found): %s\n", cfg.UserId.String(), cfg.CheckName)
|
||||
return iter.DropItem()
|
||||
if err = iter.DropItem(); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -222,7 +215,10 @@ func (tu *tidyUpUsecase) TidyCheckerConfigurations(dropInvalid bool) error {
|
|||
domain, err := tu.store.GetDomain(*cfg.DomainId)
|
||||
if errors.Is(err, happydns.ErrDomainNotFound) {
|
||||
log.Printf("Deleting orphan checker configuration (domain %s not found): %s\n", cfg.DomainId.String(), cfg.CheckName)
|
||||
return iter.DropItem()
|
||||
if err = iter.DropItem(); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -257,22 +253,28 @@ func (tu *tidyUpUsecase) TidyCheckerConfigurations(dropInvalid bool) error {
|
|||
}
|
||||
if !found {
|
||||
log.Printf("Deleting orphan checker configuration (service %s not found in domain %s): %s\n", cfg.ServiceId.String(), cfg.DomainId.String(), cfg.CheckName)
|
||||
return iter.DropItem()
|
||||
if err = iter.DropItem(); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return iter.Err()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyExecutions(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidyExecutions() error {
|
||||
iter, err := tu.store.ListAllExecutions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
err = iterateTidy(iter, dropInvalid, func(exec *happydns.Execution) error {
|
||||
for iter.Next() {
|
||||
exec := iter.Item()
|
||||
|
||||
drop := false
|
||||
|
||||
if exec.Target.UserId != "" {
|
||||
|
|
@ -296,53 +298,58 @@ func (tu *tidyUpUsecase) TidyExecutions(dropInvalid bool) error {
|
|||
}
|
||||
|
||||
if !drop && exec.PlanID != nil {
|
||||
if _, err := tu.store.GetCheckPlan(*exec.PlanID); errors.Is(err, happydns.ErrCheckPlanNotFound) {
|
||||
if _, err = tu.store.GetCheckPlan(*exec.PlanID); errors.Is(err, happydns.ErrCheckPlanNotFound) {
|
||||
log.Printf("Deleting orphan execution (plan %s not found): %s\n", exec.PlanID.String(), exec.Id.String())
|
||||
drop = true
|
||||
}
|
||||
}
|
||||
|
||||
if drop {
|
||||
if err := tu.store.DeleteExecution(exec.Id); err != nil {
|
||||
if err = tu.store.DeleteExecution(exec.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
}
|
||||
|
||||
if err = iter.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tu.store.TidyExecutionIndexes()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyObservationCache(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidyObservationCache() error {
|
||||
iter, err := tu.store.ListAllCachedObservations()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
return iterateTidy(iter, dropInvalid, func(entry *happydns.ObservationCacheEntry) error {
|
||||
if _, err := tu.store.GetSnapshot(entry.SnapshotID); errors.Is(err, happydns.ErrSnapshotNotFound) {
|
||||
for iter.Next() {
|
||||
entry := iter.Item()
|
||||
|
||||
if _, err = tu.store.GetSnapshot(entry.SnapshotID); errors.Is(err, happydns.ErrSnapshotNotFound) {
|
||||
log.Printf("Deleting stale observation cache entry (snapshot %s not found)\n", entry.SnapshotID.String())
|
||||
if err = iter.DropItem(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return iter.Err()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyDomains(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidyDomains() error {
|
||||
iter, err := tu.store.ListAllDomains()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
return iterateTidy(iter, dropInvalid, func(domain *happydns.Domain) error {
|
||||
if _, err := tu.store.GetUser(domain.Owner); errors.Is(err, happydns.ErrUserNotFound) {
|
||||
for iter.Next() {
|
||||
domain := iter.Item()
|
||||
|
||||
if _, err = tu.store.GetUser(domain.Owner); errors.Is(err, happydns.ErrUserNotFound) {
|
||||
// Drop domain of unexistant users
|
||||
log.Printf("Deleting orphan domain (user %s not found): %v\n", domain.Owner.String(), domain)
|
||||
if err = iter.DropItem(); err != nil {
|
||||
|
|
@ -350,45 +357,51 @@ func (tu *tidyUpUsecase) TidyDomains(dropInvalid bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
if _, err := tu.store.GetProvider(domain.ProviderId); errors.Is(err, happydns.ErrProviderNotFound) {
|
||||
if _, err = tu.store.GetProvider(domain.ProviderId); errors.Is(err, happydns.ErrProviderNotFound) {
|
||||
// Drop domain of unexistant provider
|
||||
log.Printf("Deleting orphan domain (provider %s not found): %v\n", domain.ProviderId.String(), domain)
|
||||
if err = iter.DropItem(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return iter.Err()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyDomainLogs(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidyDomainLogs() error {
|
||||
iter, err := tu.store.ListAllDomainLogs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
return iterateTidy(iter, dropInvalid, func(l *happydns.DomainLogWithDomainId) error {
|
||||
if _, err := tu.store.GetDomain(l.DomainId); errors.Is(err, happydns.ErrDomainNotFound) {
|
||||
for iter.Next() {
|
||||
l := iter.Item()
|
||||
|
||||
if _, err = tu.store.GetDomain(l.DomainId); errors.Is(err, happydns.ErrDomainNotFound) {
|
||||
// Drop domain of unexistant provider
|
||||
log.Printf("Deleting orphan domain log (domain %s not found): %v\n", l.DomainId.String(), l)
|
||||
if err = iter.DropItem(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return iter.Err()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyProviders(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidyProviders() error {
|
||||
iter, err := tu.store.ListAllProviders()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
return iterateTidy(iter, dropInvalid, func(prvd *happydns.ProviderMessage) error {
|
||||
_, err := tu.store.GetUser(prvd.Owner)
|
||||
for iter.Next() {
|
||||
prvd := iter.Item()
|
||||
|
||||
_, err = tu.store.GetUser(prvd.Owner)
|
||||
if errors.Is(err, happydns.ErrUserNotFound) {
|
||||
// Drop providers of unexistant users
|
||||
log.Printf("Deleting orphan provider (user %s not found): %v\n", prvd.Owner.String(), prvd)
|
||||
|
|
@ -396,19 +409,22 @@ func (tu *tidyUpUsecase) TidyProviders(dropInvalid bool) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return iter.Err()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidySessions(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidySessions() error {
|
||||
iter, err := tu.store.ListAllSessions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
return iterateTidy(iter, dropInvalid, func(session *happydns.Session) error {
|
||||
_, err := tu.store.GetUser(session.IdUser)
|
||||
for iter.Next() {
|
||||
session := iter.Item()
|
||||
|
||||
_, err = tu.store.GetUser(session.IdUser)
|
||||
if errors.Is(err, happydns.ErrUserNotFound) {
|
||||
// Drop session from unexistant users
|
||||
log.Printf("Deleting orphan session (user %s not found): %v\n", session.IdUser.String(), session)
|
||||
|
|
@ -416,11 +432,12 @@ func (tu *tidyUpUsecase) TidySessions(dropInvalid bool) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return iter.Err()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidySnapshots(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidySnapshots() error {
|
||||
// Collect all snapshot IDs referenced by evaluations.
|
||||
evalIter, err := tu.store.ListAllEvaluations()
|
||||
if err != nil {
|
||||
|
|
@ -429,12 +446,13 @@ func (tu *tidyUpUsecase) TidySnapshots(dropInvalid bool) error {
|
|||
defer evalIter.Close()
|
||||
|
||||
referencedSnapshots := make(map[string]struct{})
|
||||
if err = iterateTidy(evalIter, dropInvalid, func(eval *happydns.CheckEvaluation) error {
|
||||
for evalIter.Next() {
|
||||
eval := evalIter.Item()
|
||||
if !eval.SnapshotID.IsEmpty() {
|
||||
referencedSnapshots[eval.SnapshotID.String()] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
}
|
||||
if err = evalIter.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -445,39 +463,44 @@ func (tu *tidyUpUsecase) TidySnapshots(dropInvalid bool) error {
|
|||
}
|
||||
defer iter.Close()
|
||||
|
||||
return iterateTidy(iter, dropInvalid, func(snap *happydns.ObservationSnapshot) error {
|
||||
for iter.Next() {
|
||||
snap := iter.Item()
|
||||
if _, ok := referencedSnapshots[snap.Id.String()]; !ok {
|
||||
log.Printf("Deleting orphan snapshot: %s\n", snap.Id.String())
|
||||
if err := iter.DropItem(); err != nil {
|
||||
if err = iter.DropItem(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return iter.Err()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyUsers(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidyUsers() error {
|
||||
iter, err := tu.store.ListAllAuthUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
return iterateTidy(iter, dropInvalid, func(authUser *happydns.UserAuth) error {
|
||||
for iter.Next() {
|
||||
authUser := iter.Item()
|
||||
|
||||
if authUser.EmailVerification == nil && authUser.LastLoggedIn == nil && time.Since(authUser.CreatedAt) > 7*24*time.Hour {
|
||||
log.Printf("Deleting user with unverified email and no login (created %s): %s\n", authUser.CreatedAt.Format(time.RFC3339), authUser.Email)
|
||||
if err := tu.store.DeleteUser(authUser.Id); err != nil && !errors.Is(err, happydns.ErrUserNotFound) {
|
||||
if err = tu.store.DeleteUser(authUser.Id); err != nil && !errors.Is(err, happydns.ErrUserNotFound) {
|
||||
return err
|
||||
}
|
||||
if err := iter.DropItem(); err != nil {
|
||||
if err = iter.DropItem(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return iter.Err()
|
||||
}
|
||||
|
||||
func (tu *tidyUpUsecase) TidyZones(dropInvalid bool) error {
|
||||
func (tu *tidyUpUsecase) TidyZones() error {
|
||||
iterdn, err := tu.store.ListAllDomains()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -485,10 +508,15 @@ func (tu *tidyUpUsecase) TidyZones(dropInvalid bool) error {
|
|||
defer iterdn.Close()
|
||||
|
||||
var referencedZones []happydns.Identifier
|
||||
if err = iterateTidy(iterdn, dropInvalid, func(domain *happydns.Domain) error {
|
||||
referencedZones = append(referencedZones, domain.ZoneHistory...)
|
||||
return nil
|
||||
}); err != nil {
|
||||
|
||||
for iterdn.Next() {
|
||||
domain := iterdn.Item()
|
||||
for _, zh := range domain.ZoneHistory {
|
||||
referencedZones = append(referencedZones, zh)
|
||||
}
|
||||
}
|
||||
|
||||
if err = iterdn.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -498,7 +526,9 @@ func (tu *tidyUpUsecase) TidyZones(dropInvalid bool) error {
|
|||
}
|
||||
defer iter.Close()
|
||||
|
||||
return iterateTidy(iter, dropInvalid, func(zone *happydns.ZoneMessage) error {
|
||||
for iter.Next() {
|
||||
zone := iter.Item()
|
||||
|
||||
foundZone := false
|
||||
for _, zid := range referencedZones {
|
||||
if zid.Equals(zone.Id) {
|
||||
|
|
@ -510,10 +540,11 @@ func (tu *tidyUpUsecase) TidyZones(dropInvalid bool) error {
|
|||
if !foundZone {
|
||||
// Drop orphan zones
|
||||
log.Printf("Deleting orphan zone: %s\n", zone.Id.String())
|
||||
if err := iter.DropItem(); err != nil {
|
||||
if err = iter.DropItem(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return iter.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func TestTidyObservationCache_RemovesStaleEntries(t *testing.T) {
|
|||
|
||||
// Run tidy.
|
||||
tu := usecase.NewTidyUpUsecase(store)
|
||||
if err := tu.TidyObservationCache(true); err != nil {
|
||||
if err := tu.TidyObservationCache(); err != nil {
|
||||
t.Fatalf("TidyObservationCache() error: %v", err)
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ func TestTidyObservationCache_EmptyCache(t *testing.T) {
|
|||
}
|
||||
|
||||
tu := usecase.NewTidyUpUsecase(store)
|
||||
if err := tu.TidyObservationCache(true); err != nil {
|
||||
if err := tu.TidyObservationCache(); err != nil {
|
||||
t.Fatalf("TidyObservationCache() on empty cache error: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,17 +22,13 @@
|
|||
package happydns
|
||||
|
||||
type Backup struct {
|
||||
Version int
|
||||
Domains []*Domain
|
||||
DomainsLogs map[string][]*DomainLog
|
||||
Errors []string
|
||||
Providers []*ProviderMessage
|
||||
Sessions []*Session
|
||||
Users []*User
|
||||
UsersAuth UserAuths
|
||||
Zones []*ZoneMessage
|
||||
CheckerConfigurations []*CheckerOptionsPositional
|
||||
CheckPlans []*CheckPlan
|
||||
CheckEvaluations []*CheckEvaluation
|
||||
Executions []*Execution
|
||||
Version int
|
||||
Domains []*Domain
|
||||
DomainsLogs map[string][]*DomainLog
|
||||
Errors []string
|
||||
Providers []*ProviderMessage
|
||||
Sessions []*Session
|
||||
Users []*User
|
||||
UsersAuth UserAuths
|
||||
Zones []*ZoneMessage
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,21 +24,18 @@ package happydns
|
|||
import ()
|
||||
|
||||
type TidyUpUseCase interface {
|
||||
// TidyAll runs every tidy pass. When dropInvalid is true, iterators
|
||||
// that encounter undecodable records (e.g. legacy schema drift) will
|
||||
// delete those records; otherwise they are only logged.
|
||||
TidyAll(dropInvalid bool) error
|
||||
TidyAuthUsers(dropInvalid bool) error
|
||||
TidyCheckEvaluations(dropInvalid bool) error
|
||||
TidyCheckPlans(dropInvalid bool) error
|
||||
TidyCheckerConfigurations(dropInvalid bool) error
|
||||
TidyExecutions(dropInvalid bool) error
|
||||
TidyObservationCache(dropInvalid bool) error
|
||||
TidySnapshots(dropInvalid bool) error
|
||||
TidyDomains(dropInvalid bool) error
|
||||
TidyDomainLogs(dropInvalid bool) error
|
||||
TidyProviders(dropInvalid bool) error
|
||||
TidySessions(dropInvalid bool) error
|
||||
TidyUsers(dropInvalid bool) error
|
||||
TidyZones(dropInvalid bool) error
|
||||
TidyAll() error
|
||||
TidyAuthUsers() error
|
||||
TidyCheckEvaluations() error
|
||||
TidyCheckPlans() error
|
||||
TidyCheckerConfigurations() error
|
||||
TidyExecutions() error
|
||||
TidyObservationCache() error
|
||||
TidySnapshots() error
|
||||
TidyDomains() error
|
||||
TidyDomainLogs() error
|
||||
TidyProviders() error
|
||||
TidySessions() error
|
||||
TidyUsers() error
|
||||
TidyZones() error
|
||||
}
|
||||
|
|
|
|||
12
package.json
12
package.json
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"name": "happydomain-monorepo",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"web",
|
||||
"web-admin"
|
||||
],
|
||||
"resolutions": {
|
||||
"vite": "^8.0.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -56,13 +56,6 @@ func GetDomainWhoisInfo(ctx context.Context, domain happydns.Origin) (*happydns.
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Some registries (e.g. Verisign for .com) return a "No match" response
|
||||
// that the parser accepts without error but produces an empty Domain
|
||||
// field. Treat this as a non-existent domain.
|
||||
if result.Domain == nil || result.Domain.Domain == "" {
|
||||
return nil, happydns.ErrDomainDoesNotExist
|
||||
}
|
||||
|
||||
return mapWhoisResult(&result), nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -315,7 +315,6 @@ func inferOperation(name string) string {
|
|||
{"Count", "count"},
|
||||
{"Create", "create"},
|
||||
{"Update", "update"},
|
||||
{"Restore", "restore"},
|
||||
{"Delete", "delete"},
|
||||
{"Clear", "delete"},
|
||||
{"Set", "set"},
|
||||
|
|
|
|||
1
web-admin/.npmrc
Symbolic link
1
web-admin/.npmrc
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../web/.npmrc
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
1
web-admin/.prettierignore
Symbolic link
1
web-admin/.prettierignore
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../web/.prettierignore
|
||||
1
web-admin/.prettierrc
Symbolic link
1
web-admin/.prettierrc
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../web/.prettierrc
|
||||
1
web-admin/eslint.config.js
Symbolic link
1
web-admin/eslint.config.js
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../web/eslint.config.js
|
||||
1
web-admin/node_modules
Symbolic link
1
web-admin/node_modules
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../web/node_modules/
|
||||
1
web-admin/package-lock.json
generated
Symbolic link
1
web-admin/package-lock.json
generated
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../web/package-lock.json
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
{
|
||||
"name": "@happydomain/web-admin",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "vitest",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||
"format": "prettier --plugin-search-dir . --write .",
|
||||
"generate:api": "openapi-ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^2.0.0",
|
||||
"@eslint/js": "^10.0.0",
|
||||
"@sveltejs/adapter-static": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.22.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||
"@types/node": "^25.0.10",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-svelte": "^3.0.0",
|
||||
"globals": "^17.0.0",
|
||||
"prettier": "^3.4.0",
|
||||
"prettier-plugin-svelte": "^3.3.0",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"svelte-preprocess": "^6.0.0",
|
||||
"tslib": "^2.8.0",
|
||||
"typescript": "^5.5.0",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"vite": "^8.0.0",
|
||||
"vitest": "^4.0.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"vite": "^8.0.0"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@hey-api/openapi-ts": "^0.95.0",
|
||||
"@sveltestrap/sveltestrap": "^7.0.0",
|
||||
"bootstrap": "^5.3.0",
|
||||
"bootstrap-icons": "^1.13.0",
|
||||
"chart.js": "^4.5.1",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"html-escaper": "^3.0.0",
|
||||
"sass": "^1.97.0",
|
||||
"sass-loader": "^16.0.0",
|
||||
"sveltekit-i18n": "^2.4.0"
|
||||
}
|
||||
}
|
||||
1
web-admin/package.json
Symbolic link
1
web-admin/package.json
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../web/package.json
|
||||
326
package-lock.json → web/package-lock.json
generated
326
package-lock.json → web/package-lock.json
generated
|
|
@ -1,16 +1,48 @@
|
|||
{
|
||||
"name": "happydomain-monorepo",
|
||||
"name": "happyDomain",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "happydomain-monorepo",
|
||||
"name": "happyDomain",
|
||||
"version": "0.0.1",
|
||||
"workspaces": [
|
||||
"web",
|
||||
"web-admin"
|
||||
]
|
||||
"dependencies": {
|
||||
"@hey-api/openapi-ts": "^0.95.0",
|
||||
"@sveltestrap/sveltestrap": "^7.0.0",
|
||||
"bootstrap": "^5.3.0",
|
||||
"bootstrap-icons": "^1.13.0",
|
||||
"chart.js": "^4.5.1",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"html-escaper": "^3.0.0",
|
||||
"sass": "^1.97.0",
|
||||
"sass-loader": "^16.0.0",
|
||||
"sveltekit-i18n": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^2.0.0",
|
||||
"@eslint/js": "^10.0.0",
|
||||
"@sveltejs/adapter-static": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.22.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||
"@types/node": "^25.0.10",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-svelte": "^3.0.0",
|
||||
"globals": "^17.0.0",
|
||||
"prettier": "^3.4.0",
|
||||
"prettier-plugin-svelte": "^3.3.0",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"svelte-preprocess": "^6.0.0",
|
||||
"tslib": "^2.8.0",
|
||||
"typescript": "^5.5.0",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"vite": "^8.0.0",
|
||||
"vitest": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.9.2",
|
||||
|
|
@ -195,14 +227,6 @@
|
|||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
}
|
||||
},
|
||||
"node_modules/@happydomain/web": {
|
||||
"resolved": "web",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@happydomain/web-admin": {
|
||||
"resolved": "web-admin",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@hey-api/codegen-core": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@hey-api/codegen-core/-/codegen-core-0.7.4.tgz",
|
||||
|
|
@ -308,43 +332,29 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
|
||||
"integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@humanfs/types": "^0.15.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/node": {
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
|
||||
"integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
|
||||
"version": "0.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
|
||||
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@humanfs/core": "^0.19.2",
|
||||
"@humanfs/types": "^0.15.0",
|
||||
"@humanfs/core": "^0.19.1",
|
||||
"@humanwhocodes/retry": "^0.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/types": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
|
||||
"integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/module-importer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
|
||||
|
|
@ -431,9 +441,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
|
||||
"integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz",
|
||||
"integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
|
|
@ -1259,17 +1269,17 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz",
|
||||
"integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==",
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.1.tgz",
|
||||
"integrity": "sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.58.2",
|
||||
"@typescript-eslint/type-utils": "8.58.2",
|
||||
"@typescript-eslint/utils": "8.58.2",
|
||||
"@typescript-eslint/visitor-keys": "8.58.2",
|
||||
"@typescript-eslint/scope-manager": "8.58.1",
|
||||
"@typescript-eslint/type-utils": "8.58.1",
|
||||
"@typescript-eslint/utils": "8.58.1",
|
||||
"@typescript-eslint/visitor-keys": "8.58.1",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
|
|
@ -1282,7 +1292,7 @@
|
|||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.58.2",
|
||||
"@typescript-eslint/parser": "^8.58.1",
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
|
|
@ -1298,16 +1308,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz",
|
||||
"integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==",
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.1.tgz",
|
||||
"integrity": "sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.58.2",
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"@typescript-eslint/typescript-estree": "8.58.2",
|
||||
"@typescript-eslint/visitor-keys": "8.58.2",
|
||||
"@typescript-eslint/scope-manager": "8.58.1",
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"@typescript-eslint/typescript-estree": "8.58.1",
|
||||
"@typescript-eslint/visitor-keys": "8.58.1",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -1323,14 +1333,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz",
|
||||
"integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==",
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.1.tgz",
|
||||
"integrity": "sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.58.2",
|
||||
"@typescript-eslint/types": "^8.58.2",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.58.1",
|
||||
"@typescript-eslint/types": "^8.58.1",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -1345,14 +1355,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz",
|
||||
"integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==",
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.1.tgz",
|
||||
"integrity": "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"@typescript-eslint/visitor-keys": "8.58.2"
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"@typescript-eslint/visitor-keys": "8.58.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
|
@ -1363,9 +1373,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz",
|
||||
"integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==",
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.1.tgz",
|
||||
"integrity": "sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
@ -1380,15 +1390,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz",
|
||||
"integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==",
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.1.tgz",
|
||||
"integrity": "sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"@typescript-eslint/typescript-estree": "8.58.2",
|
||||
"@typescript-eslint/utils": "8.58.2",
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"@typescript-eslint/typescript-estree": "8.58.1",
|
||||
"@typescript-eslint/utils": "8.58.1",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
},
|
||||
|
|
@ -1405,9 +1415,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz",
|
||||
"integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==",
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.1.tgz",
|
||||
"integrity": "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
@ -1419,16 +1429,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz",
|
||||
"integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==",
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.1.tgz",
|
||||
"integrity": "sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.58.2",
|
||||
"@typescript-eslint/tsconfig-utils": "8.58.2",
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"@typescript-eslint/visitor-keys": "8.58.2",
|
||||
"@typescript-eslint/project-service": "8.58.1",
|
||||
"@typescript-eslint/tsconfig-utils": "8.58.1",
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"@typescript-eslint/visitor-keys": "8.58.1",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
|
|
@ -1447,16 +1457,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz",
|
||||
"integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==",
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.1.tgz",
|
||||
"integrity": "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.58.2",
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"@typescript-eslint/typescript-estree": "8.58.2"
|
||||
"@typescript-eslint/scope-manager": "8.58.1",
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"@typescript-eslint/typescript-estree": "8.58.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
|
@ -1471,13 +1481,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz",
|
||||
"integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==",
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.1.tgz",
|
||||
"integrity": "sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"eslint-visitor-keys": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -2072,18 +2082,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "10.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.1.tgz",
|
||||
"integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==",
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz",
|
||||
"integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@eslint/config-array": "^0.23.5",
|
||||
"@eslint/config-helpers": "^0.5.5",
|
||||
"@eslint/core": "^1.2.1",
|
||||
"@eslint/plugin-kit": "^0.7.1",
|
||||
"@eslint/config-array": "^0.23.4",
|
||||
"@eslint/config-helpers": "^0.5.4",
|
||||
"@eslint/core": "^1.2.0",
|
||||
"@eslint/plugin-kit": "^0.7.0",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.2",
|
||||
|
|
@ -3292,9 +3302,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.10",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
|
||||
"integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
|
||||
"version": "8.5.9",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz",
|
||||
"integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -3451,9 +3461,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.8.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
|
||||
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
|
||||
"version": "3.8.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.2.tgz",
|
||||
"integrity": "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
|
@ -3745,16 +3755,16 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/std-env": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
|
||||
"integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz",
|
||||
"integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "5.55.4",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.4.tgz",
|
||||
"integrity": "sha512-q8DFohk6vUswSng95IZb9nzWJnbINZsK7OiM1snAa3qCjJBL0ZQpvMyAaVXjUukdM75J/m8UE8xwqat8Ors/zQ==",
|
||||
"version": "5.55.3",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.3.tgz",
|
||||
"integrity": "sha512-dS1N+i3bA1v+c4UDb750MlN5vCO82G6vxh8HeTsPsTdJ1BLsN1zxSyDlIdBBqUjqZ/BxEwM8UrFf98aaoVnZFQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/remapping": "^2.3.4",
|
||||
|
|
@ -4083,16 +4093,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz",
|
||||
"integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==",
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.1.tgz",
|
||||
"integrity": "sha512-gf6/oHChByg9HJvhMO1iBexJh12AqqTfnuxscMDOVqfJW3htsdRJI/GfPpHTTcyeB8cSTUY2JcZmVgoyPqcrDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.58.2",
|
||||
"@typescript-eslint/parser": "8.58.2",
|
||||
"@typescript-eslint/typescript-estree": "8.58.2",
|
||||
"@typescript-eslint/utils": "8.58.2"
|
||||
"@typescript-eslint/eslint-plugin": "8.58.1",
|
||||
"@typescript-eslint/parser": "8.58.1",
|
||||
"@typescript-eslint/typescript-estree": "8.58.1",
|
||||
"@typescript-eslint/utils": "8.58.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
|
@ -4410,86 +4420,6 @@
|
|||
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz",
|
||||
"integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"web": {
|
||||
"name": "@happydomain/web",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@hey-api/openapi-ts": "^0.95.0",
|
||||
"@sveltestrap/sveltestrap": "^7.0.0",
|
||||
"bootstrap": "^5.3.0",
|
||||
"bootstrap-icons": "^1.13.0",
|
||||
"chart.js": "^4.5.1",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"html-escaper": "^3.0.0",
|
||||
"sass": "^1.97.0",
|
||||
"sass-loader": "^16.0.0",
|
||||
"sveltekit-i18n": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^2.0.0",
|
||||
"@eslint/js": "^10.0.0",
|
||||
"@sveltejs/adapter-static": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.22.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||
"@types/node": "^25.0.10",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-svelte": "^3.0.0",
|
||||
"globals": "^17.0.0",
|
||||
"prettier": "^3.4.0",
|
||||
"prettier-plugin-svelte": "^3.3.0",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"svelte-preprocess": "^6.0.0",
|
||||
"tslib": "^2.8.0",
|
||||
"typescript": "^5.5.0",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"vite": "^8.0.0",
|
||||
"vitest": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"web-admin": {
|
||||
"name": "@happydomain/web-admin",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@hey-api/openapi-ts": "^0.95.0",
|
||||
"@sveltestrap/sveltestrap": "^7.0.0",
|
||||
"bootstrap": "^5.3.0",
|
||||
"bootstrap-icons": "^1.13.0",
|
||||
"chart.js": "^4.5.1",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"html-escaper": "^3.0.0",
|
||||
"sass": "^1.97.0",
|
||||
"sass-loader": "^16.0.0",
|
||||
"sveltekit-i18n": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^2.0.0",
|
||||
"@eslint/js": "^10.0.0",
|
||||
"@sveltejs/adapter-static": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.22.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||
"@types/node": "^25.0.10",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-svelte": "^3.0.0",
|
||||
"globals": "^17.0.0",
|
||||
"prettier": "^3.4.0",
|
||||
"prettier-plugin-svelte": "^3.3.0",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"svelte-preprocess": "^6.0.0",
|
||||
"tslib": "^2.8.0",
|
||||
"typescript": "^5.5.0",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"vite": "^8.0.0",
|
||||
"vitest": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "@happydomain/web",
|
||||
"name": "happyDomain",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@
|
|||
</div>
|
||||
{:then sessions}
|
||||
<ListGroup>
|
||||
{#each [...sessions].sort((a, b) => new Date(b.upd || b.time).getTime() - new Date(a.upd || a.time).getTime()) as session (session.id)}
|
||||
{#each sessions as session (session.id)}
|
||||
<ListGroupItem class="d-flex align-items-center justify-content-between">
|
||||
<div class="flex-fill" style="max-width:90%">
|
||||
<div class="text-truncate">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue