Update module golang.org/x/net to v0.46.0 #5
11 changed files with 5628 additions and 5541 deletions
22
.drone-manifest.yml
Normal file
22
.drone-manifest.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
image: happydomain/happydeliver:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||||
|
{{#if build.tags}}
|
||||||
|
tags:
|
||||||
|
{{#each build.tags}}
|
||||||
|
- {{this}}
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
manifests:
|
||||||
|
- image: happydomain/happydeliver:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||||
|
platform:
|
||||||
|
architecture: amd64
|
||||||
|
os: linux
|
||||||
|
- image: happydomain/happydeliver:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||||
|
platform:
|
||||||
|
architecture: arm64
|
||||||
|
os: linux
|
||||||
|
variant: v8
|
||||||
|
- image: happydomain/happydeliver:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||||
|
platform:
|
||||||
|
architecture: arm
|
||||||
|
os: linux
|
||||||
|
variant: v7
|
||||||
156
.drone.yml
Normal file
156
.drone.yml
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: build-arm64
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: arm64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: frontend
|
||||||
|
image: node:22-alpine
|
||||||
|
commands:
|
||||||
|
- cd web
|
||||||
|
- npm install --network-timeout=100000
|
||||||
|
- npm run generate:api
|
||||||
|
- npm run build
|
||||||
|
|
||||||
|
- name: backend-commit
|
||||||
|
image: golang:1-alpine
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache git
|
||||||
|
- go generate ./...
|
||||||
|
- go build -tags netgo -ldflags '-w -X main.Version=${DRONE_BRANCH}-${DRONE_COMMIT} -X main.build=${DRONE_BUILD_NUMBER}' -o happydeliver-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDeliver
|
||||||
|
- ln happydeliver-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydeliver
|
||||||
|
environment:
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- tag
|
||||||
|
|
||||||
|
- name: backend-tag
|
||||||
|
image: golang:1-alpine
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache git
|
||||||
|
- go generate ./...
|
||||||
|
- go build -tags netgo -ldflags '-w -X main.Version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}' -o happydeliver-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDeliver/
|
||||||
|
- ln happydeliver-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydeliver
|
||||||
|
environment:
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
|
||||||
|
- name: build-commit macOS
|
||||||
|
image: golang:1-alpine
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache git
|
||||||
|
- go build -tags netgo -ldflags '-w -X "main.Version=${DRONE_BRANCH}-${DRONE_COMMIT}" -X main.build=${DRONE_BUILD_NUMBER}' -o happydeliver-darwin-${DRONE_STAGE_ARCH} ./cmd/happyDeliver/
|
||||||
|
environment:
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
GOOS: darwin
|
||||||
|
GOARCH: arm64
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- tag
|
||||||
|
|
||||||
|
- name: build-tag macOS
|
||||||
|
image: golang:1-alpine
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache git
|
||||||
|
- go build -tags netgo -ldflags '-w -X "main.Version=${DRONE_TAG##v}" -X main.build=${DRONE_BUILD_NUMBER}' -o happydeliver-darwin-${DRONE_STAGE_ARCH} ./cmd/happyDeliver/
|
||||||
|
environment:
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
GOOS: darwin
|
||||||
|
GOARCH: arm64
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
|
||||||
|
- name: publish on Docker Hub
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
repo: happydomain/happydeliver
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
exclude:
|
||||||
|
- renovate/*
|
||||||
|
event:
|
||||||
|
- cron
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: build-amd64
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: publish on Docker Hub
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
repo: happydomain/happydeliver
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
exclude:
|
||||||
|
- renovate/*
|
||||||
|
event:
|
||||||
|
- cron
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: docker-manifest
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: arm64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: publish on Docker Hub
|
||||||
|
image: plugins/manifest
|
||||||
|
settings:
|
||||||
|
auto_tag: true
|
||||||
|
ignore_missing: true
|
||||||
|
spec: .drone-manifest.yml
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
exclude:
|
||||||
|
- renovate/*
|
||||||
|
event:
|
||||||
|
- cron
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- build-amd64
|
||||||
|
- build-arm64
|
||||||
2
go.mod
2
go.mod
|
|
@ -7,7 +7,7 @@ require (
|
||||||
github.com/gin-gonic/gin v1.11.0
|
github.com/gin-gonic/gin v1.11.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/oapi-codegen/runtime v1.1.2
|
github.com/oapi-codegen/runtime v1.1.2
|
||||||
golang.org/x/net v0.45.0
|
golang.org/x/net v0.46.0
|
||||||
gorm.io/driver/postgres v1.6.0
|
gorm.io/driver/postgres v1.6.0
|
||||||
gorm.io/driver/sqlite v1.6.0
|
gorm.io/driver/sqlite v1.6.0
|
||||||
gorm.io/gorm v1.31.0
|
gorm.io/gorm v1.31.0
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -187,8 +187,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||||
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
|
|
||||||
108
internal/app/cleanup.go
Normal file
108
internal/app/cleanup.go
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
// This file is part of the happyDeliver (R) project.
|
||||||
|
// Copyright (c) 2025 happyDomain
|
||||||
|
// Authors: Pierre-Olivier Mercier, et al.
|
||||||
|
//
|
||||||
|
// This program is offered under a commercial and under the AGPL license.
|
||||||
|
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||||
|
//
|
||||||
|
// For AGPL licensing:
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.happydns.org/happyDeliver/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// How often to run the cleanup check
|
||||||
|
cleanupInterval = 1 * time.Hour
|
||||||
|
)
|
||||||
|
|
||||||
|
// CleanupService handles periodic cleanup of old reports
|
||||||
|
type CleanupService struct {
|
||||||
|
store storage.Storage
|
||||||
|
retention time.Duration
|
||||||
|
ticker *time.Ticker
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCleanupService creates a new cleanup service
|
||||||
|
func NewCleanupService(store storage.Storage, retention time.Duration) *CleanupService {
|
||||||
|
return &CleanupService{
|
||||||
|
store: store,
|
||||||
|
retention: retention,
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start begins the cleanup service in a background goroutine
|
||||||
|
func (s *CleanupService) Start(ctx context.Context) {
|
||||||
|
if s.retention <= 0 {
|
||||||
|
log.Println("Report retention is disabled (keeping reports forever)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Starting cleanup service: will delete reports older than %s", s.retention)
|
||||||
|
|
||||||
|
// Run cleanup immediately on startup
|
||||||
|
s.runCleanup()
|
||||||
|
|
||||||
|
// Then run periodically
|
||||||
|
s.ticker = time.NewTicker(cleanupInterval)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.ticker.C:
|
||||||
|
s.runCleanup()
|
||||||
|
case <-ctx.Done():
|
||||||
|
s.Stop()
|
||||||
|
return
|
||||||
|
case <-s.done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the cleanup service
|
||||||
|
func (s *CleanupService) Stop() {
|
||||||
|
if s.ticker != nil {
|
||||||
|
s.ticker.Stop()
|
||||||
|
}
|
||||||
|
close(s.done)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runCleanup performs the actual cleanup operation
|
||||||
|
func (s *CleanupService) runCleanup() {
|
||||||
|
cutoffTime := time.Now().Add(-s.retention)
|
||||||
|
log.Printf("Running cleanup: deleting reports older than %s", cutoffTime.Format(time.RFC3339))
|
||||||
|
|
||||||
|
deleted, err := s.store.DeleteOldReports(cutoffTime)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error during cleanup: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if deleted > 0 {
|
||||||
|
log.Printf("Cleanup completed: deleted %d old report(s)", deleted)
|
||||||
|
} else {
|
||||||
|
log.Printf("Cleanup completed: no old reports to delete")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
|
@ -49,6 +50,12 @@ func RunServer(cfg *config.Config) error {
|
||||||
|
|
||||||
log.Printf("Connected to %s database", cfg.Database.Type)
|
log.Printf("Connected to %s database", cfg.Database.Type)
|
||||||
|
|
||||||
|
// Start cleanup service for old reports
|
||||||
|
ctx := context.Background()
|
||||||
|
cleanupSvc := NewCleanupService(store, cfg.ReportRetention)
|
||||||
|
cleanupSvc.Start(ctx)
|
||||||
|
defer cleanupSvc.Stop()
|
||||||
|
|
||||||
// Start LMTP server in background
|
// Start LMTP server in background
|
||||||
go func() {
|
go func() {
|
||||||
if err := lmtp.StartServer(cfg.Email.LMTPAddr, store, cfg); err != nil {
|
if err := lmtp.StartServer(cfg.Email.LMTPAddr, store, cfg); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ func declareFlags(o *Config) {
|
||||||
flag.DurationVar(&o.Analysis.DNSTimeout, "dns-timeout", o.Analysis.DNSTimeout, "Timeout when performing DNS query")
|
flag.DurationVar(&o.Analysis.DNSTimeout, "dns-timeout", o.Analysis.DNSTimeout, "Timeout when performing DNS query")
|
||||||
flag.DurationVar(&o.Analysis.HTTPTimeout, "http-timeout", o.Analysis.HTTPTimeout, "Timeout when performing HTTP query")
|
flag.DurationVar(&o.Analysis.HTTPTimeout, "http-timeout", o.Analysis.HTTPTimeout, "Timeout when performing HTTP query")
|
||||||
flag.Var(&StringArray{&o.Analysis.RBLs}, "rbl", "Append a RBL (use this option multiple time to append multiple RBLs)")
|
flag.Var(&StringArray{&o.Analysis.RBLs}, "rbl", "Append a RBL (use this option multiple time to append multiple RBLs)")
|
||||||
|
flag.DurationVar(&o.ReportRetention, "report-retention", o.ReportRetention, "How long to keep reports (e.g., 720h, 30d). 0 = keep forever")
|
||||||
|
|
||||||
// Others flags are declared in some other files likes sources, storages, ... when they need specials configurations
|
// Others flags are declared in some other files likes sources, storages, ... when they need specials configurations
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,12 @@ import (
|
||||||
|
|
||||||
// Config represents the application configuration
|
// Config represents the application configuration
|
||||||
type Config struct {
|
type Config struct {
|
||||||
DevProxy string
|
DevProxy string
|
||||||
Bind string
|
Bind string
|
||||||
Database DatabaseConfig
|
Database DatabaseConfig
|
||||||
Email EmailConfig
|
Email EmailConfig
|
||||||
Analysis AnalysisConfig
|
Analysis AnalysisConfig
|
||||||
|
ReportRetention time.Duration // How long to keep reports. 0 = keep forever
|
||||||
}
|
}
|
||||||
|
|
||||||
// DatabaseConfig contains database connection settings
|
// DatabaseConfig contains database connection settings
|
||||||
|
|
@ -65,8 +66,9 @@ type AnalysisConfig struct {
|
||||||
// DefaultConfig returns a configuration with sensible defaults
|
// DefaultConfig returns a configuration with sensible defaults
|
||||||
func DefaultConfig() *Config {
|
func DefaultConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
DevProxy: "",
|
DevProxy: "",
|
||||||
Bind: ":8081",
|
Bind: ":8081",
|
||||||
|
ReportRetention: 0, // Keep reports forever by default
|
||||||
Database: DatabaseConfig{
|
Database: DatabaseConfig{
|
||||||
Type: "sqlite",
|
Type: "sqlite",
|
||||||
DSN: "happydeliver.db",
|
DSN: "happydeliver.db",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ package storage
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
|
|
@ -42,6 +43,7 @@ type Storage interface {
|
||||||
CreateReport(testID uuid.UUID, rawEmail []byte, reportJSON []byte) (*Report, error)
|
CreateReport(testID uuid.UUID, rawEmail []byte, reportJSON []byte) (*Report, error)
|
||||||
GetReport(testID uuid.UUID) (reportJSON []byte, rawEmail []byte, err error)
|
GetReport(testID uuid.UUID) (reportJSON []byte, rawEmail []byte, err error)
|
||||||
ReportExists(testID uuid.UUID) (bool, error)
|
ReportExists(testID uuid.UUID) (bool, error)
|
||||||
|
DeleteOldReports(olderThan time.Time) (int64, error)
|
||||||
|
|
||||||
// Close closes the database connection
|
// Close closes the database connection
|
||||||
Close() error
|
Close() error
|
||||||
|
|
@ -115,6 +117,15 @@ func (s *DBStorage) GetReport(testID uuid.UUID) ([]byte, []byte, error) {
|
||||||
return dbReport.ReportJSON, dbReport.RawEmail, nil
|
return dbReport.ReportJSON, dbReport.RawEmail, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteOldReports deletes reports older than the specified time
|
||||||
|
func (s *DBStorage) DeleteOldReports(olderThan time.Time) (int64, error) {
|
||||||
|
result := s.db.Where("created_at < ?", olderThan).Delete(&Report{})
|
||||||
|
if result.Error != nil {
|
||||||
|
return 0, fmt.Errorf("failed to delete old reports: %w", result.Error)
|
||||||
|
}
|
||||||
|
return result.RowsAffected, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Close closes the database connection
|
// Close closes the database connection
|
||||||
func (s *DBStorage) Close() error {
|
func (s *DBStorage) Close() error {
|
||||||
sqlDB, err := s.db.DB()
|
sqlDB, err := s.db.DB()
|
||||||
|
|
|
||||||
10840
web/package-lock.json
generated
10840
web/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -18,7 +18,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.4.0",
|
"@eslint/compat": "^1.4.0",
|
||||||
"@eslint/js": "^9.36.0",
|
"@eslint/js": "^9.36.0",
|
||||||
"@hey-api/openapi-ts": "0.80.0",
|
"@hey-api/openapi-ts": "0.85.2",
|
||||||
"@sveltejs/adapter-static": "^3.0.9",
|
"@sveltejs/adapter-static": "^3.0.9",
|
||||||
"@sveltejs/kit": "^2.43.2",
|
"@sveltejs/kit": "^2.43.2",
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.2.0",
|
"@sveltejs/vite-plugin-svelte": "^6.2.0",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue