diff --git a/.github/workflows/pr-build-check.yml b/.github/workflows/pr-build-check.yml index 7697afd..e98e6ff 100644 --- a/.github/workflows/pr-build-check.yml +++ b/.github/workflows/pr-build-check.yml @@ -1,10 +1,15 @@ -name: PR Build Check +name: Frontend build check on: pull_request: + paths: + - "dash/**" + push: branches: - main + paths: + - "dash/**" jobs: build: @@ -23,12 +28,3 @@ jobs: - name: Build frontend working-directory: dash run: bun run build - - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: '1.22' - - - name: Build server - working-directory: server - run: go build ./... diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..61db259 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,30 @@ +name: Tests + +on: + pull_request: + paths: + - "test/**" + - "server/**" + + push: + branches: + - main + paths: + - "test/**" + - "server/**" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.22' + + - name: Run all tests + working-directory: test + run: go test -v ./... diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..10e796c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "www"] + path = www + url = https://github.com/trymist/www diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c108a67 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,47 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on Keep a Changelog, +and this project adheres to Semantic Versioning. + +## [1.0.3] - 2026-01-12 + +### Added +- deployment cleanup on startup + +### Fixed +- infinite logging when file not found + +### Changed +- logs UI +- replace raw git commands with go-git +- replace raw docker commands with moby + +--- + +## [1.0.2] - 2025-12-31 + +### Added +- individual docker container statistics +- owner's email as let's encrypt email on signup + +--- + +## [1.0.1] - 2025-12-27 + +### Added +- Versioning in dashboard +- `git_clone_url` field for apps + +### Fixed +- update script + +--- + +## [1.0.0] - 2025-12-24 + +### Added +- Initial release +- Core paas functionalities +- web dashboard +- service deployment via docker diff --git a/README.md b/README.md index 4287724..5aaed4a 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ A lightweight, self-hostable Platform-as-a-Service built for developers. Deploy Docker applications from Git with automatic builds, custom domains, SSL certificates, and real-time monitoring. [![PR Build Check](https://github.com/corecollectives/Mist/actions/workflows/pr-build-check.yml/badge.svg)](https://github.com/corecollectives/Mist/actions/workflows/pr-build-check.yml) +[![Tests](https://github.com/corecollectives/Mist/actions/workflows/tests.yml/badge.svg)](https://github.com/corecollectives/Mist/actions/workflows/tests.yml) ## Quick Start @@ -31,7 +32,7 @@ After installation, access the dashboard at `http://your-server-ip:8080` ## Documentation -Full documentation is available at [trymist.cloud](https://trymist.cloud/guide) +Full documentation is available at [trymist.cloud](https://trymist.cloud/guide/getting-started.html) ## Development @@ -54,8 +55,8 @@ fyrer ## Community - [GitHub](https://github.com/corecollectives/mist) -- [Discord](https://discord.gg/kxK8XHR6) -- [Documentation](https://trymist.cloud/guide) +- [Discord](https://discord.gg/hr6TCQDDkj) +- [Documentation](https://trymist.cloud/guide/getting-started.html) ## License diff --git a/bin/mist-cli b/bin/mist-cli deleted file mode 100755 index 9f39b50..0000000 Binary files a/bin/mist-cli and /dev/null differ diff --git a/bin/server b/bin/server deleted file mode 100755 index 186499b..0000000 Binary files a/bin/server and /dev/null differ diff --git a/cli/cmd/db.go b/cli/cmd/db.go index d667d82..557a78a 100644 --- a/cli/cmd/db.go +++ b/cli/cmd/db.go @@ -1,36 +1,27 @@ package cmd import ( - "database/sql" "fmt" "os" "github.com/corecollectives/mist/models" _ "github.com/mattn/go-sqlite3" + "gorm.io/driver/sqlite" + "gorm.io/gorm" ) const dbPath = "/var/lib/mist/mist.db" -// initDB initializes the database connection for CLI -// It expects the database file to already exist func initDB() error { - // Check if database file exists if _, err := os.Stat(dbPath); os.IsNotExist(err) { return fmt.Errorf("database file not found at %s. Please ensure Mist is installed and running", dbPath) } - // Open database connection - db, err := sql.Open("sqlite3", dbPath) + db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{}) if err != nil { return fmt.Errorf("failed to open database: %v", err) } - // Test connection - if err := db.Ping(); err != nil { - return fmt.Errorf("failed to connect to database: %v", err) - } - - // Set the database instance for models models.SetDB(db) return nil } diff --git a/cli/cmd/user.go b/cli/cmd/user.go index d836fe3..1c546bf 100644 --- a/cli/cmd/user.go +++ b/cli/cmd/user.go @@ -128,7 +128,6 @@ func changePassword(args []string) { } func listUsers(args []string) { - // Initialize database if err := initDB(); err != nil { fmt.Printf("Error: %v\n", err) os.Exit(1) diff --git a/cli/go.mod b/cli/go.mod index 64b2941..a61e38e 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -4,16 +4,23 @@ go 1.25.1 require ( github.com/corecollectives/mist v0.0.0 + github.com/mattn/go-sqlite3 v1.14.33 golang.org/x/crypto v0.43.0 golang.org/x/term v0.36.0 + gorm.io/driver/sqlite v1.6.0 + gorm.io/gorm v1.31.1 ) require ( + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mattn/go-sqlite3 v1.14.32 // indirect github.com/rs/zerolog v1.34.0 // indirect golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.33.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/corecollectives/mist => ../server diff --git a/cli/go.sum b/cli/go.sum index 5582f98..f1199d7 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -1,12 +1,18 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= -github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= +github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= @@ -20,3 +26,13 @@ golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= +gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= +gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= diff --git a/dash/src/components/applications/environment-variables.tsx b/dash/src/components/applications/environment-variables.tsx index 9d55af0..9ec1870 100644 --- a/dash/src/components/applications/environment-variables.tsx +++ b/dash/src/components/applications/environment-variables.tsx @@ -334,7 +334,7 @@ export const EnvironmentVariables = ({ appId }: EnvironmentVariablesProps) => {

) : (
- {envVars.map((env) => ( + {envVars.map((env) => env ? (
{editingId === env.id ? (
@@ -394,7 +394,7 @@ export const EnvironmentVariables = ({ appId }: EnvironmentVariablesProps) => {
)}
- ))} + ) : null)}
)} diff --git a/dash/src/components/deployments/deployment-list.tsx b/dash/src/components/deployments/deployment-list.tsx index 6e9639f..b44f58d 100644 --- a/dash/src/components/deployments/deployment-list.tsx +++ b/dash/src/components/deployments/deployment-list.tsx @@ -5,13 +5,15 @@ import { Button } from "@/components/ui/button" import { toast } from "sonner" import { DeploymentMonitor } from "@/components/deployments" import type { Deployment, App } from "@/types" -import { Loader2, Clock, CheckCircle2, XCircle, PlayCircle, AlertCircle } from "lucide-react" +import { Loader2, Clock, CheckCircle2, XCircle, PlayCircle, AlertCircle, Square, SquareSlash } from "lucide-react" import { deploymentsService } from "@/services" +import { cn } from "@/lib/utils" export const DeploymentsTab = ({ appId, app }: { appId: number; app?: App }) => { const [deployments, setDeployments] = useState([]) const [loading, setLoading] = useState(true) const [deploying, setDeploying] = useState(false) + const [stoppingIds, setStoppingIds] = useState>(new Set()) const [selectedDeployment, setSelectedDeployment] = useState(null) const fetchDeployments = async () => { @@ -51,6 +53,25 @@ export const DeploymentsTab = ({ appId, app }: { appId: number; app?: App }) => fetchDeployments() } + const handleStop = async (deploymentId: number) => { + if (stoppingIds.has(deploymentId)) return + try { + setStoppingIds(prev => new Set(prev).add(deploymentId)) + await deploymentsService.stopDeployment(deploymentId) + toast.success('Deployment stopped') + fetchDeployments() + } catch (error) { + console.error('Stop deployment error:', error) + toast.error(error instanceof Error ? error.message : 'Failed to stop deployment') + } finally { + setStoppingIds(prev => { + const next = new Set(prev) + next.delete(deploymentId) + return next + }) + } + } + useEffect(() => { fetchDeployments() @@ -59,14 +80,14 @@ export const DeploymentsTab = ({ appId, app }: { appId: number; app?: App }) => return () => clearInterval(interval) }, [appId]) - // Helper to get status icon and color + // Helper to get status badge with styles const getStatusBadge = (deployment: Deployment) => { const { status, stage } = deployment switch (status) { case 'success': return ( - + Success @@ -82,23 +103,49 @@ export const DeploymentsTab = ({ appId, app }: { appId: number; app?: App }) => case 'deploying': case 'cloning': return ( - + {stage.charAt(0).toUpperCase() + stage.slice(1)} ) case 'pending': return ( - + Pending ) + case 'stopped': + return ( + + + Stopped + + ) default: return {status} } } + // Check if deployment can be stopped + const canStopDeployment = (deployment: Deployment) => { + return ['pending', 'building', 'deploying', 'cloning'].includes(deployment.status) && deployment.status !== 'stopped' + } + + // Get border class based on status + const getDeploymentBorderClass = (status: string) => { + switch (status) { + case 'success': + return 'border-green-200 dark:border-green-800 bg-green-50/50 dark:bg-green-950/20' + case 'failed': + return 'border-red-200 dark:border-red-800 bg-red-50/50 dark:bg-red-950/20' + case 'stopped': + return 'border-slate-200 dark:border-slate-700 bg-slate-50/50 dark:bg-slate-900/20' + default: + return 'border-border bg-muted/20 hover:bg-muted/30' + } + } + return ( <> {/* Deployment Monitor */} @@ -159,7 +206,10 @@ export const DeploymentsTab = ({ appId, app }: { appId: number; app?: App }) => {deployments.map((d) => (
@@ -170,7 +220,7 @@ export const DeploymentsTab = ({ appId, app }: { appId: number; app?: App }) => {/* Progress indicator for in-progress deployments */} - {d.status !== 'success' && d.status !== 'failed' && d.progress > 0 && ( + {d.status !== 'success' && d.status !== 'failed' && d.status !== 'stopped' && d.progress > 0 && (

)} - {d.error_message && ( + {/* Show error message for failed deployments */} + {d.error_message && d.status === 'failed' && (

{d.error_message}

)} + + {/* Show stopped message for stopped deployments */} + {d.status === 'stopped' && ( +

+ + Deployment was stopped by user +

+ )}
@@ -226,14 +285,32 @@ export const DeploymentsTab = ({ appId, app }: { appId: number; app?: App }) =>
- +
+ {canStopDeployment(d) && ( + + )} + +
))}
diff --git a/dash/src/components/deployments/deployment-monitor.tsx b/dash/src/components/deployments/deployment-monitor.tsx index 3f761fd..40fded0 100644 --- a/dash/src/components/deployments/deployment-monitor.tsx +++ b/dash/src/components/deployments/deployment-monitor.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { Sheet, SheetContent, @@ -6,8 +6,10 @@ import { SheetTitle, } from '@/components/ui/sheet'; import { Badge } from '@/components/ui/badge'; -import { Terminal, CheckCircle2, XCircle, AlertCircle, Loader2 } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Terminal, CheckCircle2, XCircle, AlertCircle, Loader2, Square, CircleSlash, SquareSlash } from 'lucide-react'; import { useDeploymentMonitor } from '@/hooks'; +import { deploymentsService } from '@/services'; import { LogLine } from '@/components/logs/log-line'; import { cn } from '@/lib/utils'; import { toast } from 'sonner'; @@ -23,6 +25,7 @@ export const DeploymentMonitor = ({ deploymentId, open, onClose, onComplete }: P const bottomRef = useRef(null); const completedRef = useRef(false); + const [stopping, setStopping] = useState(false); const { logs, status, error, isConnected, isLoading, isLive, reset } = useDeploymentMonitor({ deploymentId, @@ -52,6 +55,20 @@ export const DeploymentMonitor = ({ deploymentId, open, onClose, onComplete }: P onClose(); }; + const handleStop = async () => { + if (stopping) return; + try { + setStopping(true); + await deploymentsService.stopDeployment(deploymentId); + toast.success('Deployment stopped'); + } catch (err) { + console.error('Stop deployment error:', err); + toast.error(err instanceof Error ? err.message : 'Failed to stop deployment'); + } finally { + setStopping(false); + } + }; + const getStatusInfo = () => { const statusValue = status?.status || 'pending'; @@ -82,15 +99,31 @@ export const DeploymentMonitor = ({ deploymentId, open, onClose, onComplete }: P icon: , label: 'Pending', }; + case 'stopped': + return { + color: 'bg-slate-500 text-white', + icon: , + label: 'Stopped', + }; default: return { - color: 'bg-gray-500 text-white', + color: 'bg-slate-500 text-white', icon: null, label: statusValue, }; } }; + const canStop = () => { + const statusValue = status?.status || 'pending'; + return ['pending', 'building', 'deploying', 'cloning'].includes(statusValue) && statusValue !== 'stopped'; + }; + + const isTerminalStatus = () => { + const statusValue = status?.status || ''; + return ['success', 'failed', 'stopped'].includes(statusValue); + }; + const statusInfo = getStatusInfo(); return ( @@ -127,6 +160,24 @@ export const DeploymentMonitor = ({ deploymentId, open, onClose, onComplete }: P #{deploymentId} + + {/* Stop Button */} + {canStop() && ( + + )}
@@ -143,8 +194,8 @@ export const DeploymentMonitor = ({ deploymentId, open, onClose, onComplete }: P
- {/* Progress Bar */} - {status.status !== 'success' && status.status !== 'failed' && ( + {/* Progress Bar - Hide for terminal statuses */} + {!isTerminalStatus() && (
)} - {/* Error Banner - Only show for live deployments */} - {error && isLive && ( + {/* Stopped Banner */} + {status?.status === 'stopped' && ( +
+ + + Deployment Stopped + + {status.duration && ( + + Stopped at {status.progress}% after {status.duration}s + + )} +
+ )} + + {/* Error Banner - Only show for live deployments with failed status */} + {error && isLive && status?.status === 'failed' && (
diff --git a/dash/src/hooks/use-deployment-monitor.ts b/dash/src/hooks/use-deployment-monitor.ts index a74a2f6..78b687a 100644 --- a/dash/src/hooks/use-deployment-monitor.ts +++ b/dash/src/hooks/use-deployment-monitor.ts @@ -219,6 +219,8 @@ export const useDeploymentMonitor = ({ progress: deployment.progress, message: deployment.status === 'success' ? 'Deployment completed successfully' + : deployment.status === 'stopped' + ? 'Deployment was stopped by user' : 'Deployment failed', error_message: deployment.error_message, duration: deployment.duration, @@ -226,6 +228,8 @@ export const useDeploymentMonitor = ({ if (deployment.status === 'failed' && deployment.error_message) { setError(deployment.error_message); + } else if (deployment.status === 'stopped') { + setError('Deployment was stopped by user'); } setIsLoading(false); @@ -302,6 +306,13 @@ export const useDeploymentMonitor = ({ setError(statusData.error_message); onError?.(statusData.error_message); } + + // Handle stopped status + if (statusData.status === 'stopped' && !hasCompletedRef.current) { + console.log('[DeploymentMonitor] Deployment stopped by user'); + hasCompletedRef.current = true; + setError('Deployment was stopped by user'); + } break; } @@ -335,8 +346,8 @@ export const useDeploymentMonitor = ({ // Check the current status from state to decide on reconnection setStatus((currentStatus) => { - // Don't reconnect if deployment is complete - if (currentStatus?.status === 'success' || currentStatus?.status === 'failed') { + // Don't reconnect if deployment is complete (success, failed, or stopped) + if (currentStatus?.status === 'success' || currentStatus?.status === 'failed' || currentStatus?.status === 'stopped') { console.log('[DeploymentMonitor] Deployment complete, not reconnecting'); return currentStatus; } diff --git a/dash/src/hooks/use-environment-variables.ts b/dash/src/hooks/use-environment-variables.ts index d0fda04..2f56f1a 100644 --- a/dash/src/hooks/use-environment-variables.ts +++ b/dash/src/hooks/use-environment-variables.ts @@ -30,8 +30,9 @@ export const useEnvironmentVariables = (options: UseEnvironmentVariablesOptions) try { setLoading(true); setError(null); - const data = await applicationsService.getEnvVariables(appId); - setEnvVars(data); + const data: Array = await applicationsService.getEnvVariables(appId); + // Filter out any null/undefined entries + setEnvVars(data.filter((env): env is EnvVariable => env != null)); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to fetch environment variables'; setError(errorMessage); diff --git a/dash/src/pages/Settings.tsx b/dash/src/pages/Settings.tsx index 760e54d..3487bb6 100644 --- a/dash/src/pages/Settings.tsx +++ b/dash/src/pages/Settings.tsx @@ -80,7 +80,7 @@ export const SettingsPage = () => { try { const settings = await settingsService.updateSystemSettings({ - wildcardDomain: wildcardDomain.trim() || null, + wildcardDomain: wildcardDomain.trim() || '', mistAppName: mistAppName.trim(), }); setWildcardDomain(settings.wildcardDomain || ''); diff --git a/dash/src/services/deployments.service.ts b/dash/src/services/deployments.service.ts index 66c1608..426c494 100644 --- a/dash/src/services/deployments.service.ts +++ b/dash/src/services/deployments.service.ts @@ -68,4 +68,21 @@ export const deploymentsService = { const host = window.location.host; return `${protocol}//${host}${API_BASE}/deployments/logs/stream?id=${deploymentId}`; }, + + /** + * Stop a deployment + */ + async stopDeployment(deploymentId: number): Promise { + const response = await fetch(`${API_BASE}/deployments/stopDep`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + credentials: 'include', + body: JSON.stringify({ deploymentId }), + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message || data.error || 'Failed to stop deployment'); + } + }, }; diff --git a/install-local.sh b/install-local.sh index ba1ce62..b140631 100755 --- a/install-local.sh +++ b/install-local.sh @@ -216,6 +216,36 @@ run_step "Building backend" "cd '$INSTALL_DIR/$GO_BACKEND_DIR' && go build -v -o chmod +x "$GO_BINARY_NAME" log "Build complete" +# ---------------- Dashboard Build ---------------- + +if [ -d "$INSTALL_DIR/dash" ]; then + if ! command -v node >/dev/null 2>&1; then + error "Node.js not found. Install from: https://nodejs.org/" + exit 1 + fi + if ! command -v npm >/dev/null 2>&1; then + error "npm not found" + exit 1 + fi + + run_step "Installing dashboard dependencies" "cd '$INSTALL_DIR/dash' && npm install" || exit 1 + run_step "Building dashboard" "cd '$INSTALL_DIR/dash' && npm run build" || exit 1 + + # Move build output to server/static + DASH_BUILD_DIR="$INSTALL_DIR/dash/dist" + STATIC_DIR="$INSTALL_DIR/server/static" + + if [ -d "$DASH_BUILD_DIR" ]; then + run_step "Moving dashboard build to static folder" "sudo rm -rf '$STATIC_DIR' && sudo mv '$DASH_BUILD_DIR' '$STATIC_DIR'" || exit 1 + log "Dashboard built and deployed to server/static" + else + error "Dashboard build output not found" + exit 1 + fi +else + warn "Dashboard directory not found, skipping dashboard build" +fi + # ---------------- CLI Tool ---------------- if [ -d "$INSTALL_DIR/cli" ]; then diff --git a/install.sh b/install.sh index 91a6b79..0827f5b 100755 --- a/install.sh +++ b/install.sh @@ -9,7 +9,7 @@ sudo rm -f "$LOG_FILE" 2>/dev/null || true REAL_USER="${SUDO_USER:-$USER}" REAL_HOME="$(getent passwd "$REAL_USER" | cut -d: -f6)" -REPO="https://github.com/corecollectives/mist" +REPO="https://github.com/trymist/mist" BRANCH="release" APP_NAME="mist" INSTALL_DIR="/opt/mist" diff --git a/server/api/RegisterRoutes.go b/server/api/RegisterRoutes.go index a3ab4f4..c4a5d22 100644 --- a/server/api/RegisterRoutes.go +++ b/server/api/RegisterRoutes.go @@ -96,6 +96,7 @@ func RegisterRoutes(mux *http.ServeMux) { mux.Handle("POST /api/deployments/create", middleware.AuthMiddleware()(http.HandlerFunc(deployments.AddDeployHandler))) mux.Handle("POST /api/deployments/getByAppId", middleware.AuthMiddleware()(http.HandlerFunc(deployments.GetByApplicationID))) mux.Handle("GET /api/deployments/logs", middleware.AuthMiddleware()(http.HandlerFunc(deployments.GetCompletedDeploymentLogsHandler))) + mux.Handle("POST /api/deployments/stopDep", middleware.AuthMiddleware()(http.HandlerFunc(deployments.StopDeployment))) mux.Handle("GET /api/templates/list", middleware.AuthMiddleware()(http.HandlerFunc(templates.ListServiceTemplates))) mux.Handle("GET /api/templates/get", middleware.AuthMiddleware()(http.HandlerFunc(templates.GetServiceTemplateByName))) diff --git a/server/api/handlers/applications/envs.go b/server/api/handlers/applications/envs.go index 35ffb3b..c2d006d 100644 --- a/server/api/handlers/applications/envs.go +++ b/server/api/handlers/applications/envs.go @@ -142,12 +142,18 @@ func UpdateEnvVariable(w http.ResponseWriter, r *http.Request) { return } + updatedEnv, err := models.GetEnvVariableByID(req.ID) + if err != nil { + handlers.SendResponse(w, http.StatusInternalServerError, false, nil, "Failed to fetch updated environment variable", err.Error()) + return + } + models.LogUserAudit(userInfo.ID, "update", "env_variable", &req.ID, map[string]interface{}{ "app_id": env.AppID, "key": req.Key, }) - handlers.SendResponse(w, http.StatusOK, true, nil, "Environment variable updated successfully", "") + handlers.SendResponse(w, http.StatusOK, true, updatedEnv, "Environment variable updated successfully", "") } func DeleteEnvVariable(w http.ResponseWriter, r *http.Request) { diff --git a/server/api/handlers/applications/preview.go b/server/api/handlers/applications/preview.go index 4640afd..7d3770f 100644 --- a/server/api/handlers/applications/preview.go +++ b/server/api/handlers/applications/preview.go @@ -1,8 +1,8 @@ package applications import ( - "database/sql" "encoding/json" + "errors" "fmt" "net" "net/http" @@ -10,6 +10,7 @@ import ( "github.com/corecollectives/mist/api/handlers" "github.com/corecollectives/mist/api/middleware" "github.com/corecollectives/mist/models" + "gorm.io/gorm" ) func getOutboundIP() string { @@ -56,7 +57,7 @@ func GetPreviewURL(w http.ResponseWriter, r *http.Request) { domain, err := models.GetPrimaryDomainByAppID(req.AppID) if err != nil { - if err == sql.ErrNoRows || err.Error() == "sql: no rows in result set" { + if errors.Is(err, gorm.ErrRecordNotFound) { app, appErr := models.GetApplicationByID(req.AppID) if appErr != nil { handlers.SendResponse(w, http.StatusInternalServerError, false, nil, "Failed to get application", appErr.Error()) diff --git a/server/api/handlers/auth/login.go b/server/api/handlers/auth/login.go index 4b6729d..b0b91dd 100644 --- a/server/api/handlers/auth/login.go +++ b/server/api/handlers/auth/login.go @@ -1,8 +1,8 @@ package auth import ( - "database/sql" "encoding/json" + "errors" "net/http" "strings" @@ -10,6 +10,7 @@ import ( "github.com/corecollectives/mist/api/middleware" "github.com/corecollectives/mist/models" "github.com/rs/zerolog/log" + "gorm.io/gorm" ) func LoginHandler(w http.ResponseWriter, r *http.Request) { @@ -28,7 +29,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) { return } user, err := models.GetUserByEmail(cred.Email) - if err == sql.ErrNoRows { + if errors.Is(err, gorm.ErrRecordNotFound) { handlers.SendResponse(w, http.StatusUnauthorized, false, nil, "Invalid email or password", "Unauthorized") return } else if err != nil { diff --git a/server/api/handlers/auth/me.go b/server/api/handlers/auth/me.go index 68bd7fb..270f5ac 100644 --- a/server/api/handlers/auth/me.go +++ b/server/api/handlers/auth/me.go @@ -1,7 +1,6 @@ package auth import ( - "database/sql" "errors" "net/http" @@ -9,6 +8,7 @@ import ( "github.com/corecollectives/mist/api/middleware" "github.com/corecollectives/mist/models" "github.com/corecollectives/mist/store" + "gorm.io/gorm" ) func MeHandler(w http.ResponseWriter, r *http.Request) { @@ -29,7 +29,7 @@ func MeHandler(w http.ResponseWriter, r *http.Request) { userId := claims.UserID user, err := models.GetUserByID(userId) - if errors.Is(err, sql.ErrNoRows) { + if errors.Is(err, gorm.ErrRecordNotFound) { handlers.SendResponse(w, http.StatusOK, true, map[string]interface{}{"setupRequired": setupRequired, "user": nil}, "User not found", "") return } else if err != nil { diff --git a/server/api/handlers/deployments/getCompletedLogs.go b/server/api/handlers/deployments/getCompletedLogs.go index e843acf..745965b 100644 --- a/server/api/handlers/deployments/getCompletedLogs.go +++ b/server/api/handlers/deployments/getCompletedLogs.go @@ -68,7 +68,7 @@ func GetCompletedDeploymentLogsHandler(w http.ResponseWriter, r *http.Request) { return } - if dep.Status != "success" && dep.Status != "failed" { + if dep.Status != "success" && dep.Status != "failed" && dep.Status != "stopped" { handlers.SendResponse(w, http.StatusBadRequest, false, nil, "deployment is still in progress, use WebSocket endpoint", "") return } diff --git a/server/api/handlers/deployments/stopDeployment.go b/server/api/handlers/deployments/stopDeployment.go new file mode 100644 index 0000000..d310e47 --- /dev/null +++ b/server/api/handlers/deployments/stopDeployment.go @@ -0,0 +1,87 @@ +package deployments + +import ( + "encoding/json" + "net/http" + "os" + "path/filepath" + "strconv" + + "github.com/corecollectives/mist/api/handlers" + "github.com/corecollectives/mist/api/middleware" + "github.com/corecollectives/mist/constants" + "github.com/corecollectives/mist/models" + "github.com/corecollectives/mist/queue" + "github.com/rs/zerolog/log" +) + +type stopDeployment struct { + DeploymentID int64 `json:"deploymentId"` +} + +func StopDeployment(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + handlers.SendResponse(w, http.StatusMethodNotAllowed, false, nil, "Method not allowed", "Only POST method is allowed") + return + } + + userInfo, ok := middleware.GetUser(r) + if !ok { + handlers.SendResponse(w, http.StatusUnauthorized, false, nil, "Not logged in", "Unauthorized") + return + } + + var req stopDeployment + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + handlers.SendResponse(w, http.StatusBadRequest, false, nil, "Invalid request body", "Could not parse JSON") + return + } + + deployment, err := models.GetDeploymentByID(req.DeploymentID) + if err != nil { + log.Error().Err(err).Int64("deployment_id", req.DeploymentID).Msg("Failed to get deployment details") + handlers.SendResponse(w, http.StatusInternalServerError, false, nil, "Failed to get deployment details", err.Error()) + return + } + + logPath := filepath.Join(constants.Constants["LogPath"].(string), deployment.CommitHash+strconv.FormatInt(req.DeploymentID, 10)+"_build_logs") + + file, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Error().Err(err).Int64("deployment_id", req.DeploymentID).Str("log_path", logPath).Msg("Failed to open log file") + handlers.SendResponse(w, http.StatusInternalServerError, false, nil, "Failed to open log file", err.Error()) + return + } + defer file.Close() + + logLine := "deployment was stopped by user\n" + _, err = file.WriteString(logLine) + if err != nil { + log.Error().Err(err).Int64("deployment_id", req.DeploymentID).Str("log_path", logPath).Msg("Failed to write to log file") + handlers.SendResponse(w, http.StatusInternalServerError, false, nil, "Failed to write to log file", err.Error()) + return + } + + wasRunning := queue.Cancel(req.DeploymentID) + message := "Deployment marked as stopped" + if wasRunning { + message = "Deployment aborted immediately" + } + + errorMsg := "deployment stopped by user" + err = models.UpdateDeploymentStatus(req.DeploymentID, "stopped", "stopped", deployment.Progress, &errorMsg) + if err != nil { + log.Error().Err(err).Int64("deployment_id", req.DeploymentID).Msg("Failed to update deployment status") + handlers.SendResponse(w, http.StatusInternalServerError, false, nil, "Failed to stop deployment", err.Error()) + return + } + + models.LogUserAudit(userInfo.ID, "stop", "deployment", &req.DeploymentID, map[string]interface{}{ + "app_id": deployment.AppID, + "commit_hash": deployment.CommitHash, + "was_running": wasRunning, + }) + + log.Info().Int64("deployment_id", req.DeploymentID).Int64("user_id", userInfo.ID).Bool("was_running", wasRunning).Msg("Deployment stopped successfully") + handlers.SendResponse(w, http.StatusOK, true, nil, message, "") +} diff --git a/server/api/handlers/github/repositories.go b/server/api/handlers/github/repositories.go index fc8f2ed..698cc17 100644 --- a/server/api/handlers/github/repositories.go +++ b/server/api/handlers/github/repositories.go @@ -1,8 +1,8 @@ package github import ( - "database/sql" "encoding/json" + "errors" "fmt" "net/http" "time" @@ -11,6 +11,7 @@ import ( "github.com/corecollectives/mist/api/middleware" "github.com/corecollectives/mist/github" "github.com/corecollectives/mist/models" + "gorm.io/gorm" ) type RepoListResponse struct { @@ -26,7 +27,7 @@ func GetRepositories(w http.ResponseWriter, r *http.Request) { } installationID, err := models.GetInstallationID(int(userData.ID)) - if err == sql.ErrNoRows { + if errors.Is(err, gorm.ErrRecordNotFound) { handlers.SendResponse(w, http.StatusNotFound, false, nil, "no installation found for user", "No installation found") return } diff --git a/server/api/handlers/projects/createProject.go b/server/api/handlers/projects/createProject.go index d80ce04..2ab25a4 100644 --- a/server/api/handlers/projects/createProject.go +++ b/server/api/handlers/projects/createProject.go @@ -1,7 +1,6 @@ package projects import ( - "database/sql" "encoding/json" "net/http" @@ -42,12 +41,13 @@ func CreateProject(w http.ResponseWriter, r *http.Request) { OwnerID: userData.ID, } if input.Description != "" { - project.Description = sql.NullString{String: input.Description, Valid: true} + desc := input.Description + project.Description = &desc + } else { + project.Description = nil } if input.Tags != nil { - for _, tag := range input.Tags { - project.Tags = append(project.Tags, sql.NullString{String: tag, Valid: true}) - } + project.Tags = input.Tags } err := project.InsertInDB() diff --git a/server/api/handlers/projects/deleteProject.go b/server/api/handlers/projects/deleteProject.go index e496859..1864a8f 100644 --- a/server/api/handlers/projects/deleteProject.go +++ b/server/api/handlers/projects/deleteProject.go @@ -1,13 +1,14 @@ package projects import ( - "database/sql" + "errors" "net/http" "strconv" "github.com/corecollectives/mist/api/handlers" "github.com/corecollectives/mist/api/middleware" "github.com/corecollectives/mist/models" + "gorm.io/gorm" ) func DeleteProject(w http.ResponseWriter, r *http.Request) { @@ -29,7 +30,7 @@ func DeleteProject(w http.ResponseWriter, r *http.Request) { } project, err := models.GetProjectByID(projectId) - if err == sql.ErrNoRows { + if errors.Is(err, gorm.ErrRecordNotFound) { handlers.SendResponse(w, http.StatusNotFound, false, nil, "Project not found", "no project with that ID") return } else if err != nil { diff --git a/server/api/handlers/projects/getProjectFromId.go b/server/api/handlers/projects/getProjectFromId.go index e60ba68..6c9b024 100644 --- a/server/api/handlers/projects/getProjectFromId.go +++ b/server/api/handlers/projects/getProjectFromId.go @@ -1,13 +1,14 @@ package projects import ( - "database/sql" + "errors" "net/http" "strconv" "github.com/corecollectives/mist/api/handlers" "github.com/corecollectives/mist/api/middleware" "github.com/corecollectives/mist/models" + "gorm.io/gorm" ) func GetProjectFromId(w http.ResponseWriter, r *http.Request) { @@ -31,7 +32,7 @@ func GetProjectFromId(w http.ResponseWriter, r *http.Request) { } project, err := models.GetProjectByID(projectId) - if err == sql.ErrNoRows { + if errors.Is(err, gorm.ErrRecordNotFound) { handlers.SendResponse(w, http.StatusNotFound, false, nil, "Project not found", "no project with that ID") return } else if err != nil { diff --git a/server/api/handlers/projects/updateMembers.go b/server/api/handlers/projects/updateMembers.go index 149c4d6..df865cd 100644 --- a/server/api/handlers/projects/updateMembers.go +++ b/server/api/handlers/projects/updateMembers.go @@ -1,14 +1,15 @@ package projects import ( - "database/sql" "encoding/json" + "errors" "net/http" "strconv" "github.com/corecollectives/mist/api/handlers" "github.com/corecollectives/mist/api/middleware" "github.com/corecollectives/mist/models" + "gorm.io/gorm" ) func UpdateMembers(w http.ResponseWriter, r *http.Request) { @@ -38,7 +39,7 @@ func UpdateMembers(w http.ResponseWriter, r *http.Request) { } project, err := models.GetProjectByID(projectId) - if err == sql.ErrNoRows { + if errors.Is(err, gorm.ErrRecordNotFound) { handlers.SendResponse(w, http.StatusNotFound, false, nil, "Project not found", "no such project") return } else if err != nil { diff --git a/server/api/handlers/projects/updateProject.go b/server/api/handlers/projects/updateProject.go index efe1a53..71974e3 100644 --- a/server/api/handlers/projects/updateProject.go +++ b/server/api/handlers/projects/updateProject.go @@ -1,14 +1,15 @@ package projects import ( - "database/sql" "encoding/json" + "errors" "net/http" "strconv" "github.com/corecollectives/mist/api/handlers" "github.com/corecollectives/mist/api/middleware" "github.com/corecollectives/mist/models" + "gorm.io/gorm" ) func UpdateProject(w http.ResponseWriter, r *http.Request) { @@ -44,7 +45,7 @@ func UpdateProject(w http.ResponseWriter, r *http.Request) { } existingProject, err := models.GetProjectByID(projectId) - if err == sql.ErrNoRows { + if errors.Is(err, gorm.ErrRecordNotFound) { handlers.SendResponse(w, http.StatusNotFound, false, nil, "Project not found", "no such project") return } else if err != nil { @@ -57,19 +58,23 @@ func UpdateProject(w http.ResponseWriter, r *http.Request) { return } - tags := make([]sql.NullString, len(input.Tags)) - for i, tag := range input.Tags { - tags[i] = sql.NullString{ - String: tag, - Valid: true, - } + // tags := make([]string, len(input.Tags)) + // for i, tag := range input.Tags { + // tags[i] = tag + // } + var desc *string + if input.Description != "" { + desc = &input.Description + + } else { + desc = nil } project := &models.Project{ ID: projectId, Name: input.Name, - Description: sql.NullString{String: input.Description, Valid: input.Description != ""}, - Tags: tags, + Description: desc, + Tags: input.Tags, } err = models.UpdateProject(project) diff --git a/server/api/handlers/updates/update.go b/server/api/handlers/updates/update.go index 3e84aa2..8cce9dd 100644 --- a/server/api/handlers/updates/update.go +++ b/server/api/handlers/updates/update.go @@ -270,13 +270,16 @@ func ClearStuckUpdate(w http.ResponseWriter, r *http.Request) { // Only allow clearing in_progress updates if updateLog.Status != "in_progress" { - handlers.SendResponse(w, http.StatusBadRequest, false, nil, "Can only clear in_progress updates", "Current status: "+updateLog.Status) + handlers.SendResponse(w, http.StatusBadRequest, false, nil, "Can only clear in_progress updates", "Current status: "+string(updateLog.Status)) return } - + currentLogs := "" + if updateLog.Logs != nil { + currentLogs = *updateLog.Logs + } // Mark as failed with note that it was manually cleared errMsg := "Update was manually cleared by administrator" - clearLog := updateLog.Logs + "\n⚠️ " + errMsg + "\n" + clearLog := currentLogs + "\n⚠️ " + errMsg + "\n" err = models.UpdateUpdateLogStatus(id, "failed", clearLog, &errMsg) if err != nil { log.Error().Err(err).Int64("update_log_id", id).Msg("Failed to clear stuck update") diff --git a/server/api/handlers/users/deleteUser.go b/server/api/handlers/users/deleteUser.go index 4684de0..a3e6e32 100644 --- a/server/api/handlers/users/deleteUser.go +++ b/server/api/handlers/users/deleteUser.go @@ -1,7 +1,7 @@ package users import ( - "database/sql" + "errors" "net/http" "strconv" @@ -9,6 +9,7 @@ import ( "github.com/corecollectives/mist/api/middleware" "github.com/corecollectives/mist/models" "github.com/corecollectives/mist/store" + "gorm.io/gorm" ) func DeleteUser(w http.ResponseWriter, r *http.Request) { @@ -43,7 +44,7 @@ func DeleteUser(w http.ResponseWriter, r *http.Request) { return } userToDeleteRole, err := models.GetUserRole(int64(id)) - if err == sql.ErrNoRows { + if errors.Is(err, gorm.ErrRecordNotFound) { handlers.SendResponse(w, http.StatusNotFound, false, nil, "User not found", "No such user") return } else if err != nil { diff --git a/server/api/handlers/users/getUserById.go b/server/api/handlers/users/getUserById.go index d8ce507..37173d7 100644 --- a/server/api/handlers/users/getUserById.go +++ b/server/api/handlers/users/getUserById.go @@ -1,13 +1,14 @@ package users import ( - "database/sql" + "errors" "net/http" "strconv" "github.com/corecollectives/mist/api/handlers" "github.com/corecollectives/mist/api/middleware" "github.com/corecollectives/mist/models" + "gorm.io/gorm" ) func GetUserById(w http.ResponseWriter, r *http.Request) { @@ -30,7 +31,7 @@ func GetUserById(w http.ResponseWriter, r *http.Request) { } user, err := models.GetUserByID(userID) if err != nil { - if err == sql.ErrNoRows { + if errors.Is(err, gorm.ErrRecordNotFound) { handlers.SendResponse(w, http.StatusNotFound, false, nil, "User not found", "No user exists with the given ID") return } diff --git a/server/db/main.go b/server/db/main.go index 4e66b46..8ff8189 100644 --- a/server/db/main.go +++ b/server/db/main.go @@ -1,16 +1,18 @@ package db import ( - "database/sql" "fmt" "os" "path/filepath" "github.com/corecollectives/mist/fs" _ "github.com/mattn/go-sqlite3" + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "gorm.io/gorm/logger" ) -func InitDB() (*sql.DB, error) { +func InitDB() (*gorm.DB, error) { dbPath := "" if os.Getenv("ENV") == "dev" { dbPath = "./mist.db" @@ -22,12 +24,18 @@ func InitDB() (*sql.DB, error) { if err != nil { return nil, fmt.Errorf("failed to create database directory: %v", err) } - db, err := sql.Open("sqlite3", dbPath) + db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{ + Logger: logger.Discard, + }) if err != nil { return nil, fmt.Errorf("failed to open database: %v", err) } - if err := runMigrations(db); err != nil { - return nil, fmt.Errorf("failed to run migrations: %v", err) + err = MigrateDB(db) + if err != nil { + return nil, err } + // if err := runMigrations(db); err != nil { + // return nil, fmt.Errorf("failed to run migrations: %v", err) + // } return db, nil } diff --git a/server/db/migrations.go b/server/db/migrations.go index c84e15f..89557aa 100644 --- a/server/db/migrations.go +++ b/server/db/migrations.go @@ -1,81 +1,155 @@ package db import ( - "database/sql" "fmt" - "os" - "path/filepath" - "sort" + "reflect" + "strings" + + "github.com/corecollectives/mist/models" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" ) -func runMigrations(db *sql.DB) error { - _, err := db.Exec(` - CREATE TABLE IF NOT EXISTS schema_migrations ( - version TEXT PRIMARY KEY - ); - `) - if err != nil { - return fmt.Errorf("failed to create schema_migrations table: %w", err) - } +func migrateExistingTable(db *gorm.DB, model interface{}) error { + migrator := db.Migrator() - rows, err := db.Query("SELECT version from schema_migrations") - if err != nil { - return fmt.Errorf("failed to query applied migrations: %w", err) + stmt := &gorm.Statement{DB: db} + if err := stmt.Parse(model); err != nil { + return fmt.Errorf("failed to parse model: %w", err) } - appliedMigrations := make(map[string]bool) - for rows.Next() { - var version string - if err := rows.Scan(&version); err != nil { - return fmt.Errorf("failed to scan applied migration version: %w", err) + tableName := stmt.Schema.Table + + for _, field := range stmt.Schema.Fields { + if field.DBName == "" { + continue } - appliedMigrations[version] = true - } - migrationsDir := "db/migrations" - files, err := os.ReadDir(migrationsDir) - if err != nil { - return fmt.Errorf("failed to read migrations directory '%s': %w", migrationsDir, err) - } + if !migrator.HasColumn(model, field.DBName) { + columnType := getSQLiteColumnType(field) + if columnType == "" { + continue + } - sort.Slice(files, func(i, j int) bool { - return files[i].Name() < files[j].Name() - }) + sql := fmt.Sprintf("ALTER TABLE `%s` ADD COLUMN `%s` %s", tableName, field.DBName, columnType) - for _, file := range files { - filename := file.Name() - if filepath.Ext(filename) != ".sql" { - continue - } - if appliedMigrations[filename] { - continue - } + if field.HasDefaultValue && field.DefaultValue != "" { + sql += fmt.Sprintf(" DEFAULT %s", field.DefaultValue) + } - filepath := filepath.Join(migrationsDir, filename) - content, err := os.ReadFile(filepath) - if err != nil { - return fmt.Errorf("failed to read migration file '%s': %w", filepath, err) + if err := db.Exec(sql).Error; err != nil { + fmt.Printf("migration.go: warning adding column %s.%s: %v\n", tableName, field.DBName, err) + } } + } - tx, err := db.Begin() - if err != nil { - return fmt.Errorf("failed to begin transaction for migration '%s': %w", filename, err) - } + return nil +} - if _, err := tx.Exec(string(content)); err != nil { - tx.Rollback() - return fmt.Errorf("failed to execute migration '%s': %w", filename, err) +func getSQLiteColumnType(field *schema.Field) string { + switch field.DataType { + case schema.Bool: + return "BOOLEAN" + case schema.Int, schema.Uint: + return "INTEGER" + case schema.Float: + return "REAL" + case schema.String: + return "TEXT" + case schema.Time: + return "DATETIME" + case schema.Bytes: + return "BLOB" + default: + kind := field.FieldType.Kind() + if kind == reflect.Ptr { + kind = field.FieldType.Elem().Kind() } - - if _, err := tx.Exec("INSERT INTO schema_migrations (version) VALUES (?)", filename); err != nil { - tx.Rollback() - return fmt.Errorf("failed to record migration '%s': %w", filename, err) + switch kind { + case reflect.Bool: + return "BOOLEAN" + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return "INTEGER" + case reflect.Float32, reflect.Float64: + return "REAL" + case reflect.String: + return "TEXT" + case reflect.Struct: + typeName := field.FieldType.String() + if strings.Contains(typeName, "Time") || strings.Contains(typeName, "DeletedAt") { + return "DATETIME" + } + return "TEXT" + default: + return "TEXT" } + } +} - if err := tx.Commit(); err != nil { - return fmt.Errorf("failed to commit transaction for migration '%s': %w", filename, err) +func MigrateDB(dbInstance *gorm.DB) error { + return migrateDbInternal(dbInstance) +} + +func migrateDbInternal(dbInstance *gorm.DB) error { + migrator := dbInstance.Migrator() + + allModels := []interface{}{ + &models.User{}, + &models.ApiToken{}, + &models.App{}, + &models.AuditLog{}, + &models.Backup{}, + &models.Deployment{}, + &models.EnvVariable{}, + &models.GithubApp{}, + &models.Project{}, + &models.ProjectMember{}, + &models.GitProvider{}, + &models.GithubInstallation{}, + &models.AppRepositories{}, + &models.Domain{}, + &models.Volume{}, + &models.Cron{}, + &models.Registry{}, + &models.SystemSettingEntry{}, + &models.Logs{}, + &models.ServiceTemplate{}, + &models.Session{}, + &models.Notification{}, + &models.UpdateLog{}, + } + + for _, model := range allModels { + if migrator.HasTable(model) { + if err := migrateExistingTable(dbInstance, model); err != nil { + fmt.Printf("migration.go: warning migrating existing table: %v\n", err) + } + } else { + if err := dbInstance.AutoMigrate(model); err != nil { + fmt.Printf("migration.go: error creating table: %v\n", err) + return err + } } + } + var wildCardDomain = models.SystemSettingEntry{ + Key: "wildcard_domain", + Value: "", + } + var MistAppName = models.SystemSettingEntry{ + Key: "mist_app_name", + Value: "mist", } + var Version = models.SystemSettingEntry{ + Key: "version", + Value: "1.0.4", + } + + dbInstance.Clauses(clause.Insert{Modifier: "OR IGNORE"}).Create(&wildCardDomain) + dbInstance.Clauses(clause.Insert{Modifier: "OR IGNORE"}).Create(&MistAppName) + dbInstance.Clauses(clause.Insert{Modifier: "OR REPLACE"}).Create(&Version) + return nil } diff --git a/server/db/migrations_archive.go b/server/db/migrations_archive.go new file mode 100644 index 0000000..c84e15f --- /dev/null +++ b/server/db/migrations_archive.go @@ -0,0 +1,81 @@ +package db + +import ( + "database/sql" + "fmt" + "os" + "path/filepath" + "sort" +) + +func runMigrations(db *sql.DB) error { + _, err := db.Exec(` + CREATE TABLE IF NOT EXISTS schema_migrations ( + version TEXT PRIMARY KEY + ); + `) + if err != nil { + return fmt.Errorf("failed to create schema_migrations table: %w", err) + } + + rows, err := db.Query("SELECT version from schema_migrations") + if err != nil { + return fmt.Errorf("failed to query applied migrations: %w", err) + } + + appliedMigrations := make(map[string]bool) + for rows.Next() { + var version string + if err := rows.Scan(&version); err != nil { + return fmt.Errorf("failed to scan applied migration version: %w", err) + } + appliedMigrations[version] = true + } + + migrationsDir := "db/migrations" + files, err := os.ReadDir(migrationsDir) + if err != nil { + return fmt.Errorf("failed to read migrations directory '%s': %w", migrationsDir, err) + } + + sort.Slice(files, func(i, j int) bool { + return files[i].Name() < files[j].Name() + }) + + for _, file := range files { + filename := file.Name() + if filepath.Ext(filename) != ".sql" { + continue + } + if appliedMigrations[filename] { + continue + } + + filepath := filepath.Join(migrationsDir, filename) + content, err := os.ReadFile(filepath) + if err != nil { + return fmt.Errorf("failed to read migration file '%s': %w", filepath, err) + } + + tx, err := db.Begin() + if err != nil { + return fmt.Errorf("failed to begin transaction for migration '%s': %w", filename, err) + } + + if _, err := tx.Exec(string(content)); err != nil { + tx.Rollback() + return fmt.Errorf("failed to execute migration '%s': %w", filename, err) + } + + if _, err := tx.Exec("INSERT INTO schema_migrations (version) VALUES (?)", filename); err != nil { + tx.Rollback() + return fmt.Errorf("failed to record migration '%s': %w", filename, err) + } + + if err := tx.Commit(); err != nil { + return fmt.Errorf("failed to commit transaction for migration '%s': %w", filename, err) + } + + } + return nil +} diff --git a/server/db/migrations/001_Create_User.sql b/server/db/migrations_archive/001_Create_User.sql similarity index 100% rename from server/db/migrations/001_Create_User.sql rename to server/db/migrations_archive/001_Create_User.sql diff --git a/server/db/migrations/002_Create_Projects.sql b/server/db/migrations_archive/002_Create_Projects.sql similarity index 100% rename from server/db/migrations/002_Create_Projects.sql rename to server/db/migrations_archive/002_Create_Projects.sql diff --git a/server/db/migrations/003_Create_Project_members.sql b/server/db/migrations_archive/003_Create_Project_members.sql similarity index 100% rename from server/db/migrations/003_Create_Project_members.sql rename to server/db/migrations_archive/003_Create_Project_members.sql diff --git a/server/db/migrations/004_Create_GitProviders.sql b/server/db/migrations_archive/004_Create_GitProviders.sql similarity index 100% rename from server/db/migrations/004_Create_GitProviders.sql rename to server/db/migrations_archive/004_Create_GitProviders.sql diff --git a/server/db/migrations/005_Create_App.sql b/server/db/migrations_archive/005_Create_App.sql similarity index 100% rename from server/db/migrations/005_Create_App.sql rename to server/db/migrations_archive/005_Create_App.sql diff --git a/server/db/migrations/006_Create_Github_app.sql b/server/db/migrations_archive/006_Create_Github_app.sql similarity index 100% rename from server/db/migrations/006_Create_Github_app.sql rename to server/db/migrations_archive/006_Create_Github_app.sql diff --git a/server/db/migrations/007_Create_Github_installations.sql b/server/db/migrations_archive/007_Create_Github_installations.sql similarity index 100% rename from server/db/migrations/007_Create_Github_installations.sql rename to server/db/migrations_archive/007_Create_Github_installations.sql diff --git a/server/db/migrations/008_Create_App_repositories.sql b/server/db/migrations_archive/008_Create_App_repositories.sql similarity index 100% rename from server/db/migrations/008_Create_App_repositories.sql rename to server/db/migrations_archive/008_Create_App_repositories.sql diff --git a/server/db/migrations/009_Create_deployments.sql b/server/db/migrations_archive/009_Create_deployments.sql similarity index 100% rename from server/db/migrations/009_Create_deployments.sql rename to server/db/migrations_archive/009_Create_deployments.sql diff --git a/server/db/migrations/010_Create_Envs.sql b/server/db/migrations_archive/010_Create_Envs.sql similarity index 100% rename from server/db/migrations/010_Create_Envs.sql rename to server/db/migrations_archive/010_Create_Envs.sql diff --git a/server/db/migrations/011_Create_domains.sql b/server/db/migrations_archive/011_Create_domains.sql similarity index 100% rename from server/db/migrations/011_Create_domains.sql rename to server/db/migrations_archive/011_Create_domains.sql diff --git a/server/db/migrations/012_Create_Volumes.sql b/server/db/migrations_archive/012_Create_Volumes.sql similarity index 100% rename from server/db/migrations/012_Create_Volumes.sql rename to server/db/migrations_archive/012_Create_Volumes.sql diff --git a/server/db/migrations/013_Create_Cron.sql b/server/db/migrations_archive/013_Create_Cron.sql similarity index 100% rename from server/db/migrations/013_Create_Cron.sql rename to server/db/migrations_archive/013_Create_Cron.sql diff --git a/server/db/migrations/014_Create_Registries.sql b/server/db/migrations_archive/014_Create_Registries.sql similarity index 100% rename from server/db/migrations/014_Create_Registries.sql rename to server/db/migrations_archive/014_Create_Registries.sql diff --git a/server/db/migrations/015_Create_System_settings.sql b/server/db/migrations_archive/015_Create_System_settings.sql similarity index 68% rename from server/db/migrations/015_Create_System_settings.sql rename to server/db/migrations_archive/015_Create_System_settings.sql index 3f58c6e..9270aa8 100644 --- a/server/db/migrations/015_Create_System_settings.sql +++ b/server/db/migrations_archive/015_Create_System_settings.sql @@ -1,5 +1,7 @@ -CREATE TABLE IF NOT EXISTS system_settings ( +CREATE TABLE IF NOT EXISTS system_settings_ ( key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); + + diff --git a/server/db/migrations/016_Create_Logs.sql b/server/db/migrations_archive/016_Create_Logs.sql similarity index 100% rename from server/db/migrations/016_Create_Logs.sql rename to server/db/migrations_archive/016_Create_Logs.sql diff --git a/server/db/migrations/017_Create_Audit_logs.sql b/server/db/migrations_archive/017_Create_Audit_logs.sql similarity index 100% rename from server/db/migrations/017_Create_Audit_logs.sql rename to server/db/migrations_archive/017_Create_Audit_logs.sql diff --git a/server/db/migrations/018_Create_Service_templates.sql b/server/db/migrations_archive/018_Create_Service_templates.sql similarity index 100% rename from server/db/migrations/018_Create_Service_templates.sql rename to server/db/migrations_archive/018_Create_Service_templates.sql diff --git a/server/db/migrations/019_Create_Api_tokens.sql b/server/db/migrations_archive/019_Create_Api_tokens.sql similarity index 100% rename from server/db/migrations/019_Create_Api_tokens.sql rename to server/db/migrations_archive/019_Create_Api_tokens.sql diff --git a/server/db/migrations/020_Create_Sessions.sql b/server/db/migrations_archive/020_Create_Sessions.sql similarity index 100% rename from server/db/migrations/020_Create_Sessions.sql rename to server/db/migrations_archive/020_Create_Sessions.sql diff --git a/server/db/migrations/021_Create_Notifications.sql b/server/db/migrations_archive/021_Create_Notifications.sql similarity index 100% rename from server/db/migrations/021_Create_Notifications.sql rename to server/db/migrations_archive/021_Create_Notifications.sql diff --git a/server/db/migrations/022_Create_Backups.sql b/server/db/migrations_archive/022_Create_Backups.sql similarity index 100% rename from server/db/migrations/022_Create_Backups.sql rename to server/db/migrations_archive/022_Create_Backups.sql diff --git a/server/db/migrations/023_Add_DNS_validation.sql b/server/db/migrations_archive/023_Add_DNS_validation.sql similarity index 100% rename from server/db/migrations/023_Add_DNS_validation.sql rename to server/db/migrations_archive/023_Add_DNS_validation.sql diff --git a/server/db/migrations/024_Add_Wildcard_domain_settings.sql b/server/db/migrations_archive/024_Add_Wildcard_domain_settings.sql similarity index 100% rename from server/db/migrations/024_Add_Wildcard_domain_settings.sql rename to server/db/migrations_archive/024_Add_Wildcard_domain_settings.sql diff --git a/server/db/migrations/025_Add_Missing_Indexes.sql b/server/db/migrations_archive/025_Add_Missing_Indexes.sql similarity index 100% rename from server/db/migrations/025_Add_Missing_Indexes.sql rename to server/db/migrations_archive/025_Add_Missing_Indexes.sql diff --git a/server/db/migrations/026_Add_Version_SystemSettings.sql b/server/db/migrations_archive/026_Add_Version_SystemSettings.sql similarity index 100% rename from server/db/migrations/026_Add_Version_SystemSettings.sql rename to server/db/migrations_archive/026_Add_Version_SystemSettings.sql diff --git a/server/db/migrations/027_Create_UpdateLogs.sql b/server/db/migrations_archive/027_Create_UpdateLogs.sql similarity index 100% rename from server/db/migrations/027_Create_UpdateLogs.sql rename to server/db/migrations_archive/027_Create_UpdateLogs.sql diff --git a/server/db/migrations/028_Version_bump_v1.0.1.sql b/server/db/migrations_archive/028_Version_bump_v1.0.1.sql similarity index 100% rename from server/db/migrations/028_Version_bump_v1.0.1.sql rename to server/db/migrations_archive/028_Version_bump_v1.0.1.sql diff --git a/server/db/migrations/029_Version_Bump_v1.0.2.sql b/server/db/migrations_archive/029_Version_Bump_v1.0.2.sql similarity index 100% rename from server/db/migrations/029_Version_Bump_v1.0.2.sql rename to server/db/migrations_archive/029_Version_Bump_v1.0.2.sql diff --git a/server/db/migrations/030_Version_bump_v1.0.3.sql b/server/db/migrations_archive/030_Version_bump_v1.0.3.sql similarity index 100% rename from server/db/migrations/030_Version_bump_v1.0.3.sql rename to server/db/migrations_archive/030_Version_bump_v1.0.3.sql diff --git a/server/docker/container.go b/server/docker/container.go index 5b9a948..1e3f9ed 100644 --- a/server/docker/container.go +++ b/server/docker/container.go @@ -253,9 +253,9 @@ func ContainerExists(name string) bool { // return true } -func RunContainer(app *models.App, imageTag, containerName string, domains []string, Port int, envVars map[string]string, logfile *os.File) error { +func RunContainer(ctx context.Context, app *models.App, imageTag, containerName string, domains []string, Port int, envVars map[string]string, logfile *os.File) error { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() cli, err := client.New(client.FromEnv) @@ -388,7 +388,7 @@ func RunContainer(app *models.App, imageTag, containerName string, domains []str hostConfig.Resources.Memory = int64(*app.MemoryLimit) * 1024 * 1024 } - resp, err := cli.ContainerCreate(ctx, client.ContainerCreateOptions{ + resp, err := cli.ContainerCreate(timeoutCtx, client.ContainerCreateOptions{ Name: containerName, Config: &config, HostConfig: &hostConfig, @@ -397,7 +397,7 @@ func RunContainer(app *models.App, imageTag, containerName string, domains []str return fmt.Errorf("failed to create container: %w", err) } - _, err = cli.ContainerStart(ctx, resp.ID, client.ContainerStartOptions{}) + _, err = cli.ContainerStart(timeoutCtx, resp.ID, client.ContainerStartOptions{}) if err != nil { return fmt.Errorf("failed to start container: %w", err) } @@ -661,7 +661,7 @@ func RecreateContainer(app *models.App) error { return fmt.Errorf("failed to stop/remove container: %w", err) } - if err := RunContainer(app, imageTag, containerName, domains, port, envVars, nil); err != nil { + if err := RunContainer(ctx, app, imageTag, containerName, domains, port, envVars, nil); err != nil { return fmt.Errorf("failed to run container: %w", err) } diff --git a/server/docker/deployer.go b/server/docker/deployer.go index 83ceaeb..d892c8d 100644 --- a/server/docker/deployer.go +++ b/server/docker/deployer.go @@ -1,7 +1,7 @@ package docker import ( - "database/sql" + "context" "encoding/json" "fmt" "os" @@ -12,9 +12,10 @@ import ( "github.com/corecollectives/mist/constants" "github.com/corecollectives/mist/models" "github.com/corecollectives/mist/utils" + "gorm.io/gorm" ) -func DeployApp(dep *models.Deployment, app *models.App, appContextPath, imageTag, containerName string, db *sql.DB, logfile *os.File, logger *utils.DeploymentLogger) error { +func DeployApp(ctx context.Context, dep *models.Deployment, app *models.App, appContextPath, imageTag, containerName string, db *gorm.DB, logfile *os.File, logger *utils.DeploymentLogger) error { logger.Info("Starting deployment process") @@ -94,7 +95,11 @@ func DeployApp(dep *models.Deployment, app *models.App, appContextPath, imageTag "image": imageName, }) - if err := PullDockerImage(imageName, logfile); err != nil { + if err := PullDockerImage(ctx, imageName, logfile); err != nil { + if ctx.Err() == context.Canceled { + logger.Info("Docker image pull canceled") + return ctx.Err() + } logger.Error(err, "Docker image pull failed") dep.Status = "failed" dep.Stage = "failed" @@ -118,7 +123,11 @@ func DeployApp(dep *models.Deployment, app *models.App, appContextPath, imageTag models.UpdateDeploymentStatus(dep.ID, "building", "building", 50, nil) logger.Info("Building Docker image with environment variables") - if err := BuildImage(imageTag, appContextPath, envVars, logfile); err != nil { + if err := BuildImage(ctx, imageTag, appContextPath, envVars, logfile); err != nil { + if ctx.Err() == context.Canceled { + logger.Info("Docker image build canceled") + return ctx.Err() + } logger.Error(err, "Docker image build failed") dep.Status = "failed" dep.Stage = "failed" @@ -143,6 +152,10 @@ func DeployApp(dep *models.Deployment, app *models.App, appContextPath, imageTag logger.Info("Stopping existing container if exists") err = StopRemoveContainer(containerName, logfile) if err != nil { + if ctx.Err() == context.Canceled { + logger.Info("Container stop/remove canceled") + return ctx.Err() + } logger.Error(err, "Failed to stop/remove existing container") dep.Status = "failed" dep.Stage = "failed" @@ -162,7 +175,11 @@ func DeployApp(dep *models.Deployment, app *models.App, appContextPath, imageTag "appType": app.AppType, }) - if err := RunContainer(app, imageTag, containerName, domains, port, envVars, logfile); err != nil { + if err := RunContainer(ctx, app, imageTag, containerName, domains, port, envVars, logfile); err != nil { + if ctx.Err() == context.Canceled { + logger.Info("Container run canceled") + return ctx.Err() + } logger.Error(err, "Failed to run container") dep.Status = "failed" dep.Stage = "failed" @@ -203,45 +220,43 @@ func DeployApp(dep *models.Deployment, app *models.App, appContextPath, imageTag return nil } -func UpdateDeployment(dep *models.Deployment, db *sql.DB) error { - stmt, err := db.Prepare("UPDATE deployments SET status=?, stage=?, progress=?, logs=?, error_message=?, finished_at=? WHERE id=?") - if err != nil { - return err - } - defer stmt.Close() - - _, err = stmt.Exec(dep.Status, dep.Stage, dep.Progress, dep.Logs, dep.ErrorMessage, dep.FinishedAt, dep.ID) - return err +func UpdateDeployment(dep *models.Deployment, db *gorm.DB) error { + return db.Model(dep).Updates(map[string]interface{}{ + "status": dep.Status, + "stage": dep.Stage, + "progress": dep.Progress, + "logs": dep.Logs, + "error_message": dep.ErrorMessage, + "finished_at": dep.FinishedAt, + }).Error } func GetLogsPath(commitHash string, depId int64) string { return filepath.Join(constants.Constants["LogPath"].(string), commitHash+strconv.FormatInt(depId, 10)+"_build_logs") } -func UpdateAppStatus(appID int64, status string, db *sql.DB) error { - query := `UPDATE apps SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?` - _, err := db.Exec(query, status, appID) - return err +func UpdateAppStatus(appID int64, status string, db *gorm.DB) error { + return db.Model(&models.App{ID: appID}).Update("status", status).Error } -func GetDeploymentConfig(deploymentID int64, app *models.App, db *sql.DB) (int, []string, map[string]string, error) { +func GetDeploymentConfig(deploymentID int64, app *models.App, db *gorm.DB) (int, []string, map[string]string, error) { appID, err := models.GetAppIDByDeploymentID(deploymentID) if err != nil { return 0, nil, nil, fmt.Errorf("get app ID failed: %w", err) } - var port *int - err = db.QueryRow("SELECT port FROM apps WHERE id = ?", appID).Scan(&port) + var port int + err = db.Model(&models.App{}).Select("port").Where("id = ?", appID).Scan(&port).Error if err != nil { return 0, nil, nil, fmt.Errorf("get port failed: %w", err) } - if port == nil { - defaultPort := 3000 - port = &defaultPort + + if port == 0 { + port = 3000 } domains, err := models.GetDomainsByAppID(appID) - if err != nil && err != sql.ErrNoRows { + if err != nil { return 0, nil, nil, fmt.Errorf("get domains failed: %w", err) } @@ -251,7 +266,7 @@ func GetDeploymentConfig(deploymentID int64, app *models.App, db *sql.DB) (int, } envs, err := models.GetEnvVariablesByAppID(appID) - if err != nil && err != sql.ErrNoRows { + if err != nil { return 0, nil, nil, fmt.Errorf("get env variables failed: %w", err) } @@ -273,5 +288,284 @@ func GetDeploymentConfig(deploymentID int64, app *models.App, db *sql.DB) (int, envMap[env.Key] = env.Value } - return *port, domainStrings, envMap, nil + return port, domainStrings, envMap, nil } + +// package docker + +// import ( +// "database/sql" +// "encoding/json" +// "fmt" +// "os" +// "path/filepath" +// "strconv" +// "time" + +// "github.com/corecollectives/mist/constants" +// "github.com/corecollectives/mist/models" +// "github.com/corecollectives/mist/utils" +// "gorm.io/gorm" +// ) + +// func DeployApp(dep *models.Deployment, app *models.App, appContextPath, imageTag, containerName string, db *gorm.DB, logfile *os.File, logger *utils.DeploymentLogger) error { + +// logger.Info("Starting deployment process") + +// logger.Info("Getting port, domains, and environment variables") +// port, domains, envVars, err := GetDeploymentConfig(dep.ID, app, db) +// if err != nil { +// logger.Error(err, "Failed to get deployment configuration") +// dep.Status = "failed" +// dep.Stage = "failed" +// dep.Progress = 0 +// errMsg := fmt.Sprintf("Failed to get deployment config: %v", err) +// dep.ErrorMessage = &errMsg +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "failed", "failed", 0, &errMsg) +// return fmt.Errorf("get deployment config failed: %w", err) +// } + +// logger.InfoWithFields("Configuration loaded", map[string]interface{}{ +// "domains": domains, +// "port": port, +// "envVars": len(envVars), +// "appType": app.AppType, +// }) + +// if app.AppType == models.AppTypeDatabase { +// logger.Info("Database app detected - pulling Docker image instead of building") + +// if app.TemplateName == nil || *app.TemplateName == "" { +// logger.Error(nil, "Database app missing template name") +// dep.Status = "failed" +// dep.Stage = "failed" +// dep.Progress = 0 +// errMsg := "Database app requires a template name" +// dep.ErrorMessage = &errMsg +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "failed", "failed", 0, &errMsg) +// return fmt.Errorf("database app missing template") +// } + +// template, err := models.GetServiceTemplateByName(*app.TemplateName) +// if err != nil { +// logger.Error(err, "Failed to get service template") +// dep.Status = "failed" +// dep.Stage = "failed" +// dep.Progress = 0 +// errMsg := fmt.Sprintf("Failed to get template: %v", err) +// dep.ErrorMessage = &errMsg +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "failed", "failed", 0, &errMsg) +// return fmt.Errorf("get template failed: %w", err) +// } + +// if template == nil { +// logger.Error(nil, "Template not found") +// dep.Status = "failed" +// dep.Stage = "failed" +// dep.Progress = 0 +// errMsg := fmt.Sprintf("Template not found: %s", *app.TemplateName) +// dep.ErrorMessage = &errMsg +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "failed", "failed", 0, &errMsg) +// return fmt.Errorf("template not found") +// } + +// dep.Status = "building" +// dep.Stage = "pulling" +// dep.Progress = 50 +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "building", "pulling", 50, nil) + +// imageName := template.DockerImage +// if template.DockerImageVersion != nil && *template.DockerImageVersion != "" { +// imageName = imageName + ":" + *template.DockerImageVersion +// } + +// logger.InfoWithFields("Pulling Docker image", map[string]interface{}{ +// "image": imageName, +// }) + +// if err := PullDockerImage(imageName, logfile); err != nil { +// logger.Error(err, "Docker image pull failed") +// dep.Status = "failed" +// dep.Stage = "failed" +// dep.Progress = 0 +// errMsg := fmt.Sprintf("Pull failed: %v", err) +// dep.ErrorMessage = &errMsg +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "failed", "failed", 0, &errMsg) +// UpdateAppStatus(app.ID, "error", db) +// return fmt.Errorf("pull image failed: %w", err) +// } + +// logger.Info("Docker image pulled successfully") +// imageTag = imageName + +// } else { +// dep.Status = "building" +// dep.Stage = "building" +// dep.Progress = 50 +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "building", "building", 50, nil) + +// logger.Info("Building Docker image with environment variables") +// if err := BuildImage(imageTag, appContextPath, envVars, logfile); err != nil { +// logger.Error(err, "Docker image build failed") +// dep.Status = "failed" +// dep.Stage = "failed" +// dep.Progress = 0 +// errMsg := fmt.Sprintf("Build failed: %v", err) +// dep.ErrorMessage = &errMsg +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "failed", "failed", 0, &errMsg) +// UpdateAppStatus(app.ID, "error", db) +// return fmt.Errorf("build image failed: %w", err) +// } + +// logger.Info("Docker image built successfully") +// } + +// dep.Status = "deploying" +// dep.Stage = "deploying" +// dep.Progress = 80 +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "deploying", "deploying", 80, nil) + +// logger.Info("Stopping existing container if exists") +// err = StopRemoveContainer(containerName, logfile) +// if err != nil { +// logger.Error(err, "Failed to stop/remove existing container") +// dep.Status = "failed" +// dep.Stage = "failed" +// dep.Progress = 0 +// errMsg := fmt.Sprintf("Failed to stop/remove container: %v", err) +// dep.ErrorMessage = &errMsg +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "failed", "failed", 0, &errMsg) +// UpdateAppStatus(app.ID, "error", db) +// return fmt.Errorf("stop/remove container failed: %w", err) +// } + +// logger.InfoWithFields("Running container", map[string]interface{}{ +// "domains": domains, +// "port": port, +// "envVars": len(envVars), +// "appType": app.AppType, +// }) + +// if err := RunContainer(app, imageTag, containerName, domains, port, envVars, logfile); err != nil { +// logger.Error(err, "Failed to run container") +// dep.Status = "failed" +// dep.Stage = "failed" +// dep.Progress = 0 +// errMsg := fmt.Sprintf("Failed to run container: %v", err) +// dep.ErrorMessage = &errMsg +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "failed", "failed", 0, &errMsg) +// UpdateAppStatus(app.ID, "error", db) +// return fmt.Errorf("run container failed: %w", err) +// } + +// dep.Status = "success" +// dep.Stage = "success" +// dep.Progress = 100 +// now := time.Now() +// dep.FinishedAt = &now +// UpdateDeployment(dep, db) +// models.UpdateDeploymentStatus(dep.ID, "success", "success", 100, nil) + +// logger.Info("Updating app status to running") +// err = UpdateAppStatus(app.ID, "running", db) +// if err != nil { +// logger.Error(err, "Failed to update app status (non-fatal)") +// } + +// logger.Info("Cleaning up old Docker images") +// if err := CleanupOldImages(app.ID, 5); err != nil { +// logger.Error(err, "Failed to cleanup old images (non-fatal)") +// } + +// logger.InfoWithFields("Deployment succeeded", map[string]interface{}{ +// "deployment_id": dep.ID, +// "container": containerName, +// "app_status": "running", +// }) + +// return nil +// } + +// func UpdateDeployment(dep *models.Deployment, db *gorm.DB) error { +// stmt, err := db.Prepare("UPDATE deployments SET status=?, stage=?, progress=?, logs=?, error_message=?, finished_at=? WHERE id=?") +// if err != nil { +// return err +// } +// defer stmt.Close() + +// _, err = stmt.Exec(dep.Status, dep.Stage, dep.Progress, dep.Logs, dep.ErrorMessage, dep.FinishedAt, dep.ID) +// return err +// } + +// func GetLogsPath(commitHash string, depId int64) string { +// return filepath.Join(constants.Constants["LogPath"].(string), commitHash+strconv.FormatInt(depId, 10)+"_build_logs") +// } + +// func UpdateAppStatus(appID int64, status string, db *sql.DB) error { +// query := `UPDATE apps SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?` +// _, err := db.Exec(query, status, appID) +// return err +// } + +// func GetDeploymentConfig(deploymentID int64, app *models.App, db *sql.DB) (int, []string, map[string]string, error) { +// appID, err := models.GetAppIDByDeploymentID(deploymentID) +// if err != nil { +// return 0, nil, nil, fmt.Errorf("get app ID failed: %w", err) +// } + +// var port *int +// err = db.QueryRow("SELECT port FROM apps WHERE id = ?", appID).Scan(&port) +// if err != nil { +// return 0, nil, nil, fmt.Errorf("get port failed: %w", err) +// } +// if port == nil { +// defaultPort := 3000 +// port = &defaultPort +// } + +// domains, err := models.GetDomainsByAppID(appID) +// if err != nil && err != sql.ErrNoRows { +// return 0, nil, nil, fmt.Errorf("get domains failed: %w", err) +// } + +// var domainStrings []string +// for _, d := range domains { +// domainStrings = append(domainStrings, d.Domain) +// } + +// envs, err := models.GetEnvVariablesByAppID(appID) +// if err != nil && err != sql.ErrNoRows { +// return 0, nil, nil, fmt.Errorf("get env variables failed: %w", err) +// } + +// envMap := make(map[string]string) + +// if app.AppType == models.AppTypeDatabase && app.TemplateName != nil { +// template, err := models.GetServiceTemplateByName(*app.TemplateName) +// if err == nil && template != nil && template.DefaultEnvVars != nil { +// var defaultEnvs map[string]string +// if err := json.Unmarshal([]byte(*template.DefaultEnvVars), &defaultEnvs); err == nil { +// for k, v := range defaultEnvs { +// envMap[k] = v +// } +// } +// } +// } + +// for _, env := range envs { +// envMap[env.Key] = env.Value +// } + +// return *port, domainStrings, envMap, nil +// } diff --git a/server/docker/deployerMain.go b/server/docker/deployerMain.go index a28a8fe..2a854bb 100644 --- a/server/docker/deployerMain.go +++ b/server/docker/deployerMain.go @@ -1,7 +1,7 @@ package docker import ( - "database/sql" + "context" "fmt" "os" "path/filepath" @@ -9,9 +9,10 @@ import ( "github.com/corecollectives/mist/constants" "github.com/corecollectives/mist/models" "github.com/corecollectives/mist/utils" + "gorm.io/gorm" ) -func DeployerMain(Id int64, db *sql.DB, logFile *os.File, logger *utils.DeploymentLogger) (string, error) { +func DeployerMain(ctx context.Context, Id int64, db *gorm.DB, logFile *os.File, logger *utils.DeploymentLogger) (string, error) { dep, err := LoadDeployment(Id, db) if err != nil { logger.Error(err, "Failed to load deployment") @@ -19,7 +20,9 @@ func DeployerMain(Id int64, db *sql.DB, logFile *os.File, logger *utils.Deployme } var appId int64 - err = db.QueryRow("SELECT app_id FROM deployments WHERE id = ?", Id).Scan(&appId) + // err = db.QueryRow("SELECT app_id FROM deployments WHERE id = ?", Id).Scan(&appId) + err = db.Table("deployments").Select("app_id").Where("id = ?", Id).Take(&appId).Error + if err != nil { logger.Error(err, "Failed to get app_id") return "", fmt.Errorf("failed to get app_id: %w", err) @@ -42,8 +45,12 @@ func DeployerMain(Id int64, db *sql.DB, logFile *os.File, logger *utils.Deployme imageTag := dep.CommitHash containerName := fmt.Sprintf("app-%d", app.ID) - err = DeployApp(dep, &app, appContextPath, imageTag, containerName, db, logFile, logger) + err = DeployApp(ctx, dep, &app, appContextPath, imageTag, containerName, db, logFile, logger) if err != nil { + if ctx.Err() == context.Canceled { + logger.Info("Deployment cancelled by user") + return "", context.Canceled + } logger.Error(err, "DeployApp failed") dep.Status = "failed" dep.Stage = "failed" @@ -55,7 +62,6 @@ func DeployerMain(Id int64, db *sql.DB, logFile *os.File, logger *utils.Deployme logger.Info("Deployment completed successfully") - // Run automatic cleanup if enabled in settings settings, err := models.GetSystemSettings() if err != nil { logger.Warn(fmt.Sprintf("Failed to get system settings for cleanup: %v", err)) diff --git a/server/docker/image.go b/server/docker/image.go index cff75f9..4420db9 100644 --- a/server/docker/image.go +++ b/server/docker/image.go @@ -12,8 +12,8 @@ import ( "github.com/rs/zerolog/log" ) -func BuildImage(imageTag, contextPath string, envVars map[string]string, logfile *os.File) error { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) +func BuildImage(ctx context.Context, imageTag, contextPath string, envVars map[string]string, logfile *os.File) error { + timeoutCtx, cancel := context.WithTimeout(ctx, 15*time.Minute) defer cancel() cli, err := client.New(client.FromEnv) if err != nil { @@ -41,11 +41,14 @@ func BuildImage(imageTag, contextPath string, envVars map[string]string, logfile log.Info().Str("image_tag", imageTag).Msg("Building Docker image") - resp, err := cli.ImageBuild(ctx, buildCtx, buildOptions) + resp, err := cli.ImageBuild(timeoutCtx, buildCtx, buildOptions) if err != nil { - if ctx.Err() == context.DeadlineExceeded { + if timeoutCtx.Err() == context.DeadlineExceeded { return fmt.Errorf("image build timed out after 15 minutes") } + if timeoutCtx.Err() == context.Canceled { + return context.Canceled + } return err } defer resp.Body.Close() @@ -88,8 +91,8 @@ func BuildImage(imageTag, contextPath string, envVars map[string]string, logfile // return nil } -func PullDockerImage(imageName string, logfile *os.File) error { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) +func PullDockerImage(ctx context.Context, imageName string, logfile *os.File) error { + timeoutCtx, cancel := context.WithTimeout(ctx, 15*time.Minute) defer cancel() cli, err := client.New(client.FromEnv) @@ -98,11 +101,14 @@ func PullDockerImage(imageName string, logfile *os.File) error { } log.Debug().Str("image_name", imageName).Msg("pulling image") - resp, err := cli.ImagePull(ctx, imageName, client.ImagePullOptions{}) + resp, err := cli.ImagePull(timeoutCtx, imageName, client.ImagePullOptions{}) if err != nil { - if ctx.Err() == context.DeadlineExceeded { + if timeoutCtx.Err() == context.DeadlineExceeded { return fmt.Errorf("image pull timed out after 15 minutes") } + if timeoutCtx.Err() == context.Canceled { + return context.Canceled + } return err } defer resp.Close() diff --git a/server/docker/loadDeployment.go b/server/docker/loadDeployment.go index 196db66..77b0281 100644 --- a/server/docker/loadDeployment.go +++ b/server/docker/loadDeployment.go @@ -1,27 +1,18 @@ package docker import ( - "database/sql" - "github.com/corecollectives/mist/models" + "gorm.io/gorm" ) -func LoadDeployment(depId int64, db *sql.DB) (*models.Deployment, error) { - row := db.QueryRow("SELECT id, app_id, commit_hash, commit_message, triggered_by, logs, status, created_at, finished_at FROM deployments WHERE id = ?", depId) +func LoadDeployment(depId int64, db *gorm.DB) (*models.Deployment, error) { + // row := db.QueryRow("SELECT id, app_id, commit_hash, commit_message, triggered_by, logs, status, created_at, finished_at FROM deployments WHERE id = ?", depId) dep := &models.Deployment{} - var triggeredBy sql.NullInt64 - var finishedAt sql.NullTime - var logs sql.NullString - err := row.Scan(&dep.ID, &dep.AppID, &dep.CommitHash, &dep.CommitMessage, &triggeredBy, &logs, &dep.Status, &dep.CreatedAt, &finishedAt) + err := db.Table("deployments").Where("id=?", depId).First(&dep).Error + if err != nil { return nil, err } - if triggeredBy.Valid { - dep.TriggeredBy = &triggeredBy.Int64 - } - if finishedAt.Valid { - dep.FinishedAt = &finishedAt.Time - } return dep, nil } diff --git a/server/git/clone.go b/server/git/clone.go index 79b140c..1f9865f 100644 --- a/server/git/clone.go +++ b/server/git/clone.go @@ -1,6 +1,7 @@ package git import ( + "context" "fmt" "os" @@ -9,18 +10,21 @@ import ( "github.com/rs/zerolog/log" ) -func CloneRepo(url string, branch string, logFile *os.File, path string) error { - _, err := fmt.Fprintf(logFile, "[GIT]: Cloning into %s", path) +func CloneRepo(ctx context.Context, url string, branch string, logFile *os.File, path string) error { + _, err := fmt.Fprintf(logFile, "[GIT]: Cloning into %s\n", path) if err != nil { log.Warn().Msg("error logging into log file") } - _, err = git.PlainClone(path, &git.CloneOptions{ + _, err = git.PlainCloneContext(ctx, path, &git.CloneOptions{ URL: url, // Progress: logFile, ReferenceName: plumbing.NewBranchReferenceName(branch), SingleBranch: true, }) if err != nil { + if ctx.Err() == context.Canceled { + return fmt.Errorf("deployment stopped by user") + } return err } diff --git a/server/github/cloneRepo.go b/server/github/cloneRepo.go index 9a6f5f0..19f0019 100644 --- a/server/github/cloneRepo.go +++ b/server/github/cloneRepo.go @@ -11,7 +11,7 @@ import ( "github.com/rs/zerolog/log" ) -func CloneRepo(appId int64, logFile *os.File) error { +func CloneRepo(ctx context.Context, appId int64, logFile *os.File) error { log.Info().Int64("app_id", appId).Msg("Starting repository clone") userId, err := models.GetUserIDByAppID(appId) @@ -71,7 +71,7 @@ func CloneRepo(appId int64, logFile *os.File) error { log.Info().Str("clone_url", cloneURL).Str("branch", branch).Str("path", path).Msg("Cloning repository") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) + ctx, cancel := context.WithTimeout(ctx, 10*time.Minute) defer cancel() // old command implementation @@ -87,7 +87,7 @@ func CloneRepo(appId int64, logFile *os.File) error { // } // new git sdk implementation - err = git.CloneRepo(repoURL, branch, logFile, path) + err = git.CloneRepo(ctx, repoURL, branch, logFile, path) if err != nil { if ctx.Err() == context.DeadlineExceeded { return fmt.Errorf("git clone timed out after 10 minutes") diff --git a/server/github/types.go b/server/github/types.go index 1a517eb..27f4da5 100644 --- a/server/github/types.go +++ b/server/github/types.go @@ -1,7 +1,5 @@ package github -import "database/sql" - type PushEvent struct { Ref string `json:"ref"` Before string `json:"before"` @@ -186,8 +184,8 @@ type GithubApp struct { type GithubInstallation struct { InstallationID int - AccessToken sql.NullString - TokenExpiresAt sql.NullTime + AccessToken *string + TokenExpiresAt *string } type LatestCommit struct { diff --git a/server/github/webHook.go b/server/github/webHook.go index 249c779..3b7a24b 100644 --- a/server/github/webHook.go +++ b/server/github/webHook.go @@ -1,12 +1,12 @@ package github import ( - "database/sql" "errors" "strings" "github.com/corecollectives/mist/models" "github.com/rs/zerolog/log" + "gorm.io/gorm" ) func CreateDeploymentFromGithubPushEvent(evt PushEvent) (int64, error) { @@ -58,7 +58,7 @@ func CreateDeploymentFromGithubPushEvent(evt PushEvent) (int64, error) { } dep, err := models.GetDeploymentByAppIDAndCommitHash(appID, commit) - if err != nil && err != sql.ErrNoRows { + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { log.Error().Err(err). Int64("app_id", appID). Str("commit", commit). diff --git a/server/go.mod b/server/go.mod index f09e037..77e377a 100644 --- a/server/go.mod +++ b/server/go.mod @@ -3,13 +3,19 @@ module github.com/corecollectives/mist go 1.25.1 require ( + github.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/gorilla/websocket v1.5.3 - github.com/mattn/go-sqlite3 v1.14.32 + github.com/mattn/go-sqlite3 v1.14.33 + github.com/moby/go-archive v0.2.0 + github.com/moby/moby/api v1.52.0 + github.com/moby/moby/client v0.2.1 github.com/rs/zerolog v1.34.0 github.com/shirou/gopsutil v3.21.11+incompatible golang.org/x/crypto v0.46.0 gopkg.in/yaml.v3 v3.0.1 + gorm.io/driver/sqlite v1.6.0 + gorm.io/gorm v1.31.1 ) require ( @@ -27,30 +33,27 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-git/gcfg/v2 v2.0.2 // indirect github.com/go-git/go-billy/v6 v6.0.0-20251217170237-e9738f50a3cd // indirect - github.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/klauspost/compress v1.18.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/kevinburke/ssh_config v1.4.0 // indirect + github.com/klauspost/compress v1.18.2 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/go-archive v0.2.0 // indirect - github.com/moby/moby/api v1.52.0 // indirect - github.com/moby/moby/client v0.2.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/user v0.4.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect - github.com/stretchr/testify v1.11.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/numcpus v0.10.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -61,6 +64,7 @@ require ( go.opentelemetry.io/otel/trace v1.35.0 // indirect golang.org/x/net v0.48.0 // indirect golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.33.0 // indirect ) replace github.com/docker/docker/api => github.com/moby/moby/api v1.52.0-beta.2 diff --git a/server/go.sum b/server/go.sum index 00b31ba..8c19508 100644 --- a/server/go.sum +++ b/server/go.sum @@ -1,7 +1,13 @@ +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -26,10 +32,14 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo= github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs= github.com/go-git/go-billy/v6 v6.0.0-20251217170237-e9738f50a3cd h1:Gd/f9cGi/3h1JOPaa6er+CkKUGyGX2DBJdFbDKVO+R0= github.com/go-git/go-billy/v6 v6.0.0-20251217170237-e9738f50a3cd/go.mod h1:d3XQcsHu1idnquxt48kAv+h+1MUiYKLH/e7LAzjP+pI= +github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20251229094738-4b14af179146 h1:xYfxAopYyL44ot6dMBIb1Z1njFM0ZBQ99HdIB99KxLs= +github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20251229094738-4b14af179146/go.mod h1:QE/75B8tBSLNGyUUbA9tw3EGHoFtYOtypa2h8YJxsWI= github.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19 h1:0lz2eJScP8v5YZQsrEw+ggWC5jNySjg4bIZo5BIh6iI= github.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19/go.mod h1:L+Evfcs7EdTqxwv854354cb6+++7TFL3hJn3Wy4g+3w= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -44,24 +54,36 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= -github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= +github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= +github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= -github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= +github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= @@ -87,6 +109,8 @@ github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= @@ -97,9 +121,8 @@ github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMT github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= @@ -116,10 +139,12 @@ go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= @@ -129,15 +154,26 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= +gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= +gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/server/lib/cleanup.go b/server/lib/cleanup.go index c0214e1..6d03887 100644 --- a/server/lib/cleanup.go +++ b/server/lib/cleanup.go @@ -104,7 +104,7 @@ func cleanupUpdates() error { Str("version", currentVersion). Msg("Completing successful update that was interrupted by service restart") - completionLog := latestLog.Logs + "\n✅ Update completed successfully (verified on restart)\n" + completionLog := *latestLog.Logs + "\n✅ Update completed successfully (verified on restart)\n" err = models.UpdateUpdateLogStatus(latestLog.ID, "success", completionLog, nil) if err != nil { log.Error().Err(err).Int64("update_log_id", latestLog.ID).Msg("Failed to complete pending update") @@ -127,7 +127,7 @@ func cleanupUpdates() error { Msg("Update appears to have failed (version mismatch detected on startup)") errMsg := "Update process was interrupted and version does not match target" - failureLog := latestLog.Logs + "\n❌ " + errMsg + "\n" + failureLog := *latestLog.Logs + "\n❌ " + errMsg + "\n" err = models.UpdateUpdateLogStatus(latestLog.ID, "failed", failureLog, &errMsg) if err != nil { log.Error().Err(err).Int64("update_log_id", latestLog.ID).Msg("Failed to mark failed update") diff --git a/server/main.go b/server/main.go index 3f1ae02..bb9ff35 100644 --- a/server/main.go +++ b/server/main.go @@ -20,7 +20,12 @@ func main() { log.Fatal().Err(err).Msg("Error initializing database") return } - defer dbInstance.Close() + sqldb, err := dbInstance.DB() + if err != nil { + log.Fatal().Err(err).Msg("Error getting sql.DB from gorm DB") + return + } + defer sqldb.Close() log.Info().Msg("Database initialized successfully") models.SetDB(dbInstance) diff --git a/server/models/apiToken.go b/server/models/apiToken.go index 75b457a..b902e04 100644 --- a/server/models/apiToken.go +++ b/server/models/apiToken.go @@ -4,21 +4,22 @@ import ( "time" "github.com/corecollectives/mist/utils" + "gorm.io/gorm" ) type ApiToken struct { - ID int64 `db:"id" json:"id"` - UserID int64 `db:"user_id" json:"userId"` - Name string `db:"name" json:"name"` - TokenHash string `db:"token_hash" json:"-"` // Never expose in JSON - TokenPrefix string `db:"token_prefix" json:"tokenPrefix"` - Scopes *string `db:"scopes" json:"scopes,omitempty"` // JSON array - LastUsedAt *time.Time `db:"last_used_at" json:"lastUsedAt,omitempty"` - LastUsedIP *string `db:"last_used_ip" json:"lastUsedIp,omitempty"` - UsageCount int `db:"usage_count" json:"usageCount"` - ExpiresAt *time.Time `db:"expires_at" json:"expiresAt,omitempty"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - RevokedAt *time.Time `db:"revoked_at" json:"revokedAt,omitempty"` + ID int64 `gorm:"primaryKey;autoIncrement:false" json:"id"` + UserID int64 `gorm:"index;not null" json:"userId"` + Name string `gorm:"type:varchar(255);not null" json:"name"` + TokenHash string `gorm:"uniqueIndex;type:varchar(255);not null" json:"-"` + TokenPrefix string `gorm:"index;type:varchar(50);not null" json:"tokenPrefix"` + Scopes *string `json:"scopes,omitempty"` + LastUsedAt *time.Time `json:"lastUsedAt,omitempty"` + LastUsedIP *string `json:"lastUsedIp,omitempty"` + UsageCount int `gorm:"default:0" json:"usageCount"` + ExpiresAt *time.Time `gorm:"index" json:"expiresAt,omitempty"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"` + RevokedAt *time.Time `json:"revokedAt,omitempty"` } func (t *ApiToken) ToJson() map[string]interface{} { @@ -38,94 +39,38 @@ func (t *ApiToken) ToJson() map[string]interface{} { } func (t *ApiToken) InsertInDB() error { - id := utils.GenerateRandomId() - t.ID = id - query := ` - INSERT INTO api_tokens ( - id, user_id, name, token_hash, token_prefix, scopes, expires_at - ) VALUES (?, ?, ?, ?, ?, ?, ?) - RETURNING created_at - ` - err := db.QueryRow(query, t.ID, t.UserID, t.Name, t.TokenHash, t.TokenPrefix, t.Scopes, t.ExpiresAt).Scan(&t.CreatedAt) - return err + t.ID = utils.GenerateRandomId() + result := db.Create(t) + return result.Error } func GetApiTokensByUserID(userID int64) ([]ApiToken, error) { var tokens []ApiToken - query := ` - SELECT id, user_id, name, token_hash, token_prefix, scopes, - last_used_at, last_used_ip, usage_count, expires_at, - created_at, revoked_at - FROM api_tokens - WHERE user_id = ? AND revoked_at IS NULL - ORDER BY created_at DESC - ` - rows, err := db.Query(query, userID) - if err != nil { - return nil, err - } - defer rows.Close() - - for rows.Next() { - var token ApiToken - err := rows.Scan( - &token.ID, &token.UserID, &token.Name, &token.TokenHash, &token.TokenPrefix, - &token.Scopes, &token.LastUsedAt, &token.LastUsedIP, &token.UsageCount, - &token.ExpiresAt, &token.CreatedAt, &token.RevokedAt, - ) - if err != nil { - return nil, err - } - tokens = append(tokens, token) - } - - return tokens, rows.Err() + result := db.Where("user_id=? AND revoked_at IS NULL", userID).Order("created_at DESC").Find(&tokens) + return tokens, result.Error } func GetApiTokenByHash(tokenHash string) (*ApiToken, error) { var token ApiToken - query := ` - SELECT id, user_id, name, token_hash, token_prefix, scopes, - last_used_at, last_used_ip, usage_count, expires_at, - created_at, revoked_at - FROM api_tokens - WHERE token_hash = ? AND revoked_at IS NULL - ` - err := db.QueryRow(query, tokenHash).Scan( - &token.ID, &token.UserID, &token.Name, &token.TokenHash, &token.TokenPrefix, - &token.Scopes, &token.LastUsedAt, &token.LastUsedIP, &token.UsageCount, - &token.ExpiresAt, &token.CreatedAt, &token.RevokedAt, - ) - if err != nil { - return nil, err + result := db.Where("token_hash=? AND revoked_at IS NULL", tokenHash).First(&token) + if result.Error != nil { + return nil, result.Error } return &token, nil } -func (t *ApiToken) UpdateUsage(ipAddress string) error { - query := ` - UPDATE api_tokens - SET last_used_at = CURRENT_TIMESTAMP, - last_used_ip = ?, - usage_count = usage_count + 1 - WHERE id = ? - ` - _, err := db.Exec(query, ipAddress, t.ID) - return err +func (t *ApiToken) UpdateUsage(lastUsedIP string) error { + return db.Model(t).Updates(map[string]interface{}{ + "last_used_at": time.Now(), + "last_used_ip": lastUsedIP, + "usage_count": gorm.Expr("usage_count + ?", 1), + }).Error } func (t *ApiToken) Revoke() error { - query := ` - UPDATE api_tokens - SET revoked_at = CURRENT_TIMESTAMP - WHERE id = ? - ` - _, err := db.Exec(query, t.ID) - return err + return db.Model(t).Update("revoked_at", time.Now()).Error } func DeleteApiToken(tokenID int64) error { - query := `DELETE FROM api_tokens WHERE id = ?` - _, err := db.Exec(query, tokenID) - return err + return db.Delete(&ApiToken{}, tokenID).Error } diff --git a/server/models/app.go b/server/models/app.go index 58ef5fc..1b55a21 100644 --- a/server/models/app.go +++ b/server/models/app.go @@ -1,7 +1,6 @@ package models import ( - "database/sql" "fmt" "time" @@ -34,39 +33,33 @@ const ( ) type App struct { - ID int64 `db:"id" json:"id"` - ProjectID int64 `db:"project_id" json:"project_id"` - CreatedBy int64 `db:"created_by" json:"created_by"` - Name string `db:"name" json:"name"` - Description *string `db:"description" json:"description,omitempty"` - - AppType AppType `db:"app_type" json:"app_type"` - TemplateName *string `db:"template_name" json:"template_name,omitempty"` - - GitProviderID *int64 `db:"git_provider_id" json:"git_provider_id,omitempty"` - GitRepository *string `db:"git_repository" json:"git_repository,omitempty"` - GitBranch string `db:"git_branch" json:"git_branch,omitempty"` - GitCloneURL *string `db:"git_clone_url" json:"git_clone_url,omitempty"` - - DeploymentStrategy DeploymentStrategy `db:"deployment_strategy" json:"deployment_strategy"` - Port *int64 `db:"port" json:"port,omitempty"` - RootDirectory string `db:"root_directory" json:"root_directory,omitempty"` - BuildCommand *string `db:"build_command" json:"build_command,omitempty"` - StartCommand *string `db:"start_command" json:"start_command,omitempty"` - DockerfilePath *string `db:"dockerfile_path" json:"dockerfile_path,omitempty"` - - CPULimit *float64 `db:"cpu_limit" json:"cpu_limit,omitempty"` - MemoryLimit *int `db:"memory_limit" json:"memory_limit,omitempty"` - RestartPolicy RestartPolicy `db:"restart_policy" json:"restart_policy"` - - HealthcheckPath *string `db:"healthcheck_path" json:"healthcheck_path,omitempty"` - HealthcheckInterval int `db:"healthcheck_interval" json:"healthcheck_interval"` - HealthcheckTimeout int `db:"healthcheck_timeout" json:"healthcheck_timeout"` - HealthcheckRetries int `db:"healthcheck_retries" json:"healthcheck_retries"` - Status AppStatus `db:"status" json:"status"` - - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + ID int64 `gorm:"primaryKey;autoIncrement:false" json:"id"` + ProjectID int64 `gorm:"uniqueIndex:idx_project_app_name;index;not null" json:"project_id"` + Name string `gorm:"uniqueIndex:idx_project_app_name;not null" json:"name"` + CreatedBy int64 `gorm:"index" json:"created_by"` + Description *string `json:"description,omitempty"` + AppType AppType `gorm:"default:'web';index" json:"app_type"` + TemplateName *string `json:"template_name,omitempty"` + GitProviderID *int64 `json:"git_provider_id,omitempty"` + GitRepository *string `json:"git_repository,omitempty"` + GitBranch string `gorm:"default:'main'" json:"git_branch,omitempty"` + GitCloneURL *string `json:"git_clone_url,omitempty"` + DeploymentStrategy DeploymentStrategy `gorm:"default:'auto'" json:"deployment_strategy"` + Port *int64 `json:"port,omitempty"` + RootDirectory string `gorm:"default:'.'" json:"root_directory,omitempty"` + BuildCommand *string `json:"build_command,omitempty"` + StartCommand *string `json:"start_command,omitempty"` + DockerfilePath *string `gorm:"default:'DOCKERFILE'" json:"dockerfile_path,omitempty"` + CPULimit *float64 `json:"cpu_limit,omitempty"` + MemoryLimit *int `json:"memory_limit,omitempty"` + RestartPolicy RestartPolicy `gorm:"default:'unless-stopped'" json:"restart_policy"` + HealthcheckPath *string `json:"healthcheck_path,omitempty"` + HealthcheckInterval int `gorm:"default:30" json:"healthcheck_interval"` + HealthcheckTimeout int `gorm:"default:10" json:"healthcheck_timeout"` + HealthcheckRetries int `gorm:"default:3" json:"healthcheck_retries"` + Status AppStatus `gorm:"default:'stopped';index" json:"status"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` } func (a *App) ToJson() map[string]interface{} { @@ -102,9 +95,7 @@ func (a *App) ToJson() map[string]interface{} { } func (a *App) InsertInDB() error { - id := utils.GenerateRandomId() - a.ID = id - + a.ID = utils.GenerateRandomId() if a.AppType == "" { a.AppType = AppTypeWeb } @@ -118,229 +109,112 @@ func (a *App) InsertInDB() error { a.Status = StatusStopped } - query := ` - INSERT INTO apps ( - id, name, description, project_id, created_by, app_type, template_name, - port, deployment_strategy, restart_policy, git_provider_id, git_repository, - git_branch, git_clone_url, root_directory, build_command, start_command, - dockerfile_path, cpu_limit, memory_limit, healthcheck_path, - healthcheck_interval, healthcheck_timeout, healthcheck_retries, status - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - RETURNING - created_at, updated_at - ` - err := db.QueryRow(query, - a.ID, a.Name, a.Description, a.ProjectID, a.CreatedBy, a.AppType, a.TemplateName, - a.Port, a.DeploymentStrategy, a.RestartPolicy, a.GitProviderID, a.GitRepository, - a.GitBranch, a.GitCloneURL, a.RootDirectory, a.BuildCommand, a.StartCommand, - a.DockerfilePath, a.CPULimit, a.MemoryLimit, a.HealthcheckPath, - a.HealthcheckInterval, a.HealthcheckTimeout, a.HealthcheckRetries, a.Status, - ).Scan(&a.CreatedAt, &a.UpdatedAt) - if err != nil { - return err - } - return nil + return db.Create(a).Error } func GetApplicationByProjectID(projectId int64) ([]App, error) { var apps []App - query := ` - SELECT id, project_id, created_by, name, description, app_type, template_name, - git_provider_id, git_repository, git_branch, git_clone_url, - deployment_strategy, port, root_directory, build_command, start_command, - dockerfile_path, cpu_limit, memory_limit, restart_policy, - healthcheck_path, healthcheck_interval, healthcheck_timeout, healthcheck_retries, - status, created_at, updated_at - FROM apps - WHERE project_id = ? - ` - rows, err := db.Query(query, projectId) - if err != nil { - return nil, err - } - defer rows.Close() - - for rows.Next() { - var app App - err := rows.Scan( - &app.ID, &app.ProjectID, &app.CreatedBy, &app.Name, &app.Description, - &app.AppType, &app.TemplateName, &app.GitProviderID, &app.GitRepository, - &app.GitBranch, &app.GitCloneURL, &app.DeploymentStrategy, &app.Port, - &app.RootDirectory, &app.BuildCommand, &app.StartCommand, &app.DockerfilePath, - &app.CPULimit, &app.MemoryLimit, &app.RestartPolicy, - &app.HealthcheckPath, &app.HealthcheckInterval, &app.HealthcheckTimeout, - &app.HealthcheckRetries, &app.Status, &app.CreatedAt, &app.UpdatedAt, - ) - if err != nil { - return nil, err - } - apps = append(apps, app) - } - if err = rows.Err(); err != nil { - return nil, err - } - return apps, nil + result := db.Where("project_id=?", projectId).Find(&apps) + return apps, result.Error } func GetApplicationByID(appId int64) (*App, error) { var app App - query := ` - SELECT id, project_id, created_by, name, description, app_type, template_name, - git_provider_id, git_repository, git_branch, git_clone_url, - deployment_strategy, port, root_directory, build_command, start_command, - dockerfile_path, cpu_limit, memory_limit, restart_policy, - healthcheck_path, healthcheck_interval, healthcheck_timeout, healthcheck_retries, - status, created_at, updated_at - FROM apps - WHERE id = ? - ` - err := db.QueryRow(query, appId).Scan( - &app.ID, &app.ProjectID, &app.CreatedBy, &app.Name, &app.Description, - &app.AppType, &app.TemplateName, &app.GitProviderID, &app.GitRepository, - &app.GitBranch, &app.GitCloneURL, &app.DeploymentStrategy, &app.Port, - &app.RootDirectory, &app.BuildCommand, &app.StartCommand, &app.DockerfilePath, - &app.CPULimit, &app.MemoryLimit, &app.RestartPolicy, - &app.HealthcheckPath, &app.HealthcheckInterval, &app.HealthcheckTimeout, - &app.HealthcheckRetries, &app.Status, &app.CreatedAt, &app.UpdatedAt, - ) - if err != nil { - return nil, err + result := db.First(&app, "id=?", appId) + if result.Error != nil { + return nil, result.Error } return &app, nil } -func (app *App) UpdateApplication() error { - query := ` - UPDATE apps - SET - name = ?, - description = ?, - app_type = ?, - template_name = ?, - git_provider_id = ?, - git_repository = ?, - git_branch = ?, - git_clone_url = ?, - deployment_strategy = ?, - port = ?, - root_directory = ?, - build_command = ?, - start_command = ?, - dockerfile_path = ?, - cpu_limit = ?, - memory_limit = ?, - restart_policy = ?, - healthcheck_path = ?, - healthcheck_interval = ?, - healthcheck_timeout = ?, - healthcheck_retries = ?, - status = ?, - updated_at = CURRENT_TIMESTAMP - WHERE id = ? - ` - _, err := db.Exec(query, - app.Name, app.Description, app.AppType, app.TemplateName, - app.GitProviderID, app.GitRepository, app.GitBranch, app.GitCloneURL, - app.DeploymentStrategy, app.Port, app.RootDirectory, - app.BuildCommand, app.StartCommand, app.DockerfilePath, - app.CPULimit, app.MemoryLimit, app.RestartPolicy, - app.HealthcheckPath, app.HealthcheckInterval, app.HealthcheckTimeout, - app.HealthcheckRetries, app.Status, app.ID, - ) - return err +func (a *App) UpdateApplication() error { + return db.Model(a).Select("Name", "Description", "AppType", "TemplateName", + "GitProviderID", "GitRepository", "GitBranch", "GitCloneURL", + "DeploymentStrategy", "Port", "RootDirectory", + "BuildCommand", "StartCommand", "DockerfilePath", + "CPULimit", "MemoryLimit", "RestartPolicy", + "HealthcheckPath", "HealthcheckInterval", "HealthcheckTimeout", "HealthcheckRetries", + "Status", "UpdatedAt").Updates(a).Error } func IsUserApplicationOwner(userId int64, appId int64) (bool, error) { - var createdBy int64 - err := db.QueryRow(` - SELECT created_by FROM apps WHERE id = ? - `, appId).Scan(&createdBy) - if err != nil { - return false, err - } - return createdBy == userId, nil + var count int64 + err := db.Model(&App{}). + Where("id = ? AND created_by = ?", appId, userId). + Count(&count).Error + + return count > 0, err } func FindApplicationIDByGitRepoAndBranch(gitRepo string, gitBranch string) (int64, error) { - var appId int64 - err := db.QueryRow(` - SELECT id FROM apps WHERE git_repository = ? AND git_branch = ? - `, gitRepo, gitBranch).Scan(&appId) + var app App + err := db.Select("id"). + Where("git_repository = ? AND git_branch = ?", gitRepo, gitBranch). + First(&app).Error + if err != nil { return 0, err } - return appId, nil + return app.ID, nil } func GetUserIDByAppID(appID int64) (*int64, error) { - query := ` - SELECT created_by FROM apps WHERE id = ? - ` - var userID int64 - err := db.QueryRow(query, appID).Scan(&userID) + var app App + err := db.Select("created_by").First(&app, appID).Error if err != nil { return nil, err } - return &userID, nil + return &app.CreatedBy, nil } -func GetAppIDByDeploymentID(depID int64) (int64, error) { - query := ` - SELECT app_id FROM deployments WHERE id = ? - ` - var appID int64 - err := db.QueryRow(query, depID).Scan(&appID) +func GetAppIDByDeploymentID(depId int64) (int64, error) { + var result struct { + AppID int64 + } + err := db.Table("deployments").Select("app_id").Where("id=?", depId).Scan(&result).Error if err != nil { return 0, err } - return appID, nil + return result.AppID, nil } func GetAppRepoInfo(appId int64) (string, string, int64, string, error) { - var repo sql.NullString - var branch sql.NullString - var name string - var projectId int64 - - err := db.QueryRow(` - SELECT git_repository, git_branch, project_id, name - FROM apps WHERE id = ? - `, appId).Scan(&repo, &branch, &projectId, &name) + var app App + err := db.Select("git_repository, git_branch, project_id, name"). + First(&app, appId).Error + if err != nil { return "", "", 0, "", err } - repoStr := "" - if repo.Valid { - repoStr = repo.String + repo := "" + if app.GitRepository != nil { + repo = *app.GitRepository } - branchStr := "" - if branch.Valid { - branchStr = branch.String - } - - return repoStr, branchStr, projectId, name, nil + return repo, app.GitBranch, app.ProjectID, app.Name, nil } -func GetAppRepoAndBranch(appID int64) (string, string, error) { - var repoName sql.NullString - var branch string - err := db.QueryRow(`SELECT git_repository, COALESCE(git_branch, 'main') FROM apps WHERE id = ?`, appID). - Scan(&repoName, &branch) +func GetAppRepoAndBranch(appId int64) (string, string, error) { + var app App + err := db.Select("git_repository, git_branch").First(&app, appId).Error if err != nil { return "", "", err } - if !repoName.Valid || repoName.String == "" { + if app.GitRepository == nil || *app.GitRepository == "" { return "", "", fmt.Errorf("app has no git repository configured") } - return repoName.String, branch, nil + + branch := app.GitBranch + if branch == "" { + branch = "main" + } + + return *app.GitRepository, branch, nil } func DeleteApplication(appID int64) error { - query := `DELETE FROM apps WHERE id = ?` - _, err := db.Exec(query, appID) - return err + return db.Delete(&App{}, appID).Error } // if git_clone_url is set, it uses that diff --git a/server/models/appRepositories.go b/server/models/appRepositories.go new file mode 100644 index 0000000..477748a --- /dev/null +++ b/server/models/appRepositories.go @@ -0,0 +1,25 @@ +package models + +import ( + "time" +) + +type AppRepositorySourceType string + +const ( + SourceGitProvider AppRepositorySourceType = "git_provider" + SourceGithubApp AppRepositorySourceType = "github_app" +) + +type AppRepositories struct { + ID int64 `gorm:"primaryKey;autoIncrement:true" json:"id"` + AppID int64 `gorm:"uniqueIndex:idx_app_repo_unique;not null;constraint:OnDelete:CASCADE" json:"app_id"` + SourceType AppRepositorySourceType `gorm:"not null" json:"source_type"` + SourceID int64 `gorm:"not null" json:"source_id"` + RepoFullName string `gorm:"uniqueIndex:idx_app_repo_unique;not null" json:"repo_full_name"` + RepoURL string `gorm:"not null" json:"repo_url"` + Branch string `gorm:"default:'main'" json:"branch"` + WebhookID int64 `json:"webhook_id"` + AutoDeploy bool `gorm:"default:false" json:"auto_deploy"` + LastSyncedAt *time.Time `json:"last_synced_at,omitempty"` +} diff --git a/server/models/auditLog.go b/server/models/auditLog.go index e8edacc..8850a80 100644 --- a/server/models/auditLog.go +++ b/server/models/auditLog.go @@ -1,25 +1,26 @@ package models import ( - "database/sql" "encoding/json" "time" + + "gorm.io/gorm" ) type AuditLog struct { - ID int64 `json:"id"` - UserID *int64 `json:"userId"` - Username *string `json:"username"` - Email *string `json:"email"` - Action string `json:"action"` - ResourceType string `json:"resourceType"` + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` + UserID *int64 `gorm:"index" json:"user_id"` + Username *string `gorm:"-" json:"username"` + Email *string `gorm:"-" json:"email"` + Action string `gorm:"not null" json:"action"` + ResourceType string `gorm:"not null" json:"resourceType"` ResourceID *int64 `json:"resourceId"` - ResourceName *string `json:"resourceName"` + ResourceName *string `gorm:"-" json:"resourceName"` Details *string `json:"details"` - IPAddress *string `json:"ipAddress"` - UserAgent *string `json:"userAgent"` - TriggerType string `json:"triggerType"` // "user", "webhook", "system" - CreatedAt time.Time `json:"createdAt"` + IPAddress *string `gorm:"-" json:"ipAddress"` + UserAgent *string `gorm:"-" json:"userAgent"` + TriggerType string `gorm:"-" json:"triggerType"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"` } type AuditLogDetails struct { @@ -30,56 +31,28 @@ type AuditLogDetails struct { } func (a *AuditLog) Create() error { - query := ` - INSERT INTO audit_logs (user_id, action, resource_type, resource_id, details, created_at) - VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) - RETURNING id, created_at - ` - return db.QueryRow(query, a.UserID, a.Action, a.ResourceType, a.ResourceID, a.Details). - Scan(&a.ID, &a.CreatedAt) + return db.Create(a).Error } -func GetAllAuditLogs(limit, offset int) ([]AuditLog, error) { - query := ` - SELECT - al.id, - al.user_id, - u.username, - u.email, - al.action, - al.resource_type, - al.resource_id, - al.details, - al.created_at - FROM audit_logs al - LEFT JOIN users u ON al.user_id = u.id - ORDER BY al.created_at DESC - LIMIT ? OFFSET ? - ` - rows, err := db.Query(query, limit, offset) - if err != nil { - return nil, err - } - defer rows.Close() +//hlper function for join logic - var logs []AuditLog - for rows.Next() { - var log AuditLog - err := rows.Scan( - &log.ID, - &log.UserID, - &log.Username, - &log.Email, - &log.Action, - &log.ResourceType, - &log.ResourceID, - &log.Details, - &log.CreatedAt, - ) - if err != nil { - return nil, err - } +func getAuditLogsQuery() *gorm.DB { + return db.Table("audit_logs").Select(`audit_logs.id, + audit_logs.user_id, + users.username, + users.email, + audit_logs.action, + audit_logs.resource_type, + audit_logs.resource_id, + audit_logs.details, + audit_logs.created_at`).Joins("LEFT JOIN users ON audit_logs.user_id=users.id").Order("audit_logs.created_at DESC") + +} +// helper function to reduce repetition +func logHelper(logs []AuditLog) []AuditLog { + for i := range logs { + log := &logs[i] if log.UserID == nil { log.TriggerType = "system" if log.Details != nil && (*log.Details != "") { @@ -93,124 +66,44 @@ func GetAllAuditLogs(limit, offset int) ([]AuditLog, error) { } else { log.TriggerType = "user" } - - logs = append(logs, log) } - return logs, nil + return logs } -func GetAuditLogsByUser(userID int64, limit, offset int) ([]AuditLog, error) { - query := ` - SELECT - al.id, - al.user_id, - u.username, - u.email, - al.action, - al.resource_type, - al.resource_id, - al.details, - al.created_at - FROM audit_logs al - LEFT JOIN users u ON al.user_id = u.id - WHERE al.user_id = ? - ORDER BY al.created_at DESC - LIMIT ? OFFSET ? - ` - rows, err := db.Query(query, userID, limit, offset) +func GetAllAuditLogs(limit, offset int) ([]AuditLog, error) { + var logs []AuditLog + err := getAuditLogsQuery().Limit(limit).Offset(offset).Scan(&logs).Error if err != nil { return nil, err } - defer rows.Close() + return logHelper(logs), nil +} +func GetAuditLogsByUserID(userID int64, limit, offset int) ([]AuditLog, error) { var logs []AuditLog - for rows.Next() { - var log AuditLog - err := rows.Scan( - &log.ID, - &log.UserID, - &log.Username, - &log.Email, - &log.Action, - &log.ResourceType, - &log.ResourceID, - &log.Details, - &log.CreatedAt, - ) - if err != nil { - return nil, err - } - log.TriggerType = "user" - logs = append(logs, log) + err := getAuditLogsQuery().Where("audit_logs.user_id = ?", userID).Limit(limit).Offset(offset).Scan(&logs).Error + if err != nil { + return nil, err + } + for i := range logs { + logs[i].TriggerType = "user" } return logs, nil } func GetAuditLogsByResource(resourceType string, resourceID int64, limit, offset int) ([]AuditLog, error) { - query := ` - SELECT - al.id, - al.user_id, - u.username, - u.email, - al.action, - al.resource_type, - al.resource_id, - al.details, - al.created_at - FROM audit_logs al - LEFT JOIN users u ON al.user_id = u.id - WHERE al.resource_type = ? AND al.resource_id = ? - ORDER BY al.created_at DESC - LIMIT ? OFFSET ? - ` - rows, err := db.Query(query, resourceType, resourceID, limit, offset) + var logs []AuditLog + err := getAuditLogsQuery().Where("audit_logs.resource_type = ? AND audit_logs.resource_id = ?", resourceType, resourceID). + Limit(limit).Offset(offset).Scan(&logs).Error if err != nil { return nil, err } - defer rows.Close() - - var logs []AuditLog - for rows.Next() { - var log AuditLog - err := rows.Scan( - &log.ID, - &log.UserID, - &log.Username, - &log.Email, - &log.Action, - &log.ResourceType, - &log.ResourceID, - &log.Details, - &log.CreatedAt, - ) - if err != nil { - return nil, err - } - - if log.UserID == nil { - log.TriggerType = "system" - if log.Details != nil && (*log.Details != "") { - var detailsMap map[string]interface{} - if err := json.Unmarshal([]byte(*log.Details), &detailsMap); err == nil { - if triggerType, ok := detailsMap["trigger_type"].(string); ok { - log.TriggerType = triggerType - } - } - } - } else { - log.TriggerType = "user" - } - - logs = append(logs, log) - } - return logs, nil + return logHelper(logs), nil } -func GetAuditLogsCount() (int, error) { - query := `SELECT COUNT(*) FROM audit_logs` - var count int - err := db.QueryRow(query).Scan(&count) +func GetAuditLogsCount() (int64, error) { + var count int64 + err := db.Model(&AuditLog{}).Count(&count).Error return count, err } @@ -257,116 +150,29 @@ func LogSystemAudit(action, resourceType string, resourceID *int64, details inte } func GetAuditLogsByResourceType(resourceType string, limit, offset int) ([]AuditLog, error) { - query := ` - SELECT - al.id, - al.user_id, - u.username, - u.email, - al.action, - al.resource_type, - al.resource_id, - al.details, - al.created_at - FROM audit_logs al - LEFT JOIN users u ON al.user_id = u.id - WHERE al.resource_type = ? - ORDER BY al.created_at DESC - LIMIT ? OFFSET ? - ` - rows, err := db.Query(query, resourceType, limit, offset) + var logs []AuditLog + err := getAuditLogsQuery().Where("audit_logs.resource_type = ?", resourceType). + Limit(limit).Offset(offset).Scan(&logs).Error if err != nil { return nil, err } - defer rows.Close() - - var logs []AuditLog - for rows.Next() { - var log AuditLog - err := rows.Scan( - &log.ID, - &log.UserID, - &log.Username, - &log.Email, - &log.Action, - &log.ResourceType, - &log.ResourceID, - &log.Details, - &log.CreatedAt, - ) - if err != nil { - return nil, err - } - - if log.UserID == nil { - log.TriggerType = "system" - if log.Details != nil && (*log.Details != "") { - var detailsMap map[string]interface{} - if err := json.Unmarshal([]byte(*log.Details), &detailsMap); err == nil { - if triggerType, ok := detailsMap["trigger_type"].(string); ok { - log.TriggerType = triggerType - } - } - } - } else { - log.TriggerType = "user" - } - - logs = append(logs, log) - } - return logs, nil + return logHelper(logs), nil } func GetAuditLogByID(id int64) (*AuditLog, error) { - query := ` - SELECT - al.id, - al.user_id, - u.username, - u.email, - al.action, - al.resource_type, - al.resource_id, - al.details, - al.created_at - FROM audit_logs al - LEFT JOIN users u ON al.user_id = u.id - WHERE al.id = ? - ` - - var log AuditLog - err := db.QueryRow(query, id).Scan( - &log.ID, - &log.UserID, - &log.Username, - &log.Email, - &log.Action, - &log.ResourceType, - &log.ResourceID, - &log.Details, - &log.CreatedAt, - ) + var logs []AuditLog + err := getAuditLogsQuery(). + Where("audit_logs.id = ?", id). + Limit(1). + Scan(&logs).Error - if err == sql.ErrNoRows { - return nil, nil - } if err != nil { return nil, err } - - if log.UserID == nil { - log.TriggerType = "system" - if log.Details != nil && (*log.Details != "") { - var detailsMap map[string]interface{} - if err := json.Unmarshal([]byte(*log.Details), &detailsMap); err == nil { - if triggerType, ok := detailsMap["trigger_type"].(string); ok { - log.TriggerType = triggerType - } - } - } - } else { - log.TriggerType = "user" + if len(logs) == 0 { + return nil, nil } - return &log, nil + enrichedLogs := logHelper(logs) + return &enrichedLogs[0], nil } diff --git a/server/models/backup.go b/server/models/backup.go index 0e3e535..6edd4cc 100644 --- a/server/models/backup.go +++ b/server/models/backup.go @@ -4,6 +4,7 @@ import ( "time" "github.com/corecollectives/mist/utils" + "gorm.io/gorm" ) type BackupType string @@ -30,34 +31,49 @@ const ( ) type Backup struct { - ID int64 `db:"id" json:"id"` - AppID int64 `db:"app_id" json:"appId"` - BackupType BackupType `db:"backup_type" json:"backupType"` - BackupName string `db:"backup_name" json:"backupName"` - FilePath string `db:"file_path" json:"filePath"` - FileSize *int64 `db:"file_size" json:"fileSize,omitempty"` - CompressionType string `db:"compression_type" json:"compressionType"` - DatabaseType *string `db:"database_type" json:"databaseType,omitempty"` - DatabaseVersion *string `db:"database_version" json:"databaseVersion,omitempty"` - StorageType StorageType `db:"storage_type" json:"storageType"` - StoragePath *string `db:"storage_path" json:"storagePath,omitempty"` - Status BackupStatus `db:"status" json:"status"` - Progress int `db:"progress" json:"progress"` - ErrorMessage *string `db:"error_message" json:"errorMessage,omitempty"` - Checksum *string `db:"checksum" json:"checksum,omitempty"` - ChecksumAlgorithm string `db:"checksum_algorithm" json:"checksumAlgorithm"` - IsVerified bool `db:"is_verified" json:"isVerified"` - VerifiedAt *time.Time `db:"verified_at" json:"verifiedAt,omitempty"` - CanRestore bool `db:"can_restore" json:"canRestore"` - LastRestoreAt *time.Time `db:"last_restore_at" json:"lastRestoreAt,omitempty"` - RestoreCount int `db:"restore_count" json:"restoreCount"` - RetentionDays *int `db:"retention_days" json:"retentionDays,omitempty"` - AutoDeleteAt *time.Time `db:"auto_delete_at" json:"autoDeleteAt,omitempty"` - CreatedBy *int64 `db:"created_by" json:"createdBy,omitempty"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - CompletedAt *time.Time `db:"completed_at" json:"completedAt,omitempty"` - Duration *int `db:"duration" json:"duration,omitempty"` - Notes *string `db:"notes" json:"notes,omitempty"` + ID int64 `gorm:"primaryKey;autoIncrement:false" json:"id"` + + AppID int64 `gorm:"index;not null;constraint:OnDelete:CASCADE" json:"appId"` + + BackupType BackupType `gorm:"index;not null" json:"backupType"` + BackupName string `gorm:"not null" json:"backupName"` + FilePath string `gorm:"not null" json:"filePath"` + FileSize *int64 `json:"fileSize,omitempty"` + + CompressionType string `gorm:"default:'gzip'" json:"compressionType"` + + DatabaseType *string `json:"databaseType,omitempty"` + DatabaseVersion *string `json:"databaseVersion,omitempty"` + + StorageType StorageType `gorm:"default:'local'" json:"storageType"` + StoragePath *string `json:"storagePath,omitempty"` + + Status BackupStatus `gorm:"default:'pending';index" json:"status"` + + Progress int `gorm:"default:0" json:"progress"` + ErrorMessage *string `json:"errorMessage,omitempty"` + Checksum *string `json:"checksum,omitempty"` + + ChecksumAlgorithm string `gorm:"default:'sha256'" json:"checksumAlgorithm"` + + IsVerified bool `gorm:"default:false" json:"isVerified"` + VerifiedAt *time.Time `json:"verifiedAt,omitempty"` + + CanRestore bool `gorm:"default:true" json:"canRestore"` + LastRestoreAt *time.Time `json:"lastRestoreAt,omitempty"` + + RestoreCount int `gorm:"default:0" json:"restoreCount"` + + RetentionDays *int `json:"retentionDays,omitempty"` + + AutoDeleteAt *time.Time `gorm:"index" json:"autoDeleteAt,omitempty"` + + CreatedBy *int64 `gorm:"constraint:OnDelete:SET NULL" json:"createdBy,omitempty"` + + CreatedAt time.Time `gorm:"autoCreateTime;index:,sort:desc" json:"createdAt"` + CompletedAt *time.Time `json:"completedAt,omitempty"` + Duration *int `json:"duration,omitempty"` + Notes *string `json:"notes,omitempty"` } func (b *Backup) ToJson() map[string]interface{} { @@ -94,127 +110,272 @@ func (b *Backup) ToJson() map[string]interface{} { } func (b *Backup) InsertInDB() error { - id := utils.GenerateRandomId() - b.ID = id - query := ` - INSERT INTO backups ( - id, app_id, backup_type, backup_name, file_path, - file_size, compression_type, database_type, database_version, - storage_type, storage_path, status, checksum_algorithm, - retention_days, auto_delete_at, created_by, notes - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - RETURNING created_at - ` - err := db.QueryRow(query, b.ID, b.AppID, b.BackupType, b.BackupName, b.FilePath, - b.FileSize, b.CompressionType, b.DatabaseType, b.DatabaseVersion, - b.StorageType, b.StoragePath, b.Status, b.ChecksumAlgorithm, - b.RetentionDays, b.AutoDeleteAt, b.CreatedBy, b.Notes).Scan(&b.CreatedAt) - return err + b.ID = utils.GenerateRandomId() + b.CanRestore = true + + return db.Create(b).Error } -func GetBackupsByAppID(appID int64) ([]Backup, error) { +func GetBackupsByAppID(appId int64) ([]Backup, error) { var backups []Backup - query := ` - SELECT id, app_id, backup_type, backup_name, file_path, file_size, - compression_type, database_type, database_version, - storage_type, storage_path, status, progress, error_message, - checksum, checksum_algorithm, is_verified, verified_at, - can_restore, last_restore_at, restore_count, - retention_days, auto_delete_at, created_by, created_at, - completed_at, duration, notes - FROM backups - WHERE app_id = ? AND status != 'deleted' - ORDER BY created_at DESC - ` - rows, err := db.Query(query, appID) - if err != nil { - return nil, err - } - defer rows.Close() - - for rows.Next() { - var backup Backup - err := rows.Scan( - &backup.ID, &backup.AppID, &backup.BackupType, &backup.BackupName, - &backup.FilePath, &backup.FileSize, &backup.CompressionType, - &backup.DatabaseType, &backup.DatabaseVersion, &backup.StorageType, - &backup.StoragePath, &backup.Status, &backup.Progress, &backup.ErrorMessage, - &backup.Checksum, &backup.ChecksumAlgorithm, &backup.IsVerified, &backup.VerifiedAt, - &backup.CanRestore, &backup.LastRestoreAt, &backup.RestoreCount, - &backup.RetentionDays, &backup.AutoDeleteAt, &backup.CreatedBy, &backup.CreatedAt, - &backup.CompletedAt, &backup.Duration, &backup.Notes, - ) - if err != nil { - return nil, err - } - backups = append(backups, backup) - } - - return backups, rows.Err() + result := db.Where("app_id=? AND status != ?", appId, BackupStatusDeleted).Order("created_at DESC").Find(&backups) + return backups, result.Error } -func GetBackupByID(backupID int64) (*Backup, error) { +func GetBackupByID(backupId int64) (*Backup, error) { var backup Backup - query := ` - SELECT id, app_id, backup_type, backup_name, file_path, file_size, - compression_type, database_type, database_version, - storage_type, storage_path, status, progress, error_message, - checksum, checksum_algorithm, is_verified, verified_at, - can_restore, last_restore_at, restore_count, - retention_days, auto_delete_at, created_by, created_at, - completed_at, duration, notes - FROM backups - WHERE id = ? - ` - err := db.QueryRow(query, backupID).Scan( - &backup.ID, &backup.AppID, &backup.BackupType, &backup.BackupName, - &backup.FilePath, &backup.FileSize, &backup.CompressionType, - &backup.DatabaseType, &backup.DatabaseVersion, &backup.StorageType, - &backup.StoragePath, &backup.Status, &backup.Progress, &backup.ErrorMessage, - &backup.Checksum, &backup.ChecksumAlgorithm, &backup.IsVerified, &backup.VerifiedAt, - &backup.CanRestore, &backup.LastRestoreAt, &backup.RestoreCount, - &backup.RetentionDays, &backup.AutoDeleteAt, &backup.CreatedBy, &backup.CreatedAt, - &backup.CompletedAt, &backup.Duration, &backup.Notes, - ) - if err != nil { - return nil, err + result := db.First(&backup, backupId) + if result.Error != nil { + return nil, result.Error } return &backup, nil } func (b *Backup) UpdateStatus(status BackupStatus, errorMsg *string) error { - query := ` - UPDATE backups - SET status = ?, error_message = ?, completed_at = CURRENT_TIMESTAMP - WHERE id = ? - ` - _, err := db.Exec(query, status, errorMsg, b.ID) - return err + update := map[string]interface{}{ + "status": status, + "error_message": errorMsg, + "completed_at": time.Now(), + } + return db.Model(b).Updates(update).Error } func (b *Backup) UpdateProgress(progress int) error { - query := `UPDATE backups SET progress = ? WHERE id = ?` - _, err := db.Exec(query, progress, b.ID) - return err + return db.Model(b).Update("progress", progress).Error } func (b *Backup) MarkAsRestored() error { - query := ` - UPDATE backups - SET last_restore_at = CURRENT_TIMESTAMP, - restore_count = restore_count + 1 - WHERE id = ? - ` - _, err := db.Exec(query, b.ID) - return err + return db.Model(b).Updates(map[string]interface{}{ + "last_restore_at": time.Now(), + "restore_count": gorm.Expr("restore_count + ?", 1), + }).Error } func DeleteExpiredBackups() error { - query := ` - UPDATE backups - SET status = 'deleted' - WHERE auto_delete_at IS NOT NULL AND auto_delete_at < CURRENT_TIMESTAMP AND status != 'deleted' - ` - _, err := db.Exec(query) - return err + return db.Where("auto_delete_at IS NOT NULL AND auto_delete_at < ? AND status != ?", time.Now(), BackupStatusDeleted).Update("status", BackupStatusDeleted).Error } + +//########################################################################################################################## + +//ARCHIVED CODE BELOW---------------------> + +// package models + +// import ( +// "time" + +// "github.com/corecollectives/mist/utils" +// ) + +// type BackupType string +// type BackupStatus string +// type StorageType string + +// const ( +// BackupTypeManual BackupType = "manual" +// BackupTypeScheduled BackupType = "scheduled" +// BackupTypePreDeployment BackupType = "pre_deployment" +// BackupTypeAutomatic BackupType = "automatic" + +// BackupStatusPending BackupStatus = "pending" +// BackupStatusInProgress BackupStatus = "in_progress" +// BackupStatusCompleted BackupStatus = "completed" +// BackupStatusFailed BackupStatus = "failed" +// BackupStatusDeleted BackupStatus = "deleted" + +// StorageTypeLocal StorageType = "local" +// StorageTypeS3 StorageType = "s3" +// StorageTypeGCS StorageType = "gcs" +// StorageTypeAzure StorageType = "azure" +// StorageTypeFTP StorageType = "ftp" +// ) + +// type Backup struct { +// ID int64 `db:"id" json:"id"` +// AppID int64 `db:"app_id" json:"appId"` +// BackupType BackupType `db:"backup_type" json:"backupType"` +// BackupName string `db:"backup_name" json:"backupName"` +// FilePath string `db:"file_path" json:"filePath"` +// FileSize *int64 `db:"file_size" json:"fileSize,omitempty"` +// CompressionType string `db:"compression_type" json:"compressionType"` +// DatabaseType *string `db:"database_type" json:"databaseType,omitempty"` +// DatabaseVersion *string `db:"database_version" json:"databaseVersion,omitempty"` +// StorageType StorageType `db:"storage_type" json:"storageType"` +// StoragePath *string `db:"storage_path" json:"storagePath,omitempty"` +// Status BackupStatus `db:"status" json:"status"` +// Progress int `db:"progress" json:"progress"` +// ErrorMessage *string `db:"error_message" json:"errorMessage,omitempty"` +// Checksum *string `db:"checksum" json:"checksum,omitempty"` +// ChecksumAlgorithm string `db:"checksum_algorithm" json:"checksumAlgorithm"` +// IsVerified bool `db:"is_verified" json:"isVerified"` +// VerifiedAt *time.Time `db:"verified_at" json:"verifiedAt,omitempty"` +// CanRestore bool `db:"can_restore" json:"canRestore"` +// LastRestoreAt *time.Time `db:"last_restore_at" json:"lastRestoreAt,omitempty"` +// RestoreCount int `db:"restore_count" json:"restoreCount"` +// RetentionDays *int `db:"retention_days" json:"retentionDays,omitempty"` +// AutoDeleteAt *time.Time `db:"auto_delete_at" json:"autoDeleteAt,omitempty"` +// CreatedBy *int64 `db:"created_by" json:"createdBy,omitempty"` +// CreatedAt time.Time `db:"created_at" json:"createdAt"` +// CompletedAt *time.Time `db:"completed_at" json:"completedAt,omitempty"` +// Duration *int `db:"duration" json:"duration,omitempty"` +// Notes *string `db:"notes" json:"notes,omitempty"` +// } + +// func (b *Backup) ToJson() map[string]interface{} { +// return map[string]interface{}{ +// "id": b.ID, +// "appId": b.AppID, +// "backupType": b.BackupType, +// "backupName": b.BackupName, +// "filePath": b.FilePath, +// "fileSize": b.FileSize, +// "compressionType": b.CompressionType, +// "databaseType": b.DatabaseType, +// "databaseVersion": b.DatabaseVersion, +// "storageType": b.StorageType, +// "storagePath": b.StoragePath, +// "status": b.Status, +// "progress": b.Progress, +// "errorMessage": b.ErrorMessage, +// "checksum": b.Checksum, +// "checksumAlgorithm": b.ChecksumAlgorithm, +// "isVerified": b.IsVerified, +// "verifiedAt": b.VerifiedAt, +// "canRestore": b.CanRestore, +// "lastRestoreAt": b.LastRestoreAt, +// "restoreCount": b.RestoreCount, +// "retentionDays": b.RetentionDays, +// "autoDeleteAt": b.AutoDeleteAt, +// "createdBy": b.CreatedBy, +// "createdAt": b.CreatedAt, +// "completedAt": b.CompletedAt, +// "duration": b.Duration, +// "notes": b.Notes, +// } +// } + +// func (b *Backup) InsertInDB() error { +// id := utils.GenerateRandomId() +// b.ID = id +// query := ` +// INSERT INTO backups ( +// id, app_id, backup_type, backup_name, file_path, +// file_size, compression_type, database_type, database_version, +// storage_type, storage_path, status, checksum_algorithm, +// retention_days, auto_delete_at, created_by, notes +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// RETURNING created_at +// ` +// err := db.QueryRow(query, b.ID, b.AppID, b.BackupType, b.BackupName, b.FilePath, +// b.FileSize, b.CompressionType, b.DatabaseType, b.DatabaseVersion, +// b.StorageType, b.StoragePath, b.Status, b.ChecksumAlgorithm, +// b.RetentionDays, b.AutoDeleteAt, b.CreatedBy, b.Notes).Scan(&b.CreatedAt) +// return err +// } + +// func GetBackupsByAppID(appID int64) ([]Backup, error) { +// var backups []Backup +// query := ` +// SELECT id, app_id, backup_type, backup_name, file_path, file_size, +// compression_type, database_type, database_version, +// storage_type, storage_path, status, progress, error_message, +// checksum, checksum_algorithm, is_verified, verified_at, +// can_restore, last_restore_at, restore_count, +// retention_days, auto_delete_at, created_by, created_at, +// completed_at, duration, notes +// FROM backups +// WHERE app_id = ? AND status != 'deleted' +// ORDER BY created_at DESC +// ` +// rows, err := db.Query(query, appID) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// for rows.Next() { +// var backup Backup +// err := rows.Scan( +// &backup.ID, &backup.AppID, &backup.BackupType, &backup.BackupName, +// &backup.FilePath, &backup.FileSize, &backup.CompressionType, +// &backup.DatabaseType, &backup.DatabaseVersion, &backup.StorageType, +// &backup.StoragePath, &backup.Status, &backup.Progress, &backup.ErrorMessage, +// &backup.Checksum, &backup.ChecksumAlgorithm, &backup.IsVerified, &backup.VerifiedAt, +// &backup.CanRestore, &backup.LastRestoreAt, &backup.RestoreCount, +// &backup.RetentionDays, &backup.AutoDeleteAt, &backup.CreatedBy, &backup.CreatedAt, +// &backup.CompletedAt, &backup.Duration, &backup.Notes, +// ) +// if err != nil { +// return nil, err +// } +// backups = append(backups, backup) +// } + +// return backups, rows.Err() +// } + +// func GetBackupByID(backupID int64) (*Backup, error) { +// var backup Backup +// query := ` +// SELECT id, app_id, backup_type, backup_name, file_path, file_size, +// compression_type, database_type, database_version, +// storage_type, storage_path, status, progress, error_message, +// checksum, checksum_algorithm, is_verified, verified_at, +// can_restore, last_restore_at, restore_count, +// retention_days, auto_delete_at, created_by, created_at, +// completed_at, duration, notes +// FROM backups +// WHERE id = ? +// ` +// err := db.QueryRow(query, backupID).Scan( +// &backup.ID, &backup.AppID, &backup.BackupType, &backup.BackupName, +// &backup.FilePath, &backup.FileSize, &backup.CompressionType, +// &backup.DatabaseType, &backup.DatabaseVersion, &backup.StorageType, +// &backup.StoragePath, &backup.Status, &backup.Progress, &backup.ErrorMessage, +// &backup.Checksum, &backup.ChecksumAlgorithm, &backup.IsVerified, &backup.VerifiedAt, +// &backup.CanRestore, &backup.LastRestoreAt, &backup.RestoreCount, +// &backup.RetentionDays, &backup.AutoDeleteAt, &backup.CreatedBy, &backup.CreatedAt, +// &backup.CompletedAt, &backup.Duration, &backup.Notes, +// ) +// if err != nil { +// return nil, err +// } +// return &backup, nil +// } + +// func (b *Backup) UpdateStatus(status BackupStatus, errorMsg *string) error { +// query := ` +// UPDATE backups +// SET status = ?, error_message = ?, completed_at = CURRENT_TIMESTAMP +// WHERE id = ? +// ` +// _, err := db.Exec(query, status, errorMsg, b.ID) +// return err +// } + +// func (b *Backup) UpdateProgress(progress int) error { +// query := `UPDATE backups SET progress = ? WHERE id = ?` +// _, err := db.Exec(query, progress, b.ID) +// return err +// } + +// func (b *Backup) MarkAsRestored() error { +// query := ` +// UPDATE backups +// SET last_restore_at = CURRENT_TIMESTAMP, +// restore_count = restore_count + 1 +// WHERE id = ? +// ` +// _, err := db.Exec(query, b.ID) +// return err +// } + +// func DeleteExpiredBackups() error { +// query := ` +// UPDATE backups +// SET status = 'deleted' +// WHERE auto_delete_at IS NOT NULL AND auto_delete_at < CURRENT_TIMESTAMP AND status != 'deleted' +// ` +// _, err := db.Exec(query) +// return err +// } diff --git a/server/models/cron.go b/server/models/cron.go new file mode 100644 index 0000000..262a4db --- /dev/null +++ b/server/models/cron.go @@ -0,0 +1,15 @@ +package models + +import "time" + +type Cron struct { + ID int64 `gorm:"primaryKey;autoIncrement:true" json:"id"` + AppID int64 `gorm:"index;constraint:OnDelete:CASCADE;not null" json:"app_id"` + Name string `gorm:"index;not null" json:"name"` + Schedule string `gorm:"not null" json:"schedule"` + Command string `gorm:"not null" json:"command"` + LastRun *time.Time `gorm:"type:timestamp" json:"last_run"` + NextRun *time.Time `gorm:"type:timestamp" json:"next_run"` + Enable bool `gorm:"default:true" json:"enable"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` +} diff --git a/server/models/deployment.go b/server/models/deployment.go index 85fc5da..242b06b 100644 --- a/server/models/deployment.go +++ b/server/models/deployment.go @@ -1,9 +1,11 @@ package models import ( + "fmt" "time" "github.com/corecollectives/mist/utils" + "gorm.io/gorm" ) type DeploymentStatus string @@ -19,28 +21,42 @@ const ( ) type Deployment struct { - ID int64 `db:"id" json:"id"` - AppID int64 `db:"app_id" json:"app_id"` - CommitHash string `db:"commit_hash" json:"commit_hash"` - CommitMessage *string `db:"commit_message" json:"commit_message,omitempty"` - CommitAuthor *string `db:"commit_author" json:"commit_author,omitempty"` - TriggeredBy *int64 `db:"triggered_by" json:"triggered_by,omitempty"` - DeploymentNumber *int `db:"deployment_number" json:"deployment_number,omitempty"` - ContainerID *string `db:"container_id" json:"container_id,omitempty"` - ContainerName *string `db:"container_name" json:"container_name,omitempty"` - ImageTag *string `db:"image_tag" json:"image_tag,omitempty"` - Logs *string `db:"logs" json:"logs,omitempty"` - BuildLogsPath *string `db:"build_logs_path" json:"build_logs_path,omitempty"` - Status DeploymentStatus `db:"status" json:"status"` - Stage string `db:"stage" json:"stage"` - Progress int `db:"progress" json:"progress"` - ErrorMessage *string `db:"error_message" json:"error_message,omitempty"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - StartedAt *time.Time `db:"started_at" json:"started_at,omitempty"` - FinishedAt *time.Time `db:"finished_at" json:"finished_at,omitempty"` - Duration *int `db:"duration" json:"duration,omitempty"` - IsActive bool `db:"is_active" json:"is_active"` - RolledBackFrom *int64 `db:"rolled_back_from" json:"rolled_back_from,omitempty"` + ID int64 `gorm:"primaryKey;autoIncrement:false" json:"id"` + + AppID int64 `gorm:"index:idx_deployments_app_id;not null;constraint:OnDelete:CASCADE" json:"app_id"` + + CommitHash string `gorm:"index:idx_deployments_commit_hash;not null" json:"commit_hash"` + + CommitMessage *string `json:"commit_message,omitempty"` + CommitAuthor *string `json:"commit_author,omitempty"` + + TriggeredBy *int64 `gorm:"constraint:OnDelete:SET NULL" json:"triggered_by,omitempty"` + + DeploymentNumber *int `json:"deployment_number,omitempty"` + + ContainerID *string `json:"container_id,omitempty"` + ContainerName *string `json:"container_name,omitempty"` + ImageTag *string `json:"image_tag,omitempty"` + + Logs *string `json:"logs,omitempty"` + BuildLogsPath *string `json:"build_logs_path,omitempty"` + + Status DeploymentStatus `gorm:"default:'pending';index:idx_deployments_status" json:"status"` + + Stage string `gorm:"default:'pending'" json:"stage"` + Progress int `gorm:"default:0" json:"progress"` + + ErrorMessage *string `json:"error_message,omitempty"` + + CreatedAt time.Time `gorm:"autoCreateTime;index:idx_deployments_created_at,sort:desc" json:"created_at"` + + StartedAt *time.Time `json:"started_at,omitempty"` + FinishedAt *time.Time `json:"finished_at,omitempty"` + Duration *int `json:"duration,omitempty"` + + IsActive bool `gorm:"default:false;index:idx_deployments_is_active" json:"is_active"` + + RolledBackFrom *int64 `gorm:"constraint:OnDelete:SET NULL" json:"rolled_back_from,omitempty"` } func (d *Deployment) ToJson() map[string]interface{} { @@ -71,258 +87,483 @@ func (d *Deployment) ToJson() map[string]interface{} { } func GetDeploymentsByAppID(appID int64) ([]Deployment, error) { - query := ` - SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, - deployment_number, container_id, container_name, image_tag, - logs, build_logs_path, status, stage, progress, error_message, - created_at, started_at, finished_at, duration, is_active, rolled_back_from - FROM deployments - WHERE app_id = ? - ORDER BY created_at DESC - ` - - rows, err := db.Query(query, appID) - if err != nil { - return nil, err - } - defer rows.Close() - var deployments []Deployment - for rows.Next() { - var d Deployment - if err := rows.Scan( - &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, - &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, - &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, - &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, - &d.IsActive, &d.RolledBackFrom, - ); err != nil { - return nil, err - } - deployments = append(deployments, d) - } - - if err := rows.Err(); err != nil { - return nil, err - } - - return deployments, nil + result := db.Where("app_id = ?", appID).Order("created_at DESC").Find(&deployments) + return deployments, result.Error } func (d *Deployment) CreateDeployment() error { - id := utils.GenerateRandomId() - d.ID = id - - var maxDeploymentNum int - err := db.QueryRow(`SELECT COALESCE(MAX(deployment_number), 0) FROM deployments WHERE app_id = ?`, d.AppID).Scan(&maxDeploymentNum) - if err == nil { - deploymentNum := maxDeploymentNum + 1 - d.DeploymentNumber = &deploymentNum - } - - query := ` - INSERT INTO deployments ( - id, app_id, commit_hash, commit_message, commit_author, triggered_by, - deployment_number, status, stage, progress - ) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', 'pending', 0) - RETURNING created_at - ` - err = db.QueryRow(query, d.ID, d.AppID, d.CommitHash, d.CommitMessage, - d.CommitAuthor, d.TriggeredBy, d.DeploymentNumber).Scan(&d.CreatedAt) - if err != nil { - return err + d.ID = utils.GenerateRandomId() + var maxDepNum *int + result := db.Model(&Deployment{}). + Where("app_id = ?", d.AppID). + Pluck("MAX(deployment_number)", &maxDepNum) + + currentNum := 0 + if result.Error == nil && maxDepNum != nil { + currentNum = *maxDepNum } + newNum := currentNum + 1 + d.DeploymentNumber = &newNum d.Status = DeploymentStatusPending d.Stage = "pending" d.Progress = 0 d.IsActive = false - return nil + + return db.Create(d).Error } func GetDeploymentByID(depID int64) (*Deployment, error) { - query := ` - SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, - deployment_number, container_id, container_name, image_tag, - logs, build_logs_path, status, stage, progress, error_message, - created_at, started_at, finished_at, duration, is_active, rolled_back_from - FROM deployments - WHERE id = ? - ` - - var d Deployment - if err := db.QueryRow(query, depID).Scan( - &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, - &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, - &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, - &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, - &d.IsActive, &d.RolledBackFrom, - ); err != nil { - return nil, err + var deployment Deployment + result := db.First(&deployment, "id = ?", depID) + if result.Error != nil { + return nil, result.Error } - - return &d, nil + return &deployment, nil } func GetDeploymentByCommitHash(commitHash string) (*Deployment, error) { - query := ` - SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, - deployment_number, container_id, container_name, image_tag, - logs, build_logs_path, status, stage, progress, error_message, - created_at, started_at, finished_at, duration, is_active, rolled_back_from - FROM deployments - WHERE commit_hash = ? - LIMIT 1 - ` - var d Deployment - if err := db.QueryRow(query, commitHash).Scan( - &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, - &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, - &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, - &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, - &d.IsActive, &d.RolledBackFrom, - ); err != nil { - return nil, err + var deployment Deployment + result := db.First(&deployment, "commit_hash = ?", commitHash) + if result.Error != nil { + return nil, result.Error } - - return &d, nil - + return &deployment, nil } func GetDeploymentByAppIDAndCommitHash(appID int64, commitHash string) (*Deployment, error) { - query := ` - SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, - deployment_number, container_id, container_name, image_tag, - logs, build_logs_path, status, stage, progress, error_message, - created_at, started_at, finished_at, duration, is_active, rolled_back_from - FROM deployments - WHERE app_id = ? AND commit_hash = ? - LIMIT 1 - ` - var d Deployment - if err := db.QueryRow(query, appID, commitHash).Scan( - &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, - &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, - &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, - &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, - &d.IsActive, &d.RolledBackFrom, - ); err != nil { - return nil, err + var deployment Deployment + result := db.First(&deployment, "app_id = ? AND commit_hash = ?", appID, commitHash) + if result.Error != nil { + return nil, result.Error } - - return &d, nil - + return &deployment, nil } - func GetActiveDeploymentByAppID(appID int64) (*Deployment, error) { - query := ` - SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, - deployment_number, container_id, container_name, image_tag, - logs, build_logs_path, status, stage, progress, error_message, - created_at, started_at, finished_at, duration, is_active, rolled_back_from - FROM deployments - WHERE app_id = ? AND is_active = 1 - LIMIT 1 - ` - - var d Deployment - if err := db.QueryRow(query, appID).Scan( - &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, - &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, - &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, - &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, - &d.IsActive, &d.RolledBackFrom, - ); err != nil { - return nil, err + var deployment Deployment + err := db.Where("app_id = ? AND is_active = ?", appID, true).First(&deployment) + if err.Error != nil { + return nil, err.Error } - - return &d, nil + return &deployment, nil } func GetCommitHashByDeploymentID(depID int64) (string, error) { - var commitHash string - err := db.QueryRow(`SELECT commit_hash FROM deployments WHERE id = ?`, depID).Scan(&commitHash) - if err != nil { - return "", err + var d Deployment + result := db.Select("commit_hash").First(&d, "id = ?", depID) + if result.Error != nil { + return "", result.Error } - return commitHash, nil + return d.CommitHash, nil } func UpdateDeploymentStatus(depID int64, status, stage string, progress int, errorMsg *string) error { - query := ` - UPDATE deployments - SET status = ?, stage = ?, progress = ?, error_message = ?, - finished_at = CASE WHEN ? IN ('success', 'failed', 'stopped') THEN CURRENT_TIMESTAMP ELSE finished_at END, - duration = CASE WHEN ? IN ('success', 'failed', 'stopped') THEN - CAST((julianday(CURRENT_TIMESTAMP) - julianday(started_at)) * 86400 AS INTEGER) - ELSE duration END - WHERE id = ? - ` - _, err := db.Exec(query, status, stage, progress, errorMsg, status, status, depID) - return err -} + var d Deployment + result := db.First(&d, "id = ?", depID) + if result.Error != nil { + return result.Error + } + updates := map[string]interface{}{ + "status": status, + "stage": stage, + "progress": progress, + "error_message": errorMsg, + } + if status == string(DeploymentStatusFailed) || status == string(DeploymentStatusSuccess) || status == string(DeploymentStatusStopped) { + now := time.Now() + updates["finished_at"] = &now + if d.StartedAt != nil { + duration := int(now.Sub(*d.StartedAt).Seconds()) + updates["duration"] = &duration + } + } + if errorMsg != nil { + fmt.Println("updated dep status: ", *errorMsg) + } + return db.Model(d).Updates(updates).Error +} func MarkDeploymentStarted(depID int64) error { - query := `UPDATE deployments SET started_at = CURRENT_TIMESTAMP WHERE id = ?` - _, err := db.Exec(query, depID) - return err + now := time.Now() + return db.Model(&Deployment{}).Where("id = ?", depID).Update("started_at", now).Error } func MarkDeploymentActive(depID int64, appID int64) error { - _, err := db.Exec(`UPDATE deployments SET is_active = 0 WHERE app_id = ?`, appID) - if err != nil { - return err - } - - _, err = db.Exec(`UPDATE deployments SET is_active = 1 WHERE id = ?`, depID) - return err + return db.Transaction(func(tx *gorm.DB) error { + err := tx.Model(&Deployment{}).Where("app_id=?", appID).Update("is_active", false).Error + if err != nil { + return err + } + err = tx.Model(&Deployment{}).Where("id = ?", depID).Update("is_active", true).Error + if err != nil { + return err + } + return nil + }) } - func UpdateContainerInfo(depID int64, containerID, containerName, imageTag string) error { - query := ` - UPDATE deployments - SET container_id = ?, container_name = ?, image_tag = ? - WHERE id = ? - ` - _, err := db.Exec(query, containerID, containerName, imageTag, depID) - return err + updates := map[string]interface{}{ + "container_id": containerID, + "container_name": containerName, + "image_tag": imageTag, + } + return db.Model(&Deployment{}).Where("id = ?", depID).Updates(updates).Error } -func GetIncompleteDeployments() ([]Deployment, error) { - query := ` - SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, - deployment_number, container_id, container_name, image_tag, - logs, build_logs_path, status, stage, progress, error_message, - created_at, started_at, finished_at, duration, is_active, rolled_back_from - FROM deployments - WHERE status = 'bulding' OR status = 'deploying' ? - ORDER BY created_at DESC - ` - - rows, err := db.Query(query) - if err != nil { - return nil, err +func GetDeploymentStatus(depID int64) (string, error) { + var status string + result := db.Model(&Deployment{}).Select("status").Where("id = ?", depID).Scan(&status) + if result.Error != nil { + return "", result.Error } - defer rows.Close() - var deployments []Deployment - for rows.Next() { - var d Deployment - if err := rows.Scan( - &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, - &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, - &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, - &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, - &d.IsActive, &d.RolledBackFrom, - ); err != nil { - return nil, err - } - deployments = append(deployments, d) + if result.RowsAffected == 0 { + return "", gorm.ErrRecordNotFound } - if err := rows.Err(); err != nil { + return status, nil +} + +//############################################################################################################# +//ARCHIVED CODE BELOW------> + +// package models + +// import ( +// "time" + +// "github.com/corecollectives/mist/utils" +// ) + +// type DeploymentStatus string + +// const ( +// DeploymentStatusPending DeploymentStatus = "pending" +// DeploymentStatusBuilding DeploymentStatus = "building" +// DeploymentStatusDeploying DeploymentStatus = "deploying" +// DeploymentStatusSuccess DeploymentStatus = "success" +// DeploymentStatusFailed DeploymentStatus = "failed" +// DeploymentStatusStopped DeploymentStatus = "stopped" +// DeploymentStatusRolledBack DeploymentStatus = "rolled_back" +// ) + +// type Deployment struct { +// ID int64 `db:"id" json:"id"` +// AppID int64 `db:"app_id" json:"app_id"` +// CommitHash string `db:"commit_hash" json:"commit_hash"` +// CommitMessage *string `db:"commit_message" json:"commit_message,omitempty"` +// CommitAuthor *string `db:"commit_author" json:"commit_author,omitempty"` +// TriggeredBy *int64 `db:"triggered_by" json:"triggered_by,omitempty"` +// DeploymentNumber *int `db:"deployment_number" json:"deployment_number,omitempty"` +// ContainerID *string `db:"container_id" json:"container_id,omitempty"` +// ContainerName *string `db:"container_name" json:"container_name,omitempty"` +// ImageTag *string `db:"image_tag" json:"image_tag,omitempty"` +// Logs *string `db:"logs" json:"logs,omitempty"` +// BuildLogsPath *string `db:"build_logs_path" json:"build_logs_path,omitempty"` +// Status DeploymentStatus `db:"status" json:"status"` +// Stage string `db:"stage" json:"stage"` +// Progress int `db:"progress" json:"progress"` +// ErrorMessage *string `db:"error_message" json:"error_message,omitempty"` +// CreatedAt time.Time `db:"created_at" json:"created_at"` +// StartedAt *time.Time `db:"started_at" json:"started_at,omitempty"` +// FinishedAt *time.Time `db:"finished_at" json:"finished_at,omitempty"` +// Duration *int `db:"duration" json:"duration,omitempty"` +// IsActive bool `db:"is_active" json:"is_active"` +// RolledBackFrom *int64 `db:"rolled_back_from" json:"rolled_back_from,omitempty"` +// } + +// func (d *Deployment) ToJson() map[string]interface{} { +// return map[string]interface{}{ +// "id": d.ID, +// "appId": d.AppID, +// "commitHash": d.CommitHash, +// "commitMessage": d.CommitMessage, +// "commitAuthor": d.CommitAuthor, +// "triggeredBy": d.TriggeredBy, +// "deploymentNumber": d.DeploymentNumber, +// "containerId": d.ContainerID, +// "containerName": d.ContainerName, +// "imageTag": d.ImageTag, +// "logs": d.Logs, +// "buildLogsPath": d.BuildLogsPath, +// "status": d.Status, +// "stage": d.Stage, +// "progress": d.Progress, +// "errorMessage": d.ErrorMessage, +// "createdAt": d.CreatedAt, +// "startedAt": d.StartedAt, +// "finishedAt": d.FinishedAt, +// "duration": d.Duration, +// "isActive": d.IsActive, +// "rolledBackFrom": d.RolledBackFrom, +// } +// } + +// func GetDeploymentsByAppID(appID int64) ([]Deployment, error) { +// query := ` +// SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, +// deployment_number, container_id, container_name, image_tag, +// logs, build_logs_path, status, stage, progress, error_message, +// created_at, started_at, finished_at, duration, is_active, rolled_back_from +// FROM deployments +// WHERE app_id = ? +// ORDER BY created_at DESC +// ` + +// rows, err := db.Query(query, appID) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// var deployments []Deployment +// for rows.Next() { +// var d Deployment +// if err := rows.Scan( +// &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, +// &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, +// &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, +// &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, +// &d.IsActive, &d.RolledBackFrom, +// ); err != nil { +// return nil, err +// } +// deployments = append(deployments, d) +// } + +// if err := rows.Err(); err != nil { +// return nil, err +// } + +// return deployments, nil +// } + +// func (d *Deployment) CreateDeployment() error { +// id := utils.GenerateRandomId() +// d.ID = id + +// var maxDeploymentNum int +// err := db.QueryRow(`SELECT COALESCE(MAX(deployment_number), 0) FROM deployments WHERE app_id = ?`, d.AppID).Scan(&maxDeploymentNum) +// if err == nil { +// deploymentNum := maxDeploymentNum + 1 +// d.DeploymentNumber = &deploymentNum +// } + +// query := ` +// INSERT INTO deployments ( +// id, app_id, commit_hash, commit_message, commit_author, triggered_by, +// deployment_number, status, stage, progress +// ) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', 'pending', 0) +// RETURNING created_at +// ` +// err = db.QueryRow(query, d.ID, d.AppID, d.CommitHash, d.CommitMessage, +// d.CommitAuthor, d.TriggeredBy, d.DeploymentNumber).Scan(&d.CreatedAt) +// if err != nil { +// return err +// } + +// d.Status = DeploymentStatusPending +// d.Stage = "pending" +// d.Progress = 0 +// d.IsActive = false +// return nil +// } + +// func GetDeploymentByID(depID int64) (*Deployment, error) { +// query := ` +// SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, +// deployment_number, container_id, container_name, image_tag, +// logs, build_logs_path, status, stage, progress, error_message, +// created_at, started_at, finished_at, duration, is_active, rolled_back_from +// FROM deployments +// WHERE id = ? +// ` + +// var d Deployment +// if err := db.QueryRow(query, depID).Scan( +// &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, +// &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, +// &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, +// &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, +// &d.IsActive, &d.RolledBackFrom, +// ); err != nil { +// return nil, err +// } + +// return &d, nil +// } + +// func GetDeploymentByCommitHash(commitHash string) (*Deployment, error) { +// query := ` +// SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, +// deployment_number, container_id, container_name, image_tag, +// logs, build_logs_path, status, stage, progress, error_message, +// created_at, started_at, finished_at, duration, is_active, rolled_back_from +// FROM deployments +// WHERE commit_hash = ? +// LIMIT 1 +// ` +// var d Deployment +// if err := db.QueryRow(query, commitHash).Scan( +// &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, +// &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, +// &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, +// &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, +// &d.IsActive, &d.RolledBackFrom, +// ); err != nil { +// return nil, err +// } + +// return &d, nil + +// } + +// func GetDeploymentByAppIDAndCommitHash(appID int64, commitHash string) (*Deployment, error) { +// query := ` +// SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, +// deployment_number, container_id, container_name, image_tag, +// logs, build_logs_path, status, stage, progress, error_message, +// created_at, started_at, finished_at, duration, is_active, rolled_back_from +// FROM deployments +// WHERE app_id = ? AND commit_hash = ? +// LIMIT 1 +// ` +// var d Deployment +// if err := db.QueryRow(query, appID, commitHash).Scan( +// &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, +// &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, +// &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, +// &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, +// &d.IsActive, &d.RolledBackFrom, +// ); err != nil { +// return nil, err +// } + +// return &d, nil + +// } + +// func GetActiveDeploymentByAppID(appID int64) (*Deployment, error) { +// query := ` +// SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, +// deployment_number, container_id, container_name, image_tag, +// logs, build_logs_path, status, stage, progress, error_message, +// created_at, started_at, finished_at, duration, is_active, rolled_back_from +// FROM deployments +// WHERE app_id = ? AND is_active = 1 +// LIMIT 1 +// ` + +// var d Deployment +// if err := db.QueryRow(query, appID).Scan( +// &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, +// &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, +// &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, +// &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, +// &d.IsActive, &d.RolledBackFrom, +// ); err != nil { +// return nil, err +// } + +// return &d, nil +// } + +// func GetCommitHashByDeploymentID(depID int64) (string, error) { +// var commitHash string +// err := db.QueryRow(`SELECT commit_hash FROM deployments WHERE id = ?`, depID).Scan(&commitHash) +// if err != nil { +// return "", err +// } +// return commitHash, nil +// } + +// func UpdateDeploymentStatus(depID int64, status, stage string, progress int, errorMsg *string) error { +// query := ` +// UPDATE deployments +// SET status = ?, stage = ?, progress = ?, error_message = ?, +// finished_at = CASE WHEN ? IN ('success', 'failed', 'stopped') THEN CURRENT_TIMESTAMP ELSE finished_at END, +// duration = CASE WHEN ? IN ('success', 'failed', 'stopped') THEN +// CAST((julianday(CURRENT_TIMESTAMP) - julianday(started_at)) * 86400 AS INTEGER) +// ELSE duration END +// WHERE id = ? +// ` +// _, err := db.Exec(query, status, stage, progress, errorMsg, status, status, depID) +// return err +// } + +// func MarkDeploymentStarted(depID int64) error { +// query := `UPDATE deployments SET started_at = CURRENT_TIMESTAMP WHERE id = ?` +// _, err := db.Exec(query, depID) +// return err +// } + +// func MarkDeploymentActive(depID int64, appID int64) error { +// _, err := db.Exec(`UPDATE deployments SET is_active = 0 WHERE app_id = ?`, appID) +// if err != nil { +// return err +// } + +// _, err = db.Exec(`UPDATE deployments SET is_active = 1 WHERE id = ?`, depID) +// return err +// } + +// func UpdateContainerInfo(depID int64, containerID, containerName, imageTag string) error { +// query := ` +// UPDATE deployments +// SET container_id = ?, container_name = ?, image_tag = ? +// WHERE id = ? +// ` +// _, err := db.Exec(query, containerID, containerName, imageTag, depID) +// return err +// } +func GetIncompleteDeployments() ([]Deployment, error) { + var deployments []Deployment + + err := db. + Where("status IN ?", []string{"building", "deploying"}). + Order("created_at DESC"). + Find(&deployments).Error + + if err != nil { return nil, err } return deployments, nil + // query := ` + // SELECT id, app_id, commit_hash, commit_message, commit_author, triggered_by, + // deployment_number, container_id, container_name, image_tag, + // logs, build_logs_path, status, stage, progress, error_message, + // created_at, started_at, finished_at, duration, is_active, rolled_back_from + // FROM deployments + // WHERE status = 'building' OR status = 'deploying' + // ORDER BY created_at DESC + // ` + // + // rows, err := db.Query(query) + // if err != nil { + // return nil, err + // } + // defer rows.Close() + // + // var deployments []Deployment + // for rows.Next() { + // var d Deployment + // if err := rows.Scan( + // &d.ID, &d.AppID, &d.CommitHash, &d.CommitMessage, &d.CommitAuthor, + // &d.TriggeredBy, &d.DeploymentNumber, &d.ContainerID, &d.ContainerName, + // &d.ImageTag, &d.Logs, &d.BuildLogsPath, &d.Status, &d.Stage, &d.Progress, + // &d.ErrorMessage, &d.CreatedAt, &d.StartedAt, &d.FinishedAt, &d.Duration, + // &d.IsActive, &d.RolledBackFrom, + // ); err != nil { + // return nil, err + // } + // deployments = append(deployments, d) + // } + // + // if err := rows.Err(); err != nil { + // return nil, err + // } + // + // return deployments, nil } diff --git a/server/models/domain.go b/server/models/domain.go index b569686..24a5f78 100644 --- a/server/models/domain.go +++ b/server/models/domain.go @@ -6,133 +6,271 @@ import ( "github.com/corecollectives/mist/utils" ) +type sslStatus string +type sslProvider string +type acmeChallengeType string + +const ( + SSLStatusPending sslStatus = "pending" + SSLStatusActive sslStatus = "active" + SSLStatusFailed sslStatus = "failed" + SSLStatusDisabled sslStatus = "disabled" + SSLStatusExpired sslStatus = "expired" + + SSLProvider sslProvider = "letsencrypt" + SSLProviderCustom sslProvider = "custom" + SSLProviderNone sslProvider = "none" + + AcmeChallengeTypeHttp01 acmeChallengeType = "http-01" + AcmeChallengeTypeDns01 acmeChallengeType = "dns-01" + AcmeChallengeTypeTlsAlpn01 acmeChallengeType = "tls-alpn-01" +) + type Domain struct { - ID int64 `json:"id"` - AppID int64 `json:"appId"` - Domain string `json:"domain"` - SslStatus string `json:"sslStatus"` - DnsConfigured bool `json:"dnsConfigured"` - DnsVerifiedAt *time.Time `json:"dnsVerifiedAt"` - LastDnsCheck *time.Time `json:"lastDnsCheck"` - DnsCheckError *string `json:"dnsCheckError"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID int64 `gorm:"primaryKey;autoIncrement:false" json:"id"` + + AppID int64 `gorm:"index;not null;constraint:OnDelete:CASCADE" json:"appId"` + + Domain string `gorm:"column:domain_name;uniqueIndex;not null" json:"domain"` + + SslStatus sslStatus `gorm:"default:'pending';index" json:"sslStatus"` + SslProvider sslProvider `gorm:"default:'letsencrypt'" json:"sslProvider,omitempty"` + + CertificatePath *string `json:"certificatePath,omitempty"` + CertificateData *string `json:"-"` + KeyPath *string `json:"keyPath,omitempty"` + KeyData *string `json:"-"` + ChainPath *string `json:"chainPath,omitempty"` + + AcmeAccountUrl *string `json:"acmeAccountUrl,omitempty"` + AcmeChallengeType acmeChallengeType `json:"acmeChallengeType,omitempty"` + + Issuer *string `json:"issuer,omitempty"` + IssuedAt *time.Time `json:"issuedAt,omitempty"` + ExpiresAt *time.Time `gorm:"index" json:"expiresAt,omitempty"` + LastRenewalAttempt *time.Time `json:"lastRenewalAttempt,omitempty"` + RenewalError *string `json:"renewalError,omitempty"` + + AutoRenew bool `gorm:"default:true" json:"autoRenew"` + ForceHttps bool `gorm:"default:false" json:"forceHttps"` + HstsEnabled bool `gorm:"default:false" json:"hstsEnabled"` + HstsMaxAge int `gorm:"default:31536000" json:"hstsMaxAge"` + RedirectWww bool `gorm:"default:false" json:"redirectWww"` + RedirectWwwToRoot bool `gorm:"default:true" json:"redirectWwwToRoot"` + + DnsConfigured bool `gorm:"default:false" json:"dnsConfigured"` + DnsVerifiedAt *time.Time `json:"dnsVerifiedAt,omitempty"` + LastDnsCheck *time.Time `json:"lastDnsCheck,omitempty"` + DnsCheckError *string `json:"dnsCheckError,omitempty"` + + CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updatedAt"` } func CreateDomain(appID int64, domain string) (*Domain, error) { - id := utils.GenerateRandomId() - query := ` - INSERT INTO domains (id, app_id, domain_name, ssl_status, dns_configured, created_at, updated_at) - VALUES (?, ?, ?, 'pending', 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) - RETURNING id, app_id, domain_name, ssl_status, dns_configured, dns_verified_at, last_dns_check, dns_check_error, created_at, updated_at - ` var d Domain - err := db.QueryRow(query, id, appID, domain).Scan( - &d.ID, &d.AppID, &d.Domain, &d.SslStatus, &d.DnsConfigured, &d.DnsVerifiedAt, &d.LastDnsCheck, &d.DnsCheckError, &d.CreatedAt, &d.UpdatedAt, - ) - if err != nil { - return nil, err + id := utils.GenerateRandomId() + d.ID = id + d.AppID = appID + d.Domain = domain + result := db.Create(&d) + if result.Error != nil { + return nil, result.Error } return &d, nil } - func GetDomainsByAppID(appID int64) ([]Domain, error) { - query := ` - SELECT id, app_id, domain_name, ssl_status, dns_configured, dns_verified_at, last_dns_check, dns_check_error, created_at, updated_at - FROM domains - WHERE app_id = ? - ORDER BY created_at ASC - ` - rows, err := db.Query(query, appID) - if err != nil { - return nil, err - } - defer rows.Close() - var domains []Domain - for rows.Next() { - var d Domain - err := rows.Scan(&d.ID, &d.AppID, &d.Domain, &d.SslStatus, &d.DnsConfigured, &d.DnsVerifiedAt, &d.LastDnsCheck, &d.DnsCheckError, &d.CreatedAt, &d.UpdatedAt) - if err != nil { - return nil, err - } - domains = append(domains, d) + result := db.Where("app_id = ?", appID).Order("created_at ASC").Find(&domains) + if result.Error != nil { + return nil, result.Error } return domains, nil } func GetPrimaryDomainByAppID(appID int64) (*Domain, error) { - query := ` - SELECT id, app_id, domain_name, ssl_status, dns_configured, dns_verified_at, last_dns_check, dns_check_error, created_at, updated_at - FROM domains - WHERE app_id = ? - ORDER BY created_at ASC - LIMIT 1 - ` var d Domain - err := db.QueryRow(query, appID).Scan(&d.ID, &d.AppID, &d.Domain, &d.SslStatus, &d.DnsConfigured, &d.DnsVerifiedAt, &d.LastDnsCheck, &d.DnsCheckError, &d.CreatedAt, &d.UpdatedAt) - if err != nil { - return nil, err + result := db.Where("app_id = ?", appID).Order("created_at ASC").First(&d) + if result.Error != nil { + return nil, result.Error } return &d, nil } func UpdateDomain(id int64, domain string) error { - query := ` - UPDATE domains - SET domain_name = ?, updated_at = CURRENT_TIMESTAMP - WHERE id = ? - ` - _, err := db.Exec(query, domain, id) - return err + result := db.Model(&Domain{}).Where("id=?", id).Update("domain_name", domain) + return result.Error } func DeleteDomain(id int64) error { - query := `DELETE FROM domains WHERE id = ?` - _, err := db.Exec(query, id) - return err + result := db.Delete(&Domain{}, id) + return result.Error } func GetDomainByID(id int64) (*Domain, error) { - query := ` - SELECT id, app_id, domain_name, ssl_status, dns_configured, dns_verified_at, last_dns_check, dns_check_error, created_at, updated_at - FROM domains - WHERE id = ? - ` var d Domain - err := db.QueryRow(query, id).Scan(&d.ID, &d.AppID, &d.Domain, &d.SslStatus, &d.DnsConfigured, &d.DnsVerifiedAt, &d.LastDnsCheck, &d.DnsCheckError, &d.CreatedAt, &d.UpdatedAt) - if err != nil { - return nil, err + result := db.First(&d, id) + if result.Error != nil { + return nil, result.Error } return &d, nil } func UpdateDomainDnsStatus(id int64, configured bool, errorMsg *string) error { - var query string - var err error + updates := map[string]interface{}{ + "last_dns_check": time.Now(), + } if configured { - query = ` - UPDATE domains - SET dns_configured = 1, - dns_verified_at = CURRENT_TIMESTAMP, - last_dns_check = CURRENT_TIMESTAMP, - dns_check_error = NULL, - updated_at = CURRENT_TIMESTAMP - WHERE id = ? - ` - _, err = db.Exec(query, id) + updates["dns_configured"] = true + updates["dns_verified_at"] = time.Now() + updates["dns_check_error"] = nil } else { - query = ` - UPDATE domains - SET dns_configured = 0, - last_dns_check = CURRENT_TIMESTAMP, - dns_check_error = ?, - updated_at = CURRENT_TIMESTAMP - WHERE id = ? - ` - _, err = db.Exec(query, errorMsg, id) + updates["dns_configured"] = false + updates["dns_check_error"] = errorMsg } - return err + return db.Model(&Domain{ID: id}).Updates(updates).Error } + +//############################################################################################################################################# +//ARCHIVED CODE BELOW-------> + +// package models + +// import ( +// "time" + +// "github.com/corecollectives/mist/utils" +// ) + +// type Domain struct { +// ID int64 `json:"id"` +// AppID int64 `json:"appId"` +// Domain string `json:"domain"` +// SslStatus string `json:"sslStatus"` +// DnsConfigured bool `json:"dnsConfigured"` +// DnsVerifiedAt *time.Time `json:"dnsVerifiedAt"` +// LastDnsCheck *time.Time `json:"lastDnsCheck"` +// DnsCheckError *string `json:"dnsCheckError"` +// CreatedAt time.Time `json:"createdAt"` +// UpdatedAt time.Time `json:"updatedAt"` +// } + +// func CreateDomain(appID int64, domain string) (*Domain, error) { +// id := utils.GenerateRandomId() +// query := ` +// INSERT INTO domains (id, app_id, domain_name, ssl_status, dns_configured, created_at, updated_at) +// VALUES (?, ?, ?, 'pending', 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) +// RETURNING id, app_id, domain_name, ssl_status, dns_configured, dns_verified_at, last_dns_check, dns_check_error, created_at, updated_at +// ` +// var d Domain +// err := db.QueryRow(query, id, appID, domain).Scan( +// &d.ID, &d.AppID, &d.Domain, &d.SslStatus, &d.DnsConfigured, &d.DnsVerifiedAt, &d.LastDnsCheck, &d.DnsCheckError, &d.CreatedAt, &d.UpdatedAt, +// ) +// if err != nil { +// return nil, err +// } +// return &d, nil +// } + +// func GetDomainsByAppID(appID int64) ([]Domain, error) { +// query := ` +// SELECT id, app_id, domain_name, ssl_status, dns_configured, dns_verified_at, last_dns_check, dns_check_error, created_at, updated_at +// FROM domains +// WHERE app_id = ? +// ORDER BY created_at ASC +// ` +// rows, err := db.Query(query, appID) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// var domains []Domain +// for rows.Next() { +// var d Domain +// err := rows.Scan(&d.ID, &d.AppID, &d.Domain, &d.SslStatus, &d.DnsConfigured, &d.DnsVerifiedAt, &d.LastDnsCheck, &d.DnsCheckError, &d.CreatedAt, &d.UpdatedAt) +// if err != nil { +// return nil, err +// } +// domains = append(domains, d) +// } +// return domains, nil +// } + +// func GetPrimaryDomainByAppID(appID int64) (*Domain, error) { +// query := ` +// SELECT id, app_id, domain_name, ssl_status, dns_configured, dns_verified_at, last_dns_check, dns_check_error, created_at, updated_at +// FROM domains +// WHERE app_id = ? +// ORDER BY created_at ASC +// LIMIT 1 +// ` +// var d Domain +// err := db.QueryRow(query, appID).Scan(&d.ID, &d.AppID, &d.Domain, &d.SslStatus, &d.DnsConfigured, &d.DnsVerifiedAt, &d.LastDnsCheck, &d.DnsCheckError, &d.CreatedAt, &d.UpdatedAt) +// if err != nil { +// return nil, err +// } +// return &d, nil +// } + +// func UpdateDomain(id int64, domain string) error { +// query := ` +// UPDATE domains +// SET domain_name = ?, updated_at = CURRENT_TIMESTAMP +// WHERE id = ? +// ` +// _, err := db.Exec(query, domain, id) +// return err +// } + +// func DeleteDomain(id int64) error { +// query := `DELETE FROM domains WHERE id = ?` +// _, err := db.Exec(query, id) +// return err +// } + +// func GetDomainByID(id int64) (*Domain, error) { +// query := ` +// SELECT id, app_id, domain_name, ssl_status, dns_configured, dns_verified_at, last_dns_check, dns_check_error, created_at, updated_at +// FROM domains +// WHERE id = ? +// ` +// var d Domain +// err := db.QueryRow(query, id).Scan(&d.ID, &d.AppID, &d.Domain, &d.SslStatus, &d.DnsConfigured, &d.DnsVerifiedAt, &d.LastDnsCheck, &d.DnsCheckError, &d.CreatedAt, &d.UpdatedAt) +// if err != nil { +// return nil, err +// } +// return &d, nil +// } + +// func UpdateDomainDnsStatus(id int64, configured bool, errorMsg *string) error { +// var query string +// var err error + +// if configured { +// query = ` +// UPDATE domains +// SET dns_configured = 1, +// dns_verified_at = CURRENT_TIMESTAMP, +// last_dns_check = CURRENT_TIMESTAMP, +// dns_check_error = NULL, +// updated_at = CURRENT_TIMESTAMP +// WHERE id = ? +// ` +// _, err = db.Exec(query, id) +// } else { +// query = ` +// UPDATE domains +// SET dns_configured = 0, +// last_dns_check = CURRENT_TIMESTAMP, +// dns_check_error = ?, +// updated_at = CURRENT_TIMESTAMP +// WHERE id = ? +// ` +// _, err = db.Exec(query, errorMsg, id) +// } + +// return err +// } diff --git a/server/models/envVariable.go b/server/models/envVariable.go index 20f33c3..c37bafa 100644 --- a/server/models/envVariable.go +++ b/server/models/envVariable.go @@ -7,82 +7,154 @@ import ( ) type EnvVariable struct { - ID int64 `json:"id"` - AppID int64 `json:"appId"` - Key string `json:"key"` - Value string `json:"value"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID int64 `gorm:"primaryKey;autoIncrement:false" json:"id"` + + AppID int64 `gorm:"uniqueIndex:idx_app_key;index;not null;constraint:OnDelete:CASCADE" json:"appId"` + + Key string `gorm:"uniqueIndex:idx_app_key;not null" json:"key"` + + Value string `gorm:"not null" json:"value"` + + IsSecret bool `gorm:"default:false" json:"isSecret,omitempty"` + + CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updatedAt"` +} + +func (EnvVariable) TableName() string { + return "envs" } func CreateEnvVariable(appID int64, key, value string) (*EnvVariable, error) { - id := utils.GenerateRandomId() - query := ` - INSERT INTO envs (id, app_id, key, value, created_at, updated_at) - VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) - RETURNING id, app_id, key, value, created_at, updated_at - ` - var env EnvVariable - err := db.QueryRow(query, id, appID, key, value).Scan( - &env.ID, &env.AppID, &env.Key, &env.Value, &env.CreatedAt, &env.UpdatedAt, - ) - if err != nil { - return nil, err + env := &EnvVariable{ + ID: utils.GenerateRandomId(), + AppID: appID, + Key: key, + Value: value, } - return &env, nil -} -func GetEnvVariablesByAppID(appID int64) ([]EnvVariable, error) { - query := ` - SELECT id, app_id, key, value, created_at, updated_at - FROM envs - WHERE app_id = ? - ORDER BY key ASC - ` - rows, err := db.Query(query, appID) - if err != nil { - return nil, err + result := db.Create(env) + if result.Error != nil { + return nil, result.Error } - defer rows.Close() + return env, nil +} +func GetEnvVariablesByAppID(appID int64) ([]EnvVariable, error) { var envs []EnvVariable - for rows.Next() { - var env EnvVariable - err := rows.Scan(&env.ID, &env.AppID, &env.Key, &env.Value, &env.CreatedAt, &env.UpdatedAt) - if err != nil { - return nil, err - } - envs = append(envs, env) - } - return envs, nil + result := db.Where("app_id = ?", appID).Order("key ASC").Find(&envs) + return envs, result.Error } func UpdateEnvVariable(id int64, key, value string) error { - query := ` - UPDATE envs - SET key = ?, value = ?, updated_at = CURRENT_TIMESTAMP - WHERE id = ? - ` - _, err := db.Exec(query, key, value, id) - return err + updates := map[string]interface{}{ + "key": key, + "value": value, + } + return db.Model(&EnvVariable{ID: id}).Updates(updates).Error } func DeleteEnvVariable(id int64) error { - query := `DELETE FROM envs WHERE id = ?` - _, err := db.Exec(query, id) - return err + return db.Delete(&EnvVariable{}, id).Error } func GetEnvVariableByID(id int64) (*EnvVariable, error) { - query := ` - SELECT id, app_id, key, value, created_at, updated_at - FROM envs - WHERE id = ? - ` var env EnvVariable - err := db.QueryRow(query, id).Scan(&env.ID, &env.AppID, &env.Key, &env.Value, &env.CreatedAt, &env.UpdatedAt) - if err != nil { - return nil, err + result := db.First(&env, id) + if result.Error != nil { + return nil, result.Error } return &env, nil } + +//########################################################################################################## +//ARCHIVED CODE BELOW + +// package models + +// import ( +// "time" + +// "github.com/corecollectives/mist/utils" +// ) + +// type EnvVariable struct { +// ID int64 `json:"id"` +// AppID int64 `json:"appId"` +// Key string `json:"key"` +// Value string `json:"value"` +// CreatedAt time.Time `json:"createdAt"` +// UpdatedAt time.Time `json:"updatedAt"` +// } + +// func CreateEnvVariable(appID int64, key, value string) (*EnvVariable, error) { +// id := utils.GenerateRandomId() +// query := ` +// INSERT INTO envs (id, app_id, key, value, created_at, updated_at) +// VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) +// RETURNING id, app_id, key, value, created_at, updated_at +// ` +// var env EnvVariable +// err := db.QueryRow(query, id, appID, key, value).Scan( +// &env.ID, &env.AppID, &env.Key, &env.Value, &env.CreatedAt, &env.UpdatedAt, +// ) +// if err != nil { +// return nil, err +// } +// return &env, nil +// } + +// func GetEnvVariablesByAppID(appID int64) ([]EnvVariable, error) { +// query := ` +// SELECT id, app_id, key, value, created_at, updated_at +// FROM envs +// WHERE app_id = ? +// ORDER BY key ASC +// ` +// rows, err := db.Query(query, appID) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// var envs []EnvVariable +// for rows.Next() { +// var env EnvVariable +// err := rows.Scan(&env.ID, &env.AppID, &env.Key, &env.Value, &env.CreatedAt, &env.UpdatedAt) +// if err != nil { +// return nil, err +// } +// envs = append(envs, env) +// } +// return envs, nil +// } + +// func UpdateEnvVariable(id int64, key, value string) error { +// query := ` +// UPDATE envs +// SET key = ?, value = ?, updated_at = CURRENT_TIMESTAMP +// WHERE id = ? +// ` +// _, err := db.Exec(query, key, value, id) +// return err +// } + +// func DeleteEnvVariable(id int64) error { +// query := `DELETE FROM envs WHERE id = ?` +// _, err := db.Exec(query, id) +// return err +// } + +// func GetEnvVariableByID(id int64) (*EnvVariable, error) { +// query := ` +// SELECT id, app_id, key, value, created_at, updated_at +// FROM envs +// WHERE id = ? +// ` +// var env EnvVariable +// err := db.QueryRow(query, id).Scan(&env.ID, &env.AppID, &env.Key, &env.Value, &env.CreatedAt, &env.UpdatedAt) +// if err != nil { +// return nil, err +// } +// return &env, nil +// } diff --git a/server/models/gitProvider.go b/server/models/gitProvider.go index fbf05f7..a2db968 100644 --- a/server/models/gitProvider.go +++ b/server/models/gitProvider.go @@ -1,7 +1,6 @@ package models import ( - "database/sql" "encoding/json" "encoding/pem" "fmt" @@ -21,36 +20,23 @@ const ( ) type GitProvider struct { - ID int64 `db:"id" json:"id"` - UserID int64 `db:"user_id" json:"user_id"` - Provider GitProviderType `db:"provider" json:"provider"` - AccessToken string `db:"access_token" json:"access_token"` - RefreshToken *string `db:"refresh_token" json:"refresh_token,omitempty"` - ExpiresAt *time.Time `db:"expires_at" json:"expires_at,omitempty"` - Username *string `db:"username" json:"username,omitempty"` - Email *string `db:"email" json:"email,omitempty"` + ID int64 `gorm:"primaryKey;autoIncrement:true" json:"id"` + UserID int64 `gorm:"uniqueIndex:idx_user_provider;index;not null;constraint:OnDelete:CASCADE" json:"user_id"` + Provider GitProviderType `gorm:"uniqueIndex:idx_user_provider;not null" json:"provider"` + AccessToken string `gorm:"not null" json:"access_token"` + RefreshToken *string `json:"refresh_token,omitempty"` + ExpiresAt *time.Time `json:"expires_at,omitempty"` + Username *string `json:"username,omitempty"` + Email *string `json:"email,omitempty"` } func (gp *GitProvider) InsertInDB() error { - query := ` - INSERT INTO git_providers (user_id, provider, access_token, refresh_token, expires_at, username, email) - VALUES (?, ?, ?, ?, ?, ?, ?) - RETURNING id - ` - err := db.QueryRow(query, gp.UserID, gp.Provider, gp.AccessToken, gp.RefreshToken, gp.ExpiresAt, gp.Username, gp.Email).Scan(&gp.ID) - return err + return db.Create(gp).Error } func GetGitProviderByID(id int64) (*GitProvider, error) { var gp GitProvider - query := ` - SELECT id, user_id, provider, access_token, refresh_token, expires_at, username, email - FROM git_providers - WHERE id = ? - ` - err := db.QueryRow(query, id).Scan( - &gp.ID, &gp.UserID, &gp.Provider, &gp.AccessToken, &gp.RefreshToken, &gp.ExpiresAt, &gp.Username, &gp.Email, - ) + err := db.First(&gp, id).Error if err != nil { return nil, err } @@ -59,14 +45,7 @@ func GetGitProviderByID(id int64) (*GitProvider, error) { func GetGitProviderByUserAndProvider(userID int64, provider GitProviderType) (*GitProvider, error) { var gp GitProvider - query := ` - SELECT id, user_id, provider, access_token, refresh_token, expires_at, username, email - FROM git_providers - WHERE user_id = ? AND provider = ? - ` - err := db.QueryRow(query, userID, provider).Scan( - &gp.ID, &gp.UserID, &gp.Provider, &gp.AccessToken, &gp.RefreshToken, &gp.ExpiresAt, &gp.Username, &gp.Email, - ) + err := db.Where("user_id = ? AND provider = ?", userID, provider).First(&gp).Error if err != nil { return nil, err } @@ -75,113 +54,61 @@ func GetGitProviderByUserAndProvider(userID int64, provider GitProviderType) (*G func GetGitProvidersByUser(userID int64) ([]GitProvider, error) { var providers []GitProvider - query := ` - SELECT id, user_id, provider, access_token, refresh_token, expires_at, username, email - FROM git_providers - WHERE user_id = ? - ` - rows, err := db.Query(query, userID) - if err != nil { - return nil, err - } - defer rows.Close() - - for rows.Next() { - var gp GitProvider - err := rows.Scan( - &gp.ID, &gp.UserID, &gp.Provider, &gp.AccessToken, &gp.RefreshToken, &gp.ExpiresAt, &gp.Username, &gp.Email, - ) - if err != nil { - return nil, err - } - providers = append(providers, gp) - } - return providers, rows.Err() + err := db.Where("user_id = ?", userID).Find(&providers).Error + return providers, err } func (gp *GitProvider) UpdateToken(accessToken string, refreshToken *string, expiresAt *time.Time) error { - query := ` - UPDATE git_providers - SET access_token = ?, refresh_token = ?, expires_at = ? - WHERE id = ? - ` - _, err := db.Exec(query, accessToken, refreshToken, expiresAt, gp.ID) - return err + return db.Model(gp).Updates(map[string]interface{}{ + "access_token": accessToken, + "refresh_token": refreshToken, + "expires_at": expiresAt, + }).Error } func DeleteGitProvider(id int64) error { - query := `DELETE FROM git_providers WHERE id = ?` - _, err := db.Exec(query, id) - return err + return db.Delete(&GitProvider{}, id).Error } func GetGitProviderAccessToken(providerID int64) (string, GitProviderType, bool, error) { - var token string - var provider GitProviderType - var expiresAt sql.NullTime - query := `SELECT access_token, provider, expires_at FROM git_providers WHERE id = ?` - err := db.QueryRow(query, providerID).Scan(&token, &provider, &expiresAt) + var gp GitProvider + err := db.Select("access_token, provider, expires_at").First(&gp, providerID).Error if err != nil { return "", "", false, err } - // check if token is expired needsRefresh := false - if expiresAt.Valid && time.Now().After(expiresAt.Time) { + if gp.ExpiresAt != nil && time.Now().After(*gp.ExpiresAt) { needsRefresh = true } - return token, provider, needsRefresh, nil + return gp.AccessToken, gp.Provider, needsRefresh, nil } func GetAppGitInfo(appID int64) (*int64, *string, string, *string, int64, string, error) { - var gitProviderID sql.NullInt64 - var gitRepository sql.NullString - var gitBranch string - var gitCloneURL sql.NullString - var projectID int64 - var appName string - - query := ` - SELECT git_provider_id, git_repository, COALESCE(git_branch, 'main'), git_clone_url, project_id, name - FROM apps - WHERE id = ? - ` - err := db.QueryRow(query, appID).Scan(&gitProviderID, &gitRepository, &gitBranch, &gitCloneURL, &projectID, &appName) + var app App + err := db.Select("git_provider_id, git_repository, git_branch, git_clone_url, project_id, name"). + First(&app, appID).Error + if err != nil { return nil, nil, "", nil, 0, "", err } - var gitProviderIDPtr *int64 - if gitProviderID.Valid { - gitProviderIDPtr = &gitProviderID.Int64 - } - - var gitRepositoryPtr *string - if gitRepository.Valid { - gitRepositoryPtr = &gitRepository.String - } - - var gitCloneURLPtr *string - if gitCloneURL.Valid { - gitCloneURLPtr = &gitCloneURL.String + gitBranch := app.GitBranch + if gitBranch == "" { + gitBranch = "main" } - return gitProviderIDPtr, gitRepositoryPtr, gitBranch, gitCloneURLPtr, projectID, appName, nil + return app.GitProviderID, app.GitRepository, gitBranch, app.GitCloneURL, app.ProjectID, app.Name, nil } -// this is used for migrating old apps that only have git_repository set func UpdateAppGitCloneURL(appID int64, gitCloneURL string, gitProviderID *int64) error { - query := ` - UPDATE apps - SET git_clone_url = ?, git_provider_id = ? - WHERE id = ? - ` - _, err := db.Exec(query, gitCloneURL, gitProviderID, appID) - return err + return db.Model(&App{ID: appID}).Updates(map[string]interface{}{ + "git_clone_url": gitCloneURL, + "git_provider_id": gitProviderID, + }).Error } -// currently only supports GitHub via GitHub App installations func RefreshGitProviderToken(providerID int64) (string, error) { provider, err := GetGitProviderByID(providerID) if err != nil { @@ -191,7 +118,6 @@ func RefreshGitProviderToken(providerID int64) (string, error) { switch provider.Provider { case GitProviderGitHub: return refreshGitHubToken(provider.UserID) - // will be implemented as we add more git-providers case GitProviderGitLab, GitProviderBitbucket, GitProviderGitea: return "", fmt.Errorf("token refresh not implemented for %s", provider.Provider) default: @@ -210,12 +136,12 @@ func refreshGitHubToken(userID int64) (string, error) { return "", fmt.Errorf("failed to get GitHub App credentials: %w", err) } - jwt, err := generateGithubJwt(appID, privateKey) + jwtToken, err := generateGithubJwt(appID, privateKey) if err != nil { return "", fmt.Errorf("failed to generate GitHub JWT: %w", err) } - newToken, newExpiry, err := regenerateGithubInstallationToken(jwt, installation.InstallationID) + newToken, newExpiry, err := regenerateGithubInstallationToken(jwtToken, installation.InstallationID) if err != nil { return "", fmt.Errorf("failed to regenerate installation token: %w", err) } @@ -225,10 +151,9 @@ func refreshGitHubToken(userID int64) (string, error) { return "", fmt.Errorf("failed to update installation token: %w", err) } - // update the git_provider token - err = (&GitProvider{ID: installation.InstallationID}).UpdateToken(newToken, nil, &newExpiry) - if err != nil { - return newToken, nil + var gp GitProvider + if err := db.Where("user_id = ? AND provider = ?", userID, GitProviderGitHub).First(&gp).Error; err == nil { + _ = gp.UpdateToken(newToken, nil, &newExpiry) } return newToken, nil @@ -251,12 +176,7 @@ func generateGithubJwt(appID int64, privateKeyPEM string) (string, error) { "iss": fmt.Sprintf("%d", appID), }) - signedToken, err := token.SignedString(privateKey) - if err != nil { - return "", err - } - - return signedToken, nil + return token.SignedString(privateKey) } func regenerateGithubInstallationToken(appJWT string, installationID int64) (string, time.Time, error) { @@ -266,6 +186,7 @@ func regenerateGithubInstallationToken(appJWT string, installationID int64) (str if err != nil { return "", time.Time{}, err } + req.Header.Set("Authorization", "Bearer "+appJWT) req.Header.Set("Accept", "application/vnd.github+json") @@ -290,3 +211,299 @@ func regenerateGithubInstallationToken(appJWT string, installationID int64) (str return tokenResp.Token, tokenResp.ExpiresAt, nil } + +//######################################################################################################## +//ARCHIVED CODE BELOW + +// package models + +// import ( +// "database/sql" +// "encoding/json" +// "encoding/pem" +// "fmt" +// "net/http" +// "time" + +// "github.com/golang-jwt/jwt" +// ) + +// type GitProviderType string + +// const ( +// GitProviderGitHub GitProviderType = "github" +// GitProviderGitLab GitProviderType = "gitlab" +// GitProviderBitbucket GitProviderType = "bitbucket" +// GitProviderGitea GitProviderType = "gitea" +// ) + +// type GitProvider struct { +// ID int64 `db:"id" json:"id"` +// UserID int64 `db:"user_id" json:"user_id"` +// Provider GitProviderType `db:"provider" json:"provider"` +// AccessToken string `db:"access_token" json:"access_token"` +// RefreshToken *string `db:"refresh_token" json:"refresh_token,omitempty"` +// ExpiresAt *time.Time `db:"expires_at" json:"expires_at,omitempty"` +// Username *string `db:"username" json:"username,omitempty"` +// Email *string `db:"email" json:"email,omitempty"` +// } + +// func (gp *GitProvider) InsertInDB() error { +// query := ` +// INSERT INTO git_providers (user_id, provider, access_token, refresh_token, expires_at, username, email) +// VALUES (?, ?, ?, ?, ?, ?, ?) +// RETURNING id +// ` +// err := db.QueryRow(query, gp.UserID, gp.Provider, gp.AccessToken, gp.RefreshToken, gp.ExpiresAt, gp.Username, gp.Email).Scan(&gp.ID) +// return err +// } + +// func GetGitProviderByID(id int64) (*GitProvider, error) { +// var gp GitProvider +// query := ` +// SELECT id, user_id, provider, access_token, refresh_token, expires_at, username, email +// FROM git_providers +// WHERE id = ? +// ` +// err := db.QueryRow(query, id).Scan( +// &gp.ID, &gp.UserID, &gp.Provider, &gp.AccessToken, &gp.RefreshToken, &gp.ExpiresAt, &gp.Username, &gp.Email, +// ) +// if err != nil { +// return nil, err +// } +// return &gp, nil +// } + +// func GetGitProviderByUserAndProvider(userID int64, provider GitProviderType) (*GitProvider, error) { +// var gp GitProvider +// query := ` +// SELECT id, user_id, provider, access_token, refresh_token, expires_at, username, email +// FROM git_providers +// WHERE user_id = ? AND provider = ? +// ` +// err := db.QueryRow(query, userID, provider).Scan( +// &gp.ID, &gp.UserID, &gp.Provider, &gp.AccessToken, &gp.RefreshToken, &gp.ExpiresAt, &gp.Username, &gp.Email, +// ) +// if err != nil { +// return nil, err +// } +// return &gp, nil +// } + +// func GetGitProvidersByUser(userID int64) ([]GitProvider, error) { +// var providers []GitProvider +// query := ` +// SELECT id, user_id, provider, access_token, refresh_token, expires_at, username, email +// FROM git_providers +// WHERE user_id = ? +// ` +// rows, err := db.Query(query, userID) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// for rows.Next() { +// var gp GitProvider +// err := rows.Scan( +// &gp.ID, &gp.UserID, &gp.Provider, &gp.AccessToken, &gp.RefreshToken, &gp.ExpiresAt, &gp.Username, &gp.Email, +// ) +// if err != nil { +// return nil, err +// } +// providers = append(providers, gp) +// } +// return providers, rows.Err() +// } + +// func (gp *GitProvider) UpdateToken(accessToken string, refreshToken *string, expiresAt *time.Time) error { +// query := ` +// UPDATE git_providers +// SET access_token = ?, refresh_token = ?, expires_at = ? +// WHERE id = ? +// ` +// _, err := db.Exec(query, accessToken, refreshToken, expiresAt, gp.ID) +// return err +// } + +// func DeleteGitProvider(id int64) error { +// query := `DELETE FROM git_providers WHERE id = ?` +// _, err := db.Exec(query, id) +// return err +// } + +// func GetGitProviderAccessToken(providerID int64) (string, GitProviderType, bool, error) { +// var token string +// var provider GitProviderType +// var expiresAt sql.NullTime +// query := `SELECT access_token, provider, expires_at FROM git_providers WHERE id = ?` +// err := db.QueryRow(query, providerID).Scan(&token, &provider, &expiresAt) +// if err != nil { +// return "", "", false, err +// } + +// // check if token is expired +// needsRefresh := false +// if expiresAt.Valid && time.Now().After(expiresAt.Time) { +// needsRefresh = true +// } + +// return token, provider, needsRefresh, nil +// } + +// func GetAppGitInfo(appID int64) (*int64, *string, string, *string, int64, string, error) { +// var gitProviderID sql.NullInt64 +// var gitRepository sql.NullString +// var gitBranch string +// var gitCloneURL sql.NullString +// var projectID int64 +// var appName string + +// query := ` +// SELECT git_provider_id, git_repository, COALESCE(git_branch, 'main'), git_clone_url, project_id, name +// FROM apps +// WHERE id = ? +// ` +// err := db.QueryRow(query, appID).Scan(&gitProviderID, &gitRepository, &gitBranch, &gitCloneURL, &projectID, &appName) +// if err != nil { +// return nil, nil, "", nil, 0, "", err +// } + +// var gitProviderIDPtr *int64 +// if gitProviderID.Valid { +// gitProviderIDPtr = &gitProviderID.Int64 +// } + +// var gitRepositoryPtr *string +// if gitRepository.Valid { +// gitRepositoryPtr = &gitRepository.String +// } + +// var gitCloneURLPtr *string +// if gitCloneURL.Valid { +// gitCloneURLPtr = &gitCloneURL.String +// } + +// return gitProviderIDPtr, gitRepositoryPtr, gitBranch, gitCloneURLPtr, projectID, appName, nil +// } + +// // this is used for migrating old apps that only have git_repository set +// func UpdateAppGitCloneURL(appID int64, gitCloneURL string, gitProviderID *int64) error { +// query := ` +// UPDATE apps +// SET git_clone_url = ?, git_provider_id = ? +// WHERE id = ? +// ` +// _, err := db.Exec(query, gitCloneURL, gitProviderID, appID) +// return err +// } + +// // currently only supports GitHub via GitHub App installations +// func RefreshGitProviderToken(providerID int64) (string, error) { +// provider, err := GetGitProviderByID(providerID) +// if err != nil { +// return "", err +// } + +// switch provider.Provider { +// case GitProviderGitHub: +// return refreshGitHubToken(provider.UserID) +// // will be implemented as we add more git-providers +// case GitProviderGitLab, GitProviderBitbucket, GitProviderGitea: +// return "", fmt.Errorf("token refresh not implemented for %s", provider.Provider) +// default: +// return "", fmt.Errorf("unknown provider type: %s", provider.Provider) +// } +// } + +// func refreshGitHubToken(userID int64) (string, error) { +// installation, err := GetInstallationByUserID(int(userID)) +// if err != nil { +// return "", fmt.Errorf("failed to get GitHub installation: %w", err) +// } + +// appID, privateKey, err := GetGithubAppIDAndPrivateKey() +// if err != nil { +// return "", fmt.Errorf("failed to get GitHub App credentials: %w", err) +// } + +// jwt, err := generateGithubJwt(appID, privateKey) +// if err != nil { +// return "", fmt.Errorf("failed to generate GitHub JWT: %w", err) +// } + +// newToken, newExpiry, err := regenerateGithubInstallationToken(jwt, installation.InstallationID) +// if err != nil { +// return "", fmt.Errorf("failed to regenerate installation token: %w", err) +// } + +// err = UpdateInstallationToken(installation.InstallationID, newToken, newExpiry) +// if err != nil { +// return "", fmt.Errorf("failed to update installation token: %w", err) +// } + +// // update the git_provider token +// err = (&GitProvider{ID: installation.InstallationID}).UpdateToken(newToken, nil, &newExpiry) +// if err != nil { +// return newToken, nil +// } + +// return newToken, nil +// } + +// func generateGithubJwt(appID int64, privateKeyPEM string) (string, error) { +// block, _ := pem.Decode([]byte(privateKeyPEM)) +// if block == nil { +// return "", fmt.Errorf("failed to decode PEM block") +// } + +// privateKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKeyPEM)) +// if err != nil { +// return "", err +// } + +// token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{ +// "iat": time.Now().Unix(), +// "exp": time.Now().Add(10 * time.Minute).Unix(), +// "iss": fmt.Sprintf("%d", appID), +// }) + +// signedToken, err := token.SignedString(privateKey) +// if err != nil { +// return "", err +// } + +// return signedToken, nil +// } + +// func regenerateGithubInstallationToken(appJWT string, installationID int64) (string, time.Time, error) { +// url := fmt.Sprintf("https://api.github.com/app/installations/%d/access_tokens", installationID) + +// req, err := http.NewRequest("POST", url, nil) +// if err != nil { +// return "", time.Time{}, err +// } +// req.Header.Set("Authorization", "Bearer "+appJWT) +// req.Header.Set("Accept", "application/vnd.github+json") + +// resp, err := http.DefaultClient.Do(req) +// if err != nil { +// return "", time.Time{}, err +// } +// defer resp.Body.Close() + +// if resp.StatusCode != http.StatusCreated { +// return "", time.Time{}, fmt.Errorf("failed to create token, status %d", resp.StatusCode) +// } + +// var tokenResp struct { +// Token string `json:"token"` +// ExpiresAt time.Time `json:"expires_at"` +// } + +// if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil { +// return "", time.Time{}, err +// } + +// return tokenResp.Token, tokenResp.ExpiresAt, nil +// } diff --git a/server/models/github.go b/server/models/github.go index d857c02..848db59 100644 --- a/server/models/github.go +++ b/server/models/github.go @@ -1,33 +1,53 @@ package models import ( - "database/sql" + "fmt" "time" + + "gorm.io/gorm" + "gorm.io/gorm/clause" ) type GithubApp struct { - ID int64 `json:"id"` - AppID int64 `json:"appId"` - Name *string `json:"name"` - Slug string `json:"slug"` - ClientID string `json:"clientId"` - ClientSecret string `json:"clientSecret"` - WebhookSecret string `json:"webhookSecret"` - PrivateKey string `json:"privateKey"` - CreatedAt time.Time `json:"createdAt"` + ID int64 `gorm:"primaryKey;autoIncrement:true" json:"id"` + + AppID int64 `gorm:"not null" json:"appId"` + ClientID string `gorm:"not null" json:"clientId"` + ClientSecret string `gorm:"not null" json:"clientSecret"` + WebhookSecret string `gorm:"not null" json:"webhookSecret"` + PrivateKey string `gorm:"not null" json:"privateKey"` + Name *string `json:"name"` + Slug string `gorm:"not null" json:"slug"` + + CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updatedAt"` +} + +func (GithubApp) TableName() string { + return "github_app" +} + +type GithubInstallation struct { + InstallationID int64 `gorm:"primaryKey;autoIncrement:false" json:"installation_id"` + + AccountLogin string `json:"account_login"` + AccountType string `json:"account_type"` + AccessToken string `json:"access_token"` + TokenExpiresAt time.Time `json:"token_expires_at"` + + UserID int `gorm:"index" json:"user_id"` + + CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` } func (app *GithubApp) InsertInDB() error { - _, err := db.Exec(` - INSERT INTO github_app (app_id, client_id, client_secret, webhook_secret, private_key, name, slug) - VALUES (?, ?, ?, ?, ?, ?, ?) - `, app.AppID, app.ClientID, app.ClientSecret, app.WebhookSecret, app.PrivateKey, app.Name, app.Slug) - return err + return db.Create(app).Error } func CheckIfAppExists() (bool, error) { - var count int - err := db.QueryRow(`SELECT COUNT(*) FROM github_app`).Scan(&count) + var count int64 + err := db.Model(&GithubApp{}).Count(&count).Error if err != nil { return false, err } @@ -35,92 +55,45 @@ func CheckIfAppExists() (bool, error) { } func GetApp(userID int) (GithubApp, bool, error) { - query := ` - SELECT - a.id, - a.name, - a.app_id, - a.client_id, - a.slug, - a.created_at, - CASE WHEN i.installation_id IS NOT NULL THEN 1 ELSE 0 END AS is_installed - FROM github_app a - LEFT JOIN github_installations i ON i.user_id = ? - WHERE a.id = 1 - ` - - row := db.QueryRow(query, userID) - var app GithubApp - var isInstalled bool - - err := row.Scan( - &app.ID, - &app.Name, - &app.AppID, - &app.ClientID, - &app.Slug, - &app.CreatedAt, - &isInstalled, - ) + err := db.First(&app, 1).Error if err != nil { - if err == sql.ErrNoRows { + if err == gorm.ErrRecordNotFound { return GithubApp{}, false, nil } return GithubApp{}, false, err } - return app, isInstalled, nil -} + var count int64 + err = db.Model(&GithubInstallation{}). + Where("user_id = ?", userID). + Count(&count).Error -type GithubInstallation struct { - InstallationID int64 `json:"installation_id"` - AccountLogin string `json:"account_login"` - AccountType string `json:"account_type"` - AccessToken string `json:"access_token"` - TokenExpiresAt time.Time `json:"token_expires_at"` - UserID int `json:"user_id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + isInstalled := count > 0 + return app, isInstalled, err } func (i *GithubInstallation) InsertOrReplace() error { - _, err := db.Exec(` - INSERT OR REPLACE INTO github_installations - (installation_id, account_login, account_type, access_token, token_expires_at, user_id, updated_at) - VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) - `, i.InstallationID, i.AccountLogin, i.AccountType, i.AccessToken, i.TokenExpiresAt, i.UserID) - return err + return db.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "installation_id"}}, + UpdateAll: true, + }).Create(i).Error } func GetInstallationID(userID int) (int64, error) { - var installationID int64 - err := db.QueryRow(` - SELECT installation_id FROM github_installations - WHERE user_id = ? - `, userID).Scan(&installationID) + var inst GithubInstallation + err := db.Select("installation_id"). + Where("user_id = ?", userID). + First(&inst).Error if err != nil { return 0, err } - return installationID, nil + return inst.InstallationID, nil } func GetInstallationByUserID(userID int) (GithubInstallation, error) { var inst GithubInstallation - err := db.QueryRow(` - SELECT installation_id, account_login, account_type, access_token, token_expires_at, user_id, created_at, updated_at - FROM github_installations - WHERE user_id = ? - `, userID).Scan( - &inst.InstallationID, - &inst.AccountLogin, - &inst.AccountType, - &inst.AccessToken, - &inst.TokenExpiresAt, - &inst.UserID, - &inst.CreatedAt, - &inst.UpdatedAt, - ) + err := db.Where("user_id = ?", userID).First(&inst).Error if err != nil { return GithubInstallation{}, err } @@ -128,63 +101,253 @@ func GetInstallationByUserID(userID int) (GithubInstallation, error) { } func GetInstallationToken(installationID int64) (string, string, int, error) { - var ( - token string - tokenExpires string - appID int - ) - err := db.QueryRow(` - SELECT i.access_token, i.token_expires_at, a.app_id - FROM github_installations i - JOIN github_app a ON a.id = 1 - WHERE i.installation_id = ? - `, installationID).Scan(&token, &tokenExpires, &appID) + type Result struct { + AccessToken string + TokenExpiresAt time.Time + AppID int + } + + var res Result + err := db.Table("github_installations as i"). + Select("i.access_token, i.token_expires_at, a.app_id"). + Joins("JOIN github_app a ON a.id = 1"). + Where("i.installation_id = ?", installationID). + Scan(&res).Error + if err != nil { return "", "", 0, err } - return token, tokenExpires, appID, nil + + return res.AccessToken, res.TokenExpiresAt.Format(time.RFC3339), res.AppID, nil } func UpdateInstallationToken(installationID int64, token string, newExpiry time.Time) error { - _, err := db.Exec(` - UPDATE github_installations - SET access_token = ?, token_expires_at = ?, updated_at = CURRENT_TIMESTAMP - WHERE installation_id = ? - `, token, newExpiry.Format(time.RFC3339), installationID) - return err + return db.Model(&GithubInstallation{InstallationID: installationID}). + Updates(map[string]interface{}{ + "access_token": token, + "token_expires_at": newExpiry, + "updated_at": time.Now(), + }).Error } func GetGithubAppCredentials(appID int) (string, string, error) { - var appNumericId string - var appPrivateKeyPEM string - - err := db.QueryRow(` - SELECT app_id, private_key FROM github_app WHERE id = 1 - `).Scan(&appNumericId, &appPrivateKeyPEM) - + var app GithubApp + err := db.Select("app_id, private_key").First(&app, 1).Error if err != nil { return "", "", err } - return appNumericId, appPrivateKeyPEM, nil + return fmt.Sprintf("%d", app.AppID), app.PrivateKey, nil } func GetInstallationIDByUserID(userID int64) (int64, error) { - var installationID int64 - err := db.QueryRow(`SELECT installation_id FROM github_installations WHERE user_id = ?`, userID). - Scan(&installationID) + var inst GithubInstallation + err := db.Select("installation_id").Where("user_id = ?", userID).First(&inst).Error if err != nil { return 0, err } - return installationID, nil + return inst.InstallationID, nil } func GetGithubAppIDAndPrivateKey() (int64, string, error) { - var appAppID int64 - var privateKey string - err := db.QueryRow(`SELECT app_id, private_key FROM github_app LIMIT 1`).Scan(&appAppID, &privateKey) + var app GithubApp + err := db.Select("app_id, private_key").First(&app).Error if err != nil { return 0, "", err } - return appAppID, privateKey, nil + return app.AppID, app.PrivateKey, nil } + +//######################################################################################################################## +//ARCHIVED CODE BELOW--------------------------------> + +// package models + +// import ( +// "database/sql" +// "time" +// ) + +// type GithubApp struct { +// ID int64 `json:"id"` +// AppID int64 `json:"appId"` +// Name *string `json:"name"` +// Slug string `json:"slug"` +// ClientID string `json:"clientId"` +// ClientSecret string `json:"clientSecret"` +// WebhookSecret string `json:"webhookSecret"` +// PrivateKey string `json:"privateKey"` +// CreatedAt time.Time `json:"createdAt"` +// } + +// func (app *GithubApp) InsertInDB() error { +// _, err := db.Exec(` +// INSERT INTO github_app (app_id, client_id, client_secret, webhook_secret, private_key, name, slug) +// VALUES (?, ?, ?, ?, ?, ?, ?) +// `, app.AppID, app.ClientID, app.ClientSecret, app.WebhookSecret, app.PrivateKey, app.Name, app.Slug) +// return err +// } + +// func CheckIfAppExists() (bool, error) { +// var count int +// err := db.QueryRow(`SELECT COUNT(*) FROM github_app`).Scan(&count) +// if err != nil { +// return false, err +// } +// return count > 0, nil +// } + +// func GetApp(userID int) (GithubApp, bool, error) { +// query := ` +// SELECT +// a.id, +// a.name, +// a.app_id, +// a.client_id, +// a.slug, +// a.created_at, +// CASE WHEN i.installation_id IS NOT NULL THEN 1 ELSE 0 END AS is_installed +// FROM github_app a +// LEFT JOIN github_installations i ON i.user_id = ? +// WHERE a.id = 1 +// ` + +// row := db.QueryRow(query, userID) + +// var app GithubApp +// var isInstalled bool + +// err := row.Scan( +// &app.ID, +// &app.Name, +// &app.AppID, +// &app.ClientID, +// &app.Slug, +// &app.CreatedAt, +// &isInstalled, +// ) +// if err != nil { +// if err == sql.ErrNoRows { +// return GithubApp{}, false, nil +// } +// return GithubApp{}, false, err +// } + +// return app, isInstalled, nil +// } + +// type GithubInstallation struct { +// InstallationID int64 `json:"installation_id"` +// AccountLogin string `json:"account_login"` +// AccountType string `json:"account_type"` +// AccessToken string `json:"access_token"` +// TokenExpiresAt time.Time `json:"token_expires_at"` +// UserID int `json:"user_id"` +// CreatedAt time.Time `json:"created_at"` +// UpdatedAt time.Time `json:"updated_at"` +// } + +// func (i *GithubInstallation) InsertOrReplace() error { +// _, err := db.Exec(` +// INSERT OR REPLACE INTO github_installations +// (installation_id, account_login, account_type, access_token, token_expires_at, user_id, updated_at) +// VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) +// `, i.InstallationID, i.AccountLogin, i.AccountType, i.AccessToken, i.TokenExpiresAt, i.UserID) +// return err +// } + +// func GetInstallationID(userID int) (int64, error) { +// var installationID int64 +// err := db.QueryRow(` +// SELECT installation_id FROM github_installations +// WHERE user_id = ? +// `, userID).Scan(&installationID) +// if err != nil { +// return 0, err +// } +// return installationID, nil +// } + +// func GetInstallationByUserID(userID int) (GithubInstallation, error) { +// var inst GithubInstallation +// err := db.QueryRow(` +// SELECT installation_id, account_login, account_type, access_token, token_expires_at, user_id, created_at, updated_at +// FROM github_installations +// WHERE user_id = ? +// `, userID).Scan( +// &inst.InstallationID, +// &inst.AccountLogin, +// &inst.AccountType, +// &inst.AccessToken, +// &inst.TokenExpiresAt, +// &inst.UserID, +// &inst.CreatedAt, +// &inst.UpdatedAt, +// ) +// if err != nil { +// return GithubInstallation{}, err +// } +// return inst, nil +// } + +// func GetInstallationToken(installationID int64) (string, string, int, error) { +// var ( +// token string +// tokenExpires string +// appID int +// ) +// err := db.QueryRow(` +// SELECT i.access_token, i.token_expires_at, a.app_id +// FROM github_installations i +// JOIN github_app a ON a.id = 1 +// WHERE i.installation_id = ? +// `, installationID).Scan(&token, &tokenExpires, &appID) +// if err != nil { +// return "", "", 0, err +// } +// return token, tokenExpires, appID, nil +// } + +// func UpdateInstallationToken(installationID int64, token string, newExpiry time.Time) error { +// _, err := db.Exec(` +// UPDATE github_installations +// SET access_token = ?, token_expires_at = ?, updated_at = CURRENT_TIMESTAMP +// WHERE installation_id = ? +// `, token, newExpiry.Format(time.RFC3339), installationID) +// return err +// } + +// func GetGithubAppCredentials(appID int) (string, string, error) { +// var appNumericId string +// var appPrivateKeyPEM string + +// err := db.QueryRow(` +// SELECT app_id, private_key FROM github_app WHERE id = 1 +// `).Scan(&appNumericId, &appPrivateKeyPEM) + +// if err != nil { +// return "", "", err +// } + +// return appNumericId, appPrivateKeyPEM, nil +// } + +// func GetInstallationIDByUserID(userID int64) (int64, error) { +// var installationID int64 +// err := db.QueryRow(`SELECT installation_id FROM github_installations WHERE user_id = ?`, userID). +// Scan(&installationID) +// if err != nil { +// return 0, err +// } +// return installationID, nil +// } + +// func GetGithubAppIDAndPrivateKey() (int64, string, error) { +// var appAppID int64 +// var privateKey string +// err := db.QueryRow(`SELECT app_id, private_key FROM github_app LIMIT 1`).Scan(&appAppID, &privateKey) +// if err != nil { +// return 0, "", err +// } +// return appAppID, privateKey, nil +// } diff --git a/server/models/logs.go b/server/models/logs.go new file mode 100644 index 0000000..662572d --- /dev/null +++ b/server/models/logs.go @@ -0,0 +1,28 @@ +package models + +import "time" + +type LogSource string + +const ( + LogSourceApp LogSource = "app" + LogSourceSystem LogSource = "system" +) + +type LogLevel string + +const ( + LogLevelInfo LogLevel = "info" + LogLevelWarn LogLevel = "warn" + LogLevelError LogLevel = "error" + LogLevelDebug LogLevel = "debug" +) + +type Logs struct { + ID int64 `gorm:"primaryKey;autoIncrement:true"` + Source LogSource `gorm:"not null"` + SourceID *int64 `gorm:"index"` + Message string `gorm:"not null"` + Level LogLevel `gorm:"not null;default:'info'"` + CreatedAt time.Time `gorm:"autoCreateTime"` +} diff --git a/server/models/main.go b/server/models/main.go index 2520495..2785c70 100644 --- a/server/models/main.go +++ b/server/models/main.go @@ -1,9 +1,12 @@ package models -import "database/sql" +import ( -var db *sql.DB + "gorm.io/gorm" +) -func SetDB(database *sql.DB) { +var db *gorm.DB + +func SetDB(database *gorm.DB) { db = database } diff --git a/server/models/notification.go b/server/models/notification.go index a0b5775..6a234d4 100644 --- a/server/models/notification.go +++ b/server/models/notification.go @@ -36,28 +36,39 @@ const ( ) type Notification struct { - ID int64 `db:"id" json:"id"` - UserID *int64 `db:"user_id" json:"userId,omitempty"` - Type NotificationType `db:"type" json:"type"` - Title string `db:"title" json:"title"` - Message string `db:"message" json:"message"` - Link *string `db:"link" json:"link,omitempty"` - ResourceType *string `db:"resource_type" json:"resourceType,omitempty"` - ResourceID *int64 `db:"resource_id" json:"resourceId,omitempty"` - EmailSent bool `db:"email_sent" json:"emailSent"` - EmailSentAt *time.Time `db:"email_sent_at" json:"emailSentAt,omitempty"` - SlackSent bool `db:"slack_sent" json:"slackSent"` - SlackSentAt *time.Time `db:"slack_sent_at" json:"slackSentAt,omitempty"` - DiscordSent bool `db:"discord_sent" json:"discordSent"` - DiscordSentAt *time.Time `db:"discord_sent_at" json:"discordSentAt,omitempty"` - WebhookSent bool `db:"webhook_sent" json:"webhookSent"` - WebhookSentAt *time.Time `db:"webhook_sent_at" json:"webhookSentAt,omitempty"` - IsRead bool `db:"is_read" json:"isRead"` - ReadAt *time.Time `db:"read_at" json:"readAt,omitempty"` - Priority NotificationPriority `db:"priority" json:"priority"` - Metadata *string `db:"metadata" json:"metadata,omitempty"` // JSON - CreatedAt time.Time `db:"created_at" json:"createdAt"` - ExpiresAt *time.Time `db:"expires_at" json:"expiresAt,omitempty"` + ID int64 `gorm:"primaryKey;autoIncrement:false" json:"id"` + + UserID *int64 `gorm:"index;constraint:OnDelete:CASCADE" json:"userId,omitempty"` + + Type NotificationType `gorm:"index;not null" json:"type"` + Title string `gorm:"not null" json:"title"` + Message string `gorm:"not null" json:"message"` + + Link *string `json:"link,omitempty"` + ResourceType *string `json:"resourceType,omitempty"` + ResourceID *int64 `json:"resourceId,omitempty"` + + EmailSent bool `gorm:"default:false" json:"emailSent"` + EmailSentAt *time.Time `json:"emailSentAt,omitempty"` + + SlackSent bool `gorm:"default:false" json:"slackSent"` + SlackSentAt *time.Time `json:"slackSentAt,omitempty"` + + DiscordSent bool `gorm:"default:false" json:"discordSent"` + DiscordSentAt *time.Time `json:"discordSentAt,omitempty"` + + WebhookSent bool `gorm:"default:false" json:"webhookSent"` + WebhookSentAt *time.Time `json:"webhookSentAt,omitempty"` + + IsRead bool `gorm:"default:false;index" json:"isRead"` + ReadAt *time.Time `json:"readAt,omitempty"` + + Priority NotificationPriority `gorm:"default:'normal';index" json:"priority"` + + Metadata *string `json:"metadata,omitempty"` + + CreatedAt time.Time `gorm:"autoCreateTime;index:,sort:desc" json:"createdAt"` + ExpiresAt *time.Time `json:"expiresAt,omitempty"` } func (n *Notification) ToJson() map[string]interface{} { @@ -88,91 +99,232 @@ func (n *Notification) ToJson() map[string]interface{} { } func (n *Notification) InsertInDB() error { - id := utils.GenerateRandomId() - n.ID = id - query := ` - INSERT INTO notifications ( - id, user_id, type, title, message, link, - resource_type, resource_id, priority, metadata, expires_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - RETURNING created_at - ` - err := db.QueryRow(query, n.ID, n.UserID, n.Type, n.Title, n.Message, n.Link, - n.ResourceType, n.ResourceID, n.Priority, n.Metadata, n.ExpiresAt).Scan(&n.CreatedAt) - return err + n.ID = utils.GenerateRandomId() + + if n.Priority == "" { + n.Priority = PriorityNormal + } + + return db.Create(n).Error } func GetNotificationsByUserID(userID int64, unreadOnly bool) ([]Notification, error) { var notifications []Notification - query := ` - SELECT id, user_id, type, title, message, link, resource_type, resource_id, - email_sent, email_sent_at, slack_sent, slack_sent_at, - discord_sent, discord_sent_at, webhook_sent, webhook_sent_at, - is_read, read_at, priority, metadata, created_at, expires_at - FROM notifications - WHERE user_id = ? OR user_id IS NULL - ` + + query := db.Where(db.Where("user_id = ?", userID).Or("user_id IS NULL")) + if unreadOnly { - query += " AND is_read = 0" + query = query.Where("is_read = ?", false) } - query += " ORDER BY created_at DESC LIMIT 100" - rows, err := db.Query(query, userID) - if err != nil { - return nil, err - } - defer rows.Close() - - for rows.Next() { - var notif Notification - err := rows.Scan( - ¬if.ID, ¬if.UserID, ¬if.Type, ¬if.Title, ¬if.Message, - ¬if.Link, ¬if.ResourceType, ¬if.ResourceID, - ¬if.EmailSent, ¬if.EmailSentAt, ¬if.SlackSent, ¬if.SlackSentAt, - ¬if.DiscordSent, ¬if.DiscordSentAt, ¬if.WebhookSent, ¬if.WebhookSentAt, - ¬if.IsRead, ¬if.ReadAt, ¬if.Priority, ¬if.Metadata, - ¬if.CreatedAt, ¬if.ExpiresAt, - ) - if err != nil { - return nil, err - } - notifications = append(notifications, notif) - } + result := query.Order("created_at DESC").Limit(100).Find(¬ifications) - return notifications, rows.Err() + return notifications, result.Error } func (n *Notification) MarkAsRead() error { - query := ` - UPDATE notifications - SET is_read = 1, read_at = CURRENT_TIMESTAMP - WHERE id = ? - ` - _, err := db.Exec(query, n.ID) - return err + return db.Model(n).Updates(map[string]interface{}{ + "is_read": true, + "read_at": time.Now(), + }).Error } func MarkAllAsRead(userID int64) error { - query := ` - UPDATE notifications - SET is_read = 1, read_at = CURRENT_TIMESTAMP - WHERE user_id = ? AND is_read = 0 - ` - _, err := db.Exec(query, userID) - return err + return db.Model(&Notification{}). + Where("user_id = ? AND is_read = ?", userID, false). + Updates(map[string]interface{}{ + "is_read": true, + "read_at": time.Now(), + }).Error } func DeleteNotification(notificationID int64) error { - query := `DELETE FROM notifications WHERE id = ?` - _, err := db.Exec(query, notificationID) - return err + return db.Delete(&Notification{}, notificationID).Error } func DeleteExpiredNotifications() error { - query := ` - DELETE FROM notifications - WHERE expires_at IS NOT NULL AND expires_at < CURRENT_TIMESTAMP - ` - _, err := db.Exec(query) - return err + return db.Where("expires_at IS NOT NULL AND expires_at < ?", time.Now()). + Delete(&Notification{}).Error } + +//###################################################################################################### +//ARCHIVED CODE BELOW + +// package models + +// import ( +// "time" + +// "github.com/corecollectives/mist/utils" +// ) + +// type NotificationType string + +// const ( +// NotificationDeploymentSuccess NotificationType = "deployment_success" +// NotificationDeploymentFailed NotificationType = "deployment_failed" +// NotificationDeploymentStarted NotificationType = "deployment_started" +// NotificationSSLExpiryWarning NotificationType = "ssl_expiry_warning" +// NotificationSSLRenewalSuccess NotificationType = "ssl_renewal_success" +// NotificationSSLRenewalFailed NotificationType = "ssl_renewal_failed" +// NotificationResourceAlert NotificationType = "resource_alert" +// NotificationAppError NotificationType = "app_error" +// NotificationAppStopped NotificationType = "app_stopped" +// NotificationBackupSuccess NotificationType = "backup_success" +// NotificationBackupFailed NotificationType = "backup_failed" +// NotificationUserInvited NotificationType = "user_invited" +// NotificationMemberAdded NotificationType = "member_added" +// NotificationSystemUpdate NotificationType = "system_update" +// NotificationCustom NotificationType = "custom" +// ) + +// type NotificationPriority string + +// const ( +// PriorityLow NotificationPriority = "low" +// PriorityNormal NotificationPriority = "normal" +// PriorityHigh NotificationPriority = "high" +// PriorityUrgent NotificationPriority = "urgent" +// ) + +// type Notification struct { +// ID int64 `db:"id" json:"id"` +// UserID *int64 `db:"user_id" json:"userId,omitempty"` +// Type NotificationType `db:"type" json:"type"` +// Title string `db:"title" json:"title"` +// Message string `db:"message" json:"message"` +// Link *string `db:"link" json:"link,omitempty"` +// ResourceType *string `db:"resource_type" json:"resourceType,omitempty"` +// ResourceID *int64 `db:"resource_id" json:"resourceId,omitempty"` +// EmailSent bool `db:"email_sent" json:"emailSent"` +// EmailSentAt *time.Time `db:"email_sent_at" json:"emailSentAt,omitempty"` +// SlackSent bool `db:"slack_sent" json:"slackSent"` +// SlackSentAt *time.Time `db:"slack_sent_at" json:"slackSentAt,omitempty"` +// DiscordSent bool `db:"discord_sent" json:"discordSent"` +// DiscordSentAt *time.Time `db:"discord_sent_at" json:"discordSentAt,omitempty"` +// WebhookSent bool `db:"webhook_sent" json:"webhookSent"` +// WebhookSentAt *time.Time `db:"webhook_sent_at" json:"webhookSentAt,omitempty"` +// IsRead bool `db:"is_read" json:"isRead"` +// ReadAt *time.Time `db:"read_at" json:"readAt,omitempty"` +// Priority NotificationPriority `db:"priority" json:"priority"` +// Metadata *string `db:"metadata" json:"metadata,omitempty"` // JSON +// CreatedAt time.Time `db:"created_at" json:"createdAt"` +// ExpiresAt *time.Time `db:"expires_at" json:"expiresAt,omitempty"` +// } + +// func (n *Notification) ToJson() map[string]interface{} { +// return map[string]interface{}{ +// "id": n.ID, +// "userId": n.UserID, +// "type": n.Type, +// "title": n.Title, +// "message": n.Message, +// "link": n.Link, +// "resourceType": n.ResourceType, +// "resourceId": n.ResourceID, +// "emailSent": n.EmailSent, +// "emailSentAt": n.EmailSentAt, +// "slackSent": n.SlackSent, +// "slackSentAt": n.SlackSentAt, +// "discordSent": n.DiscordSent, +// "discordSentAt": n.DiscordSentAt, +// "webhookSent": n.WebhookSent, +// "webhookSentAt": n.WebhookSentAt, +// "isRead": n.IsRead, +// "readAt": n.ReadAt, +// "priority": n.Priority, +// "metadata": n.Metadata, +// "createdAt": n.CreatedAt, +// "expiresAt": n.ExpiresAt, +// } +// } + +// func (n *Notification) InsertInDB() error { +// id := utils.GenerateRandomId() +// n.ID = id +// query := ` +// INSERT INTO notifications ( +// id, user_id, type, title, message, link, +// resource_type, resource_id, priority, metadata, expires_at +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// RETURNING created_at +// ` +// err := db.QueryRow(query, n.ID, n.UserID, n.Type, n.Title, n.Message, n.Link, +// n.ResourceType, n.ResourceID, n.Priority, n.Metadata, n.ExpiresAt).Scan(&n.CreatedAt) +// return err +// } + +// func GetNotificationsByUserID(userID int64, unreadOnly bool) ([]Notification, error) { +// var notifications []Notification +// query := ` +// SELECT id, user_id, type, title, message, link, resource_type, resource_id, +// email_sent, email_sent_at, slack_sent, slack_sent_at, +// discord_sent, discord_sent_at, webhook_sent, webhook_sent_at, +// is_read, read_at, priority, metadata, created_at, expires_at +// FROM notifications +// WHERE user_id = ? OR user_id IS NULL +// ` +// if unreadOnly { +// query += " AND is_read = 0" +// } +// query += " ORDER BY created_at DESC LIMIT 100" + +// rows, err := db.Query(query, userID) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// for rows.Next() { +// var notif Notification +// err := rows.Scan( +// ¬if.ID, ¬if.UserID, ¬if.Type, ¬if.Title, ¬if.Message, +// ¬if.Link, ¬if.ResourceType, ¬if.ResourceID, +// ¬if.EmailSent, ¬if.EmailSentAt, ¬if.SlackSent, ¬if.SlackSentAt, +// ¬if.DiscordSent, ¬if.DiscordSentAt, ¬if.WebhookSent, ¬if.WebhookSentAt, +// ¬if.IsRead, ¬if.ReadAt, ¬if.Priority, ¬if.Metadata, +// ¬if.CreatedAt, ¬if.ExpiresAt, +// ) +// if err != nil { +// return nil, err +// } +// notifications = append(notifications, notif) +// } + +// return notifications, rows.Err() +// } + +// func (n *Notification) MarkAsRead() error { +// query := ` +// UPDATE notifications +// SET is_read = 1, read_at = CURRENT_TIMESTAMP +// WHERE id = ? +// ` +// _, err := db.Exec(query, n.ID) +// return err +// } + +// func MarkAllAsRead(userID int64) error { +// query := ` +// UPDATE notifications +// SET is_read = 1, read_at = CURRENT_TIMESTAMP +// WHERE user_id = ? AND is_read = 0 +// ` +// _, err := db.Exec(query, userID) +// return err +// } + +// func DeleteNotification(notificationID int64) error { +// query := `DELETE FROM notifications WHERE id = ?` +// _, err := db.Exec(query, notificationID) +// return err +// } + +// func DeleteExpiredNotifications() error { +// query := ` +// DELETE FROM notifications +// WHERE expires_at IS NOT NULL AND expires_at < CURRENT_TIMESTAMP +// ` +// _, err := db.Exec(query) +// return err +// } diff --git a/server/models/project.go b/server/models/project.go index ed1c3ff..6c1fb70 100644 --- a/server/models/project.go +++ b/server/models/project.go @@ -1,40 +1,63 @@ package models import ( - "database/sql" "strings" "time" "github.com/corecollectives/mist/utils" + "gorm.io/gorm" ) type Project struct { - ID int64 `json:"id"` - Name string `json:"name"` - Description sql.NullString `json:"description"` - Tags []sql.NullString `json:"tags"` - OwnerID int64 `json:"ownerId"` - Owner *User `json:"owner,omitempty"` - ProjectMembers []User `json:"projectMembers"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID int64 `gorm:"primaryKey;autoIncrement:false" json:"id"` + Name string `gorm:"not null" json:"name"` + Description *string `json:"description"` + + TagsString string `gorm:"column:tags" json:"-"` + Tags []string `gorm:"-" json:"tags"` + + OwnerID int64 `gorm:"not null" json:"ownerId"` + Owner *User `gorm:"foreignKey:OwnerID" json:"owner,omitempty"` + + ProjectMembers []User `gorm:"many2many:project_members;" json:"projectMembers"` + + CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updatedAt"` } -func (p *Project) ToJSON() map[string]interface{} { - tags := []string{} - for _, tag := range p.Tags { - if tag.Valid { - tags = append(tags, tag.String) - } +func (p *Project) BeforeSave(tx *gorm.DB) (err error) { + if len(p.Tags) > 0 { + p.TagsString = strings.Join(p.Tags, ",") + } else { + p.TagsString = "" } - if len(tags) == 0 { + return +} + +func (p *Project) AfterFind(tx *gorm.DB) (err error) { + if p.TagsString != "" { + p.Tags = strings.Split(p.TagsString, ",") + } else { + p.Tags = []string{} + } + return +} + +func (p *Project) ToJSON() map[string]interface{} { + tags := p.Tags + if tags == nil { tags = []string{} } + desc := "" + if p.Description != nil { + desc = *p.Description + } + return map[string]interface{}{ "id": p.ID, "name": p.Name, - "description": p.Description.String, + "description": desc, "tags": tags, "ownerId": p.OwnerID, "owner": p.Owner, @@ -47,285 +70,436 @@ func (p *Project) ToJSON() map[string]interface{} { func (p *Project) InsertInDB() error { p.ID = utils.GenerateRandomId() - tagsStr := "" - if len(p.Tags) > 0 { - for i, tag := range p.Tags { - if i > 0 { - tagsStr += "," - } - tagsStr += tag.String - } - } - - var desc interface{} - if p.Description.Valid { - desc = p.Description.String - } else { - desc = nil - } - - query := ` - INSERT INTO projects (id, name, description, tags, owner_id) - VALUES ($1, $2, $3, $4, $5) - RETURNING id, created_at, updated_at - ` - err := db.QueryRow(query, p.ID, p.Name, desc, tagsStr, p.OwnerID).Scan( - &p.ID, &p.CreatedAt, &p.UpdatedAt, - ) - if err != nil { + if err := db.Create(p).Error; err != nil { return err } - _, err = db.Exec(` - INSERT INTO project_members (project_id, user_id) - VALUES ($1, $2) - ON CONFLICT DO NOTHING - `, p.ID, p.OwnerID) - if err != nil { - return err - } - - user, err := GetUserByID(p.OwnerID) - if err != nil { + owner := User{ID: p.OwnerID} + if err := db.Model(p).Association("ProjectMembers").Append(&owner); err != nil { return err } - p.Owner = user - p.ProjectMembers = []User{*user} + p.Owner = &owner + p.ProjectMembers = []User{owner} return nil } func GetProjectByID(projectID int64) (*Project, error) { - query := ` - SELECT - p.id, p.name, p.description, p.tags, p.owner_id, p.created_at, p.updated_at, - u.id, u.username, u.email, u.role, u.avatar_url, u.created_at - FROM projects p - JOIN users u ON p.owner_id = u.id - WHERE p.id = $1 - ` - - project := &Project{} - owner := &User{} - var tagsStr sql.NullString - - err := db.QueryRow(query, projectID).Scan( - &project.ID, - &project.Name, - &project.Description, - &tagsStr, - &project.OwnerID, - &project.CreatedAt, - &project.UpdatedAt, - &owner.ID, - &owner.Username, - &owner.Email, - &owner.Role, - &owner.AvatarURL, - &owner.CreatedAt, - ) - if err != nil { - return nil, err - } - - if tagsStr.Valid { - strTags := strings.Split(tagsStr.String, ",") - tags := make([]sql.NullString, len(strTags)) - for i, s := range strTags { - tags[i] = sql.NullString{ - String: s, - Valid: true, - } - } - project.Tags = tags - } - - project.Owner = owner + var p Project + err := db.Preload("Owner"). + Preload("ProjectMembers"). + First(&p, projectID).Error - memberQuery := ` - SELECT u.id, u.username, u.email, u.role, u.avatar_url, u.created_at - FROM users u - JOIN project_members pm ON u.id = pm.user_id - WHERE pm.project_id = $1 - ` - rows, err := db.Query(memberQuery, projectID) if err != nil { return nil, err } - defer rows.Close() - - var members []User - for rows.Next() { - var member User - if err := rows.Scan( - &member.ID, - &member.Username, - &member.Email, - &member.Role, - &member.AvatarURL, - &member.CreatedAt, - ); err != nil { - return nil, err - } - members = append(members, member) - } - project.ProjectMembers = members - - return project, nil + return &p, nil } func DeleteProjectByID(projectID int64) error { - query := `DELETE FROM projects WHERE id = $1` - _, err := db.Exec(query, projectID) - return err + return db.Delete(&Project{}, projectID).Error } func UpdateProject(p *Project) error { - query := ` - UPDATE projects - SET name = $1, description = $2, tags = $3, updated_at = CURRENT_TIMESTAMP - WHERE id = $4 - RETURNING updated_at - ` - tagsStr := "" - if len(p.Tags) > 0 { - for i, tag := range p.Tags { - if i > 0 { - tagsStr += "," - } - tagsStr += tag.String - } - } - - return db.QueryRow(query, p.Name, p.Description, tagsStr, p.ID).Scan(&p.UpdatedAt) + return db.Model(p).Updates(map[string]interface{}{ + "name": p.Name, + "description": p.Description, + "tags": strings.Join(p.Tags, ","), + "updated_at": time.Now(), + }).Error } func GetProjectsUserIsPartOf(userID int64) ([]Project, error) { - query := ` - SELECT - p.id, p.name, p.description, p.tags, p.owner_id, p.created_at, p.updated_at, - u.id, u.username, u.email, u.role, u.avatar_url, u.created_at - FROM projects p - JOIN project_members pm ON p.id = pm.project_id - JOIN users u ON p.owner_id = u.id - WHERE pm.user_id = $1 - ` - - rows, err := db.Query(query, userID) - if err != nil { - return nil, err - } - defer rows.Close() - var projects []Project - for rows.Next() { - var project Project - var owner User - var tagsStr sql.NullString - - err := rows.Scan( - &project.ID, - &project.Name, - &project.Description, - &tagsStr, - &project.OwnerID, - &project.CreatedAt, - &project.UpdatedAt, - &owner.ID, - &owner.Username, - &owner.Email, - &owner.Role, - &owner.AvatarURL, - &owner.CreatedAt, - ) - if err != nil { - return nil, err - } - if tagsStr.Valid { - strTags := strings.Split(tagsStr.String, ",") - tags := make([]sql.NullString, len(strTags)) - for i, s := range strTags { - tags[i] = sql.NullString{ - String: s, - Valid: true, - } - } - project.Tags = tags - } - - project.Owner = &owner - projects = append(projects, project) - } + err := db.Preload("Owner"). + Joins("JOIN project_members pm ON pm.project_id = projects.id"). + Where("pm.user_id = ?", userID). + Find(&projects).Error - return projects, nil + return projects, err } func HasUserAccessToProject(userID, projectID int64) (bool, error) { - query := ` - SELECT COUNT(1) - FROM project_members - WHERE project_id = $1 AND user_id = $2 - ` - var count int - err := db.QueryRow(query, projectID, userID).Scan(&count) - if err != nil { - return false, err - } - return count > 0, nil + var count int64 + err := db.Table("project_members"). + Where("project_id = ? AND user_id = ?", projectID, userID). + Count(&count).Error + + return count > 0, err } func IsUserProjectOwner(userID, projectID int64) (bool, error) { - query := `SELECT owner_id FROM projects WHERE id = $1` - var ownerID int64 - err := db.QueryRow(query, projectID).Scan(&ownerID) - if err != nil { - return false, err - } - return ownerID == userID, nil + var count int64 + err := db.Model(&Project{}). + Where("id = ? AND owner_id = ?", projectID, userID). + Count(&count).Error + return count > 0, err } func UpdateProjectMembers(projectID int64, userIDs []int64) error { - query := `SELECT owner_id FROM projects WHERE id = $1` - var ownerID int64 - err := db.QueryRow(query, projectID).Scan(&ownerID) - if err != nil { - return err - } - - tx, err := db.Begin() - if err != nil { - return err - } - defer tx.Rollback() + return db.Transaction(func(tx *gorm.DB) error { + var project Project + if err := tx.Select("owner_id").First(&project, projectID).Error; err != nil { + return err + } - _, err = tx.Exec(`DELETE FROM project_members WHERE project_id = $1 AND user_id != $2`, projectID, ownerID) - if err != nil { - return err - } + ownerIncluded := false + for _, uid := range userIDs { + if uid == project.OwnerID { + ownerIncluded = true + break + } + } + if !ownerIncluded { + userIDs = append(userIDs, project.OwnerID) + } - ownerIncluded := false - for _, userID := range userIDs { - if userID == ownerID { - ownerIncluded = true - break + var users []User + for _, uid := range userIDs { + users = append(users, User{ID: uid}) } - } - if !ownerIncluded { - userIDs = append(userIDs, ownerID) - } - for _, userID := range userIDs { - _, err = tx.Exec(` - INSERT INTO project_members (project_id, user_id) - VALUES ($1, $2) - ON CONFLICT (project_id, user_id) DO NOTHING - `, projectID, userID) - if err != nil { + if err := tx.Model(&Project{ID: projectID}).Association("ProjectMembers").Replace(users); err != nil { return err } - } - return tx.Commit() + return nil + }) } + +//############################################################################################################ +//ARCHIVED CODE BELOW + +// package models + +// import ( +// "database/sql" +// "strings" +// "time" + +// "github.com/corecollectives/mist/utils" +// ) + +// type Project struct { +// ID int64 `json:"id"` +// Name string `json:"name"` +// Description sql.NullString `json:"description"` +// Tags []sql.NullString `json:"tags"` +// OwnerID int64 `json:"ownerId"` +// Owner *User `json:"owner,omitempty"` +// ProjectMembers []User `json:"projectMembers"` +// CreatedAt time.Time `json:"createdAt"` +// UpdatedAt time.Time `json:"updatedAt"` +// } + +// func (p *Project) ToJSON() map[string]interface{} { +// tags := []string{} +// for _, tag := range p.Tags { +// if tag.Valid { +// tags = append(tags, tag.String) +// } +// } +// if len(tags) == 0 { +// tags = []string{} +// } + +// return map[string]interface{}{ +// "id": p.ID, +// "name": p.Name, +// "description": p.Description.String, +// "tags": tags, +// "ownerId": p.OwnerID, +// "owner": p.Owner, +// "projectMembers": p.ProjectMembers, +// "createdAt": p.CreatedAt, +// "updatedAt": p.UpdatedAt, +// } +// } + +// func (p *Project) InsertInDB() error { +// p.ID = utils.GenerateRandomId() + +// tagsStr := "" +// if len(p.Tags) > 0 { +// for i, tag := range p.Tags { +// if i > 0 { +// tagsStr += "," +// } +// tagsStr += tag.String +// } +// } + +// var desc interface{} +// if p.Description.Valid { +// desc = p.Description.String +// } else { +// desc = nil +// } + +// query := ` +// INSERT INTO projects (id, name, description, tags, owner_id) +// VALUES ($1, $2, $3, $4, $5) +// RETURNING id, created_at, updated_at +// ` +// err := db.QueryRow(query, p.ID, p.Name, desc, tagsStr, p.OwnerID).Scan( +// &p.ID, &p.CreatedAt, &p.UpdatedAt, +// ) +// if err != nil { +// return err +// } + +// _, err = db.Exec(` +// INSERT INTO project_members (project_id, user_id) +// VALUES ($1, $2) +// ON CONFLICT DO NOTHING +// `, p.ID, p.OwnerID) +// if err != nil { +// return err +// } + +// user, err := GetUserByID(p.OwnerID) +// if err != nil { +// return err +// } +// p.Owner = user + +// p.ProjectMembers = []User{*user} + +// return nil +// } + +// func GetProjectByID(projectID int64) (*Project, error) { +// query := ` +// SELECT +// p.id, p.name, p.description, p.tags, p.owner_id, p.created_at, p.updated_at, +// u.id, u.username, u.email, u.role, u.avatar_url, u.created_at +// FROM projects p +// JOIN users u ON p.owner_id = u.id +// WHERE p.id = $1 +// ` + +// project := &Project{} +// owner := &User{} +// var tagsStr sql.NullString + +// err := db.QueryRow(query, projectID).Scan( +// &project.ID, +// &project.Name, +// &project.Description, +// &tagsStr, +// &project.OwnerID, +// &project.CreatedAt, +// &project.UpdatedAt, +// &owner.ID, +// &owner.Username, +// &owner.Email, +// &owner.Role, +// &owner.AvatarURL, +// &owner.CreatedAt, +// ) +// if err != nil { +// return nil, err +// } + +// if tagsStr.Valid { +// strTags := strings.Split(tagsStr.String, ",") +// tags := make([]sql.NullString, len(strTags)) +// for i, s := range strTags { +// tags[i] = sql.NullString{ +// String: s, +// Valid: true, +// } +// } +// project.Tags = tags +// } + +// project.Owner = owner + +// memberQuery := ` +// SELECT u.id, u.username, u.email, u.role, u.avatar_url, u.created_at +// FROM users u +// JOIN project_members pm ON u.id = pm.user_id +// WHERE pm.project_id = $1 +// ` +// rows, err := db.Query(memberQuery, projectID) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// var members []User +// for rows.Next() { +// var member User +// if err := rows.Scan( +// &member.ID, +// &member.Username, +// &member.Email, +// &member.Role, +// &member.AvatarURL, +// &member.CreatedAt, +// ); err != nil { +// return nil, err +// } +// members = append(members, member) +// } +// project.ProjectMembers = members + +// return project, nil +// } + +// func DeleteProjectByID(projectID int64) error { +// query := `DELETE FROM projects WHERE id = $1` +// _, err := db.Exec(query, projectID) +// return err +// } + +// func UpdateProject(p *Project) error { +// query := ` +// UPDATE projects +// SET name = $1, description = $2, tags = $3, updated_at = CURRENT_TIMESTAMP +// WHERE id = $4 +// RETURNING updated_at +// ` +// tagsStr := "" +// if len(p.Tags) > 0 { +// for i, tag := range p.Tags { +// if i > 0 { +// tagsStr += "," +// } +// tagsStr += tag.String +// } +// } + +// return db.QueryRow(query, p.Name, p.Description, tagsStr, p.ID).Scan(&p.UpdatedAt) +// } + +// func GetProjectsUserIsPartOf(userID int64) ([]Project, error) { +// query := ` +// SELECT +// p.id, p.name, p.description, p.tags, p.owner_id, p.created_at, p.updated_at, +// u.id, u.username, u.email, u.role, u.avatar_url, u.created_at +// FROM projects p +// JOIN project_members pm ON p.id = pm.project_id +// JOIN users u ON p.owner_id = u.id +// WHERE pm.user_id = $1 +// ` + +// rows, err := db.Query(query, userID) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// var projects []Project +// for rows.Next() { +// var project Project +// var owner User +// var tagsStr sql.NullString + +// err := rows.Scan( +// &project.ID, +// &project.Name, +// &project.Description, +// &tagsStr, +// &project.OwnerID, +// &project.CreatedAt, +// &project.UpdatedAt, +// &owner.ID, +// &owner.Username, +// &owner.Email, +// &owner.Role, +// &owner.AvatarURL, +// &owner.CreatedAt, +// ) +// if err != nil { +// return nil, err +// } + +// if tagsStr.Valid { +// strTags := strings.Split(tagsStr.String, ",") +// tags := make([]sql.NullString, len(strTags)) +// for i, s := range strTags { +// tags[i] = sql.NullString{ +// String: s, +// Valid: true, +// } +// } +// project.Tags = tags +// } + +// project.Owner = &owner +// projects = append(projects, project) +// } + +// return projects, nil +// } + +// func HasUserAccessToProject(userID, projectID int64) (bool, error) { +// query := ` +// SELECT COUNT(1) +// FROM project_members +// WHERE project_id = $1 AND user_id = $2 +// ` +// var count int +// err := db.QueryRow(query, projectID, userID).Scan(&count) +// if err != nil { +// return false, err +// } +// return count > 0, nil +// } + +// func IsUserProjectOwner(userID, projectID int64) (bool, error) { +// query := `SELECT owner_id FROM projects WHERE id = $1` +// var ownerID int64 +// err := db.QueryRow(query, projectID).Scan(&ownerID) +// if err != nil { +// return false, err +// } +// return ownerID == userID, nil +// } + +// func UpdateProjectMembers(projectID int64, userIDs []int64) error { +// query := `SELECT owner_id FROM projects WHERE id = $1` +// var ownerID int64 +// err := db.QueryRow(query, projectID).Scan(&ownerID) +// if err != nil { +// return err +// } + +// tx, err := db.Begin() +// if err != nil { +// return err +// } +// defer tx.Rollback() + +// _, err = tx.Exec(`DELETE FROM project_members WHERE project_id = $1 AND user_id != $2`, projectID, ownerID) +// if err != nil { +// return err +// } + +// ownerIncluded := false +// for _, userID := range userIDs { +// if userID == ownerID { +// ownerIncluded = true +// break +// } +// } +// if !ownerIncluded { +// userIDs = append(userIDs, ownerID) +// } + +// for _, userID := range userIDs { +// _, err = tx.Exec(` +// INSERT INTO project_members (project_id, user_id) +// VALUES ($1, $2) +// ON CONFLICT (project_id, user_id) DO NOTHING +// `, projectID, userID) +// if err != nil { +// return err +// } +// } + +// return tx.Commit() +// } diff --git a/server/models/projectMembers.go b/server/models/projectMembers.go new file mode 100644 index 0000000..cda414d --- /dev/null +++ b/server/models/projectMembers.go @@ -0,0 +1,15 @@ +package models + +import "time" + +type ProjectMember struct { + UserID int64 `gorm:"primaryKey;autoIncrement:false" json:"user_id"` + + ProjectID int64 `gorm:"primaryKey;autoIncrement:false" json:"project_id"` + + AddedAt time.Time `gorm:"autoCreateTime" json:"added_at"` +} + +func (ProjectMember) TableName() string { + return "project_members" +} diff --git a/server/models/registries.go b/server/models/registries.go new file mode 100644 index 0000000..509885e --- /dev/null +++ b/server/models/registries.go @@ -0,0 +1,20 @@ +package models + +import "time" + +type Registry struct { + ID int64 `gorm:"primaryKey;autoIncrement:true" json:"id"` + + ProjectID int64 `gorm:"uniqueIndex:idx_project_registry;not null;constraint:OnDelete:CASCADE" json:"projectId"` + + RegistryURL string `gorm:"uniqueIndex:idx_project_registry;not null" json:"registryUrl"` + + Username string `json:"username"` + Password string `json:"password"` + + CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"` +} + +func (Registry) TableName() string { + return "registries" +} diff --git a/server/models/serviceTemplate.go b/server/models/serviceTemplate.go index 36be0c1..f75c9ae 100644 --- a/server/models/serviceTemplate.go +++ b/server/models/serviceTemplate.go @@ -1,8 +1,9 @@ package models import ( - "database/sql" "time" + + "gorm.io/gorm" ) type ServiceTemplateCategory string @@ -16,32 +17,48 @@ const ( ) type ServiceTemplate struct { - ID int64 `db:"id" json:"id"` - Name string `db:"name" json:"name"` - DisplayName string `db:"display_name" json:"displayName"` - Category ServiceTemplateCategory `db:"category" json:"category"` - Description *string `db:"description" json:"description,omitempty"` - IconURL *string `db:"icon_url" json:"iconUrl,omitempty"` - DockerImage string `db:"docker_image" json:"dockerImage"` - DockerImageVersion *string `db:"docker_image_version" json:"dockerImageVersion,omitempty"` - DefaultPort int `db:"default_port" json:"defaultPort"` - DefaultEnvVars *string `db:"default_env_vars" json:"defaultEnvVars,omitempty"` - RequiredEnvVars *string `db:"required_env_vars" json:"requiredEnvVars,omitempty"` - DefaultVolumePath *string `db:"default_volume_path" json:"defaultVolumePath,omitempty"` - VolumeRequired bool `db:"volume_required" json:"volumeRequired"` - RecommendedCPU *float64 `db:"recommended_cpu" json:"recommendedCpu,omitempty"` - RecommendedMemory *int `db:"recommended_memory" json:"recommendedMemory,omitempty"` - MinMemory *int `db:"min_memory" json:"minMemory,omitempty"` - HealthcheckCommand *string `db:"healthcheck_command" json:"healthcheckCommand,omitempty"` - HealthcheckInterval int `db:"healthcheck_interval" json:"healthcheckInterval"` - AdminUIImage *string `db:"admin_ui_image" json:"adminUiImage,omitempty"` - AdminUIPort *int `db:"admin_ui_port" json:"adminUiPort,omitempty"` - SetupInstructions *string `db:"setup_instructions" json:"setupInstructions,omitempty"` - IsActive bool `db:"is_active" json:"isActive"` - IsFeatured bool `db:"is_featured" json:"isFeatured"` - SortOrder int `db:"sort_order" json:"sortOrder"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` + ID int64 `gorm:"primaryKey;autoIncrement:true" json:"id"` + + Name string `gorm:"uniqueIndex;not null" json:"name"` + + DisplayName string `gorm:"not null" json:"displayName"` + + Category ServiceTemplateCategory `gorm:"default:'database';index" json:"category"` + + Description *string `json:"description,omitempty"` + IconURL *string `json:"iconUrl,omitempty"` + + DockerImage string `gorm:"not null" json:"dockerImage"` + DockerImageVersion *string `json:"dockerImageVersion,omitempty"` + + DefaultPort int `gorm:"not null" json:"defaultPort"` + DefaultEnvVars *string `json:"defaultEnvVars,omitempty"` + RequiredEnvVars *string `json:"requiredEnvVars,omitempty"` + + DefaultVolumePath *string `json:"defaultVolumePath,omitempty"` + + VolumeRequired bool `gorm:"default:true" json:"volumeRequired"` + + RecommendedCPU *float64 `json:"recommendedCpu,omitempty"` + RecommendedMemory *int `json:"recommendedMemory,omitempty"` + MinMemory *int `json:"minMemory,omitempty"` + + HealthcheckCommand *string `json:"healthcheckCommand,omitempty"` + + HealthcheckInterval int `gorm:"default:30" json:"healthcheckInterval"` + + AdminUIImage *string `json:"adminUiImage,omitempty"` + AdminUIPort *int `json:"adminUiPort,omitempty"` + SetupInstructions *string `json:"setupInstructions,omitempty"` + + IsActive bool `gorm:"default:true;index" json:"isActive"` + + IsFeatured bool `gorm:"default:false" json:"isFeatured"` + + SortOrder int `gorm:"default:0;index" json:"sortOrder"` + + CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updatedAt"` } func (st *ServiceTemplate) ToJson() map[string]interface{} { @@ -77,77 +94,21 @@ func (st *ServiceTemplate) ToJson() map[string]interface{} { func GetAllServiceTemplates() ([]ServiceTemplate, error) { var templates []ServiceTemplate - query := ` - SELECT id, name, display_name, category, description, icon_url, - docker_image, docker_image_version, default_port, default_env_vars, - required_env_vars, default_volume_path, volume_required, - recommended_cpu, recommended_memory, min_memory, - healthcheck_command, healthcheck_interval, - admin_ui_image, admin_ui_port, setup_instructions, - is_active, is_featured, sort_order, created_at, updated_at - FROM service_templates - WHERE is_active = 1 - ORDER BY sort_order, display_name - ` - rows, err := db.Query(query) - if err != nil { - return nil, err - } - defer rows.Close() - - for rows.Next() { - var template ServiceTemplate - err := rows.Scan( - &template.ID, &template.Name, &template.DisplayName, &template.Category, - &template.Description, &template.IconURL, &template.DockerImage, - &template.DockerImageVersion, &template.DefaultPort, &template.DefaultEnvVars, - &template.RequiredEnvVars, &template.DefaultVolumePath, &template.VolumeRequired, - &template.RecommendedCPU, &template.RecommendedMemory, &template.MinMemory, - &template.HealthcheckCommand, &template.HealthcheckInterval, - &template.AdminUIImage, &template.AdminUIPort, &template.SetupInstructions, - &template.IsActive, &template.IsFeatured, &template.SortOrder, - &template.CreatedAt, &template.UpdatedAt, - ) - if err != nil { - return nil, err - } - templates = append(templates, template) - } - - if err = rows.Err(); err != nil { - return nil, err - } - return templates, nil + err := db.Where("is_active = ?", true). + Order("sort_order, display_name"). + Find(&templates).Error + return templates, err } func GetServiceTemplateByName(name string) (*ServiceTemplate, error) { var template ServiceTemplate - query := ` - SELECT id, name, display_name, category, description, icon_url, - docker_image, docker_image_version, default_port, default_env_vars, - required_env_vars, default_volume_path, volume_required, - recommended_cpu, recommended_memory, min_memory, - healthcheck_command, healthcheck_interval, - admin_ui_image, admin_ui_port, setup_instructions, - is_active, is_featured, sort_order, created_at, updated_at - FROM service_templates - WHERE name = ? AND is_active = 1 - ` - err := db.QueryRow(query, name).Scan( - &template.ID, &template.Name, &template.DisplayName, &template.Category, - &template.Description, &template.IconURL, &template.DockerImage, - &template.DockerImageVersion, &template.DefaultPort, &template.DefaultEnvVars, - &template.RequiredEnvVars, &template.DefaultVolumePath, &template.VolumeRequired, - &template.RecommendedCPU, &template.RecommendedMemory, &template.MinMemory, - &template.HealthcheckCommand, &template.HealthcheckInterval, - &template.AdminUIImage, &template.AdminUIPort, &template.SetupInstructions, - &template.IsActive, &template.IsFeatured, &template.SortOrder, - &template.CreatedAt, &template.UpdatedAt, - ) - if err == sql.ErrNoRows { - return nil, nil - } + err := db.Where("name = ? AND is_active = ?", name, true). + First(&template).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } return nil, err } return &template, nil @@ -155,45 +116,211 @@ func GetServiceTemplateByName(name string) (*ServiceTemplate, error) { func GetServiceTemplatesByCategory(category ServiceTemplateCategory) ([]ServiceTemplate, error) { var templates []ServiceTemplate - query := ` - SELECT id, name, display_name, category, description, icon_url, - docker_image, docker_image_version, default_port, default_env_vars, - required_env_vars, default_volume_path, volume_required, - recommended_cpu, recommended_memory, min_memory, - healthcheck_command, healthcheck_interval, - admin_ui_image, admin_ui_port, setup_instructions, - is_active, is_featured, sort_order, created_at, updated_at - FROM service_templates - WHERE category = ? AND is_active = 1 - ORDER BY sort_order, display_name - ` - rows, err := db.Query(query, category) - if err != nil { - return nil, err - } - defer rows.Close() - - for rows.Next() { - var template ServiceTemplate - err := rows.Scan( - &template.ID, &template.Name, &template.DisplayName, &template.Category, - &template.Description, &template.IconURL, &template.DockerImage, - &template.DockerImageVersion, &template.DefaultPort, &template.DefaultEnvVars, - &template.RequiredEnvVars, &template.DefaultVolumePath, &template.VolumeRequired, - &template.RecommendedCPU, &template.RecommendedMemory, &template.MinMemory, - &template.HealthcheckCommand, &template.HealthcheckInterval, - &template.AdminUIImage, &template.AdminUIPort, &template.SetupInstructions, - &template.IsActive, &template.IsFeatured, &template.SortOrder, - &template.CreatedAt, &template.UpdatedAt, - ) - if err != nil { - return nil, err - } - templates = append(templates, template) - } - - if err = rows.Err(); err != nil { - return nil, err - } - return templates, nil + err := db.Where("category = ? AND is_active = ?", category, true). + Order("sort_order, display_name"). + Find(&templates).Error + return templates, err } + +//############################################################################################################### +//ARCHIVED CODE BELOW + +// package models + +// import ( +// "database/sql" +// "time" +// ) + +// type ServiceTemplateCategory string + +// const ( +// CategoryDatabase ServiceTemplateCategory = "database" +// CategoryCache ServiceTemplateCategory = "cache" +// CategoryQueue ServiceTemplateCategory = "queue" +// CategoryStorage ServiceTemplateCategory = "storage" +// CategoryOther ServiceTemplateCategory = "other" +// ) + +// type ServiceTemplate struct { +// ID int64 `db:"id" json:"id"` +// Name string `db:"name" json:"name"` +// DisplayName string `db:"display_name" json:"displayName"` +// Category ServiceTemplateCategory `db:"category" json:"category"` +// Description *string `db:"description" json:"description,omitempty"` +// IconURL *string `db:"icon_url" json:"iconUrl,omitempty"` +// DockerImage string `db:"docker_image" json:"dockerImage"` +// DockerImageVersion *string `db:"docker_image_version" json:"dockerImageVersion,omitempty"` +// DefaultPort int `db:"default_port" json:"defaultPort"` +// DefaultEnvVars *string `db:"default_env_vars" json:"defaultEnvVars,omitempty"` +// RequiredEnvVars *string `db:"required_env_vars" json:"requiredEnvVars,omitempty"` +// DefaultVolumePath *string `db:"default_volume_path" json:"defaultVolumePath,omitempty"` +// VolumeRequired bool `db:"volume_required" json:"volumeRequired"` +// RecommendedCPU *float64 `db:"recommended_cpu" json:"recommendedCpu,omitempty"` +// RecommendedMemory *int `db:"recommended_memory" json:"recommendedMemory,omitempty"` +// MinMemory *int `db:"min_memory" json:"minMemory,omitempty"` +// HealthcheckCommand *string `db:"healthcheck_command" json:"healthcheckCommand,omitempty"` +// HealthcheckInterval int `db:"healthcheck_interval" json:"healthcheckInterval"` +// AdminUIImage *string `db:"admin_ui_image" json:"adminUiImage,omitempty"` +// AdminUIPort *int `db:"admin_ui_port" json:"adminUiPort,omitempty"` +// SetupInstructions *string `db:"setup_instructions" json:"setupInstructions,omitempty"` +// IsActive bool `db:"is_active" json:"isActive"` +// IsFeatured bool `db:"is_featured" json:"isFeatured"` +// SortOrder int `db:"sort_order" json:"sortOrder"` +// CreatedAt time.Time `db:"created_at" json:"createdAt"` +// UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` +// } + +// func (st *ServiceTemplate) ToJson() map[string]interface{} { +// return map[string]interface{}{ +// "id": st.ID, +// "name": st.Name, +// "displayName": st.DisplayName, +// "category": st.Category, +// "description": st.Description, +// "iconUrl": st.IconURL, +// "dockerImage": st.DockerImage, +// "dockerImageVersion": st.DockerImageVersion, +// "defaultPort": st.DefaultPort, +// "defaultEnvVars": st.DefaultEnvVars, +// "requiredEnvVars": st.RequiredEnvVars, +// "defaultVolumePath": st.DefaultVolumePath, +// "volumeRequired": st.VolumeRequired, +// "recommendedCpu": st.RecommendedCPU, +// "recommendedMemory": st.RecommendedMemory, +// "minMemory": st.MinMemory, +// "healthcheckCommand": st.HealthcheckCommand, +// "healthcheckInterval": st.HealthcheckInterval, +// "adminUiImage": st.AdminUIImage, +// "adminUiPort": st.AdminUIPort, +// "setupInstructions": st.SetupInstructions, +// "isActive": st.IsActive, +// "isFeatured": st.IsFeatured, +// "sortOrder": st.SortOrder, +// "createdAt": st.CreatedAt, +// "updatedAt": st.UpdatedAt, +// } +// } + +// func GetAllServiceTemplates() ([]ServiceTemplate, error) { +// var templates []ServiceTemplate +// query := ` +// SELECT id, name, display_name, category, description, icon_url, +// docker_image, docker_image_version, default_port, default_env_vars, +// required_env_vars, default_volume_path, volume_required, +// recommended_cpu, recommended_memory, min_memory, +// healthcheck_command, healthcheck_interval, +// admin_ui_image, admin_ui_port, setup_instructions, +// is_active, is_featured, sort_order, created_at, updated_at +// FROM service_templates +// WHERE is_active = 1 +// ORDER BY sort_order, display_name +// ` +// rows, err := db.Query(query) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// for rows.Next() { +// var template ServiceTemplate +// err := rows.Scan( +// &template.ID, &template.Name, &template.DisplayName, &template.Category, +// &template.Description, &template.IconURL, &template.DockerImage, +// &template.DockerImageVersion, &template.DefaultPort, &template.DefaultEnvVars, +// &template.RequiredEnvVars, &template.DefaultVolumePath, &template.VolumeRequired, +// &template.RecommendedCPU, &template.RecommendedMemory, &template.MinMemory, +// &template.HealthcheckCommand, &template.HealthcheckInterval, +// &template.AdminUIImage, &template.AdminUIPort, &template.SetupInstructions, +// &template.IsActive, &template.IsFeatured, &template.SortOrder, +// &template.CreatedAt, &template.UpdatedAt, +// ) +// if err != nil { +// return nil, err +// } +// templates = append(templates, template) +// } + +// if err = rows.Err(); err != nil { +// return nil, err +// } +// return templates, nil +// } + +// func GetServiceTemplateByName(name string) (*ServiceTemplate, error) { +// var template ServiceTemplate +// query := ` +// SELECT id, name, display_name, category, description, icon_url, +// docker_image, docker_image_version, default_port, default_env_vars, +// required_env_vars, default_volume_path, volume_required, +// recommended_cpu, recommended_memory, min_memory, +// healthcheck_command, healthcheck_interval, +// admin_ui_image, admin_ui_port, setup_instructions, +// is_active, is_featured, sort_order, created_at, updated_at +// FROM service_templates +// WHERE name = ? AND is_active = 1 +// ` +// err := db.QueryRow(query, name).Scan( +// &template.ID, &template.Name, &template.DisplayName, &template.Category, +// &template.Description, &template.IconURL, &template.DockerImage, +// &template.DockerImageVersion, &template.DefaultPort, &template.DefaultEnvVars, +// &template.RequiredEnvVars, &template.DefaultVolumePath, &template.VolumeRequired, +// &template.RecommendedCPU, &template.RecommendedMemory, &template.MinMemory, +// &template.HealthcheckCommand, &template.HealthcheckInterval, +// &template.AdminUIImage, &template.AdminUIPort, &template.SetupInstructions, +// &template.IsActive, &template.IsFeatured, &template.SortOrder, +// &template.CreatedAt, &template.UpdatedAt, +// ) +// if err == sql.ErrNoRows { +// return nil, nil +// } +// if err != nil { +// return nil, err +// } +// return &template, nil +// } + +// func GetServiceTemplatesByCategory(category ServiceTemplateCategory) ([]ServiceTemplate, error) { +// var templates []ServiceTemplate +// query := ` +// SELECT id, name, display_name, category, description, icon_url, +// docker_image, docker_image_version, default_port, default_env_vars, +// required_env_vars, default_volume_path, volume_required, +// recommended_cpu, recommended_memory, min_memory, +// healthcheck_command, healthcheck_interval, +// admin_ui_image, admin_ui_port, setup_instructions, +// is_active, is_featured, sort_order, created_at, updated_at +// FROM service_templates +// WHERE category = ? AND is_active = 1 +// ORDER BY sort_order, display_name +// ` +// rows, err := db.Query(query, category) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// for rows.Next() { +// var template ServiceTemplate +// err := rows.Scan( +// &template.ID, &template.Name, &template.DisplayName, &template.Category, +// &template.Description, &template.IconURL, &template.DockerImage, +// &template.DockerImageVersion, &template.DefaultPort, &template.DefaultEnvVars, +// &template.RequiredEnvVars, &template.DefaultVolumePath, &template.VolumeRequired, +// &template.RecommendedCPU, &template.RecommendedMemory, &template.MinMemory, +// &template.HealthcheckCommand, &template.HealthcheckInterval, +// &template.AdminUIImage, &template.AdminUIPort, &template.SetupInstructions, +// &template.IsActive, &template.IsFeatured, &template.SortOrder, +// &template.CreatedAt, &template.UpdatedAt, +// ) +// if err != nil { +// return nil, err +// } +// templates = append(templates, template) +// } + +// if err = rows.Err(); err != nil { +// return nil, err +// } +// return templates, nil +// } diff --git a/server/models/session.go b/server/models/session.go index 68c0ab4..dbdad14 100644 --- a/server/models/session.go +++ b/server/models/session.go @@ -4,22 +4,31 @@ import ( "time" ) +type DeviceType string + +const ( + DeviceDesktop DeviceType = "desktop" + DeviceMobile DeviceType = "mobile" + DeviceTablet DeviceType = "tablet" + DeviceUnknown DeviceType = "unknown" +) + type Session struct { - ID string `db:"id" json:"id"` - UserID int64 `db:"user_id" json:"userId"` - SessionData *string `db:"session_data" json:"sessionData,omitempty"` // JSON - IPAddress *string `db:"ip_address" json:"ipAddress,omitempty"` - UserAgent *string `db:"user_agent" json:"userAgent,omitempty"` - DeviceType *string `db:"device_type" json:"deviceType,omitempty"` - Browser *string `db:"browser" json:"browser,omitempty"` - OS *string `db:"os" json:"os,omitempty"` - Location *string `db:"location" json:"location,omitempty"` - IsActive bool `db:"is_active" json:"isActive"` - LastActivityAt time.Time `db:"last_activity_at" json:"lastActivityAt"` - RevokedAt *time.Time `db:"revoked_at" json:"revokedAt,omitempty"` - RevokedReason *string `db:"revoked_reason" json:"revokedReason,omitempty"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - ExpiresAt time.Time `db:"expires_at" json:"expiresAt"` + ID string `gorm:"primaryKey" json:"id"` + UserID int64 `gorm:"index;not null;constraint:OnDelete:CASCADE" json:"userId"` + SessionData *string `json:"sessionData,omitempty"` + IPAddress *string `json:"ipAddress,omitempty"` + UserAgent *string `json:"userAgent,omitempty"` + DeviceType *string `json:"deviceType,omitempty"` + Browser *string `json:"browser,omitempty"` + OS *string `json:"os,omitempty"` + Location *string `json:"location,omitempty"` + IsActive bool `gorm:"default:true;index" json:"isActive"` + LastActivityAt time.Time `gorm:"autoUpdateTime" json:"lastActivityAt"` + RevokedAt *time.Time `json:"revokedAt,omitempty"` + RevokedReason *string `json:"revokedReason,omitempty"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"` + ExpiresAt time.Time `gorm:"not null;index" json:"expiresAt"` } func (s *Session) ToJson() map[string]interface{} { @@ -43,34 +52,12 @@ func (s *Session) ToJson() map[string]interface{} { } func (s *Session) InsertInDB() error { - query := ` - INSERT INTO sessions ( - id, user_id, session_data, ip_address, user_agent, - device_type, browser, os, location, expires_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - RETURNING created_at, last_activity_at - ` - err := db.QueryRow(query, s.ID, s.UserID, s.SessionData, s.IPAddress, s.UserAgent, - s.DeviceType, s.Browser, s.OS, s.Location, s.ExpiresAt).Scan(&s.CreatedAt, &s.LastActivityAt) - return err + return db.Create(s).Error } func GetSessionByID(sessionID string) (*Session, error) { var session Session - query := ` - SELECT id, user_id, session_data, ip_address, user_agent, - device_type, browser, os, location, is_active, - last_activity_at, revoked_at, revoked_reason, - created_at, expires_at - FROM sessions - WHERE id = ? AND is_active = 1 - ` - err := db.QueryRow(query, sessionID).Scan( - &session.ID, &session.UserID, &session.SessionData, &session.IPAddress, - &session.UserAgent, &session.DeviceType, &session.Browser, &session.OS, - &session.Location, &session.IsActive, &session.LastActivityAt, - &session.RevokedAt, &session.RevokedReason, &session.CreatedAt, &session.ExpiresAt, - ) + err := db.Where("id = ? AND is_active = ?", sessionID, true).First(&session).Error if err != nil { return nil, err } @@ -79,77 +66,193 @@ func GetSessionByID(sessionID string) (*Session, error) { func GetSessionsByUserID(userID int64) ([]Session, error) { var sessions []Session - query := ` - SELECT id, user_id, session_data, ip_address, user_agent, - device_type, browser, os, location, is_active, - last_activity_at, revoked_at, revoked_reason, - created_at, expires_at - FROM sessions - WHERE user_id = ? AND is_active = 1 - ORDER BY last_activity_at DESC - ` - rows, err := db.Query(query, userID) - if err != nil { - return nil, err - } - defer rows.Close() - - for rows.Next() { - var session Session - err := rows.Scan( - &session.ID, &session.UserID, &session.SessionData, &session.IPAddress, - &session.UserAgent, &session.DeviceType, &session.Browser, &session.OS, - &session.Location, &session.IsActive, &session.LastActivityAt, - &session.RevokedAt, &session.RevokedReason, &session.CreatedAt, &session.ExpiresAt, - ) - if err != nil { - return nil, err - } - sessions = append(sessions, session) - } - - return sessions, rows.Err() + err := db.Where("user_id = ? AND is_active = ?", userID, true). + Order("last_activity_at DESC"). + Find(&sessions).Error + return sessions, err } func (s *Session) UpdateActivity() error { - query := ` - UPDATE sessions - SET last_activity_at = CURRENT_TIMESTAMP - WHERE id = ? - ` - _, err := db.Exec(query, s.ID) - return err + return db.Model(s).Update("last_activity_at", time.Now()).Error } func (s *Session) Revoke(reason string) error { - query := ` - UPDATE sessions - SET is_active = 0, - revoked_at = CURRENT_TIMESTAMP, - revoked_reason = ? - WHERE id = ? - ` - _, err := db.Exec(query, reason, s.ID) - return err + return db.Model(s).Updates(map[string]interface{}{ + "is_active": false, + "revoked_at": time.Now(), + "revoked_reason": reason, + }).Error } func RevokeAllUserSessions(userID int64, reason string) error { - query := ` - UPDATE sessions - SET is_active = 0, - revoked_at = CURRENT_TIMESTAMP, - revoked_reason = ? - WHERE user_id = ? AND is_active = 1 - ` - _, err := db.Exec(query, reason, userID) - return err + return db.Model(&Session{}). + Where("user_id = ? AND is_active = ?", userID, true). + Updates(map[string]interface{}{ + "is_active": false, + "revoked_at": time.Now(), + "revoked_reason": reason, + }).Error } func DeleteExpiredSessions() error { - query := ` - DELETE FROM sessions - WHERE expires_at < CURRENT_TIMESTAMP - ` - _, err := db.Exec(query) - return err + return db.Where("expires_at < ?", time.Now()).Delete(&Session{}).Error } + +//################################################################################################ +//ARCHIVED CODE BELOW + +// package models + +// import ( +// "time" +// ) + +// type Session struct { +// ID string `db:"id" json:"id"` +// UserID int64 `db:"user_id" json:"userId"` +// SessionData *string `db:"session_data" json:"sessionData,omitempty"` // JSON +// IPAddress *string `db:"ip_address" json:"ipAddress,omitempty"` +// UserAgent *string `db:"user_agent" json:"userAgent,omitempty"` +// DeviceType *string `db:"device_type" json:"deviceType,omitempty"` +// Browser *string `db:"browser" json:"browser,omitempty"` +// OS *string `db:"os" json:"os,omitempty"` +// Location *string `db:"location" json:"location,omitempty"` +// IsActive bool `db:"is_active" json:"isActive"` +// LastActivityAt time.Time `db:"last_activity_at" json:"lastActivityAt"` +// RevokedAt *time.Time `db:"revoked_at" json:"revokedAt,omitempty"` +// RevokedReason *string `db:"revoked_reason" json:"revokedReason,omitempty"` +// CreatedAt time.Time `db:"created_at" json:"createdAt"` +// ExpiresAt time.Time `db:"expires_at" json:"expiresAt"` +// } + +// func (s *Session) ToJson() map[string]interface{} { +// return map[string]interface{}{ +// "id": s.ID, +// "userId": s.UserID, +// "sessionData": s.SessionData, +// "ipAddress": s.IPAddress, +// "userAgent": s.UserAgent, +// "deviceType": s.DeviceType, +// "browser": s.Browser, +// "os": s.OS, +// "location": s.Location, +// "isActive": s.IsActive, +// "lastActivityAt": s.LastActivityAt, +// "revokedAt": s.RevokedAt, +// "revokedReason": s.RevokedReason, +// "createdAt": s.CreatedAt, +// "expiresAt": s.ExpiresAt, +// } +// } + +// func (s *Session) InsertInDB() error { +// query := ` +// INSERT INTO sessions ( +// id, user_id, session_data, ip_address, user_agent, +// device_type, browser, os, location, expires_at +// ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// RETURNING created_at, last_activity_at +// ` +// err := db.QueryRow(query, s.ID, s.UserID, s.SessionData, s.IPAddress, s.UserAgent, +// s.DeviceType, s.Browser, s.OS, s.Location, s.ExpiresAt).Scan(&s.CreatedAt, &s.LastActivityAt) +// return err +// } + +// func GetSessionByID(sessionID string) (*Session, error) { +// var session Session +// query := ` +// SELECT id, user_id, session_data, ip_address, user_agent, +// device_type, browser, os, location, is_active, +// last_activity_at, revoked_at, revoked_reason, +// created_at, expires_at +// FROM sessions +// WHERE id = ? AND is_active = 1 +// ` +// err := db.QueryRow(query, sessionID).Scan( +// &session.ID, &session.UserID, &session.SessionData, &session.IPAddress, +// &session.UserAgent, &session.DeviceType, &session.Browser, &session.OS, +// &session.Location, &session.IsActive, &session.LastActivityAt, +// &session.RevokedAt, &session.RevokedReason, &session.CreatedAt, &session.ExpiresAt, +// ) +// if err != nil { +// return nil, err +// } +// return &session, nil +// } + +// func GetSessionsByUserID(userID int64) ([]Session, error) { +// var sessions []Session +// query := ` +// SELECT id, user_id, session_data, ip_address, user_agent, +// device_type, browser, os, location, is_active, +// last_activity_at, revoked_at, revoked_reason, +// created_at, expires_at +// FROM sessions +// WHERE user_id = ? AND is_active = 1 +// ORDER BY last_activity_at DESC +// ` +// rows, err := db.Query(query, userID) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// for rows.Next() { +// var session Session +// err := rows.Scan( +// &session.ID, &session.UserID, &session.SessionData, &session.IPAddress, +// &session.UserAgent, &session.DeviceType, &session.Browser, &session.OS, +// &session.Location, &session.IsActive, &session.LastActivityAt, +// &session.RevokedAt, &session.RevokedReason, &session.CreatedAt, &session.ExpiresAt, +// ) +// if err != nil { +// return nil, err +// } +// sessions = append(sessions, session) +// } + +// return sessions, rows.Err() +// } + +// func (s *Session) UpdateActivity() error { +// query := ` +// UPDATE sessions +// SET last_activity_at = CURRENT_TIMESTAMP +// WHERE id = ? +// ` +// _, err := db.Exec(query, s.ID) +// return err +// } + +// func (s *Session) Revoke(reason string) error { +// query := ` +// UPDATE sessions +// SET is_active = 0, +// revoked_at = CURRENT_TIMESTAMP, +// revoked_reason = ? +// WHERE id = ? +// ` +// _, err := db.Exec(query, reason, s.ID) +// return err +// } + +// func RevokeAllUserSessions(userID int64, reason string) error { +// query := ` +// UPDATE sessions +// SET is_active = 0, +// revoked_at = CURRENT_TIMESTAMP, +// revoked_reason = ? +// WHERE user_id = ? AND is_active = 1 +// ` +// _, err := db.Exec(query, reason, userID) +// return err +// } + +// func DeleteExpiredSessions() error { +// query := ` +// DELETE FROM sessions +// WHERE expires_at < CURRENT_TIMESTAMP +// ` +// _, err := db.Exec(query) +// return err +// } diff --git a/server/models/systemSettings.go b/server/models/systemSettings.go index 1e61c0e..8af0dcf 100644 --- a/server/models/systemSettings.go +++ b/server/models/systemSettings.go @@ -2,11 +2,13 @@ package models import ( "crypto/rand" - "database/sql" "encoding/base64" "fmt" + "time" "github.com/rs/zerolog/log" + "gorm.io/gorm" + "gorm.io/gorm/clause" ) type SystemSettings struct { @@ -21,6 +23,16 @@ type SystemSettings struct { AutoCleanupImages bool `json:"autoCleanupImages"` } +type SystemSettingEntry struct { + Key string `gorm:"primaryKey" json:"key"` + Value string `json:"value"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` +} + +func (SystemSettingEntry) TableName() string { + return "system_settings" +} + func generateRandomSecret(length int) (string, error) { bytes := make([]byte, length) if _, err := rand.Read(bytes); err != nil { @@ -30,41 +42,43 @@ func generateRandomSecret(length int) (string, error) { } func GetSystemSetting(key string) (string, error) { - var value string - err := db.QueryRow(`SELECT value FROM system_settings WHERE key = ?`, key).Scan(&value) - if err == sql.ErrNoRows { - return "", nil - } + var entry SystemSettingEntry + err := db.Where("key = ?", key).First(&entry).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return "", nil + } return "", err } - return value, nil + return entry.Value, nil } func SetSystemSetting(key, value string) error { - _, err := db.Exec(` - INSERT INTO system_settings (key, value, updated_at) - VALUES (?, ?, CURRENT_TIMESTAMP) - ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = CURRENT_TIMESTAMP - `, key, value, value) - return err + entry := SystemSettingEntry{ + Key: key, + Value: value, + } + + return db.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "key"}}, + DoUpdates: clause.AssignmentColumns([]string{"value", "updated_at"}), + }).Create(&entry).Error } func GetSystemSettings() (*SystemSettings, error) { var settings SystemSettings - var wildcardDomain sql.NullString - err := db.QueryRow(`SELECT value FROM system_settings WHERE key = ?`, "wildcard_domain").Scan(&wildcardDomain) - if err != nil && err != sql.ErrNoRows { + wildcardVal, err := GetSystemSetting("wildcard_domain") + if err != nil { return nil, err } - if wildcardDomain.Valid && wildcardDomain.String != "" { - settings.WildcardDomain = &wildcardDomain.String + if wildcardVal != "" { + settings.WildcardDomain = &wildcardVal } - var mistAppName string - err = db.QueryRow(`SELECT value FROM system_settings WHERE key = ?`, "mist_app_name").Scan(&mistAppName) - if err != nil && err != sql.ErrNoRows { + mistAppName, err := GetSystemSetting("mist_app_name") + if err != nil { return nil, err } if mistAppName == "" { @@ -98,8 +112,6 @@ func GetSystemSettings() (*SystemSettings, error) { if err != nil { return nil, err } - // Default to empty string - same-origin requests are always allowed - // Users only need to configure this for cross-origin requests settings.AllowedOrigins = allowedOrigins prodMode, err := GetSystemSetting("production_mode") @@ -138,21 +150,12 @@ func UpdateSystemSettings(wildcardDomain *string, mistAppName string) (*SystemSe if wildcardDomain != nil { wildcardValue = *wildcardDomain } - _, err := db.Exec(` - INSERT INTO system_settings (key, value, updated_at) - VALUES (?, ?, CURRENT_TIMESTAMP) - ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = CURRENT_TIMESTAMP - `, "wildcard_domain", wildcardValue, wildcardValue) - if err != nil { + + if err := SetSystemSetting("wildcard_domain", wildcardValue); err != nil { return nil, err } - _, err = db.Exec(` - INSERT INTO system_settings (key, value, updated_at) - VALUES (?, ?, CURRENT_TIMESTAMP) - ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = CURRENT_TIMESTAMP - `, "mist_app_name", mistAppName, mistAppName) - if err != nil { + if err := SetSystemSetting("mist_app_name", mistAppName); err != nil { return nil, err } @@ -203,7 +206,6 @@ func UpdateDockerSettings(autoCleanupContainers, autoCleanupImages bool) error { return nil } -// UpdateSystemSettings updates all settings from the SystemSettings struct func (s *SystemSettings) UpdateSystemSettings() error { wildcardValue := "" if s.WildcardDomain != nil { @@ -272,3 +274,281 @@ func GenerateAutoDomain(projectName, appName string) (string, error) { return projectName + "-" + appName + "." + wildcardDomain, nil } + +//############################################################################################################ +//ARCHIVED CODE BELOW + +// package models + +// import ( +// "crypto/rand" +// "database/sql" +// "encoding/base64" +// "fmt" + +// "github.com/rs/zerolog/log" +// ) + +// type SystemSettings struct { +// WildcardDomain *string `json:"wildcardDomain"` +// MistAppName string `json:"mistAppName"` +// JwtSecret string `json:"-"` +// GithubWebhookSecret string `json:"-"` +// AllowedOrigins string `json:"allowedOrigins"` +// ProductionMode bool `json:"productionMode"` +// SecureCookies bool `json:"secureCookies"` +// AutoCleanupContainers bool `json:"autoCleanupContainers"` +// AutoCleanupImages bool `json:"autoCleanupImages"` +// } + +// func generateRandomSecret(length int) (string, error) { +// bytes := make([]byte, length) +// if _, err := rand.Read(bytes); err != nil { +// return "", err +// } +// return base64.URLEncoding.EncodeToString(bytes), nil +// } + +// func GetSystemSetting(key string) (string, error) { +// var value string +// err := db.QueryRow(`SELECT value FROM system_settings WHERE key = ?`, key).Scan(&value) +// if err == sql.ErrNoRows { +// return "", nil +// } +// if err != nil { +// return "", err +// } +// return value, nil +// } + +// func SetSystemSetting(key, value string) error { +// _, err := db.Exec(` +// INSERT INTO system_settings (key, value, updated_at) +// VALUES (?, ?, CURRENT_TIMESTAMP) +// ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = CURRENT_TIMESTAMP +// `, key, value, value) +// return err +// } + +// func GetSystemSettings() (*SystemSettings, error) { +// var settings SystemSettings + +// var wildcardDomain sql.NullString +// err := db.QueryRow(`SELECT value FROM system_settings WHERE key = ?`, "wildcard_domain").Scan(&wildcardDomain) +// if err != nil && err != sql.ErrNoRows { +// return nil, err +// } +// if wildcardDomain.Valid && wildcardDomain.String != "" { +// settings.WildcardDomain = &wildcardDomain.String +// } + +// var mistAppName string +// err = db.QueryRow(`SELECT value FROM system_settings WHERE key = ?`, "mist_app_name").Scan(&mistAppName) +// if err != nil && err != sql.ErrNoRows { +// return nil, err +// } +// if mistAppName == "" { +// mistAppName = "mist" +// } +// settings.MistAppName = mistAppName + +// jwtSecret, err := GetSystemSetting("jwt_secret") +// if err != nil { +// return nil, err +// } +// if jwtSecret == "" { +// jwtSecret, err = generateRandomSecret(64) +// if err != nil { +// return nil, fmt.Errorf("failed to generate JWT secret: %w", err) +// } +// if err := SetSystemSetting("jwt_secret", jwtSecret); err != nil { +// return nil, fmt.Errorf("failed to save JWT secret: %w", err) +// } +// log.Info().Msg("Auto-generated JWT secret and saved to database") +// } +// settings.JwtSecret = jwtSecret + +// githubSecret, err := GetSystemSetting("github_webhook_secret") +// if err != nil { +// return nil, err +// } +// settings.GithubWebhookSecret = githubSecret + +// allowedOrigins, err := GetSystemSetting("allowed_origins") +// if err != nil { +// return nil, err +// } +// // Default to empty string - same-origin requests are always allowed +// // Users only need to configure this for cross-origin requests +// settings.AllowedOrigins = allowedOrigins + +// prodMode, err := GetSystemSetting("production_mode") +// if err != nil { +// return nil, err +// } +// settings.ProductionMode = prodMode == "true" + +// secureCookies, err := GetSystemSetting("secure_cookies") +// if err != nil { +// return nil, err +// } +// if secureCookies == "" { +// settings.SecureCookies = settings.ProductionMode +// } else { +// settings.SecureCookies = secureCookies == "true" +// } + +// autoCleanupContainers, err := GetSystemSetting("auto_cleanup_containers") +// if err != nil { +// return nil, err +// } +// settings.AutoCleanupContainers = autoCleanupContainers == "true" + +// autoCleanupImages, err := GetSystemSetting("auto_cleanup_images") +// if err != nil { +// return nil, err +// } +// settings.AutoCleanupImages = autoCleanupImages == "true" + +// return &settings, nil +// } + +// func UpdateSystemSettings(wildcardDomain *string, mistAppName string) (*SystemSettings, error) { +// wildcardValue := "" +// if wildcardDomain != nil { +// wildcardValue = *wildcardDomain +// } +// _, err := db.Exec(` +// INSERT INTO system_settings (key, value, updated_at) +// VALUES (?, ?, CURRENT_TIMESTAMP) +// ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = CURRENT_TIMESTAMP +// `, "wildcard_domain", wildcardValue, wildcardValue) +// if err != nil { +// return nil, err +// } + +// _, err = db.Exec(` +// INSERT INTO system_settings (key, value, updated_at) +// VALUES (?, ?, CURRENT_TIMESTAMP) +// ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = CURRENT_TIMESTAMP +// `, "mist_app_name", mistAppName, mistAppName) +// if err != nil { +// return nil, err +// } + +// return GetSystemSettings() +// } + +// func UpdateSecuritySettings(allowedOrigins string, productionMode, secureCookies bool) error { +// if err := SetSystemSetting("allowed_origins", allowedOrigins); err != nil { +// return err +// } + +// prodModeStr := "false" +// if productionMode { +// prodModeStr = "true" +// } +// if err := SetSystemSetting("production_mode", prodModeStr); err != nil { +// return err +// } + +// secureCookiesStr := "false" +// if secureCookies { +// secureCookiesStr = "true" +// } +// if err := SetSystemSetting("secure_cookies", secureCookiesStr); err != nil { +// return err +// } + +// return nil +// } + +// func UpdateDockerSettings(autoCleanupContainers, autoCleanupImages bool) error { +// cleanupContainersStr := "false" +// if autoCleanupContainers { +// cleanupContainersStr = "true" +// } +// if err := SetSystemSetting("auto_cleanup_containers", cleanupContainersStr); err != nil { +// return err +// } + +// cleanupImagesStr := "false" +// if autoCleanupImages { +// cleanupImagesStr = "true" +// } +// if err := SetSystemSetting("auto_cleanup_images", cleanupImagesStr); err != nil { +// return err +// } + +// return nil +// } + +// // UpdateSystemSettings updates all settings from the SystemSettings struct +// func (s *SystemSettings) UpdateSystemSettings() error { +// wildcardValue := "" +// if s.WildcardDomain != nil { +// wildcardValue = *s.WildcardDomain +// } +// if err := SetSystemSetting("wildcard_domain", wildcardValue); err != nil { +// return err +// } + +// if err := SetSystemSetting("mist_app_name", s.MistAppName); err != nil { +// return err +// } + +// prodModeStr := "false" +// if s.ProductionMode { +// prodModeStr = "true" +// } +// if err := SetSystemSetting("production_mode", prodModeStr); err != nil { +// return err +// } + +// secureCookiesStr := "false" +// if s.SecureCookies { +// secureCookiesStr = "true" +// } +// if err := SetSystemSetting("secure_cookies", secureCookiesStr); err != nil { +// return err +// } + +// cleanupContainersStr := "false" +// if s.AutoCleanupContainers { +// cleanupContainersStr = "true" +// } +// if err := SetSystemSetting("auto_cleanup_containers", cleanupContainersStr); err != nil { +// return err +// } + +// cleanupImagesStr := "false" +// if s.AutoCleanupImages { +// cleanupImagesStr = "true" +// } +// if err := SetSystemSetting("auto_cleanup_images", cleanupImagesStr); err != nil { +// return err +// } + +// return nil +// } + +// func GenerateAutoDomain(projectName, appName string) (string, error) { +// settings, err := GetSystemSettings() +// if err != nil { +// return "", err +// } + +// if settings.WildcardDomain == nil || *settings.WildcardDomain == "" { +// return "", nil +// } + +// wildcardDomain := *settings.WildcardDomain +// if len(wildcardDomain) > 0 && wildcardDomain[0] == '*' { +// wildcardDomain = wildcardDomain[1:] +// } +// if len(wildcardDomain) > 0 && wildcardDomain[0] == '.' { +// wildcardDomain = wildcardDomain[1:] +// } + +// return projectName + "-" + appName + "." + wildcardDomain, nil +// } diff --git a/server/models/temp.go b/server/models/temp.go new file mode 100644 index 0000000..778a4c0 --- /dev/null +++ b/server/models/temp.go @@ -0,0 +1 @@ +package models \ No newline at end of file diff --git a/server/models/updateLog.go b/server/models/updateLog.go index 0169779..1d9da56 100644 --- a/server/models/updateLog.go +++ b/server/models/updateLog.go @@ -1,47 +1,45 @@ package models import ( - "database/sql" "strings" "time" "github.com/rs/zerolog/log" + "gorm.io/gorm" +) + +type UpdateStatus string + +const ( + UpdateStatusInProgress UpdateStatus = "in_progress" + UpdateStatusSuccess UpdateStatus = "success" + UpdateStatusFailed UpdateStatus = "failed" ) type UpdateLog struct { - ID int64 `json:"id"` - VersionFrom string `json:"versionFrom"` - VersionTo string `json:"versionTo"` - Status string `json:"status"` // in_progress, success, failed - Logs string `json:"logs"` - ErrorMessage *string `json:"errorMessage"` - StartedBy int64 `json:"startedBy"` - StartedAt time.Time `json:"startedAt"` - CompletedAt *time.Time `json:"completedAt"` - Username string `json:"username"` + ID int64 `gorm:"primaryKey;autoIncrement:true" json:"id"` + VersionFrom string `gorm:"not null" json:"version_from"` + VersionTo string `gorm:"not null" json:"version_to"` + Status UpdateStatus `gorm:"index;not null" json:"status"` + Logs *string `json:"logs"` + ErrorMessage *string `json:"error_message"` + StartedBy int64 `gorm:"not null;constraint:OnDelete:CASCADE" json:"started_by"` + StartedAt time.Time `gorm:"autoCreateTime;index:,sort:desc" json:"started_at"` + CompletedAt *time.Time `json:"completed_at"` + Username *string `gorm:"-" json:"username"` } func CreateUpdateLog(versionFrom, versionTo string, startedBy int64) (*UpdateLog, error) { - query := ` - INSERT INTO update_logs (version_from, version_to, status, logs, started_by, started_at) - VALUES (?, ?, 'in_progress', '', ?, CURRENT_TIMESTAMP) - RETURNING id, version_from, version_to, status, logs, error_message, started_by, started_at, completed_at - ` - - updateLog := &UpdateLog{} - err := db.QueryRow(query, versionFrom, versionTo, startedBy).Scan( - &updateLog.ID, - &updateLog.VersionFrom, - &updateLog.VersionTo, - &updateLog.Status, - &updateLog.Logs, - &updateLog.ErrorMessage, - &updateLog.StartedBy, - &updateLog.StartedAt, - &updateLog.CompletedAt, - ) + emptyLogs := "" + updateLog := &UpdateLog{ + VersionFrom: versionFrom, + VersionTo: versionTo, + Status: UpdateStatusInProgress, + Logs: &emptyLogs, + StartedBy: startedBy, + } - if err != nil { + if err := db.Create(updateLog).Error; err != nil { log.Error().Err(err).Msg("Failed to create update log") return nil, err } @@ -56,14 +54,14 @@ func CreateUpdateLog(versionFrom, versionTo string, startedBy int64) (*UpdateLog return updateLog, nil } -func UpdateUpdateLogStatus(id int64, status string, logs string, errorMessage *string) error { - query := ` - UPDATE update_logs - SET status = ?, logs = ?, error_message = ?, completed_at = CURRENT_TIMESTAMP - WHERE id = ? - ` +func UpdateUpdateLogStatus(id int64, status UpdateStatus, logs string, errorMessage *string) error { + err := db.Model(&UpdateLog{ID: id}).Updates(map[string]interface{}{ + "status": status, + "logs": logs, + "error_message": errorMessage, + "completed_at": time.Now(), + }).Error - _, err := db.Exec(query, status, logs, errorMessage, id) if err != nil { log.Error().Err(err).Int64("update_log_id", id).Msg("Failed to update log status") return err @@ -71,20 +69,16 @@ func UpdateUpdateLogStatus(id int64, status string, logs string, errorMessage *s log.Info(). Int64("update_log_id", id). - Str("status", status). + Str("status", string(status)). Msg("Update log status updated") return nil } func AppendUpdateLog(id int64, logLine string) error { - query := ` - UPDATE update_logs - SET logs = logs || ? - WHERE id = ? - ` + err := db.Model(&UpdateLog{ID: id}). + Update("logs", gorm.Expr("logs || ?", logLine+"\n")).Error - _, err := db.Exec(query, logLine+"\n", id) if err != nil { log.Error().Err(err).Int64("update_log_id", id).Msg("Failed to append log line") return err @@ -94,84 +88,42 @@ func AppendUpdateLog(id int64, logLine string) error { } func GetUpdateLogs(limit int) ([]UpdateLog, error) { - query := ` - SELECT - ul.id, ul.version_from, ul.version_to, ul.status, - ul.logs, ul.error_message, ul.started_by, ul.started_at, - ul.completed_at, u.username - FROM update_logs ul - LEFT JOIN users u ON ul.started_by = u.id - ORDER BY ul.started_at DESC - LIMIT ? - ` - - rows, err := db.Query(query, limit) + var logs []UpdateLog + + err := db.Table("update_logs"). + Select("update_logs.*, users.username"). + Joins("LEFT JOIN users ON update_logs.started_by = users.id"). + Order("update_logs.started_at DESC"). + Limit(limit). + Scan(&logs).Error + if err != nil { log.Error().Err(err).Msg("Failed to query update logs") return nil, err } - defer rows.Close() - - var logs []UpdateLog - for rows.Next() { - var updateLog UpdateLog - err := rows.Scan( - &updateLog.ID, - &updateLog.VersionFrom, - &updateLog.VersionTo, - &updateLog.Status, - &updateLog.Logs, - &updateLog.ErrorMessage, - &updateLog.StartedBy, - &updateLog.StartedAt, - &updateLog.CompletedAt, - &updateLog.Username, - ) - if err != nil { - log.Error().Err(err).Msg("Failed to scan update log row") - return nil, err - } - logs = append(logs, updateLog) - } return logs, nil } func GetUpdateLogByID(id int64) (*UpdateLog, error) { - query := ` - SELECT - ul.id, ul.version_from, ul.version_to, ul.status, - ul.logs, ul.error_message, ul.started_by, ul.started_at, - ul.completed_at, u.username - FROM update_logs ul - LEFT JOIN users u ON ul.started_by = u.id - WHERE ul.id = ? - ` - - updateLog := &UpdateLog{} - err := db.QueryRow(query, id).Scan( - &updateLog.ID, - &updateLog.VersionFrom, - &updateLog.VersionTo, - &updateLog.Status, - &updateLog.Logs, - &updateLog.ErrorMessage, - &updateLog.StartedBy, - &updateLog.StartedAt, - &updateLog.CompletedAt, - &updateLog.Username, - ) - - if err == sql.ErrNoRows { - return nil, nil - } + var updateLog UpdateLog + + err := db.Table("update_logs"). + Select("update_logs.*, users.username"). + Joins("LEFT JOIN users ON update_logs.started_by = users.id"). + Where("update_logs.id = ?", id). + Scan(&updateLog).Error if err != nil { log.Error().Err(err).Int64("update_log_id", id).Msg("Failed to get update log by ID") return nil, err } - return updateLog, nil + if updateLog.ID == 0 { + return nil, nil + } + + return &updateLog, nil } func GetUpdateLogsAsString() (string, error) { @@ -195,12 +147,17 @@ func GetUpdateLogsAsString() (string, error) { builder.WriteString(log.VersionTo) builder.WriteString("\n") builder.WriteString("Status: ") - builder.WriteString(log.Status) + builder.WriteString(string(log.Status)) builder.WriteString("\n") builder.WriteString("Started: ") builder.WriteString(log.StartedAt.Format("2006-01-02 15:04:05")) builder.WriteString(" by ") - builder.WriteString(log.Username) + if log.Username != nil { + builder.WriteString(*log.Username) + } else { + builder.WriteString("unknown") + } + builder.WriteString("\n") if log.CompletedAt != nil { builder.WriteString("Completed: ") @@ -217,3 +174,413 @@ func GetUpdateLogsAsString() (string, error) { return builder.String(), nil } + +func CheckAndCompletePendingUpdates() error { + logs, err := GetUpdateLogs(1) + if err != nil { + return err + } + + if len(logs) == 0 { + return nil + } + + latestLog := logs[0] + + if latestLog.Status != UpdateStatusInProgress { + return nil + } + + log.Info(). + Int64("update_log_id", latestLog.ID). + Str("from_version", latestLog.VersionFrom). + Str("to_version", latestLog.VersionTo). + Str("age", time.Since(latestLog.StartedAt).String()). + Msg("Found in-progress update on startup, checking status") + + currentVersion, err := GetSystemSetting("version") + if err != nil { + log.Error().Err(err).Msg("Failed to get current version for update completion check") + return err + } + + if currentVersion == "" { + currentVersion = "1.0.0" + } + + if currentVersion == latestLog.VersionTo { + log.Info(). + Int64("update_log_id", latestLog.ID). + Str("version", currentVersion). + Msg("Completing successful update that was interrupted by service restart") + + existing := "" + if latestLog.Logs != nil { + existing = *latestLog.Logs + } + + completionLog := existing + "\n✅ Update completed successfully (verified on restart)\n" + + err = UpdateUpdateLogStatus(latestLog.ID, UpdateStatusSuccess, completionLog, nil) + if err != nil { + log.Error().Err(err).Int64("update_log_id", latestLog.ID).Msg("Failed to complete pending update") + return err + } + + log.Info(). + Int64("update_log_id", latestLog.ID). + Str("from", latestLog.VersionFrom). + Str("to", latestLog.VersionTo). + Msg("Successfully completed pending update") + return nil + } + + log.Warn(). + Int64("update_log_id", latestLog.ID). + Str("expected_version", latestLog.VersionTo). + Str("current_version", currentVersion). + Str("age", time.Since(latestLog.StartedAt).String()). + Msg("Update appears to have failed (version mismatch detected on startup)") + + errMsg := "Update process was interrupted and version does not match target" + existing := "" + if latestLog.Logs != nil { + existing = *latestLog.Logs + } + + failureLog := existing + "\n❌ " + errMsg + "\n" + err = UpdateUpdateLogStatus(latestLog.ID, UpdateStatusFailed, failureLog, &errMsg) + if err != nil { + log.Error().Err(err).Int64("update_log_id", latestLog.ID).Msg("Failed to mark failed update") + return err + } + + log.Info(). + Int64("update_log_id", latestLog.ID). + Msg("Marked failed update as failed") + + return nil +} + +//############################################################################################################################ +//ARCHIVED CODE BELOW + +// package models + +// import ( +// "database/sql" +// "strings" +// "time" + +// "github.com/rs/zerolog/log" +// ) + +// type UpdateStatus string + +// const ( +// UpdateStatusInProgress UpdateStatus = "in_progress" +// UpdateStatusSuccess UpdateStatus = "success" +// UpdateStatusFailed UpdateStatus = "failed" +// ) + +// type UpdateLog struct { +// ID int64 +// VersionFrom string +// VersionTo string +// Status UpdateStatus +// Logs *string +// ErrorMessage *string +// StartedBy int64 +// StartedAt time.Time +// CompletedAt *time.Time +// Username *string +// } + +// func CreateUpdateLog(versionFrom, versionTo string, startedBy int64) (*UpdateLog, error) { +// query := ` +// INSERT INTO update_logs (version_from, version_to, status, logs, started_by, started_at) +// VALUES (?, ?, 'in_progress', '', ?, CURRENT_TIMESTAMP) +// RETURNING id, version_from, version_to, status, logs, error_message, started_by, started_at, completed_at +// ` + +// updateLog := &UpdateLog{} +// err := db.QueryRow(query, versionFrom, versionTo, startedBy).Scan( +// &updateLog.ID, +// &updateLog.VersionFrom, +// &updateLog.VersionTo, +// &updateLog.Status, +// &updateLog.Logs, +// &updateLog.ErrorMessage, +// &updateLog.StartedBy, +// &updateLog.StartedAt, +// &updateLog.CompletedAt, +// ) + +// if err != nil { +// log.Error().Err(err).Msg("Failed to create update log") +// return nil, err +// } + +// log.Info(). +// Int64("update_log_id", updateLog.ID). +// Str("from", versionFrom). +// Str("to", versionTo). +// Int64("started_by", startedBy). +// Msg("Update log created") + +// return updateLog, nil +// } + +// func UpdateUpdateLogStatus(id int64, status UpdateStatus, logs string, errorMessage *string) error { +// query := ` +// UPDATE update_logs +// SET status = ?, logs = ?, error_message = ?, completed_at = CURRENT_TIMESTAMP +// WHERE id = ? +// ` + +// _, err := db.Exec(query, status, logs, errorMessage, id) +// if err != nil { +// log.Error().Err(err).Int64("update_log_id", id).Msg("Failed to update log status") +// return err +// } + +// log.Info(). +// Int64("update_log_id", id). +// Str("status", string(status)). +// Msg("Update log status updated") + +// return nil +// } + +// func AppendUpdateLog(id int64, logLine string) error { +// query := ` +// UPDATE update_logs +// SET logs = logs || ? +// WHERE id = ? +// ` + +// _, err := db.Exec(query, logLine+"\n", id) +// if err != nil { +// log.Error().Err(err).Int64("update_log_id", id).Msg("Failed to append log line") +// return err +// } + +// return nil +// } + +// func GetUpdateLogs(limit int) ([]UpdateLog, error) { +// query := ` +// SELECT +// ul.id, ul.version_from, ul.version_to, ul.status, +// ul.logs, ul.error_message, ul.started_by, ul.started_at, +// ul.completed_at, u.username +// FROM update_logs ul +// LEFT JOIN users u ON ul.started_by = u.id +// ORDER BY ul.started_at DESC +// LIMIT ? +// ` + +// rows, err := db.Query(query, limit) +// if err != nil { +// log.Error().Err(err).Msg("Failed to query update logs") +// return nil, err +// } +// defer rows.Close() + +// var logs []UpdateLog +// for rows.Next() { +// var updateLog UpdateLog +// err := rows.Scan( +// &updateLog.ID, +// &updateLog.VersionFrom, +// &updateLog.VersionTo, +// &updateLog.Status, +// &updateLog.Logs, +// &updateLog.ErrorMessage, +// &updateLog.StartedBy, +// &updateLog.StartedAt, +// &updateLog.CompletedAt, +// &updateLog.Username, +// ) +// if err != nil { +// log.Error().Err(err).Msg("Failed to scan update log row") +// return nil, err +// } +// logs = append(logs, updateLog) +// } + +// return logs, nil +// } + +// func GetUpdateLogByID(id int64) (*UpdateLog, error) { +// query := ` +// SELECT +// ul.id, ul.version_from, ul.version_to, ul.status, +// ul.logs, ul.error_message, ul.started_by, ul.started_at, +// ul.completed_at, u.username +// FROM update_logs ul +// LEFT JOIN users u ON ul.started_by = u.id +// WHERE ul.id = ? +// ` + +// updateLog := &UpdateLog{} +// err := db.QueryRow(query, id).Scan( +// &updateLog.ID, +// &updateLog.VersionFrom, +// &updateLog.VersionTo, +// &updateLog.Status, +// &updateLog.Logs, +// &updateLog.ErrorMessage, +// &updateLog.StartedBy, +// &updateLog.StartedAt, +// &updateLog.CompletedAt, +// &updateLog.Username, +// ) + +// if err == sql.ErrNoRows { +// return nil, nil +// } + +// if err != nil { +// log.Error().Err(err).Int64("update_log_id", id).Msg("Failed to get update log by ID") +// return nil, err +// } + +// return updateLog, nil +// } + +// func GetUpdateLogsAsString() (string, error) { +// logs, err := GetUpdateLogs(10) +// if err != nil { +// return "", err +// } + +// if len(logs) == 0 { +// return "No update history available", nil +// } + +// var builder strings.Builder +// builder.WriteString("Recent Update History:\n") +// builder.WriteString("======================\n\n") + +// for _, log := range logs { +// builder.WriteString("Version: ") +// builder.WriteString(log.VersionFrom) +// builder.WriteString(" → ") +// builder.WriteString(log.VersionTo) +// builder.WriteString("\n") +// builder.WriteString("Status: ") +// builder.WriteString(string(log.Status)) +// builder.WriteString("\n") +// builder.WriteString("Started: ") +// builder.WriteString(log.StartedAt.Format("2006-01-02 15:04:05")) +// builder.WriteString(" by ") +// if log.Username != nil { +// builder.WriteString(*log.Username) +// } else { +// builder.WriteString("unknown") +// } + +// builder.WriteString("\n") +// if log.CompletedAt != nil { +// builder.WriteString("Completed: ") +// builder.WriteString(log.CompletedAt.Format("2006-01-02 15:04:05")) +// builder.WriteString("\n") +// } +// if log.ErrorMessage != nil && *log.ErrorMessage != "" { +// builder.WriteString("Error: ") +// builder.WriteString(*log.ErrorMessage) +// builder.WriteString("\n") +// } +// builder.WriteString("\n") +// } + +// return builder.String(), nil +// } + +// func CheckAndCompletePendingUpdates() error { +// logs, err := GetUpdateLogs(1) +// if err != nil { +// return err +// } + +// if len(logs) == 0 { +// return nil +// } + +// latestLog := logs[0] + +// if latestLog.Status != UpdateStatusInProgress { +// return nil +// } + +// log.Info(). +// Int64("update_log_id", latestLog.ID). +// Str("from_version", latestLog.VersionFrom). +// Str("to_version", latestLog.VersionTo). +// Str("age", time.Since(latestLog.StartedAt).String()). +// Msg("Found in-progress update on startup, checking status") + +// currentVersion, err := GetSystemSetting("version") +// if err != nil { +// log.Error().Err(err).Msg("Failed to get current version for update completion check") +// return err +// } + +// if currentVersion == "" { +// currentVersion = "1.0.0" +// } + +// if currentVersion == latestLog.VersionTo { +// log.Info(). +// Int64("update_log_id", latestLog.ID). +// Str("version", currentVersion). +// Msg("Completing successful update that was interrupted by service restart") + +// existing := "" +// if latestLog.Logs != nil { +// existing = *latestLog.Logs +// } + +// completionLog := existing + "\n✅ Update completed successfully (verified on restart)\n" + +// err = UpdateUpdateLogStatus(latestLog.ID, UpdateStatusSuccess, completionLog, nil) +// if err != nil { +// log.Error().Err(err).Int64("update_log_id", latestLog.ID).Msg("Failed to complete pending update") +// return err +// } + +// log.Info(). +// Int64("update_log_id", latestLog.ID). +// Str("from", latestLog.VersionFrom). +// Str("to", latestLog.VersionTo). +// Msg("Successfully completed pending update") +// return nil +// } + +// log.Warn(). +// Int64("update_log_id", latestLog.ID). +// Str("expected_version", latestLog.VersionTo). +// Str("current_version", currentVersion). +// Str("age", time.Since(latestLog.StartedAt).String()). +// Msg("Update appears to have failed (version mismatch detected on startup)") + +// errMsg := "Update process was interrupted and version does not match target" +// existing := "" +// if latestLog.Logs != nil { +// existing = *latestLog.Logs +// } + +// failureLog := existing + "\n❌ " + errMsg + "\n" +// err = UpdateUpdateLogStatus(latestLog.ID, UpdateStatusFailed, failureLog, &errMsg) +// if err != nil { +// log.Error().Err(err).Int64("update_log_id", latestLog.ID).Msg("Failed to mark failed update") +// return err +// } + +// log.Info(). +// Int64("update_log_id", latestLog.ID). +// Msg("Marked failed update as failed") + +// return nil +// } diff --git a/server/models/user.go b/server/models/user.go index 2eefe21..3f13a17 100644 --- a/server/models/user.go +++ b/server/models/user.go @@ -5,30 +5,53 @@ import ( "github.com/corecollectives/mist/utils" "golang.org/x/crypto/bcrypt" + "gorm.io/gorm" ) type User struct { - ID int64 `json:"id"` - Username string `json:"username"` - Email string `json:"email"` - PasswordHash string `json:"-"` - Role string `json:"role"` - AvatarURL *string `json:"avatarUrl"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID int64 `gorm:"primaryKey;autoIncrement:false" json:"id"` + Username string `gorm:"uniqueIndex;not null" json:"username"` + Email string `gorm:"uniqueIndex;not null" json:"email"` + PasswordHash string `gorm:"not null" json:"-"` + Role string `gorm:"default:'user';index" json:"role"` + + FullName *string `json:"fullName,omitempty"` + AvatarURL *string `json:"avatarUrl,omitempty"` + Bio *string `json:"bio,omitempty"` + + EmailVerified bool `gorm:"default:false" json:"emailVerified"` + EmailVerificationToken *string `json:"-"` + EmailVerificationSentAt *time.Time `json:"-"` + + PasswordResetToken *string `json:"-"` + PasswordResetExpiresAt *time.Time `json:"-"` + PasswordChangedAt *time.Time `json:"-"` + + TwoFactorEnabled bool `gorm:"default:false" json:"twoFactorEnabled"` + TwoFactorSecret *string `json:"-"` + TwoFactorBackupCodes *string `json:"-"` + + LastLoginAt *time.Time `json:"lastLoginAt,omitempty"` + LastLoginIP *string `json:"lastLoginIp,omitempty"` + FailedLoginAttempts int `gorm:"default:0" json:"failedLoginAttempts"` + AccountLockedUntil *time.Time `json:"accountLockedUntil,omitempty"` + + Timezone string `gorm:"default:'UTC'" json:"timezone"` + Language string `gorm:"default:'en'" json:"language"` + NotificationPreferences *string `json:"notificationPreferences,omitempty"` + + IsActive bool `gorm:"default:true;index" json:"isActive"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"` + UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updatedAt"` + DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt,omitempty"` } func (u *User) Create() error { - query := ` - INSERT INTO users (id, username, email, password_hash, role, avatar_url) - VALUES ($1, $2, $3, $4, $5, $6) - RETURNING id, username, email, role, avatar_url, created_at, updated_at - ` u.ID = utils.GenerateRandomId() - err := db.QueryRow(query, u.ID, u.Username, u.Email, u.PasswordHash, u.Role, u.AvatarURL).Scan( - &u.ID, &u.Username, &u.Email, &u.Role, &u.AvatarURL, &u.CreatedAt, &u.UpdatedAt, - ) - return err + if u.Role == "" { + u.Role = "user" + } + return db.Create(u).Error } func (u *User) SetPassword(password string) error { @@ -41,45 +64,35 @@ func (u *User) SetPassword(password string) error { } func GetUserByID(userID int64) (*User, error) { - query := ` - SELECT id, username, email, role, avatar_url, created_at, updated_at - FROM users - WHERE id = $1 - ` - user := &User{} - err := db.QueryRow(query, userID).Scan( - &user.ID, &user.Username, &user.Email, &user.Role, &user.AvatarURL, &user.CreatedAt, &user.UpdatedAt, - ) + var user User + err := db.First(&user, userID).Error if err != nil { return nil, err } - return user, nil + return &user, nil } func DeleteUserByID(userID int64) error { - query := `DELETE FROM users WHERE id = $1` - _, err := db.Exec(query, userID) - return err + return db.Delete(&User{}, userID).Error } func UpdateUser(u *User) error { - query := ` - UPDATE users - SET username = $1, email = $2, role = $3, avatar_url = $4, updated_at = CURRENT_TIMESTAMP - WHERE id = $5 - RETURNING updated_at - ` - return db.QueryRow(query, u.Username, u.Email, u.Role, u.AvatarURL, u.ID).Scan(&u.UpdatedAt) + return db.Model(u).Updates(map[string]interface{}{ + "username": u.Username, + "email": u.Email, + "role": u.Role, + "avatar_url": u.AvatarURL, + "updated_at": time.Now(), + }).Error } func (u *User) MatchPassword(password string) bool { - query := ` - SELECT password_hash - FROM users - WHERE id = $1 - ` var storedHash string - err := db.QueryRow(query, u.ID).Scan(&storedHash) + err := db.Model(&User{}). + Select("password_hash"). + Where("id = ?", u.ID). + Scan(&storedHash).Error + if err != nil { return false } @@ -88,101 +101,250 @@ func (u *User) MatchPassword(password string) bool { } func (u *User) UpdatePassword() error { - query := ` - UPDATE users - SET password_hash = $1, updated_at = CURRENT_TIMESTAMP - WHERE id = $2 - RETURNING updated_at - ` - return db.QueryRow(query, u.PasswordHash, u.ID).Scan(&u.UpdatedAt) - + return db.Model(u).Updates(map[string]interface{}{ + "password_hash": u.PasswordHash, + "updated_at": time.Now(), + }).Error } func GetUserByEmail(email string) (*User, error) { - query := ` - SELECT id, username, email, role, avatar_url, created_at, updated_at - FROM users - WHERE email = $1 - ` - user := &User{} - err := db.QueryRow(query, email).Scan( - &user.ID, &user.Username, &user.Email, &user.Role, &user.AvatarURL, &user.CreatedAt, &user.UpdatedAt, - ) + var user User + err := db.Where("email = ?", email).First(&user).Error if err != nil { return nil, err } - return user, nil + return &user, nil } func GetUserByUsername(username string) (*User, error) { - query := ` - SELECT id, username, email, role, avatar_url, created_at, updated_at - FROM users - WHERE username = $1 - ` - user := &User{} - err := db.QueryRow(query, username).Scan( - &user.ID, &user.Username, &user.Email, &user.Role, &user.AvatarURL, &user.CreatedAt, &user.UpdatedAt, - ) + var user User + err := db.Where("username = ?", username).First(&user).Error if err != nil { return nil, err } - return user, nil + return &user, nil } func UpdateUserPassword(userID int64, passwordHash string) error { - query := ` - UPDATE users - SET password_hash = $1, updated_at = CURRENT_TIMESTAMP - WHERE id = $2 - ` - _, err := db.Exec(query, passwordHash, userID) - return err + return db.Model(&User{ID: userID}).Updates(map[string]interface{}{ + "password_hash": passwordHash, + "updated_at": time.Now(), + }).Error } func GetAllUsers() ([]User, error) { - query := ` - SELECT id, username, email, role, avatar_url, created_at, updated_at - FROM users - ` - rows, err := db.Query(query) - if err != nil { - return nil, err - } - defer rows.Close() - var users []User - for rows.Next() { - user := User{} - err := rows.Scan(&user.ID, &user.Username, &user.Email, &user.Role, &user.AvatarURL, &user.CreatedAt, &user.UpdatedAt) - if err != nil { - return nil, err - } - users = append(users, user) - } - return users, nil + err := db.Find(&users).Error + return users, err } func GetUserRole(userID int64) (string, error) { - query := ` - SELECT role - FROM users - WHERE id = $1 - ` var role string - err := db.QueryRow(query, userID).Scan(&role) + err := db.Model(&User{}). + Select("role"). + Where("id = ?", userID). + Scan(&role).Error + if err != nil { return "", err } return role, nil } -func GetUserCount() (int, error) { - query := `SELECT COUNT(*) FROM users` - var count int - err := db.QueryRow(query).Scan(&count) - if err != nil { - return 0, err - } - return count, nil +func GetUserCount() (int64, error) { + var count int64 + err := db.Model(&User{}).Count(&count).Error + return count, err } + +//############################################################################################################## +//ARCHIVED CODE BELOW + +// package models + +// import ( +// "time" + +// "github.com/corecollectives/mist/utils" +// "golang.org/x/crypto/bcrypt" +// ) + +// type User struct { +// ID int64 `json:"id"` +// Username string `json:"username"` +// Email string `json:"email"` +// PasswordHash string `json:"-"` +// Role string `json:"role"` +// AvatarURL *string `json:"avatarUrl"` +// CreatedAt time.Time `json:"createdAt"` +// UpdatedAt time.Time `json:"updatedAt"` +// } + +// func (u *User) Create() error { +// query := ` +// INSERT INTO users (id, username, email, password_hash, role, avatar_url) +// VALUES ($1, $2, $3, $4, $5, $6) +// RETURNING id, username, email, role, avatar_url, created_at, updated_at +// ` +// u.ID = utils.GenerateRandomId() +// err := db.QueryRow(query, u.ID, u.Username, u.Email, u.PasswordHash, u.Role, u.AvatarURL).Scan( +// &u.ID, &u.Username, &u.Email, &u.Role, &u.AvatarURL, &u.CreatedAt, &u.UpdatedAt, +// ) +// return err +// } + +// func (u *User) SetPassword(password string) error { +// hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) +// if err != nil { +// return err +// } +// u.PasswordHash = string(hashedPassword) +// return nil +// } + +// func GetUserByID(userID int64) (*User, error) { +// query := ` +// SELECT id, username, email, role, avatar_url, created_at, updated_at +// FROM users +// WHERE id = $1 +// ` +// user := &User{} +// err := db.QueryRow(query, userID).Scan( +// &user.ID, &user.Username, &user.Email, &user.Role, &user.AvatarURL, &user.CreatedAt, &user.UpdatedAt, +// ) +// if err != nil { +// return nil, err +// } +// return user, nil +// } + +// func DeleteUserByID(userID int64) error { +// query := `DELETE FROM users WHERE id = $1` +// _, err := db.Exec(query, userID) +// return err +// } + +// func UpdateUser(u *User) error { +// query := ` +// UPDATE users +// SET username = $1, email = $2, role = $3, avatar_url = $4, updated_at = CURRENT_TIMESTAMP +// WHERE id = $5 +// RETURNING updated_at +// ` +// return db.QueryRow(query, u.Username, u.Email, u.Role, u.AvatarURL, u.ID).Scan(&u.UpdatedAt) +// } + +// func (u *User) MatchPassword(password string) bool { +// query := ` +// SELECT password_hash +// FROM users +// WHERE id = $1 +// ` +// var storedHash string +// err := db.QueryRow(query, u.ID).Scan(&storedHash) +// if err != nil { +// return false +// } + +// return bcrypt.CompareHashAndPassword([]byte(storedHash), []byte(password)) == nil +// } + +// func (u *User) UpdatePassword() error { +// query := ` +// UPDATE users +// SET password_hash = $1, updated_at = CURRENT_TIMESTAMP +// WHERE id = $2 +// RETURNING updated_at +// ` +// return db.QueryRow(query, u.PasswordHash, u.ID).Scan(&u.UpdatedAt) + +// } + +// func GetUserByEmail(email string) (*User, error) { +// query := ` +// SELECT id, username, email, role, avatar_url, created_at, updated_at +// FROM users +// WHERE email = $1 +// ` +// user := &User{} +// err := db.QueryRow(query, email).Scan( +// &user.ID, &user.Username, &user.Email, &user.Role, &user.AvatarURL, &user.CreatedAt, &user.UpdatedAt, +// ) +// if err != nil { +// return nil, err +// } +// return user, nil +// } + +// func GetUserByUsername(username string) (*User, error) { +// query := ` +// SELECT id, username, email, role, avatar_url, created_at, updated_at +// FROM users +// WHERE username = $1 +// ` +// user := &User{} +// err := db.QueryRow(query, username).Scan( +// &user.ID, &user.Username, &user.Email, &user.Role, &user.AvatarURL, &user.CreatedAt, &user.UpdatedAt, +// ) +// if err != nil { +// return nil, err +// } +// return user, nil +// } + +// func UpdateUserPassword(userID int64, passwordHash string) error { +// query := ` +// UPDATE users +// SET password_hash = $1, updated_at = CURRENT_TIMESTAMP +// WHERE id = $2 +// ` +// _, err := db.Exec(query, passwordHash, userID) +// return err +// } + +// func GetAllUsers() ([]User, error) { +// query := ` +// SELECT id, username, email, role, avatar_url, created_at, updated_at +// FROM users +// ` +// rows, err := db.Query(query) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// var users []User +// for rows.Next() { +// user := User{} +// err := rows.Scan(&user.ID, &user.Username, &user.Email, &user.Role, &user.AvatarURL, &user.CreatedAt, &user.UpdatedAt) +// if err != nil { +// return nil, err +// } +// users = append(users, user) +// } +// return users, nil +// } + +// func GetUserRole(userID int64) (string, error) { +// query := ` +// SELECT role +// FROM users +// WHERE id = $1 +// ` +// var role string +// err := db.QueryRow(query, userID).Scan(&role) +// if err != nil { +// return "", err +// } +// return role, nil +// } + +// func GetUserCount() (int, error) { +// query := `SELECT COUNT(*) FROM users` +// var count int +// err := db.QueryRow(query).Scan(&count) +// if err != nil { +// return 0, err +// } +// return count, nil +// } diff --git a/server/models/volume.go b/server/models/volume.go index e2e88d8..cc3fc74 100644 --- a/server/models/volume.go +++ b/server/models/volume.go @@ -1,18 +1,19 @@ package models import ( - "database/sql" "time" + + "gorm.io/gorm" ) type Volume struct { - ID int64 `db:"id" json:"id"` - AppID int64 `db:"app_id" json:"appId"` - Name string `db:"name" json:"name"` - HostPath string `db:"host_path" json:"hostPath"` - ContainerPath string `db:"container_path" json:"containerPath"` - ReadOnly bool `db:"read_only" json:"readOnly"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` + ID int64 `gorm:"primaryKey;autoIncrement:true" json:"id"` + AppID int64 `gorm:"uniqueIndex:idx_app_vol_name;not null;constraint:OnDelete:CASCADE" json:"appId"` + Name string `gorm:"uniqueIndex:idx_app_vol_name;not null" json:"name"` + HostPath string `gorm:"not null" json:"hostPath"` + ContainerPath string `gorm:"not null" json:"containerPath"` + ReadOnly bool `gorm:"default:false" json:"readOnly"` + CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"` } func (v *Volume) ToJson() map[string]interface{} { @@ -28,78 +29,156 @@ func (v *Volume) ToJson() map[string]interface{} { } func GetVolumesByAppID(appID int64) ([]Volume, error) { - query := ` - SELECT id, app_id, name, host_path, container_path, read_only, created_at - FROM volumes - WHERE app_id = ? - ORDER BY created_at DESC - ` - rows, err := db.Query(query, appID) - if err != nil { - return nil, err - } - defer rows.Close() - var volumes []Volume - for rows.Next() { - var vol Volume - err := rows.Scan(&vol.ID, &vol.AppID, &vol.Name, &vol.HostPath, &vol.ContainerPath, &vol.ReadOnly, &vol.CreatedAt) - if err != nil { - return nil, err - } - volumes = append(volumes, vol) - } - - return volumes, rows.Err() + err := db.Where("app_id = ?", appID).Order("created_at DESC").Find(&volumes).Error + return volumes, err } func GetVolumeByID(id int64) (*Volume, error) { - query := ` - SELECT id, app_id, name, host_path, container_path, read_only, created_at - FROM volumes - WHERE id = ? - ` var vol Volume - err := db.QueryRow(query, id).Scan(&vol.ID, &vol.AppID, &vol.Name, &vol.HostPath, &vol.ContainerPath, &vol.ReadOnly, &vol.CreatedAt) - if err == sql.ErrNoRows { - return nil, nil - } + err := db.First(&vol, id).Error if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } return nil, err } return &vol, nil } func CreateVolume(appID int64, name, hostPath, containerPath string, readOnly bool) (*Volume, error) { - query := ` - INSERT INTO volumes (app_id, name, host_path, container_path, read_only, created_at) - VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) - ` - result, err := db.Exec(query, appID, name, hostPath, containerPath, readOnly) - if err != nil { - return nil, err + vol := &Volume{ + AppID: appID, + Name: name, + HostPath: hostPath, + ContainerPath: containerPath, + ReadOnly: readOnly, } - - id, err := result.LastInsertId() + err := db.Create(vol).Error if err != nil { return nil, err } - - return GetVolumeByID(id) + return vol, nil } func UpdateVolume(id int64, name, hostPath, containerPath string, readOnly bool) error { - query := ` - UPDATE volumes - SET name = ?, host_path = ?, container_path = ?, read_only = ? - WHERE id = ? - ` - _, err := db.Exec(query, name, hostPath, containerPath, readOnly, id) - return err + return db.Model(&Volume{ID: id}).Updates(map[string]interface{}{ + "name": name, + "host_path": hostPath, + "container_path": containerPath, + "read_only": readOnly, + }).Error } func DeleteVolume(id int64) error { - query := `DELETE FROM volumes WHERE id = ?` - _, err := db.Exec(query, id) - return err + return db.Delete(&Volume{}, id).Error } + +//############################################################################################################## +//ARCHIVED CODE BELOW + +// package models + +// import ( +// "database/sql" +// "time" +// ) + +// type Volume struct { +// ID int64 `db:"id" json:"id"` +// AppID int64 `db:"app_id" json:"appId"` +// Name string `db:"name" json:"name"` +// HostPath string `db:"host_path" json:"hostPath"` +// ContainerPath string `db:"container_path" json:"containerPath"` +// ReadOnly bool `db:"read_only" json:"readOnly"` +// CreatedAt time.Time `db:"created_at" json:"createdAt"` +// } + +// func (v *Volume) ToJson() map[string]interface{} { +// return map[string]interface{}{ +// "id": v.ID, +// "appId": v.AppID, +// "name": v.Name, +// "hostPath": v.HostPath, +// "containerPath": v.ContainerPath, +// "readOnly": v.ReadOnly, +// "createdAt": v.CreatedAt, +// } +// } + +// func GetVolumesByAppID(appID int64) ([]Volume, error) { +// query := ` +// SELECT id, app_id, name, host_path, container_path, read_only, created_at +// FROM volumes +// WHERE app_id = ? +// ORDER BY created_at DESC +// ` +// rows, err := db.Query(query, appID) +// if err != nil { +// return nil, err +// } +// defer rows.Close() + +// var volumes []Volume +// for rows.Next() { +// var vol Volume +// err := rows.Scan(&vol.ID, &vol.AppID, &vol.Name, &vol.HostPath, &vol.ContainerPath, &vol.ReadOnly, &vol.CreatedAt) +// if err != nil { +// return nil, err +// } +// volumes = append(volumes, vol) +// } + +// return volumes, rows.Err() +// } + +// func GetVolumeByID(id int64) (*Volume, error) { +// query := ` +// SELECT id, app_id, name, host_path, container_path, read_only, created_at +// FROM volumes +// WHERE id = ? +// ` +// var vol Volume +// err := db.QueryRow(query, id).Scan(&vol.ID, &vol.AppID, &vol.Name, &vol.HostPath, &vol.ContainerPath, &vol.ReadOnly, &vol.CreatedAt) +// if err == sql.ErrNoRows { +// return nil, nil +// } +// if err != nil { +// return nil, err +// } +// return &vol, nil +// } + +// func CreateVolume(appID int64, name, hostPath, containerPath string, readOnly bool) (*Volume, error) { +// query := ` +// INSERT INTO volumes (app_id, name, host_path, container_path, read_only, created_at) +// VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) +// ` +// result, err := db.Exec(query, appID, name, hostPath, containerPath, readOnly) +// if err != nil { +// return nil, err +// } + +// id, err := result.LastInsertId() +// if err != nil { +// return nil, err +// } + +// return GetVolumeByID(id) +// } + +// func UpdateVolume(id int64, name, hostPath, containerPath string, readOnly bool) error { +// query := ` +// UPDATE volumes +// SET name = ?, host_path = ?, container_path = ?, read_only = ? +// WHERE id = ? +// ` +// _, err := db.Exec(query, name, hostPath, containerPath, readOnly, id) +// return err +// } + +// func DeleteVolume(id int64) error { +// query := `DELETE FROM volumes WHERE id = ?` +// _, err := db.Exec(query, id) +// return err +// } diff --git a/server/queue/ctxManager.go b/server/queue/ctxManager.go new file mode 100644 index 0000000..ae7c2af --- /dev/null +++ b/server/queue/ctxManager.go @@ -0,0 +1,32 @@ +package queue + +import ( + "context" + "sync" +) + +var activeDeployments = make(map[int64]context.CancelFunc) +var mu sync.Mutex + +func Register(id int64, cancel context.CancelFunc) { + mu.Lock() + defer mu.Unlock() + activeDeployments[id] = cancel +} + +func Unregister(id int64) { + mu.Lock() + defer mu.Unlock() + delete(activeDeployments, id) +} + +func Cancel(id int64) bool { + mu.Lock() + defer mu.Unlock() + if cancel, exists := activeDeployments[id]; exists { + cancel() + delete(activeDeployments, id) + return true + } + return false +} diff --git a/server/queue/deployQueue.go b/server/queue/deployQueue.go index 5418e39..994f1f7 100644 --- a/server/queue/deployQueue.go +++ b/server/queue/deployQueue.go @@ -2,11 +2,12 @@ package queue import ( "context" - "database/sql" "fmt" "sync" + "github.com/corecollectives/mist/models" "github.com/rs/zerolog/log" + "gorm.io/gorm" ) type Queue struct { @@ -18,7 +19,7 @@ type Queue struct { var queue *Queue -func NewQueue(buffer int, db *sql.DB) *Queue { +func NewQueue(buffer int, db *gorm.DB) *Queue { ctx, cancel := context.WithCancel(context.Background()) q := &Queue{ jobs: make(chan int64, buffer), @@ -36,11 +37,20 @@ func GetQueue() *Queue { return queue } -func (q *Queue) StartWorker(db *sql.DB) { +func (q *Queue) StartWorker(db *gorm.DB) { q.wg.Add(1) go func() { defer q.wg.Done() for id := range q.jobs { + status, err := models.GetDeploymentStatus(id) + if err != nil { + log.Error().Err(err).Msg("Failed to get deployment status") + continue + } + if status == "stopped" { + log.Info().Msgf("Deployment %d has been stopped before processing, skipping", id) + continue + } q.HandleWork(id, db) } diff --git a/server/queue/handleWork.go b/server/queue/handleWork.go index d6dfed6..76ff2ab 100644 --- a/server/queue/handleWork.go +++ b/server/queue/handleWork.go @@ -1,7 +1,7 @@ package queue import ( - "database/sql" + "context" "fmt" "sync" @@ -10,6 +10,7 @@ import ( "github.com/corecollectives/mist/github" "github.com/corecollectives/mist/models" "github.com/corecollectives/mist/utils" + "gorm.io/gorm" ) // to prevent concurrent deployments of same app @@ -19,7 +20,7 @@ import ( // then this will be helpful var deploymentLocks sync.Map -func (q *Queue) HandleWork(id int64, db *sql.DB) { +func (q *Queue) HandleWork(id int64, db *gorm.DB) { defer func() { if r := recover(); r != nil { errMsg := fmt.Sprintf("panic during deployment: %v", r) @@ -27,6 +28,11 @@ func (q *Queue) HandleWork(id int64, db *sql.DB) { } }() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + Register(id, cancel) + defer Unregister(id) + appId, err := models.GetAppIDByDeploymentID(id) if err != nil { errMsg := fmt.Sprintf("Failed to get app ID: %v", err) @@ -79,8 +85,14 @@ func (q *Queue) HandleWork(id int64, db *sql.DB) { logger.Info("Cloning repository") models.UpdateDeploymentStatus(id, "cloning", "cloning", 20, nil) - err = github.CloneRepo(appId, logFile) + err = github.CloneRepo(ctx, appId, logFile) if err != nil { + if ctx.Err() == context.Canceled { + logger.Info("Deployment cancelled by user") + errMsg := "deployment stopped by user" + models.UpdateDeploymentStatus(id, "stopped", "stopped", dep.Progress, &errMsg) + return + } logger.Error(err, "Failed to clone repository") errMsg := fmt.Sprintf("Failed to clone repository: %v", err) models.UpdateDeploymentStatus(id, "failed", "failed", 0, &errMsg) @@ -92,8 +104,14 @@ func (q *Queue) HandleWork(id int64, db *sql.DB) { logger.Info("Skipping git clone for database app") } - _, err = docker.DeployerMain(id, db, logFile, logger) + _, err = docker.DeployerMain(ctx, id, db, logFile, logger) if err != nil { + if ctx.Err() == context.Canceled { + logger.Info("Deployment cancelled by user") + errMsg := "deployment stopped by user" + models.UpdateDeploymentStatus(id, "stopped", "stopped", dep.Progress, &errMsg) + return + } logger.Error(err, "Deployment failed") errMsg := fmt.Sprintf("Deployment failed: %v", err) models.UpdateDeploymentStatus(id, "failed", "failed", 0, &errMsg) diff --git a/server/queue/main.go b/server/queue/main.go index bff4daf..0bb1675 100644 --- a/server/queue/main.go +++ b/server/queue/main.go @@ -1,8 +1,10 @@ package queue -import "database/sql" +import ( + "gorm.io/gorm" +) -func InitQueue(db *sql.DB) *Queue { +func InitQueue(db *gorm.DB) *Queue { q := NewQueue(5, db) return q } diff --git a/server/utils/traefik.go b/server/utils/traefik.go index 563d8d9..712dd3d 100644 --- a/server/utils/traefik.go +++ b/server/utils/traefik.go @@ -29,7 +29,10 @@ func GenerateDynamicConfig(wildcardDomain *string, mistAppName string) error { } dynamicConfigPath := filepath.Join(TraefikConfigDir, TraefikDynamicFile) - content := generateDynamicYAML(wildcardDomain, mistAppName) + content, err := generateDynamicYAML(wildcardDomain, mistAppName) + if err != nil { + return fmt.Errorf("failed to generate dynamic YAML: %w", err) + } if err := os.WriteFile(dynamicConfigPath, []byte(content), 0644); err != nil { return fmt.Errorf("failed to write dynamic config: %w", err) @@ -42,61 +45,60 @@ func GenerateDynamicConfig(wildcardDomain *string, mistAppName string) error { return nil } -func generateDynamicYAML(wildcardDomain *string, mistAppName string) string { - var b strings.Builder - - b.WriteString(`http: - routers: -`) - - var mistDomain string - if wildcardDomain != nil && *wildcardDomain != "" { - domain := strings.TrimPrefix(*wildcardDomain, "*") - domain = strings.TrimPrefix(domain, ".") - - mistDomain = mistAppName + "." + domain - - b.WriteString(fmt.Sprintf(` - mist-dashboard: - rule: Host(`+"`%s`"+`) - entryPoints: - - websecure - service: mist-dashboard - tls: - certResolver: le - - mist-dashboard-http: - rule: Host(`+"`%s`"+`) - entryPoints: - - web - middlewares: - - https-redirect - service: mist-dashboard -`, mistDomain, mistDomain)) +func generateDynamicYAML(wildcardDomain *string, mistAppName string) ([]byte, error) { + cfg := map[string]any{ + "http": map[string]any{ + "routers": map[string]any{}, + "services": map[string]any{}, + "middlewares": map[string]any{ + "https-redirect": map[string]any{ + "redirectScheme": map[string]any{ + "scheme": "https", + "permanent": true, + }, + }, + }, + }, } - b.WriteString(` - services: -`) - - if mistDomain != "" { - b.WriteString(` - mist-dashboard: - loadBalancer: - servers: - - url: "http://172.17.0.1:8080" -`) + if wildcardDomain == nil || *wildcardDomain == "" { + return yaml.Marshal(cfg) } - b.WriteString(` - middlewares: - https-redirect: - redirectScheme: - scheme: https - permanent: true -`) + domain := strings.TrimPrefix(*wildcardDomain, "*") + domain = strings.TrimPrefix(domain, ".") + + mistDomain := mistAppName + "." + domain + + httpConfig := cfg["http"].(map[string]any) + httpConfig["routers"] = map[string]any{ + "mist-dashboard": map[string]any{ + "rule": fmt.Sprintf("Host(`%s`)", mistDomain), + "entryPoints": []string{"websecure"}, + "service": "mist-dashboard", + "tls": map[string]any{ + "certResolver": "le", + }, + }, + "mist-dashboard-http": map[string]any{ + "rule": fmt.Sprintf("Host(`%s`)", mistDomain), + "entryPoints": []string{"web"}, + "middlewares": []string{"https-redirect"}, + "service": "mist-dashboard", + }, + } + + httpConfig["services"] = map[string]any{ + "mist-dashboard": map[string]any{ + "loadBalancer": map[string]any{ + "servers": []map[string]any{ + {"url": "http://172.17.0.1:8080"}, + }, + }, + }, + } - return b.String() + return yaml.Marshal(cfg) } func ChangeLetsEncryptEmail(email string) error { diff --git a/test/database/database_test.go b/test/database/database_test.go new file mode 100644 index 0000000..0573daf --- /dev/null +++ b/test/database/database_test.go @@ -0,0 +1,2305 @@ +package db + +import ( + "fmt" + "os" + "path/filepath" + "testing" + "time" + + mistdb "github.com/corecollectives/mist/db" + "github.com/corecollectives/mist/models" + "github.com/corecollectives/mist/utils" + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +func setupTestDB(t *testing.T) *gorm.DB { + tmpDir := t.TempDir() + dbPath := filepath.Join(tmpDir, "test.db") + + db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{ + Logger: logger.Default.LogMode(logger.Silent), + }) + if err != nil { + t.Fatalf("failed to open test database: %v", err) + } + + err = mistdb.MigrateDB(db) + if err != nil { + t.Fatalf("failed to run migrations: %v", err) + } + + models.SetDB(db) + return db +} + +func cleanupDB(db *gorm.DB) { + sqlDB, _ := db.DB() + sqlDB.Close() +} + +func TestInitDB_Success(t *testing.T) { + os.Setenv("ENV", "dev") + defer os.Unsetenv("ENV") + + db, err := mistdb.InitDB() + if err != nil { + t.Fatalf("InitDB failed: %v", err) + } + defer cleanupDB(db) + + if db == nil { + t.Fatal("db should not be nil") + } +} + +func TestInitDB_FailsOnInvalidPath(t *testing.T) { + origEnv := os.Getenv("ENV") + os.Setenv("ENV", "dev") + defer os.Setenv("ENV", origEnv) + + t.Skip("Skipping test that depends on filesystem permissions") +} + +func TestMigrateDb_CreatesAllTables(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + expectedTables := []string{ + "users", "api_tokens", "apps", "audit_logs", "backups", + "deployments", "envs", "projects", "project_members", + "git_providers", "github_installations", "app_repositories", "domains", + "volumes", "crons", "registries", "sessions", "notifications", + } + + for _, table := range expectedTables { + if !db.Migrator().HasTable(table) { + t.Errorf("table %s should exist", table) + } + } +} + +func TestMigrateDb_SkipsExistingTables(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + err := mistdb.MigrateDB(db) + if err != nil { + t.Errorf("re-running migrations should not fail: %v", err) + } +} + +func TestUser_Create(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "testuser", + Email: "test@example.com", + PasswordHash: "hash", + } + + err := user.Create() + if err != nil { + t.Fatalf("CreateUser failed: %v", err) + } + + if user.ID == 0 { + t.Error("user ID should be set after creation") + } + + if user.Role != "user" { + t.Error("default role should be user") + } +} + +func TestUser_Create_DuplicateUsername(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user1 := &models.User{ + ID: utils.GenerateRandomId(), + Username: "uniqueuser", + Email: "user1@example.com", + PasswordHash: "hash", + } + user2 := &models.User{ + ID: utils.GenerateRandomId(), + Username: "uniqueuser", + Email: "user2@example.com", + PasswordHash: "hash", + } + + if err := user1.Create(); err != nil { + t.Fatalf("first user creation failed: %v", err) + } + + err := user2.Create() + if err == nil { + t.Error("should fail with duplicate username") + } +} + +func TestUser_Create_DuplicateEmail(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user1 := &models.User{ + ID: utils.GenerateRandomId(), + Username: "user1", + Email: "same@example.com", + PasswordHash: "hash", + } + user2 := &models.User{ + ID: utils.GenerateRandomId(), + Username: "user2", + Email: "same@example.com", + PasswordHash: "hash", + } + + if err := user1.Create(); err != nil { + t.Fatalf("first user creation failed: %v", err) + } + + err := user2.Create() + if err == nil { + t.Error("should fail with duplicate email") + } +} + +func TestUser_SetPassword(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "passuser", + Email: "pass@example.com", + } + + err := user.SetPassword("testpassword123") + if err != nil { + t.Fatalf("SetPassword failed: %v", err) + } + + if user.PasswordHash == "testpassword123" { + t.Error("password should not be stored in plaintext") + } + + if len(user.PasswordHash) < 50 { + t.Error("password hash should be properly hashed") + } +} + +func TestUser_MatchPassword_Success(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "matchuser", + Email: "match@example.com", + } + user.SetPassword("correctpassword") + user.Create() + + if !user.MatchPassword("correctpassword") { + t.Error("MatchPassword should return true for correct password") + } +} + +func TestUser_MatchPassword_WrongPassword(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "wrongpass", + Email: "wrong@example.com", + } + user.SetPassword("correctpassword") + user.Create() + + if user.MatchPassword("wrongpassword") { + t.Error("MatchPassword should return false for wrong password") + } +} + +func TestGetUserByID_Exists(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + origUser := &models.User{ + ID: utils.GenerateRandomId(), + Username: "getbyid", + Email: "getbyid@example.com", + PasswordHash: "hash", + Role: "admin", + } + origUser.Create() + + user, err := models.GetUserByID(origUser.ID) + if err != nil { + t.Fatalf("GetUserByID failed: %v", err) + } + + if user.Username != origUser.Username { + t.Error("retrieved user should match original") + } +} + +func TestGetUserByID_NotExists(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + _, err := models.GetUserByID(999999) + if err == nil { + t.Error("should return error for non-existent user") + } +} + +func TestGetUserByEmail_Exists(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + origUser := &models.User{ + ID: utils.GenerateRandomId(), + Username: "emailtest", + Email: "findme@example.com", + PasswordHash: "hash", + } + origUser.Create() + + user, err := models.GetUserByEmail("findme@example.com") + if err != nil { + t.Fatalf("GetUserByEmail failed: %v", err) + } + + if user.ID != origUser.ID { + t.Error("retrieved user should match by email") + } +} + +func TestGetUserByUsername_Exists(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + origUser := &models.User{ + ID: utils.GenerateRandomId(), + Username: "uniquetestuser", + Email: "unique@example.com", + PasswordHash: "hash", + } + origUser.Create() + + user, err := models.GetUserByUsername("uniquetestuser") + if err != nil { + t.Fatalf("GetUserByUsername failed: %v", err) + } + + if user.ID != origUser.ID { + t.Error("retrieved user should match by username") + } +} + +func TestUpdateUser(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "updateme", + Email: "update@example.com", + PasswordHash: "hash", + } + user.Create() + + newUsername := "updatedname" + newEmail := "newemail@example.com" + user.Username = newUsername + user.Email = newEmail + + err := models.UpdateUser(user) + if err != nil { + t.Fatalf("UpdateUser failed: %v", err) + } + + updated, _ := models.GetUserByID(user.ID) + if updated.Username != newUsername { + t.Error("username should be updated") + } + if updated.Email != newEmail { + t.Error("email should be updated") + } +} + +func TestUpdateUserPassword(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "passupdate", + Email: "passupdate@example.com", + } + user.SetPassword("oldpassword") + user.Create() + + user.SetPassword("newpassword") + err := user.UpdatePassword() + if err != nil { + t.Fatalf("UpdatePassword failed: %v", err) + } + + if user.MatchPassword("oldpassword") { + t.Error("old password should not match") + } + if !user.MatchPassword("newpassword") { + t.Error("new password should match") + } +} + +func TestDeleteUser(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "deleteme", + Email: "delete@example.com", + PasswordHash: "hash", + } + user.Create() + + err := models.DeleteUserByID(user.ID) + if err != nil { + t.Fatalf("DeleteUser failed: %v", err) + } + + _, err = models.GetUserByID(user.ID) + if err == nil { + t.Error("user should be deleted") + } +} + +func TestGetAllUsers(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + for i := 0; i < 5; i++ { + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "listuser" + string(rune('a'+i)), + Email: "listuser" + string(rune('a'+i)) + "@example.com", + PasswordHash: "hash", + } + user.Create() + } + + users, err := models.GetAllUsers() + if err != nil { + t.Fatalf("GetAllUsers failed: %v", err) + } + + if len(users) < 5 { + t.Error("should return all users") + } +} + +func TestGetUserCount(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + initial, _ := models.GetUserCount() + + for i := 0; i < 3; i++ { + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "countuser" + string(rune('a'+i)), + Email: "countuser" + string(rune('a'+i)) + "@example.com", + PasswordHash: "hash", + } + user.Create() + } + + count, err := models.GetUserCount() + if err != nil { + t.Fatalf("GetUserCount failed: %v", err) + } + + if count != initial+3 { + t.Error("count should include all created users") + } +} + +func TestUserRoleRetrieval(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + adminUser := &models.User{ + ID: utils.GenerateRandomId(), + Username: "adminuser", + Email: "admin@example.com", + PasswordHash: "hash", + Role: "admin", + } + adminUser.Create() + + role, err := models.GetUserRole(adminUser.ID) + if err != nil { + t.Fatalf("GetUserRole failed: %v", err) + } + + if role != "admin" { + t.Error("should return correct role") + } +} + +func TestUser_OptionalFields(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + fullName := "Test Full Name" + avatarURL := "https://example.com/avatar.png" + bio := "Test bio" + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "optionalfields", + Email: "optional@example.com", + PasswordHash: "hash", + FullName: &fullName, + AvatarURL: &avatarURL, + Bio: &bio, + } + user.Create() + + retrieved, _ := models.GetUserByID(user.ID) + if retrieved.FullName == nil || *retrieved.FullName != fullName { + t.Error("FullName should be stored correctly") + } + if retrieved.AvatarURL == nil || *retrieved.AvatarURL != avatarURL { + t.Error("AvatarURL should be stored correctly") + } + if retrieved.Bio == nil || *retrieved.Bio != bio { + t.Error("Bio should be stored correctly") + } +} + +func TestProject_Insert(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "projectowner", + Email: "projectowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + desc := "Test project description" + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Test Project", + Description: &desc, + OwnerID: owner.ID, + Tags: []string{"tag1", "tag2"}, + } + + err := project.InsertInDB() + if err != nil { + t.Fatalf("InsertInDB failed: %v", err) + } + + if project.Owner == nil { + t.Error("owner should be populated after insert") + } +} + +func TestProject_GetByID(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "getprojectowner", + Email: "getprojectowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Get Project Test", + OwnerID: owner.ID, + Tags: []string{"test"}, + } + project.InsertInDB() + + retrieved, err := models.GetProjectByID(project.ID) + if err != nil { + t.Fatalf("GetProjectByID failed: %v", err) + } + + if retrieved.Name != project.Name { + t.Error("project name should match") + } +} + +func TestProject_Update(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "updateprojectowner", + Email: "updateprojectowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Original Name", + OwnerID: owner.ID, + } + project.InsertInDB() + + newName := "Updated Name" + newTags := []string{"newtag"} + project.Name = newName + project.Tags = newTags + + err := models.UpdateProject(project) + if err != nil { + t.Fatalf("UpdateProject failed: %v", err) + } + + updated, _ := models.GetProjectByID(project.ID) + if updated.Name != newName { + t.Error("name should be updated") + } +} + +func TestProject_Delete(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "deleteprojectowner", + Email: "deleteprojectowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Delete Project Test", + OwnerID: owner.ID, + } + project.InsertInDB() + + err := models.DeleteProjectByID(project.ID) + if err != nil { + t.Fatalf("DeleteProject failed: %v", err) + } + + _, err = models.GetProjectByID(project.ID) + if err == nil { + t.Error("project should be deleted") + } +} + +func TestProject_HasUserAccess(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "accessuser", + Email: "accessuser@example.com", + PasswordHash: "hash", + } + user.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Access Project", + OwnerID: user.ID, + } + project.InsertInDB() + + hasAccess, err := models.HasUserAccessToProject(user.ID, project.ID) + if err != nil { + t.Fatalf("HasUserAccessToProject failed: %v", err) + } + + if !hasAccess { + t.Error("owner should have access to project") + } +} + +func TestProject_IsUserOwner(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "owneruser", + Email: "owneruser@example.com", + PasswordHash: "hash", + } + user.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Owner Project", + OwnerID: user.ID, + } + project.InsertInDB() + + isOwner, err := models.IsUserProjectOwner(user.ID, project.ID) + if err != nil { + t.Fatalf("IsUserProjectOwner failed: %v", err) + } + + if !isOwner { + t.Error("user should be project owner") + } +} + +func TestProject_UpdateMembers(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "memberowner", + Email: "memberowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + member1 := &models.User{ + ID: utils.GenerateRandomId(), + Username: "member1", + Email: "member1@example.com", + PasswordHash: "hash", + } + member1.Create() + + member2 := &models.User{ + ID: utils.GenerateRandomId(), + Username: "member2", + Email: "member2@example.com", + PasswordHash: "hash", + } + member2.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Members Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + err := models.UpdateProjectMembers(project.ID, []int64{member1.ID, member2.ID}) + if err != nil { + t.Fatalf("UpdateProjectMembers failed: %v", err) + } + + retrieved, _ := models.GetProjectByID(project.ID) + if len(retrieved.ProjectMembers) < 2 { + t.Error("project should have members") + } +} + +func TestProject_OwnerAlwaysIncludedInMembers(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "forcedowner", + Email: "forcedowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Forced Owner Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + err := models.UpdateProjectMembers(project.ID, []int64{}) + if err != nil { + t.Fatalf("UpdateProjectMembers failed: %v", err) + } + + retrieved, _ := models.GetProjectByID(project.ID) + found := false + for _, member := range retrieved.ProjectMembers { + if member.ID == owner.ID { + found = true + break + } + } + + if !found { + t.Error("owner should always be included in members") + } +} + +func TestProject_GetProjectsUserIsPartOf(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "partofuser", + Email: "partofuser@example.com", + PasswordHash: "hash", + } + user.Create() + + project1 := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Project 1", + OwnerID: user.ID, + } + project1.InsertInDB() + + projects, err := models.GetProjectsUserIsPartOf(user.ID) + if err != nil { + t.Fatalf("GetProjectsUserIsPartOf failed: %v", err) + } + + if len(projects) < 1 { + t.Error("should return projects user is part of") + } +} + +func TestApp_Insert(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "appowner", + Email: "appowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "App Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Test App", + CreatedBy: owner.ID, + AppType: models.AppTypeWeb, + GitBranch: "main", + } + + err := app.InsertInDB() + if err != nil { + t.Fatalf("InsertInDB failed: %v", err) + } + + if app.ID == 0 { + t.Error("app ID should be set") + } + if app.AppType != models.AppTypeWeb { + t.Error("default app type should be web") + } + if app.Status != models.StatusStopped { + t.Error("default status should be stopped") + } +} + +func TestApp_GetByID(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "getappowner", + Email: "getappowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Get App Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Get App Test", + CreatedBy: owner.ID, + } + app.InsertInDB() + + retrieved, err := models.GetApplicationByID(app.ID) + if err != nil { + t.Fatalf("GetApplicationByID failed: %v", err) + } + + if retrieved.Name != app.Name { + t.Error("retrieved app should match") + } +} + +func TestApp_GetByProjectID(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "listappowner", + Email: "listappowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "List App Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + for i := 0; i < 3; i++ { + app := &models.App{ + ProjectID: project.ID, + Name: "App " + string(rune('a'+i)), + CreatedBy: owner.ID, + } + app.InsertInDB() + } + + apps, err := models.GetApplicationByProjectID(project.ID) + if err != nil { + t.Fatalf("GetApplicationByProjectID failed: %v", err) + } + + if len(apps) < 3 { + t.Error("should return all apps for project") + } +} + +func TestApp_Update(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "updateappowner", + Email: "updateappowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Update App Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Original App Name", + CreatedBy: owner.ID, + } + app.InsertInDB() + + newName := "Updated App Name" + port := int64(8080) + app.Name = newName + app.Port = &port + + err := app.UpdateApplication() + if err != nil { + t.Fatalf("UpdateApplication failed: %v", err) + } + + updated, _ := models.GetApplicationByID(app.ID) + if updated.Name != newName { + t.Error("name should be updated") + } + if updated.Port == nil || *updated.Port != 8080 { + t.Error("port should be updated") + } +} + +func TestApp_Delete(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "deleteappowner", + Email: "deleteappowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Delete App Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Delete App Test", + CreatedBy: owner.ID, + } + app.InsertInDB() + + err := models.DeleteApplication(app.ID) + if err != nil { + t.Fatalf("DeleteApplication failed: %v", err) + } + + _, err = models.GetApplicationByID(app.ID) + if err == nil { + t.Error("app should be deleted") + } +} + +func TestApp_IsUserOwner(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "appownerverify", + Email: "appownerverify@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "App Owner Verify Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "App Owner Verify Test", + CreatedBy: owner.ID, + } + app.InsertInDB() + + isOwner, err := models.IsUserApplicationOwner(owner.ID, app.ID) + if err != nil { + t.Fatalf("IsUserApplicationOwner failed: %v", err) + } + + if !isOwner { + t.Error("user should be app owner") + } +} + +func TestApp_CascadingDelete(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "cascadeappowner", + Email: "cascadeappowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Cascade App Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Cascade App Test", + CreatedBy: owner.ID, + } + app.InsertInDB() + + deployment := &models.Deployment{ + ID: utils.GenerateRandomId(), + AppID: app.ID, + CommitHash: "abc123", + } + deployment.CreateDeployment() + + models.DeleteApplication(app.ID) + + var appCount int64 + db.Model(&models.App{}).Where("id = ?", app.ID).Count(&appCount) + if appCount != 0 { + t.Error("app should be deleted") + } +} + +func TestDeployment_Create(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "deployowner", + Email: "deployowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Deploy Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Deploy App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + deployment := &models.Deployment{ + ID: utils.GenerateRandomId(), + AppID: app.ID, + CommitHash: "abc123def456", + TriggeredBy: &owner.ID, + } + + err := deployment.CreateDeployment() + if err != nil { + t.Fatalf("CreateDeployment failed: %v", err) + } + + if deployment.DeploymentNumber == nil { + t.Error("deployment number should be set") + } + if *deployment.DeploymentNumber != 1 { + t.Error("first deployment should have number 1") + } + if deployment.Status != models.DeploymentStatusPending { + t.Error("default status should be pending") + } +} + +func TestDeployment_AutoIncrementNumber(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "multideployowner", + Email: "multideployowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Multi Deploy Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Multi Deploy App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + for i := 0; i < 3; i++ { + deployment := &models.Deployment{ + ID: utils.GenerateRandomId(), + AppID: app.ID, + CommitHash: fmt.Sprintf("commit_%d_%d", time.Now().UnixNano(), i), + } + err := deployment.CreateDeployment() + if err != nil { + t.Fatalf("deployment creation failed: %v", err) + } + + if deployment.DeploymentNumber == nil { + t.Errorf("deployment %d: deployment number is nil", i) + } else { + t.Logf("deployment %d: got number %d", i, *deployment.DeploymentNumber) + } + } + + deployments, _ := models.GetDeploymentsByAppID(app.ID) + if len(deployments) != 3 { + t.Errorf("expected 3 deployments, got %d", len(deployments)) + } +} + +func TestDeployment_GetByID(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "getdeployowner", + Email: "getdeployowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Get Deploy Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Get Deploy App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + deployment := &models.Deployment{ + ID: utils.GenerateRandomId(), + AppID: app.ID, + CommitHash: "getdeploy123", + } + deployment.CreateDeployment() + + retrieved, err := models.GetDeploymentByID(deployment.ID) + if err != nil { + t.Fatalf("GetDeploymentByID failed: %v", err) + } + + if retrieved.CommitHash != deployment.CommitHash { + t.Error("retrieved deployment should match") + } +} + +func TestDeployment_GetByAppID(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "listdeployowner", + Email: "listdeployowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "List Deploy Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "List Deploy App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + for i := 0; i < 3; i++ { + deployment := &models.Deployment{ + ID: utils.GenerateRandomId(), + AppID: app.ID, + CommitHash: "listdeploy" + string(rune('a'+i)), + } + deployment.CreateDeployment() + } + + deployments, err := models.GetDeploymentsByAppID(app.ID) + if err != nil { + t.Fatalf("GetDeploymentsByAppID failed: %v", err) + } + + if len(deployments) < 3 { + t.Error("should return all deployments for app") + } +} + +func TestDeployment_UpdateStatus(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "statusdeployowner", + Email: "statusdeployowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Status Deploy Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Status Deploy App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + deployment := &models.Deployment{ + ID: utils.GenerateRandomId(), + AppID: app.ID, + CommitHash: "statusdeploy123", + } + deployment.CreateDeployment() + + err := models.UpdateDeploymentStatus(deployment.ID, string(models.DeploymentStatusSuccess), "completed", 100, nil) + if err != nil { + t.Fatalf("UpdateDeploymentStatus failed: %v", err) + } + + updated, _ := models.GetDeploymentByID(deployment.ID) + if updated.Status != models.DeploymentStatusSuccess { + t.Error("status should be updated") + } + if updated.FinishedAt == nil { + t.Error("finished_at should be set on completion") + } +} + +func TestDeployment_MarkActive(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "activedeployowner", + Email: "activedeployowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Active Deploy Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Active Deploy App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + deployment1 := &models.Deployment{ + ID: utils.GenerateRandomId(), + AppID: app.ID, + CommitHash: "active1", + } + deployment1.CreateDeployment() + + deployment2 := &models.Deployment{ + ID: utils.GenerateRandomId(), + AppID: app.ID, + CommitHash: "active2", + } + deployment2.CreateDeployment() + + err := models.MarkDeploymentActive(deployment1.ID, app.ID) + if err != nil { + t.Fatalf("MarkDeploymentActive failed: %v", err) + } + + d1, _ := models.GetDeploymentByID(deployment1.ID) + d2, _ := models.GetDeploymentByID(deployment2.ID) + + if !d1.IsActive { + t.Error("first deployment should be active") + } + if d2.IsActive { + t.Error("second deployment should not be active") + } +} + +func TestDeployment_GetIncomplete(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "incompletedeployowner", + Email: "incompletedeployowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Incomplete Deploy Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Incomplete Deploy App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + deployment1 := &models.Deployment{ + ID: utils.GenerateRandomId(), + AppID: app.ID, + CommitHash: "incomplete1", + } + deployment1.CreateDeployment() + + deployment2 := &models.Deployment{ + ID: utils.GenerateRandomId(), + AppID: app.ID, + CommitHash: "incomplete2", + } + deployment2.CreateDeployment() + + models.UpdateDeploymentStatus(deployment2.ID, string(models.DeploymentStatusBuilding), "building", 50, nil) + + incomplete, err := models.GetIncompleteDeployments() + if err != nil { + t.Fatalf("GetIncompleteDeployments failed: %v", err) + } + + found := false + for _, d := range incomplete { + if d.ID == deployment2.ID && d.AppID == app.ID { + found = true + break + } + } + + if !found { + t.Logf("Incomplete deployments found: %d", len(incomplete)) + for _, d := range incomplete { + t.Logf(" - ID: %d, AppID: %d, Status: %s", d.ID, d.AppID, d.Status) + } + t.Error("should return incomplete deployments") + } +} + +func TestDomain_Create(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "domainowner", + Email: "domainowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Domain Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Domain App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + domain, err := models.CreateDomain(app.ID, "example.com") + if err != nil { + t.Fatalf("CreateDomain failed: %v", err) + } + + if domain.Domain != "example.com" { + t.Error("domain name should match") + } + if domain.AppID != app.ID { + t.Error("app id should match") + } + if domain.SslStatus != models.SSLStatusPending { + t.Error("default ssl status should be pending") + } +} + +func TestDomain_GetByAppID(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "listdomainowner", + Email: "listdomainowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "List Domain Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "List Domain App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + models.CreateDomain(app.ID, "domain1.example.com") + models.CreateDomain(app.ID, "domain2.example.com") + + domains, err := models.GetDomainsByAppID(app.ID) + if err != nil { + t.Fatalf("GetDomainsByAppID failed: %v", err) + } + + if len(domains) < 2 { + t.Error("should return all domains for app") + } +} + +func TestDomain_GetPrimary(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "primarydomainowner", + Email: "primarydomainowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Primary Domain Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Primary Domain App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + models.CreateDomain(app.ID, "primary.example.com") + models.CreateDomain(app.ID, "secondary.example.com") + + primary, err := models.GetPrimaryDomainByAppID(app.ID) + if err != nil { + t.Fatalf("GetPrimaryDomainByAppID failed: %v", err) + } + + if primary.Domain != "primary.example.com" { + t.Error("first created domain should be primary") + } +} + +func TestDomain_Update(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "updatedomainowner", + Email: "updatedomainowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Update Domain Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Update Domain App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + domain, _ := models.CreateDomain(app.ID, "olddomain.com") + + err := models.UpdateDomain(domain.ID, "newdomain.com") + if err != nil { + t.Fatalf("UpdateDomain failed: %v", err) + } + + updated, _ := models.GetDomainByID(domain.ID) + if updated.Domain != "newdomain.com" { + t.Error("domain should be updated") + } +} + +func TestDomain_Delete(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "deletedomainowner", + Email: "deletedomainowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Delete Domain Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Delete Domain App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + domain, _ := models.CreateDomain(app.ID, "deleteme.com") + + err := models.DeleteDomain(domain.ID) + if err != nil { + t.Fatalf("DeleteDomain failed: %v", err) + } + + _, err = models.GetDomainByID(domain.ID) + if err == nil { + t.Error("domain should be deleted") + } +} + +func TestDomain_UniqueConstraint(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "uniquedomainowner", + Email: "uniquedomainowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Unique Domain Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app1 := &models.App{ + ProjectID: project.ID, + Name: "Unique Domain App 1", + CreatedBy: owner.ID, + } + app1.InsertInDB() + + app2 := &models.App{ + ProjectID: project.ID, + Name: "Unique Domain App 2", + CreatedBy: owner.ID, + } + app2.InsertInDB() + + _, err := models.CreateDomain(app1.ID, "same-domain.com") + if err != nil { + t.Fatalf("first domain creation failed: %v", err) + } + + _, err = models.CreateDomain(app2.ID, "same-domain.com") + if err == nil { + t.Error("should fail with duplicate domain") + } +} + +func TestEnvVariable_Create(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "envarowner", + Email: "envarowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "EnvVar Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "EnvVar App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + env, err := models.CreateEnvVariable(app.ID, "TEST_VAR", "test_value") + if err != nil { + t.Fatalf("CreateEnvVariable failed: %v", err) + } + + if env.Key != "TEST_VAR" { + t.Error("key should match") + } + if env.Value != "test_value" { + t.Error("value should match") + } +} + +func TestEnvVariable_GetByAppID(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "listenvarowner", + Email: "listenvarowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "List EnvVar Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "List EnvVar App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + models.CreateEnvVariable(app.ID, "VAR1", "val1") + models.CreateEnvVariable(app.ID, "VAR2", "val2") + models.CreateEnvVariable(app.ID, "VAR3", "val3") + + envs, err := models.GetEnvVariablesByAppID(app.ID) + if err != nil { + t.Fatalf("GetEnvVariablesByAppID failed: %v", err) + } + + if len(envs) < 3 { + t.Error("should return all env variables") + } +} + +func TestEnvVariable_Update(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "updateenvarowner", + Email: "updateenvarowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Update EnvVar Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Update EnvVar App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + env, _ := models.CreateEnvVariable(app.ID, "UPDATEME", "oldvalue") + + err := models.UpdateEnvVariable(env.ID, "UPDATEME", "newvalue") + if err != nil { + t.Fatalf("UpdateEnvVariable failed: %v", err) + } + + updated, _ := models.GetEnvVariableByID(env.ID) + if updated.Value != "newvalue" { + t.Error("value should be updated") + } +} + +func TestEnvVariable_Delete(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "deleteenvarowner", + Email: "deleteenvarowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Delete EnvVar Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Delete EnvVar App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + env, _ := models.CreateEnvVariable(app.ID, "DELETEME", "value") + + err := models.DeleteEnvVariable(env.ID) + if err != nil { + t.Fatalf("DeleteEnvVariable failed: %v", err) + } + + _, err = models.GetEnvVariableByID(env.ID) + if err == nil { + t.Error("env variable should be deleted") + } +} + +func TestEnvVariable_UniqueConstraint(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "uniqueenvarowner", + Email: "uniqueenvarowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Unique EnvVar Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Unique EnvVar App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + _, err := models.CreateEnvVariable(app.ID, "UNIQUE_VAR", "value1") + if err != nil { + t.Fatalf("first env var creation failed: %v", err) + } + + _, err = models.CreateEnvVariable(app.ID, "UNIQUE_VAR", "value2") + if err == nil { + t.Error("should fail with duplicate key for same app") + } +} + +func TestVolume_Create(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "volumeowner", + Email: "volumeowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Volume Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Volume App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + vol, err := models.CreateVolume(app.ID, "data", "/host/data", "/container/data", false) + if err != nil { + t.Fatalf("CreateVolume failed: %v", err) + } + + if vol.Name != "data" { + t.Error("name should match") + } + if vol.HostPath != "/host/data" { + t.Error("host path should match") + } + if vol.ContainerPath != "/container/data" { + t.Error("container path should match") + } +} + +func TestVolume_GetByAppID(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "listvolumeowner", + Email: "listvolumeowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "List Volume Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "List Volume App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + models.CreateVolume(app.ID, "vol1", "/h1", "/c1", false) + models.CreateVolume(app.ID, "vol2", "/h2", "/c2", true) + + vols, err := models.GetVolumesByAppID(app.ID) + if err != nil { + t.Fatalf("GetVolumesByAppID failed: %v", err) + } + + if len(vols) < 2 { + t.Error("should return all volumes") + } +} + +func TestVolume_Update(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "updatevolumeowner", + Email: "updatevolumeowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Update Volume Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Update Volume App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + vol, _ := models.CreateVolume(app.ID, "oldname", "/old/host", "/old/container", false) + + err := models.UpdateVolume(vol.ID, "newname", "/new/host", "/new/container", true) + if err != nil { + t.Fatalf("UpdateVolume failed: %v", err) + } + + updated, _ := models.GetVolumeByID(vol.ID) + if updated.Name != "newname" { + t.Error("name should be updated") + } + if updated.ReadOnly != true { + t.Error("readOnly should be updated") + } +} + +func TestVolume_Delete(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + owner := &models.User{ + ID: utils.GenerateRandomId(), + Username: "deletevolumeowner", + Email: "deletevolumeowner@example.com", + PasswordHash: "hash", + } + owner.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Delete Volume Project", + OwnerID: owner.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Delete Volume App", + CreatedBy: owner.ID, + } + app.InsertInDB() + + vol, _ := models.CreateVolume(app.ID, "deleteme", "/host", "/container", false) + + err := models.DeleteVolume(vol.ID) + if err != nil { + t.Fatalf("DeleteVolume failed: %v", err) + } + + result, err := models.GetVolumeByID(vol.ID) + if err != nil { + t.Fatalf("GetVolumeByID failed: %v", err) + } + if result != nil { + t.Error("volume should be deleted") + } +} + +func TestTransaction_Success(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "transuser", + Email: "trans@example.com", + PasswordHash: "hash", + } + user.Create() + + err := db.Transaction(func(tx *gorm.DB) error { + user.Role = "admin" + return tx.Save(user).Error + }) + + if err != nil { + t.Fatalf("Transaction failed: %v", err) + } + + updated, _ := models.GetUserByID(user.ID) + if updated.Role != "admin" { + t.Error("role should be updated within transaction") + } +} + +func TestTransaction_Rollback(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "rollbackuser", + Email: "rollback@example.com", + PasswordHash: "hash", + } + user.Create() + + originalRole := user.Role + + err := db.Transaction(func(tx *gorm.DB) error { + user.Role = "admin" + if err := tx.Save(user).Error; err != nil { + return err + } + return fmt.Errorf("force rollback") + }) + + if err == nil { + t.Error("transaction should fail") + } + + updated, _ := models.GetUserByID(user.ID) + if updated.Role != originalRole { + t.Error("changes should be rolled back") + } +} + +func TestSoftDelete(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "softdeleteuser", + Email: "softdelete@example.com", + PasswordHash: "hash", + } + user.Create() + + db.Delete(user) + + var count int64 + db.Model(&models.User{}).Where("id = ?", user.ID).Count(&count) + if count != 0 { + t.Error("soft deleted user should not be found with regular query") + } + + var deleted models.User + db.Unscoped().Where("id = ?", user.ID).First(&deleted) + if deleted.ID != user.ID { + t.Error("soft deleted user should be retrievable with Unscoped") + } +} + +func TestConcurrentWrites(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "concurrentuser", + Email: "concurrent@example.com", + PasswordHash: "hash", + } + user.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Concurrent Project", + OwnerID: user.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Concurrent App", + CreatedBy: user.ID, + } + app.InsertInDB() + + results := make(chan error, 10) + + for i := 0; i < 10; i++ { + go func(idx int) { + deployment := &models.Deployment{ + ID: utils.GenerateRandomId(), + AppID: app.ID, + CommitHash: fmt.Sprintf("commit%d", idx), + } + results <- deployment.CreateDeployment() + }(i) + } + + for i := 0; i < 10; i++ { + if err := <-results; err != nil { + t.Errorf("concurrent write failed: %v", err) + } + } + + deployments, _ := models.GetDeploymentsByAppID(app.ID) + if len(deployments) != 10 { + t.Error("all concurrent deployments should be created") + } +} + +func TestBoundaryValues(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "boundaryuser", + Email: "boundary@example.com", + PasswordHash: "hash", + } + user.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Boundary Project", + OwnerID: user.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Boundary App", + CreatedBy: user.ID, + CPULimit: float64Ptr(0.0), + MemoryLimit: intPtr(0), + } + app.InsertInDB() + + retrieved, _ := models.GetApplicationByID(app.ID) + if retrieved.CPULimit != nil && *retrieved.CPULimit != 0.0 { + t.Error("zero CPU limit should be preserved") + } + if retrieved.MemoryLimit != nil && *retrieved.MemoryLimit != 0 { + t.Error("zero memory limit should be preserved") + } +} + +func float64Ptr(f float64) *float64 { + return &f +} + +func intPtr(i int) *int { + return &i +} + +func TestLargePayload(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "largeuser", + Email: "large@example.com", + PasswordHash: "hash", + } + user.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Large Project", + OwnerID: user.ID, + Tags: make([]string, 100), + } + for i := range project.Tags { + project.Tags[i] = fmt.Sprintf("tag%d", i) + } + project.InsertInDB() + + retrieved, _ := models.GetProjectByID(project.ID) + if len(retrieved.Tags) != 100 { + t.Error("all tags should be stored and retrieved") + } +} + +func TestNullFields(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "nulluser", + Email: "null@example.com", + PasswordHash: "hash", + FullName: nil, + AvatarURL: nil, + Bio: nil, + } + user.Create() + + retrieved, _ := models.GetUserByID(user.ID) + if retrieved.FullName != nil { + t.Error("nil FullName should remain nil") + } + if retrieved.AvatarURL != nil { + t.Error("nil AvatarURL should remain nil") + } + if retrieved.Bio != nil { + t.Error("nil Bio should remain nil") + } +} + +func TestRelatedRecordsCascade(t *testing.T) { + db := setupTestDB(t) + defer cleanupDB(db) + + user := &models.User{ + ID: utils.GenerateRandomId(), + Username: "cascadeuser", + Email: "cascade@example.com", + PasswordHash: "hash", + } + user.Create() + + project := &models.Project{ + ID: utils.GenerateRandomId(), + Name: "Cascade Project", + OwnerID: user.ID, + } + project.InsertInDB() + + app := &models.App{ + ProjectID: project.ID, + Name: "Cascade App", + CreatedBy: user.ID, + } + app.InsertInDB() + + models.CreateDomain(app.ID, "cascade.example.com") + models.CreateVolume(app.ID, "data", "/host", "/container", false) + + var envCount, domainCount, volumeCount int64 + db.Model(&models.EnvVariable{}).Where("app_id = ?", app.ID).Count(&envCount) + db.Model(&models.Domain{}).Where("app_id = ?", app.ID).Count(&domainCount) + db.Model(&models.Volume{}).Where("app_id = ?", app.ID).Count(&volumeCount) + + if envCount != 0 || domainCount != 1 || volumeCount != 1 { + t.Error("related records should be created") + } + + models.DeleteApplication(app.ID) + + var appCount int64 + db.Model(&models.App{}).Where("id = ?", app.ID).Count(&appCount) + if appCount != 0 { + t.Error("app should be deleted") + } + +} diff --git a/test/go.mod b/test/go.mod index 9503784..d8929e6 100644 --- a/test/go.mod +++ b/test/go.mod @@ -5,51 +5,24 @@ go 1.25.1 replace github.com/corecollectives/mist => ../server require ( - github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v1.3.0 // indirect - github.com/cloudflare/circl v1.6.1 // indirect - github.com/containerd/errdefs v1.0.0 // indirect - github.com/containerd/errdefs/pkg v0.3.0 // indirect - github.com/corecollectives/mist v0.0.0 // indirect - github.com/cyphar/filepath-securejoin v0.6.1 // indirect - github.com/distribution/reference v0.6.0 // indirect - github.com/docker/go-connections v0.6.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/emirpasic/gods v1.18.1 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-git/gcfg/v2 v2.0.2 // indirect - github.com/go-git/go-billy/v6 v6.0.0-20251217170237-e9738f50a3cd // indirect - github.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/corecollectives/mist v0.0.0 + gorm.io/driver/sqlite v1.6.0 + gorm.io/gorm v1.31.1 +) + +require ( github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/gorilla/websocket v1.5.3 // indirect - github.com/kevinburke/ssh_config v1.4.0 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mattn/go-sqlite3 v1.14.32 // indirect - github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/moby/api v1.52.0 // indirect - github.com/moby/moby/client v0.2.1 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/pjbgf/sha1cd v0.5.0 // indirect + github.com/mattn/go-sqlite3 v1.14.33 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/zerolog v1.34.0 // indirect - github.com/sergi/go-diff v1.4.0 // indirect - github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/tklauser/go-sysconf v0.3.15 // indirect - github.com/tklauser/numcpus v0.10.0 // indirect - github.com/yusufpapurcu/wmi v1.2.4 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect golang.org/x/crypto v0.46.0 // indirect - golang.org/x/net v0.48.0 // indirect golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.33.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/test/go.sum b/test/go.sum index 7ddb3ac..1b6c656 100644 --- a/test/go.sum +++ b/test/go.sum @@ -1,114 +1,49 @@ -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= -github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= -github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= -github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= -github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= -github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo= -github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs= -github.com/go-git/go-billy/v6 v6.0.0-20251217170237-e9738f50a3cd h1:Gd/f9cGi/3h1JOPaa6er+CkKUGyGX2DBJdFbDKVO+R0= -github.com/go-git/go-billy/v6 v6.0.0-20251217170237-e9738f50a3cd/go.mod h1:d3XQcsHu1idnquxt48kAv+h+1MUiYKLH/e7LAzjP+pI= -github.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19 h1:0lz2eJScP8v5YZQsrEw+ggWC5jNySjg4bIZo5BIh6iI= -github.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19/go.mod h1:L+Evfcs7EdTqxwv854354cb6+++7TFL3hJn3Wy4g+3w= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= -github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= -github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg= -github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= -github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k= -github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= -github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= -github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= +github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= +github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= -github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= -github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= -github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= -github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= -github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= -github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= -github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= -github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= +gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= +gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= diff --git a/test/main.go b/test/main.go deleted file mode 100644 index 56e5404..0000000 --- a/test/main.go +++ /dev/null @@ -1 +0,0 @@ -package test diff --git a/www b/www new file mode 160000 index 0000000..651f471 --- /dev/null +++ b/www @@ -0,0 +1 @@ +Subproject commit 651f471d42a6799574a828e6bed18f14612e9b4e diff --git a/www/README.md b/www/README.md deleted file mode 100644 index 1043dd7..0000000 --- a/www/README.md +++ /dev/null @@ -1,151 +0,0 @@ -# Mist Documentation - -This directory contains the official documentation for Mist PaaS. - -## Overview - -The documentation is built using [VitePress](https://vitepress.dev/), a Vite & Vue powered static site generator. - -## Development - -### Prerequisites - -- Node.js 18+ -- npm or yarn - -### Install Dependencies - -```bash -npm install -``` - -### Development Server - -Start the development server with hot-reload: - -```bash -npm run docs:dev -``` - -Visit `http://localhost:5173` to view the documentation. - -### Build - -Build the static documentation site: - -```bash -npm run docs:build -``` - -Output will be in `docs/.vitepress/dist/`. - -### Preview Build - -Preview the built documentation: - -```bash -npm run docs:preview -``` - -## Structure - -``` -www/ -├── docs/ -│ ├── .vitepress/ -│ │ ├── config.js # VitePress configuration -│ │ └── theme/ # Custom theme -│ │ ├── index.js # Theme entry -│ │ └── custom.css # Custom styles (matches dash theme) -│ ├── guide/ # User guides -│ │ ├── what-is-mist.md -│ │ ├── getting-started.md -│ │ ├── applications.md -│ │ ├── deployments.md -│ │ ├── environment-variables.md -│ │ ├── domains.md -│ │ ├── git-integration.md -│ │ ├── logs.md -│ │ ├── metrics.md -│ │ ├── authentication.md -│ │ ├── users.md -│ │ ├── audit-logs.md -│ │ ├── databases.md # Coming Soon -│ │ ├── ssl-automation.md # Coming Soon -│ │ ├── rollback.md # Coming Soon -│ │ ├── notifications.md # Coming Soon -│ │ └── cli.md # Coming Soon -│ ├── api/ # API documentation -│ │ ├── overview.md -│ │ ├── authentication.md -│ │ ├── projects.md -│ │ ├── applications.md -│ │ ├── deployments.md -│ │ ├── environment-variables.md -│ │ ├── domains.md -│ │ ├── users.md -│ │ ├── github.md -│ │ └── websockets.md -│ ├── deployment/ # Deployment guides -│ │ ├── installation.md -│ │ ├── configuration.md -│ │ ├── traefik.md -│ │ ├── github-app.md -│ │ ├── upgrading.md -│ │ ├── requirements.md -│ │ ├── security.md -│ │ └── backup.md -│ ├── public/ # Static assets -│ │ └── mist.png # Logo -│ └── index.md # Home page -├── package.json -└── README.md # This file -``` - -## Theme - -The documentation uses a custom theme that matches the Mist dashboard's dark purple color scheme using OKLCH colors. See `docs/.vitepress/theme/custom.css` for the theme implementation. - -### Color Scheme - -- Primary: `oklch(0.488 0.243 264.376)` - Purple -- Background: `oklch(0.141 0.005 285.823)` - Very dark purple -- Card: `oklch(0.21 0.006 285.885)` - Dark purple - -## Contributing - -To add or update documentation: - -1. Create or edit markdown files in the appropriate directory -2. Update `docs/.vitepress/config.js` navigation if adding new pages -3. Test locally with `npm run docs:dev` -4. Build with `npm run docs:build` to verify no errors -5. Submit a pull request - -## Coming Soon Sections - -Pages marked with "Coming Soon" badges represent planned features. These sections include: - -- Database Services -- SSL Automation -- Deployment Rollback -- Notifications -- CLI Tool - -As these features are implemented, update the corresponding documentation pages and remove the "Coming Soon" banners. - -## Deployment - -The documentation can be deployed to any static hosting service: - -- GitHub Pages -- Netlify -- Vercel -- Cloudflare Pages -- Your own web server - -Simply deploy the contents of `docs/.vitepress/dist/` after building. - -## License - -MIT diff --git a/www/bun.lock b/www/bun.lock deleted file mode 100644 index 0d8f030..0000000 --- a/www/bun.lock +++ /dev/null @@ -1,354 +0,0 @@ -{ - "lockfileVersion": 1, - "configVersion": 0, - "workspaces": { - "": { - "name": "www", - "devDependencies": { - "vitepress": "^1.6.4", - "vue": "^3.5.26", - }, - }, - }, - "packages": { - "@algolia/abtesting": ["@algolia/abtesting@1.12.1", "", { "dependencies": { "@algolia/client-common": "5.46.1", "@algolia/requester-browser-xhr": "5.46.1", "@algolia/requester-fetch": "5.46.1", "@algolia/requester-node-http": "5.46.1" } }, "sha512-Y+7e2uPe376OH5O73OB1+vR40ZhbV2kzGh/AR/dPCWguoBOp1IK0o+uZQLX+7i32RMMBEKl3pj6KVEav100Kvg=="], - - "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.17.7", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", "@algolia/autocomplete-shared": "1.17.7" } }, "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q=="], - - "@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.17.7", "", { "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A=="], - - "@algolia/autocomplete-preset-algolia": ["@algolia/autocomplete-preset-algolia@1.17.7", "", { "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA=="], - - "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.17.7", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg=="], - - "@algolia/client-abtesting": ["@algolia/client-abtesting@5.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1", "@algolia/requester-browser-xhr": "5.46.1", "@algolia/requester-fetch": "5.46.1", "@algolia/requester-node-http": "5.46.1" } }, "sha512-5SWfl0UGuKxMBYlU2Y9BnlIKKEyhFU5jHE9F9jAd8nbhxZNLk0y7fXE+AZeFtyK1lkVw6O4B/e6c3XIVVCkmqw=="], - - "@algolia/client-analytics": ["@algolia/client-analytics@5.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1", "@algolia/requester-browser-xhr": "5.46.1", "@algolia/requester-fetch": "5.46.1", "@algolia/requester-node-http": "5.46.1" } }, "sha512-496K6B1l/0Jvyp3MbW/YIgmm1a6nkTrKXBM7DoEy9YAOJ8GywGpa2UYjNCW1UrOTt+em1ECzDjRx7PIzTR9YvA=="], - - "@algolia/client-common": ["@algolia/client-common@5.46.1", "", {}, "sha512-3u6AuZ1Kiss6V5JPuZfVIUYfPi8im06QBCgKqLg82GUBJ3SwhiTdSZFIEgz2mzFuitFdW1PQi3c/65zE/3FgIw=="], - - "@algolia/client-insights": ["@algolia/client-insights@5.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1", "@algolia/requester-browser-xhr": "5.46.1", "@algolia/requester-fetch": "5.46.1", "@algolia/requester-node-http": "5.46.1" } }, "sha512-LwuWjdO35HHl1rxtdn48t920Xl26Dl0SMxjxjFeAK/OwK/pIVfYjOZl/f3Pnm7Kixze+6HjpByVxEaqhTuAFaw=="], - - "@algolia/client-personalization": ["@algolia/client-personalization@5.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1", "@algolia/requester-browser-xhr": "5.46.1", "@algolia/requester-fetch": "5.46.1", "@algolia/requester-node-http": "5.46.1" } }, "sha512-6LvJAlfEsn9SVq63MYAFX2iUxztUK2Q7BVZtI1vN87lDiJ/tSVFKgKS/jBVO03A39ePxJQiFv6EKv7lmoGlWtQ=="], - - "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1", "@algolia/requester-browser-xhr": "5.46.1", "@algolia/requester-fetch": "5.46.1", "@algolia/requester-node-http": "5.46.1" } }, "sha512-9GLUCyGGo7YOXHcNqbzca82XYHJTbuiI6iT0FTGc0BrnV2N4OcrznUuVKic/duiLSun5gcy/G2Bciw5Sav9f9w=="], - - "@algolia/client-search": ["@algolia/client-search@5.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1", "@algolia/requester-browser-xhr": "5.46.1", "@algolia/requester-fetch": "5.46.1", "@algolia/requester-node-http": "5.46.1" } }, "sha512-NL76o/BoEgU4ObY5oBEC3o6KSPpuXsnSta00tAxTm1iKUWOGR34DQEKhUt8xMHhMKleUNPM/rLPFiIVtfsGU8w=="], - - "@algolia/ingestion": ["@algolia/ingestion@1.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1", "@algolia/requester-browser-xhr": "5.46.1", "@algolia/requester-fetch": "5.46.1", "@algolia/requester-node-http": "5.46.1" } }, "sha512-52Nc8WKC1FFXsdlXlTMl1Re/pTAbd2DiJiNdYmgHiikZcfF96G+Opx4qKiLUG1q7zp9e+ahNwXF6ED0XChMywg=="], - - "@algolia/monitoring": ["@algolia/monitoring@1.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1", "@algolia/requester-browser-xhr": "5.46.1", "@algolia/requester-fetch": "5.46.1", "@algolia/requester-node-http": "5.46.1" } }, "sha512-1x2/2Y/eqz6l3QcEZ8u/zMhSCpjlhePyizJd3sXrmg031HjayYT5+IxikjpqkdF7TU/deCTd/TFUcxLJ2ZHXiQ=="], - - "@algolia/recommend": ["@algolia/recommend@5.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1", "@algolia/requester-browser-xhr": "5.46.1", "@algolia/requester-fetch": "5.46.1", "@algolia/requester-node-http": "5.46.1" } }, "sha512-SSd3KlQuplxV3aRs5+Z09XilFesgpPjtCG7BGRxLTVje5hn9BLmhjO4W3gKw01INUt44Z1r0Fwx5uqnhAouunA=="], - - "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1" } }, "sha512-3GfCwudeW6/3caKSdmOP6RXZEL4F3GiemCaXEStkTt2Re8f7NcGYAAZnGlHsCzvhlNEuDzPYdYxh4UweY8l/2w=="], - - "@algolia/requester-fetch": ["@algolia/requester-fetch@5.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1" } }, "sha512-JUAxYfmnLYTVtAOFxVvXJ4GDHIhMuaP7JGyZXa/nCk3P8RrN5FCNTdRyftSnxyzwSIAd8qH3CjdBS9WwxxqcHQ=="], - - "@algolia/requester-node-http": ["@algolia/requester-node-http@5.46.1", "", { "dependencies": { "@algolia/client-common": "5.46.1" } }, "sha512-VwbhV1xvTGiek3d2pOS6vNBC4dtbNadyRT+i1niZpGhOJWz1XnfhxNboVbXPGAyMJYz7kDrolbDvEzIDT93uUA=="], - - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], - - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], - - "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" } }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], - - "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], - - "@docsearch/css": ["@docsearch/css@3.8.2", "", {}, "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ=="], - - "@docsearch/js": ["@docsearch/js@3.8.2", "", { "dependencies": { "@docsearch/react": "3.8.2", "preact": "^10.0.0" } }, "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ=="], - - "@docsearch/react": ["@docsearch/react@3.8.2", "", { "dependencies": { "@algolia/autocomplete-core": "1.17.7", "@algolia/autocomplete-preset-algolia": "1.17.7", "@docsearch/css": "3.8.2", "algoliasearch": "^5.14.2" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", "react": ">= 16.8.0 < 19.0.0", "react-dom": ">= 16.8.0 < 19.0.0", "search-insights": ">= 1 < 3" }, "optionalPeers": ["@types/react", "react", "react-dom"] }, "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg=="], - - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], - - "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], - - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], - - "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], - - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], - - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], - - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], - - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], - - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], - - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], - - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], - - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], - - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], - - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], - - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], - - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], - - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], - - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], - - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], - - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], - - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], - - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], - - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - - "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.63", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-xZl2UWCwE58VlqZ+pDPmaUhE2tq8MVSTJRr4/9nzzHlDdjJ0Ud1VxNXPrwTSgESKY29iCQw3S0r2nJTSNNngHw=="], - - "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], - - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.54.0", "", { "os": "android", "cpu": "arm" }, "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng=="], - - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.54.0", "", { "os": "android", "cpu": "arm64" }, "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw=="], - - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.54.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw=="], - - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.54.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A=="], - - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.54.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA=="], - - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.54.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ=="], - - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.54.0", "", { "os": "linux", "cpu": "arm" }, "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ=="], - - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.54.0", "", { "os": "linux", "cpu": "arm" }, "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA=="], - - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.54.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng=="], - - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.54.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg=="], - - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw=="], - - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.54.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA=="], - - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ=="], - - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A=="], - - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.54.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ=="], - - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.54.0", "", { "os": "linux", "cpu": "x64" }, "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ=="], - - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.54.0", "", { "os": "linux", "cpu": "x64" }, "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw=="], - - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.54.0", "", { "os": "none", "cpu": "arm64" }, "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg=="], - - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.54.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw=="], - - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.54.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ=="], - - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.54.0", "", { "os": "win32", "cpu": "x64" }, "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ=="], - - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.54.0", "", { "os": "win32", "cpu": "x64" }, "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg=="], - - "@shikijs/core": ["@shikijs/core@2.5.0", "", { "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="], - - "@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="], - - "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="], - - "@shikijs/langs": ["@shikijs/langs@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w=="], - - "@shikijs/themes": ["@shikijs/themes@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw=="], - - "@shikijs/transformers": ["@shikijs/transformers@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/types": "2.5.0" } }, "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg=="], - - "@shikijs/types": ["@shikijs/types@2.5.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw=="], - - "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], - - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - - "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], - - "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="], - - "@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="], - - "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], - - "@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="], - - "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], - - "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="], - - "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], - - "@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.4", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="], - - "@vue/compiler-core": ["@vue/compiler-core@3.5.26", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/shared": "3.5.26", "entities": "^7.0.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w=="], - - "@vue/compiler-dom": ["@vue/compiler-dom@3.5.26", "", { "dependencies": { "@vue/compiler-core": "3.5.26", "@vue/shared": "3.5.26" } }, "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A=="], - - "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.26", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/compiler-core": "3.5.26", "@vue/compiler-dom": "3.5.26", "@vue/compiler-ssr": "3.5.26", "@vue/shared": "3.5.26", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA=="], - - "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.26", "", { "dependencies": { "@vue/compiler-dom": "3.5.26", "@vue/shared": "3.5.26" } }, "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw=="], - - "@vue/devtools-api": ["@vue/devtools-api@7.7.9", "", { "dependencies": { "@vue/devtools-kit": "^7.7.9" } }, "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g=="], - - "@vue/devtools-kit": ["@vue/devtools-kit@7.7.9", "", { "dependencies": { "@vue/devtools-shared": "^7.7.9", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA=="], - - "@vue/devtools-shared": ["@vue/devtools-shared@7.7.9", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA=="], - - "@vue/reactivity": ["@vue/reactivity@3.5.26", "", { "dependencies": { "@vue/shared": "3.5.26" } }, "sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ=="], - - "@vue/runtime-core": ["@vue/runtime-core@3.5.26", "", { "dependencies": { "@vue/reactivity": "3.5.26", "@vue/shared": "3.5.26" } }, "sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q=="], - - "@vue/runtime-dom": ["@vue/runtime-dom@3.5.26", "", { "dependencies": { "@vue/reactivity": "3.5.26", "@vue/runtime-core": "3.5.26", "@vue/shared": "3.5.26", "csstype": "^3.2.3" } }, "sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ=="], - - "@vue/server-renderer": ["@vue/server-renderer@3.5.26", "", { "dependencies": { "@vue/compiler-ssr": "3.5.26", "@vue/shared": "3.5.26" }, "peerDependencies": { "vue": "3.5.26" } }, "sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA=="], - - "@vue/shared": ["@vue/shared@3.5.26", "", {}, "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A=="], - - "@vueuse/core": ["@vueuse/core@12.8.2", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" } }, "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ=="], - - "@vueuse/integrations": ["@vueuse/integrations@12.8.2", "", { "dependencies": { "@vueuse/core": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" }, "peerDependencies": { "async-validator": "^4", "axios": "^1", "change-case": "^5", "drauu": "^0.4", "focus-trap": "^7", "fuse.js": "^7", "idb-keyval": "^6", "jwt-decode": "^4", "nprogress": "^0.2", "qrcode": "^1.5", "sortablejs": "^1", "universal-cookie": "^7" }, "optionalPeers": ["async-validator", "axios", "change-case", "drauu", "fuse.js", "idb-keyval", "jwt-decode", "nprogress", "qrcode", "sortablejs", "universal-cookie"] }, "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g=="], - - "@vueuse/metadata": ["@vueuse/metadata@12.8.2", "", {}, "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A=="], - - "@vueuse/shared": ["@vueuse/shared@12.8.2", "", { "dependencies": { "vue": "^3.5.13" } }, "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w=="], - - "algoliasearch": ["algoliasearch@5.46.1", "", { "dependencies": { "@algolia/abtesting": "1.12.1", "@algolia/client-abtesting": "5.46.1", "@algolia/client-analytics": "5.46.1", "@algolia/client-common": "5.46.1", "@algolia/client-insights": "5.46.1", "@algolia/client-personalization": "5.46.1", "@algolia/client-query-suggestions": "5.46.1", "@algolia/client-search": "5.46.1", "@algolia/ingestion": "1.46.1", "@algolia/monitoring": "1.46.1", "@algolia/recommend": "5.46.1", "@algolia/requester-browser-xhr": "5.46.1", "@algolia/requester-fetch": "5.46.1", "@algolia/requester-node-http": "5.46.1" } }, "sha512-39ol8Ulqb3MntofkXHlrcXKyU8BU0PXvQrXPBIX6eXj/EO4VT7651mhGVORI2oF8ydya9nFzT3fYDoqme/KL6w=="], - - "birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="], - - "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], - - "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], - - "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], - - "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], - - "copy-anything": ["copy-anything@4.0.5", "", { "dependencies": { "is-what": "^5.2.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="], - - "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], - - "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], - - "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], - - "emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="], - - "entities": ["entities@7.0.0", "", {}, "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ=="], - - "esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": "bin/esbuild" }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - - "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - - "focus-trap": ["focus-trap@7.6.6", "", { "dependencies": { "tabbable": "^6.3.0" } }, "sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q=="], - - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - - "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], - - "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], - - "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], - - "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], - - "is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="], - - "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], - - "mark.js": ["mark.js@8.11.1", "", {}, "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ=="], - - "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], - - "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], - - "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], - - "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], - - "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], - - "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], - - "minisearch": ["minisearch@7.2.0", "", {}, "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg=="], - - "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], - - "nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "oniguruma-to-es": ["oniguruma-to-es@3.1.1", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="], - - "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], - - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - - "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - - "preact": ["preact@10.28.0", "", {}, "sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA=="], - - "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], - - "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], - - "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], - - "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], - - "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - - "rollup": ["rollup@4.54.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.54.0", "@rollup/rollup-android-arm64": "4.54.0", "@rollup/rollup-darwin-arm64": "4.54.0", "@rollup/rollup-darwin-x64": "4.54.0", "@rollup/rollup-freebsd-arm64": "4.54.0", "@rollup/rollup-freebsd-x64": "4.54.0", "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", "@rollup/rollup-linux-arm-musleabihf": "4.54.0", "@rollup/rollup-linux-arm64-gnu": "4.54.0", "@rollup/rollup-linux-arm64-musl": "4.54.0", "@rollup/rollup-linux-loong64-gnu": "4.54.0", "@rollup/rollup-linux-ppc64-gnu": "4.54.0", "@rollup/rollup-linux-riscv64-gnu": "4.54.0", "@rollup/rollup-linux-riscv64-musl": "4.54.0", "@rollup/rollup-linux-s390x-gnu": "4.54.0", "@rollup/rollup-linux-x64-gnu": "4.54.0", "@rollup/rollup-linux-x64-musl": "4.54.0", "@rollup/rollup-openharmony-arm64": "4.54.0", "@rollup/rollup-win32-arm64-msvc": "4.54.0", "@rollup/rollup-win32-ia32-msvc": "4.54.0", "@rollup/rollup-win32-x64-gnu": "4.54.0", "@rollup/rollup-win32-x64-msvc": "4.54.0", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw=="], - - "search-insights": ["search-insights@2.17.3", "", {}, "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ=="], - - "shiki": ["shiki@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/langs": "2.5.0", "@shikijs/themes": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ=="], - - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - - "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], - - "speakingurl": ["speakingurl@14.0.1", "", {}, "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="], - - "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], - - "superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="], - - "tabbable": ["tabbable@6.3.0", "", {}, "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ=="], - - "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], - - "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], - - "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], - - "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], - - "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="], - - "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], - - "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], - - "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - - "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": "bin/vite.js" }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], - - "vitepress": ["vitepress@1.6.4", "", { "dependencies": { "@docsearch/css": "3.8.2", "@docsearch/js": "3.8.2", "@iconify-json/simple-icons": "^1.2.21", "@shikijs/core": "^2.1.0", "@shikijs/transformers": "^2.1.0", "@shikijs/types": "^2.1.0", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools-api": "^7.7.0", "@vue/shared": "^3.5.13", "@vueuse/core": "^12.4.0", "@vueuse/integrations": "^12.4.0", "focus-trap": "^7.6.4", "mark.js": "8.11.1", "minisearch": "^7.1.1", "shiki": "^2.1.0", "vite": "^5.4.14", "vue": "^3.5.13" }, "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" }, "optionalPeers": ["markdown-it-mathjax3"], "bin": "bin/vitepress.js" }, "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg=="], - - "vue": ["vue@3.5.26", "", { "dependencies": { "@vue/compiler-dom": "3.5.26", "@vue/compiler-sfc": "3.5.26", "@vue/runtime-dom": "3.5.26", "@vue/server-renderer": "3.5.26", "@vue/shared": "3.5.26" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA=="], - - "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], - } -} diff --git a/www/docs/.vitepress/cache/deps/_metadata.json b/www/docs/.vitepress/cache/deps/_metadata.json deleted file mode 100644 index 0174529..0000000 --- a/www/docs/.vitepress/cache/deps/_metadata.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "hash": "a6985175", - "configHash": "202a3a6a", - "lockfileHash": "d734444e", - "browserHash": "ffe086b0", - "optimized": { - "vue": { - "src": "../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js", - "file": "vue.js", - "fileHash": "1e83a252", - "needsInterop": false - }, - "vitepress > @vue/devtools-api": { - "src": "../../../../node_modules/@vue/devtools-api/dist/index.js", - "file": "vitepress___@vue_devtools-api.js", - "fileHash": "dabc8034", - "needsInterop": false - }, - "vitepress > @vueuse/core": { - "src": "../../../../node_modules/@vueuse/core/index.mjs", - "file": "vitepress___@vueuse_core.js", - "fileHash": "aedcf797", - "needsInterop": false - }, - "vitepress > @vueuse/integrations/useFocusTrap": { - "src": "../../../../node_modules/@vueuse/integrations/useFocusTrap.mjs", - "file": "vitepress___@vueuse_integrations_useFocusTrap.js", - "fileHash": "fbb05e5d", - "needsInterop": false - }, - "vitepress > mark.js/src/vanilla.js": { - "src": "../../../../node_modules/mark.js/src/vanilla.js", - "file": "vitepress___mark__js_src_vanilla__js.js", - "fileHash": "3fe456ed", - "needsInterop": false - }, - "vitepress > minisearch": { - "src": "../../../../node_modules/minisearch/dist/es/index.js", - "file": "vitepress___minisearch.js", - "fileHash": "5364dc96", - "needsInterop": false - } - }, - "chunks": { - "chunk-2CLQ7TTZ": { - "file": "chunk-2CLQ7TTZ.js" - }, - "chunk-LE5NDSFD": { - "file": "chunk-LE5NDSFD.js" - } - } -} \ No newline at end of file diff --git a/www/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js b/www/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js deleted file mode 100644 index 8cd09fb..0000000 --- a/www/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js +++ /dev/null @@ -1,9719 +0,0 @@ -import { - Fragment, - TransitionGroup, - computed, - customRef, - defineComponent, - effectScope, - getCurrentInstance, - getCurrentScope, - h, - hasInjectionContext, - inject, - isReactive, - isReadonly, - isRef, - markRaw, - nextTick, - onBeforeMount, - onBeforeUnmount, - onBeforeUpdate, - onMounted, - onScopeDispose, - onUnmounted, - onUpdated, - provide, - reactive, - readonly, - ref, - shallowReactive, - shallowRef, - toRaw, - toRef, - toRefs, - toValue, - unref, - watch, - watchEffect -} from "./chunk-LE5NDSFD.js"; - -// node_modules/@vueuse/shared/index.mjs -function computedEager(fn, options) { - var _a; - const result = shallowRef(); - watchEffect(() => { - result.value = fn(); - }, { - ...options, - flush: (_a = options == null ? void 0 : options.flush) != null ? _a : "sync" - }); - return readonly(result); -} -function computedWithControl(source, fn) { - let v = void 0; - let track; - let trigger; - const dirty = shallowRef(true); - const update = () => { - dirty.value = true; - trigger(); - }; - watch(source, update, { flush: "sync" }); - const get2 = typeof fn === "function" ? fn : fn.get; - const set2 = typeof fn === "function" ? void 0 : fn.set; - const result = customRef((_track, _trigger) => { - track = _track; - trigger = _trigger; - return { - get() { - if (dirty.value) { - v = get2(v); - dirty.value = false; - } - track(); - return v; - }, - set(v2) { - set2 == null ? void 0 : set2(v2); - } - }; - }); - if (Object.isExtensible(result)) - result.trigger = update; - return result; -} -function tryOnScopeDispose(fn) { - if (getCurrentScope()) { - onScopeDispose(fn); - return true; - } - return false; -} -function createEventHook() { - const fns = /* @__PURE__ */ new Set(); - const off = (fn) => { - fns.delete(fn); - }; - const clear = () => { - fns.clear(); - }; - const on = (fn) => { - fns.add(fn); - const offFn = () => off(fn); - tryOnScopeDispose(offFn); - return { - off: offFn - }; - }; - const trigger = (...args) => { - return Promise.all(Array.from(fns).map((fn) => fn(...args))); - }; - return { - on, - off, - trigger, - clear - }; -} -function createGlobalState(stateFactory) { - let initialized = false; - let state; - const scope = effectScope(true); - return (...args) => { - if (!initialized) { - state = scope.run(() => stateFactory(...args)); - initialized = true; - } - return state; - }; -} -var localProvidedStateMap = /* @__PURE__ */ new WeakMap(); -var injectLocal = (...args) => { - var _a; - const key = args[0]; - const instance = (_a = getCurrentInstance()) == null ? void 0 : _a.proxy; - if (instance == null && !hasInjectionContext()) - throw new Error("injectLocal must be called in setup"); - if (instance && localProvidedStateMap.has(instance) && key in localProvidedStateMap.get(instance)) - return localProvidedStateMap.get(instance)[key]; - return inject(...args); -}; -var provideLocal = (key, value) => { - var _a; - const instance = (_a = getCurrentInstance()) == null ? void 0 : _a.proxy; - if (instance == null) - throw new Error("provideLocal must be called in setup"); - if (!localProvidedStateMap.has(instance)) - localProvidedStateMap.set(instance, /* @__PURE__ */ Object.create(null)); - const localProvidedState = localProvidedStateMap.get(instance); - localProvidedState[key] = value; - provide(key, value); -}; -function createInjectionState(composable, options) { - const key = (options == null ? void 0 : options.injectionKey) || Symbol(composable.name || "InjectionState"); - const defaultValue = options == null ? void 0 : options.defaultValue; - const useProvidingState = (...args) => { - const state = composable(...args); - provideLocal(key, state); - return state; - }; - const useInjectedState = () => injectLocal(key, defaultValue); - return [useProvidingState, useInjectedState]; -} -function createRef(value, deep) { - if (deep === true) { - return ref(value); - } else { - return shallowRef(value); - } -} -function createSharedComposable(composable) { - let subscribers = 0; - let state; - let scope; - const dispose = () => { - subscribers -= 1; - if (scope && subscribers <= 0) { - scope.stop(); - state = void 0; - scope = void 0; - } - }; - return (...args) => { - subscribers += 1; - if (!scope) { - scope = effectScope(true); - state = scope.run(() => composable(...args)); - } - tryOnScopeDispose(dispose); - return state; - }; -} -function extendRef(ref2, extend, { enumerable = false, unwrap = true } = {}) { - for (const [key, value] of Object.entries(extend)) { - if (key === "value") - continue; - if (isRef(value) && unwrap) { - Object.defineProperty(ref2, key, { - get() { - return value.value; - }, - set(v) { - value.value = v; - }, - enumerable - }); - } else { - Object.defineProperty(ref2, key, { value, enumerable }); - } - } - return ref2; -} -function get(obj, key) { - if (key == null) - return unref(obj); - return unref(obj)[key]; -} -function isDefined(v) { - return unref(v) != null; -} -function makeDestructurable(obj, arr) { - if (typeof Symbol !== "undefined") { - const clone = { ...obj }; - Object.defineProperty(clone, Symbol.iterator, { - enumerable: false, - value() { - let index = 0; - return { - next: () => ({ - value: arr[index++], - done: index > arr.length - }) - }; - } - }); - return clone; - } else { - return Object.assign([...arr], obj); - } -} -function reactify(fn, options) { - const unrefFn = (options == null ? void 0 : options.computedGetter) === false ? unref : toValue; - return function(...args) { - return computed(() => fn.apply(this, args.map((i) => unrefFn(i)))); - }; -} -function reactifyObject(obj, optionsOrKeys = {}) { - let keys2 = []; - let options; - if (Array.isArray(optionsOrKeys)) { - keys2 = optionsOrKeys; - } else { - options = optionsOrKeys; - const { includeOwnProperties = true } = optionsOrKeys; - keys2.push(...Object.keys(obj)); - if (includeOwnProperties) - keys2.push(...Object.getOwnPropertyNames(obj)); - } - return Object.fromEntries( - keys2.map((key) => { - const value = obj[key]; - return [ - key, - typeof value === "function" ? reactify(value.bind(obj), options) : value - ]; - }) - ); -} -function toReactive(objectRef) { - if (!isRef(objectRef)) - return reactive(objectRef); - const proxy = new Proxy({}, { - get(_, p, receiver) { - return unref(Reflect.get(objectRef.value, p, receiver)); - }, - set(_, p, value) { - if (isRef(objectRef.value[p]) && !isRef(value)) - objectRef.value[p].value = value; - else - objectRef.value[p] = value; - return true; - }, - deleteProperty(_, p) { - return Reflect.deleteProperty(objectRef.value, p); - }, - has(_, p) { - return Reflect.has(objectRef.value, p); - }, - ownKeys() { - return Object.keys(objectRef.value); - }, - getOwnPropertyDescriptor() { - return { - enumerable: true, - configurable: true - }; - } - }); - return reactive(proxy); -} -function reactiveComputed(fn) { - return toReactive(computed(fn)); -} -function reactiveOmit(obj, ...keys2) { - const flatKeys = keys2.flat(); - const predicate = flatKeys[0]; - return reactiveComputed(() => typeof predicate === "function" ? Object.fromEntries(Object.entries(toRefs(obj)).filter(([k, v]) => !predicate(toValue(v), k))) : Object.fromEntries(Object.entries(toRefs(obj)).filter((e) => !flatKeys.includes(e[0])))); -} -var isClient = typeof window !== "undefined" && typeof document !== "undefined"; -var isWorker = typeof WorkerGlobalScope !== "undefined" && globalThis instanceof WorkerGlobalScope; -var isDef = (val) => typeof val !== "undefined"; -var notNullish = (val) => val != null; -var assert = (condition, ...infos) => { - if (!condition) - console.warn(...infos); -}; -var toString = Object.prototype.toString; -var isObject = (val) => toString.call(val) === "[object Object]"; -var now = () => Date.now(); -var timestamp = () => +Date.now(); -var clamp = (n, min, max) => Math.min(max, Math.max(min, n)); -var noop = () => { -}; -var rand = (min, max) => { - min = Math.ceil(min); - max = Math.floor(max); - return Math.floor(Math.random() * (max - min + 1)) + min; -}; -var hasOwn = (val, key) => Object.prototype.hasOwnProperty.call(val, key); -var isIOS = getIsIOS(); -function getIsIOS() { - var _a, _b; - return isClient && ((_a = window == null ? void 0 : window.navigator) == null ? void 0 : _a.userAgent) && (/iP(?:ad|hone|od)/.test(window.navigator.userAgent) || ((_b = window == null ? void 0 : window.navigator) == null ? void 0 : _b.maxTouchPoints) > 2 && /iPad|Macintosh/.test(window == null ? void 0 : window.navigator.userAgent)); -} -function createFilterWrapper(filter, fn) { - function wrapper(...args) { - return new Promise((resolve, reject) => { - Promise.resolve(filter(() => fn.apply(this, args), { fn, thisArg: this, args })).then(resolve).catch(reject); - }); - } - return wrapper; -} -var bypassFilter = (invoke2) => { - return invoke2(); -}; -function debounceFilter(ms, options = {}) { - let timer; - let maxTimer; - let lastRejector = noop; - const _clearTimeout = (timer2) => { - clearTimeout(timer2); - lastRejector(); - lastRejector = noop; - }; - let lastInvoker; - const filter = (invoke2) => { - const duration = toValue(ms); - const maxDuration = toValue(options.maxWait); - if (timer) - _clearTimeout(timer); - if (duration <= 0 || maxDuration !== void 0 && maxDuration <= 0) { - if (maxTimer) { - _clearTimeout(maxTimer); - maxTimer = null; - } - return Promise.resolve(invoke2()); - } - return new Promise((resolve, reject) => { - lastRejector = options.rejectOnCancel ? reject : resolve; - lastInvoker = invoke2; - if (maxDuration && !maxTimer) { - maxTimer = setTimeout(() => { - if (timer) - _clearTimeout(timer); - maxTimer = null; - resolve(lastInvoker()); - }, maxDuration); - } - timer = setTimeout(() => { - if (maxTimer) - _clearTimeout(maxTimer); - maxTimer = null; - resolve(invoke2()); - }, duration); - }); - }; - return filter; -} -function throttleFilter(...args) { - let lastExec = 0; - let timer; - let isLeading = true; - let lastRejector = noop; - let lastValue; - let ms; - let trailing; - let leading; - let rejectOnCancel; - if (!isRef(args[0]) && typeof args[0] === "object") - ({ delay: ms, trailing = true, leading = true, rejectOnCancel = false } = args[0]); - else - [ms, trailing = true, leading = true, rejectOnCancel = false] = args; - const clear = () => { - if (timer) { - clearTimeout(timer); - timer = void 0; - lastRejector(); - lastRejector = noop; - } - }; - const filter = (_invoke) => { - const duration = toValue(ms); - const elapsed = Date.now() - lastExec; - const invoke2 = () => { - return lastValue = _invoke(); - }; - clear(); - if (duration <= 0) { - lastExec = Date.now(); - return invoke2(); - } - if (elapsed > duration && (leading || !isLeading)) { - lastExec = Date.now(); - invoke2(); - } else if (trailing) { - lastValue = new Promise((resolve, reject) => { - lastRejector = rejectOnCancel ? reject : resolve; - timer = setTimeout(() => { - lastExec = Date.now(); - isLeading = true; - resolve(invoke2()); - clear(); - }, Math.max(0, duration - elapsed)); - }); - } - if (!leading && !timer) - timer = setTimeout(() => isLeading = true, duration); - isLeading = false; - return lastValue; - }; - return filter; -} -function pausableFilter(extendFilter = bypassFilter, options = {}) { - const { - initialState = "active" - } = options; - const isActive = toRef2(initialState === "active"); - function pause() { - isActive.value = false; - } - function resume() { - isActive.value = true; - } - const eventFilter = (...args) => { - if (isActive.value) - extendFilter(...args); - }; - return { isActive: readonly(isActive), pause, resume, eventFilter }; -} -function cacheStringFunction(fn) { - const cache = /* @__PURE__ */ Object.create(null); - return (str) => { - const hit = cache[str]; - return hit || (cache[str] = fn(str)); - }; -} -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, "-$1").toLowerCase()); -var camelizeRE = /-(\w)/g; -var camelize = cacheStringFunction((str) => { - return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : ""); -}); -function promiseTimeout(ms, throwOnTimeout = false, reason = "Timeout") { - return new Promise((resolve, reject) => { - if (throwOnTimeout) - setTimeout(() => reject(reason), ms); - else - setTimeout(resolve, ms); - }); -} -function identity(arg) { - return arg; -} -function createSingletonPromise(fn) { - let _promise; - function wrapper() { - if (!_promise) - _promise = fn(); - return _promise; - } - wrapper.reset = async () => { - const _prev = _promise; - _promise = void 0; - if (_prev) - await _prev; - }; - return wrapper; -} -function invoke(fn) { - return fn(); -} -function containsProp(obj, ...props) { - return props.some((k) => k in obj); -} -function increaseWithUnit(target, delta) { - var _a; - if (typeof target === "number") - return target + delta; - const value = ((_a = target.match(/^-?\d+\.?\d*/)) == null ? void 0 : _a[0]) || ""; - const unit = target.slice(value.length); - const result = Number.parseFloat(value) + delta; - if (Number.isNaN(result)) - return target; - return result + unit; -} -function pxValue(px) { - return px.endsWith("rem") ? Number.parseFloat(px) * 16 : Number.parseFloat(px); -} -function objectPick(obj, keys2, omitUndefined = false) { - return keys2.reduce((n, k) => { - if (k in obj) { - if (!omitUndefined || obj[k] !== void 0) - n[k] = obj[k]; - } - return n; - }, {}); -} -function objectOmit(obj, keys2, omitUndefined = false) { - return Object.fromEntries(Object.entries(obj).filter(([key, value]) => { - return (!omitUndefined || value !== void 0) && !keys2.includes(key); - })); -} -function objectEntries(obj) { - return Object.entries(obj); -} -function getLifeCycleTarget(target) { - return target || getCurrentInstance(); -} -function toArray(value) { - return Array.isArray(value) ? value : [value]; -} -function toRef2(...args) { - if (args.length !== 1) - return toRef(...args); - const r = args[0]; - return typeof r === "function" ? readonly(customRef(() => ({ get: r, set: noop }))) : ref(r); -} -var resolveRef = toRef2; -function reactivePick(obj, ...keys2) { - const flatKeys = keys2.flat(); - const predicate = flatKeys[0]; - return reactiveComputed(() => typeof predicate === "function" ? Object.fromEntries(Object.entries(toRefs(obj)).filter(([k, v]) => predicate(toValue(v), k))) : Object.fromEntries(flatKeys.map((k) => [k, toRef2(obj, k)]))); -} -function refAutoReset(defaultValue, afterMs = 1e4) { - return customRef((track, trigger) => { - let value = toValue(defaultValue); - let timer; - const resetAfter = () => setTimeout(() => { - value = toValue(defaultValue); - trigger(); - }, toValue(afterMs)); - tryOnScopeDispose(() => { - clearTimeout(timer); - }); - return { - get() { - track(); - return value; - }, - set(newValue) { - value = newValue; - trigger(); - clearTimeout(timer); - timer = resetAfter(); - } - }; - }); -} -function useDebounceFn(fn, ms = 200, options = {}) { - return createFilterWrapper( - debounceFilter(ms, options), - fn - ); -} -function refDebounced(value, ms = 200, options = {}) { - const debounced = ref(value.value); - const updater = useDebounceFn(() => { - debounced.value = value.value; - }, ms, options); - watch(value, () => updater()); - return debounced; -} -function refDefault(source, defaultValue) { - return computed({ - get() { - var _a; - return (_a = source.value) != null ? _a : defaultValue; - }, - set(value) { - source.value = value; - } - }); -} -function useThrottleFn(fn, ms = 200, trailing = false, leading = true, rejectOnCancel = false) { - return createFilterWrapper( - throttleFilter(ms, trailing, leading, rejectOnCancel), - fn - ); -} -function refThrottled(value, delay = 200, trailing = true, leading = true) { - if (delay <= 0) - return value; - const throttled = ref(value.value); - const updater = useThrottleFn(() => { - throttled.value = value.value; - }, delay, trailing, leading); - watch(value, () => updater()); - return throttled; -} -function refWithControl(initial, options = {}) { - let source = initial; - let track; - let trigger; - const ref2 = customRef((_track, _trigger) => { - track = _track; - trigger = _trigger; - return { - get() { - return get2(); - }, - set(v) { - set2(v); - } - }; - }); - function get2(tracking = true) { - if (tracking) - track(); - return source; - } - function set2(value, triggering = true) { - var _a, _b; - if (value === source) - return; - const old = source; - if (((_a = options.onBeforeChange) == null ? void 0 : _a.call(options, value, old)) === false) - return; - source = value; - (_b = options.onChanged) == null ? void 0 : _b.call(options, value, old); - if (triggering) - trigger(); - } - const untrackedGet = () => get2(false); - const silentSet = (v) => set2(v, false); - const peek = () => get2(false); - const lay = (v) => set2(v, false); - return extendRef( - ref2, - { - get: get2, - set: set2, - untrackedGet, - silentSet, - peek, - lay - }, - { enumerable: true } - ); -} -var controlledRef = refWithControl; -function set(...args) { - if (args.length === 2) { - const [ref2, value] = args; - ref2.value = value; - } - if (args.length === 3) { - const [target, key, value] = args; - target[key] = value; - } -} -function watchWithFilter(source, cb, options = {}) { - const { - eventFilter = bypassFilter, - ...watchOptions - } = options; - return watch( - source, - createFilterWrapper( - eventFilter, - cb - ), - watchOptions - ); -} -function watchPausable(source, cb, options = {}) { - const { - eventFilter: filter, - initialState = "active", - ...watchOptions - } = options; - const { eventFilter, pause, resume, isActive } = pausableFilter(filter, { initialState }); - const stop = watchWithFilter( - source, - cb, - { - ...watchOptions, - eventFilter - } - ); - return { stop, pause, resume, isActive }; -} -function syncRef(left, right, ...[options]) { - const { - flush = "sync", - deep = false, - immediate = true, - direction = "both", - transform = {} - } = options || {}; - const watchers = []; - const transformLTR = "ltr" in transform && transform.ltr || ((v) => v); - const transformRTL = "rtl" in transform && transform.rtl || ((v) => v); - if (direction === "both" || direction === "ltr") { - watchers.push(watchPausable( - left, - (newValue) => { - watchers.forEach((w) => w.pause()); - right.value = transformLTR(newValue); - watchers.forEach((w) => w.resume()); - }, - { flush, deep, immediate } - )); - } - if (direction === "both" || direction === "rtl") { - watchers.push(watchPausable( - right, - (newValue) => { - watchers.forEach((w) => w.pause()); - left.value = transformRTL(newValue); - watchers.forEach((w) => w.resume()); - }, - { flush, deep, immediate } - )); - } - const stop = () => { - watchers.forEach((w) => w.stop()); - }; - return stop; -} -function syncRefs(source, targets, options = {}) { - const { - flush = "sync", - deep = false, - immediate = true - } = options; - const targetsArray = toArray(targets); - return watch( - source, - (newValue) => targetsArray.forEach((target) => target.value = newValue), - { flush, deep, immediate } - ); -} -function toRefs2(objectRef, options = {}) { - if (!isRef(objectRef)) - return toRefs(objectRef); - const result = Array.isArray(objectRef.value) ? Array.from({ length: objectRef.value.length }) : {}; - for (const key in objectRef.value) { - result[key] = customRef(() => ({ - get() { - return objectRef.value[key]; - }, - set(v) { - var _a; - const replaceRef = (_a = toValue(options.replaceRef)) != null ? _a : true; - if (replaceRef) { - if (Array.isArray(objectRef.value)) { - const copy = [...objectRef.value]; - copy[key] = v; - objectRef.value = copy; - } else { - const newObject = { ...objectRef.value, [key]: v }; - Object.setPrototypeOf(newObject, Object.getPrototypeOf(objectRef.value)); - objectRef.value = newObject; - } - } else { - objectRef.value[key] = v; - } - } - })); - } - return result; -} -var toValue2 = toValue; -var resolveUnref = toValue; -function tryOnBeforeMount(fn, sync = true, target) { - const instance = getLifeCycleTarget(target); - if (instance) - onBeforeMount(fn, target); - else if (sync) - fn(); - else - nextTick(fn); -} -function tryOnBeforeUnmount(fn, target) { - const instance = getLifeCycleTarget(target); - if (instance) - onBeforeUnmount(fn, target); -} -function tryOnMounted(fn, sync = true, target) { - const instance = getLifeCycleTarget(); - if (instance) - onMounted(fn, target); - else if (sync) - fn(); - else - nextTick(fn); -} -function tryOnUnmounted(fn, target) { - const instance = getLifeCycleTarget(target); - if (instance) - onUnmounted(fn, target); -} -function createUntil(r, isNot = false) { - function toMatch(condition, { flush = "sync", deep = false, timeout, throwOnTimeout } = {}) { - let stop = null; - const watcher = new Promise((resolve) => { - stop = watch( - r, - (v) => { - if (condition(v) !== isNot) { - if (stop) - stop(); - else - nextTick(() => stop == null ? void 0 : stop()); - resolve(v); - } - }, - { - flush, - deep, - immediate: true - } - ); - }); - const promises = [watcher]; - if (timeout != null) { - promises.push( - promiseTimeout(timeout, throwOnTimeout).then(() => toValue(r)).finally(() => stop == null ? void 0 : stop()) - ); - } - return Promise.race(promises); - } - function toBe(value, options) { - if (!isRef(value)) - return toMatch((v) => v === value, options); - const { flush = "sync", deep = false, timeout, throwOnTimeout } = options != null ? options : {}; - let stop = null; - const watcher = new Promise((resolve) => { - stop = watch( - [r, value], - ([v1, v2]) => { - if (isNot !== (v1 === v2)) { - if (stop) - stop(); - else - nextTick(() => stop == null ? void 0 : stop()); - resolve(v1); - } - }, - { - flush, - deep, - immediate: true - } - ); - }); - const promises = [watcher]; - if (timeout != null) { - promises.push( - promiseTimeout(timeout, throwOnTimeout).then(() => toValue(r)).finally(() => { - stop == null ? void 0 : stop(); - return toValue(r); - }) - ); - } - return Promise.race(promises); - } - function toBeTruthy(options) { - return toMatch((v) => Boolean(v), options); - } - function toBeNull(options) { - return toBe(null, options); - } - function toBeUndefined(options) { - return toBe(void 0, options); - } - function toBeNaN(options) { - return toMatch(Number.isNaN, options); - } - function toContains(value, options) { - return toMatch((v) => { - const array = Array.from(v); - return array.includes(value) || array.includes(toValue(value)); - }, options); - } - function changed(options) { - return changedTimes(1, options); - } - function changedTimes(n = 1, options) { - let count = -1; - return toMatch(() => { - count += 1; - return count >= n; - }, options); - } - if (Array.isArray(toValue(r))) { - const instance = { - toMatch, - toContains, - changed, - changedTimes, - get not() { - return createUntil(r, !isNot); - } - }; - return instance; - } else { - const instance = { - toMatch, - toBe, - toBeTruthy, - toBeNull, - toBeNaN, - toBeUndefined, - changed, - changedTimes, - get not() { - return createUntil(r, !isNot); - } - }; - return instance; - } -} -function until(r) { - return createUntil(r); -} -function defaultComparator(value, othVal) { - return value === othVal; -} -function useArrayDifference(...args) { - var _a, _b; - const list = args[0]; - const values = args[1]; - let compareFn = (_a = args[2]) != null ? _a : defaultComparator; - const { - symmetric = false - } = (_b = args[3]) != null ? _b : {}; - if (typeof compareFn === "string") { - const key = compareFn; - compareFn = (value, othVal) => value[key] === othVal[key]; - } - const diff1 = computed(() => toValue(list).filter((x) => toValue(values).findIndex((y) => compareFn(x, y)) === -1)); - if (symmetric) { - const diff2 = computed(() => toValue(values).filter((x) => toValue(list).findIndex((y) => compareFn(x, y)) === -1)); - return computed(() => symmetric ? [...toValue(diff1), ...toValue(diff2)] : toValue(diff1)); - } else { - return diff1; - } -} -function useArrayEvery(list, fn) { - return computed(() => toValue(list).every((element, index, array) => fn(toValue(element), index, array))); -} -function useArrayFilter(list, fn) { - return computed(() => toValue(list).map((i) => toValue(i)).filter(fn)); -} -function useArrayFind(list, fn) { - return computed(() => toValue( - toValue(list).find((element, index, array) => fn(toValue(element), index, array)) - )); -} -function useArrayFindIndex(list, fn) { - return computed(() => toValue(list).findIndex((element, index, array) => fn(toValue(element), index, array))); -} -function findLast(arr, cb) { - let index = arr.length; - while (index-- > 0) { - if (cb(arr[index], index, arr)) - return arr[index]; - } - return void 0; -} -function useArrayFindLast(list, fn) { - return computed(() => toValue( - !Array.prototype.findLast ? findLast(toValue(list), (element, index, array) => fn(toValue(element), index, array)) : toValue(list).findLast((element, index, array) => fn(toValue(element), index, array)) - )); -} -function isArrayIncludesOptions(obj) { - return isObject(obj) && containsProp(obj, "formIndex", "comparator"); -} -function useArrayIncludes(...args) { - var _a; - const list = args[0]; - const value = args[1]; - let comparator = args[2]; - let formIndex = 0; - if (isArrayIncludesOptions(comparator)) { - formIndex = (_a = comparator.fromIndex) != null ? _a : 0; - comparator = comparator.comparator; - } - if (typeof comparator === "string") { - const key = comparator; - comparator = (element, value2) => element[key] === toValue(value2); - } - comparator = comparator != null ? comparator : (element, value2) => element === toValue(value2); - return computed(() => toValue(list).slice(formIndex).some((element, index, array) => comparator( - toValue(element), - toValue(value), - index, - toValue(array) - ))); -} -function useArrayJoin(list, separator) { - return computed(() => toValue(list).map((i) => toValue(i)).join(toValue(separator))); -} -function useArrayMap(list, fn) { - return computed(() => toValue(list).map((i) => toValue(i)).map(fn)); -} -function useArrayReduce(list, reducer, ...args) { - const reduceCallback = (sum, value, index) => reducer(toValue(sum), toValue(value), index); - return computed(() => { - const resolved = toValue(list); - return args.length ? resolved.reduce(reduceCallback, typeof args[0] === "function" ? toValue(args[0]()) : toValue(args[0])) : resolved.reduce(reduceCallback); - }); -} -function useArraySome(list, fn) { - return computed(() => toValue(list).some((element, index, array) => fn(toValue(element), index, array))); -} -function uniq(array) { - return Array.from(new Set(array)); -} -function uniqueElementsBy(array, fn) { - return array.reduce((acc, v) => { - if (!acc.some((x) => fn(v, x, array))) - acc.push(v); - return acc; - }, []); -} -function useArrayUnique(list, compareFn) { - return computed(() => { - const resolvedList = toValue(list).map((element) => toValue(element)); - return compareFn ? uniqueElementsBy(resolvedList, compareFn) : uniq(resolvedList); - }); -} -function useCounter(initialValue = 0, options = {}) { - let _initialValue = unref(initialValue); - const count = shallowRef(initialValue); - const { - max = Number.POSITIVE_INFINITY, - min = Number.NEGATIVE_INFINITY - } = options; - const inc = (delta = 1) => count.value = Math.max(Math.min(max, count.value + delta), min); - const dec = (delta = 1) => count.value = Math.min(Math.max(min, count.value - delta), max); - const get2 = () => count.value; - const set2 = (val) => count.value = Math.max(min, Math.min(max, val)); - const reset = (val = _initialValue) => { - _initialValue = val; - return set2(val); - }; - return { count, inc, dec, get: get2, set: set2, reset }; -} -var REGEX_PARSE = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[T\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/i; -var REGEX_FORMAT = /[YMDHhms]o|\[([^\]]+)\]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a{1,2}|A{1,2}|m{1,2}|s{1,2}|Z{1,2}|z{1,4}|SSS/g; -function defaultMeridiem(hours, minutes, isLowercase, hasPeriod) { - let m = hours < 12 ? "AM" : "PM"; - if (hasPeriod) - m = m.split("").reduce((acc, curr) => acc += `${curr}.`, ""); - return isLowercase ? m.toLowerCase() : m; -} -function formatOrdinal(num) { - const suffixes = ["th", "st", "nd", "rd"]; - const v = num % 100; - return num + (suffixes[(v - 20) % 10] || suffixes[v] || suffixes[0]); -} -function formatDate(date, formatStr, options = {}) { - var _a; - const years = date.getFullYear(); - const month = date.getMonth(); - const days = date.getDate(); - const hours = date.getHours(); - const minutes = date.getMinutes(); - const seconds = date.getSeconds(); - const milliseconds = date.getMilliseconds(); - const day = date.getDay(); - const meridiem = (_a = options.customMeridiem) != null ? _a : defaultMeridiem; - const stripTimeZone = (dateString) => { - var _a2; - return (_a2 = dateString.split(" ")[1]) != null ? _a2 : ""; - }; - const matches = { - Yo: () => formatOrdinal(years), - YY: () => String(years).slice(-2), - YYYY: () => years, - M: () => month + 1, - Mo: () => formatOrdinal(month + 1), - MM: () => `${month + 1}`.padStart(2, "0"), - MMM: () => date.toLocaleDateString(toValue(options.locales), { month: "short" }), - MMMM: () => date.toLocaleDateString(toValue(options.locales), { month: "long" }), - D: () => String(days), - Do: () => formatOrdinal(days), - DD: () => `${days}`.padStart(2, "0"), - H: () => String(hours), - Ho: () => formatOrdinal(hours), - HH: () => `${hours}`.padStart(2, "0"), - h: () => `${hours % 12 || 12}`.padStart(1, "0"), - ho: () => formatOrdinal(hours % 12 || 12), - hh: () => `${hours % 12 || 12}`.padStart(2, "0"), - m: () => String(minutes), - mo: () => formatOrdinal(minutes), - mm: () => `${minutes}`.padStart(2, "0"), - s: () => String(seconds), - so: () => formatOrdinal(seconds), - ss: () => `${seconds}`.padStart(2, "0"), - SSS: () => `${milliseconds}`.padStart(3, "0"), - d: () => day, - dd: () => date.toLocaleDateString(toValue(options.locales), { weekday: "narrow" }), - ddd: () => date.toLocaleDateString(toValue(options.locales), { weekday: "short" }), - dddd: () => date.toLocaleDateString(toValue(options.locales), { weekday: "long" }), - A: () => meridiem(hours, minutes), - AA: () => meridiem(hours, minutes, false, true), - a: () => meridiem(hours, minutes, true), - aa: () => meridiem(hours, minutes, true, true), - z: () => stripTimeZone(date.toLocaleDateString(toValue(options.locales), { timeZoneName: "shortOffset" })), - zz: () => stripTimeZone(date.toLocaleDateString(toValue(options.locales), { timeZoneName: "shortOffset" })), - zzz: () => stripTimeZone(date.toLocaleDateString(toValue(options.locales), { timeZoneName: "shortOffset" })), - zzzz: () => stripTimeZone(date.toLocaleDateString(toValue(options.locales), { timeZoneName: "longOffset" })) - }; - return formatStr.replace(REGEX_FORMAT, (match, $1) => { - var _a2, _b; - return (_b = $1 != null ? $1 : (_a2 = matches[match]) == null ? void 0 : _a2.call(matches)) != null ? _b : match; - }); -} -function normalizeDate(date) { - if (date === null) - return new Date(Number.NaN); - if (date === void 0) - return /* @__PURE__ */ new Date(); - if (date instanceof Date) - return new Date(date); - if (typeof date === "string" && !/Z$/i.test(date)) { - const d = date.match(REGEX_PARSE); - if (d) { - const m = d[2] - 1 || 0; - const ms = (d[7] || "0").substring(0, 3); - return new Date(d[1], m, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, ms); - } - } - return new Date(date); -} -function useDateFormat(date, formatStr = "HH:mm:ss", options = {}) { - return computed(() => formatDate(normalizeDate(toValue(date)), toValue(formatStr), options)); -} -function useIntervalFn(cb, interval = 1e3, options = {}) { - const { - immediate = true, - immediateCallback = false - } = options; - let timer = null; - const isActive = shallowRef(false); - function clean() { - if (timer) { - clearInterval(timer); - timer = null; - } - } - function pause() { - isActive.value = false; - clean(); - } - function resume() { - const intervalValue = toValue(interval); - if (intervalValue <= 0) - return; - isActive.value = true; - if (immediateCallback) - cb(); - clean(); - if (isActive.value) - timer = setInterval(cb, intervalValue); - } - if (immediate && isClient) - resume(); - if (isRef(interval) || typeof interval === "function") { - const stopWatch = watch(interval, () => { - if (isActive.value && isClient) - resume(); - }); - tryOnScopeDispose(stopWatch); - } - tryOnScopeDispose(pause); - return { - isActive, - pause, - resume - }; -} -function useInterval(interval = 1e3, options = {}) { - const { - controls: exposeControls = false, - immediate = true, - callback - } = options; - const counter = shallowRef(0); - const update = () => counter.value += 1; - const reset = () => { - counter.value = 0; - }; - const controls = useIntervalFn( - callback ? () => { - update(); - callback(counter.value); - } : update, - interval, - { immediate } - ); - if (exposeControls) { - return { - counter, - reset, - ...controls - }; - } else { - return counter; - } -} -function useLastChanged(source, options = {}) { - var _a; - const ms = shallowRef((_a = options.initialValue) != null ? _a : null); - watch( - source, - () => ms.value = timestamp(), - options - ); - return ms; -} -function useTimeoutFn(cb, interval, options = {}) { - const { - immediate = true, - immediateCallback = false - } = options; - const isPending = shallowRef(false); - let timer = null; - function clear() { - if (timer) { - clearTimeout(timer); - timer = null; - } - } - function stop() { - isPending.value = false; - clear(); - } - function start(...args) { - if (immediateCallback) - cb(); - clear(); - isPending.value = true; - timer = setTimeout(() => { - isPending.value = false; - timer = null; - cb(...args); - }, toValue(interval)); - } - if (immediate) { - isPending.value = true; - if (isClient) - start(); - } - tryOnScopeDispose(stop); - return { - isPending: readonly(isPending), - start, - stop - }; -} -function useTimeout(interval = 1e3, options = {}) { - const { - controls: exposeControls = false, - callback - } = options; - const controls = useTimeoutFn( - callback != null ? callback : noop, - interval, - options - ); - const ready = computed(() => !controls.isPending.value); - if (exposeControls) { - return { - ready, - ...controls - }; - } else { - return ready; - } -} -function useToNumber(value, options = {}) { - const { - method = "parseFloat", - radix, - nanToZero - } = options; - return computed(() => { - let resolved = toValue(value); - if (typeof method === "function") - resolved = method(resolved); - else if (typeof resolved === "string") - resolved = Number[method](resolved, radix); - if (nanToZero && Number.isNaN(resolved)) - resolved = 0; - return resolved; - }); -} -function useToString(value) { - return computed(() => `${toValue(value)}`); -} -function useToggle(initialValue = false, options = {}) { - const { - truthyValue = true, - falsyValue = false - } = options; - const valueIsRef = isRef(initialValue); - const _value = shallowRef(initialValue); - function toggle(value) { - if (arguments.length) { - _value.value = value; - return _value.value; - } else { - const truthy = toValue(truthyValue); - _value.value = _value.value === truthy ? toValue(falsyValue) : truthy; - return _value.value; - } - } - if (valueIsRef) - return toggle; - else - return [_value, toggle]; -} -function watchArray(source, cb, options) { - let oldList = (options == null ? void 0 : options.immediate) ? [] : [...typeof source === "function" ? source() : Array.isArray(source) ? source : toValue(source)]; - return watch(source, (newList, _, onCleanup) => { - const oldListRemains = Array.from({ length: oldList.length }); - const added = []; - for (const obj of newList) { - let found = false; - for (let i = 0; i < oldList.length; i++) { - if (!oldListRemains[i] && obj === oldList[i]) { - oldListRemains[i] = true; - found = true; - break; - } - } - if (!found) - added.push(obj); - } - const removed = oldList.filter((_2, i) => !oldListRemains[i]); - cb(newList, oldList, added, removed, onCleanup); - oldList = [...newList]; - }, options); -} -function watchAtMost(source, cb, options) { - const { - count, - ...watchOptions - } = options; - const current = shallowRef(0); - const stop = watchWithFilter( - source, - (...args) => { - current.value += 1; - if (current.value >= toValue(count)) - nextTick(() => stop()); - cb(...args); - }, - watchOptions - ); - return { count: current, stop }; -} -function watchDebounced(source, cb, options = {}) { - const { - debounce = 0, - maxWait = void 0, - ...watchOptions - } = options; - return watchWithFilter( - source, - cb, - { - ...watchOptions, - eventFilter: debounceFilter(debounce, { maxWait }) - } - ); -} -function watchDeep(source, cb, options) { - return watch( - source, - cb, - { - ...options, - deep: true - } - ); -} -function watchIgnorable(source, cb, options = {}) { - const { - eventFilter = bypassFilter, - ...watchOptions - } = options; - const filteredCb = createFilterWrapper( - eventFilter, - cb - ); - let ignoreUpdates; - let ignorePrevAsyncUpdates; - let stop; - if (watchOptions.flush === "sync") { - const ignore = shallowRef(false); - ignorePrevAsyncUpdates = () => { - }; - ignoreUpdates = (updater) => { - ignore.value = true; - updater(); - ignore.value = false; - }; - stop = watch( - source, - (...args) => { - if (!ignore.value) - filteredCb(...args); - }, - watchOptions - ); - } else { - const disposables = []; - const ignoreCounter = shallowRef(0); - const syncCounter = shallowRef(0); - ignorePrevAsyncUpdates = () => { - ignoreCounter.value = syncCounter.value; - }; - disposables.push( - watch( - source, - () => { - syncCounter.value++; - }, - { ...watchOptions, flush: "sync" } - ) - ); - ignoreUpdates = (updater) => { - const syncCounterPrev = syncCounter.value; - updater(); - ignoreCounter.value += syncCounter.value - syncCounterPrev; - }; - disposables.push( - watch( - source, - (...args) => { - const ignore = ignoreCounter.value > 0 && ignoreCounter.value === syncCounter.value; - ignoreCounter.value = 0; - syncCounter.value = 0; - if (ignore) - return; - filteredCb(...args); - }, - watchOptions - ) - ); - stop = () => { - disposables.forEach((fn) => fn()); - }; - } - return { stop, ignoreUpdates, ignorePrevAsyncUpdates }; -} -function watchImmediate(source, cb, options) { - return watch( - source, - cb, - { - ...options, - immediate: true - } - ); -} -function watchOnce(source, cb, options) { - const stop = watch(source, (...args) => { - nextTick(() => stop()); - return cb(...args); - }, options); - return stop; -} -function watchThrottled(source, cb, options = {}) { - const { - throttle = 0, - trailing = true, - leading = true, - ...watchOptions - } = options; - return watchWithFilter( - source, - cb, - { - ...watchOptions, - eventFilter: throttleFilter(throttle, trailing, leading) - } - ); -} -function watchTriggerable(source, cb, options = {}) { - let cleanupFn; - function onEffect() { - if (!cleanupFn) - return; - const fn = cleanupFn; - cleanupFn = void 0; - fn(); - } - function onCleanup(callback) { - cleanupFn = callback; - } - const _cb = (value, oldValue) => { - onEffect(); - return cb(value, oldValue, onCleanup); - }; - const res = watchIgnorable(source, _cb, options); - const { ignoreUpdates } = res; - const trigger = () => { - let res2; - ignoreUpdates(() => { - res2 = _cb(getWatchSources(source), getOldValue(source)); - }); - return res2; - }; - return { - ...res, - trigger - }; -} -function getWatchSources(sources) { - if (isReactive(sources)) - return sources; - if (Array.isArray(sources)) - return sources.map((item) => toValue(item)); - return toValue(sources); -} -function getOldValue(source) { - return Array.isArray(source) ? source.map(() => void 0) : void 0; -} -function whenever(source, cb, options) { - const stop = watch( - source, - (v, ov, onInvalidate) => { - if (v) { - if (options == null ? void 0 : options.once) - nextTick(() => stop()); - cb(v, ov, onInvalidate); - } - }, - { - ...options, - once: false - } - ); - return stop; -} - -// node_modules/@vueuse/core/index.mjs -function computedAsync(evaluationCallback, initialState, optionsOrRef) { - let options; - if (isRef(optionsOrRef)) { - options = { - evaluating: optionsOrRef - }; - } else { - options = optionsOrRef || {}; - } - const { - lazy = false, - evaluating = void 0, - shallow = true, - onError = noop - } = options; - const started = shallowRef(!lazy); - const current = shallow ? shallowRef(initialState) : ref(initialState); - let counter = 0; - watchEffect(async (onInvalidate) => { - if (!started.value) - return; - counter++; - const counterAtBeginning = counter; - let hasFinished = false; - if (evaluating) { - Promise.resolve().then(() => { - evaluating.value = true; - }); - } - try { - const result = await evaluationCallback((cancelCallback) => { - onInvalidate(() => { - if (evaluating) - evaluating.value = false; - if (!hasFinished) - cancelCallback(); - }); - }); - if (counterAtBeginning === counter) - current.value = result; - } catch (e) { - onError(e); - } finally { - if (evaluating && counterAtBeginning === counter) - evaluating.value = false; - hasFinished = true; - } - }); - if (lazy) { - return computed(() => { - started.value = true; - return current.value; - }); - } else { - return current; - } -} -function computedInject(key, options, defaultSource, treatDefaultAsFactory) { - let source = inject(key); - if (defaultSource) - source = inject(key, defaultSource); - if (treatDefaultAsFactory) - source = inject(key, defaultSource, treatDefaultAsFactory); - if (typeof options === "function") { - return computed((ctx) => options(source, ctx)); - } else { - return computed({ - get: (ctx) => options.get(source, ctx), - set: options.set - }); - } -} -function createReusableTemplate(options = {}) { - const { - inheritAttrs = true - } = options; - const render = shallowRef(); - const define = defineComponent({ - setup(_, { slots }) { - return () => { - render.value = slots.default; - }; - } - }); - const reuse = defineComponent({ - inheritAttrs, - props: options.props, - setup(props, { attrs, slots }) { - return () => { - var _a; - if (!render.value && true) - throw new Error("[VueUse] Failed to find the definition of reusable template"); - const vnode = (_a = render.value) == null ? void 0 : _a.call(render, { - ...options.props == null ? keysToCamelKebabCase(attrs) : props, - $slots: slots - }); - return inheritAttrs && (vnode == null ? void 0 : vnode.length) === 1 ? vnode[0] : vnode; - }; - } - }); - return makeDestructurable( - { define, reuse }, - [define, reuse] - ); -} -function keysToCamelKebabCase(obj) { - const newObj = {}; - for (const key in obj) - newObj[camelize(key)] = obj[key]; - return newObj; -} -function createTemplatePromise(options = {}) { - let index = 0; - const instances = ref([]); - function create(...args) { - const props = shallowReactive({ - key: index++, - args, - promise: void 0, - resolve: () => { - }, - reject: () => { - }, - isResolving: false, - options - }); - instances.value.push(props); - props.promise = new Promise((_resolve, _reject) => { - props.resolve = (v) => { - props.isResolving = true; - return _resolve(v); - }; - props.reject = _reject; - }).finally(() => { - props.promise = void 0; - const index2 = instances.value.indexOf(props); - if (index2 !== -1) - instances.value.splice(index2, 1); - }); - return props.promise; - } - function start(...args) { - if (options.singleton && instances.value.length > 0) - return instances.value[0].promise; - return create(...args); - } - const component = defineComponent((_, { slots }) => { - const renderList = () => instances.value.map((props) => { - var _a; - return h(Fragment, { key: props.key }, (_a = slots.default) == null ? void 0 : _a.call(slots, props)); - }); - if (options.transition) - return () => h(TransitionGroup, options.transition, renderList); - return renderList; - }); - component.start = start; - return component; -} -function createUnrefFn(fn) { - return function(...args) { - return fn.apply(this, args.map((i) => toValue(i))); - }; -} -var defaultWindow = isClient ? window : void 0; -var defaultDocument = isClient ? window.document : void 0; -var defaultNavigator = isClient ? window.navigator : void 0; -var defaultLocation = isClient ? window.location : void 0; -function unrefElement(elRef) { - var _a; - const plain = toValue(elRef); - return (_a = plain == null ? void 0 : plain.$el) != null ? _a : plain; -} -function useEventListener(...args) { - const cleanups = []; - const cleanup = () => { - cleanups.forEach((fn) => fn()); - cleanups.length = 0; - }; - const register = (el, event, listener, options) => { - el.addEventListener(event, listener, options); - return () => el.removeEventListener(event, listener, options); - }; - const firstParamTargets = computed(() => { - const test = toArray(toValue(args[0])).filter((e) => e != null); - return test.every((e) => typeof e !== "string") ? test : void 0; - }); - const stopWatch = watchImmediate( - () => { - var _a, _b; - return [ - (_b = (_a = firstParamTargets.value) == null ? void 0 : _a.map((e) => unrefElement(e))) != null ? _b : [defaultWindow].filter((e) => e != null), - toArray(toValue(firstParamTargets.value ? args[1] : args[0])), - toArray(unref(firstParamTargets.value ? args[2] : args[1])), - // @ts-expect-error - TypeScript gets the correct types, but somehow still complains - toValue(firstParamTargets.value ? args[3] : args[2]) - ]; - }, - ([raw_targets, raw_events, raw_listeners, raw_options]) => { - cleanup(); - if (!(raw_targets == null ? void 0 : raw_targets.length) || !(raw_events == null ? void 0 : raw_events.length) || !(raw_listeners == null ? void 0 : raw_listeners.length)) - return; - const optionsClone = isObject(raw_options) ? { ...raw_options } : raw_options; - cleanups.push( - ...raw_targets.flatMap( - (el) => raw_events.flatMap( - (event) => raw_listeners.map((listener) => register(el, event, listener, optionsClone)) - ) - ) - ); - }, - { flush: "post" } - ); - const stop = () => { - stopWatch(); - cleanup(); - }; - tryOnScopeDispose(cleanup); - return stop; -} -var _iOSWorkaround = false; -function onClickOutside(target, handler, options = {}) { - const { window: window2 = defaultWindow, ignore = [], capture = true, detectIframe = false, controls = false } = options; - if (!window2) { - return controls ? { stop: noop, cancel: noop, trigger: noop } : noop; - } - if (isIOS && !_iOSWorkaround) { - _iOSWorkaround = true; - const listenerOptions = { passive: true }; - Array.from(window2.document.body.children).forEach((el) => useEventListener(el, "click", noop, listenerOptions)); - useEventListener(window2.document.documentElement, "click", noop, listenerOptions); - } - let shouldListen = true; - const shouldIgnore = (event) => { - return toValue(ignore).some((target2) => { - if (typeof target2 === "string") { - return Array.from(window2.document.querySelectorAll(target2)).some((el) => el === event.target || event.composedPath().includes(el)); - } else { - const el = unrefElement(target2); - return el && (event.target === el || event.composedPath().includes(el)); - } - }); - }; - function hasMultipleRoots(target2) { - const vm = toValue(target2); - return vm && vm.$.subTree.shapeFlag === 16; - } - function checkMultipleRoots(target2, event) { - const vm = toValue(target2); - const children = vm.$.subTree && vm.$.subTree.children; - if (children == null || !Array.isArray(children)) - return false; - return children.some((child) => child.el === event.target || event.composedPath().includes(child.el)); - } - const listener = (event) => { - const el = unrefElement(target); - if (event.target == null) - return; - if (!(el instanceof Element) && hasMultipleRoots(target) && checkMultipleRoots(target, event)) - return; - if (!el || el === event.target || event.composedPath().includes(el)) - return; - if ("detail" in event && event.detail === 0) - shouldListen = !shouldIgnore(event); - if (!shouldListen) { - shouldListen = true; - return; - } - handler(event); - }; - let isProcessingClick = false; - const cleanup = [ - useEventListener(window2, "click", (event) => { - if (!isProcessingClick) { - isProcessingClick = true; - setTimeout(() => { - isProcessingClick = false; - }, 0); - listener(event); - } - }, { passive: true, capture }), - useEventListener(window2, "pointerdown", (e) => { - const el = unrefElement(target); - shouldListen = !shouldIgnore(e) && !!(el && !e.composedPath().includes(el)); - }, { passive: true }), - detectIframe && useEventListener(window2, "blur", (event) => { - setTimeout(() => { - var _a; - const el = unrefElement(target); - if (((_a = window2.document.activeElement) == null ? void 0 : _a.tagName) === "IFRAME" && !(el == null ? void 0 : el.contains(window2.document.activeElement))) { - handler(event); - } - }, 0); - }, { passive: true }) - ].filter(Boolean); - const stop = () => cleanup.forEach((fn) => fn()); - if (controls) { - return { - stop, - cancel: () => { - shouldListen = false; - }, - trigger: (event) => { - shouldListen = true; - listener(event); - shouldListen = false; - } - }; - } - return stop; -} -function useMounted() { - const isMounted = shallowRef(false); - const instance = getCurrentInstance(); - if (instance) { - onMounted(() => { - isMounted.value = true; - }, instance); - } - return isMounted; -} -function useSupported(callback) { - const isMounted = useMounted(); - return computed(() => { - isMounted.value; - return Boolean(callback()); - }); -} -function useMutationObserver(target, callback, options = {}) { - const { window: window2 = defaultWindow, ...mutationOptions } = options; - let observer; - const isSupported = useSupported(() => window2 && "MutationObserver" in window2); - const cleanup = () => { - if (observer) { - observer.disconnect(); - observer = void 0; - } - }; - const targets = computed(() => { - const value = toValue(target); - const items = toArray(value).map(unrefElement).filter(notNullish); - return new Set(items); - }); - const stopWatch = watch( - () => targets.value, - (targets2) => { - cleanup(); - if (isSupported.value && targets2.size) { - observer = new MutationObserver(callback); - targets2.forEach((el) => observer.observe(el, mutationOptions)); - } - }, - { immediate: true, flush: "post" } - ); - const takeRecords = () => { - return observer == null ? void 0 : observer.takeRecords(); - }; - const stop = () => { - stopWatch(); - cleanup(); - }; - tryOnScopeDispose(stop); - return { - isSupported, - stop, - takeRecords - }; -} -function onElementRemoval(target, callback, options = {}) { - const { - window: window2 = defaultWindow, - document: document2 = window2 == null ? void 0 : window2.document, - flush = "sync" - } = options; - if (!window2 || !document2) - return noop; - let stopFn; - const cleanupAndUpdate = (fn) => { - stopFn == null ? void 0 : stopFn(); - stopFn = fn; - }; - const stopWatch = watchEffect(() => { - const el = unrefElement(target); - if (el) { - const { stop } = useMutationObserver( - document2, - (mutationsList) => { - const targetRemoved = mutationsList.map((mutation) => [...mutation.removedNodes]).flat().some((node) => node === el || node.contains(el)); - if (targetRemoved) { - callback(mutationsList); - } - }, - { - window: window2, - childList: true, - subtree: true - } - ); - cleanupAndUpdate(stop); - } - }, { flush }); - const stopHandle = () => { - stopWatch(); - cleanupAndUpdate(); - }; - tryOnScopeDispose(stopHandle); - return stopHandle; -} -function createKeyPredicate(keyFilter) { - if (typeof keyFilter === "function") - return keyFilter; - else if (typeof keyFilter === "string") - return (event) => event.key === keyFilter; - else if (Array.isArray(keyFilter)) - return (event) => keyFilter.includes(event.key); - return () => true; -} -function onKeyStroke(...args) { - let key; - let handler; - let options = {}; - if (args.length === 3) { - key = args[0]; - handler = args[1]; - options = args[2]; - } else if (args.length === 2) { - if (typeof args[1] === "object") { - key = true; - handler = args[0]; - options = args[1]; - } else { - key = args[0]; - handler = args[1]; - } - } else { - key = true; - handler = args[0]; - } - const { - target = defaultWindow, - eventName = "keydown", - passive = false, - dedupe = false - } = options; - const predicate = createKeyPredicate(key); - const listener = (e) => { - if (e.repeat && toValue(dedupe)) - return; - if (predicate(e)) - handler(e); - }; - return useEventListener(target, eventName, listener, passive); -} -function onKeyDown(key, handler, options = {}) { - return onKeyStroke(key, handler, { ...options, eventName: "keydown" }); -} -function onKeyPressed(key, handler, options = {}) { - return onKeyStroke(key, handler, { ...options, eventName: "keypress" }); -} -function onKeyUp(key, handler, options = {}) { - return onKeyStroke(key, handler, { ...options, eventName: "keyup" }); -} -var DEFAULT_DELAY = 500; -var DEFAULT_THRESHOLD = 10; -function onLongPress(target, handler, options) { - var _a, _b; - const elementRef = computed(() => unrefElement(target)); - let timeout; - let posStart; - let startTimestamp; - let hasLongPressed = false; - function clear() { - if (timeout) { - clearTimeout(timeout); - timeout = void 0; - } - posStart = void 0; - startTimestamp = void 0; - hasLongPressed = false; - } - function onRelease(ev) { - var _a2, _b2, _c; - const [_startTimestamp, _posStart, _hasLongPressed] = [startTimestamp, posStart, hasLongPressed]; - clear(); - if (!(options == null ? void 0 : options.onMouseUp) || !_posStart || !_startTimestamp) - return; - if (((_a2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _a2.self) && ev.target !== elementRef.value) - return; - if ((_b2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _b2.prevent) - ev.preventDefault(); - if ((_c = options == null ? void 0 : options.modifiers) == null ? void 0 : _c.stop) - ev.stopPropagation(); - const dx = ev.x - _posStart.x; - const dy = ev.y - _posStart.y; - const distance = Math.sqrt(dx * dx + dy * dy); - options.onMouseUp(ev.timeStamp - _startTimestamp, distance, _hasLongPressed); - } - function onDown(ev) { - var _a2, _b2, _c, _d; - if (((_a2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _a2.self) && ev.target !== elementRef.value) - return; - clear(); - if ((_b2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _b2.prevent) - ev.preventDefault(); - if ((_c = options == null ? void 0 : options.modifiers) == null ? void 0 : _c.stop) - ev.stopPropagation(); - posStart = { - x: ev.x, - y: ev.y - }; - startTimestamp = ev.timeStamp; - timeout = setTimeout( - () => { - hasLongPressed = true; - handler(ev); - }, - (_d = options == null ? void 0 : options.delay) != null ? _d : DEFAULT_DELAY - ); - } - function onMove(ev) { - var _a2, _b2, _c, _d; - if (((_a2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _a2.self) && ev.target !== elementRef.value) - return; - if (!posStart || (options == null ? void 0 : options.distanceThreshold) === false) - return; - if ((_b2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _b2.prevent) - ev.preventDefault(); - if ((_c = options == null ? void 0 : options.modifiers) == null ? void 0 : _c.stop) - ev.stopPropagation(); - const dx = ev.x - posStart.x; - const dy = ev.y - posStart.y; - const distance = Math.sqrt(dx * dx + dy * dy); - if (distance >= ((_d = options == null ? void 0 : options.distanceThreshold) != null ? _d : DEFAULT_THRESHOLD)) - clear(); - } - const listenerOptions = { - capture: (_a = options == null ? void 0 : options.modifiers) == null ? void 0 : _a.capture, - once: (_b = options == null ? void 0 : options.modifiers) == null ? void 0 : _b.once - }; - const cleanup = [ - useEventListener(elementRef, "pointerdown", onDown, listenerOptions), - useEventListener(elementRef, "pointermove", onMove, listenerOptions), - useEventListener(elementRef, ["pointerup", "pointerleave"], onRelease, listenerOptions) - ]; - const stop = () => cleanup.forEach((fn) => fn()); - return stop; -} -function isFocusedElementEditable() { - const { activeElement, body } = document; - if (!activeElement) - return false; - if (activeElement === body) - return false; - switch (activeElement.tagName) { - case "INPUT": - case "TEXTAREA": - return true; - } - return activeElement.hasAttribute("contenteditable"); -} -function isTypedCharValid({ - keyCode, - metaKey, - ctrlKey, - altKey -}) { - if (metaKey || ctrlKey || altKey) - return false; - if (keyCode >= 48 && keyCode <= 57 || keyCode >= 96 && keyCode <= 105) - return true; - if (keyCode >= 65 && keyCode <= 90) - return true; - return false; -} -function onStartTyping(callback, options = {}) { - const { document: document2 = defaultDocument } = options; - const keydown = (event) => { - if (!isFocusedElementEditable() && isTypedCharValid(event)) { - callback(event); - } - }; - if (document2) - useEventListener(document2, "keydown", keydown, { passive: true }); -} -function templateRef(key, initialValue = null) { - const instance = getCurrentInstance(); - let _trigger = () => { - }; - const element = customRef((track, trigger) => { - _trigger = trigger; - return { - get() { - var _a, _b; - track(); - return (_b = (_a = instance == null ? void 0 : instance.proxy) == null ? void 0 : _a.$refs[key]) != null ? _b : initialValue; - }, - set() { - } - }; - }); - tryOnMounted(_trigger); - onUpdated(_trigger); - return element; -} -function useActiveElement(options = {}) { - var _a; - const { - window: window2 = defaultWindow, - deep = true, - triggerOnRemoval = false - } = options; - const document2 = (_a = options.document) != null ? _a : window2 == null ? void 0 : window2.document; - const getDeepActiveElement = () => { - var _a2; - let element = document2 == null ? void 0 : document2.activeElement; - if (deep) { - while (element == null ? void 0 : element.shadowRoot) - element = (_a2 = element == null ? void 0 : element.shadowRoot) == null ? void 0 : _a2.activeElement; - } - return element; - }; - const activeElement = shallowRef(); - const trigger = () => { - activeElement.value = getDeepActiveElement(); - }; - if (window2) { - const listenerOptions = { - capture: true, - passive: true - }; - useEventListener( - window2, - "blur", - (event) => { - if (event.relatedTarget !== null) - return; - trigger(); - }, - listenerOptions - ); - useEventListener( - window2, - "focus", - trigger, - listenerOptions - ); - } - if (triggerOnRemoval) { - onElementRemoval(activeElement, trigger, { document: document2 }); - } - trigger(); - return activeElement; -} -function useRafFn(fn, options = {}) { - const { - immediate = true, - fpsLimit = void 0, - window: window2 = defaultWindow, - once = false - } = options; - const isActive = shallowRef(false); - const intervalLimit = computed(() => { - return fpsLimit ? 1e3 / toValue(fpsLimit) : null; - }); - let previousFrameTimestamp = 0; - let rafId = null; - function loop(timestamp2) { - if (!isActive.value || !window2) - return; - if (!previousFrameTimestamp) - previousFrameTimestamp = timestamp2; - const delta = timestamp2 - previousFrameTimestamp; - if (intervalLimit.value && delta < intervalLimit.value) { - rafId = window2.requestAnimationFrame(loop); - return; - } - previousFrameTimestamp = timestamp2; - fn({ delta, timestamp: timestamp2 }); - if (once) { - isActive.value = false; - rafId = null; - return; - } - rafId = window2.requestAnimationFrame(loop); - } - function resume() { - if (!isActive.value && window2) { - isActive.value = true; - previousFrameTimestamp = 0; - rafId = window2.requestAnimationFrame(loop); - } - } - function pause() { - isActive.value = false; - if (rafId != null && window2) { - window2.cancelAnimationFrame(rafId); - rafId = null; - } - } - if (immediate) - resume(); - tryOnScopeDispose(pause); - return { - isActive: readonly(isActive), - pause, - resume - }; -} -function useAnimate(target, keyframes, options) { - let config; - let animateOptions; - if (isObject(options)) { - config = options; - animateOptions = objectOmit(options, ["window", "immediate", "commitStyles", "persist", "onReady", "onError"]); - } else { - config = { duration: options }; - animateOptions = options; - } - const { - window: window2 = defaultWindow, - immediate = true, - commitStyles, - persist, - playbackRate: _playbackRate = 1, - onReady, - onError = (e) => { - console.error(e); - } - } = config; - const isSupported = useSupported(() => window2 && HTMLElement && "animate" in HTMLElement.prototype); - const animate = shallowRef(void 0); - const store = shallowReactive({ - startTime: null, - currentTime: null, - timeline: null, - playbackRate: _playbackRate, - pending: false, - playState: immediate ? "idle" : "paused", - replaceState: "active" - }); - const pending = computed(() => store.pending); - const playState = computed(() => store.playState); - const replaceState = computed(() => store.replaceState); - const startTime = computed({ - get() { - return store.startTime; - }, - set(value) { - store.startTime = value; - if (animate.value) - animate.value.startTime = value; - } - }); - const currentTime = computed({ - get() { - return store.currentTime; - }, - set(value) { - store.currentTime = value; - if (animate.value) { - animate.value.currentTime = value; - syncResume(); - } - } - }); - const timeline = computed({ - get() { - return store.timeline; - }, - set(value) { - store.timeline = value; - if (animate.value) - animate.value.timeline = value; - } - }); - const playbackRate = computed({ - get() { - return store.playbackRate; - }, - set(value) { - store.playbackRate = value; - if (animate.value) - animate.value.playbackRate = value; - } - }); - const play = () => { - if (animate.value) { - try { - animate.value.play(); - syncResume(); - } catch (e) { - syncPause(); - onError(e); - } - } else { - update(); - } - }; - const pause = () => { - var _a; - try { - (_a = animate.value) == null ? void 0 : _a.pause(); - syncPause(); - } catch (e) { - onError(e); - } - }; - const reverse = () => { - var _a; - if (!animate.value) - update(); - try { - (_a = animate.value) == null ? void 0 : _a.reverse(); - syncResume(); - } catch (e) { - syncPause(); - onError(e); - } - }; - const finish = () => { - var _a; - try { - (_a = animate.value) == null ? void 0 : _a.finish(); - syncPause(); - } catch (e) { - onError(e); - } - }; - const cancel = () => { - var _a; - try { - (_a = animate.value) == null ? void 0 : _a.cancel(); - syncPause(); - } catch (e) { - onError(e); - } - }; - watch(() => unrefElement(target), (el) => { - if (el) { - update(); - } else { - animate.value = void 0; - } - }); - watch(() => keyframes, (value) => { - if (animate.value) { - update(); - const targetEl = unrefElement(target); - if (targetEl) { - animate.value.effect = new KeyframeEffect( - targetEl, - toValue(value), - animateOptions - ); - } - } - }, { deep: true }); - tryOnMounted(() => update(true), false); - tryOnScopeDispose(cancel); - function update(init) { - const el = unrefElement(target); - if (!isSupported.value || !el) - return; - if (!animate.value) - animate.value = el.animate(toValue(keyframes), animateOptions); - if (persist) - animate.value.persist(); - if (_playbackRate !== 1) - animate.value.playbackRate = _playbackRate; - if (init && !immediate) - animate.value.pause(); - else - syncResume(); - onReady == null ? void 0 : onReady(animate.value); - } - const listenerOptions = { passive: true }; - useEventListener(animate, ["cancel", "finish", "remove"], syncPause, listenerOptions); - useEventListener(animate, "finish", () => { - var _a; - if (commitStyles) - (_a = animate.value) == null ? void 0 : _a.commitStyles(); - }, listenerOptions); - const { resume: resumeRef, pause: pauseRef } = useRafFn(() => { - if (!animate.value) - return; - store.pending = animate.value.pending; - store.playState = animate.value.playState; - store.replaceState = animate.value.replaceState; - store.startTime = animate.value.startTime; - store.currentTime = animate.value.currentTime; - store.timeline = animate.value.timeline; - store.playbackRate = animate.value.playbackRate; - }, { immediate: false }); - function syncResume() { - if (isSupported.value) - resumeRef(); - } - function syncPause() { - if (isSupported.value && window2) - window2.requestAnimationFrame(pauseRef); - } - return { - isSupported, - animate, - // actions - play, - pause, - reverse, - finish, - cancel, - // state - pending, - playState, - replaceState, - startTime, - currentTime, - timeline, - playbackRate - }; -} -function useAsyncQueue(tasks, options) { - const { - interrupt = true, - onError = noop, - onFinished = noop, - signal - } = options || {}; - const promiseState = { - aborted: "aborted", - fulfilled: "fulfilled", - pending: "pending", - rejected: "rejected" - }; - const initialResult = Array.from(Array.from({ length: tasks.length }), () => ({ state: promiseState.pending, data: null })); - const result = reactive(initialResult); - const activeIndex = shallowRef(-1); - if (!tasks || tasks.length === 0) { - onFinished(); - return { - activeIndex, - result - }; - } - function updateResult(state, res) { - activeIndex.value++; - result[activeIndex.value].data = res; - result[activeIndex.value].state = state; - } - tasks.reduce((prev, curr) => { - return prev.then((prevRes) => { - var _a; - if (signal == null ? void 0 : signal.aborted) { - updateResult(promiseState.aborted, new Error("aborted")); - return; - } - if (((_a = result[activeIndex.value]) == null ? void 0 : _a.state) === promiseState.rejected && interrupt) { - onFinished(); - return; - } - const done = curr(prevRes).then((currentRes) => { - updateResult(promiseState.fulfilled, currentRes); - if (activeIndex.value === tasks.length - 1) - onFinished(); - return currentRes; - }); - if (!signal) - return done; - return Promise.race([done, whenAborted(signal)]); - }).catch((e) => { - if (signal == null ? void 0 : signal.aborted) { - updateResult(promiseState.aborted, e); - return e; - } - updateResult(promiseState.rejected, e); - onError(); - return e; - }); - }, Promise.resolve()); - return { - activeIndex, - result - }; -} -function whenAborted(signal) { - return new Promise((resolve, reject) => { - const error = new Error("aborted"); - if (signal.aborted) - reject(error); - else - signal.addEventListener("abort", () => reject(error), { once: true }); - }); -} -function useAsyncState(promise, initialState, options) { - const { - immediate = true, - delay = 0, - onError = noop, - onSuccess = noop, - resetOnExecute = true, - shallow = true, - throwError - } = options != null ? options : {}; - const state = shallow ? shallowRef(initialState) : ref(initialState); - const isReady = shallowRef(false); - const isLoading = shallowRef(false); - const error = shallowRef(void 0); - async function execute(delay2 = 0, ...args) { - if (resetOnExecute) - state.value = initialState; - error.value = void 0; - isReady.value = false; - isLoading.value = true; - if (delay2 > 0) - await promiseTimeout(delay2); - const _promise = typeof promise === "function" ? promise(...args) : promise; - try { - const data = await _promise; - state.value = data; - isReady.value = true; - onSuccess(data); - } catch (e) { - error.value = e; - onError(e); - if (throwError) - throw e; - } finally { - isLoading.value = false; - } - return state.value; - } - if (immediate) { - execute(delay); - } - const shell = { - state, - isReady, - isLoading, - error, - execute - }; - function waitUntilIsLoaded() { - return new Promise((resolve, reject) => { - until(isLoading).toBe(false).then(() => resolve(shell)).catch(reject); - }); - } - return { - ...shell, - then(onFulfilled, onRejected) { - return waitUntilIsLoaded().then(onFulfilled, onRejected); - } - }; -} -var defaults = { - array: (v) => JSON.stringify(v), - object: (v) => JSON.stringify(v), - set: (v) => JSON.stringify(Array.from(v)), - map: (v) => JSON.stringify(Object.fromEntries(v)), - null: () => "" -}; -function getDefaultSerialization(target) { - if (!target) - return defaults.null; - if (target instanceof Map) - return defaults.map; - else if (target instanceof Set) - return defaults.set; - else if (Array.isArray(target)) - return defaults.array; - else - return defaults.object; -} -function useBase64(target, options) { - const base64 = shallowRef(""); - const promise = shallowRef(); - function execute() { - if (!isClient) - return; - promise.value = new Promise((resolve, reject) => { - try { - const _target = toValue(target); - if (_target == null) { - resolve(""); - } else if (typeof _target === "string") { - resolve(blobToBase64(new Blob([_target], { type: "text/plain" }))); - } else if (_target instanceof Blob) { - resolve(blobToBase64(_target)); - } else if (_target instanceof ArrayBuffer) { - resolve(window.btoa(String.fromCharCode(...new Uint8Array(_target)))); - } else if (_target instanceof HTMLCanvasElement) { - resolve(_target.toDataURL(options == null ? void 0 : options.type, options == null ? void 0 : options.quality)); - } else if (_target instanceof HTMLImageElement) { - const img = _target.cloneNode(false); - img.crossOrigin = "Anonymous"; - imgLoaded(img).then(() => { - const canvas = document.createElement("canvas"); - const ctx = canvas.getContext("2d"); - canvas.width = img.width; - canvas.height = img.height; - ctx.drawImage(img, 0, 0, canvas.width, canvas.height); - resolve(canvas.toDataURL(options == null ? void 0 : options.type, options == null ? void 0 : options.quality)); - }).catch(reject); - } else if (typeof _target === "object") { - const _serializeFn = (options == null ? void 0 : options.serializer) || getDefaultSerialization(_target); - const serialized = _serializeFn(_target); - return resolve(blobToBase64(new Blob([serialized], { type: "application/json" }))); - } else { - reject(new Error("target is unsupported types")); - } - } catch (error) { - reject(error); - } - }); - promise.value.then((res) => { - base64.value = (options == null ? void 0 : options.dataUrl) === false ? res.replace(/^data:.*?;base64,/, "") : res; - }); - return promise.value; - } - if (isRef(target) || typeof target === "function") - watch(target, execute, { immediate: true }); - else - execute(); - return { - base64, - promise, - execute - }; -} -function imgLoaded(img) { - return new Promise((resolve, reject) => { - if (!img.complete) { - img.onload = () => { - resolve(); - }; - img.onerror = reject; - } else { - resolve(); - } - }); -} -function blobToBase64(blob) { - return new Promise((resolve, reject) => { - const fr = new FileReader(); - fr.onload = (e) => { - resolve(e.target.result); - }; - fr.onerror = reject; - fr.readAsDataURL(blob); - }); -} -function useBattery(options = {}) { - const { navigator: navigator2 = defaultNavigator } = options; - const events2 = ["chargingchange", "chargingtimechange", "dischargingtimechange", "levelchange"]; - const isSupported = useSupported(() => navigator2 && "getBattery" in navigator2 && typeof navigator2.getBattery === "function"); - const charging = shallowRef(false); - const chargingTime = shallowRef(0); - const dischargingTime = shallowRef(0); - const level = shallowRef(1); - let battery; - function updateBatteryInfo() { - charging.value = this.charging; - chargingTime.value = this.chargingTime || 0; - dischargingTime.value = this.dischargingTime || 0; - level.value = this.level; - } - if (isSupported.value) { - navigator2.getBattery().then((_battery) => { - battery = _battery; - updateBatteryInfo.call(battery); - useEventListener(battery, events2, updateBatteryInfo, { passive: true }); - }); - } - return { - isSupported, - charging, - chargingTime, - dischargingTime, - level - }; -} -function useBluetooth(options) { - let { - acceptAllDevices = false - } = options || {}; - const { - filters = void 0, - optionalServices = void 0, - navigator: navigator2 = defaultNavigator - } = options || {}; - const isSupported = useSupported(() => navigator2 && "bluetooth" in navigator2); - const device = shallowRef(); - const error = shallowRef(null); - watch(device, () => { - connectToBluetoothGATTServer(); - }); - async function requestDevice() { - if (!isSupported.value) - return; - error.value = null; - if (filters && filters.length > 0) - acceptAllDevices = false; - try { - device.value = await (navigator2 == null ? void 0 : navigator2.bluetooth.requestDevice({ - acceptAllDevices, - filters, - optionalServices - })); - } catch (err) { - error.value = err; - } - } - const server = shallowRef(); - const isConnected = shallowRef(false); - function reset() { - isConnected.value = false; - device.value = void 0; - server.value = void 0; - } - async function connectToBluetoothGATTServer() { - error.value = null; - if (device.value && device.value.gatt) { - useEventListener(device, "gattserverdisconnected", reset, { passive: true }); - try { - server.value = await device.value.gatt.connect(); - isConnected.value = server.value.connected; - } catch (err) { - error.value = err; - } - } - } - tryOnMounted(() => { - var _a; - if (device.value) - (_a = device.value.gatt) == null ? void 0 : _a.connect(); - }); - tryOnScopeDispose(() => { - var _a; - if (device.value) - (_a = device.value.gatt) == null ? void 0 : _a.disconnect(); - }); - return { - isSupported, - isConnected: readonly(isConnected), - // Device: - device, - requestDevice, - // Server: - server, - // Errors: - error - }; -} -var ssrWidthSymbol = Symbol("vueuse-ssr-width"); -function useSSRWidth() { - const ssrWidth = hasInjectionContext() ? injectLocal(ssrWidthSymbol, null) : null; - return typeof ssrWidth === "number" ? ssrWidth : void 0; -} -function provideSSRWidth(width, app) { - if (app !== void 0) { - app.provide(ssrWidthSymbol, width); - } else { - provideLocal(ssrWidthSymbol, width); - } -} -function useMediaQuery(query, options = {}) { - const { window: window2 = defaultWindow, ssrWidth = useSSRWidth() } = options; - const isSupported = useSupported(() => window2 && "matchMedia" in window2 && typeof window2.matchMedia === "function"); - const ssrSupport = shallowRef(typeof ssrWidth === "number"); - const mediaQuery = shallowRef(); - const matches = shallowRef(false); - const handler = (event) => { - matches.value = event.matches; - }; - watchEffect(() => { - if (ssrSupport.value) { - ssrSupport.value = !isSupported.value; - const queryStrings = toValue(query).split(","); - matches.value = queryStrings.some((queryString) => { - const not = queryString.includes("not all"); - const minWidth = queryString.match(/\(\s*min-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/); - const maxWidth = queryString.match(/\(\s*max-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/); - let res = Boolean(minWidth || maxWidth); - if (minWidth && res) { - res = ssrWidth >= pxValue(minWidth[1]); - } - if (maxWidth && res) { - res = ssrWidth <= pxValue(maxWidth[1]); - } - return not ? !res : res; - }); - return; - } - if (!isSupported.value) - return; - mediaQuery.value = window2.matchMedia(toValue(query)); - matches.value = mediaQuery.value.matches; - }); - useEventListener(mediaQuery, "change", handler, { passive: true }); - return computed(() => matches.value); -} -var breakpointsTailwind = { - "sm": 640, - "md": 768, - "lg": 1024, - "xl": 1280, - "2xl": 1536 -}; -var breakpointsBootstrapV5 = { - xs: 0, - sm: 576, - md: 768, - lg: 992, - xl: 1200, - xxl: 1400 -}; -var breakpointsVuetifyV2 = { - xs: 0, - sm: 600, - md: 960, - lg: 1264, - xl: 1904 -}; -var breakpointsVuetifyV3 = { - xs: 0, - sm: 600, - md: 960, - lg: 1280, - xl: 1920, - xxl: 2560 -}; -var breakpointsVuetify = breakpointsVuetifyV2; -var breakpointsAntDesign = { - xs: 480, - sm: 576, - md: 768, - lg: 992, - xl: 1200, - xxl: 1600 -}; -var breakpointsQuasar = { - xs: 0, - sm: 600, - md: 1024, - lg: 1440, - xl: 1920 -}; -var breakpointsSematic = { - mobileS: 320, - mobileM: 375, - mobileL: 425, - tablet: 768, - laptop: 1024, - laptopL: 1440, - desktop4K: 2560 -}; -var breakpointsMasterCss = { - "3xs": 360, - "2xs": 480, - "xs": 600, - "sm": 768, - "md": 1024, - "lg": 1280, - "xl": 1440, - "2xl": 1600, - "3xl": 1920, - "4xl": 2560 -}; -var breakpointsPrimeFlex = { - sm: 576, - md: 768, - lg: 992, - xl: 1200 -}; -var breakpointsElement = { - xs: 0, - sm: 768, - md: 992, - lg: 1200, - xl: 1920 -}; -function useBreakpoints(breakpoints, options = {}) { - function getValue2(k, delta) { - let v = toValue(breakpoints[toValue(k)]); - if (delta != null) - v = increaseWithUnit(v, delta); - if (typeof v === "number") - v = `${v}px`; - return v; - } - const { window: window2 = defaultWindow, strategy = "min-width", ssrWidth = useSSRWidth() } = options; - const ssrSupport = typeof ssrWidth === "number"; - const mounted = ssrSupport ? shallowRef(false) : { value: true }; - if (ssrSupport) { - tryOnMounted(() => mounted.value = !!window2); - } - function match(query, size) { - if (!mounted.value && ssrSupport) { - return query === "min" ? ssrWidth >= pxValue(size) : ssrWidth <= pxValue(size); - } - if (!window2) - return false; - return window2.matchMedia(`(${query}-width: ${size})`).matches; - } - const greaterOrEqual = (k) => { - return useMediaQuery(() => `(min-width: ${getValue2(k)})`, options); - }; - const smallerOrEqual = (k) => { - return useMediaQuery(() => `(max-width: ${getValue2(k)})`, options); - }; - const shortcutMethods = Object.keys(breakpoints).reduce((shortcuts, k) => { - Object.defineProperty(shortcuts, k, { - get: () => strategy === "min-width" ? greaterOrEqual(k) : smallerOrEqual(k), - enumerable: true, - configurable: true - }); - return shortcuts; - }, {}); - function current() { - const points = Object.keys(breakpoints).map((k) => [k, shortcutMethods[k], pxValue(getValue2(k))]).sort((a, b) => a[2] - b[2]); - return computed(() => points.filter(([, v]) => v.value).map(([k]) => k)); - } - return Object.assign(shortcutMethods, { - greaterOrEqual, - smallerOrEqual, - greater(k) { - return useMediaQuery(() => `(min-width: ${getValue2(k, 0.1)})`, options); - }, - smaller(k) { - return useMediaQuery(() => `(max-width: ${getValue2(k, -0.1)})`, options); - }, - between(a, b) { - return useMediaQuery(() => `(min-width: ${getValue2(a)}) and (max-width: ${getValue2(b, -0.1)})`, options); - }, - isGreater(k) { - return match("min", getValue2(k, 0.1)); - }, - isGreaterOrEqual(k) { - return match("min", getValue2(k)); - }, - isSmaller(k) { - return match("max", getValue2(k, -0.1)); - }, - isSmallerOrEqual(k) { - return match("max", getValue2(k)); - }, - isInBetween(a, b) { - return match("min", getValue2(a)) && match("max", getValue2(b, -0.1)); - }, - current, - active() { - const bps = current(); - return computed(() => bps.value.length === 0 ? "" : bps.value.at(strategy === "min-width" ? -1 : 0)); - } - }); -} -function useBroadcastChannel(options) { - const { - name, - window: window2 = defaultWindow - } = options; - const isSupported = useSupported(() => window2 && "BroadcastChannel" in window2); - const isClosed = shallowRef(false); - const channel = ref(); - const data = ref(); - const error = shallowRef(null); - const post = (data2) => { - if (channel.value) - channel.value.postMessage(data2); - }; - const close = () => { - if (channel.value) - channel.value.close(); - isClosed.value = true; - }; - if (isSupported.value) { - tryOnMounted(() => { - error.value = null; - channel.value = new BroadcastChannel(name); - const listenerOptions = { - passive: true - }; - useEventListener(channel, "message", (e) => { - data.value = e.data; - }, listenerOptions); - useEventListener(channel, "messageerror", (e) => { - error.value = e; - }, listenerOptions); - useEventListener(channel, "close", () => { - isClosed.value = true; - }, listenerOptions); - }); - } - tryOnScopeDispose(() => { - close(); - }); - return { - isSupported, - channel, - data, - post, - close, - error, - isClosed - }; -} -var WRITABLE_PROPERTIES = [ - "hash", - "host", - "hostname", - "href", - "pathname", - "port", - "protocol", - "search" -]; -function useBrowserLocation(options = {}) { - const { window: window2 = defaultWindow } = options; - const refs = Object.fromEntries( - WRITABLE_PROPERTIES.map((key) => [key, ref()]) - ); - for (const [key, ref2] of objectEntries(refs)) { - watch(ref2, (value) => { - if (!(window2 == null ? void 0 : window2.location) || window2.location[key] === value) - return; - window2.location[key] = value; - }); - } - const buildState = (trigger) => { - var _a; - const { state: state2, length } = (window2 == null ? void 0 : window2.history) || {}; - const { origin } = (window2 == null ? void 0 : window2.location) || {}; - for (const key of WRITABLE_PROPERTIES) - refs[key].value = (_a = window2 == null ? void 0 : window2.location) == null ? void 0 : _a[key]; - return reactive({ - trigger, - state: state2, - length, - origin, - ...refs - }); - }; - const state = ref(buildState("load")); - if (window2) { - const listenerOptions = { passive: true }; - useEventListener(window2, "popstate", () => state.value = buildState("popstate"), listenerOptions); - useEventListener(window2, "hashchange", () => state.value = buildState("hashchange"), listenerOptions); - } - return state; -} -function useCached(refValue, comparator = (a, b) => a === b, options) { - const { deepRefs = true, ...watchOptions } = options || {}; - const cachedValue = createRef(refValue.value, deepRefs); - watch(() => refValue.value, (value) => { - if (!comparator(value, cachedValue.value)) - cachedValue.value = value; - }, watchOptions); - return cachedValue; -} -function usePermission(permissionDesc, options = {}) { - const { - controls = false, - navigator: navigator2 = defaultNavigator - } = options; - const isSupported = useSupported(() => navigator2 && "permissions" in navigator2); - const permissionStatus = shallowRef(); - const desc = typeof permissionDesc === "string" ? { name: permissionDesc } : permissionDesc; - const state = shallowRef(); - const update = () => { - var _a, _b; - state.value = (_b = (_a = permissionStatus.value) == null ? void 0 : _a.state) != null ? _b : "prompt"; - }; - useEventListener(permissionStatus, "change", update, { passive: true }); - const query = createSingletonPromise(async () => { - if (!isSupported.value) - return; - if (!permissionStatus.value) { - try { - permissionStatus.value = await navigator2.permissions.query(desc); - } catch (e) { - permissionStatus.value = void 0; - } finally { - update(); - } - } - if (controls) - return toRaw(permissionStatus.value); - }); - query(); - if (controls) { - return { - state, - isSupported, - query - }; - } else { - return state; - } -} -function useClipboard(options = {}) { - const { - navigator: navigator2 = defaultNavigator, - read = false, - source, - copiedDuring = 1500, - legacy = false - } = options; - const isClipboardApiSupported = useSupported(() => navigator2 && "clipboard" in navigator2); - const permissionRead = usePermission("clipboard-read"); - const permissionWrite = usePermission("clipboard-write"); - const isSupported = computed(() => isClipboardApiSupported.value || legacy); - const text = shallowRef(""); - const copied = shallowRef(false); - const timeout = useTimeoutFn(() => copied.value = false, copiedDuring, { immediate: false }); - async function updateText() { - let useLegacy = !(isClipboardApiSupported.value && isAllowed(permissionRead.value)); - if (!useLegacy) { - try { - text.value = await navigator2.clipboard.readText(); - } catch (e) { - useLegacy = true; - } - } - if (useLegacy) { - text.value = legacyRead(); - } - } - if (isSupported.value && read) - useEventListener(["copy", "cut"], updateText, { passive: true }); - async function copy(value = toValue(source)) { - if (isSupported.value && value != null) { - let useLegacy = !(isClipboardApiSupported.value && isAllowed(permissionWrite.value)); - if (!useLegacy) { - try { - await navigator2.clipboard.writeText(value); - } catch (e) { - useLegacy = true; - } - } - if (useLegacy) - legacyCopy(value); - text.value = value; - copied.value = true; - timeout.start(); - } - } - function legacyCopy(value) { - const ta = document.createElement("textarea"); - ta.value = value != null ? value : ""; - ta.style.position = "absolute"; - ta.style.opacity = "0"; - document.body.appendChild(ta); - ta.select(); - document.execCommand("copy"); - ta.remove(); - } - function legacyRead() { - var _a, _b, _c; - return (_c = (_b = (_a = document == null ? void 0 : document.getSelection) == null ? void 0 : _a.call(document)) == null ? void 0 : _b.toString()) != null ? _c : ""; - } - function isAllowed(status) { - return status === "granted" || status === "prompt"; - } - return { - isSupported, - text, - copied, - copy - }; -} -function useClipboardItems(options = {}) { - const { - navigator: navigator2 = defaultNavigator, - read = false, - source, - copiedDuring = 1500 - } = options; - const isSupported = useSupported(() => navigator2 && "clipboard" in navigator2); - const content = ref([]); - const copied = shallowRef(false); - const timeout = useTimeoutFn(() => copied.value = false, copiedDuring, { immediate: false }); - function updateContent() { - if (isSupported.value) { - navigator2.clipboard.read().then((items) => { - content.value = items; - }); - } - } - if (isSupported.value && read) - useEventListener(["copy", "cut"], updateContent, { passive: true }); - async function copy(value = toValue(source)) { - if (isSupported.value && value != null) { - await navigator2.clipboard.write(value); - content.value = value; - copied.value = true; - timeout.start(); - } - } - return { - isSupported, - content, - copied, - copy - }; -} -function cloneFnJSON(source) { - return JSON.parse(JSON.stringify(source)); -} -function useCloned(source, options = {}) { - const cloned = ref({}); - const isModified = shallowRef(false); - let _lastSync = false; - const { - manual, - clone = cloneFnJSON, - // watch options - deep = true, - immediate = true - } = options; - watch(cloned, () => { - if (_lastSync) { - _lastSync = false; - return; - } - isModified.value = true; - }, { - deep: true, - flush: "sync" - }); - function sync() { - _lastSync = true; - isModified.value = false; - cloned.value = clone(toValue(source)); - } - if (!manual && (isRef(source) || typeof source === "function")) { - watch(source, sync, { - ...options, - deep, - immediate - }); - } else { - sync(); - } - return { cloned, isModified, sync }; -} -var _global = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {}; -var globalKey = "__vueuse_ssr_handlers__"; -var handlers = getHandlers(); -function getHandlers() { - if (!(globalKey in _global)) - _global[globalKey] = _global[globalKey] || {}; - return _global[globalKey]; -} -function getSSRHandler(key, fallback) { - return handlers[key] || fallback; -} -function setSSRHandler(key, fn) { - handlers[key] = fn; -} -function usePreferredDark(options) { - return useMediaQuery("(prefers-color-scheme: dark)", options); -} -function guessSerializerType(rawInit) { - return rawInit == null ? "any" : rawInit instanceof Set ? "set" : rawInit instanceof Map ? "map" : rawInit instanceof Date ? "date" : typeof rawInit === "boolean" ? "boolean" : typeof rawInit === "string" ? "string" : typeof rawInit === "object" ? "object" : !Number.isNaN(rawInit) ? "number" : "any"; -} -var StorageSerializers = { - boolean: { - read: (v) => v === "true", - write: (v) => String(v) - }, - object: { - read: (v) => JSON.parse(v), - write: (v) => JSON.stringify(v) - }, - number: { - read: (v) => Number.parseFloat(v), - write: (v) => String(v) - }, - any: { - read: (v) => v, - write: (v) => String(v) - }, - string: { - read: (v) => v, - write: (v) => String(v) - }, - map: { - read: (v) => new Map(JSON.parse(v)), - write: (v) => JSON.stringify(Array.from(v.entries())) - }, - set: { - read: (v) => new Set(JSON.parse(v)), - write: (v) => JSON.stringify(Array.from(v)) - }, - date: { - read: (v) => new Date(v), - write: (v) => v.toISOString() - } -}; -var customStorageEventName = "vueuse-storage"; -function useStorage(key, defaults2, storage, options = {}) { - var _a; - const { - flush = "pre", - deep = true, - listenToStorageChanges = true, - writeDefaults = true, - mergeDefaults = false, - shallow, - window: window2 = defaultWindow, - eventFilter, - onError = (e) => { - console.error(e); - }, - initOnMounted - } = options; - const data = (shallow ? shallowRef : ref)(typeof defaults2 === "function" ? defaults2() : defaults2); - const keyComputed = computed(() => toValue(key)); - if (!storage) { - try { - storage = getSSRHandler("getDefaultStorage", () => { - var _a2; - return (_a2 = defaultWindow) == null ? void 0 : _a2.localStorage; - })(); - } catch (e) { - onError(e); - } - } - if (!storage) - return data; - const rawInit = toValue(defaults2); - const type = guessSerializerType(rawInit); - const serializer = (_a = options.serializer) != null ? _a : StorageSerializers[type]; - const { pause: pauseWatch, resume: resumeWatch } = watchPausable( - data, - () => write(data.value), - { flush, deep, eventFilter } - ); - watch(keyComputed, () => update(), { flush }); - if (window2 && listenToStorageChanges) { - tryOnMounted(() => { - if (storage instanceof Storage) - useEventListener(window2, "storage", update, { passive: true }); - else - useEventListener(window2, customStorageEventName, updateFromCustomEvent); - if (initOnMounted) - update(); - }); - } - if (!initOnMounted) - update(); - function dispatchWriteEvent(oldValue, newValue) { - if (window2) { - const payload = { - key: keyComputed.value, - oldValue, - newValue, - storageArea: storage - }; - window2.dispatchEvent(storage instanceof Storage ? new StorageEvent("storage", payload) : new CustomEvent(customStorageEventName, { - detail: payload - })); - } - } - function write(v) { - try { - const oldValue = storage.getItem(keyComputed.value); - if (v == null) { - dispatchWriteEvent(oldValue, null); - storage.removeItem(keyComputed.value); - } else { - const serialized = serializer.write(v); - if (oldValue !== serialized) { - storage.setItem(keyComputed.value, serialized); - dispatchWriteEvent(oldValue, serialized); - } - } - } catch (e) { - onError(e); - } - } - function read(event) { - const rawValue = event ? event.newValue : storage.getItem(keyComputed.value); - if (rawValue == null) { - if (writeDefaults && rawInit != null) - storage.setItem(keyComputed.value, serializer.write(rawInit)); - return rawInit; - } else if (!event && mergeDefaults) { - const value = serializer.read(rawValue); - if (typeof mergeDefaults === "function") - return mergeDefaults(value, rawInit); - else if (type === "object" && !Array.isArray(value)) - return { ...rawInit, ...value }; - return value; - } else if (typeof rawValue !== "string") { - return rawValue; - } else { - return serializer.read(rawValue); - } - } - function update(event) { - if (event && event.storageArea !== storage) - return; - if (event && event.key == null) { - data.value = rawInit; - return; - } - if (event && event.key !== keyComputed.value) - return; - pauseWatch(); - try { - if ((event == null ? void 0 : event.newValue) !== serializer.write(data.value)) - data.value = read(event); - } catch (e) { - onError(e); - } finally { - if (event) - nextTick(resumeWatch); - else - resumeWatch(); - } - } - function updateFromCustomEvent(event) { - update(event.detail); - } - return data; -} -var CSS_DISABLE_TRANS = "*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}"; -function useColorMode(options = {}) { - const { - selector = "html", - attribute = "class", - initialValue = "auto", - window: window2 = defaultWindow, - storage, - storageKey = "vueuse-color-scheme", - listenToStorageChanges = true, - storageRef, - emitAuto, - disableTransition = true - } = options; - const modes = { - auto: "", - light: "light", - dark: "dark", - ...options.modes || {} - }; - const preferredDark = usePreferredDark({ window: window2 }); - const system = computed(() => preferredDark.value ? "dark" : "light"); - const store = storageRef || (storageKey == null ? toRef2(initialValue) : useStorage(storageKey, initialValue, storage, { window: window2, listenToStorageChanges })); - const state = computed(() => store.value === "auto" ? system.value : store.value); - const updateHTMLAttrs = getSSRHandler( - "updateHTMLAttrs", - (selector2, attribute2, value) => { - const el = typeof selector2 === "string" ? window2 == null ? void 0 : window2.document.querySelector(selector2) : unrefElement(selector2); - if (!el) - return; - const classesToAdd = /* @__PURE__ */ new Set(); - const classesToRemove = /* @__PURE__ */ new Set(); - let attributeToChange = null; - if (attribute2 === "class") { - const current = value.split(/\s/g); - Object.values(modes).flatMap((i) => (i || "").split(/\s/g)).filter(Boolean).forEach((v) => { - if (current.includes(v)) - classesToAdd.add(v); - else - classesToRemove.add(v); - }); - } else { - attributeToChange = { key: attribute2, value }; - } - if (classesToAdd.size === 0 && classesToRemove.size === 0 && attributeToChange === null) - return; - let style; - if (disableTransition) { - style = window2.document.createElement("style"); - style.appendChild(document.createTextNode(CSS_DISABLE_TRANS)); - window2.document.head.appendChild(style); - } - for (const c of classesToAdd) { - el.classList.add(c); - } - for (const c of classesToRemove) { - el.classList.remove(c); - } - if (attributeToChange) { - el.setAttribute(attributeToChange.key, attributeToChange.value); - } - if (disableTransition) { - window2.getComputedStyle(style).opacity; - document.head.removeChild(style); - } - } - ); - function defaultOnChanged(mode) { - var _a; - updateHTMLAttrs(selector, attribute, (_a = modes[mode]) != null ? _a : mode); - } - function onChanged(mode) { - if (options.onChanged) - options.onChanged(mode, defaultOnChanged); - else - defaultOnChanged(mode); - } - watch(state, onChanged, { flush: "post", immediate: true }); - tryOnMounted(() => onChanged(state.value)); - const auto = computed({ - get() { - return emitAuto ? store.value : state.value; - }, - set(v) { - store.value = v; - } - }); - return Object.assign(auto, { store, system, state }); -} -function useConfirmDialog(revealed = shallowRef(false)) { - const confirmHook = createEventHook(); - const cancelHook = createEventHook(); - const revealHook = createEventHook(); - let _resolve = noop; - const reveal = (data) => { - revealHook.trigger(data); - revealed.value = true; - return new Promise((resolve) => { - _resolve = resolve; - }); - }; - const confirm = (data) => { - revealed.value = false; - confirmHook.trigger(data); - _resolve({ data, isCanceled: false }); - }; - const cancel = (data) => { - revealed.value = false; - cancelHook.trigger(data); - _resolve({ data, isCanceled: true }); - }; - return { - isRevealed: computed(() => revealed.value), - reveal, - confirm, - cancel, - onReveal: revealHook.on, - onConfirm: confirmHook.on, - onCancel: cancelHook.on - }; -} -function useCountdown(initialCountdown, options) { - var _a, _b; - const remaining = shallowRef(toValue(initialCountdown)); - const intervalController = useIntervalFn(() => { - var _a2, _b2; - const value = remaining.value - 1; - remaining.value = value < 0 ? 0 : value; - (_a2 = options == null ? void 0 : options.onTick) == null ? void 0 : _a2.call(options); - if (remaining.value <= 0) { - intervalController.pause(); - (_b2 = options == null ? void 0 : options.onComplete) == null ? void 0 : _b2.call(options); - } - }, (_a = options == null ? void 0 : options.interval) != null ? _a : 1e3, { immediate: (_b = options == null ? void 0 : options.immediate) != null ? _b : false }); - const reset = (countdown) => { - var _a2; - remaining.value = (_a2 = toValue(countdown)) != null ? _a2 : toValue(initialCountdown); - }; - const stop = () => { - intervalController.pause(); - reset(); - }; - const resume = () => { - if (!intervalController.isActive.value) { - if (remaining.value > 0) { - intervalController.resume(); - } - } - }; - const start = (countdown) => { - reset(countdown); - intervalController.resume(); - }; - return { - remaining, - reset, - stop, - start, - pause: intervalController.pause, - resume, - isActive: intervalController.isActive - }; -} -function useCssVar(prop, target, options = {}) { - const { window: window2 = defaultWindow, initialValue, observe = false } = options; - const variable = shallowRef(initialValue); - const elRef = computed(() => { - var _a; - return unrefElement(target) || ((_a = window2 == null ? void 0 : window2.document) == null ? void 0 : _a.documentElement); - }); - function updateCssVar() { - var _a; - const key = toValue(prop); - const el = toValue(elRef); - if (el && window2 && key) { - const value = (_a = window2.getComputedStyle(el).getPropertyValue(key)) == null ? void 0 : _a.trim(); - variable.value = value || variable.value || initialValue; - } - } - if (observe) { - useMutationObserver(elRef, updateCssVar, { - attributeFilter: ["style", "class"], - window: window2 - }); - } - watch( - [elRef, () => toValue(prop)], - (_, old) => { - if (old[0] && old[1]) - old[0].style.removeProperty(old[1]); - updateCssVar(); - }, - { immediate: true } - ); - watch( - [variable, elRef], - ([val, el]) => { - const raw_prop = toValue(prop); - if ((el == null ? void 0 : el.style) && raw_prop) { - if (val == null) - el.style.removeProperty(raw_prop); - else - el.style.setProperty(raw_prop, val); - } - }, - { immediate: true } - ); - return variable; -} -function useCurrentElement(rootComponent) { - const vm = getCurrentInstance(); - const currentElement = computedWithControl( - () => null, - () => rootComponent ? unrefElement(rootComponent) : vm.proxy.$el - ); - onUpdated(currentElement.trigger); - onMounted(currentElement.trigger); - return currentElement; -} -function useCycleList(list, options) { - const state = shallowRef(getInitialValue()); - const listRef = toRef2(list); - const index = computed({ - get() { - var _a; - const targetList = listRef.value; - let index2 = (options == null ? void 0 : options.getIndexOf) ? options.getIndexOf(state.value, targetList) : targetList.indexOf(state.value); - if (index2 < 0) - index2 = (_a = options == null ? void 0 : options.fallbackIndex) != null ? _a : 0; - return index2; - }, - set(v) { - set2(v); - } - }); - function set2(i) { - const targetList = listRef.value; - const length = targetList.length; - const index2 = (i % length + length) % length; - const value = targetList[index2]; - state.value = value; - return value; - } - function shift(delta = 1) { - return set2(index.value + delta); - } - function next(n = 1) { - return shift(n); - } - function prev(n = 1) { - return shift(-n); - } - function getInitialValue() { - var _a, _b; - return (_b = toValue((_a = options == null ? void 0 : options.initialValue) != null ? _a : toValue(list)[0])) != null ? _b : void 0; - } - watch(listRef, () => set2(index.value)); - return { - state, - index, - next, - prev, - go: set2 - }; -} -function useDark(options = {}) { - const { - valueDark = "dark", - valueLight = "" - } = options; - const mode = useColorMode({ - ...options, - onChanged: (mode2, defaultHandler) => { - var _a; - if (options.onChanged) - (_a = options.onChanged) == null ? void 0 : _a.call(options, mode2 === "dark", defaultHandler, mode2); - else - defaultHandler(mode2); - }, - modes: { - dark: valueDark, - light: valueLight - } - }); - const system = computed(() => mode.system.value); - const isDark = computed({ - get() { - return mode.value === "dark"; - }, - set(v) { - const modeVal = v ? "dark" : "light"; - if (system.value === modeVal) - mode.value = "auto"; - else - mode.value = modeVal; - } - }); - return isDark; -} -function fnBypass(v) { - return v; -} -function fnSetSource(source, value) { - return source.value = value; -} -function defaultDump(clone) { - return clone ? typeof clone === "function" ? clone : cloneFnJSON : fnBypass; -} -function defaultParse(clone) { - return clone ? typeof clone === "function" ? clone : cloneFnJSON : fnBypass; -} -function useManualRefHistory(source, options = {}) { - const { - clone = false, - dump = defaultDump(clone), - parse = defaultParse(clone), - setSource = fnSetSource - } = options; - function _createHistoryRecord() { - return markRaw({ - snapshot: dump(source.value), - timestamp: timestamp() - }); - } - const last = ref(_createHistoryRecord()); - const undoStack = ref([]); - const redoStack = ref([]); - const _setSource = (record) => { - setSource(source, parse(record.snapshot)); - last.value = record; - }; - const commit = () => { - undoStack.value.unshift(last.value); - last.value = _createHistoryRecord(); - if (options.capacity && undoStack.value.length > options.capacity) - undoStack.value.splice(options.capacity, Number.POSITIVE_INFINITY); - if (redoStack.value.length) - redoStack.value.splice(0, redoStack.value.length); - }; - const clear = () => { - undoStack.value.splice(0, undoStack.value.length); - redoStack.value.splice(0, redoStack.value.length); - }; - const undo = () => { - const state = undoStack.value.shift(); - if (state) { - redoStack.value.unshift(last.value); - _setSource(state); - } - }; - const redo = () => { - const state = redoStack.value.shift(); - if (state) { - undoStack.value.unshift(last.value); - _setSource(state); - } - }; - const reset = () => { - _setSource(last.value); - }; - const history = computed(() => [last.value, ...undoStack.value]); - const canUndo = computed(() => undoStack.value.length > 0); - const canRedo = computed(() => redoStack.value.length > 0); - return { - source, - undoStack, - redoStack, - last, - history, - canUndo, - canRedo, - clear, - commit, - reset, - undo, - redo - }; -} -function useRefHistory(source, options = {}) { - const { - deep = false, - flush = "pre", - eventFilter - } = options; - const { - eventFilter: composedFilter, - pause, - resume: resumeTracking, - isActive: isTracking - } = pausableFilter(eventFilter); - const { - ignoreUpdates, - ignorePrevAsyncUpdates, - stop - } = watchIgnorable( - source, - commit, - { deep, flush, eventFilter: composedFilter } - ); - function setSource(source2, value) { - ignorePrevAsyncUpdates(); - ignoreUpdates(() => { - source2.value = value; - }); - } - const manualHistory = useManualRefHistory(source, { ...options, clone: options.clone || deep, setSource }); - const { clear, commit: manualCommit } = manualHistory; - function commit() { - ignorePrevAsyncUpdates(); - manualCommit(); - } - function resume(commitNow) { - resumeTracking(); - if (commitNow) - commit(); - } - function batch(fn) { - let canceled = false; - const cancel = () => canceled = true; - ignoreUpdates(() => { - fn(cancel); - }); - if (!canceled) - commit(); - } - function dispose() { - stop(); - clear(); - } - return { - ...manualHistory, - isTracking, - pause, - resume, - commit, - batch, - dispose - }; -} -function useDebouncedRefHistory(source, options = {}) { - const filter = options.debounce ? debounceFilter(options.debounce) : void 0; - const history = useRefHistory(source, { ...options, eventFilter: filter }); - return { - ...history - }; -} -function useDeviceMotion(options = {}) { - const { - window: window2 = defaultWindow, - requestPermissions = false, - eventFilter = bypassFilter - } = options; - const isSupported = useSupported(() => typeof DeviceMotionEvent !== "undefined"); - const requirePermissions = useSupported(() => isSupported.value && "requestPermission" in DeviceMotionEvent && typeof DeviceMotionEvent.requestPermission === "function"); - const permissionGranted = shallowRef(false); - const acceleration = ref({ x: null, y: null, z: null }); - const rotationRate = ref({ alpha: null, beta: null, gamma: null }); - const interval = shallowRef(0); - const accelerationIncludingGravity = ref({ - x: null, - y: null, - z: null - }); - function init() { - if (window2) { - const onDeviceMotion = createFilterWrapper( - eventFilter, - (event) => { - var _a, _b, _c, _d, _e, _f, _g, _h, _i; - acceleration.value = { - x: ((_a = event.acceleration) == null ? void 0 : _a.x) || null, - y: ((_b = event.acceleration) == null ? void 0 : _b.y) || null, - z: ((_c = event.acceleration) == null ? void 0 : _c.z) || null - }; - accelerationIncludingGravity.value = { - x: ((_d = event.accelerationIncludingGravity) == null ? void 0 : _d.x) || null, - y: ((_e = event.accelerationIncludingGravity) == null ? void 0 : _e.y) || null, - z: ((_f = event.accelerationIncludingGravity) == null ? void 0 : _f.z) || null - }; - rotationRate.value = { - alpha: ((_g = event.rotationRate) == null ? void 0 : _g.alpha) || null, - beta: ((_h = event.rotationRate) == null ? void 0 : _h.beta) || null, - gamma: ((_i = event.rotationRate) == null ? void 0 : _i.gamma) || null - }; - interval.value = event.interval; - } - ); - useEventListener(window2, "devicemotion", onDeviceMotion, { passive: true }); - } - } - const ensurePermissions = async () => { - if (!requirePermissions.value) - permissionGranted.value = true; - if (permissionGranted.value) - return; - if (requirePermissions.value) { - const requestPermission = DeviceMotionEvent.requestPermission; - try { - const response = await requestPermission(); - if (response === "granted") { - permissionGranted.value = true; - init(); - } - } catch (error) { - console.error(error); - } - } - }; - if (isSupported.value) { - if (requestPermissions && requirePermissions.value) { - ensurePermissions().then(() => init()); - } else { - init(); - } - } - return { - acceleration, - accelerationIncludingGravity, - rotationRate, - interval, - isSupported, - requirePermissions, - ensurePermissions, - permissionGranted - }; -} -function useDeviceOrientation(options = {}) { - const { window: window2 = defaultWindow } = options; - const isSupported = useSupported(() => window2 && "DeviceOrientationEvent" in window2); - const isAbsolute = shallowRef(false); - const alpha = shallowRef(null); - const beta = shallowRef(null); - const gamma = shallowRef(null); - if (window2 && isSupported.value) { - useEventListener(window2, "deviceorientation", (event) => { - isAbsolute.value = event.absolute; - alpha.value = event.alpha; - beta.value = event.beta; - gamma.value = event.gamma; - }, { passive: true }); - } - return { - isSupported, - isAbsolute, - alpha, - beta, - gamma - }; -} -function useDevicePixelRatio(options = {}) { - const { - window: window2 = defaultWindow - } = options; - const pixelRatio = shallowRef(1); - const query = useMediaQuery(() => `(resolution: ${pixelRatio.value}dppx)`, options); - let stop = noop; - if (window2) { - stop = watchImmediate(query, () => pixelRatio.value = window2.devicePixelRatio); - } - return { - pixelRatio: readonly(pixelRatio), - stop - }; -} -function useDevicesList(options = {}) { - const { - navigator: navigator2 = defaultNavigator, - requestPermissions = false, - constraints = { audio: true, video: true }, - onUpdated: onUpdated2 - } = options; - const devices = ref([]); - const videoInputs = computed(() => devices.value.filter((i) => i.kind === "videoinput")); - const audioInputs = computed(() => devices.value.filter((i) => i.kind === "audioinput")); - const audioOutputs = computed(() => devices.value.filter((i) => i.kind === "audiooutput")); - const isSupported = useSupported(() => navigator2 && navigator2.mediaDevices && navigator2.mediaDevices.enumerateDevices); - const permissionGranted = shallowRef(false); - let stream; - async function update() { - if (!isSupported.value) - return; - devices.value = await navigator2.mediaDevices.enumerateDevices(); - onUpdated2 == null ? void 0 : onUpdated2(devices.value); - if (stream) { - stream.getTracks().forEach((t) => t.stop()); - stream = null; - } - } - async function ensurePermissions() { - const deviceName = constraints.video ? "camera" : "microphone"; - if (!isSupported.value) - return false; - if (permissionGranted.value) - return true; - const { state, query } = usePermission(deviceName, { controls: true }); - await query(); - if (state.value !== "granted") { - let granted = true; - try { - stream = await navigator2.mediaDevices.getUserMedia(constraints); - } catch (e) { - stream = null; - granted = false; - } - update(); - permissionGranted.value = granted; - } else { - permissionGranted.value = true; - } - return permissionGranted.value; - } - if (isSupported.value) { - if (requestPermissions) - ensurePermissions(); - useEventListener(navigator2.mediaDevices, "devicechange", update, { passive: true }); - update(); - } - return { - devices, - ensurePermissions, - permissionGranted, - videoInputs, - audioInputs, - audioOutputs, - isSupported - }; -} -function useDisplayMedia(options = {}) { - var _a; - const enabled = shallowRef((_a = options.enabled) != null ? _a : false); - const video = options.video; - const audio = options.audio; - const { navigator: navigator2 = defaultNavigator } = options; - const isSupported = useSupported(() => { - var _a2; - return (_a2 = navigator2 == null ? void 0 : navigator2.mediaDevices) == null ? void 0 : _a2.getDisplayMedia; - }); - const constraint = { audio, video }; - const stream = shallowRef(); - async function _start() { - var _a2; - if (!isSupported.value || stream.value) - return; - stream.value = await navigator2.mediaDevices.getDisplayMedia(constraint); - (_a2 = stream.value) == null ? void 0 : _a2.getTracks().forEach((t) => useEventListener(t, "ended", stop, { passive: true })); - return stream.value; - } - async function _stop() { - var _a2; - (_a2 = stream.value) == null ? void 0 : _a2.getTracks().forEach((t) => t.stop()); - stream.value = void 0; - } - function stop() { - _stop(); - enabled.value = false; - } - async function start() { - await _start(); - if (stream.value) - enabled.value = true; - return stream.value; - } - watch( - enabled, - (v) => { - if (v) - _start(); - else - _stop(); - }, - { immediate: true } - ); - return { - isSupported, - stream, - start, - stop, - enabled - }; -} -function useDocumentVisibility(options = {}) { - const { document: document2 = defaultDocument } = options; - if (!document2) - return shallowRef("visible"); - const visibility = shallowRef(document2.visibilityState); - useEventListener(document2, "visibilitychange", () => { - visibility.value = document2.visibilityState; - }, { passive: true }); - return visibility; -} -function useDraggable(target, options = {}) { - var _a; - const { - pointerTypes, - preventDefault: preventDefault2, - stopPropagation, - exact, - onMove, - onEnd, - onStart, - initialValue, - axis = "both", - draggingElement = defaultWindow, - containerElement, - handle: draggingHandle = target, - buttons = [0] - } = options; - const position = ref( - (_a = toValue(initialValue)) != null ? _a : { x: 0, y: 0 } - ); - const pressedDelta = ref(); - const filterEvent = (e) => { - if (pointerTypes) - return pointerTypes.includes(e.pointerType); - return true; - }; - const handleEvent = (e) => { - if (toValue(preventDefault2)) - e.preventDefault(); - if (toValue(stopPropagation)) - e.stopPropagation(); - }; - const start = (e) => { - var _a2; - if (!toValue(buttons).includes(e.button)) - return; - if (toValue(options.disabled) || !filterEvent(e)) - return; - if (toValue(exact) && e.target !== toValue(target)) - return; - const container = toValue(containerElement); - const containerRect = (_a2 = container == null ? void 0 : container.getBoundingClientRect) == null ? void 0 : _a2.call(container); - const targetRect = toValue(target).getBoundingClientRect(); - const pos = { - x: e.clientX - (container ? targetRect.left - containerRect.left + container.scrollLeft : targetRect.left), - y: e.clientY - (container ? targetRect.top - containerRect.top + container.scrollTop : targetRect.top) - }; - if ((onStart == null ? void 0 : onStart(pos, e)) === false) - return; - pressedDelta.value = pos; - handleEvent(e); - }; - const move = (e) => { - if (toValue(options.disabled) || !filterEvent(e)) - return; - if (!pressedDelta.value) - return; - const container = toValue(containerElement); - const targetRect = toValue(target).getBoundingClientRect(); - let { x, y } = position.value; - if (axis === "x" || axis === "both") { - x = e.clientX - pressedDelta.value.x; - if (container) - x = Math.min(Math.max(0, x), container.scrollWidth - targetRect.width); - } - if (axis === "y" || axis === "both") { - y = e.clientY - pressedDelta.value.y; - if (container) - y = Math.min(Math.max(0, y), container.scrollHeight - targetRect.height); - } - position.value = { - x, - y - }; - onMove == null ? void 0 : onMove(position.value, e); - handleEvent(e); - }; - const end = (e) => { - if (toValue(options.disabled) || !filterEvent(e)) - return; - if (!pressedDelta.value) - return; - pressedDelta.value = void 0; - onEnd == null ? void 0 : onEnd(position.value, e); - handleEvent(e); - }; - if (isClient) { - const config = () => { - var _a2; - return { - capture: (_a2 = options.capture) != null ? _a2 : true, - passive: !toValue(preventDefault2) - }; - }; - useEventListener(draggingHandle, "pointerdown", start, config); - useEventListener(draggingElement, "pointermove", move, config); - useEventListener(draggingElement, "pointerup", end, config); - } - return { - ...toRefs2(position), - position, - isDragging: computed(() => !!pressedDelta.value), - style: computed( - () => `left:${position.value.x}px;top:${position.value.y}px;` - ) - }; -} -function useDropZone(target, options = {}) { - var _a, _b; - const isOverDropZone = shallowRef(false); - const files = shallowRef(null); - let counter = 0; - let isValid = true; - if (isClient) { - const _options = typeof options === "function" ? { onDrop: options } : options; - const multiple = (_a = _options.multiple) != null ? _a : true; - const preventDefaultForUnhandled = (_b = _options.preventDefaultForUnhandled) != null ? _b : false; - const getFiles = (event) => { - var _a2, _b2; - const list = Array.from((_b2 = (_a2 = event.dataTransfer) == null ? void 0 : _a2.files) != null ? _b2 : []); - return list.length === 0 ? null : multiple ? list : [list[0]]; - }; - const checkDataTypes = (types) => { - const dataTypes = unref(_options.dataTypes); - if (typeof dataTypes === "function") - return dataTypes(types); - if (!(dataTypes == null ? void 0 : dataTypes.length)) - return true; - if (types.length === 0) - return false; - return types.every( - (type) => dataTypes.some((allowedType) => type.includes(allowedType)) - ); - }; - const checkValidity = (items) => { - const types = Array.from(items != null ? items : []).map((item) => item.type); - const dataTypesValid = checkDataTypes(types); - const multipleFilesValid = multiple || items.length <= 1; - return dataTypesValid && multipleFilesValid; - }; - const isSafari = () => /^(?:(?!chrome|android).)*safari/i.test(navigator.userAgent) && !("chrome" in window); - const handleDragEvent = (event, eventType) => { - var _a2, _b2, _c, _d, _e, _f; - const dataTransferItemList = (_a2 = event.dataTransfer) == null ? void 0 : _a2.items; - isValid = (_b2 = dataTransferItemList && checkValidity(dataTransferItemList)) != null ? _b2 : false; - if (preventDefaultForUnhandled) { - event.preventDefault(); - } - if (!isSafari() && !isValid) { - if (event.dataTransfer) { - event.dataTransfer.dropEffect = "none"; - } - return; - } - event.preventDefault(); - if (event.dataTransfer) { - event.dataTransfer.dropEffect = "copy"; - } - const currentFiles = getFiles(event); - switch (eventType) { - case "enter": - counter += 1; - isOverDropZone.value = true; - (_c = _options.onEnter) == null ? void 0 : _c.call(_options, null, event); - break; - case "over": - (_d = _options.onOver) == null ? void 0 : _d.call(_options, null, event); - break; - case "leave": - counter -= 1; - if (counter === 0) - isOverDropZone.value = false; - (_e = _options.onLeave) == null ? void 0 : _e.call(_options, null, event); - break; - case "drop": - counter = 0; - isOverDropZone.value = false; - if (isValid) { - files.value = currentFiles; - (_f = _options.onDrop) == null ? void 0 : _f.call(_options, currentFiles, event); - } - break; - } - }; - useEventListener(target, "dragenter", (event) => handleDragEvent(event, "enter")); - useEventListener(target, "dragover", (event) => handleDragEvent(event, "over")); - useEventListener(target, "dragleave", (event) => handleDragEvent(event, "leave")); - useEventListener(target, "drop", (event) => handleDragEvent(event, "drop")); - } - return { - files, - isOverDropZone - }; -} -function useResizeObserver(target, callback, options = {}) { - const { window: window2 = defaultWindow, ...observerOptions } = options; - let observer; - const isSupported = useSupported(() => window2 && "ResizeObserver" in window2); - const cleanup = () => { - if (observer) { - observer.disconnect(); - observer = void 0; - } - }; - const targets = computed(() => { - const _targets = toValue(target); - return Array.isArray(_targets) ? _targets.map((el) => unrefElement(el)) : [unrefElement(_targets)]; - }); - const stopWatch = watch( - targets, - (els) => { - cleanup(); - if (isSupported.value && window2) { - observer = new ResizeObserver(callback); - for (const _el of els) { - if (_el) - observer.observe(_el, observerOptions); - } - } - }, - { immediate: true, flush: "post" } - ); - const stop = () => { - cleanup(); - stopWatch(); - }; - tryOnScopeDispose(stop); - return { - isSupported, - stop - }; -} -function useElementBounding(target, options = {}) { - const { - reset = true, - windowResize = true, - windowScroll = true, - immediate = true, - updateTiming = "sync" - } = options; - const height = shallowRef(0); - const bottom = shallowRef(0); - const left = shallowRef(0); - const right = shallowRef(0); - const top = shallowRef(0); - const width = shallowRef(0); - const x = shallowRef(0); - const y = shallowRef(0); - function recalculate() { - const el = unrefElement(target); - if (!el) { - if (reset) { - height.value = 0; - bottom.value = 0; - left.value = 0; - right.value = 0; - top.value = 0; - width.value = 0; - x.value = 0; - y.value = 0; - } - return; - } - const rect = el.getBoundingClientRect(); - height.value = rect.height; - bottom.value = rect.bottom; - left.value = rect.left; - right.value = rect.right; - top.value = rect.top; - width.value = rect.width; - x.value = rect.x; - y.value = rect.y; - } - function update() { - if (updateTiming === "sync") - recalculate(); - else if (updateTiming === "next-frame") - requestAnimationFrame(() => recalculate()); - } - useResizeObserver(target, update); - watch(() => unrefElement(target), (ele) => !ele && update()); - useMutationObserver(target, update, { - attributeFilter: ["style", "class"] - }); - if (windowScroll) - useEventListener("scroll", update, { capture: true, passive: true }); - if (windowResize) - useEventListener("resize", update, { passive: true }); - tryOnMounted(() => { - if (immediate) - update(); - }); - return { - height, - bottom, - left, - right, - top, - width, - x, - y, - update - }; -} -function useElementByPoint(options) { - const { - x, - y, - document: document2 = defaultDocument, - multiple, - interval = "requestAnimationFrame", - immediate = true - } = options; - const isSupported = useSupported(() => { - if (toValue(multiple)) - return document2 && "elementsFromPoint" in document2; - return document2 && "elementFromPoint" in document2; - }); - const element = shallowRef(null); - const cb = () => { - var _a, _b; - element.value = toValue(multiple) ? (_a = document2 == null ? void 0 : document2.elementsFromPoint(toValue(x), toValue(y))) != null ? _a : [] : (_b = document2 == null ? void 0 : document2.elementFromPoint(toValue(x), toValue(y))) != null ? _b : null; - }; - const controls = interval === "requestAnimationFrame" ? useRafFn(cb, { immediate }) : useIntervalFn(cb, interval, { immediate }); - return { - isSupported, - element, - ...controls - }; -} -function useElementHover(el, options = {}) { - const { - delayEnter = 0, - delayLeave = 0, - triggerOnRemoval = false, - window: window2 = defaultWindow - } = options; - const isHovered = shallowRef(false); - let timer; - const toggle = (entering) => { - const delay = entering ? delayEnter : delayLeave; - if (timer) { - clearTimeout(timer); - timer = void 0; - } - if (delay) - timer = setTimeout(() => isHovered.value = entering, delay); - else - isHovered.value = entering; - }; - if (!window2) - return isHovered; - useEventListener(el, "mouseenter", () => toggle(true), { passive: true }); - useEventListener(el, "mouseleave", () => toggle(false), { passive: true }); - if (triggerOnRemoval) { - onElementRemoval( - computed(() => unrefElement(el)), - () => toggle(false) - ); - } - return isHovered; -} -function useElementSize(target, initialSize = { width: 0, height: 0 }, options = {}) { - const { window: window2 = defaultWindow, box = "content-box" } = options; - const isSVG = computed(() => { - var _a, _b; - return (_b = (_a = unrefElement(target)) == null ? void 0 : _a.namespaceURI) == null ? void 0 : _b.includes("svg"); - }); - const width = shallowRef(initialSize.width); - const height = shallowRef(initialSize.height); - const { stop: stop1 } = useResizeObserver( - target, - ([entry]) => { - const boxSize = box === "border-box" ? entry.borderBoxSize : box === "content-box" ? entry.contentBoxSize : entry.devicePixelContentBoxSize; - if (window2 && isSVG.value) { - const $elem = unrefElement(target); - if ($elem) { - const rect = $elem.getBoundingClientRect(); - width.value = rect.width; - height.value = rect.height; - } - } else { - if (boxSize) { - const formatBoxSize = toArray(boxSize); - width.value = formatBoxSize.reduce((acc, { inlineSize }) => acc + inlineSize, 0); - height.value = formatBoxSize.reduce((acc, { blockSize }) => acc + blockSize, 0); - } else { - width.value = entry.contentRect.width; - height.value = entry.contentRect.height; - } - } - }, - options - ); - tryOnMounted(() => { - const ele = unrefElement(target); - if (ele) { - width.value = "offsetWidth" in ele ? ele.offsetWidth : initialSize.width; - height.value = "offsetHeight" in ele ? ele.offsetHeight : initialSize.height; - } - }); - const stop2 = watch( - () => unrefElement(target), - (ele) => { - width.value = ele ? initialSize.width : 0; - height.value = ele ? initialSize.height : 0; - } - ); - function stop() { - stop1(); - stop2(); - } - return { - width, - height, - stop - }; -} -function useIntersectionObserver(target, callback, options = {}) { - const { - root, - rootMargin = "0px", - threshold = 0, - window: window2 = defaultWindow, - immediate = true - } = options; - const isSupported = useSupported(() => window2 && "IntersectionObserver" in window2); - const targets = computed(() => { - const _target = toValue(target); - return toArray(_target).map(unrefElement).filter(notNullish); - }); - let cleanup = noop; - const isActive = shallowRef(immediate); - const stopWatch = isSupported.value ? watch( - () => [targets.value, unrefElement(root), isActive.value], - ([targets2, root2]) => { - cleanup(); - if (!isActive.value) - return; - if (!targets2.length) - return; - const observer = new IntersectionObserver( - callback, - { - root: unrefElement(root2), - rootMargin, - threshold - } - ); - targets2.forEach((el) => el && observer.observe(el)); - cleanup = () => { - observer.disconnect(); - cleanup = noop; - }; - }, - { immediate, flush: "post" } - ) : noop; - const stop = () => { - cleanup(); - stopWatch(); - isActive.value = false; - }; - tryOnScopeDispose(stop); - return { - isSupported, - isActive, - pause() { - cleanup(); - isActive.value = false; - }, - resume() { - isActive.value = true; - }, - stop - }; -} -function useElementVisibility(element, options = {}) { - const { - window: window2 = defaultWindow, - scrollTarget, - threshold = 0, - rootMargin, - once = false - } = options; - const elementIsVisible = shallowRef(false); - const { stop } = useIntersectionObserver( - element, - (intersectionObserverEntries) => { - let isIntersecting = elementIsVisible.value; - let latestTime = 0; - for (const entry of intersectionObserverEntries) { - if (entry.time >= latestTime) { - latestTime = entry.time; - isIntersecting = entry.isIntersecting; - } - } - elementIsVisible.value = isIntersecting; - if (once) { - watchOnce(elementIsVisible, () => { - stop(); - }); - } - }, - { - root: scrollTarget, - window: window2, - threshold, - rootMargin: toValue(rootMargin) - } - ); - return elementIsVisible; -} -var events = /* @__PURE__ */ new Map(); -function useEventBus(key) { - const scope = getCurrentScope(); - function on(listener) { - var _a; - const listeners = events.get(key) || /* @__PURE__ */ new Set(); - listeners.add(listener); - events.set(key, listeners); - const _off = () => off(listener); - (_a = scope == null ? void 0 : scope.cleanups) == null ? void 0 : _a.push(_off); - return _off; - } - function once(listener) { - function _listener(...args) { - off(_listener); - listener(...args); - } - return on(_listener); - } - function off(listener) { - const listeners = events.get(key); - if (!listeners) - return; - listeners.delete(listener); - if (!listeners.size) - reset(); - } - function reset() { - events.delete(key); - } - function emit(event, payload) { - var _a; - (_a = events.get(key)) == null ? void 0 : _a.forEach((v) => v(event, payload)); - } - return { on, once, off, emit, reset }; -} -function resolveNestedOptions$1(options) { - if (options === true) - return {}; - return options; -} -function useEventSource(url, events2 = [], options = {}) { - const event = shallowRef(null); - const data = shallowRef(null); - const status = shallowRef("CONNECTING"); - const eventSource = ref(null); - const error = shallowRef(null); - const urlRef = toRef2(url); - const lastEventId = shallowRef(null); - let explicitlyClosed = false; - let retried = 0; - const { - withCredentials = false, - immediate = true, - autoConnect = true, - autoReconnect - } = options; - const close = () => { - if (isClient && eventSource.value) { - eventSource.value.close(); - eventSource.value = null; - status.value = "CLOSED"; - explicitlyClosed = true; - } - }; - const _init = () => { - if (explicitlyClosed || typeof urlRef.value === "undefined") - return; - const es = new EventSource(urlRef.value, { withCredentials }); - status.value = "CONNECTING"; - eventSource.value = es; - es.onopen = () => { - status.value = "OPEN"; - error.value = null; - }; - es.onerror = (e) => { - status.value = "CLOSED"; - error.value = e; - if (es.readyState === 2 && !explicitlyClosed && autoReconnect) { - es.close(); - const { - retries = -1, - delay = 1e3, - onFailed - } = resolveNestedOptions$1(autoReconnect); - retried += 1; - if (typeof retries === "number" && (retries < 0 || retried < retries)) - setTimeout(_init, delay); - else if (typeof retries === "function" && retries()) - setTimeout(_init, delay); - else - onFailed == null ? void 0 : onFailed(); - } - }; - es.onmessage = (e) => { - event.value = null; - data.value = e.data; - lastEventId.value = e.lastEventId; - }; - for (const event_name of events2) { - useEventListener(es, event_name, (e) => { - event.value = event_name; - data.value = e.data || null; - }, { passive: true }); - } - }; - const open = () => { - if (!isClient) - return; - close(); - explicitlyClosed = false; - retried = 0; - _init(); - }; - if (immediate) - open(); - if (autoConnect) - watch(urlRef, open); - tryOnScopeDispose(close); - return { - eventSource, - event, - data, - status, - error, - open, - close, - lastEventId - }; -} -function useEyeDropper(options = {}) { - const { initialValue = "" } = options; - const isSupported = useSupported(() => typeof window !== "undefined" && "EyeDropper" in window); - const sRGBHex = shallowRef(initialValue); - async function open(openOptions) { - if (!isSupported.value) - return; - const eyeDropper = new window.EyeDropper(); - const result = await eyeDropper.open(openOptions); - sRGBHex.value = result.sRGBHex; - return result; - } - return { isSupported, sRGBHex, open }; -} -function useFavicon(newIcon = null, options = {}) { - const { - baseUrl = "", - rel = "icon", - document: document2 = defaultDocument - } = options; - const favicon = toRef2(newIcon); - const applyIcon = (icon) => { - const elements = document2 == null ? void 0 : document2.head.querySelectorAll(`link[rel*="${rel}"]`); - if (!elements || elements.length === 0) { - const link = document2 == null ? void 0 : document2.createElement("link"); - if (link) { - link.rel = rel; - link.href = `${baseUrl}${icon}`; - link.type = `image/${icon.split(".").pop()}`; - document2 == null ? void 0 : document2.head.append(link); - } - return; - } - elements == null ? void 0 : elements.forEach((el) => el.href = `${baseUrl}${icon}`); - }; - watch( - favicon, - (i, o) => { - if (typeof i === "string" && i !== o) - applyIcon(i); - }, - { immediate: true } - ); - return favicon; -} -var payloadMapping = { - json: "application/json", - text: "text/plain" -}; -function isFetchOptions(obj) { - return obj && containsProp(obj, "immediate", "refetch", "initialData", "timeout", "beforeFetch", "afterFetch", "onFetchError", "fetch", "updateDataOnError"); -} -var reAbsolute = /^(?:[a-z][a-z\d+\-.]*:)?\/\//i; -function isAbsoluteURL(url) { - return reAbsolute.test(url); -} -function headersToObject(headers) { - if (typeof Headers !== "undefined" && headers instanceof Headers) - return Object.fromEntries(headers.entries()); - return headers; -} -function combineCallbacks(combination, ...callbacks) { - if (combination === "overwrite") { - return async (ctx) => { - let callback; - for (let i = callbacks.length - 1; i >= 0; i--) { - if (callbacks[i] != null) { - callback = callbacks[i]; - break; - } - } - if (callback) - return { ...ctx, ...await callback(ctx) }; - return ctx; - }; - } else { - return async (ctx) => { - for (const callback of callbacks) { - if (callback) - ctx = { ...ctx, ...await callback(ctx) }; - } - return ctx; - }; - } -} -function createFetch(config = {}) { - const _combination = config.combination || "chain"; - const _options = config.options || {}; - const _fetchOptions = config.fetchOptions || {}; - function useFactoryFetch(url, ...args) { - const computedUrl = computed(() => { - const baseUrl = toValue(config.baseUrl); - const targetUrl = toValue(url); - return baseUrl && !isAbsoluteURL(targetUrl) ? joinPaths(baseUrl, targetUrl) : targetUrl; - }); - let options = _options; - let fetchOptions = _fetchOptions; - if (args.length > 0) { - if (isFetchOptions(args[0])) { - options = { - ...options, - ...args[0], - beforeFetch: combineCallbacks(_combination, _options.beforeFetch, args[0].beforeFetch), - afterFetch: combineCallbacks(_combination, _options.afterFetch, args[0].afterFetch), - onFetchError: combineCallbacks(_combination, _options.onFetchError, args[0].onFetchError) - }; - } else { - fetchOptions = { - ...fetchOptions, - ...args[0], - headers: { - ...headersToObject(fetchOptions.headers) || {}, - ...headersToObject(args[0].headers) || {} - } - }; - } - } - if (args.length > 1 && isFetchOptions(args[1])) { - options = { - ...options, - ...args[1], - beforeFetch: combineCallbacks(_combination, _options.beforeFetch, args[1].beforeFetch), - afterFetch: combineCallbacks(_combination, _options.afterFetch, args[1].afterFetch), - onFetchError: combineCallbacks(_combination, _options.onFetchError, args[1].onFetchError) - }; - } - return useFetch(computedUrl, fetchOptions, options); - } - return useFactoryFetch; -} -function useFetch(url, ...args) { - var _a; - const supportsAbort = typeof AbortController === "function"; - let fetchOptions = {}; - let options = { - immediate: true, - refetch: false, - timeout: 0, - updateDataOnError: false - }; - const config = { - method: "GET", - type: "text", - payload: void 0 - }; - if (args.length > 0) { - if (isFetchOptions(args[0])) - options = { ...options, ...args[0] }; - else - fetchOptions = args[0]; - } - if (args.length > 1) { - if (isFetchOptions(args[1])) - options = { ...options, ...args[1] }; - } - const { - fetch = (_a = defaultWindow) == null ? void 0 : _a.fetch, - initialData, - timeout - } = options; - const responseEvent = createEventHook(); - const errorEvent = createEventHook(); - const finallyEvent = createEventHook(); - const isFinished = shallowRef(false); - const isFetching = shallowRef(false); - const aborted = shallowRef(false); - const statusCode = shallowRef(null); - const response = shallowRef(null); - const error = shallowRef(null); - const data = shallowRef(initialData || null); - const canAbort = computed(() => supportsAbort && isFetching.value); - let controller; - let timer; - const abort = () => { - if (supportsAbort) { - controller == null ? void 0 : controller.abort(); - controller = new AbortController(); - controller.signal.onabort = () => aborted.value = true; - fetchOptions = { - ...fetchOptions, - signal: controller.signal - }; - } - }; - const loading = (isLoading) => { - isFetching.value = isLoading; - isFinished.value = !isLoading; - }; - if (timeout) - timer = useTimeoutFn(abort, timeout, { immediate: false }); - let executeCounter = 0; - const execute = async (throwOnFailed = false) => { - var _a2, _b; - abort(); - loading(true); - error.value = null; - statusCode.value = null; - aborted.value = false; - executeCounter += 1; - const currentExecuteCounter = executeCounter; - const defaultFetchOptions = { - method: config.method, - headers: {} - }; - const payload = toValue(config.payload); - if (payload) { - const headers = headersToObject(defaultFetchOptions.headers); - const proto = Object.getPrototypeOf(payload); - if (!config.payloadType && payload && (proto === Object.prototype || Array.isArray(proto)) && !(payload instanceof FormData)) - config.payloadType = "json"; - if (config.payloadType) - headers["Content-Type"] = (_a2 = payloadMapping[config.payloadType]) != null ? _a2 : config.payloadType; - defaultFetchOptions.body = config.payloadType === "json" ? JSON.stringify(payload) : payload; - } - let isCanceled = false; - const context = { - url: toValue(url), - options: { - ...defaultFetchOptions, - ...fetchOptions - }, - cancel: () => { - isCanceled = true; - } - }; - if (options.beforeFetch) - Object.assign(context, await options.beforeFetch(context)); - if (isCanceled || !fetch) { - loading(false); - return Promise.resolve(null); - } - let responseData = null; - if (timer) - timer.start(); - return fetch( - context.url, - { - ...defaultFetchOptions, - ...context.options, - headers: { - ...headersToObject(defaultFetchOptions.headers), - ...headersToObject((_b = context.options) == null ? void 0 : _b.headers) - } - } - ).then(async (fetchResponse) => { - response.value = fetchResponse; - statusCode.value = fetchResponse.status; - responseData = await fetchResponse.clone()[config.type](); - if (!fetchResponse.ok) { - data.value = initialData || null; - throw new Error(fetchResponse.statusText); - } - if (options.afterFetch) { - ({ data: responseData } = await options.afterFetch({ - data: responseData, - response: fetchResponse, - context, - execute - })); - } - data.value = responseData; - responseEvent.trigger(fetchResponse); - return fetchResponse; - }).catch(async (fetchError) => { - let errorData = fetchError.message || fetchError.name; - if (options.onFetchError) { - ({ error: errorData, data: responseData } = await options.onFetchError({ - data: responseData, - error: fetchError, - response: response.value, - context, - execute - })); - } - error.value = errorData; - if (options.updateDataOnError) - data.value = responseData; - errorEvent.trigger(fetchError); - if (throwOnFailed) - throw fetchError; - return null; - }).finally(() => { - if (currentExecuteCounter === executeCounter) - loading(false); - if (timer) - timer.stop(); - finallyEvent.trigger(null); - }); - }; - const refetch = toRef2(options.refetch); - watch( - [ - refetch, - toRef2(url) - ], - ([refetch2]) => refetch2 && execute(), - { deep: true } - ); - const shell = { - isFinished: readonly(isFinished), - isFetching: readonly(isFetching), - statusCode, - response, - error, - data, - canAbort, - aborted, - abort, - execute, - onFetchResponse: responseEvent.on, - onFetchError: errorEvent.on, - onFetchFinally: finallyEvent.on, - // method - get: setMethod("GET"), - put: setMethod("PUT"), - post: setMethod("POST"), - delete: setMethod("DELETE"), - patch: setMethod("PATCH"), - head: setMethod("HEAD"), - options: setMethod("OPTIONS"), - // type - json: setType("json"), - text: setType("text"), - blob: setType("blob"), - arrayBuffer: setType("arrayBuffer"), - formData: setType("formData") - }; - function setMethod(method) { - return (payload, payloadType) => { - if (!isFetching.value) { - config.method = method; - config.payload = payload; - config.payloadType = payloadType; - if (isRef(config.payload)) { - watch( - [ - refetch, - toRef2(config.payload) - ], - ([refetch2]) => refetch2 && execute(), - { deep: true } - ); - } - return { - ...shell, - then(onFulfilled, onRejected) { - return waitUntilFinished().then(onFulfilled, onRejected); - } - }; - } - return void 0; - }; - } - function waitUntilFinished() { - return new Promise((resolve, reject) => { - until(isFinished).toBe(true).then(() => resolve(shell)).catch(reject); - }); - } - function setType(type) { - return () => { - if (!isFetching.value) { - config.type = type; - return { - ...shell, - then(onFulfilled, onRejected) { - return waitUntilFinished().then(onFulfilled, onRejected); - } - }; - } - return void 0; - }; - } - if (options.immediate) - Promise.resolve().then(() => execute()); - return { - ...shell, - then(onFulfilled, onRejected) { - return waitUntilFinished().then(onFulfilled, onRejected); - } - }; -} -function joinPaths(start, end) { - if (!start.endsWith("/") && !end.startsWith("/")) { - return `${start}/${end}`; - } - if (start.endsWith("/") && end.startsWith("/")) { - return `${start.slice(0, -1)}${end}`; - } - return `${start}${end}`; -} -var DEFAULT_OPTIONS = { - multiple: true, - accept: "*", - reset: false, - directory: false -}; -function prepareInitialFiles(files) { - if (!files) - return null; - if (files instanceof FileList) - return files; - const dt = new DataTransfer(); - for (const file of files) { - dt.items.add(file); - } - return dt.files; -} -function useFileDialog(options = {}) { - const { - document: document2 = defaultDocument - } = options; - const files = ref(prepareInitialFiles(options.initialFiles)); - const { on: onChange, trigger: changeTrigger } = createEventHook(); - const { on: onCancel, trigger: cancelTrigger } = createEventHook(); - let input; - if (document2) { - input = document2.createElement("input"); - input.type = "file"; - input.onchange = (event) => { - const result = event.target; - files.value = result.files; - changeTrigger(files.value); - }; - input.oncancel = () => { - cancelTrigger(); - }; - } - const reset = () => { - files.value = null; - if (input && input.value) { - input.value = ""; - changeTrigger(null); - } - }; - const open = (localOptions) => { - if (!input) - return; - const _options = { - ...DEFAULT_OPTIONS, - ...options, - ...localOptions - }; - input.multiple = _options.multiple; - input.accept = _options.accept; - input.webkitdirectory = _options.directory; - if (hasOwn(_options, "capture")) - input.capture = _options.capture; - if (_options.reset) - reset(); - input.click(); - }; - return { - files: readonly(files), - open, - reset, - onCancel, - onChange - }; -} -function useFileSystemAccess(options = {}) { - const { - window: _window = defaultWindow, - dataType = "Text" - } = options; - const window2 = _window; - const isSupported = useSupported(() => window2 && "showSaveFilePicker" in window2 && "showOpenFilePicker" in window2); - const fileHandle = shallowRef(); - const data = shallowRef(); - const file = shallowRef(); - const fileName = computed(() => { - var _a, _b; - return (_b = (_a = file.value) == null ? void 0 : _a.name) != null ? _b : ""; - }); - const fileMIME = computed(() => { - var _a, _b; - return (_b = (_a = file.value) == null ? void 0 : _a.type) != null ? _b : ""; - }); - const fileSize = computed(() => { - var _a, _b; - return (_b = (_a = file.value) == null ? void 0 : _a.size) != null ? _b : 0; - }); - const fileLastModified = computed(() => { - var _a, _b; - return (_b = (_a = file.value) == null ? void 0 : _a.lastModified) != null ? _b : 0; - }); - async function open(_options = {}) { - if (!isSupported.value) - return; - const [handle] = await window2.showOpenFilePicker({ ...toValue(options), ..._options }); - fileHandle.value = handle; - await updateData(); - } - async function create(_options = {}) { - if (!isSupported.value) - return; - fileHandle.value = await window2.showSaveFilePicker({ ...options, ..._options }); - data.value = void 0; - await updateData(); - } - async function save(_options = {}) { - if (!isSupported.value) - return; - if (!fileHandle.value) - return saveAs(_options); - if (data.value) { - const writableStream = await fileHandle.value.createWritable(); - await writableStream.write(data.value); - await writableStream.close(); - } - await updateFile(); - } - async function saveAs(_options = {}) { - if (!isSupported.value) - return; - fileHandle.value = await window2.showSaveFilePicker({ ...options, ..._options }); - if (data.value) { - const writableStream = await fileHandle.value.createWritable(); - await writableStream.write(data.value); - await writableStream.close(); - } - await updateFile(); - } - async function updateFile() { - var _a; - file.value = await ((_a = fileHandle.value) == null ? void 0 : _a.getFile()); - } - async function updateData() { - var _a, _b; - await updateFile(); - const type = toValue(dataType); - if (type === "Text") - data.value = await ((_a = file.value) == null ? void 0 : _a.text()); - else if (type === "ArrayBuffer") - data.value = await ((_b = file.value) == null ? void 0 : _b.arrayBuffer()); - else if (type === "Blob") - data.value = file.value; - } - watch(() => toValue(dataType), updateData); - return { - isSupported, - data, - file, - fileName, - fileMIME, - fileSize, - fileLastModified, - open, - create, - save, - saveAs, - updateData - }; -} -function useFocus(target, options = {}) { - const { initialValue = false, focusVisible = false, preventScroll = false } = options; - const innerFocused = shallowRef(false); - const targetElement = computed(() => unrefElement(target)); - const listenerOptions = { passive: true }; - useEventListener(targetElement, "focus", (event) => { - var _a, _b; - if (!focusVisible || ((_b = (_a = event.target).matches) == null ? void 0 : _b.call(_a, ":focus-visible"))) - innerFocused.value = true; - }, listenerOptions); - useEventListener(targetElement, "blur", () => innerFocused.value = false, listenerOptions); - const focused = computed({ - get: () => innerFocused.value, - set(value) { - var _a, _b; - if (!value && innerFocused.value) - (_a = targetElement.value) == null ? void 0 : _a.blur(); - else if (value && !innerFocused.value) - (_b = targetElement.value) == null ? void 0 : _b.focus({ preventScroll }); - } - }); - watch( - targetElement, - () => { - focused.value = initialValue; - }, - { immediate: true, flush: "post" } - ); - return { focused }; -} -var EVENT_FOCUS_IN = "focusin"; -var EVENT_FOCUS_OUT = "focusout"; -var PSEUDO_CLASS_FOCUS_WITHIN = ":focus-within"; -function useFocusWithin(target, options = {}) { - const { window: window2 = defaultWindow } = options; - const targetElement = computed(() => unrefElement(target)); - const _focused = shallowRef(false); - const focused = computed(() => _focused.value); - const activeElement = useActiveElement(options); - if (!window2 || !activeElement.value) { - return { focused }; - } - const listenerOptions = { passive: true }; - useEventListener(targetElement, EVENT_FOCUS_IN, () => _focused.value = true, listenerOptions); - useEventListener(targetElement, EVENT_FOCUS_OUT, () => { - var _a, _b, _c; - return _focused.value = (_c = (_b = (_a = targetElement.value) == null ? void 0 : _a.matches) == null ? void 0 : _b.call(_a, PSEUDO_CLASS_FOCUS_WITHIN)) != null ? _c : false; - }, listenerOptions); - return { focused }; -} -function useFps(options) { - var _a; - const fps = shallowRef(0); - if (typeof performance === "undefined") - return fps; - const every = (_a = options == null ? void 0 : options.every) != null ? _a : 10; - let last = performance.now(); - let ticks = 0; - useRafFn(() => { - ticks += 1; - if (ticks >= every) { - const now2 = performance.now(); - const diff = now2 - last; - fps.value = Math.round(1e3 / (diff / ticks)); - last = now2; - ticks = 0; - } - }); - return fps; -} -var eventHandlers = [ - "fullscreenchange", - "webkitfullscreenchange", - "webkitendfullscreen", - "mozfullscreenchange", - "MSFullscreenChange" -]; -function useFullscreen(target, options = {}) { - const { - document: document2 = defaultDocument, - autoExit = false - } = options; - const targetRef = computed(() => { - var _a; - return (_a = unrefElement(target)) != null ? _a : document2 == null ? void 0 : document2.documentElement; - }); - const isFullscreen = shallowRef(false); - const requestMethod = computed(() => { - return [ - "requestFullscreen", - "webkitRequestFullscreen", - "webkitEnterFullscreen", - "webkitEnterFullScreen", - "webkitRequestFullScreen", - "mozRequestFullScreen", - "msRequestFullscreen" - ].find((m) => document2 && m in document2 || targetRef.value && m in targetRef.value); - }); - const exitMethod = computed(() => { - return [ - "exitFullscreen", - "webkitExitFullscreen", - "webkitExitFullScreen", - "webkitCancelFullScreen", - "mozCancelFullScreen", - "msExitFullscreen" - ].find((m) => document2 && m in document2 || targetRef.value && m in targetRef.value); - }); - const fullscreenEnabled = computed(() => { - return [ - "fullScreen", - "webkitIsFullScreen", - "webkitDisplayingFullscreen", - "mozFullScreen", - "msFullscreenElement" - ].find((m) => document2 && m in document2 || targetRef.value && m in targetRef.value); - }); - const fullscreenElementMethod = [ - "fullscreenElement", - "webkitFullscreenElement", - "mozFullScreenElement", - "msFullscreenElement" - ].find((m) => document2 && m in document2); - const isSupported = useSupported(() => targetRef.value && document2 && requestMethod.value !== void 0 && exitMethod.value !== void 0 && fullscreenEnabled.value !== void 0); - const isCurrentElementFullScreen = () => { - if (fullscreenElementMethod) - return (document2 == null ? void 0 : document2[fullscreenElementMethod]) === targetRef.value; - return false; - }; - const isElementFullScreen = () => { - if (fullscreenEnabled.value) { - if (document2 && document2[fullscreenEnabled.value] != null) { - return document2[fullscreenEnabled.value]; - } else { - const target2 = targetRef.value; - if ((target2 == null ? void 0 : target2[fullscreenEnabled.value]) != null) { - return Boolean(target2[fullscreenEnabled.value]); - } - } - } - return false; - }; - async function exit() { - if (!isSupported.value || !isFullscreen.value) - return; - if (exitMethod.value) { - if ((document2 == null ? void 0 : document2[exitMethod.value]) != null) { - await document2[exitMethod.value](); - } else { - const target2 = targetRef.value; - if ((target2 == null ? void 0 : target2[exitMethod.value]) != null) - await target2[exitMethod.value](); - } - } - isFullscreen.value = false; - } - async function enter() { - if (!isSupported.value || isFullscreen.value) - return; - if (isElementFullScreen()) - await exit(); - const target2 = targetRef.value; - if (requestMethod.value && (target2 == null ? void 0 : target2[requestMethod.value]) != null) { - await target2[requestMethod.value](); - isFullscreen.value = true; - } - } - async function toggle() { - await (isFullscreen.value ? exit() : enter()); - } - const handlerCallback = () => { - const isElementFullScreenValue = isElementFullScreen(); - if (!isElementFullScreenValue || isElementFullScreenValue && isCurrentElementFullScreen()) - isFullscreen.value = isElementFullScreenValue; - }; - const listenerOptions = { capture: false, passive: true }; - useEventListener(document2, eventHandlers, handlerCallback, listenerOptions); - useEventListener(() => unrefElement(targetRef), eventHandlers, handlerCallback, listenerOptions); - if (autoExit) - tryOnScopeDispose(exit); - return { - isSupported, - isFullscreen, - enter, - exit, - toggle - }; -} -function mapGamepadToXbox360Controller(gamepad) { - return computed(() => { - if (gamepad.value) { - return { - buttons: { - a: gamepad.value.buttons[0], - b: gamepad.value.buttons[1], - x: gamepad.value.buttons[2], - y: gamepad.value.buttons[3] - }, - bumper: { - left: gamepad.value.buttons[4], - right: gamepad.value.buttons[5] - }, - triggers: { - left: gamepad.value.buttons[6], - right: gamepad.value.buttons[7] - }, - stick: { - left: { - horizontal: gamepad.value.axes[0], - vertical: gamepad.value.axes[1], - button: gamepad.value.buttons[10] - }, - right: { - horizontal: gamepad.value.axes[2], - vertical: gamepad.value.axes[3], - button: gamepad.value.buttons[11] - } - }, - dpad: { - up: gamepad.value.buttons[12], - down: gamepad.value.buttons[13], - left: gamepad.value.buttons[14], - right: gamepad.value.buttons[15] - }, - back: gamepad.value.buttons[8], - start: gamepad.value.buttons[9] - }; - } - return null; - }); -} -function useGamepad(options = {}) { - const { - navigator: navigator2 = defaultNavigator - } = options; - const isSupported = useSupported(() => navigator2 && "getGamepads" in navigator2); - const gamepads = ref([]); - const onConnectedHook = createEventHook(); - const onDisconnectedHook = createEventHook(); - const stateFromGamepad = (gamepad) => { - const hapticActuators = []; - const vibrationActuator = "vibrationActuator" in gamepad ? gamepad.vibrationActuator : null; - if (vibrationActuator) - hapticActuators.push(vibrationActuator); - if (gamepad.hapticActuators) - hapticActuators.push(...gamepad.hapticActuators); - return { - id: gamepad.id, - index: gamepad.index, - connected: gamepad.connected, - mapping: gamepad.mapping, - timestamp: gamepad.timestamp, - vibrationActuator: gamepad.vibrationActuator, - hapticActuators, - axes: gamepad.axes.map((axes) => axes), - buttons: gamepad.buttons.map((button) => ({ pressed: button.pressed, touched: button.touched, value: button.value })) - }; - }; - const updateGamepadState = () => { - const _gamepads = (navigator2 == null ? void 0 : navigator2.getGamepads()) || []; - for (const gamepad of _gamepads) { - if (gamepad && gamepads.value[gamepad.index]) - gamepads.value[gamepad.index] = stateFromGamepad(gamepad); - } - }; - const { isActive, pause, resume } = useRafFn(updateGamepadState); - const onGamepadConnected = (gamepad) => { - if (!gamepads.value.some(({ index }) => index === gamepad.index)) { - gamepads.value.push(stateFromGamepad(gamepad)); - onConnectedHook.trigger(gamepad.index); - } - resume(); - }; - const onGamepadDisconnected = (gamepad) => { - gamepads.value = gamepads.value.filter((x) => x.index !== gamepad.index); - onDisconnectedHook.trigger(gamepad.index); - }; - const listenerOptions = { passive: true }; - useEventListener("gamepadconnected", (e) => onGamepadConnected(e.gamepad), listenerOptions); - useEventListener("gamepaddisconnected", (e) => onGamepadDisconnected(e.gamepad), listenerOptions); - tryOnMounted(() => { - const _gamepads = (navigator2 == null ? void 0 : navigator2.getGamepads()) || []; - for (const gamepad of _gamepads) { - if (gamepad && gamepads.value[gamepad.index]) - onGamepadConnected(gamepad); - } - }); - pause(); - return { - isSupported, - onConnected: onConnectedHook.on, - onDisconnected: onDisconnectedHook.on, - gamepads, - pause, - resume, - isActive - }; -} -function useGeolocation(options = {}) { - const { - enableHighAccuracy = true, - maximumAge = 3e4, - timeout = 27e3, - navigator: navigator2 = defaultNavigator, - immediate = true - } = options; - const isSupported = useSupported(() => navigator2 && "geolocation" in navigator2); - const locatedAt = shallowRef(null); - const error = shallowRef(null); - const coords = ref({ - accuracy: 0, - latitude: Number.POSITIVE_INFINITY, - longitude: Number.POSITIVE_INFINITY, - altitude: null, - altitudeAccuracy: null, - heading: null, - speed: null - }); - function updatePosition(position) { - locatedAt.value = position.timestamp; - coords.value = position.coords; - error.value = null; - } - let watcher; - function resume() { - if (isSupported.value) { - watcher = navigator2.geolocation.watchPosition( - updatePosition, - (err) => error.value = err, - { - enableHighAccuracy, - maximumAge, - timeout - } - ); - } - } - if (immediate) - resume(); - function pause() { - if (watcher && navigator2) - navigator2.geolocation.clearWatch(watcher); - } - tryOnScopeDispose(() => { - pause(); - }); - return { - isSupported, - coords, - locatedAt, - error, - resume, - pause - }; -} -var defaultEvents$1 = ["mousemove", "mousedown", "resize", "keydown", "touchstart", "wheel"]; -var oneMinute = 6e4; -function useIdle(timeout = oneMinute, options = {}) { - const { - initialState = false, - listenForVisibilityChange = true, - events: events2 = defaultEvents$1, - window: window2 = defaultWindow, - eventFilter = throttleFilter(50) - } = options; - const idle = shallowRef(initialState); - const lastActive = shallowRef(timestamp()); - let timer; - const reset = () => { - idle.value = false; - clearTimeout(timer); - timer = setTimeout(() => idle.value = true, timeout); - }; - const onEvent = createFilterWrapper( - eventFilter, - () => { - lastActive.value = timestamp(); - reset(); - } - ); - if (window2) { - const document2 = window2.document; - const listenerOptions = { passive: true }; - for (const event of events2) - useEventListener(window2, event, onEvent, listenerOptions); - if (listenForVisibilityChange) { - useEventListener(document2, "visibilitychange", () => { - if (!document2.hidden) - onEvent(); - }, listenerOptions); - } - reset(); - } - return { - idle, - lastActive, - reset - }; -} -async function loadImage(options) { - return new Promise((resolve, reject) => { - const img = new Image(); - const { src, srcset, sizes, class: clazz, loading, crossorigin, referrerPolicy, width, height, decoding, fetchPriority, ismap, usemap } = options; - img.src = src; - if (srcset != null) - img.srcset = srcset; - if (sizes != null) - img.sizes = sizes; - if (clazz != null) - img.className = clazz; - if (loading != null) - img.loading = loading; - if (crossorigin != null) - img.crossOrigin = crossorigin; - if (referrerPolicy != null) - img.referrerPolicy = referrerPolicy; - if (width != null) - img.width = width; - if (height != null) - img.height = height; - if (decoding != null) - img.decoding = decoding; - if (fetchPriority != null) - img.fetchPriority = fetchPriority; - if (ismap != null) - img.isMap = ismap; - if (usemap != null) - img.useMap = usemap; - img.onload = () => resolve(img); - img.onerror = reject; - }); -} -function useImage(options, asyncStateOptions = {}) { - const state = useAsyncState( - () => loadImage(toValue(options)), - void 0, - { - resetOnExecute: true, - ...asyncStateOptions - } - ); - watch( - () => toValue(options), - () => state.execute(asyncStateOptions.delay), - { deep: true } - ); - return state; -} -function resolveElement(el) { - if (typeof Window !== "undefined" && el instanceof Window) - return el.document.documentElement; - if (typeof Document !== "undefined" && el instanceof Document) - return el.documentElement; - return el; -} -var ARRIVED_STATE_THRESHOLD_PIXELS = 1; -function useScroll(element, options = {}) { - const { - throttle = 0, - idle = 200, - onStop = noop, - onScroll = noop, - offset = { - left: 0, - right: 0, - top: 0, - bottom: 0 - }, - eventListenerOptions = { - capture: false, - passive: true - }, - behavior = "auto", - window: window2 = defaultWindow, - onError = (e) => { - console.error(e); - } - } = options; - const internalX = shallowRef(0); - const internalY = shallowRef(0); - const x = computed({ - get() { - return internalX.value; - }, - set(x2) { - scrollTo(x2, void 0); - } - }); - const y = computed({ - get() { - return internalY.value; - }, - set(y2) { - scrollTo(void 0, y2); - } - }); - function scrollTo(_x, _y) { - var _a, _b, _c, _d; - if (!window2) - return; - const _element = toValue(element); - if (!_element) - return; - (_c = _element instanceof Document ? window2.document.body : _element) == null ? void 0 : _c.scrollTo({ - top: (_a = toValue(_y)) != null ? _a : y.value, - left: (_b = toValue(_x)) != null ? _b : x.value, - behavior: toValue(behavior) - }); - const scrollContainer = ((_d = _element == null ? void 0 : _element.document) == null ? void 0 : _d.documentElement) || (_element == null ? void 0 : _element.documentElement) || _element; - if (x != null) - internalX.value = scrollContainer.scrollLeft; - if (y != null) - internalY.value = scrollContainer.scrollTop; - } - const isScrolling = shallowRef(false); - const arrivedState = reactive({ - left: true, - right: false, - top: true, - bottom: false - }); - const directions = reactive({ - left: false, - right: false, - top: false, - bottom: false - }); - const onScrollEnd = (e) => { - if (!isScrolling.value) - return; - isScrolling.value = false; - directions.left = false; - directions.right = false; - directions.top = false; - directions.bottom = false; - onStop(e); - }; - const onScrollEndDebounced = useDebounceFn(onScrollEnd, throttle + idle); - const setArrivedState = (target) => { - var _a; - if (!window2) - return; - const el = ((_a = target == null ? void 0 : target.document) == null ? void 0 : _a.documentElement) || (target == null ? void 0 : target.documentElement) || unrefElement(target); - const { display, flexDirection, direction } = getComputedStyle(el); - const directionMultipler = direction === "rtl" ? -1 : 1; - const scrollLeft = el.scrollLeft; - directions.left = scrollLeft < internalX.value; - directions.right = scrollLeft > internalX.value; - const left = Math.abs(scrollLeft * directionMultipler) <= (offset.left || 0); - const right = Math.abs(scrollLeft * directionMultipler) + el.clientWidth >= el.scrollWidth - (offset.right || 0) - ARRIVED_STATE_THRESHOLD_PIXELS; - if (display === "flex" && flexDirection === "row-reverse") { - arrivedState.left = right; - arrivedState.right = left; - } else { - arrivedState.left = left; - arrivedState.right = right; - } - internalX.value = scrollLeft; - let scrollTop = el.scrollTop; - if (target === window2.document && !scrollTop) - scrollTop = window2.document.body.scrollTop; - directions.top = scrollTop < internalY.value; - directions.bottom = scrollTop > internalY.value; - const top = Math.abs(scrollTop) <= (offset.top || 0); - const bottom = Math.abs(scrollTop) + el.clientHeight >= el.scrollHeight - (offset.bottom || 0) - ARRIVED_STATE_THRESHOLD_PIXELS; - if (display === "flex" && flexDirection === "column-reverse") { - arrivedState.top = bottom; - arrivedState.bottom = top; - } else { - arrivedState.top = top; - arrivedState.bottom = bottom; - } - internalY.value = scrollTop; - }; - const onScrollHandler = (e) => { - var _a; - if (!window2) - return; - const eventTarget = (_a = e.target.documentElement) != null ? _a : e.target; - setArrivedState(eventTarget); - isScrolling.value = true; - onScrollEndDebounced(e); - onScroll(e); - }; - useEventListener( - element, - "scroll", - throttle ? useThrottleFn(onScrollHandler, throttle, true, false) : onScrollHandler, - eventListenerOptions - ); - tryOnMounted(() => { - try { - const _element = toValue(element); - if (!_element) - return; - setArrivedState(_element); - } catch (e) { - onError(e); - } - }); - useEventListener( - element, - "scrollend", - onScrollEnd, - eventListenerOptions - ); - return { - x, - y, - isScrolling, - arrivedState, - directions, - measure() { - const _element = toValue(element); - if (window2 && _element) - setArrivedState(_element); - } - }; -} -function useInfiniteScroll(element, onLoadMore, options = {}) { - var _a; - const { - direction = "bottom", - interval = 100, - canLoadMore = () => true - } = options; - const state = reactive(useScroll( - element, - { - ...options, - offset: { - [direction]: (_a = options.distance) != null ? _a : 0, - ...options.offset - } - } - )); - const promise = ref(); - const isLoading = computed(() => !!promise.value); - const observedElement = computed(() => { - return resolveElement(toValue(element)); - }); - const isElementVisible = useElementVisibility(observedElement); - function checkAndLoad() { - state.measure(); - if (!observedElement.value || !isElementVisible.value || !canLoadMore(observedElement.value)) - return; - const { scrollHeight, clientHeight, scrollWidth, clientWidth } = observedElement.value; - const isNarrower = direction === "bottom" || direction === "top" ? scrollHeight <= clientHeight : scrollWidth <= clientWidth; - if (state.arrivedState[direction] || isNarrower) { - if (!promise.value) { - promise.value = Promise.all([ - onLoadMore(state), - new Promise((resolve) => setTimeout(resolve, interval)) - ]).finally(() => { - promise.value = null; - nextTick(() => checkAndLoad()); - }); - } - } - } - const stop = watch( - () => [state.arrivedState[direction], isElementVisible.value], - checkAndLoad, - { immediate: true } - ); - tryOnUnmounted(stop); - return { - isLoading, - reset() { - nextTick(() => checkAndLoad()); - } - }; -} -var defaultEvents = ["mousedown", "mouseup", "keydown", "keyup"]; -function useKeyModifier(modifier, options = {}) { - const { - events: events2 = defaultEvents, - document: document2 = defaultDocument, - initial = null - } = options; - const state = shallowRef(initial); - if (document2) { - events2.forEach((listenerEvent) => { - useEventListener(document2, listenerEvent, (evt) => { - if (typeof evt.getModifierState === "function") - state.value = evt.getModifierState(modifier); - }, { passive: true }); - }); - } - return state; -} -function useLocalStorage(key, initialValue, options = {}) { - const { window: window2 = defaultWindow } = options; - return useStorage(key, initialValue, window2 == null ? void 0 : window2.localStorage, options); -} -var DefaultMagicKeysAliasMap = { - ctrl: "control", - command: "meta", - cmd: "meta", - option: "alt", - up: "arrowup", - down: "arrowdown", - left: "arrowleft", - right: "arrowright" -}; -function useMagicKeys(options = {}) { - const { - reactive: useReactive = false, - target = defaultWindow, - aliasMap = DefaultMagicKeysAliasMap, - passive = true, - onEventFired = noop - } = options; - const current = reactive(/* @__PURE__ */ new Set()); - const obj = { - toJSON() { - return {}; - }, - current - }; - const refs = useReactive ? reactive(obj) : obj; - const metaDeps = /* @__PURE__ */ new Set(); - const usedKeys = /* @__PURE__ */ new Set(); - function setRefs(key, value) { - if (key in refs) { - if (useReactive) - refs[key] = value; - else - refs[key].value = value; - } - } - function reset() { - current.clear(); - for (const key of usedKeys) - setRefs(key, false); - } - function updateRefs(e, value) { - var _a, _b; - const key = (_a = e.key) == null ? void 0 : _a.toLowerCase(); - const code = (_b = e.code) == null ? void 0 : _b.toLowerCase(); - const values = [code, key].filter(Boolean); - if (key) { - if (value) - current.add(key); - else - current.delete(key); - } - for (const key2 of values) { - usedKeys.add(key2); - setRefs(key2, value); - } - if (key === "meta" && !value) { - metaDeps.forEach((key2) => { - current.delete(key2); - setRefs(key2, false); - }); - metaDeps.clear(); - } else if (typeof e.getModifierState === "function" && e.getModifierState("Meta") && value) { - [...current, ...values].forEach((key2) => metaDeps.add(key2)); - } - } - useEventListener(target, "keydown", (e) => { - updateRefs(e, true); - return onEventFired(e); - }, { passive }); - useEventListener(target, "keyup", (e) => { - updateRefs(e, false); - return onEventFired(e); - }, { passive }); - useEventListener("blur", reset, { passive }); - useEventListener("focus", reset, { passive }); - const proxy = new Proxy( - refs, - { - get(target2, prop, rec) { - if (typeof prop !== "string") - return Reflect.get(target2, prop, rec); - prop = prop.toLowerCase(); - if (prop in aliasMap) - prop = aliasMap[prop]; - if (!(prop in refs)) { - if (/[+_-]/.test(prop)) { - const keys2 = prop.split(/[+_-]/g).map((i) => i.trim()); - refs[prop] = computed(() => keys2.map((key) => toValue(proxy[key])).every(Boolean)); - } else { - refs[prop] = shallowRef(false); - } - } - const r = Reflect.get(target2, prop, rec); - return useReactive ? toValue(r) : r; - } - } - ); - return proxy; -} -function usingElRef(source, cb) { - if (toValue(source)) - cb(toValue(source)); -} -function timeRangeToArray(timeRanges) { - let ranges = []; - for (let i = 0; i < timeRanges.length; ++i) - ranges = [...ranges, [timeRanges.start(i), timeRanges.end(i)]]; - return ranges; -} -function tracksToArray(tracks) { - return Array.from(tracks).map(({ label, kind, language, mode, activeCues, cues, inBandMetadataTrackDispatchType }, id) => ({ id, label, kind, language, mode, activeCues, cues, inBandMetadataTrackDispatchType })); -} -var defaultOptions = { - src: "", - tracks: [] -}; -function useMediaControls(target, options = {}) { - target = toRef2(target); - options = { - ...defaultOptions, - ...options - }; - const { - document: document2 = defaultDocument - } = options; - const listenerOptions = { passive: true }; - const currentTime = shallowRef(0); - const duration = shallowRef(0); - const seeking = shallowRef(false); - const volume = shallowRef(1); - const waiting = shallowRef(false); - const ended = shallowRef(false); - const playing = shallowRef(false); - const rate = shallowRef(1); - const stalled = shallowRef(false); - const buffered = ref([]); - const tracks = ref([]); - const selectedTrack = shallowRef(-1); - const isPictureInPicture = shallowRef(false); - const muted = shallowRef(false); - const supportsPictureInPicture = document2 && "pictureInPictureEnabled" in document2; - const sourceErrorEvent = createEventHook(); - const playbackErrorEvent = createEventHook(); - const disableTrack = (track) => { - usingElRef(target, (el) => { - if (track) { - const id = typeof track === "number" ? track : track.id; - el.textTracks[id].mode = "disabled"; - } else { - for (let i = 0; i < el.textTracks.length; ++i) - el.textTracks[i].mode = "disabled"; - } - selectedTrack.value = -1; - }); - }; - const enableTrack = (track, disableTracks = true) => { - usingElRef(target, (el) => { - const id = typeof track === "number" ? track : track.id; - if (disableTracks) - disableTrack(); - el.textTracks[id].mode = "showing"; - selectedTrack.value = id; - }); - }; - const togglePictureInPicture = () => { - return new Promise((resolve, reject) => { - usingElRef(target, async (el) => { - if (supportsPictureInPicture) { - if (!isPictureInPicture.value) { - el.requestPictureInPicture().then(resolve).catch(reject); - } else { - document2.exitPictureInPicture().then(resolve).catch(reject); - } - } - }); - }); - }; - watchEffect(() => { - if (!document2) - return; - const el = toValue(target); - if (!el) - return; - const src = toValue(options.src); - let sources = []; - if (!src) - return; - if (typeof src === "string") - sources = [{ src }]; - else if (Array.isArray(src)) - sources = src; - else if (isObject(src)) - sources = [src]; - el.querySelectorAll("source").forEach((e) => { - e.remove(); - }); - sources.forEach(({ src: src2, type, media }) => { - const source = document2.createElement("source"); - source.setAttribute("src", src2); - source.setAttribute("type", type || ""); - source.setAttribute("media", media || ""); - useEventListener(source, "error", sourceErrorEvent.trigger, listenerOptions); - el.appendChild(source); - }); - el.load(); - }); - watch([target, volume], () => { - const el = toValue(target); - if (!el) - return; - el.volume = volume.value; - }); - watch([target, muted], () => { - const el = toValue(target); - if (!el) - return; - el.muted = muted.value; - }); - watch([target, rate], () => { - const el = toValue(target); - if (!el) - return; - el.playbackRate = rate.value; - }); - watchEffect(() => { - if (!document2) - return; - const textTracks = toValue(options.tracks); - const el = toValue(target); - if (!textTracks || !textTracks.length || !el) - return; - el.querySelectorAll("track").forEach((e) => e.remove()); - textTracks.forEach(({ default: isDefault, kind, label, src, srcLang }, i) => { - const track = document2.createElement("track"); - track.default = isDefault || false; - track.kind = kind; - track.label = label; - track.src = src; - track.srclang = srcLang; - if (track.default) - selectedTrack.value = i; - el.appendChild(track); - }); - }); - const { ignoreUpdates: ignoreCurrentTimeUpdates } = watchIgnorable(currentTime, (time) => { - const el = toValue(target); - if (!el) - return; - el.currentTime = time; - }); - const { ignoreUpdates: ignorePlayingUpdates } = watchIgnorable(playing, (isPlaying) => { - const el = toValue(target); - if (!el) - return; - if (isPlaying) { - el.play().catch((e) => { - playbackErrorEvent.trigger(e); - throw e; - }); - } else { - el.pause(); - } - }); - useEventListener( - target, - "timeupdate", - () => ignoreCurrentTimeUpdates(() => currentTime.value = toValue(target).currentTime), - listenerOptions - ); - useEventListener( - target, - "durationchange", - () => duration.value = toValue(target).duration, - listenerOptions - ); - useEventListener( - target, - "progress", - () => buffered.value = timeRangeToArray(toValue(target).buffered), - listenerOptions - ); - useEventListener( - target, - "seeking", - () => seeking.value = true, - listenerOptions - ); - useEventListener( - target, - "seeked", - () => seeking.value = false, - listenerOptions - ); - useEventListener( - target, - ["waiting", "loadstart"], - () => { - waiting.value = true; - ignorePlayingUpdates(() => playing.value = false); - }, - listenerOptions - ); - useEventListener( - target, - "loadeddata", - () => waiting.value = false, - listenerOptions - ); - useEventListener( - target, - "playing", - () => { - waiting.value = false; - ended.value = false; - ignorePlayingUpdates(() => playing.value = true); - }, - listenerOptions - ); - useEventListener( - target, - "ratechange", - () => rate.value = toValue(target).playbackRate, - listenerOptions - ); - useEventListener( - target, - "stalled", - () => stalled.value = true, - listenerOptions - ); - useEventListener( - target, - "ended", - () => ended.value = true, - listenerOptions - ); - useEventListener( - target, - "pause", - () => ignorePlayingUpdates(() => playing.value = false), - listenerOptions - ); - useEventListener( - target, - "play", - () => ignorePlayingUpdates(() => playing.value = true), - listenerOptions - ); - useEventListener( - target, - "enterpictureinpicture", - () => isPictureInPicture.value = true, - listenerOptions - ); - useEventListener( - target, - "leavepictureinpicture", - () => isPictureInPicture.value = false, - listenerOptions - ); - useEventListener( - target, - "volumechange", - () => { - const el = toValue(target); - if (!el) - return; - volume.value = el.volume; - muted.value = el.muted; - }, - listenerOptions - ); - const listeners = []; - const stop = watch([target], () => { - const el = toValue(target); - if (!el) - return; - stop(); - listeners[0] = useEventListener(el.textTracks, "addtrack", () => tracks.value = tracksToArray(el.textTracks), listenerOptions); - listeners[1] = useEventListener(el.textTracks, "removetrack", () => tracks.value = tracksToArray(el.textTracks), listenerOptions); - listeners[2] = useEventListener(el.textTracks, "change", () => tracks.value = tracksToArray(el.textTracks), listenerOptions); - }); - tryOnScopeDispose(() => listeners.forEach((listener) => listener())); - return { - currentTime, - duration, - waiting, - seeking, - ended, - stalled, - buffered, - playing, - rate, - // Volume - volume, - muted, - // Tracks - tracks, - selectedTrack, - enableTrack, - disableTrack, - // Picture in Picture - supportsPictureInPicture, - togglePictureInPicture, - isPictureInPicture, - // Events - onSourceError: sourceErrorEvent.on, - onPlaybackError: playbackErrorEvent.on - }; -} -function useMemoize(resolver, options) { - const initCache = () => { - if (options == null ? void 0 : options.cache) - return shallowReactive(options.cache); - return shallowReactive(/* @__PURE__ */ new Map()); - }; - const cache = initCache(); - const generateKey = (...args) => (options == null ? void 0 : options.getKey) ? options.getKey(...args) : JSON.stringify(args); - const _loadData = (key, ...args) => { - cache.set(key, resolver(...args)); - return cache.get(key); - }; - const loadData = (...args) => _loadData(generateKey(...args), ...args); - const deleteData = (...args) => { - cache.delete(generateKey(...args)); - }; - const clearData = () => { - cache.clear(); - }; - const memoized = (...args) => { - const key = generateKey(...args); - if (cache.has(key)) - return cache.get(key); - return _loadData(key, ...args); - }; - memoized.load = loadData; - memoized.delete = deleteData; - memoized.clear = clearData; - memoized.generateKey = generateKey; - memoized.cache = cache; - return memoized; -} -function useMemory(options = {}) { - const memory = ref(); - const isSupported = useSupported(() => typeof performance !== "undefined" && "memory" in performance); - if (isSupported.value) { - const { interval = 1e3 } = options; - useIntervalFn(() => { - memory.value = performance.memory; - }, interval, { immediate: options.immediate, immediateCallback: options.immediateCallback }); - } - return { isSupported, memory }; -} -var UseMouseBuiltinExtractors = { - page: (event) => [event.pageX, event.pageY], - client: (event) => [event.clientX, event.clientY], - screen: (event) => [event.screenX, event.screenY], - movement: (event) => event instanceof MouseEvent ? [event.movementX, event.movementY] : null -}; -function useMouse(options = {}) { - const { - type = "page", - touch = true, - resetOnTouchEnds = false, - initialValue = { x: 0, y: 0 }, - window: window2 = defaultWindow, - target = window2, - scroll = true, - eventFilter - } = options; - let _prevMouseEvent = null; - let _prevScrollX = 0; - let _prevScrollY = 0; - const x = shallowRef(initialValue.x); - const y = shallowRef(initialValue.y); - const sourceType = shallowRef(null); - const extractor = typeof type === "function" ? type : UseMouseBuiltinExtractors[type]; - const mouseHandler = (event) => { - const result = extractor(event); - _prevMouseEvent = event; - if (result) { - [x.value, y.value] = result; - sourceType.value = "mouse"; - } - if (window2) { - _prevScrollX = window2.scrollX; - _prevScrollY = window2.scrollY; - } - }; - const touchHandler = (event) => { - if (event.touches.length > 0) { - const result = extractor(event.touches[0]); - if (result) { - [x.value, y.value] = result; - sourceType.value = "touch"; - } - } - }; - const scrollHandler = () => { - if (!_prevMouseEvent || !window2) - return; - const pos = extractor(_prevMouseEvent); - if (_prevMouseEvent instanceof MouseEvent && pos) { - x.value = pos[0] + window2.scrollX - _prevScrollX; - y.value = pos[1] + window2.scrollY - _prevScrollY; - } - }; - const reset = () => { - x.value = initialValue.x; - y.value = initialValue.y; - }; - const mouseHandlerWrapper = eventFilter ? (event) => eventFilter(() => mouseHandler(event), {}) : (event) => mouseHandler(event); - const touchHandlerWrapper = eventFilter ? (event) => eventFilter(() => touchHandler(event), {}) : (event) => touchHandler(event); - const scrollHandlerWrapper = eventFilter ? () => eventFilter(() => scrollHandler(), {}) : () => scrollHandler(); - if (target) { - const listenerOptions = { passive: true }; - useEventListener(target, ["mousemove", "dragover"], mouseHandlerWrapper, listenerOptions); - if (touch && type !== "movement") { - useEventListener(target, ["touchstart", "touchmove"], touchHandlerWrapper, listenerOptions); - if (resetOnTouchEnds) - useEventListener(target, "touchend", reset, listenerOptions); - } - if (scroll && type === "page") - useEventListener(window2, "scroll", scrollHandlerWrapper, listenerOptions); - } - return { - x, - y, - sourceType - }; -} -function useMouseInElement(target, options = {}) { - const { - handleOutside = true, - window: window2 = defaultWindow - } = options; - const type = options.type || "page"; - const { x, y, sourceType } = useMouse(options); - const targetRef = shallowRef(target != null ? target : window2 == null ? void 0 : window2.document.body); - const elementX = shallowRef(0); - const elementY = shallowRef(0); - const elementPositionX = shallowRef(0); - const elementPositionY = shallowRef(0); - const elementHeight = shallowRef(0); - const elementWidth = shallowRef(0); - const isOutside = shallowRef(true); - let stop = () => { - }; - if (window2) { - stop = watch( - [targetRef, x, y], - () => { - const el = unrefElement(targetRef); - if (!el || !(el instanceof Element)) - return; - const { - left, - top, - width, - height - } = el.getBoundingClientRect(); - elementPositionX.value = left + (type === "page" ? window2.pageXOffset : 0); - elementPositionY.value = top + (type === "page" ? window2.pageYOffset : 0); - elementHeight.value = height; - elementWidth.value = width; - const elX = x.value - elementPositionX.value; - const elY = y.value - elementPositionY.value; - isOutside.value = width === 0 || height === 0 || elX < 0 || elY < 0 || elX > width || elY > height; - if (handleOutside || !isOutside.value) { - elementX.value = elX; - elementY.value = elY; - } - }, - { immediate: true } - ); - useEventListener( - document, - "mouseleave", - () => isOutside.value = true, - { passive: true } - ); - } - return { - x, - y, - sourceType, - elementX, - elementY, - elementPositionX, - elementPositionY, - elementHeight, - elementWidth, - isOutside, - stop - }; -} -function useMousePressed(options = {}) { - const { - touch = true, - drag = true, - capture = false, - initialValue = false, - window: window2 = defaultWindow - } = options; - const pressed = shallowRef(initialValue); - const sourceType = shallowRef(null); - if (!window2) { - return { - pressed, - sourceType - }; - } - const onPressed = (srcType) => (event) => { - var _a; - pressed.value = true; - sourceType.value = srcType; - (_a = options.onPressed) == null ? void 0 : _a.call(options, event); - }; - const onReleased = (event) => { - var _a; - pressed.value = false; - sourceType.value = null; - (_a = options.onReleased) == null ? void 0 : _a.call(options, event); - }; - const target = computed(() => unrefElement(options.target) || window2); - const listenerOptions = { passive: true, capture }; - useEventListener(target, "mousedown", onPressed("mouse"), listenerOptions); - useEventListener(window2, "mouseleave", onReleased, listenerOptions); - useEventListener(window2, "mouseup", onReleased, listenerOptions); - if (drag) { - useEventListener(target, "dragstart", onPressed("mouse"), listenerOptions); - useEventListener(window2, "drop", onReleased, listenerOptions); - useEventListener(window2, "dragend", onReleased, listenerOptions); - } - if (touch) { - useEventListener(target, "touchstart", onPressed("touch"), listenerOptions); - useEventListener(window2, "touchend", onReleased, listenerOptions); - useEventListener(window2, "touchcancel", onReleased, listenerOptions); - } - return { - pressed, - sourceType - }; -} -function useNavigatorLanguage(options = {}) { - const { window: window2 = defaultWindow } = options; - const navigator2 = window2 == null ? void 0 : window2.navigator; - const isSupported = useSupported(() => navigator2 && "language" in navigator2); - const language = shallowRef(navigator2 == null ? void 0 : navigator2.language); - useEventListener(window2, "languagechange", () => { - if (navigator2) - language.value = navigator2.language; - }, { passive: true }); - return { - isSupported, - language - }; -} -function useNetwork(options = {}) { - const { window: window2 = defaultWindow } = options; - const navigator2 = window2 == null ? void 0 : window2.navigator; - const isSupported = useSupported(() => navigator2 && "connection" in navigator2); - const isOnline = shallowRef(true); - const saveData = shallowRef(false); - const offlineAt = shallowRef(void 0); - const onlineAt = shallowRef(void 0); - const downlink = shallowRef(void 0); - const downlinkMax = shallowRef(void 0); - const rtt = shallowRef(void 0); - const effectiveType = shallowRef(void 0); - const type = shallowRef("unknown"); - const connection = isSupported.value && navigator2.connection; - function updateNetworkInformation() { - if (!navigator2) - return; - isOnline.value = navigator2.onLine; - offlineAt.value = isOnline.value ? void 0 : Date.now(); - onlineAt.value = isOnline.value ? Date.now() : void 0; - if (connection) { - downlink.value = connection.downlink; - downlinkMax.value = connection.downlinkMax; - effectiveType.value = connection.effectiveType; - rtt.value = connection.rtt; - saveData.value = connection.saveData; - type.value = connection.type; - } - } - const listenerOptions = { passive: true }; - if (window2) { - useEventListener(window2, "offline", () => { - isOnline.value = false; - offlineAt.value = Date.now(); - }, listenerOptions); - useEventListener(window2, "online", () => { - isOnline.value = true; - onlineAt.value = Date.now(); - }, listenerOptions); - } - if (connection) - useEventListener(connection, "change", updateNetworkInformation, listenerOptions); - updateNetworkInformation(); - return { - isSupported, - isOnline: readonly(isOnline), - saveData: readonly(saveData), - offlineAt: readonly(offlineAt), - onlineAt: readonly(onlineAt), - downlink: readonly(downlink), - downlinkMax: readonly(downlinkMax), - effectiveType: readonly(effectiveType), - rtt: readonly(rtt), - type: readonly(type) - }; -} -function useNow(options = {}) { - const { - controls: exposeControls = false, - interval = "requestAnimationFrame" - } = options; - const now2 = ref(/* @__PURE__ */ new Date()); - const update = () => now2.value = /* @__PURE__ */ new Date(); - const controls = interval === "requestAnimationFrame" ? useRafFn(update, { immediate: true }) : useIntervalFn(update, interval, { immediate: true }); - if (exposeControls) { - return { - now: now2, - ...controls - }; - } else { - return now2; - } -} -function useObjectUrl(object) { - const url = shallowRef(); - const release = () => { - if (url.value) - URL.revokeObjectURL(url.value); - url.value = void 0; - }; - watch( - () => toValue(object), - (newObject) => { - release(); - if (newObject) - url.value = URL.createObjectURL(newObject); - }, - { immediate: true } - ); - tryOnScopeDispose(release); - return readonly(url); -} -function useClamp(value, min, max) { - if (typeof value === "function" || isReadonly(value)) - return computed(() => clamp(toValue(value), toValue(min), toValue(max))); - const _value = ref(value); - return computed({ - get() { - return _value.value = clamp(_value.value, toValue(min), toValue(max)); - }, - set(value2) { - _value.value = clamp(value2, toValue(min), toValue(max)); - } - }); -} -function useOffsetPagination(options) { - const { - total = Number.POSITIVE_INFINITY, - pageSize = 10, - page = 1, - onPageChange = noop, - onPageSizeChange = noop, - onPageCountChange = noop - } = options; - const currentPageSize = useClamp(pageSize, 1, Number.POSITIVE_INFINITY); - const pageCount = computed(() => Math.max( - 1, - Math.ceil(toValue(total) / toValue(currentPageSize)) - )); - const currentPage = useClamp(page, 1, pageCount); - const isFirstPage = computed(() => currentPage.value === 1); - const isLastPage = computed(() => currentPage.value === pageCount.value); - if (isRef(page)) { - syncRef(page, currentPage, { - direction: isReadonly(page) ? "ltr" : "both" - }); - } - if (isRef(pageSize)) { - syncRef(pageSize, currentPageSize, { - direction: isReadonly(pageSize) ? "ltr" : "both" - }); - } - function prev() { - currentPage.value--; - } - function next() { - currentPage.value++; - } - const returnValue = { - currentPage, - currentPageSize, - pageCount, - isFirstPage, - isLastPage, - prev, - next - }; - watch(currentPage, () => { - onPageChange(reactive(returnValue)); - }); - watch(currentPageSize, () => { - onPageSizeChange(reactive(returnValue)); - }); - watch(pageCount, () => { - onPageCountChange(reactive(returnValue)); - }); - return returnValue; -} -function useOnline(options = {}) { - const { isOnline } = useNetwork(options); - return isOnline; -} -function usePageLeave(options = {}) { - const { window: window2 = defaultWindow } = options; - const isLeft = shallowRef(false); - const handler = (event) => { - if (!window2) - return; - event = event || window2.event; - const from = event.relatedTarget || event.toElement; - isLeft.value = !from; - }; - if (window2) { - const listenerOptions = { passive: true }; - useEventListener(window2, "mouseout", handler, listenerOptions); - useEventListener(window2.document, "mouseleave", handler, listenerOptions); - useEventListener(window2.document, "mouseenter", handler, listenerOptions); - } - return isLeft; -} -function useScreenOrientation(options = {}) { - const { - window: window2 = defaultWindow - } = options; - const isSupported = useSupported(() => window2 && "screen" in window2 && "orientation" in window2.screen); - const screenOrientation = isSupported.value ? window2.screen.orientation : {}; - const orientation = ref(screenOrientation.type); - const angle = shallowRef(screenOrientation.angle || 0); - if (isSupported.value) { - useEventListener(window2, "orientationchange", () => { - orientation.value = screenOrientation.type; - angle.value = screenOrientation.angle; - }, { passive: true }); - } - const lockOrientation = (type) => { - if (isSupported.value && typeof screenOrientation.lock === "function") - return screenOrientation.lock(type); - return Promise.reject(new Error("Not supported")); - }; - const unlockOrientation = () => { - if (isSupported.value && typeof screenOrientation.unlock === "function") - screenOrientation.unlock(); - }; - return { - isSupported, - orientation, - angle, - lockOrientation, - unlockOrientation - }; -} -function useParallax(target, options = {}) { - const { - deviceOrientationTiltAdjust = (i) => i, - deviceOrientationRollAdjust = (i) => i, - mouseTiltAdjust = (i) => i, - mouseRollAdjust = (i) => i, - window: window2 = defaultWindow - } = options; - const orientation = reactive(useDeviceOrientation({ window: window2 })); - const screenOrientation = reactive(useScreenOrientation({ window: window2 })); - const { - elementX: x, - elementY: y, - elementWidth: width, - elementHeight: height - } = useMouseInElement(target, { handleOutside: false, window: window2 }); - const source = computed(() => { - if (orientation.isSupported && (orientation.alpha != null && orientation.alpha !== 0 || orientation.gamma != null && orientation.gamma !== 0)) { - return "deviceOrientation"; - } - return "mouse"; - }); - const roll = computed(() => { - if (source.value === "deviceOrientation") { - let value; - switch (screenOrientation.orientation) { - case "landscape-primary": - value = orientation.gamma / 90; - break; - case "landscape-secondary": - value = -orientation.gamma / 90; - break; - case "portrait-primary": - value = -orientation.beta / 90; - break; - case "portrait-secondary": - value = orientation.beta / 90; - break; - default: - value = -orientation.beta / 90; - } - return deviceOrientationRollAdjust(value); - } else { - const value = -(y.value - height.value / 2) / height.value; - return mouseRollAdjust(value); - } - }); - const tilt = computed(() => { - if (source.value === "deviceOrientation") { - let value; - switch (screenOrientation.orientation) { - case "landscape-primary": - value = orientation.beta / 90; - break; - case "landscape-secondary": - value = -orientation.beta / 90; - break; - case "portrait-primary": - value = orientation.gamma / 90; - break; - case "portrait-secondary": - value = -orientation.gamma / 90; - break; - default: - value = orientation.gamma / 90; - } - return deviceOrientationTiltAdjust(value); - } else { - const value = (x.value - width.value / 2) / width.value; - return mouseTiltAdjust(value); - } - }); - return { roll, tilt, source }; -} -function useParentElement(element = useCurrentElement()) { - const parentElement = shallowRef(); - const update = () => { - const el = unrefElement(element); - if (el) - parentElement.value = el.parentElement; - }; - tryOnMounted(update); - watch(() => toValue(element), update); - return parentElement; -} -function usePerformanceObserver(options, callback) { - const { - window: window2 = defaultWindow, - immediate = true, - ...performanceOptions - } = options; - const isSupported = useSupported(() => window2 && "PerformanceObserver" in window2); - let observer; - const stop = () => { - observer == null ? void 0 : observer.disconnect(); - }; - const start = () => { - if (isSupported.value) { - stop(); - observer = new PerformanceObserver(callback); - observer.observe(performanceOptions); - } - }; - tryOnScopeDispose(stop); - if (immediate) - start(); - return { - isSupported, - start, - stop - }; -} -var defaultState = { - x: 0, - y: 0, - pointerId: 0, - pressure: 0, - tiltX: 0, - tiltY: 0, - width: 0, - height: 0, - twist: 0, - pointerType: null -}; -var keys = Object.keys(defaultState); -function usePointer(options = {}) { - const { - target = defaultWindow - } = options; - const isInside = shallowRef(false); - const state = ref(options.initialValue || {}); - Object.assign(state.value, defaultState, state.value); - const handler = (event) => { - isInside.value = true; - if (options.pointerTypes && !options.pointerTypes.includes(event.pointerType)) - return; - state.value = objectPick(event, keys, false); - }; - if (target) { - const listenerOptions = { passive: true }; - useEventListener(target, ["pointerdown", "pointermove", "pointerup"], handler, listenerOptions); - useEventListener(target, "pointerleave", () => isInside.value = false, listenerOptions); - } - return { - ...toRefs2(state), - isInside - }; -} -function usePointerLock(target, options = {}) { - const { document: document2 = defaultDocument } = options; - const isSupported = useSupported(() => document2 && "pointerLockElement" in document2); - const element = shallowRef(); - const triggerElement = shallowRef(); - let targetElement; - if (isSupported.value) { - const listenerOptions = { passive: true }; - useEventListener(document2, "pointerlockchange", () => { - var _a; - const currentElement = (_a = document2.pointerLockElement) != null ? _a : element.value; - if (targetElement && currentElement === targetElement) { - element.value = document2.pointerLockElement; - if (!element.value) - targetElement = triggerElement.value = null; - } - }, listenerOptions); - useEventListener(document2, "pointerlockerror", () => { - var _a; - const currentElement = (_a = document2.pointerLockElement) != null ? _a : element.value; - if (targetElement && currentElement === targetElement) { - const action = document2.pointerLockElement ? "release" : "acquire"; - throw new Error(`Failed to ${action} pointer lock.`); - } - }, listenerOptions); - } - async function lock(e) { - var _a; - if (!isSupported.value) - throw new Error("Pointer Lock API is not supported by your browser."); - triggerElement.value = e instanceof Event ? e.currentTarget : null; - targetElement = e instanceof Event ? (_a = unrefElement(target)) != null ? _a : triggerElement.value : unrefElement(e); - if (!targetElement) - throw new Error("Target element undefined."); - targetElement.requestPointerLock(); - return await until(element).toBe(targetElement); - } - async function unlock() { - if (!element.value) - return false; - document2.exitPointerLock(); - await until(element).toBeNull(); - return true; - } - return { - isSupported, - element, - triggerElement, - lock, - unlock - }; -} -function usePointerSwipe(target, options = {}) { - const targetRef = toRef2(target); - const { - threshold = 50, - onSwipe, - onSwipeEnd, - onSwipeStart, - disableTextSelect = false - } = options; - const posStart = reactive({ x: 0, y: 0 }); - const updatePosStart = (x, y) => { - posStart.x = x; - posStart.y = y; - }; - const posEnd = reactive({ x: 0, y: 0 }); - const updatePosEnd = (x, y) => { - posEnd.x = x; - posEnd.y = y; - }; - const distanceX = computed(() => posStart.x - posEnd.x); - const distanceY = computed(() => posStart.y - posEnd.y); - const { max, abs } = Math; - const isThresholdExceeded = computed(() => max(abs(distanceX.value), abs(distanceY.value)) >= threshold); - const isSwiping = shallowRef(false); - const isPointerDown = shallowRef(false); - const direction = computed(() => { - if (!isThresholdExceeded.value) - return "none"; - if (abs(distanceX.value) > abs(distanceY.value)) { - return distanceX.value > 0 ? "left" : "right"; - } else { - return distanceY.value > 0 ? "up" : "down"; - } - }); - const eventIsAllowed = (e) => { - var _a, _b, _c; - const isReleasingButton = e.buttons === 0; - const isPrimaryButton = e.buttons === 1; - return (_c = (_b = (_a = options.pointerTypes) == null ? void 0 : _a.includes(e.pointerType)) != null ? _b : isReleasingButton || isPrimaryButton) != null ? _c : true; - }; - const listenerOptions = { passive: true }; - const stops = [ - useEventListener(target, "pointerdown", (e) => { - if (!eventIsAllowed(e)) - return; - isPointerDown.value = true; - const eventTarget = e.target; - eventTarget == null ? void 0 : eventTarget.setPointerCapture(e.pointerId); - const { clientX: x, clientY: y } = e; - updatePosStart(x, y); - updatePosEnd(x, y); - onSwipeStart == null ? void 0 : onSwipeStart(e); - }, listenerOptions), - useEventListener(target, "pointermove", (e) => { - if (!eventIsAllowed(e)) - return; - if (!isPointerDown.value) - return; - const { clientX: x, clientY: y } = e; - updatePosEnd(x, y); - if (!isSwiping.value && isThresholdExceeded.value) - isSwiping.value = true; - if (isSwiping.value) - onSwipe == null ? void 0 : onSwipe(e); - }, listenerOptions), - useEventListener(target, "pointerup", (e) => { - if (!eventIsAllowed(e)) - return; - if (isSwiping.value) - onSwipeEnd == null ? void 0 : onSwipeEnd(e, direction.value); - isPointerDown.value = false; - isSwiping.value = false; - }, listenerOptions) - ]; - tryOnMounted(() => { - var _a, _b, _c, _d, _e, _f, _g, _h; - (_b = (_a = targetRef.value) == null ? void 0 : _a.style) == null ? void 0 : _b.setProperty("touch-action", "none"); - if (disableTextSelect) { - (_d = (_c = targetRef.value) == null ? void 0 : _c.style) == null ? void 0 : _d.setProperty("-webkit-user-select", "none"); - (_f = (_e = targetRef.value) == null ? void 0 : _e.style) == null ? void 0 : _f.setProperty("-ms-user-select", "none"); - (_h = (_g = targetRef.value) == null ? void 0 : _g.style) == null ? void 0 : _h.setProperty("user-select", "none"); - } - }); - const stop = () => stops.forEach((s) => s()); - return { - isSwiping: readonly(isSwiping), - direction: readonly(direction), - posStart: readonly(posStart), - posEnd: readonly(posEnd), - distanceX, - distanceY, - stop - }; -} -function usePreferredColorScheme(options) { - const isLight = useMediaQuery("(prefers-color-scheme: light)", options); - const isDark = useMediaQuery("(prefers-color-scheme: dark)", options); - return computed(() => { - if (isDark.value) - return "dark"; - if (isLight.value) - return "light"; - return "no-preference"; - }); -} -function usePreferredContrast(options) { - const isMore = useMediaQuery("(prefers-contrast: more)", options); - const isLess = useMediaQuery("(prefers-contrast: less)", options); - const isCustom = useMediaQuery("(prefers-contrast: custom)", options); - return computed(() => { - if (isMore.value) - return "more"; - if (isLess.value) - return "less"; - if (isCustom.value) - return "custom"; - return "no-preference"; - }); -} -function usePreferredLanguages(options = {}) { - const { window: window2 = defaultWindow } = options; - if (!window2) - return ref(["en"]); - const navigator2 = window2.navigator; - const value = ref(navigator2.languages); - useEventListener(window2, "languagechange", () => { - value.value = navigator2.languages; - }, { passive: true }); - return value; -} -function usePreferredReducedMotion(options) { - const isReduced = useMediaQuery("(prefers-reduced-motion: reduce)", options); - return computed(() => { - if (isReduced.value) - return "reduce"; - return "no-preference"; - }); -} -function usePreferredReducedTransparency(options) { - const isReduced = useMediaQuery("(prefers-reduced-transparency: reduce)", options); - return computed(() => { - if (isReduced.value) - return "reduce"; - return "no-preference"; - }); -} -function usePrevious(value, initialValue) { - const previous = shallowRef(initialValue); - watch( - toRef2(value), - (_, oldValue) => { - previous.value = oldValue; - }, - { flush: "sync" } - ); - return readonly(previous); -} -var topVarName = "--vueuse-safe-area-top"; -var rightVarName = "--vueuse-safe-area-right"; -var bottomVarName = "--vueuse-safe-area-bottom"; -var leftVarName = "--vueuse-safe-area-left"; -function useScreenSafeArea() { - const top = shallowRef(""); - const right = shallowRef(""); - const bottom = shallowRef(""); - const left = shallowRef(""); - if (isClient) { - const topCssVar = useCssVar(topVarName); - const rightCssVar = useCssVar(rightVarName); - const bottomCssVar = useCssVar(bottomVarName); - const leftCssVar = useCssVar(leftVarName); - topCssVar.value = "env(safe-area-inset-top, 0px)"; - rightCssVar.value = "env(safe-area-inset-right, 0px)"; - bottomCssVar.value = "env(safe-area-inset-bottom, 0px)"; - leftCssVar.value = "env(safe-area-inset-left, 0px)"; - update(); - useEventListener("resize", useDebounceFn(update), { passive: true }); - } - function update() { - top.value = getValue(topVarName); - right.value = getValue(rightVarName); - bottom.value = getValue(bottomVarName); - left.value = getValue(leftVarName); - } - return { - top, - right, - bottom, - left, - update - }; -} -function getValue(position) { - return getComputedStyle(document.documentElement).getPropertyValue(position); -} -function useScriptTag(src, onLoaded = noop, options = {}) { - const { - immediate = true, - manual = false, - type = "text/javascript", - async = true, - crossOrigin, - referrerPolicy, - noModule, - defer, - document: document2 = defaultDocument, - attrs = {} - } = options; - const scriptTag = shallowRef(null); - let _promise = null; - const loadScript = (waitForScriptLoad) => new Promise((resolve, reject) => { - const resolveWithElement = (el2) => { - scriptTag.value = el2; - resolve(el2); - return el2; - }; - if (!document2) { - resolve(false); - return; - } - let shouldAppend = false; - let el = document2.querySelector(`script[src="${toValue(src)}"]`); - if (!el) { - el = document2.createElement("script"); - el.type = type; - el.async = async; - el.src = toValue(src); - if (defer) - el.defer = defer; - if (crossOrigin) - el.crossOrigin = crossOrigin; - if (noModule) - el.noModule = noModule; - if (referrerPolicy) - el.referrerPolicy = referrerPolicy; - Object.entries(attrs).forEach(([name, value]) => el == null ? void 0 : el.setAttribute(name, value)); - shouldAppend = true; - } else if (el.hasAttribute("data-loaded")) { - resolveWithElement(el); - } - const listenerOptions = { - passive: true - }; - useEventListener(el, "error", (event) => reject(event), listenerOptions); - useEventListener(el, "abort", (event) => reject(event), listenerOptions); - useEventListener(el, "load", () => { - el.setAttribute("data-loaded", "true"); - onLoaded(el); - resolveWithElement(el); - }, listenerOptions); - if (shouldAppend) - el = document2.head.appendChild(el); - if (!waitForScriptLoad) - resolveWithElement(el); - }); - const load = (waitForScriptLoad = true) => { - if (!_promise) - _promise = loadScript(waitForScriptLoad); - return _promise; - }; - const unload = () => { - if (!document2) - return; - _promise = null; - if (scriptTag.value) - scriptTag.value = null; - const el = document2.querySelector(`script[src="${toValue(src)}"]`); - if (el) - document2.head.removeChild(el); - }; - if (immediate && !manual) - tryOnMounted(load); - if (!manual) - tryOnUnmounted(unload); - return { scriptTag, load, unload }; -} -function checkOverflowScroll(ele) { - const style = window.getComputedStyle(ele); - if (style.overflowX === "scroll" || style.overflowY === "scroll" || style.overflowX === "auto" && ele.clientWidth < ele.scrollWidth || style.overflowY === "auto" && ele.clientHeight < ele.scrollHeight) { - return true; - } else { - const parent = ele.parentNode; - if (!parent || parent.tagName === "BODY") - return false; - return checkOverflowScroll(parent); - } -} -function preventDefault(rawEvent) { - const e = rawEvent || window.event; - const _target = e.target; - if (checkOverflowScroll(_target)) - return false; - if (e.touches.length > 1) - return true; - if (e.preventDefault) - e.preventDefault(); - return false; -} -var elInitialOverflow = /* @__PURE__ */ new WeakMap(); -function useScrollLock(element, initialState = false) { - const isLocked = shallowRef(initialState); - let stopTouchMoveListener = null; - let initialOverflow = ""; - watch(toRef2(element), (el) => { - const target = resolveElement(toValue(el)); - if (target) { - const ele = target; - if (!elInitialOverflow.get(ele)) - elInitialOverflow.set(ele, ele.style.overflow); - if (ele.style.overflow !== "hidden") - initialOverflow = ele.style.overflow; - if (ele.style.overflow === "hidden") - return isLocked.value = true; - if (isLocked.value) - return ele.style.overflow = "hidden"; - } - }, { - immediate: true - }); - const lock = () => { - const el = resolveElement(toValue(element)); - if (!el || isLocked.value) - return; - if (isIOS) { - stopTouchMoveListener = useEventListener( - el, - "touchmove", - (e) => { - preventDefault(e); - }, - { passive: false } - ); - } - el.style.overflow = "hidden"; - isLocked.value = true; - }; - const unlock = () => { - const el = resolveElement(toValue(element)); - if (!el || !isLocked.value) - return; - if (isIOS) - stopTouchMoveListener == null ? void 0 : stopTouchMoveListener(); - el.style.overflow = initialOverflow; - elInitialOverflow.delete(el); - isLocked.value = false; - }; - tryOnScopeDispose(unlock); - return computed({ - get() { - return isLocked.value; - }, - set(v) { - if (v) - lock(); - else unlock(); - } - }); -} -function useSessionStorage(key, initialValue, options = {}) { - const { window: window2 = defaultWindow } = options; - return useStorage(key, initialValue, window2 == null ? void 0 : window2.sessionStorage, options); -} -function useShare(shareOptions = {}, options = {}) { - const { navigator: navigator2 = defaultNavigator } = options; - const _navigator = navigator2; - const isSupported = useSupported(() => _navigator && "canShare" in _navigator); - const share = async (overrideOptions = {}) => { - if (isSupported.value) { - const data = { - ...toValue(shareOptions), - ...toValue(overrideOptions) - }; - let granted = true; - if (data.files && _navigator.canShare) - granted = _navigator.canShare({ files: data.files }); - if (granted) - return _navigator.share(data); - } - }; - return { - isSupported, - share - }; -} -var defaultSortFn = (source, compareFn) => source.sort(compareFn); -var defaultCompare = (a, b) => a - b; -function useSorted(...args) { - var _a, _b, _c, _d; - const [source] = args; - let compareFn = defaultCompare; - let options = {}; - if (args.length === 2) { - if (typeof args[1] === "object") { - options = args[1]; - compareFn = (_a = options.compareFn) != null ? _a : defaultCompare; - } else { - compareFn = (_b = args[1]) != null ? _b : defaultCompare; - } - } else if (args.length > 2) { - compareFn = (_c = args[1]) != null ? _c : defaultCompare; - options = (_d = args[2]) != null ? _d : {}; - } - const { - dirty = false, - sortFn = defaultSortFn - } = options; - if (!dirty) - return computed(() => sortFn([...toValue(source)], compareFn)); - watchEffect(() => { - const result = sortFn(toValue(source), compareFn); - if (isRef(source)) - source.value = result; - else - source.splice(0, source.length, ...result); - }); - return source; -} -function useSpeechRecognition(options = {}) { - const { - interimResults = true, - continuous = true, - maxAlternatives = 1, - window: window2 = defaultWindow - } = options; - const lang = toRef2(options.lang || "en-US"); - const isListening = shallowRef(false); - const isFinal = shallowRef(false); - const result = shallowRef(""); - const error = shallowRef(void 0); - let recognition; - const start = () => { - isListening.value = true; - }; - const stop = () => { - isListening.value = false; - }; - const toggle = (value = !isListening.value) => { - if (value) { - start(); - } else { - stop(); - } - }; - const SpeechRecognition = window2 && (window2.SpeechRecognition || window2.webkitSpeechRecognition); - const isSupported = useSupported(() => SpeechRecognition); - if (isSupported.value) { - recognition = new SpeechRecognition(); - recognition.continuous = continuous; - recognition.interimResults = interimResults; - recognition.lang = toValue(lang); - recognition.maxAlternatives = maxAlternatives; - recognition.onstart = () => { - isListening.value = true; - isFinal.value = false; - }; - watch(lang, (lang2) => { - if (recognition && !isListening.value) - recognition.lang = lang2; - }); - recognition.onresult = (event) => { - const currentResult = event.results[event.resultIndex]; - const { transcript } = currentResult[0]; - isFinal.value = currentResult.isFinal; - result.value = transcript; - error.value = void 0; - }; - recognition.onerror = (event) => { - error.value = event; - }; - recognition.onend = () => { - isListening.value = false; - recognition.lang = toValue(lang); - }; - watch(isListening, (newValue, oldValue) => { - if (newValue === oldValue) - return; - if (newValue) - recognition.start(); - else - recognition.stop(); - }); - } - tryOnScopeDispose(() => { - stop(); - }); - return { - isSupported, - isListening, - isFinal, - recognition, - result, - error, - toggle, - start, - stop - }; -} -function useSpeechSynthesis(text, options = {}) { - const { - pitch = 1, - rate = 1, - volume = 1, - window: window2 = defaultWindow - } = options; - const synth = window2 && window2.speechSynthesis; - const isSupported = useSupported(() => synth); - const isPlaying = shallowRef(false); - const status = shallowRef("init"); - const spokenText = toRef2(text || ""); - const lang = toRef2(options.lang || "en-US"); - const error = shallowRef(void 0); - const toggle = (value = !isPlaying.value) => { - isPlaying.value = value; - }; - const bindEventsForUtterance = (utterance2) => { - utterance2.lang = toValue(lang); - utterance2.voice = toValue(options.voice) || null; - utterance2.pitch = toValue(pitch); - utterance2.rate = toValue(rate); - utterance2.volume = volume; - utterance2.onstart = () => { - isPlaying.value = true; - status.value = "play"; - }; - utterance2.onpause = () => { - isPlaying.value = false; - status.value = "pause"; - }; - utterance2.onresume = () => { - isPlaying.value = true; - status.value = "play"; - }; - utterance2.onend = () => { - isPlaying.value = false; - status.value = "end"; - }; - utterance2.onerror = (event) => { - error.value = event; - }; - }; - const utterance = computed(() => { - isPlaying.value = false; - status.value = "init"; - const newUtterance = new SpeechSynthesisUtterance(spokenText.value); - bindEventsForUtterance(newUtterance); - return newUtterance; - }); - const speak = () => { - synth.cancel(); - if (utterance) - synth.speak(utterance.value); - }; - const stop = () => { - synth.cancel(); - isPlaying.value = false; - }; - if (isSupported.value) { - bindEventsForUtterance(utterance.value); - watch(lang, (lang2) => { - if (utterance.value && !isPlaying.value) - utterance.value.lang = lang2; - }); - if (options.voice) { - watch(options.voice, () => { - synth.cancel(); - }); - } - watch(isPlaying, () => { - if (isPlaying.value) - synth.resume(); - else - synth.pause(); - }); - } - tryOnScopeDispose(() => { - isPlaying.value = false; - }); - return { - isSupported, - isPlaying, - status, - utterance, - error, - stop, - toggle, - speak - }; -} -function useStepper(steps, initialStep) { - const stepsRef = ref(steps); - const stepNames = computed(() => Array.isArray(stepsRef.value) ? stepsRef.value : Object.keys(stepsRef.value)); - const index = ref(stepNames.value.indexOf(initialStep != null ? initialStep : stepNames.value[0])); - const current = computed(() => at(index.value)); - const isFirst = computed(() => index.value === 0); - const isLast = computed(() => index.value === stepNames.value.length - 1); - const next = computed(() => stepNames.value[index.value + 1]); - const previous = computed(() => stepNames.value[index.value - 1]); - function at(index2) { - if (Array.isArray(stepsRef.value)) - return stepsRef.value[index2]; - return stepsRef.value[stepNames.value[index2]]; - } - function get2(step) { - if (!stepNames.value.includes(step)) - return; - return at(stepNames.value.indexOf(step)); - } - function goTo(step) { - if (stepNames.value.includes(step)) - index.value = stepNames.value.indexOf(step); - } - function goToNext() { - if (isLast.value) - return; - index.value++; - } - function goToPrevious() { - if (isFirst.value) - return; - index.value--; - } - function goBackTo(step) { - if (isAfter(step)) - goTo(step); - } - function isNext(step) { - return stepNames.value.indexOf(step) === index.value + 1; - } - function isPrevious(step) { - return stepNames.value.indexOf(step) === index.value - 1; - } - function isCurrent(step) { - return stepNames.value.indexOf(step) === index.value; - } - function isBefore(step) { - return index.value < stepNames.value.indexOf(step); - } - function isAfter(step) { - return index.value > stepNames.value.indexOf(step); - } - return { - steps: stepsRef, - stepNames, - index, - current, - next, - previous, - isFirst, - isLast, - at, - get: get2, - goTo, - goToNext, - goToPrevious, - goBackTo, - isNext, - isPrevious, - isCurrent, - isBefore, - isAfter - }; -} -function useStorageAsync(key, initialValue, storage, options = {}) { - var _a; - const { - flush = "pre", - deep = true, - listenToStorageChanges = true, - writeDefaults = true, - mergeDefaults = false, - shallow, - window: window2 = defaultWindow, - eventFilter, - onError = (e) => { - console.error(e); - } - } = options; - const rawInit = toValue(initialValue); - const type = guessSerializerType(rawInit); - const data = (shallow ? shallowRef : ref)(toValue(initialValue)); - const serializer = (_a = options.serializer) != null ? _a : StorageSerializers[type]; - if (!storage) { - try { - storage = getSSRHandler("getDefaultStorageAsync", () => { - var _a2; - return (_a2 = defaultWindow) == null ? void 0 : _a2.localStorage; - })(); - } catch (e) { - onError(e); - } - } - async function read(event) { - if (!storage || event && event.key !== key) - return; - try { - const rawValue = event ? event.newValue : await storage.getItem(key); - if (rawValue == null) { - data.value = rawInit; - if (writeDefaults && rawInit !== null) - await storage.setItem(key, await serializer.write(rawInit)); - } else if (mergeDefaults) { - const value = await serializer.read(rawValue); - if (typeof mergeDefaults === "function") - data.value = mergeDefaults(value, rawInit); - else if (type === "object" && !Array.isArray(value)) - data.value = { ...rawInit, ...value }; - else data.value = value; - } else { - data.value = await serializer.read(rawValue); - } - } catch (e) { - onError(e); - } - } - read(); - if (window2 && listenToStorageChanges) - useEventListener(window2, "storage", (e) => Promise.resolve().then(() => read(e)), { passive: true }); - if (storage) { - watchWithFilter( - data, - async () => { - try { - if (data.value == null) - await storage.removeItem(key); - else - await storage.setItem(key, await serializer.write(data.value)); - } catch (e) { - onError(e); - } - }, - { - flush, - deep, - eventFilter - } - ); - } - return data; -} -var _id = 0; -function useStyleTag(css, options = {}) { - const isLoaded = shallowRef(false); - const { - document: document2 = defaultDocument, - immediate = true, - manual = false, - id = `vueuse_styletag_${++_id}` - } = options; - const cssRef = shallowRef(css); - let stop = () => { - }; - const load = () => { - if (!document2) - return; - const el = document2.getElementById(id) || document2.createElement("style"); - if (!el.isConnected) { - el.id = id; - if (options.media) - el.media = options.media; - document2.head.appendChild(el); - } - if (isLoaded.value) - return; - stop = watch( - cssRef, - (value) => { - el.textContent = value; - }, - { immediate: true } - ); - isLoaded.value = true; - }; - const unload = () => { - if (!document2 || !isLoaded.value) - return; - stop(); - document2.head.removeChild(document2.getElementById(id)); - isLoaded.value = false; - }; - if (immediate && !manual) - tryOnMounted(load); - if (!manual) - tryOnScopeDispose(unload); - return { - id, - css: cssRef, - unload, - load, - isLoaded: readonly(isLoaded) - }; -} -function useSwipe(target, options = {}) { - const { - threshold = 50, - onSwipe, - onSwipeEnd, - onSwipeStart, - passive = true - } = options; - const coordsStart = reactive({ x: 0, y: 0 }); - const coordsEnd = reactive({ x: 0, y: 0 }); - const diffX = computed(() => coordsStart.x - coordsEnd.x); - const diffY = computed(() => coordsStart.y - coordsEnd.y); - const { max, abs } = Math; - const isThresholdExceeded = computed(() => max(abs(diffX.value), abs(diffY.value)) >= threshold); - const isSwiping = shallowRef(false); - const direction = computed(() => { - if (!isThresholdExceeded.value) - return "none"; - if (abs(diffX.value) > abs(diffY.value)) { - return diffX.value > 0 ? "left" : "right"; - } else { - return diffY.value > 0 ? "up" : "down"; - } - }); - const getTouchEventCoords = (e) => [e.touches[0].clientX, e.touches[0].clientY]; - const updateCoordsStart = (x, y) => { - coordsStart.x = x; - coordsStart.y = y; - }; - const updateCoordsEnd = (x, y) => { - coordsEnd.x = x; - coordsEnd.y = y; - }; - const listenerOptions = { passive, capture: !passive }; - const onTouchEnd = (e) => { - if (isSwiping.value) - onSwipeEnd == null ? void 0 : onSwipeEnd(e, direction.value); - isSwiping.value = false; - }; - const stops = [ - useEventListener(target, "touchstart", (e) => { - if (e.touches.length !== 1) - return; - const [x, y] = getTouchEventCoords(e); - updateCoordsStart(x, y); - updateCoordsEnd(x, y); - onSwipeStart == null ? void 0 : onSwipeStart(e); - }, listenerOptions), - useEventListener(target, "touchmove", (e) => { - if (e.touches.length !== 1) - return; - const [x, y] = getTouchEventCoords(e); - updateCoordsEnd(x, y); - if (listenerOptions.capture && !listenerOptions.passive && Math.abs(diffX.value) > Math.abs(diffY.value)) - e.preventDefault(); - if (!isSwiping.value && isThresholdExceeded.value) - isSwiping.value = true; - if (isSwiping.value) - onSwipe == null ? void 0 : onSwipe(e); - }, listenerOptions), - useEventListener(target, ["touchend", "touchcancel"], onTouchEnd, listenerOptions) - ]; - const stop = () => stops.forEach((s) => s()); - return { - isSwiping, - direction, - coordsStart, - coordsEnd, - lengthX: diffX, - lengthY: diffY, - stop, - // TODO: Remove in the next major version - isPassiveEventSupported: true - }; -} -function useTemplateRefsList() { - const refs = ref([]); - refs.value.set = (el) => { - if (el) - refs.value.push(el); - }; - onBeforeUpdate(() => { - refs.value.length = 0; - }); - return refs; -} -function useTextDirection(options = {}) { - const { - document: document2 = defaultDocument, - selector = "html", - observe = false, - initialValue = "ltr" - } = options; - function getValue2() { - var _a, _b; - return (_b = (_a = document2 == null ? void 0 : document2.querySelector(selector)) == null ? void 0 : _a.getAttribute("dir")) != null ? _b : initialValue; - } - const dir = ref(getValue2()); - tryOnMounted(() => dir.value = getValue2()); - if (observe && document2) { - useMutationObserver( - document2.querySelector(selector), - () => dir.value = getValue2(), - { attributes: true } - ); - } - return computed({ - get() { - return dir.value; - }, - set(v) { - var _a, _b; - dir.value = v; - if (!document2) - return; - if (dir.value) - (_a = document2.querySelector(selector)) == null ? void 0 : _a.setAttribute("dir", dir.value); - else - (_b = document2.querySelector(selector)) == null ? void 0 : _b.removeAttribute("dir"); - } - }); -} -function getRangesFromSelection(selection) { - var _a; - const rangeCount = (_a = selection.rangeCount) != null ? _a : 0; - return Array.from({ length: rangeCount }, (_, i) => selection.getRangeAt(i)); -} -function useTextSelection(options = {}) { - const { - window: window2 = defaultWindow - } = options; - const selection = ref(null); - const text = computed(() => { - var _a, _b; - return (_b = (_a = selection.value) == null ? void 0 : _a.toString()) != null ? _b : ""; - }); - const ranges = computed(() => selection.value ? getRangesFromSelection(selection.value) : []); - const rects = computed(() => ranges.value.map((range) => range.getBoundingClientRect())); - function onSelectionChange() { - selection.value = null; - if (window2) - selection.value = window2.getSelection(); - } - if (window2) - useEventListener(window2.document, "selectionchange", onSelectionChange, { passive: true }); - return { - text, - rects, - ranges, - selection - }; -} -function tryRequestAnimationFrame(window2 = defaultWindow, fn) { - if (window2 && typeof window2.requestAnimationFrame === "function") { - window2.requestAnimationFrame(fn); - } else { - fn(); - } -} -function useTextareaAutosize(options = {}) { - var _a, _b; - const { window: window2 = defaultWindow } = options; - const textarea = toRef2(options == null ? void 0 : options.element); - const input = toRef2((_a = options == null ? void 0 : options.input) != null ? _a : ""); - const styleProp = (_b = options == null ? void 0 : options.styleProp) != null ? _b : "height"; - const textareaScrollHeight = shallowRef(1); - const textareaOldWidth = shallowRef(0); - function triggerResize() { - var _a2; - if (!textarea.value) - return; - let height = ""; - textarea.value.style[styleProp] = "1px"; - textareaScrollHeight.value = (_a2 = textarea.value) == null ? void 0 : _a2.scrollHeight; - const _styleTarget = toValue(options == null ? void 0 : options.styleTarget); - if (_styleTarget) - _styleTarget.style[styleProp] = `${textareaScrollHeight.value}px`; - else - height = `${textareaScrollHeight.value}px`; - textarea.value.style[styleProp] = height; - } - watch([input, textarea], () => nextTick(triggerResize), { immediate: true }); - watch(textareaScrollHeight, () => { - var _a2; - return (_a2 = options == null ? void 0 : options.onResize) == null ? void 0 : _a2.call(options); - }); - useResizeObserver(textarea, ([{ contentRect }]) => { - if (textareaOldWidth.value === contentRect.width) - return; - tryRequestAnimationFrame(window2, () => { - textareaOldWidth.value = contentRect.width; - triggerResize(); - }); - }); - if (options == null ? void 0 : options.watch) - watch(options.watch, triggerResize, { immediate: true, deep: true }); - return { - textarea, - input, - triggerResize - }; -} -function useThrottledRefHistory(source, options = {}) { - const { throttle = 200, trailing = true } = options; - const filter = throttleFilter(throttle, trailing); - const history = useRefHistory(source, { ...options, eventFilter: filter }); - return { - ...history - }; -} -var DEFAULT_UNITS = [ - { max: 6e4, value: 1e3, name: "second" }, - { max: 276e4, value: 6e4, name: "minute" }, - { max: 72e6, value: 36e5, name: "hour" }, - { max: 5184e5, value: 864e5, name: "day" }, - { max: 24192e5, value: 6048e5, name: "week" }, - { max: 28512e6, value: 2592e6, name: "month" }, - { max: Number.POSITIVE_INFINITY, value: 31536e6, name: "year" } -]; -var DEFAULT_MESSAGES = { - justNow: "just now", - past: (n) => n.match(/\d/) ? `${n} ago` : n, - future: (n) => n.match(/\d/) ? `in ${n}` : n, - month: (n, past) => n === 1 ? past ? "last month" : "next month" : `${n} month${n > 1 ? "s" : ""}`, - year: (n, past) => n === 1 ? past ? "last year" : "next year" : `${n} year${n > 1 ? "s" : ""}`, - day: (n, past) => n === 1 ? past ? "yesterday" : "tomorrow" : `${n} day${n > 1 ? "s" : ""}`, - week: (n, past) => n === 1 ? past ? "last week" : "next week" : `${n} week${n > 1 ? "s" : ""}`, - hour: (n) => `${n} hour${n > 1 ? "s" : ""}`, - minute: (n) => `${n} minute${n > 1 ? "s" : ""}`, - second: (n) => `${n} second${n > 1 ? "s" : ""}`, - invalid: "" -}; -function DEFAULT_FORMATTER(date) { - return date.toISOString().slice(0, 10); -} -function useTimeAgo(time, options = {}) { - const { - controls: exposeControls = false, - updateInterval = 3e4 - } = options; - const { now: now2, ...controls } = useNow({ interval: updateInterval, controls: true }); - const timeAgo = computed(() => formatTimeAgo(new Date(toValue(time)), options, toValue(now2))); - if (exposeControls) { - return { - timeAgo, - ...controls - }; - } else { - return timeAgo; - } -} -function formatTimeAgo(from, options = {}, now2 = Date.now()) { - var _a; - const { - max, - messages = DEFAULT_MESSAGES, - fullDateFormatter = DEFAULT_FORMATTER, - units = DEFAULT_UNITS, - showSecond = false, - rounding = "round" - } = options; - const roundFn = typeof rounding === "number" ? (n) => +n.toFixed(rounding) : Math[rounding]; - const diff = +now2 - +from; - const absDiff = Math.abs(diff); - function getValue2(diff2, unit) { - return roundFn(Math.abs(diff2) / unit.value); - } - function format(diff2, unit) { - const val = getValue2(diff2, unit); - const past = diff2 > 0; - const str = applyFormat(unit.name, val, past); - return applyFormat(past ? "past" : "future", str, past); - } - function applyFormat(name, val, isPast) { - const formatter = messages[name]; - if (typeof formatter === "function") - return formatter(val, isPast); - return formatter.replace("{0}", val.toString()); - } - if (absDiff < 6e4 && !showSecond) - return messages.justNow; - if (typeof max === "number" && absDiff > max) - return fullDateFormatter(new Date(from)); - if (typeof max === "string") { - const unitMax = (_a = units.find((i) => i.name === max)) == null ? void 0 : _a.max; - if (unitMax && absDiff > unitMax) - return fullDateFormatter(new Date(from)); - } - for (const [idx, unit] of units.entries()) { - const val = getValue2(diff, unit); - if (val <= 0 && units[idx - 1]) - return format(diff, units[idx - 1]); - if (absDiff < unit.max) - return format(diff, unit); - } - return messages.invalid; -} -function useTimeoutPoll(fn, interval, options = {}) { - const { - immediate = true, - immediateCallback = false - } = options; - const { start } = useTimeoutFn(loop, interval, { immediate }); - const isActive = shallowRef(false); - async function loop() { - if (!isActive.value) - return; - await fn(); - start(); - } - function resume() { - if (!isActive.value) { - isActive.value = true; - if (immediateCallback) - fn(); - start(); - } - } - function pause() { - isActive.value = false; - } - if (immediate && isClient) - resume(); - tryOnScopeDispose(pause); - return { - isActive, - pause, - resume - }; -} -function useTimestamp(options = {}) { - const { - controls: exposeControls = false, - offset = 0, - immediate = true, - interval = "requestAnimationFrame", - callback - } = options; - const ts = shallowRef(timestamp() + offset); - const update = () => ts.value = timestamp() + offset; - const cb = callback ? () => { - update(); - callback(ts.value); - } : update; - const controls = interval === "requestAnimationFrame" ? useRafFn(cb, { immediate }) : useIntervalFn(cb, interval, { immediate }); - if (exposeControls) { - return { - timestamp: ts, - ...controls - }; - } else { - return ts; - } -} -function useTitle(newTitle = null, options = {}) { - var _a, _b, _c; - const { - document: document2 = defaultDocument, - restoreOnUnmount = (t) => t - } = options; - const originalTitle = (_a = document2 == null ? void 0 : document2.title) != null ? _a : ""; - const title = toRef2((_b = newTitle != null ? newTitle : document2 == null ? void 0 : document2.title) != null ? _b : null); - const isReadonly2 = !!(newTitle && typeof newTitle === "function"); - function format(t) { - if (!("titleTemplate" in options)) - return t; - const template = options.titleTemplate || "%s"; - return typeof template === "function" ? template(t) : toValue(template).replace(/%s/g, t); - } - watch( - title, - (newValue, oldValue) => { - if (newValue !== oldValue && document2) - document2.title = format(newValue != null ? newValue : ""); - }, - { immediate: true } - ); - if (options.observe && !options.titleTemplate && document2 && !isReadonly2) { - useMutationObserver( - (_c = document2.head) == null ? void 0 : _c.querySelector("title"), - () => { - if (document2 && document2.title !== title.value) - title.value = format(document2.title); - }, - { childList: true } - ); - } - tryOnScopeDispose(() => { - if (restoreOnUnmount) { - const restoredTitle = restoreOnUnmount(originalTitle, title.value || ""); - if (restoredTitle != null && document2) - document2.title = restoredTitle; - } - }); - return title; -} -var _TransitionPresets = { - easeInSine: [0.12, 0, 0.39, 0], - easeOutSine: [0.61, 1, 0.88, 1], - easeInOutSine: [0.37, 0, 0.63, 1], - easeInQuad: [0.11, 0, 0.5, 0], - easeOutQuad: [0.5, 1, 0.89, 1], - easeInOutQuad: [0.45, 0, 0.55, 1], - easeInCubic: [0.32, 0, 0.67, 0], - easeOutCubic: [0.33, 1, 0.68, 1], - easeInOutCubic: [0.65, 0, 0.35, 1], - easeInQuart: [0.5, 0, 0.75, 0], - easeOutQuart: [0.25, 1, 0.5, 1], - easeInOutQuart: [0.76, 0, 0.24, 1], - easeInQuint: [0.64, 0, 0.78, 0], - easeOutQuint: [0.22, 1, 0.36, 1], - easeInOutQuint: [0.83, 0, 0.17, 1], - easeInExpo: [0.7, 0, 0.84, 0], - easeOutExpo: [0.16, 1, 0.3, 1], - easeInOutExpo: [0.87, 0, 0.13, 1], - easeInCirc: [0.55, 0, 1, 0.45], - easeOutCirc: [0, 0.55, 0.45, 1], - easeInOutCirc: [0.85, 0, 0.15, 1], - easeInBack: [0.36, 0, 0.66, -0.56], - easeOutBack: [0.34, 1.56, 0.64, 1], - easeInOutBack: [0.68, -0.6, 0.32, 1.6] -}; -var TransitionPresets = Object.assign({}, { linear: identity }, _TransitionPresets); -function createEasingFunction([p0, p1, p2, p3]) { - const a = (a1, a2) => 1 - 3 * a2 + 3 * a1; - const b = (a1, a2) => 3 * a2 - 6 * a1; - const c = (a1) => 3 * a1; - const calcBezier = (t, a1, a2) => ((a(a1, a2) * t + b(a1, a2)) * t + c(a1)) * t; - const getSlope = (t, a1, a2) => 3 * a(a1, a2) * t * t + 2 * b(a1, a2) * t + c(a1); - const getTforX = (x) => { - let aGuessT = x; - for (let i = 0; i < 4; ++i) { - const currentSlope = getSlope(aGuessT, p0, p2); - if (currentSlope === 0) - return aGuessT; - const currentX = calcBezier(aGuessT, p0, p2) - x; - aGuessT -= currentX / currentSlope; - } - return aGuessT; - }; - return (x) => p0 === p1 && p2 === p3 ? x : calcBezier(getTforX(x), p1, p3); -} -function lerp(a, b, alpha) { - return a + alpha * (b - a); -} -function toVec(t) { - return (typeof t === "number" ? [t] : t) || []; -} -function executeTransition(source, from, to, options = {}) { - var _a, _b; - const fromVal = toValue(from); - const toVal = toValue(to); - const v1 = toVec(fromVal); - const v2 = toVec(toVal); - const duration = (_a = toValue(options.duration)) != null ? _a : 1e3; - const startedAt = Date.now(); - const endAt = Date.now() + duration; - const trans = typeof options.transition === "function" ? options.transition : (_b = toValue(options.transition)) != null ? _b : identity; - const ease = typeof trans === "function" ? trans : createEasingFunction(trans); - return new Promise((resolve) => { - source.value = fromVal; - const tick = () => { - var _a2; - if ((_a2 = options.abort) == null ? void 0 : _a2.call(options)) { - resolve(); - return; - } - const now2 = Date.now(); - const alpha = ease((now2 - startedAt) / duration); - const arr = toVec(source.value).map((n, i) => lerp(v1[i], v2[i], alpha)); - if (Array.isArray(source.value)) - source.value = arr.map((n, i) => { - var _a3, _b2; - return lerp((_a3 = v1[i]) != null ? _a3 : 0, (_b2 = v2[i]) != null ? _b2 : 0, alpha); - }); - else if (typeof source.value === "number") - source.value = arr[0]; - if (now2 < endAt) { - requestAnimationFrame(tick); - } else { - source.value = toVal; - resolve(); - } - }; - tick(); - }); -} -function useTransition(source, options = {}) { - let currentId = 0; - const sourceVal = () => { - const v = toValue(source); - return typeof v === "number" ? v : v.map(toValue); - }; - const outputRef = ref(sourceVal()); - watch(sourceVal, async (to) => { - var _a, _b; - if (toValue(options.disabled)) - return; - const id = ++currentId; - if (options.delay) - await promiseTimeout(toValue(options.delay)); - if (id !== currentId) - return; - const toVal = Array.isArray(to) ? to.map(toValue) : toValue(to); - (_a = options.onStarted) == null ? void 0 : _a.call(options); - await executeTransition(outputRef, outputRef.value, toVal, { - ...options, - abort: () => { - var _a2; - return id !== currentId || ((_a2 = options.abort) == null ? void 0 : _a2.call(options)); - } - }); - (_b = options.onFinished) == null ? void 0 : _b.call(options); - }, { deep: true }); - watch(() => toValue(options.disabled), (disabled) => { - if (disabled) { - currentId++; - outputRef.value = sourceVal(); - } - }); - tryOnScopeDispose(() => { - currentId++; - }); - return computed(() => toValue(options.disabled) ? sourceVal() : outputRef.value); -} -function useUrlSearchParams(mode = "history", options = {}) { - const { - initialValue = {}, - removeNullishValues = true, - removeFalsyValues = false, - write: enableWrite = true, - writeMode = "replace", - window: window2 = defaultWindow - } = options; - if (!window2) - return reactive(initialValue); - const state = reactive({}); - function getRawParams() { - if (mode === "history") { - return window2.location.search || ""; - } else if (mode === "hash") { - const hash = window2.location.hash || ""; - const index = hash.indexOf("?"); - return index > 0 ? hash.slice(index) : ""; - } else { - return (window2.location.hash || "").replace(/^#/, ""); - } - } - function constructQuery(params) { - const stringified = params.toString(); - if (mode === "history") - return `${stringified ? `?${stringified}` : ""}${window2.location.hash || ""}`; - if (mode === "hash-params") - return `${window2.location.search || ""}${stringified ? `#${stringified}` : ""}`; - const hash = window2.location.hash || "#"; - const index = hash.indexOf("?"); - if (index > 0) - return `${window2.location.search || ""}${hash.slice(0, index)}${stringified ? `?${stringified}` : ""}`; - return `${window2.location.search || ""}${hash}${stringified ? `?${stringified}` : ""}`; - } - function read() { - return new URLSearchParams(getRawParams()); - } - function updateState(params) { - const unusedKeys = new Set(Object.keys(state)); - for (const key of params.keys()) { - const paramsForKey = params.getAll(key); - state[key] = paramsForKey.length > 1 ? paramsForKey : params.get(key) || ""; - unusedKeys.delete(key); - } - Array.from(unusedKeys).forEach((key) => delete state[key]); - } - const { pause, resume } = watchPausable( - state, - () => { - const params = new URLSearchParams(""); - Object.keys(state).forEach((key) => { - const mapEntry = state[key]; - if (Array.isArray(mapEntry)) - mapEntry.forEach((value) => params.append(key, value)); - else if (removeNullishValues && mapEntry == null) - params.delete(key); - else if (removeFalsyValues && !mapEntry) - params.delete(key); - else - params.set(key, mapEntry); - }); - write(params, false); - }, - { deep: true } - ); - function write(params, shouldUpdate) { - pause(); - if (shouldUpdate) - updateState(params); - if (writeMode === "replace") { - window2.history.replaceState( - window2.history.state, - window2.document.title, - window2.location.pathname + constructQuery(params) - ); - } else { - window2.history.pushState( - window2.history.state, - window2.document.title, - window2.location.pathname + constructQuery(params) - ); - } - resume(); - } - function onChanged() { - if (!enableWrite) - return; - write(read(), true); - } - const listenerOptions = { passive: true }; - useEventListener(window2, "popstate", onChanged, listenerOptions); - if (mode !== "history") - useEventListener(window2, "hashchange", onChanged, listenerOptions); - const initial = read(); - if (initial.keys().next().value) - updateState(initial); - else - Object.assign(state, initialValue); - return state; -} -function useUserMedia(options = {}) { - var _a, _b; - const enabled = shallowRef((_a = options.enabled) != null ? _a : false); - const autoSwitch = shallowRef((_b = options.autoSwitch) != null ? _b : true); - const constraints = ref(options.constraints); - const { navigator: navigator2 = defaultNavigator } = options; - const isSupported = useSupported(() => { - var _a2; - return (_a2 = navigator2 == null ? void 0 : navigator2.mediaDevices) == null ? void 0 : _a2.getUserMedia; - }); - const stream = shallowRef(); - function getDeviceOptions(type) { - switch (type) { - case "video": { - if (constraints.value) - return constraints.value.video || false; - break; - } - case "audio": { - if (constraints.value) - return constraints.value.audio || false; - break; - } - } - } - async function _start() { - if (!isSupported.value || stream.value) - return; - stream.value = await navigator2.mediaDevices.getUserMedia({ - video: getDeviceOptions("video"), - audio: getDeviceOptions("audio") - }); - return stream.value; - } - function _stop() { - var _a2; - (_a2 = stream.value) == null ? void 0 : _a2.getTracks().forEach((t) => t.stop()); - stream.value = void 0; - } - function stop() { - _stop(); - enabled.value = false; - } - async function start() { - await _start(); - if (stream.value) - enabled.value = true; - return stream.value; - } - async function restart() { - _stop(); - return await start(); - } - watch( - enabled, - (v) => { - if (v) - _start(); - else _stop(); - }, - { immediate: true } - ); - watch( - constraints, - () => { - if (autoSwitch.value && stream.value) - restart(); - }, - { immediate: true } - ); - tryOnScopeDispose(() => { - stop(); - }); - return { - isSupported, - stream, - start, - stop, - restart, - constraints, - enabled, - autoSwitch - }; -} -function useVModel(props, key, emit, options = {}) { - var _a, _b, _c; - const { - clone = false, - passive = false, - eventName, - deep = false, - defaultValue, - shouldEmit - } = options; - const vm = getCurrentInstance(); - const _emit = emit || (vm == null ? void 0 : vm.emit) || ((_a = vm == null ? void 0 : vm.$emit) == null ? void 0 : _a.bind(vm)) || ((_c = (_b = vm == null ? void 0 : vm.proxy) == null ? void 0 : _b.$emit) == null ? void 0 : _c.bind(vm == null ? void 0 : vm.proxy)); - let event = eventName; - if (!key) { - key = "modelValue"; - } - event = event || `update:${key.toString()}`; - const cloneFn = (val) => !clone ? val : typeof clone === "function" ? clone(val) : cloneFnJSON(val); - const getValue2 = () => isDef(props[key]) ? cloneFn(props[key]) : defaultValue; - const triggerEmit = (value) => { - if (shouldEmit) { - if (shouldEmit(value)) - _emit(event, value); - } else { - _emit(event, value); - } - }; - if (passive) { - const initialValue = getValue2(); - const proxy = ref(initialValue); - let isUpdating = false; - watch( - () => props[key], - (v) => { - if (!isUpdating) { - isUpdating = true; - proxy.value = cloneFn(v); - nextTick(() => isUpdating = false); - } - } - ); - watch( - proxy, - (v) => { - if (!isUpdating && (v !== props[key] || deep)) - triggerEmit(v); - }, - { deep } - ); - return proxy; - } else { - return computed({ - get() { - return getValue2(); - }, - set(value) { - triggerEmit(value); - } - }); - } -} -function useVModels(props, emit, options = {}) { - const ret = {}; - for (const key in props) { - ret[key] = useVModel( - props, - key, - emit, - options - ); - } - return ret; -} -function useVibrate(options) { - const { - pattern = [], - interval = 0, - navigator: navigator2 = defaultNavigator - } = options || {}; - const isSupported = useSupported(() => typeof navigator2 !== "undefined" && "vibrate" in navigator2); - const patternRef = toRef2(pattern); - let intervalControls; - const vibrate = (pattern2 = patternRef.value) => { - if (isSupported.value) - navigator2.vibrate(pattern2); - }; - const stop = () => { - if (isSupported.value) - navigator2.vibrate(0); - intervalControls == null ? void 0 : intervalControls.pause(); - }; - if (interval > 0) { - intervalControls = useIntervalFn( - vibrate, - interval, - { - immediate: false, - immediateCallback: false - } - ); - } - return { - isSupported, - pattern, - intervalControls, - vibrate, - stop - }; -} -function useVirtualList(list, options) { - const { containerStyle, wrapperProps, scrollTo, calculateRange, currentList, containerRef } = "itemHeight" in options ? useVerticalVirtualList(options, list) : useHorizontalVirtualList(options, list); - return { - list: currentList, - scrollTo, - containerProps: { - ref: containerRef, - onScroll: () => { - calculateRange(); - }, - style: containerStyle - }, - wrapperProps - }; -} -function useVirtualListResources(list) { - const containerRef = shallowRef(null); - const size = useElementSize(containerRef); - const currentList = ref([]); - const source = shallowRef(list); - const state = ref({ start: 0, end: 10 }); - return { state, source, currentList, size, containerRef }; -} -function createGetViewCapacity(state, source, itemSize) { - return (containerSize) => { - if (typeof itemSize === "number") - return Math.ceil(containerSize / itemSize); - const { start = 0 } = state.value; - let sum = 0; - let capacity = 0; - for (let i = start; i < source.value.length; i++) { - const size = itemSize(i); - sum += size; - capacity = i; - if (sum > containerSize) - break; - } - return capacity - start; - }; -} -function createGetOffset(source, itemSize) { - return (scrollDirection) => { - if (typeof itemSize === "number") - return Math.floor(scrollDirection / itemSize) + 1; - let sum = 0; - let offset = 0; - for (let i = 0; i < source.value.length; i++) { - const size = itemSize(i); - sum += size; - if (sum >= scrollDirection) { - offset = i; - break; - } - } - return offset + 1; - }; -} -function createCalculateRange(type, overscan, getOffset, getViewCapacity, { containerRef, state, currentList, source }) { - return () => { - const element = containerRef.value; - if (element) { - const offset = getOffset(type === "vertical" ? element.scrollTop : element.scrollLeft); - const viewCapacity = getViewCapacity(type === "vertical" ? element.clientHeight : element.clientWidth); - const from = offset - overscan; - const to = offset + viewCapacity + overscan; - state.value = { - start: from < 0 ? 0 : from, - end: to > source.value.length ? source.value.length : to - }; - currentList.value = source.value.slice(state.value.start, state.value.end).map((ele, index) => ({ - data: ele, - index: index + state.value.start - })); - } - }; -} -function createGetDistance(itemSize, source) { - return (index) => { - if (typeof itemSize === "number") { - const size2 = index * itemSize; - return size2; - } - const size = source.value.slice(0, index).reduce((sum, _, i) => sum + itemSize(i), 0); - return size; - }; -} -function useWatchForSizes(size, list, containerRef, calculateRange) { - watch([size.width, size.height, list, containerRef], () => { - calculateRange(); - }); -} -function createComputedTotalSize(itemSize, source) { - return computed(() => { - if (typeof itemSize === "number") - return source.value.length * itemSize; - return source.value.reduce((sum, _, index) => sum + itemSize(index), 0); - }); -} -var scrollToDictionaryForElementScrollKey = { - horizontal: "scrollLeft", - vertical: "scrollTop" -}; -function createScrollTo(type, calculateRange, getDistance, containerRef) { - return (index) => { - if (containerRef.value) { - containerRef.value[scrollToDictionaryForElementScrollKey[type]] = getDistance(index); - calculateRange(); - } - }; -} -function useHorizontalVirtualList(options, list) { - const resources = useVirtualListResources(list); - const { state, source, currentList, size, containerRef } = resources; - const containerStyle = { overflowX: "auto" }; - const { itemWidth, overscan = 5 } = options; - const getViewCapacity = createGetViewCapacity(state, source, itemWidth); - const getOffset = createGetOffset(source, itemWidth); - const calculateRange = createCalculateRange("horizontal", overscan, getOffset, getViewCapacity, resources); - const getDistanceLeft = createGetDistance(itemWidth, source); - const offsetLeft = computed(() => getDistanceLeft(state.value.start)); - const totalWidth = createComputedTotalSize(itemWidth, source); - useWatchForSizes(size, list, containerRef, calculateRange); - const scrollTo = createScrollTo("horizontal", calculateRange, getDistanceLeft, containerRef); - const wrapperProps = computed(() => { - return { - style: { - height: "100%", - width: `${totalWidth.value - offsetLeft.value}px`, - marginLeft: `${offsetLeft.value}px`, - display: "flex" - } - }; - }); - return { - scrollTo, - calculateRange, - wrapperProps, - containerStyle, - currentList, - containerRef - }; -} -function useVerticalVirtualList(options, list) { - const resources = useVirtualListResources(list); - const { state, source, currentList, size, containerRef } = resources; - const containerStyle = { overflowY: "auto" }; - const { itemHeight, overscan = 5 } = options; - const getViewCapacity = createGetViewCapacity(state, source, itemHeight); - const getOffset = createGetOffset(source, itemHeight); - const calculateRange = createCalculateRange("vertical", overscan, getOffset, getViewCapacity, resources); - const getDistanceTop = createGetDistance(itemHeight, source); - const offsetTop = computed(() => getDistanceTop(state.value.start)); - const totalHeight = createComputedTotalSize(itemHeight, source); - useWatchForSizes(size, list, containerRef, calculateRange); - const scrollTo = createScrollTo("vertical", calculateRange, getDistanceTop, containerRef); - const wrapperProps = computed(() => { - return { - style: { - width: "100%", - height: `${totalHeight.value - offsetTop.value}px`, - marginTop: `${offsetTop.value}px` - } - }; - }); - return { - calculateRange, - scrollTo, - containerStyle, - wrapperProps, - currentList, - containerRef - }; -} -function useWakeLock(options = {}) { - const { - navigator: navigator2 = defaultNavigator, - document: document2 = defaultDocument - } = options; - const requestedType = shallowRef(false); - const sentinel = shallowRef(null); - const documentVisibility = useDocumentVisibility({ document: document2 }); - const isSupported = useSupported(() => navigator2 && "wakeLock" in navigator2); - const isActive = computed(() => !!sentinel.value && documentVisibility.value === "visible"); - if (isSupported.value) { - useEventListener(sentinel, "release", () => { - var _a, _b; - requestedType.value = (_b = (_a = sentinel.value) == null ? void 0 : _a.type) != null ? _b : false; - }, { passive: true }); - whenever( - () => documentVisibility.value === "visible" && (document2 == null ? void 0 : document2.visibilityState) === "visible" && requestedType.value, - (type) => { - requestedType.value = false; - forceRequest(type); - } - ); - } - async function forceRequest(type) { - var _a; - await ((_a = sentinel.value) == null ? void 0 : _a.release()); - sentinel.value = isSupported.value ? await navigator2.wakeLock.request(type) : null; - } - async function request(type) { - if (documentVisibility.value === "visible") - await forceRequest(type); - else - requestedType.value = type; - } - async function release() { - requestedType.value = false; - const s = sentinel.value; - sentinel.value = null; - await (s == null ? void 0 : s.release()); - } - return { - sentinel, - isSupported, - isActive, - request, - forceRequest, - release - }; -} -function useWebNotification(options = {}) { - const { - window: window2 = defaultWindow, - requestPermissions: _requestForPermissions = true - } = options; - const defaultWebNotificationOptions = options; - const isSupported = useSupported(() => { - if (!window2 || !("Notification" in window2)) - return false; - if (Notification.permission === "granted") - return true; - try { - const notification2 = new Notification(""); - notification2.onshow = () => { - notification2.close(); - }; - } catch (e) { - if (e.name === "TypeError") - return false; - } - return true; - }); - const permissionGranted = shallowRef(isSupported.value && "permission" in Notification && Notification.permission === "granted"); - const notification = ref(null); - const ensurePermissions = async () => { - if (!isSupported.value) - return; - if (!permissionGranted.value && Notification.permission !== "denied") { - const result = await Notification.requestPermission(); - if (result === "granted") - permissionGranted.value = true; - } - return permissionGranted.value; - }; - const { on: onClick, trigger: clickTrigger } = createEventHook(); - const { on: onShow, trigger: showTrigger } = createEventHook(); - const { on: onError, trigger: errorTrigger } = createEventHook(); - const { on: onClose, trigger: closeTrigger } = createEventHook(); - const show = async (overrides) => { - if (!isSupported.value || !permissionGranted.value) - return; - const options2 = Object.assign({}, defaultWebNotificationOptions, overrides); - notification.value = new Notification(options2.title || "", options2); - notification.value.onclick = clickTrigger; - notification.value.onshow = showTrigger; - notification.value.onerror = errorTrigger; - notification.value.onclose = closeTrigger; - return notification.value; - }; - const close = () => { - if (notification.value) - notification.value.close(); - notification.value = null; - }; - if (_requestForPermissions) - tryOnMounted(ensurePermissions); - tryOnScopeDispose(close); - if (isSupported.value && window2) { - const document2 = window2.document; - useEventListener(document2, "visibilitychange", (e) => { - e.preventDefault(); - if (document2.visibilityState === "visible") { - close(); - } - }); - } - return { - isSupported, - notification, - ensurePermissions, - permissionGranted, - show, - close, - onClick, - onShow, - onError, - onClose - }; -} -var DEFAULT_PING_MESSAGE = "ping"; -function resolveNestedOptions(options) { - if (options === true) - return {}; - return options; -} -function useWebSocket(url, options = {}) { - const { - onConnected, - onDisconnected, - onError, - onMessage, - immediate = true, - autoConnect = true, - autoClose = true, - protocols = [] - } = options; - const data = ref(null); - const status = shallowRef("CLOSED"); - const wsRef = ref(); - const urlRef = toRef2(url); - let heartbeatPause; - let heartbeatResume; - let explicitlyClosed = false; - let retried = 0; - let bufferedData = []; - let retryTimeout; - let pongTimeoutWait; - const _sendBuffer = () => { - if (bufferedData.length && wsRef.value && status.value === "OPEN") { - for (const buffer of bufferedData) - wsRef.value.send(buffer); - bufferedData = []; - } - }; - const resetRetry = () => { - if (retryTimeout != null) { - clearTimeout(retryTimeout); - retryTimeout = void 0; - } - }; - const resetHeartbeat = () => { - clearTimeout(pongTimeoutWait); - pongTimeoutWait = void 0; - }; - const close = (code = 1e3, reason) => { - resetRetry(); - if (!isClient && !isWorker || !wsRef.value) - return; - explicitlyClosed = true; - resetHeartbeat(); - heartbeatPause == null ? void 0 : heartbeatPause(); - wsRef.value.close(code, reason); - wsRef.value = void 0; - }; - const send = (data2, useBuffer = true) => { - if (!wsRef.value || status.value !== "OPEN") { - if (useBuffer) - bufferedData.push(data2); - return false; - } - _sendBuffer(); - wsRef.value.send(data2); - return true; - }; - const _init = () => { - if (explicitlyClosed || typeof urlRef.value === "undefined") - return; - const ws = new WebSocket(urlRef.value, protocols); - wsRef.value = ws; - status.value = "CONNECTING"; - ws.onopen = () => { - status.value = "OPEN"; - retried = 0; - onConnected == null ? void 0 : onConnected(ws); - heartbeatResume == null ? void 0 : heartbeatResume(); - _sendBuffer(); - }; - ws.onclose = (ev) => { - status.value = "CLOSED"; - resetHeartbeat(); - heartbeatPause == null ? void 0 : heartbeatPause(); - onDisconnected == null ? void 0 : onDisconnected(ws, ev); - if (!explicitlyClosed && options.autoReconnect && (wsRef.value == null || ws === wsRef.value)) { - const { - retries = -1, - delay = 1e3, - onFailed - } = resolveNestedOptions(options.autoReconnect); - const checkRetires = typeof retries === "function" ? retries : () => typeof retries === "number" && (retries < 0 || retried < retries); - if (checkRetires(retried)) { - retried += 1; - retryTimeout = setTimeout(_init, delay); - } else { - onFailed == null ? void 0 : onFailed(); - } - } - }; - ws.onerror = (e) => { - onError == null ? void 0 : onError(ws, e); - }; - ws.onmessage = (e) => { - if (options.heartbeat) { - resetHeartbeat(); - const { - message = DEFAULT_PING_MESSAGE, - responseMessage = message - } = resolveNestedOptions(options.heartbeat); - if (e.data === toValue(responseMessage)) - return; - } - data.value = e.data; - onMessage == null ? void 0 : onMessage(ws, e); - }; - }; - if (options.heartbeat) { - const { - message = DEFAULT_PING_MESSAGE, - interval = 1e3, - pongTimeout = 1e3 - } = resolveNestedOptions(options.heartbeat); - const { pause, resume } = useIntervalFn( - () => { - send(toValue(message), false); - if (pongTimeoutWait != null) - return; - pongTimeoutWait = setTimeout(() => { - close(); - explicitlyClosed = false; - }, pongTimeout); - }, - interval, - { immediate: false } - ); - heartbeatPause = pause; - heartbeatResume = resume; - } - if (autoClose) { - if (isClient) - useEventListener("beforeunload", () => close(), { passive: true }); - tryOnScopeDispose(close); - } - const open = () => { - if (!isClient && !isWorker) - return; - close(); - explicitlyClosed = false; - retried = 0; - _init(); - }; - if (immediate) - open(); - if (autoConnect) - watch(urlRef, open); - return { - data, - status, - close, - send, - open, - ws: wsRef - }; -} -function useWebWorker(arg0, workerOptions, options) { - const { - window: window2 = defaultWindow - } = options != null ? options : {}; - const data = ref(null); - const worker = shallowRef(); - const post = (...args) => { - if (!worker.value) - return; - worker.value.postMessage(...args); - }; - const terminate = function terminate2() { - if (!worker.value) - return; - worker.value.terminate(); - }; - if (window2) { - if (typeof arg0 === "string") - worker.value = new Worker(arg0, workerOptions); - else if (typeof arg0 === "function") - worker.value = arg0(); - else - worker.value = arg0; - worker.value.onmessage = (e) => { - data.value = e.data; - }; - tryOnScopeDispose(() => { - if (worker.value) - worker.value.terminate(); - }); - } - return { - data, - post, - terminate, - worker - }; -} -function depsParser(deps, localDeps) { - if (deps.length === 0 && localDeps.length === 0) - return ""; - const depsString = deps.map((dep) => `'${dep}'`).toString(); - const depsFunctionString = localDeps.filter((dep) => typeof dep === "function").map((fn) => { - const str = fn.toString(); - if (str.trim().startsWith("function")) { - return str; - } else { - const name = fn.name; - return `const ${name} = ${str}`; - } - }).join(";"); - const importString = `importScripts(${depsString});`; - return `${depsString.trim() === "" ? "" : importString} ${depsFunctionString}`; -} -function jobRunner(userFunc) { - return (e) => { - const userFuncArgs = e.data[0]; - return Promise.resolve(userFunc.apply(void 0, userFuncArgs)).then((result) => { - postMessage(["SUCCESS", result]); - }).catch((error) => { - postMessage(["ERROR", error]); - }); - }; -} -function createWorkerBlobUrl(fn, deps, localDeps) { - const blobCode = `${depsParser(deps, localDeps)}; onmessage=(${jobRunner})(${fn})`; - const blob = new Blob([blobCode], { type: "text/javascript" }); - const url = URL.createObjectURL(blob); - return url; -} -function useWebWorkerFn(fn, options = {}) { - const { - dependencies = [], - localDependencies = [], - timeout, - window: window2 = defaultWindow - } = options; - const worker = ref(); - const workerStatus = shallowRef("PENDING"); - const promise = ref({}); - const timeoutId = shallowRef(); - const workerTerminate = (status = "PENDING") => { - if (worker.value && worker.value._url && window2) { - worker.value.terminate(); - URL.revokeObjectURL(worker.value._url); - promise.value = {}; - worker.value = void 0; - window2.clearTimeout(timeoutId.value); - workerStatus.value = status; - } - }; - workerTerminate(); - tryOnScopeDispose(workerTerminate); - const generateWorker = () => { - const blobUrl = createWorkerBlobUrl(fn, dependencies, localDependencies); - const newWorker = new Worker(blobUrl); - newWorker._url = blobUrl; - newWorker.onmessage = (e) => { - const { resolve = () => { - }, reject = () => { - } } = promise.value; - const [status, result] = e.data; - switch (status) { - case "SUCCESS": - resolve(result); - workerTerminate(status); - break; - default: - reject(result); - workerTerminate("ERROR"); - break; - } - }; - newWorker.onerror = (e) => { - const { reject = () => { - } } = promise.value; - e.preventDefault(); - reject(e); - workerTerminate("ERROR"); - }; - if (timeout) { - timeoutId.value = setTimeout( - () => workerTerminate("TIMEOUT_EXPIRED"), - timeout - ); - } - return newWorker; - }; - const callWorker = (...fnArgs) => new Promise((resolve, reject) => { - var _a; - promise.value = { - resolve, - reject - }; - (_a = worker.value) == null ? void 0 : _a.postMessage([[...fnArgs]]); - workerStatus.value = "RUNNING"; - }); - const workerFn = (...fnArgs) => { - if (workerStatus.value === "RUNNING") { - console.error( - "[useWebWorkerFn] You can only run one instance of the worker at a time." - ); - return Promise.reject(); - } - worker.value = generateWorker(); - return callWorker(...fnArgs); - }; - return { - workerFn, - workerStatus, - workerTerminate - }; -} -function useWindowFocus(options = {}) { - const { window: window2 = defaultWindow } = options; - if (!window2) - return shallowRef(false); - const focused = shallowRef(window2.document.hasFocus()); - const listenerOptions = { passive: true }; - useEventListener(window2, "blur", () => { - focused.value = false; - }, listenerOptions); - useEventListener(window2, "focus", () => { - focused.value = true; - }, listenerOptions); - return focused; -} -function useWindowScroll(options = {}) { - const { window: window2 = defaultWindow, ...rest } = options; - return useScroll(window2, rest); -} -function useWindowSize(options = {}) { - const { - window: window2 = defaultWindow, - initialWidth = Number.POSITIVE_INFINITY, - initialHeight = Number.POSITIVE_INFINITY, - listenOrientation = true, - includeScrollbar = true, - type = "inner" - } = options; - const width = shallowRef(initialWidth); - const height = shallowRef(initialHeight); - const update = () => { - if (window2) { - if (type === "outer") { - width.value = window2.outerWidth; - height.value = window2.outerHeight; - } else if (type === "visual" && window2.visualViewport) { - const { width: visualViewportWidth, height: visualViewportHeight, scale } = window2.visualViewport; - width.value = Math.round(visualViewportWidth * scale); - height.value = Math.round(visualViewportHeight * scale); - } else if (includeScrollbar) { - width.value = window2.innerWidth; - height.value = window2.innerHeight; - } else { - width.value = window2.document.documentElement.clientWidth; - height.value = window2.document.documentElement.clientHeight; - } - } - }; - update(); - tryOnMounted(update); - const listenerOptions = { passive: true }; - useEventListener("resize", update, listenerOptions); - if (window2 && type === "visual" && window2.visualViewport) { - useEventListener(window2.visualViewport, "resize", update, listenerOptions); - } - if (listenOrientation) { - const matches = useMediaQuery("(orientation: portrait)"); - watch(matches, () => update()); - } - return { width, height }; -} - -export { - computedEager, - computedWithControl, - tryOnScopeDispose, - createEventHook, - createGlobalState, - injectLocal, - provideLocal, - createInjectionState, - createRef, - createSharedComposable, - extendRef, - get, - isDefined, - makeDestructurable, - reactify, - reactifyObject, - toReactive, - reactiveComputed, - reactiveOmit, - isClient, - isWorker, - isDef, - notNullish, - assert, - isObject, - now, - timestamp, - clamp, - noop, - rand, - hasOwn, - isIOS, - createFilterWrapper, - bypassFilter, - debounceFilter, - throttleFilter, - pausableFilter, - hyphenate, - camelize, - promiseTimeout, - identity, - createSingletonPromise, - invoke, - containsProp, - increaseWithUnit, - pxValue, - objectPick, - objectOmit, - objectEntries, - getLifeCycleTarget, - toArray, - toRef2 as toRef, - resolveRef, - reactivePick, - refAutoReset, - useDebounceFn, - refDebounced, - refDefault, - useThrottleFn, - refThrottled, - refWithControl, - controlledRef, - set, - watchWithFilter, - watchPausable, - syncRef, - syncRefs, - toRefs2 as toRefs, - toValue2 as toValue, - resolveUnref, - tryOnBeforeMount, - tryOnBeforeUnmount, - tryOnMounted, - tryOnUnmounted, - until, - useArrayDifference, - useArrayEvery, - useArrayFilter, - useArrayFind, - useArrayFindIndex, - useArrayFindLast, - useArrayIncludes, - useArrayJoin, - useArrayMap, - useArrayReduce, - useArraySome, - useArrayUnique, - useCounter, - formatDate, - normalizeDate, - useDateFormat, - useIntervalFn, - useInterval, - useLastChanged, - useTimeoutFn, - useTimeout, - useToNumber, - useToString, - useToggle, - watchArray, - watchAtMost, - watchDebounced, - watchDeep, - watchIgnorable, - watchImmediate, - watchOnce, - watchThrottled, - watchTriggerable, - whenever, - computedAsync, - computedInject, - createReusableTemplate, - createTemplatePromise, - createUnrefFn, - defaultWindow, - defaultDocument, - defaultNavigator, - defaultLocation, - unrefElement, - useEventListener, - onClickOutside, - useMounted, - useSupported, - useMutationObserver, - onElementRemoval, - onKeyStroke, - onKeyDown, - onKeyPressed, - onKeyUp, - onLongPress, - onStartTyping, - templateRef, - useActiveElement, - useRafFn, - useAnimate, - useAsyncQueue, - useAsyncState, - useBase64, - useBattery, - useBluetooth, - useSSRWidth, - provideSSRWidth, - useMediaQuery, - breakpointsTailwind, - breakpointsBootstrapV5, - breakpointsVuetifyV2, - breakpointsVuetifyV3, - breakpointsVuetify, - breakpointsAntDesign, - breakpointsQuasar, - breakpointsSematic, - breakpointsMasterCss, - breakpointsPrimeFlex, - breakpointsElement, - useBreakpoints, - useBroadcastChannel, - useBrowserLocation, - useCached, - usePermission, - useClipboard, - useClipboardItems, - cloneFnJSON, - useCloned, - getSSRHandler, - setSSRHandler, - usePreferredDark, - StorageSerializers, - customStorageEventName, - useStorage, - useColorMode, - useConfirmDialog, - useCountdown, - useCssVar, - useCurrentElement, - useCycleList, - useDark, - useManualRefHistory, - useRefHistory, - useDebouncedRefHistory, - useDeviceMotion, - useDeviceOrientation, - useDevicePixelRatio, - useDevicesList, - useDisplayMedia, - useDocumentVisibility, - useDraggable, - useDropZone, - useResizeObserver, - useElementBounding, - useElementByPoint, - useElementHover, - useElementSize, - useIntersectionObserver, - useElementVisibility, - useEventBus, - useEventSource, - useEyeDropper, - useFavicon, - createFetch, - useFetch, - useFileDialog, - useFileSystemAccess, - useFocus, - useFocusWithin, - useFps, - useFullscreen, - mapGamepadToXbox360Controller, - useGamepad, - useGeolocation, - useIdle, - useImage, - useScroll, - useInfiniteScroll, - useKeyModifier, - useLocalStorage, - DefaultMagicKeysAliasMap, - useMagicKeys, - useMediaControls, - useMemoize, - useMemory, - useMouse, - useMouseInElement, - useMousePressed, - useNavigatorLanguage, - useNetwork, - useNow, - useObjectUrl, - useOffsetPagination, - useOnline, - usePageLeave, - useScreenOrientation, - useParallax, - useParentElement, - usePerformanceObserver, - usePointer, - usePointerLock, - usePointerSwipe, - usePreferredColorScheme, - usePreferredContrast, - usePreferredLanguages, - usePreferredReducedMotion, - usePreferredReducedTransparency, - usePrevious, - useScreenSafeArea, - useScriptTag, - useScrollLock, - useSessionStorage, - useShare, - useSorted, - useSpeechRecognition, - useSpeechSynthesis, - useStepper, - useStorageAsync, - useStyleTag, - useSwipe, - useTemplateRefsList, - useTextDirection, - useTextSelection, - useTextareaAutosize, - useThrottledRefHistory, - useTimeAgo, - formatTimeAgo, - useTimeoutPoll, - useTimestamp, - useTitle, - TransitionPresets, - executeTransition, - useTransition, - useUrlSearchParams, - useUserMedia, - useVModel, - useVModels, - useVibrate, - useVirtualList, - useWakeLock, - useWebNotification, - useWebSocket, - useWebWorker, - useWebWorkerFn, - useWindowFocus, - useWindowScroll, - useWindowSize -}; -//# sourceMappingURL=chunk-2CLQ7TTZ.js.map diff --git a/www/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js.map b/www/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js.map deleted file mode 100644 index 64aea30..0000000 --- a/www/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js.map +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": 3, - "sources": ["../../../../node_modules/@vueuse/shared/index.mjs", "../../../../node_modules/@vueuse/core/index.mjs"], - "sourcesContent": ["import { shallowRef, watchEffect, readonly, watch, customRef, getCurrentScope, onScopeDispose, effectScope, getCurrentInstance, hasInjectionContext, inject, provide, ref, isRef, unref, toValue as toValue$1, computed, reactive, toRefs as toRefs$1, toRef as toRef$1, onBeforeMount, nextTick, onBeforeUnmount, onMounted, onUnmounted, isReactive } from 'vue';\n\nfunction computedEager(fn, options) {\n var _a;\n const result = shallowRef();\n watchEffect(() => {\n result.value = fn();\n }, {\n ...options,\n flush: (_a = options == null ? void 0 : options.flush) != null ? _a : \"sync\"\n });\n return readonly(result);\n}\n\nfunction computedWithControl(source, fn) {\n let v = void 0;\n let track;\n let trigger;\n const dirty = shallowRef(true);\n const update = () => {\n dirty.value = true;\n trigger();\n };\n watch(source, update, { flush: \"sync\" });\n const get = typeof fn === \"function\" ? fn : fn.get;\n const set = typeof fn === \"function\" ? void 0 : fn.set;\n const result = customRef((_track, _trigger) => {\n track = _track;\n trigger = _trigger;\n return {\n get() {\n if (dirty.value) {\n v = get(v);\n dirty.value = false;\n }\n track();\n return v;\n },\n set(v2) {\n set == null ? void 0 : set(v2);\n }\n };\n });\n if (Object.isExtensible(result))\n result.trigger = update;\n return result;\n}\n\nfunction tryOnScopeDispose(fn) {\n if (getCurrentScope()) {\n onScopeDispose(fn);\n return true;\n }\n return false;\n}\n\nfunction createEventHook() {\n const fns = /* @__PURE__ */ new Set();\n const off = (fn) => {\n fns.delete(fn);\n };\n const clear = () => {\n fns.clear();\n };\n const on = (fn) => {\n fns.add(fn);\n const offFn = () => off(fn);\n tryOnScopeDispose(offFn);\n return {\n off: offFn\n };\n };\n const trigger = (...args) => {\n return Promise.all(Array.from(fns).map((fn) => fn(...args)));\n };\n return {\n on,\n off,\n trigger,\n clear\n };\n}\n\nfunction createGlobalState(stateFactory) {\n let initialized = false;\n let state;\n const scope = effectScope(true);\n return (...args) => {\n if (!initialized) {\n state = scope.run(() => stateFactory(...args));\n initialized = true;\n }\n return state;\n };\n}\n\nconst localProvidedStateMap = /* @__PURE__ */ new WeakMap();\n\nconst injectLocal = (...args) => {\n var _a;\n const key = args[0];\n const instance = (_a = getCurrentInstance()) == null ? void 0 : _a.proxy;\n if (instance == null && !hasInjectionContext())\n throw new Error(\"injectLocal must be called in setup\");\n if (instance && localProvidedStateMap.has(instance) && key in localProvidedStateMap.get(instance))\n return localProvidedStateMap.get(instance)[key];\n return inject(...args);\n};\n\nconst provideLocal = (key, value) => {\n var _a;\n const instance = (_a = getCurrentInstance()) == null ? void 0 : _a.proxy;\n if (instance == null)\n throw new Error(\"provideLocal must be called in setup\");\n if (!localProvidedStateMap.has(instance))\n localProvidedStateMap.set(instance, /* @__PURE__ */ Object.create(null));\n const localProvidedState = localProvidedStateMap.get(instance);\n localProvidedState[key] = value;\n provide(key, value);\n};\n\nfunction createInjectionState(composable, options) {\n const key = (options == null ? void 0 : options.injectionKey) || Symbol(composable.name || \"InjectionState\");\n const defaultValue = options == null ? void 0 : options.defaultValue;\n const useProvidingState = (...args) => {\n const state = composable(...args);\n provideLocal(key, state);\n return state;\n };\n const useInjectedState = () => injectLocal(key, defaultValue);\n return [useProvidingState, useInjectedState];\n}\n\nfunction createRef(value, deep) {\n if (deep === true) {\n return ref(value);\n } else {\n return shallowRef(value);\n }\n}\n\nfunction createSharedComposable(composable) {\n let subscribers = 0;\n let state;\n let scope;\n const dispose = () => {\n subscribers -= 1;\n if (scope && subscribers <= 0) {\n scope.stop();\n state = void 0;\n scope = void 0;\n }\n };\n return (...args) => {\n subscribers += 1;\n if (!scope) {\n scope = effectScope(true);\n state = scope.run(() => composable(...args));\n }\n tryOnScopeDispose(dispose);\n return state;\n };\n}\n\nfunction extendRef(ref, extend, { enumerable = false, unwrap = true } = {}) {\n for (const [key, value] of Object.entries(extend)) {\n if (key === \"value\")\n continue;\n if (isRef(value) && unwrap) {\n Object.defineProperty(ref, key, {\n get() {\n return value.value;\n },\n set(v) {\n value.value = v;\n },\n enumerable\n });\n } else {\n Object.defineProperty(ref, key, { value, enumerable });\n }\n }\n return ref;\n}\n\nfunction get(obj, key) {\n if (key == null)\n return unref(obj);\n return unref(obj)[key];\n}\n\nfunction isDefined(v) {\n return unref(v) != null;\n}\n\nfunction makeDestructurable(obj, arr) {\n if (typeof Symbol !== \"undefined\") {\n const clone = { ...obj };\n Object.defineProperty(clone, Symbol.iterator, {\n enumerable: false,\n value() {\n let index = 0;\n return {\n next: () => ({\n value: arr[index++],\n done: index > arr.length\n })\n };\n }\n });\n return clone;\n } else {\n return Object.assign([...arr], obj);\n }\n}\n\nfunction reactify(fn, options) {\n const unrefFn = (options == null ? void 0 : options.computedGetter) === false ? unref : toValue$1;\n return function(...args) {\n return computed(() => fn.apply(this, args.map((i) => unrefFn(i))));\n };\n}\n\nfunction reactifyObject(obj, optionsOrKeys = {}) {\n let keys = [];\n let options;\n if (Array.isArray(optionsOrKeys)) {\n keys = optionsOrKeys;\n } else {\n options = optionsOrKeys;\n const { includeOwnProperties = true } = optionsOrKeys;\n keys.push(...Object.keys(obj));\n if (includeOwnProperties)\n keys.push(...Object.getOwnPropertyNames(obj));\n }\n return Object.fromEntries(\n keys.map((key) => {\n const value = obj[key];\n return [\n key,\n typeof value === \"function\" ? reactify(value.bind(obj), options) : value\n ];\n })\n );\n}\n\nfunction toReactive(objectRef) {\n if (!isRef(objectRef))\n return reactive(objectRef);\n const proxy = new Proxy({}, {\n get(_, p, receiver) {\n return unref(Reflect.get(objectRef.value, p, receiver));\n },\n set(_, p, value) {\n if (isRef(objectRef.value[p]) && !isRef(value))\n objectRef.value[p].value = value;\n else\n objectRef.value[p] = value;\n return true;\n },\n deleteProperty(_, p) {\n return Reflect.deleteProperty(objectRef.value, p);\n },\n has(_, p) {\n return Reflect.has(objectRef.value, p);\n },\n ownKeys() {\n return Object.keys(objectRef.value);\n },\n getOwnPropertyDescriptor() {\n return {\n enumerable: true,\n configurable: true\n };\n }\n });\n return reactive(proxy);\n}\n\nfunction reactiveComputed(fn) {\n return toReactive(computed(fn));\n}\n\nfunction reactiveOmit(obj, ...keys) {\n const flatKeys = keys.flat();\n const predicate = flatKeys[0];\n return reactiveComputed(() => typeof predicate === \"function\" ? Object.fromEntries(Object.entries(toRefs$1(obj)).filter(([k, v]) => !predicate(toValue$1(v), k))) : Object.fromEntries(Object.entries(toRefs$1(obj)).filter((e) => !flatKeys.includes(e[0]))));\n}\n\nconst isClient = typeof window !== \"undefined\" && typeof document !== \"undefined\";\nconst isWorker = typeof WorkerGlobalScope !== \"undefined\" && globalThis instanceof WorkerGlobalScope;\nconst isDef = (val) => typeof val !== \"undefined\";\nconst notNullish = (val) => val != null;\nconst assert = (condition, ...infos) => {\n if (!condition)\n console.warn(...infos);\n};\nconst toString = Object.prototype.toString;\nconst isObject = (val) => toString.call(val) === \"[object Object]\";\nconst now = () => Date.now();\nconst timestamp = () => +Date.now();\nconst clamp = (n, min, max) => Math.min(max, Math.max(min, n));\nconst noop = () => {\n};\nconst rand = (min, max) => {\n min = Math.ceil(min);\n max = Math.floor(max);\n return Math.floor(Math.random() * (max - min + 1)) + min;\n};\nconst hasOwn = (val, key) => Object.prototype.hasOwnProperty.call(val, key);\nconst isIOS = /* @__PURE__ */ getIsIOS();\nfunction getIsIOS() {\n var _a, _b;\n return isClient && ((_a = window == null ? void 0 : window.navigator) == null ? void 0 : _a.userAgent) && (/iP(?:ad|hone|od)/.test(window.navigator.userAgent) || ((_b = window == null ? void 0 : window.navigator) == null ? void 0 : _b.maxTouchPoints) > 2 && /iPad|Macintosh/.test(window == null ? void 0 : window.navigator.userAgent));\n}\n\nfunction createFilterWrapper(filter, fn) {\n function wrapper(...args) {\n return new Promise((resolve, reject) => {\n Promise.resolve(filter(() => fn.apply(this, args), { fn, thisArg: this, args })).then(resolve).catch(reject);\n });\n }\n return wrapper;\n}\nconst bypassFilter = (invoke) => {\n return invoke();\n};\nfunction debounceFilter(ms, options = {}) {\n let timer;\n let maxTimer;\n let lastRejector = noop;\n const _clearTimeout = (timer2) => {\n clearTimeout(timer2);\n lastRejector();\n lastRejector = noop;\n };\n let lastInvoker;\n const filter = (invoke) => {\n const duration = toValue$1(ms);\n const maxDuration = toValue$1(options.maxWait);\n if (timer)\n _clearTimeout(timer);\n if (duration <= 0 || maxDuration !== void 0 && maxDuration <= 0) {\n if (maxTimer) {\n _clearTimeout(maxTimer);\n maxTimer = null;\n }\n return Promise.resolve(invoke());\n }\n return new Promise((resolve, reject) => {\n lastRejector = options.rejectOnCancel ? reject : resolve;\n lastInvoker = invoke;\n if (maxDuration && !maxTimer) {\n maxTimer = setTimeout(() => {\n if (timer)\n _clearTimeout(timer);\n maxTimer = null;\n resolve(lastInvoker());\n }, maxDuration);\n }\n timer = setTimeout(() => {\n if (maxTimer)\n _clearTimeout(maxTimer);\n maxTimer = null;\n resolve(invoke());\n }, duration);\n });\n };\n return filter;\n}\nfunction throttleFilter(...args) {\n let lastExec = 0;\n let timer;\n let isLeading = true;\n let lastRejector = noop;\n let lastValue;\n let ms;\n let trailing;\n let leading;\n let rejectOnCancel;\n if (!isRef(args[0]) && typeof args[0] === \"object\")\n ({ delay: ms, trailing = true, leading = true, rejectOnCancel = false } = args[0]);\n else\n [ms, trailing = true, leading = true, rejectOnCancel = false] = args;\n const clear = () => {\n if (timer) {\n clearTimeout(timer);\n timer = void 0;\n lastRejector();\n lastRejector = noop;\n }\n };\n const filter = (_invoke) => {\n const duration = toValue$1(ms);\n const elapsed = Date.now() - lastExec;\n const invoke = () => {\n return lastValue = _invoke();\n };\n clear();\n if (duration <= 0) {\n lastExec = Date.now();\n return invoke();\n }\n if (elapsed > duration && (leading || !isLeading)) {\n lastExec = Date.now();\n invoke();\n } else if (trailing) {\n lastValue = new Promise((resolve, reject) => {\n lastRejector = rejectOnCancel ? reject : resolve;\n timer = setTimeout(() => {\n lastExec = Date.now();\n isLeading = true;\n resolve(invoke());\n clear();\n }, Math.max(0, duration - elapsed));\n });\n }\n if (!leading && !timer)\n timer = setTimeout(() => isLeading = true, duration);\n isLeading = false;\n return lastValue;\n };\n return filter;\n}\nfunction pausableFilter(extendFilter = bypassFilter, options = {}) {\n const {\n initialState = \"active\"\n } = options;\n const isActive = toRef(initialState === \"active\");\n function pause() {\n isActive.value = false;\n }\n function resume() {\n isActive.value = true;\n }\n const eventFilter = (...args) => {\n if (isActive.value)\n extendFilter(...args);\n };\n return { isActive: readonly(isActive), pause, resume, eventFilter };\n}\n\nfunction cacheStringFunction(fn) {\n const cache = /* @__PURE__ */ Object.create(null);\n return (str) => {\n const hit = cache[str];\n return hit || (cache[str] = fn(str));\n };\n}\nconst hyphenateRE = /\\B([A-Z])/g;\nconst hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, \"-$1\").toLowerCase());\nconst camelizeRE = /-(\\w)/g;\nconst camelize = cacheStringFunction((str) => {\n return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : \"\");\n});\n\nfunction promiseTimeout(ms, throwOnTimeout = false, reason = \"Timeout\") {\n return new Promise((resolve, reject) => {\n if (throwOnTimeout)\n setTimeout(() => reject(reason), ms);\n else\n setTimeout(resolve, ms);\n });\n}\nfunction identity(arg) {\n return arg;\n}\nfunction createSingletonPromise(fn) {\n let _promise;\n function wrapper() {\n if (!_promise)\n _promise = fn();\n return _promise;\n }\n wrapper.reset = async () => {\n const _prev = _promise;\n _promise = void 0;\n if (_prev)\n await _prev;\n };\n return wrapper;\n}\nfunction invoke(fn) {\n return fn();\n}\nfunction containsProp(obj, ...props) {\n return props.some((k) => k in obj);\n}\nfunction increaseWithUnit(target, delta) {\n var _a;\n if (typeof target === \"number\")\n return target + delta;\n const value = ((_a = target.match(/^-?\\d+\\.?\\d*/)) == null ? void 0 : _a[0]) || \"\";\n const unit = target.slice(value.length);\n const result = Number.parseFloat(value) + delta;\n if (Number.isNaN(result))\n return target;\n return result + unit;\n}\nfunction pxValue(px) {\n return px.endsWith(\"rem\") ? Number.parseFloat(px) * 16 : Number.parseFloat(px);\n}\nfunction objectPick(obj, keys, omitUndefined = false) {\n return keys.reduce((n, k) => {\n if (k in obj) {\n if (!omitUndefined || obj[k] !== void 0)\n n[k] = obj[k];\n }\n return n;\n }, {});\n}\nfunction objectOmit(obj, keys, omitUndefined = false) {\n return Object.fromEntries(Object.entries(obj).filter(([key, value]) => {\n return (!omitUndefined || value !== void 0) && !keys.includes(key);\n }));\n}\nfunction objectEntries(obj) {\n return Object.entries(obj);\n}\nfunction getLifeCycleTarget(target) {\n return target || getCurrentInstance();\n}\nfunction toArray(value) {\n return Array.isArray(value) ? value : [value];\n}\n\nfunction toRef(...args) {\n if (args.length !== 1)\n return toRef$1(...args);\n const r = args[0];\n return typeof r === \"function\" ? readonly(customRef(() => ({ get: r, set: noop }))) : ref(r);\n}\nconst resolveRef = toRef;\n\nfunction reactivePick(obj, ...keys) {\n const flatKeys = keys.flat();\n const predicate = flatKeys[0];\n return reactiveComputed(() => typeof predicate === \"function\" ? Object.fromEntries(Object.entries(toRefs$1(obj)).filter(([k, v]) => predicate(toValue$1(v), k))) : Object.fromEntries(flatKeys.map((k) => [k, toRef(obj, k)])));\n}\n\nfunction refAutoReset(defaultValue, afterMs = 1e4) {\n return customRef((track, trigger) => {\n let value = toValue$1(defaultValue);\n let timer;\n const resetAfter = () => setTimeout(() => {\n value = toValue$1(defaultValue);\n trigger();\n }, toValue$1(afterMs));\n tryOnScopeDispose(() => {\n clearTimeout(timer);\n });\n return {\n get() {\n track();\n return value;\n },\n set(newValue) {\n value = newValue;\n trigger();\n clearTimeout(timer);\n timer = resetAfter();\n }\n };\n });\n}\n\nfunction useDebounceFn(fn, ms = 200, options = {}) {\n return createFilterWrapper(\n debounceFilter(ms, options),\n fn\n );\n}\n\nfunction refDebounced(value, ms = 200, options = {}) {\n const debounced = ref(value.value);\n const updater = useDebounceFn(() => {\n debounced.value = value.value;\n }, ms, options);\n watch(value, () => updater());\n return debounced;\n}\n\nfunction refDefault(source, defaultValue) {\n return computed({\n get() {\n var _a;\n return (_a = source.value) != null ? _a : defaultValue;\n },\n set(value) {\n source.value = value;\n }\n });\n}\n\nfunction useThrottleFn(fn, ms = 200, trailing = false, leading = true, rejectOnCancel = false) {\n return createFilterWrapper(\n throttleFilter(ms, trailing, leading, rejectOnCancel),\n fn\n );\n}\n\nfunction refThrottled(value, delay = 200, trailing = true, leading = true) {\n if (delay <= 0)\n return value;\n const throttled = ref(value.value);\n const updater = useThrottleFn(() => {\n throttled.value = value.value;\n }, delay, trailing, leading);\n watch(value, () => updater());\n return throttled;\n}\n\nfunction refWithControl(initial, options = {}) {\n let source = initial;\n let track;\n let trigger;\n const ref = customRef((_track, _trigger) => {\n track = _track;\n trigger = _trigger;\n return {\n get() {\n return get();\n },\n set(v) {\n set(v);\n }\n };\n });\n function get(tracking = true) {\n if (tracking)\n track();\n return source;\n }\n function set(value, triggering = true) {\n var _a, _b;\n if (value === source)\n return;\n const old = source;\n if (((_a = options.onBeforeChange) == null ? void 0 : _a.call(options, value, old)) === false)\n return;\n source = value;\n (_b = options.onChanged) == null ? void 0 : _b.call(options, value, old);\n if (triggering)\n trigger();\n }\n const untrackedGet = () => get(false);\n const silentSet = (v) => set(v, false);\n const peek = () => get(false);\n const lay = (v) => set(v, false);\n return extendRef(\n ref,\n {\n get,\n set,\n untrackedGet,\n silentSet,\n peek,\n lay\n },\n { enumerable: true }\n );\n}\nconst controlledRef = refWithControl;\n\nfunction set(...args) {\n if (args.length === 2) {\n const [ref, value] = args;\n ref.value = value;\n }\n if (args.length === 3) {\n const [target, key, value] = args;\n target[key] = value;\n }\n}\n\nfunction watchWithFilter(source, cb, options = {}) {\n const {\n eventFilter = bypassFilter,\n ...watchOptions\n } = options;\n return watch(\n source,\n createFilterWrapper(\n eventFilter,\n cb\n ),\n watchOptions\n );\n}\n\nfunction watchPausable(source, cb, options = {}) {\n const {\n eventFilter: filter,\n initialState = \"active\",\n ...watchOptions\n } = options;\n const { eventFilter, pause, resume, isActive } = pausableFilter(filter, { initialState });\n const stop = watchWithFilter(\n source,\n cb,\n {\n ...watchOptions,\n eventFilter\n }\n );\n return { stop, pause, resume, isActive };\n}\n\nfunction syncRef(left, right, ...[options]) {\n const {\n flush = \"sync\",\n deep = false,\n immediate = true,\n direction = \"both\",\n transform = {}\n } = options || {};\n const watchers = [];\n const transformLTR = \"ltr\" in transform && transform.ltr || ((v) => v);\n const transformRTL = \"rtl\" in transform && transform.rtl || ((v) => v);\n if (direction === \"both\" || direction === \"ltr\") {\n watchers.push(watchPausable(\n left,\n (newValue) => {\n watchers.forEach((w) => w.pause());\n right.value = transformLTR(newValue);\n watchers.forEach((w) => w.resume());\n },\n { flush, deep, immediate }\n ));\n }\n if (direction === \"both\" || direction === \"rtl\") {\n watchers.push(watchPausable(\n right,\n (newValue) => {\n watchers.forEach((w) => w.pause());\n left.value = transformRTL(newValue);\n watchers.forEach((w) => w.resume());\n },\n { flush, deep, immediate }\n ));\n }\n const stop = () => {\n watchers.forEach((w) => w.stop());\n };\n return stop;\n}\n\nfunction syncRefs(source, targets, options = {}) {\n const {\n flush = \"sync\",\n deep = false,\n immediate = true\n } = options;\n const targetsArray = toArray(targets);\n return watch(\n source,\n (newValue) => targetsArray.forEach((target) => target.value = newValue),\n { flush, deep, immediate }\n );\n}\n\nfunction toRefs(objectRef, options = {}) {\n if (!isRef(objectRef))\n return toRefs$1(objectRef);\n const result = Array.isArray(objectRef.value) ? Array.from({ length: objectRef.value.length }) : {};\n for (const key in objectRef.value) {\n result[key] = customRef(() => ({\n get() {\n return objectRef.value[key];\n },\n set(v) {\n var _a;\n const replaceRef = (_a = toValue$1(options.replaceRef)) != null ? _a : true;\n if (replaceRef) {\n if (Array.isArray(objectRef.value)) {\n const copy = [...objectRef.value];\n copy[key] = v;\n objectRef.value = copy;\n } else {\n const newObject = { ...objectRef.value, [key]: v };\n Object.setPrototypeOf(newObject, Object.getPrototypeOf(objectRef.value));\n objectRef.value = newObject;\n }\n } else {\n objectRef.value[key] = v;\n }\n }\n }));\n }\n return result;\n}\n\nconst toValue = toValue$1;\nconst resolveUnref = toValue$1;\n\nfunction tryOnBeforeMount(fn, sync = true, target) {\n const instance = getLifeCycleTarget(target);\n if (instance)\n onBeforeMount(fn, target);\n else if (sync)\n fn();\n else\n nextTick(fn);\n}\n\nfunction tryOnBeforeUnmount(fn, target) {\n const instance = getLifeCycleTarget(target);\n if (instance)\n onBeforeUnmount(fn, target);\n}\n\nfunction tryOnMounted(fn, sync = true, target) {\n const instance = getLifeCycleTarget();\n if (instance)\n onMounted(fn, target);\n else if (sync)\n fn();\n else\n nextTick(fn);\n}\n\nfunction tryOnUnmounted(fn, target) {\n const instance = getLifeCycleTarget(target);\n if (instance)\n onUnmounted(fn, target);\n}\n\nfunction createUntil(r, isNot = false) {\n function toMatch(condition, { flush = \"sync\", deep = false, timeout, throwOnTimeout } = {}) {\n let stop = null;\n const watcher = new Promise((resolve) => {\n stop = watch(\n r,\n (v) => {\n if (condition(v) !== isNot) {\n if (stop)\n stop();\n else\n nextTick(() => stop == null ? void 0 : stop());\n resolve(v);\n }\n },\n {\n flush,\n deep,\n immediate: true\n }\n );\n });\n const promises = [watcher];\n if (timeout != null) {\n promises.push(\n promiseTimeout(timeout, throwOnTimeout).then(() => toValue$1(r)).finally(() => stop == null ? void 0 : stop())\n );\n }\n return Promise.race(promises);\n }\n function toBe(value, options) {\n if (!isRef(value))\n return toMatch((v) => v === value, options);\n const { flush = \"sync\", deep = false, timeout, throwOnTimeout } = options != null ? options : {};\n let stop = null;\n const watcher = new Promise((resolve) => {\n stop = watch(\n [r, value],\n ([v1, v2]) => {\n if (isNot !== (v1 === v2)) {\n if (stop)\n stop();\n else\n nextTick(() => stop == null ? void 0 : stop());\n resolve(v1);\n }\n },\n {\n flush,\n deep,\n immediate: true\n }\n );\n });\n const promises = [watcher];\n if (timeout != null) {\n promises.push(\n promiseTimeout(timeout, throwOnTimeout).then(() => toValue$1(r)).finally(() => {\n stop == null ? void 0 : stop();\n return toValue$1(r);\n })\n );\n }\n return Promise.race(promises);\n }\n function toBeTruthy(options) {\n return toMatch((v) => Boolean(v), options);\n }\n function toBeNull(options) {\n return toBe(null, options);\n }\n function toBeUndefined(options) {\n return toBe(void 0, options);\n }\n function toBeNaN(options) {\n return toMatch(Number.isNaN, options);\n }\n function toContains(value, options) {\n return toMatch((v) => {\n const array = Array.from(v);\n return array.includes(value) || array.includes(toValue$1(value));\n }, options);\n }\n function changed(options) {\n return changedTimes(1, options);\n }\n function changedTimes(n = 1, options) {\n let count = -1;\n return toMatch(() => {\n count += 1;\n return count >= n;\n }, options);\n }\n if (Array.isArray(toValue$1(r))) {\n const instance = {\n toMatch,\n toContains,\n changed,\n changedTimes,\n get not() {\n return createUntil(r, !isNot);\n }\n };\n return instance;\n } else {\n const instance = {\n toMatch,\n toBe,\n toBeTruthy,\n toBeNull,\n toBeNaN,\n toBeUndefined,\n changed,\n changedTimes,\n get not() {\n return createUntil(r, !isNot);\n }\n };\n return instance;\n }\n}\nfunction until(r) {\n return createUntil(r);\n}\n\nfunction defaultComparator(value, othVal) {\n return value === othVal;\n}\nfunction useArrayDifference(...args) {\n var _a, _b;\n const list = args[0];\n const values = args[1];\n let compareFn = (_a = args[2]) != null ? _a : defaultComparator;\n const {\n symmetric = false\n } = (_b = args[3]) != null ? _b : {};\n if (typeof compareFn === \"string\") {\n const key = compareFn;\n compareFn = (value, othVal) => value[key] === othVal[key];\n }\n const diff1 = computed(() => toValue$1(list).filter((x) => toValue$1(values).findIndex((y) => compareFn(x, y)) === -1));\n if (symmetric) {\n const diff2 = computed(() => toValue$1(values).filter((x) => toValue$1(list).findIndex((y) => compareFn(x, y)) === -1));\n return computed(() => symmetric ? [...toValue$1(diff1), ...toValue$1(diff2)] : toValue$1(diff1));\n } else {\n return diff1;\n }\n}\n\nfunction useArrayEvery(list, fn) {\n return computed(() => toValue$1(list).every((element, index, array) => fn(toValue$1(element), index, array)));\n}\n\nfunction useArrayFilter(list, fn) {\n return computed(() => toValue$1(list).map((i) => toValue$1(i)).filter(fn));\n}\n\nfunction useArrayFind(list, fn) {\n return computed(() => toValue$1(\n toValue$1(list).find((element, index, array) => fn(toValue$1(element), index, array))\n ));\n}\n\nfunction useArrayFindIndex(list, fn) {\n return computed(() => toValue$1(list).findIndex((element, index, array) => fn(toValue$1(element), index, array)));\n}\n\nfunction findLast(arr, cb) {\n let index = arr.length;\n while (index-- > 0) {\n if (cb(arr[index], index, arr))\n return arr[index];\n }\n return void 0;\n}\nfunction useArrayFindLast(list, fn) {\n return computed(() => toValue$1(\n !Array.prototype.findLast ? findLast(toValue$1(list), (element, index, array) => fn(toValue$1(element), index, array)) : toValue$1(list).findLast((element, index, array) => fn(toValue$1(element), index, array))\n ));\n}\n\nfunction isArrayIncludesOptions(obj) {\n return isObject(obj) && containsProp(obj, \"formIndex\", \"comparator\");\n}\nfunction useArrayIncludes(...args) {\n var _a;\n const list = args[0];\n const value = args[1];\n let comparator = args[2];\n let formIndex = 0;\n if (isArrayIncludesOptions(comparator)) {\n formIndex = (_a = comparator.fromIndex) != null ? _a : 0;\n comparator = comparator.comparator;\n }\n if (typeof comparator === \"string\") {\n const key = comparator;\n comparator = (element, value2) => element[key] === toValue$1(value2);\n }\n comparator = comparator != null ? comparator : (element, value2) => element === toValue$1(value2);\n return computed(() => toValue$1(list).slice(formIndex).some((element, index, array) => comparator(\n toValue$1(element),\n toValue$1(value),\n index,\n toValue$1(array)\n )));\n}\n\nfunction useArrayJoin(list, separator) {\n return computed(() => toValue$1(list).map((i) => toValue$1(i)).join(toValue$1(separator)));\n}\n\nfunction useArrayMap(list, fn) {\n return computed(() => toValue$1(list).map((i) => toValue$1(i)).map(fn));\n}\n\nfunction useArrayReduce(list, reducer, ...args) {\n const reduceCallback = (sum, value, index) => reducer(toValue$1(sum), toValue$1(value), index);\n return computed(() => {\n const resolved = toValue$1(list);\n return args.length ? resolved.reduce(reduceCallback, typeof args[0] === \"function\" ? toValue$1(args[0]()) : toValue$1(args[0])) : resolved.reduce(reduceCallback);\n });\n}\n\nfunction useArraySome(list, fn) {\n return computed(() => toValue$1(list).some((element, index, array) => fn(toValue$1(element), index, array)));\n}\n\nfunction uniq(array) {\n return Array.from(new Set(array));\n}\nfunction uniqueElementsBy(array, fn) {\n return array.reduce((acc, v) => {\n if (!acc.some((x) => fn(v, x, array)))\n acc.push(v);\n return acc;\n }, []);\n}\nfunction useArrayUnique(list, compareFn) {\n return computed(() => {\n const resolvedList = toValue$1(list).map((element) => toValue$1(element));\n return compareFn ? uniqueElementsBy(resolvedList, compareFn) : uniq(resolvedList);\n });\n}\n\nfunction useCounter(initialValue = 0, options = {}) {\n let _initialValue = unref(initialValue);\n const count = shallowRef(initialValue);\n const {\n max = Number.POSITIVE_INFINITY,\n min = Number.NEGATIVE_INFINITY\n } = options;\n const inc = (delta = 1) => count.value = Math.max(Math.min(max, count.value + delta), min);\n const dec = (delta = 1) => count.value = Math.min(Math.max(min, count.value - delta), max);\n const get = () => count.value;\n const set = (val) => count.value = Math.max(min, Math.min(max, val));\n const reset = (val = _initialValue) => {\n _initialValue = val;\n return set(val);\n };\n return { count, inc, dec, get, set, reset };\n}\n\nconst REGEX_PARSE = /^(\\d{4})[-/]?(\\d{1,2})?[-/]?(\\d{0,2})[T\\s]*(\\d{1,2})?:?(\\d{1,2})?:?(\\d{1,2})?[.:]?(\\d+)?$/i;\nconst REGEX_FORMAT = /[YMDHhms]o|\\[([^\\]]+)\\]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a{1,2}|A{1,2}|m{1,2}|s{1,2}|Z{1,2}|z{1,4}|SSS/g;\nfunction defaultMeridiem(hours, minutes, isLowercase, hasPeriod) {\n let m = hours < 12 ? \"AM\" : \"PM\";\n if (hasPeriod)\n m = m.split(\"\").reduce((acc, curr) => acc += `${curr}.`, \"\");\n return isLowercase ? m.toLowerCase() : m;\n}\nfunction formatOrdinal(num) {\n const suffixes = [\"th\", \"st\", \"nd\", \"rd\"];\n const v = num % 100;\n return num + (suffixes[(v - 20) % 10] || suffixes[v] || suffixes[0]);\n}\nfunction formatDate(date, formatStr, options = {}) {\n var _a;\n const years = date.getFullYear();\n const month = date.getMonth();\n const days = date.getDate();\n const hours = date.getHours();\n const minutes = date.getMinutes();\n const seconds = date.getSeconds();\n const milliseconds = date.getMilliseconds();\n const day = date.getDay();\n const meridiem = (_a = options.customMeridiem) != null ? _a : defaultMeridiem;\n const stripTimeZone = (dateString) => {\n var _a2;\n return (_a2 = dateString.split(\" \")[1]) != null ? _a2 : \"\";\n };\n const matches = {\n Yo: () => formatOrdinal(years),\n YY: () => String(years).slice(-2),\n YYYY: () => years,\n M: () => month + 1,\n Mo: () => formatOrdinal(month + 1),\n MM: () => `${month + 1}`.padStart(2, \"0\"),\n MMM: () => date.toLocaleDateString(toValue$1(options.locales), { month: \"short\" }),\n MMMM: () => date.toLocaleDateString(toValue$1(options.locales), { month: \"long\" }),\n D: () => String(days),\n Do: () => formatOrdinal(days),\n DD: () => `${days}`.padStart(2, \"0\"),\n H: () => String(hours),\n Ho: () => formatOrdinal(hours),\n HH: () => `${hours}`.padStart(2, \"0\"),\n h: () => `${hours % 12 || 12}`.padStart(1, \"0\"),\n ho: () => formatOrdinal(hours % 12 || 12),\n hh: () => `${hours % 12 || 12}`.padStart(2, \"0\"),\n m: () => String(minutes),\n mo: () => formatOrdinal(minutes),\n mm: () => `${minutes}`.padStart(2, \"0\"),\n s: () => String(seconds),\n so: () => formatOrdinal(seconds),\n ss: () => `${seconds}`.padStart(2, \"0\"),\n SSS: () => `${milliseconds}`.padStart(3, \"0\"),\n d: () => day,\n dd: () => date.toLocaleDateString(toValue$1(options.locales), { weekday: \"narrow\" }),\n ddd: () => date.toLocaleDateString(toValue$1(options.locales), { weekday: \"short\" }),\n dddd: () => date.toLocaleDateString(toValue$1(options.locales), { weekday: \"long\" }),\n A: () => meridiem(hours, minutes),\n AA: () => meridiem(hours, minutes, false, true),\n a: () => meridiem(hours, minutes, true),\n aa: () => meridiem(hours, minutes, true, true),\n z: () => stripTimeZone(date.toLocaleDateString(toValue$1(options.locales), { timeZoneName: \"shortOffset\" })),\n zz: () => stripTimeZone(date.toLocaleDateString(toValue$1(options.locales), { timeZoneName: \"shortOffset\" })),\n zzz: () => stripTimeZone(date.toLocaleDateString(toValue$1(options.locales), { timeZoneName: \"shortOffset\" })),\n zzzz: () => stripTimeZone(date.toLocaleDateString(toValue$1(options.locales), { timeZoneName: \"longOffset\" }))\n };\n return formatStr.replace(REGEX_FORMAT, (match, $1) => {\n var _a2, _b;\n return (_b = $1 != null ? $1 : (_a2 = matches[match]) == null ? void 0 : _a2.call(matches)) != null ? _b : match;\n });\n}\nfunction normalizeDate(date) {\n if (date === null)\n return new Date(Number.NaN);\n if (date === void 0)\n return /* @__PURE__ */ new Date();\n if (date instanceof Date)\n return new Date(date);\n if (typeof date === \"string\" && !/Z$/i.test(date)) {\n const d = date.match(REGEX_PARSE);\n if (d) {\n const m = d[2] - 1 || 0;\n const ms = (d[7] || \"0\").substring(0, 3);\n return new Date(d[1], m, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, ms);\n }\n }\n return new Date(date);\n}\nfunction useDateFormat(date, formatStr = \"HH:mm:ss\", options = {}) {\n return computed(() => formatDate(normalizeDate(toValue$1(date)), toValue$1(formatStr), options));\n}\n\nfunction useIntervalFn(cb, interval = 1e3, options = {}) {\n const {\n immediate = true,\n immediateCallback = false\n } = options;\n let timer = null;\n const isActive = shallowRef(false);\n function clean() {\n if (timer) {\n clearInterval(timer);\n timer = null;\n }\n }\n function pause() {\n isActive.value = false;\n clean();\n }\n function resume() {\n const intervalValue = toValue$1(interval);\n if (intervalValue <= 0)\n return;\n isActive.value = true;\n if (immediateCallback)\n cb();\n clean();\n if (isActive.value)\n timer = setInterval(cb, intervalValue);\n }\n if (immediate && isClient)\n resume();\n if (isRef(interval) || typeof interval === \"function\") {\n const stopWatch = watch(interval, () => {\n if (isActive.value && isClient)\n resume();\n });\n tryOnScopeDispose(stopWatch);\n }\n tryOnScopeDispose(pause);\n return {\n isActive,\n pause,\n resume\n };\n}\n\nfunction useInterval(interval = 1e3, options = {}) {\n const {\n controls: exposeControls = false,\n immediate = true,\n callback\n } = options;\n const counter = shallowRef(0);\n const update = () => counter.value += 1;\n const reset = () => {\n counter.value = 0;\n };\n const controls = useIntervalFn(\n callback ? () => {\n update();\n callback(counter.value);\n } : update,\n interval,\n { immediate }\n );\n if (exposeControls) {\n return {\n counter,\n reset,\n ...controls\n };\n } else {\n return counter;\n }\n}\n\nfunction useLastChanged(source, options = {}) {\n var _a;\n const ms = shallowRef((_a = options.initialValue) != null ? _a : null);\n watch(\n source,\n () => ms.value = timestamp(),\n options\n );\n return ms;\n}\n\nfunction useTimeoutFn(cb, interval, options = {}) {\n const {\n immediate = true,\n immediateCallback = false\n } = options;\n const isPending = shallowRef(false);\n let timer = null;\n function clear() {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n }\n function stop() {\n isPending.value = false;\n clear();\n }\n function start(...args) {\n if (immediateCallback)\n cb();\n clear();\n isPending.value = true;\n timer = setTimeout(() => {\n isPending.value = false;\n timer = null;\n cb(...args);\n }, toValue$1(interval));\n }\n if (immediate) {\n isPending.value = true;\n if (isClient)\n start();\n }\n tryOnScopeDispose(stop);\n return {\n isPending: readonly(isPending),\n start,\n stop\n };\n}\n\nfunction useTimeout(interval = 1e3, options = {}) {\n const {\n controls: exposeControls = false,\n callback\n } = options;\n const controls = useTimeoutFn(\n callback != null ? callback : noop,\n interval,\n options\n );\n const ready = computed(() => !controls.isPending.value);\n if (exposeControls) {\n return {\n ready,\n ...controls\n };\n } else {\n return ready;\n }\n}\n\nfunction useToNumber(value, options = {}) {\n const {\n method = \"parseFloat\",\n radix,\n nanToZero\n } = options;\n return computed(() => {\n let resolved = toValue$1(value);\n if (typeof method === \"function\")\n resolved = method(resolved);\n else if (typeof resolved === \"string\")\n resolved = Number[method](resolved, radix);\n if (nanToZero && Number.isNaN(resolved))\n resolved = 0;\n return resolved;\n });\n}\n\nfunction useToString(value) {\n return computed(() => `${toValue$1(value)}`);\n}\n\nfunction useToggle(initialValue = false, options = {}) {\n const {\n truthyValue = true,\n falsyValue = false\n } = options;\n const valueIsRef = isRef(initialValue);\n const _value = shallowRef(initialValue);\n function toggle(value) {\n if (arguments.length) {\n _value.value = value;\n return _value.value;\n } else {\n const truthy = toValue$1(truthyValue);\n _value.value = _value.value === truthy ? toValue$1(falsyValue) : truthy;\n return _value.value;\n }\n }\n if (valueIsRef)\n return toggle;\n else\n return [_value, toggle];\n}\n\nfunction watchArray(source, cb, options) {\n let oldList = (options == null ? void 0 : options.immediate) ? [] : [...typeof source === \"function\" ? source() : Array.isArray(source) ? source : toValue$1(source)];\n return watch(source, (newList, _, onCleanup) => {\n const oldListRemains = Array.from({ length: oldList.length });\n const added = [];\n for (const obj of newList) {\n let found = false;\n for (let i = 0; i < oldList.length; i++) {\n if (!oldListRemains[i] && obj === oldList[i]) {\n oldListRemains[i] = true;\n found = true;\n break;\n }\n }\n if (!found)\n added.push(obj);\n }\n const removed = oldList.filter((_2, i) => !oldListRemains[i]);\n cb(newList, oldList, added, removed, onCleanup);\n oldList = [...newList];\n }, options);\n}\n\nfunction watchAtMost(source, cb, options) {\n const {\n count,\n ...watchOptions\n } = options;\n const current = shallowRef(0);\n const stop = watchWithFilter(\n source,\n (...args) => {\n current.value += 1;\n if (current.value >= toValue$1(count))\n nextTick(() => stop());\n cb(...args);\n },\n watchOptions\n );\n return { count: current, stop };\n}\n\nfunction watchDebounced(source, cb, options = {}) {\n const {\n debounce = 0,\n maxWait = void 0,\n ...watchOptions\n } = options;\n return watchWithFilter(\n source,\n cb,\n {\n ...watchOptions,\n eventFilter: debounceFilter(debounce, { maxWait })\n }\n );\n}\n\nfunction watchDeep(source, cb, options) {\n return watch(\n source,\n cb,\n {\n ...options,\n deep: true\n }\n );\n}\n\nfunction watchIgnorable(source, cb, options = {}) {\n const {\n eventFilter = bypassFilter,\n ...watchOptions\n } = options;\n const filteredCb = createFilterWrapper(\n eventFilter,\n cb\n );\n let ignoreUpdates;\n let ignorePrevAsyncUpdates;\n let stop;\n if (watchOptions.flush === \"sync\") {\n const ignore = shallowRef(false);\n ignorePrevAsyncUpdates = () => {\n };\n ignoreUpdates = (updater) => {\n ignore.value = true;\n updater();\n ignore.value = false;\n };\n stop = watch(\n source,\n (...args) => {\n if (!ignore.value)\n filteredCb(...args);\n },\n watchOptions\n );\n } else {\n const disposables = [];\n const ignoreCounter = shallowRef(0);\n const syncCounter = shallowRef(0);\n ignorePrevAsyncUpdates = () => {\n ignoreCounter.value = syncCounter.value;\n };\n disposables.push(\n watch(\n source,\n () => {\n syncCounter.value++;\n },\n { ...watchOptions, flush: \"sync\" }\n )\n );\n ignoreUpdates = (updater) => {\n const syncCounterPrev = syncCounter.value;\n updater();\n ignoreCounter.value += syncCounter.value - syncCounterPrev;\n };\n disposables.push(\n watch(\n source,\n (...args) => {\n const ignore = ignoreCounter.value > 0 && ignoreCounter.value === syncCounter.value;\n ignoreCounter.value = 0;\n syncCounter.value = 0;\n if (ignore)\n return;\n filteredCb(...args);\n },\n watchOptions\n )\n );\n stop = () => {\n disposables.forEach((fn) => fn());\n };\n }\n return { stop, ignoreUpdates, ignorePrevAsyncUpdates };\n}\n\nfunction watchImmediate(source, cb, options) {\n return watch(\n source,\n cb,\n {\n ...options,\n immediate: true\n }\n );\n}\n\nfunction watchOnce(source, cb, options) {\n const stop = watch(source, (...args) => {\n nextTick(() => stop());\n return cb(...args);\n }, options);\n return stop;\n}\n\nfunction watchThrottled(source, cb, options = {}) {\n const {\n throttle = 0,\n trailing = true,\n leading = true,\n ...watchOptions\n } = options;\n return watchWithFilter(\n source,\n cb,\n {\n ...watchOptions,\n eventFilter: throttleFilter(throttle, trailing, leading)\n }\n );\n}\n\nfunction watchTriggerable(source, cb, options = {}) {\n let cleanupFn;\n function onEffect() {\n if (!cleanupFn)\n return;\n const fn = cleanupFn;\n cleanupFn = void 0;\n fn();\n }\n function onCleanup(callback) {\n cleanupFn = callback;\n }\n const _cb = (value, oldValue) => {\n onEffect();\n return cb(value, oldValue, onCleanup);\n };\n const res = watchIgnorable(source, _cb, options);\n const { ignoreUpdates } = res;\n const trigger = () => {\n let res2;\n ignoreUpdates(() => {\n res2 = _cb(getWatchSources(source), getOldValue(source));\n });\n return res2;\n };\n return {\n ...res,\n trigger\n };\n}\nfunction getWatchSources(sources) {\n if (isReactive(sources))\n return sources;\n if (Array.isArray(sources))\n return sources.map((item) => toValue$1(item));\n return toValue$1(sources);\n}\nfunction getOldValue(source) {\n return Array.isArray(source) ? source.map(() => void 0) : void 0;\n}\n\nfunction whenever(source, cb, options) {\n const stop = watch(\n source,\n (v, ov, onInvalidate) => {\n if (v) {\n if (options == null ? void 0 : options.once)\n nextTick(() => stop());\n cb(v, ov, onInvalidate);\n }\n },\n {\n ...options,\n once: false\n }\n );\n return stop;\n}\n\nexport { assert, refAutoReset as autoResetRef, bypassFilter, camelize, clamp, computedEager, computedWithControl, containsProp, computedWithControl as controlledComputed, controlledRef, createEventHook, createFilterWrapper, createGlobalState, createInjectionState, reactify as createReactiveFn, createRef, createSharedComposable, createSingletonPromise, debounceFilter, refDebounced as debouncedRef, watchDebounced as debouncedWatch, computedEager as eagerComputed, extendRef, formatDate, get, getLifeCycleTarget, hasOwn, hyphenate, identity, watchIgnorable as ignorableWatch, increaseWithUnit, injectLocal, invoke, isClient, isDef, isDefined, isIOS, isObject, isWorker, makeDestructurable, noop, normalizeDate, notNullish, now, objectEntries, objectOmit, objectPick, pausableFilter, watchPausable as pausableWatch, promiseTimeout, provideLocal, pxValue, rand, reactify, reactifyObject, reactiveComputed, reactiveOmit, reactivePick, refAutoReset, refDebounced, refDefault, refThrottled, refWithControl, resolveRef, resolveUnref, set, syncRef, syncRefs, throttleFilter, refThrottled as throttledRef, watchThrottled as throttledWatch, timestamp, toArray, toReactive, toRef, toRefs, toValue, tryOnBeforeMount, tryOnBeforeUnmount, tryOnMounted, tryOnScopeDispose, tryOnUnmounted, until, useArrayDifference, useArrayEvery, useArrayFilter, useArrayFind, useArrayFindIndex, useArrayFindLast, useArrayIncludes, useArrayJoin, useArrayMap, useArrayReduce, useArraySome, useArrayUnique, useCounter, useDateFormat, refDebounced as useDebounce, useDebounceFn, useInterval, useIntervalFn, useLastChanged, refThrottled as useThrottle, useThrottleFn, useTimeout, useTimeoutFn, useToNumber, useToString, useToggle, watchArray, watchAtMost, watchDebounced, watchDeep, watchIgnorable, watchImmediate, watchOnce, watchPausable, watchThrottled, watchTriggerable, watchWithFilter, whenever };\n", "import { noop, makeDestructurable, camelize, isClient, toArray, watchImmediate, isObject, tryOnScopeDispose, isIOS, notNullish, tryOnMounted, objectOmit, promiseTimeout, until, injectLocal, provideLocal, pxValue, increaseWithUnit, objectEntries, createRef, createSingletonPromise, useTimeoutFn, pausableWatch, toRef, createEventHook, useIntervalFn, computedWithControl, timestamp, pausableFilter, watchIgnorable, debounceFilter, bypassFilter, createFilterWrapper, toRefs, watchOnce, containsProp, hasOwn, throttleFilter, useDebounceFn, useThrottleFn, tryOnUnmounted, clamp, syncRef, objectPick, watchWithFilter, identity, isDef, whenever, isWorker } from '@vueuse/shared';\nexport * from '@vueuse/shared';\nimport { isRef, shallowRef, ref, watchEffect, computed, inject, defineComponent, h, TransitionGroup, shallowReactive, Fragment, toValue, unref, getCurrentInstance, onMounted, watch, customRef, onUpdated, readonly, reactive, hasInjectionContext, toRaw, nextTick, markRaw, getCurrentScope, isReadonly, onBeforeUpdate } from 'vue';\n\nfunction computedAsync(evaluationCallback, initialState, optionsOrRef) {\n let options;\n if (isRef(optionsOrRef)) {\n options = {\n evaluating: optionsOrRef\n };\n } else {\n options = optionsOrRef || {};\n }\n const {\n lazy = false,\n evaluating = void 0,\n shallow = true,\n onError = noop\n } = options;\n const started = shallowRef(!lazy);\n const current = shallow ? shallowRef(initialState) : ref(initialState);\n let counter = 0;\n watchEffect(async (onInvalidate) => {\n if (!started.value)\n return;\n counter++;\n const counterAtBeginning = counter;\n let hasFinished = false;\n if (evaluating) {\n Promise.resolve().then(() => {\n evaluating.value = true;\n });\n }\n try {\n const result = await evaluationCallback((cancelCallback) => {\n onInvalidate(() => {\n if (evaluating)\n evaluating.value = false;\n if (!hasFinished)\n cancelCallback();\n });\n });\n if (counterAtBeginning === counter)\n current.value = result;\n } catch (e) {\n onError(e);\n } finally {\n if (evaluating && counterAtBeginning === counter)\n evaluating.value = false;\n hasFinished = true;\n }\n });\n if (lazy) {\n return computed(() => {\n started.value = true;\n return current.value;\n });\n } else {\n return current;\n }\n}\n\nfunction computedInject(key, options, defaultSource, treatDefaultAsFactory) {\n let source = inject(key);\n if (defaultSource)\n source = inject(key, defaultSource);\n if (treatDefaultAsFactory)\n source = inject(key, defaultSource, treatDefaultAsFactory);\n if (typeof options === \"function\") {\n return computed((ctx) => options(source, ctx));\n } else {\n return computed({\n get: (ctx) => options.get(source, ctx),\n set: options.set\n });\n }\n}\n\nfunction createReusableTemplate(options = {}) {\n const {\n inheritAttrs = true\n } = options;\n const render = shallowRef();\n const define = /*@__PURE__*/ defineComponent({\n setup(_, { slots }) {\n return () => {\n render.value = slots.default;\n };\n }\n });\n const reuse = /*@__PURE__*/ defineComponent({\n inheritAttrs,\n props: options.props,\n setup(props, { attrs, slots }) {\n return () => {\n var _a;\n if (!render.value && process.env.NODE_ENV !== \"production\")\n throw new Error(\"[VueUse] Failed to find the definition of reusable template\");\n const vnode = (_a = render.value) == null ? void 0 : _a.call(render, {\n ...options.props == null ? keysToCamelKebabCase(attrs) : props,\n $slots: slots\n });\n return inheritAttrs && (vnode == null ? void 0 : vnode.length) === 1 ? vnode[0] : vnode;\n };\n }\n });\n return makeDestructurable(\n { define, reuse },\n [define, reuse]\n );\n}\nfunction keysToCamelKebabCase(obj) {\n const newObj = {};\n for (const key in obj)\n newObj[camelize(key)] = obj[key];\n return newObj;\n}\n\nfunction createTemplatePromise(options = {}) {\n let index = 0;\n const instances = ref([]);\n function create(...args) {\n const props = shallowReactive({\n key: index++,\n args,\n promise: void 0,\n resolve: () => {\n },\n reject: () => {\n },\n isResolving: false,\n options\n });\n instances.value.push(props);\n props.promise = new Promise((_resolve, _reject) => {\n props.resolve = (v) => {\n props.isResolving = true;\n return _resolve(v);\n };\n props.reject = _reject;\n }).finally(() => {\n props.promise = void 0;\n const index2 = instances.value.indexOf(props);\n if (index2 !== -1)\n instances.value.splice(index2, 1);\n });\n return props.promise;\n }\n function start(...args) {\n if (options.singleton && instances.value.length > 0)\n return instances.value[0].promise;\n return create(...args);\n }\n const component = /*@__PURE__*/ defineComponent((_, { slots }) => {\n const renderList = () => instances.value.map((props) => {\n var _a;\n return h(Fragment, { key: props.key }, (_a = slots.default) == null ? void 0 : _a.call(slots, props));\n });\n if (options.transition)\n return () => h(TransitionGroup, options.transition, renderList);\n return renderList;\n });\n component.start = start;\n return component;\n}\n\nfunction createUnrefFn(fn) {\n return function(...args) {\n return fn.apply(this, args.map((i) => toValue(i)));\n };\n}\n\nconst defaultWindow = isClient ? window : void 0;\nconst defaultDocument = isClient ? window.document : void 0;\nconst defaultNavigator = isClient ? window.navigator : void 0;\nconst defaultLocation = isClient ? window.location : void 0;\n\nfunction unrefElement(elRef) {\n var _a;\n const plain = toValue(elRef);\n return (_a = plain == null ? void 0 : plain.$el) != null ? _a : plain;\n}\n\nfunction useEventListener(...args) {\n const cleanups = [];\n const cleanup = () => {\n cleanups.forEach((fn) => fn());\n cleanups.length = 0;\n };\n const register = (el, event, listener, options) => {\n el.addEventListener(event, listener, options);\n return () => el.removeEventListener(event, listener, options);\n };\n const firstParamTargets = computed(() => {\n const test = toArray(toValue(args[0])).filter((e) => e != null);\n return test.every((e) => typeof e !== \"string\") ? test : void 0;\n });\n const stopWatch = watchImmediate(\n () => {\n var _a, _b;\n return [\n (_b = (_a = firstParamTargets.value) == null ? void 0 : _a.map((e) => unrefElement(e))) != null ? _b : [defaultWindow].filter((e) => e != null),\n toArray(toValue(firstParamTargets.value ? args[1] : args[0])),\n toArray(unref(firstParamTargets.value ? args[2] : args[1])),\n // @ts-expect-error - TypeScript gets the correct types, but somehow still complains\n toValue(firstParamTargets.value ? args[3] : args[2])\n ];\n },\n ([raw_targets, raw_events, raw_listeners, raw_options]) => {\n cleanup();\n if (!(raw_targets == null ? void 0 : raw_targets.length) || !(raw_events == null ? void 0 : raw_events.length) || !(raw_listeners == null ? void 0 : raw_listeners.length))\n return;\n const optionsClone = isObject(raw_options) ? { ...raw_options } : raw_options;\n cleanups.push(\n ...raw_targets.flatMap(\n (el) => raw_events.flatMap(\n (event) => raw_listeners.map((listener) => register(el, event, listener, optionsClone))\n )\n )\n );\n },\n { flush: \"post\" }\n );\n const stop = () => {\n stopWatch();\n cleanup();\n };\n tryOnScopeDispose(cleanup);\n return stop;\n}\n\nlet _iOSWorkaround = false;\nfunction onClickOutside(target, handler, options = {}) {\n const { window = defaultWindow, ignore = [], capture = true, detectIframe = false, controls = false } = options;\n if (!window) {\n return controls ? { stop: noop, cancel: noop, trigger: noop } : noop;\n }\n if (isIOS && !_iOSWorkaround) {\n _iOSWorkaround = true;\n const listenerOptions = { passive: true };\n Array.from(window.document.body.children).forEach((el) => useEventListener(el, \"click\", noop, listenerOptions));\n useEventListener(window.document.documentElement, \"click\", noop, listenerOptions);\n }\n let shouldListen = true;\n const shouldIgnore = (event) => {\n return toValue(ignore).some((target2) => {\n if (typeof target2 === \"string\") {\n return Array.from(window.document.querySelectorAll(target2)).some((el) => el === event.target || event.composedPath().includes(el));\n } else {\n const el = unrefElement(target2);\n return el && (event.target === el || event.composedPath().includes(el));\n }\n });\n };\n function hasMultipleRoots(target2) {\n const vm = toValue(target2);\n return vm && vm.$.subTree.shapeFlag === 16;\n }\n function checkMultipleRoots(target2, event) {\n const vm = toValue(target2);\n const children = vm.$.subTree && vm.$.subTree.children;\n if (children == null || !Array.isArray(children))\n return false;\n return children.some((child) => child.el === event.target || event.composedPath().includes(child.el));\n }\n const listener = (event) => {\n const el = unrefElement(target);\n if (event.target == null)\n return;\n if (!(el instanceof Element) && hasMultipleRoots(target) && checkMultipleRoots(target, event))\n return;\n if (!el || el === event.target || event.composedPath().includes(el))\n return;\n if (\"detail\" in event && event.detail === 0)\n shouldListen = !shouldIgnore(event);\n if (!shouldListen) {\n shouldListen = true;\n return;\n }\n handler(event);\n };\n let isProcessingClick = false;\n const cleanup = [\n useEventListener(window, \"click\", (event) => {\n if (!isProcessingClick) {\n isProcessingClick = true;\n setTimeout(() => {\n isProcessingClick = false;\n }, 0);\n listener(event);\n }\n }, { passive: true, capture }),\n useEventListener(window, \"pointerdown\", (e) => {\n const el = unrefElement(target);\n shouldListen = !shouldIgnore(e) && !!(el && !e.composedPath().includes(el));\n }, { passive: true }),\n detectIframe && useEventListener(window, \"blur\", (event) => {\n setTimeout(() => {\n var _a;\n const el = unrefElement(target);\n if (((_a = window.document.activeElement) == null ? void 0 : _a.tagName) === \"IFRAME\" && !(el == null ? void 0 : el.contains(window.document.activeElement))) {\n handler(event);\n }\n }, 0);\n }, { passive: true })\n ].filter(Boolean);\n const stop = () => cleanup.forEach((fn) => fn());\n if (controls) {\n return {\n stop,\n cancel: () => {\n shouldListen = false;\n },\n trigger: (event) => {\n shouldListen = true;\n listener(event);\n shouldListen = false;\n }\n };\n }\n return stop;\n}\n\nfunction useMounted() {\n const isMounted = shallowRef(false);\n const instance = getCurrentInstance();\n if (instance) {\n onMounted(() => {\n isMounted.value = true;\n }, instance);\n }\n return isMounted;\n}\n\nfunction useSupported(callback) {\n const isMounted = useMounted();\n return computed(() => {\n isMounted.value;\n return Boolean(callback());\n });\n}\n\nfunction useMutationObserver(target, callback, options = {}) {\n const { window = defaultWindow, ...mutationOptions } = options;\n let observer;\n const isSupported = useSupported(() => window && \"MutationObserver\" in window);\n const cleanup = () => {\n if (observer) {\n observer.disconnect();\n observer = void 0;\n }\n };\n const targets = computed(() => {\n const value = toValue(target);\n const items = toArray(value).map(unrefElement).filter(notNullish);\n return new Set(items);\n });\n const stopWatch = watch(\n () => targets.value,\n (targets2) => {\n cleanup();\n if (isSupported.value && targets2.size) {\n observer = new MutationObserver(callback);\n targets2.forEach((el) => observer.observe(el, mutationOptions));\n }\n },\n { immediate: true, flush: \"post\" }\n );\n const takeRecords = () => {\n return observer == null ? void 0 : observer.takeRecords();\n };\n const stop = () => {\n stopWatch();\n cleanup();\n };\n tryOnScopeDispose(stop);\n return {\n isSupported,\n stop,\n takeRecords\n };\n}\n\nfunction onElementRemoval(target, callback, options = {}) {\n const {\n window = defaultWindow,\n document = window == null ? void 0 : window.document,\n flush = \"sync\"\n } = options;\n if (!window || !document)\n return noop;\n let stopFn;\n const cleanupAndUpdate = (fn) => {\n stopFn == null ? void 0 : stopFn();\n stopFn = fn;\n };\n const stopWatch = watchEffect(() => {\n const el = unrefElement(target);\n if (el) {\n const { stop } = useMutationObserver(\n document,\n (mutationsList) => {\n const targetRemoved = mutationsList.map((mutation) => [...mutation.removedNodes]).flat().some((node) => node === el || node.contains(el));\n if (targetRemoved) {\n callback(mutationsList);\n }\n },\n {\n window,\n childList: true,\n subtree: true\n }\n );\n cleanupAndUpdate(stop);\n }\n }, { flush });\n const stopHandle = () => {\n stopWatch();\n cleanupAndUpdate();\n };\n tryOnScopeDispose(stopHandle);\n return stopHandle;\n}\n\nfunction createKeyPredicate(keyFilter) {\n if (typeof keyFilter === \"function\")\n return keyFilter;\n else if (typeof keyFilter === \"string\")\n return (event) => event.key === keyFilter;\n else if (Array.isArray(keyFilter))\n return (event) => keyFilter.includes(event.key);\n return () => true;\n}\nfunction onKeyStroke(...args) {\n let key;\n let handler;\n let options = {};\n if (args.length === 3) {\n key = args[0];\n handler = args[1];\n options = args[2];\n } else if (args.length === 2) {\n if (typeof args[1] === \"object\") {\n key = true;\n handler = args[0];\n options = args[1];\n } else {\n key = args[0];\n handler = args[1];\n }\n } else {\n key = true;\n handler = args[0];\n }\n const {\n target = defaultWindow,\n eventName = \"keydown\",\n passive = false,\n dedupe = false\n } = options;\n const predicate = createKeyPredicate(key);\n const listener = (e) => {\n if (e.repeat && toValue(dedupe))\n return;\n if (predicate(e))\n handler(e);\n };\n return useEventListener(target, eventName, listener, passive);\n}\nfunction onKeyDown(key, handler, options = {}) {\n return onKeyStroke(key, handler, { ...options, eventName: \"keydown\" });\n}\nfunction onKeyPressed(key, handler, options = {}) {\n return onKeyStroke(key, handler, { ...options, eventName: \"keypress\" });\n}\nfunction onKeyUp(key, handler, options = {}) {\n return onKeyStroke(key, handler, { ...options, eventName: \"keyup\" });\n}\n\nconst DEFAULT_DELAY = 500;\nconst DEFAULT_THRESHOLD = 10;\nfunction onLongPress(target, handler, options) {\n var _a, _b;\n const elementRef = computed(() => unrefElement(target));\n let timeout;\n let posStart;\n let startTimestamp;\n let hasLongPressed = false;\n function clear() {\n if (timeout) {\n clearTimeout(timeout);\n timeout = void 0;\n }\n posStart = void 0;\n startTimestamp = void 0;\n hasLongPressed = false;\n }\n function onRelease(ev) {\n var _a2, _b2, _c;\n const [_startTimestamp, _posStart, _hasLongPressed] = [startTimestamp, posStart, hasLongPressed];\n clear();\n if (!(options == null ? void 0 : options.onMouseUp) || !_posStart || !_startTimestamp)\n return;\n if (((_a2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _a2.self) && ev.target !== elementRef.value)\n return;\n if ((_b2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _b2.prevent)\n ev.preventDefault();\n if ((_c = options == null ? void 0 : options.modifiers) == null ? void 0 : _c.stop)\n ev.stopPropagation();\n const dx = ev.x - _posStart.x;\n const dy = ev.y - _posStart.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n options.onMouseUp(ev.timeStamp - _startTimestamp, distance, _hasLongPressed);\n }\n function onDown(ev) {\n var _a2, _b2, _c, _d;\n if (((_a2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _a2.self) && ev.target !== elementRef.value)\n return;\n clear();\n if ((_b2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _b2.prevent)\n ev.preventDefault();\n if ((_c = options == null ? void 0 : options.modifiers) == null ? void 0 : _c.stop)\n ev.stopPropagation();\n posStart = {\n x: ev.x,\n y: ev.y\n };\n startTimestamp = ev.timeStamp;\n timeout = setTimeout(\n () => {\n hasLongPressed = true;\n handler(ev);\n },\n (_d = options == null ? void 0 : options.delay) != null ? _d : DEFAULT_DELAY\n );\n }\n function onMove(ev) {\n var _a2, _b2, _c, _d;\n if (((_a2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _a2.self) && ev.target !== elementRef.value)\n return;\n if (!posStart || (options == null ? void 0 : options.distanceThreshold) === false)\n return;\n if ((_b2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _b2.prevent)\n ev.preventDefault();\n if ((_c = options == null ? void 0 : options.modifiers) == null ? void 0 : _c.stop)\n ev.stopPropagation();\n const dx = ev.x - posStart.x;\n const dy = ev.y - posStart.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n if (distance >= ((_d = options == null ? void 0 : options.distanceThreshold) != null ? _d : DEFAULT_THRESHOLD))\n clear();\n }\n const listenerOptions = {\n capture: (_a = options == null ? void 0 : options.modifiers) == null ? void 0 : _a.capture,\n once: (_b = options == null ? void 0 : options.modifiers) == null ? void 0 : _b.once\n };\n const cleanup = [\n useEventListener(elementRef, \"pointerdown\", onDown, listenerOptions),\n useEventListener(elementRef, \"pointermove\", onMove, listenerOptions),\n useEventListener(elementRef, [\"pointerup\", \"pointerleave\"], onRelease, listenerOptions)\n ];\n const stop = () => cleanup.forEach((fn) => fn());\n return stop;\n}\n\nfunction isFocusedElementEditable() {\n const { activeElement, body } = document;\n if (!activeElement)\n return false;\n if (activeElement === body)\n return false;\n switch (activeElement.tagName) {\n case \"INPUT\":\n case \"TEXTAREA\":\n return true;\n }\n return activeElement.hasAttribute(\"contenteditable\");\n}\nfunction isTypedCharValid({\n keyCode,\n metaKey,\n ctrlKey,\n altKey\n}) {\n if (metaKey || ctrlKey || altKey)\n return false;\n if (keyCode >= 48 && keyCode <= 57 || keyCode >= 96 && keyCode <= 105)\n return true;\n if (keyCode >= 65 && keyCode <= 90)\n return true;\n return false;\n}\nfunction onStartTyping(callback, options = {}) {\n const { document: document2 = defaultDocument } = options;\n const keydown = (event) => {\n if (!isFocusedElementEditable() && isTypedCharValid(event)) {\n callback(event);\n }\n };\n if (document2)\n useEventListener(document2, \"keydown\", keydown, { passive: true });\n}\n\nfunction templateRef(key, initialValue = null) {\n const instance = getCurrentInstance();\n let _trigger = () => {\n };\n const element = customRef((track, trigger) => {\n _trigger = trigger;\n return {\n get() {\n var _a, _b;\n track();\n return (_b = (_a = instance == null ? void 0 : instance.proxy) == null ? void 0 : _a.$refs[key]) != null ? _b : initialValue;\n },\n set() {\n }\n };\n });\n tryOnMounted(_trigger);\n onUpdated(_trigger);\n return element;\n}\n\nfunction useActiveElement(options = {}) {\n var _a;\n const {\n window = defaultWindow,\n deep = true,\n triggerOnRemoval = false\n } = options;\n const document = (_a = options.document) != null ? _a : window == null ? void 0 : window.document;\n const getDeepActiveElement = () => {\n var _a2;\n let element = document == null ? void 0 : document.activeElement;\n if (deep) {\n while (element == null ? void 0 : element.shadowRoot)\n element = (_a2 = element == null ? void 0 : element.shadowRoot) == null ? void 0 : _a2.activeElement;\n }\n return element;\n };\n const activeElement = shallowRef();\n const trigger = () => {\n activeElement.value = getDeepActiveElement();\n };\n if (window) {\n const listenerOptions = {\n capture: true,\n passive: true\n };\n useEventListener(\n window,\n \"blur\",\n (event) => {\n if (event.relatedTarget !== null)\n return;\n trigger();\n },\n listenerOptions\n );\n useEventListener(\n window,\n \"focus\",\n trigger,\n listenerOptions\n );\n }\n if (triggerOnRemoval) {\n onElementRemoval(activeElement, trigger, { document });\n }\n trigger();\n return activeElement;\n}\n\nfunction useRafFn(fn, options = {}) {\n const {\n immediate = true,\n fpsLimit = void 0,\n window = defaultWindow,\n once = false\n } = options;\n const isActive = shallowRef(false);\n const intervalLimit = computed(() => {\n return fpsLimit ? 1e3 / toValue(fpsLimit) : null;\n });\n let previousFrameTimestamp = 0;\n let rafId = null;\n function loop(timestamp) {\n if (!isActive.value || !window)\n return;\n if (!previousFrameTimestamp)\n previousFrameTimestamp = timestamp;\n const delta = timestamp - previousFrameTimestamp;\n if (intervalLimit.value && delta < intervalLimit.value) {\n rafId = window.requestAnimationFrame(loop);\n return;\n }\n previousFrameTimestamp = timestamp;\n fn({ delta, timestamp });\n if (once) {\n isActive.value = false;\n rafId = null;\n return;\n }\n rafId = window.requestAnimationFrame(loop);\n }\n function resume() {\n if (!isActive.value && window) {\n isActive.value = true;\n previousFrameTimestamp = 0;\n rafId = window.requestAnimationFrame(loop);\n }\n }\n function pause() {\n isActive.value = false;\n if (rafId != null && window) {\n window.cancelAnimationFrame(rafId);\n rafId = null;\n }\n }\n if (immediate)\n resume();\n tryOnScopeDispose(pause);\n return {\n isActive: readonly(isActive),\n pause,\n resume\n };\n}\n\nfunction useAnimate(target, keyframes, options) {\n let config;\n let animateOptions;\n if (isObject(options)) {\n config = options;\n animateOptions = objectOmit(options, [\"window\", \"immediate\", \"commitStyles\", \"persist\", \"onReady\", \"onError\"]);\n } else {\n config = { duration: options };\n animateOptions = options;\n }\n const {\n window = defaultWindow,\n immediate = true,\n commitStyles,\n persist,\n playbackRate: _playbackRate = 1,\n onReady,\n onError = (e) => {\n console.error(e);\n }\n } = config;\n const isSupported = useSupported(() => window && HTMLElement && \"animate\" in HTMLElement.prototype);\n const animate = shallowRef(void 0);\n const store = shallowReactive({\n startTime: null,\n currentTime: null,\n timeline: null,\n playbackRate: _playbackRate,\n pending: false,\n playState: immediate ? \"idle\" : \"paused\",\n replaceState: \"active\"\n });\n const pending = computed(() => store.pending);\n const playState = computed(() => store.playState);\n const replaceState = computed(() => store.replaceState);\n const startTime = computed({\n get() {\n return store.startTime;\n },\n set(value) {\n store.startTime = value;\n if (animate.value)\n animate.value.startTime = value;\n }\n });\n const currentTime = computed({\n get() {\n return store.currentTime;\n },\n set(value) {\n store.currentTime = value;\n if (animate.value) {\n animate.value.currentTime = value;\n syncResume();\n }\n }\n });\n const timeline = computed({\n get() {\n return store.timeline;\n },\n set(value) {\n store.timeline = value;\n if (animate.value)\n animate.value.timeline = value;\n }\n });\n const playbackRate = computed({\n get() {\n return store.playbackRate;\n },\n set(value) {\n store.playbackRate = value;\n if (animate.value)\n animate.value.playbackRate = value;\n }\n });\n const play = () => {\n if (animate.value) {\n try {\n animate.value.play();\n syncResume();\n } catch (e) {\n syncPause();\n onError(e);\n }\n } else {\n update();\n }\n };\n const pause = () => {\n var _a;\n try {\n (_a = animate.value) == null ? void 0 : _a.pause();\n syncPause();\n } catch (e) {\n onError(e);\n }\n };\n const reverse = () => {\n var _a;\n if (!animate.value)\n update();\n try {\n (_a = animate.value) == null ? void 0 : _a.reverse();\n syncResume();\n } catch (e) {\n syncPause();\n onError(e);\n }\n };\n const finish = () => {\n var _a;\n try {\n (_a = animate.value) == null ? void 0 : _a.finish();\n syncPause();\n } catch (e) {\n onError(e);\n }\n };\n const cancel = () => {\n var _a;\n try {\n (_a = animate.value) == null ? void 0 : _a.cancel();\n syncPause();\n } catch (e) {\n onError(e);\n }\n };\n watch(() => unrefElement(target), (el) => {\n if (el) {\n update();\n } else {\n animate.value = void 0;\n }\n });\n watch(() => keyframes, (value) => {\n if (animate.value) {\n update();\n const targetEl = unrefElement(target);\n if (targetEl) {\n animate.value.effect = new KeyframeEffect(\n targetEl,\n toValue(value),\n animateOptions\n );\n }\n }\n }, { deep: true });\n tryOnMounted(() => update(true), false);\n tryOnScopeDispose(cancel);\n function update(init) {\n const el = unrefElement(target);\n if (!isSupported.value || !el)\n return;\n if (!animate.value)\n animate.value = el.animate(toValue(keyframes), animateOptions);\n if (persist)\n animate.value.persist();\n if (_playbackRate !== 1)\n animate.value.playbackRate = _playbackRate;\n if (init && !immediate)\n animate.value.pause();\n else\n syncResume();\n onReady == null ? void 0 : onReady(animate.value);\n }\n const listenerOptions = { passive: true };\n useEventListener(animate, [\"cancel\", \"finish\", \"remove\"], syncPause, listenerOptions);\n useEventListener(animate, \"finish\", () => {\n var _a;\n if (commitStyles)\n (_a = animate.value) == null ? void 0 : _a.commitStyles();\n }, listenerOptions);\n const { resume: resumeRef, pause: pauseRef } = useRafFn(() => {\n if (!animate.value)\n return;\n store.pending = animate.value.pending;\n store.playState = animate.value.playState;\n store.replaceState = animate.value.replaceState;\n store.startTime = animate.value.startTime;\n store.currentTime = animate.value.currentTime;\n store.timeline = animate.value.timeline;\n store.playbackRate = animate.value.playbackRate;\n }, { immediate: false });\n function syncResume() {\n if (isSupported.value)\n resumeRef();\n }\n function syncPause() {\n if (isSupported.value && window)\n window.requestAnimationFrame(pauseRef);\n }\n return {\n isSupported,\n animate,\n // actions\n play,\n pause,\n reverse,\n finish,\n cancel,\n // state\n pending,\n playState,\n replaceState,\n startTime,\n currentTime,\n timeline,\n playbackRate\n };\n}\n\nfunction useAsyncQueue(tasks, options) {\n const {\n interrupt = true,\n onError = noop,\n onFinished = noop,\n signal\n } = options || {};\n const promiseState = {\n aborted: \"aborted\",\n fulfilled: \"fulfilled\",\n pending: \"pending\",\n rejected: \"rejected\"\n };\n const initialResult = Array.from(Array.from({ length: tasks.length }), () => ({ state: promiseState.pending, data: null }));\n const result = reactive(initialResult);\n const activeIndex = shallowRef(-1);\n if (!tasks || tasks.length === 0) {\n onFinished();\n return {\n activeIndex,\n result\n };\n }\n function updateResult(state, res) {\n activeIndex.value++;\n result[activeIndex.value].data = res;\n result[activeIndex.value].state = state;\n }\n tasks.reduce((prev, curr) => {\n return prev.then((prevRes) => {\n var _a;\n if (signal == null ? void 0 : signal.aborted) {\n updateResult(promiseState.aborted, new Error(\"aborted\"));\n return;\n }\n if (((_a = result[activeIndex.value]) == null ? void 0 : _a.state) === promiseState.rejected && interrupt) {\n onFinished();\n return;\n }\n const done = curr(prevRes).then((currentRes) => {\n updateResult(promiseState.fulfilled, currentRes);\n if (activeIndex.value === tasks.length - 1)\n onFinished();\n return currentRes;\n });\n if (!signal)\n return done;\n return Promise.race([done, whenAborted(signal)]);\n }).catch((e) => {\n if (signal == null ? void 0 : signal.aborted) {\n updateResult(promiseState.aborted, e);\n return e;\n }\n updateResult(promiseState.rejected, e);\n onError();\n return e;\n });\n }, Promise.resolve());\n return {\n activeIndex,\n result\n };\n}\nfunction whenAborted(signal) {\n return new Promise((resolve, reject) => {\n const error = new Error(\"aborted\");\n if (signal.aborted)\n reject(error);\n else\n signal.addEventListener(\"abort\", () => reject(error), { once: true });\n });\n}\n\nfunction useAsyncState(promise, initialState, options) {\n const {\n immediate = true,\n delay = 0,\n onError = noop,\n onSuccess = noop,\n resetOnExecute = true,\n shallow = true,\n throwError\n } = options != null ? options : {};\n const state = shallow ? shallowRef(initialState) : ref(initialState);\n const isReady = shallowRef(false);\n const isLoading = shallowRef(false);\n const error = shallowRef(void 0);\n async function execute(delay2 = 0, ...args) {\n if (resetOnExecute)\n state.value = initialState;\n error.value = void 0;\n isReady.value = false;\n isLoading.value = true;\n if (delay2 > 0)\n await promiseTimeout(delay2);\n const _promise = typeof promise === \"function\" ? promise(...args) : promise;\n try {\n const data = await _promise;\n state.value = data;\n isReady.value = true;\n onSuccess(data);\n } catch (e) {\n error.value = e;\n onError(e);\n if (throwError)\n throw e;\n } finally {\n isLoading.value = false;\n }\n return state.value;\n }\n if (immediate) {\n execute(delay);\n }\n const shell = {\n state,\n isReady,\n isLoading,\n error,\n execute\n };\n function waitUntilIsLoaded() {\n return new Promise((resolve, reject) => {\n until(isLoading).toBe(false).then(() => resolve(shell)).catch(reject);\n });\n }\n return {\n ...shell,\n then(onFulfilled, onRejected) {\n return waitUntilIsLoaded().then(onFulfilled, onRejected);\n }\n };\n}\n\nconst defaults = {\n array: (v) => JSON.stringify(v),\n object: (v) => JSON.stringify(v),\n set: (v) => JSON.stringify(Array.from(v)),\n map: (v) => JSON.stringify(Object.fromEntries(v)),\n null: () => \"\"\n};\nfunction getDefaultSerialization(target) {\n if (!target)\n return defaults.null;\n if (target instanceof Map)\n return defaults.map;\n else if (target instanceof Set)\n return defaults.set;\n else if (Array.isArray(target))\n return defaults.array;\n else\n return defaults.object;\n}\n\nfunction useBase64(target, options) {\n const base64 = shallowRef(\"\");\n const promise = shallowRef();\n function execute() {\n if (!isClient)\n return;\n promise.value = new Promise((resolve, reject) => {\n try {\n const _target = toValue(target);\n if (_target == null) {\n resolve(\"\");\n } else if (typeof _target === \"string\") {\n resolve(blobToBase64(new Blob([_target], { type: \"text/plain\" })));\n } else if (_target instanceof Blob) {\n resolve(blobToBase64(_target));\n } else if (_target instanceof ArrayBuffer) {\n resolve(window.btoa(String.fromCharCode(...new Uint8Array(_target))));\n } else if (_target instanceof HTMLCanvasElement) {\n resolve(_target.toDataURL(options == null ? void 0 : options.type, options == null ? void 0 : options.quality));\n } else if (_target instanceof HTMLImageElement) {\n const img = _target.cloneNode(false);\n img.crossOrigin = \"Anonymous\";\n imgLoaded(img).then(() => {\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n canvas.width = img.width;\n canvas.height = img.height;\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height);\n resolve(canvas.toDataURL(options == null ? void 0 : options.type, options == null ? void 0 : options.quality));\n }).catch(reject);\n } else if (typeof _target === \"object\") {\n const _serializeFn = (options == null ? void 0 : options.serializer) || getDefaultSerialization(_target);\n const serialized = _serializeFn(_target);\n return resolve(blobToBase64(new Blob([serialized], { type: \"application/json\" })));\n } else {\n reject(new Error(\"target is unsupported types\"));\n }\n } catch (error) {\n reject(error);\n }\n });\n promise.value.then((res) => {\n base64.value = (options == null ? void 0 : options.dataUrl) === false ? res.replace(/^data:.*?;base64,/, \"\") : res;\n });\n return promise.value;\n }\n if (isRef(target) || typeof target === \"function\")\n watch(target, execute, { immediate: true });\n else\n execute();\n return {\n base64,\n promise,\n execute\n };\n}\nfunction imgLoaded(img) {\n return new Promise((resolve, reject) => {\n if (!img.complete) {\n img.onload = () => {\n resolve();\n };\n img.onerror = reject;\n } else {\n resolve();\n }\n });\n}\nfunction blobToBase64(blob) {\n return new Promise((resolve, reject) => {\n const fr = new FileReader();\n fr.onload = (e) => {\n resolve(e.target.result);\n };\n fr.onerror = reject;\n fr.readAsDataURL(blob);\n });\n}\n\nfunction useBattery(options = {}) {\n const { navigator = defaultNavigator } = options;\n const events = [\"chargingchange\", \"chargingtimechange\", \"dischargingtimechange\", \"levelchange\"];\n const isSupported = useSupported(() => navigator && \"getBattery\" in navigator && typeof navigator.getBattery === \"function\");\n const charging = shallowRef(false);\n const chargingTime = shallowRef(0);\n const dischargingTime = shallowRef(0);\n const level = shallowRef(1);\n let battery;\n function updateBatteryInfo() {\n charging.value = this.charging;\n chargingTime.value = this.chargingTime || 0;\n dischargingTime.value = this.dischargingTime || 0;\n level.value = this.level;\n }\n if (isSupported.value) {\n navigator.getBattery().then((_battery) => {\n battery = _battery;\n updateBatteryInfo.call(battery);\n useEventListener(battery, events, updateBatteryInfo, { passive: true });\n });\n }\n return {\n isSupported,\n charging,\n chargingTime,\n dischargingTime,\n level\n };\n}\n\nfunction useBluetooth(options) {\n let {\n acceptAllDevices = false\n } = options || {};\n const {\n filters = void 0,\n optionalServices = void 0,\n navigator = defaultNavigator\n } = options || {};\n const isSupported = useSupported(() => navigator && \"bluetooth\" in navigator);\n const device = shallowRef();\n const error = shallowRef(null);\n watch(device, () => {\n connectToBluetoothGATTServer();\n });\n async function requestDevice() {\n if (!isSupported.value)\n return;\n error.value = null;\n if (filters && filters.length > 0)\n acceptAllDevices = false;\n try {\n device.value = await (navigator == null ? void 0 : navigator.bluetooth.requestDevice({\n acceptAllDevices,\n filters,\n optionalServices\n }));\n } catch (err) {\n error.value = err;\n }\n }\n const server = shallowRef();\n const isConnected = shallowRef(false);\n function reset() {\n isConnected.value = false;\n device.value = void 0;\n server.value = void 0;\n }\n async function connectToBluetoothGATTServer() {\n error.value = null;\n if (device.value && device.value.gatt) {\n useEventListener(device, \"gattserverdisconnected\", reset, { passive: true });\n try {\n server.value = await device.value.gatt.connect();\n isConnected.value = server.value.connected;\n } catch (err) {\n error.value = err;\n }\n }\n }\n tryOnMounted(() => {\n var _a;\n if (device.value)\n (_a = device.value.gatt) == null ? void 0 : _a.connect();\n });\n tryOnScopeDispose(() => {\n var _a;\n if (device.value)\n (_a = device.value.gatt) == null ? void 0 : _a.disconnect();\n });\n return {\n isSupported,\n isConnected: readonly(isConnected),\n // Device:\n device,\n requestDevice,\n // Server:\n server,\n // Errors:\n error\n };\n}\n\nconst ssrWidthSymbol = Symbol(\"vueuse-ssr-width\");\nfunction useSSRWidth() {\n const ssrWidth = hasInjectionContext() ? injectLocal(ssrWidthSymbol, null) : null;\n return typeof ssrWidth === \"number\" ? ssrWidth : void 0;\n}\nfunction provideSSRWidth(width, app) {\n if (app !== void 0) {\n app.provide(ssrWidthSymbol, width);\n } else {\n provideLocal(ssrWidthSymbol, width);\n }\n}\n\nfunction useMediaQuery(query, options = {}) {\n const { window = defaultWindow, ssrWidth = useSSRWidth() } = options;\n const isSupported = useSupported(() => window && \"matchMedia\" in window && typeof window.matchMedia === \"function\");\n const ssrSupport = shallowRef(typeof ssrWidth === \"number\");\n const mediaQuery = shallowRef();\n const matches = shallowRef(false);\n const handler = (event) => {\n matches.value = event.matches;\n };\n watchEffect(() => {\n if (ssrSupport.value) {\n ssrSupport.value = !isSupported.value;\n const queryStrings = toValue(query).split(\",\");\n matches.value = queryStrings.some((queryString) => {\n const not = queryString.includes(\"not all\");\n const minWidth = queryString.match(/\\(\\s*min-width:\\s*(-?\\d+(?:\\.\\d*)?[a-z]+\\s*)\\)/);\n const maxWidth = queryString.match(/\\(\\s*max-width:\\s*(-?\\d+(?:\\.\\d*)?[a-z]+\\s*)\\)/);\n let res = Boolean(minWidth || maxWidth);\n if (minWidth && res) {\n res = ssrWidth >= pxValue(minWidth[1]);\n }\n if (maxWidth && res) {\n res = ssrWidth <= pxValue(maxWidth[1]);\n }\n return not ? !res : res;\n });\n return;\n }\n if (!isSupported.value)\n return;\n mediaQuery.value = window.matchMedia(toValue(query));\n matches.value = mediaQuery.value.matches;\n });\n useEventListener(mediaQuery, \"change\", handler, { passive: true });\n return computed(() => matches.value);\n}\n\nconst breakpointsTailwind = {\n \"sm\": 640,\n \"md\": 768,\n \"lg\": 1024,\n \"xl\": 1280,\n \"2xl\": 1536\n};\nconst breakpointsBootstrapV5 = {\n xs: 0,\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200,\n xxl: 1400\n};\nconst breakpointsVuetifyV2 = {\n xs: 0,\n sm: 600,\n md: 960,\n lg: 1264,\n xl: 1904\n};\nconst breakpointsVuetifyV3 = {\n xs: 0,\n sm: 600,\n md: 960,\n lg: 1280,\n xl: 1920,\n xxl: 2560\n};\nconst breakpointsVuetify = breakpointsVuetifyV2;\nconst breakpointsAntDesign = {\n xs: 480,\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200,\n xxl: 1600\n};\nconst breakpointsQuasar = {\n xs: 0,\n sm: 600,\n md: 1024,\n lg: 1440,\n xl: 1920\n};\nconst breakpointsSematic = {\n mobileS: 320,\n mobileM: 375,\n mobileL: 425,\n tablet: 768,\n laptop: 1024,\n laptopL: 1440,\n desktop4K: 2560\n};\nconst breakpointsMasterCss = {\n \"3xs\": 360,\n \"2xs\": 480,\n \"xs\": 600,\n \"sm\": 768,\n \"md\": 1024,\n \"lg\": 1280,\n \"xl\": 1440,\n \"2xl\": 1600,\n \"3xl\": 1920,\n \"4xl\": 2560\n};\nconst breakpointsPrimeFlex = {\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200\n};\nconst breakpointsElement = {\n xs: 0,\n sm: 768,\n md: 992,\n lg: 1200,\n xl: 1920\n};\n\nfunction useBreakpoints(breakpoints, options = {}) {\n function getValue(k, delta) {\n let v = toValue(breakpoints[toValue(k)]);\n if (delta != null)\n v = increaseWithUnit(v, delta);\n if (typeof v === \"number\")\n v = `${v}px`;\n return v;\n }\n const { window = defaultWindow, strategy = \"min-width\", ssrWidth = useSSRWidth() } = options;\n const ssrSupport = typeof ssrWidth === \"number\";\n const mounted = ssrSupport ? shallowRef(false) : { value: true };\n if (ssrSupport) {\n tryOnMounted(() => mounted.value = !!window);\n }\n function match(query, size) {\n if (!mounted.value && ssrSupport) {\n return query === \"min\" ? ssrWidth >= pxValue(size) : ssrWidth <= pxValue(size);\n }\n if (!window)\n return false;\n return window.matchMedia(`(${query}-width: ${size})`).matches;\n }\n const greaterOrEqual = (k) => {\n return useMediaQuery(() => `(min-width: ${getValue(k)})`, options);\n };\n const smallerOrEqual = (k) => {\n return useMediaQuery(() => `(max-width: ${getValue(k)})`, options);\n };\n const shortcutMethods = Object.keys(breakpoints).reduce((shortcuts, k) => {\n Object.defineProperty(shortcuts, k, {\n get: () => strategy === \"min-width\" ? greaterOrEqual(k) : smallerOrEqual(k),\n enumerable: true,\n configurable: true\n });\n return shortcuts;\n }, {});\n function current() {\n const points = Object.keys(breakpoints).map((k) => [k, shortcutMethods[k], pxValue(getValue(k))]).sort((a, b) => a[2] - b[2]);\n return computed(() => points.filter(([, v]) => v.value).map(([k]) => k));\n }\n return Object.assign(shortcutMethods, {\n greaterOrEqual,\n smallerOrEqual,\n greater(k) {\n return useMediaQuery(() => `(min-width: ${getValue(k, 0.1)})`, options);\n },\n smaller(k) {\n return useMediaQuery(() => `(max-width: ${getValue(k, -0.1)})`, options);\n },\n between(a, b) {\n return useMediaQuery(() => `(min-width: ${getValue(a)}) and (max-width: ${getValue(b, -0.1)})`, options);\n },\n isGreater(k) {\n return match(\"min\", getValue(k, 0.1));\n },\n isGreaterOrEqual(k) {\n return match(\"min\", getValue(k));\n },\n isSmaller(k) {\n return match(\"max\", getValue(k, -0.1));\n },\n isSmallerOrEqual(k) {\n return match(\"max\", getValue(k));\n },\n isInBetween(a, b) {\n return match(\"min\", getValue(a)) && match(\"max\", getValue(b, -0.1));\n },\n current,\n active() {\n const bps = current();\n return computed(() => bps.value.length === 0 ? \"\" : bps.value.at(strategy === \"min-width\" ? -1 : 0));\n }\n });\n}\n\nfunction useBroadcastChannel(options) {\n const {\n name,\n window = defaultWindow\n } = options;\n const isSupported = useSupported(() => window && \"BroadcastChannel\" in window);\n const isClosed = shallowRef(false);\n const channel = ref();\n const data = ref();\n const error = shallowRef(null);\n const post = (data2) => {\n if (channel.value)\n channel.value.postMessage(data2);\n };\n const close = () => {\n if (channel.value)\n channel.value.close();\n isClosed.value = true;\n };\n if (isSupported.value) {\n tryOnMounted(() => {\n error.value = null;\n channel.value = new BroadcastChannel(name);\n const listenerOptions = {\n passive: true\n };\n useEventListener(channel, \"message\", (e) => {\n data.value = e.data;\n }, listenerOptions);\n useEventListener(channel, \"messageerror\", (e) => {\n error.value = e;\n }, listenerOptions);\n useEventListener(channel, \"close\", () => {\n isClosed.value = true;\n }, listenerOptions);\n });\n }\n tryOnScopeDispose(() => {\n close();\n });\n return {\n isSupported,\n channel,\n data,\n post,\n close,\n error,\n isClosed\n };\n}\n\nconst WRITABLE_PROPERTIES = [\n \"hash\",\n \"host\",\n \"hostname\",\n \"href\",\n \"pathname\",\n \"port\",\n \"protocol\",\n \"search\"\n];\nfunction useBrowserLocation(options = {}) {\n const { window = defaultWindow } = options;\n const refs = Object.fromEntries(\n WRITABLE_PROPERTIES.map((key) => [key, ref()])\n );\n for (const [key, ref] of objectEntries(refs)) {\n watch(ref, (value) => {\n if (!(window == null ? void 0 : window.location) || window.location[key] === value)\n return;\n window.location[key] = value;\n });\n }\n const buildState = (trigger) => {\n var _a;\n const { state: state2, length } = (window == null ? void 0 : window.history) || {};\n const { origin } = (window == null ? void 0 : window.location) || {};\n for (const key of WRITABLE_PROPERTIES)\n refs[key].value = (_a = window == null ? void 0 : window.location) == null ? void 0 : _a[key];\n return reactive({\n trigger,\n state: state2,\n length,\n origin,\n ...refs\n });\n };\n const state = ref(buildState(\"load\"));\n if (window) {\n const listenerOptions = { passive: true };\n useEventListener(window, \"popstate\", () => state.value = buildState(\"popstate\"), listenerOptions);\n useEventListener(window, \"hashchange\", () => state.value = buildState(\"hashchange\"), listenerOptions);\n }\n return state;\n}\n\nfunction useCached(refValue, comparator = (a, b) => a === b, options) {\n const { deepRefs = true, ...watchOptions } = options || {};\n const cachedValue = createRef(refValue.value, deepRefs);\n watch(() => refValue.value, (value) => {\n if (!comparator(value, cachedValue.value))\n cachedValue.value = value;\n }, watchOptions);\n return cachedValue;\n}\n\nfunction usePermission(permissionDesc, options = {}) {\n const {\n controls = false,\n navigator = defaultNavigator\n } = options;\n const isSupported = useSupported(() => navigator && \"permissions\" in navigator);\n const permissionStatus = shallowRef();\n const desc = typeof permissionDesc === \"string\" ? { name: permissionDesc } : permissionDesc;\n const state = shallowRef();\n const update = () => {\n var _a, _b;\n state.value = (_b = (_a = permissionStatus.value) == null ? void 0 : _a.state) != null ? _b : \"prompt\";\n };\n useEventListener(permissionStatus, \"change\", update, { passive: true });\n const query = createSingletonPromise(async () => {\n if (!isSupported.value)\n return;\n if (!permissionStatus.value) {\n try {\n permissionStatus.value = await navigator.permissions.query(desc);\n } catch (e) {\n permissionStatus.value = void 0;\n } finally {\n update();\n }\n }\n if (controls)\n return toRaw(permissionStatus.value);\n });\n query();\n if (controls) {\n return {\n state,\n isSupported,\n query\n };\n } else {\n return state;\n }\n}\n\nfunction useClipboard(options = {}) {\n const {\n navigator = defaultNavigator,\n read = false,\n source,\n copiedDuring = 1500,\n legacy = false\n } = options;\n const isClipboardApiSupported = useSupported(() => navigator && \"clipboard\" in navigator);\n const permissionRead = usePermission(\"clipboard-read\");\n const permissionWrite = usePermission(\"clipboard-write\");\n const isSupported = computed(() => isClipboardApiSupported.value || legacy);\n const text = shallowRef(\"\");\n const copied = shallowRef(false);\n const timeout = useTimeoutFn(() => copied.value = false, copiedDuring, { immediate: false });\n async function updateText() {\n let useLegacy = !(isClipboardApiSupported.value && isAllowed(permissionRead.value));\n if (!useLegacy) {\n try {\n text.value = await navigator.clipboard.readText();\n } catch (e) {\n useLegacy = true;\n }\n }\n if (useLegacy) {\n text.value = legacyRead();\n }\n }\n if (isSupported.value && read)\n useEventListener([\"copy\", \"cut\"], updateText, { passive: true });\n async function copy(value = toValue(source)) {\n if (isSupported.value && value != null) {\n let useLegacy = !(isClipboardApiSupported.value && isAllowed(permissionWrite.value));\n if (!useLegacy) {\n try {\n await navigator.clipboard.writeText(value);\n } catch (e) {\n useLegacy = true;\n }\n }\n if (useLegacy)\n legacyCopy(value);\n text.value = value;\n copied.value = true;\n timeout.start();\n }\n }\n function legacyCopy(value) {\n const ta = document.createElement(\"textarea\");\n ta.value = value != null ? value : \"\";\n ta.style.position = \"absolute\";\n ta.style.opacity = \"0\";\n document.body.appendChild(ta);\n ta.select();\n document.execCommand(\"copy\");\n ta.remove();\n }\n function legacyRead() {\n var _a, _b, _c;\n return (_c = (_b = (_a = document == null ? void 0 : document.getSelection) == null ? void 0 : _a.call(document)) == null ? void 0 : _b.toString()) != null ? _c : \"\";\n }\n function isAllowed(status) {\n return status === \"granted\" || status === \"prompt\";\n }\n return {\n isSupported,\n text,\n copied,\n copy\n };\n}\n\nfunction useClipboardItems(options = {}) {\n const {\n navigator = defaultNavigator,\n read = false,\n source,\n copiedDuring = 1500\n } = options;\n const isSupported = useSupported(() => navigator && \"clipboard\" in navigator);\n const content = ref([]);\n const copied = shallowRef(false);\n const timeout = useTimeoutFn(() => copied.value = false, copiedDuring, { immediate: false });\n function updateContent() {\n if (isSupported.value) {\n navigator.clipboard.read().then((items) => {\n content.value = items;\n });\n }\n }\n if (isSupported.value && read)\n useEventListener([\"copy\", \"cut\"], updateContent, { passive: true });\n async function copy(value = toValue(source)) {\n if (isSupported.value && value != null) {\n await navigator.clipboard.write(value);\n content.value = value;\n copied.value = true;\n timeout.start();\n }\n }\n return {\n isSupported,\n content,\n copied,\n copy\n };\n}\n\nfunction cloneFnJSON(source) {\n return JSON.parse(JSON.stringify(source));\n}\nfunction useCloned(source, options = {}) {\n const cloned = ref({});\n const isModified = shallowRef(false);\n let _lastSync = false;\n const {\n manual,\n clone = cloneFnJSON,\n // watch options\n deep = true,\n immediate = true\n } = options;\n watch(cloned, () => {\n if (_lastSync) {\n _lastSync = false;\n return;\n }\n isModified.value = true;\n }, {\n deep: true,\n flush: \"sync\"\n });\n function sync() {\n _lastSync = true;\n isModified.value = false;\n cloned.value = clone(toValue(source));\n }\n if (!manual && (isRef(source) || typeof source === \"function\")) {\n watch(source, sync, {\n ...options,\n deep,\n immediate\n });\n } else {\n sync();\n }\n return { cloned, isModified, sync };\n}\n\nconst _global = typeof globalThis !== \"undefined\" ? globalThis : typeof window !== \"undefined\" ? window : typeof global !== \"undefined\" ? global : typeof self !== \"undefined\" ? self : {};\nconst globalKey = \"__vueuse_ssr_handlers__\";\nconst handlers = /* @__PURE__ */ getHandlers();\nfunction getHandlers() {\n if (!(globalKey in _global))\n _global[globalKey] = _global[globalKey] || {};\n return _global[globalKey];\n}\nfunction getSSRHandler(key, fallback) {\n return handlers[key] || fallback;\n}\nfunction setSSRHandler(key, fn) {\n handlers[key] = fn;\n}\n\nfunction usePreferredDark(options) {\n return useMediaQuery(\"(prefers-color-scheme: dark)\", options);\n}\n\nfunction guessSerializerType(rawInit) {\n return rawInit == null ? \"any\" : rawInit instanceof Set ? \"set\" : rawInit instanceof Map ? \"map\" : rawInit instanceof Date ? \"date\" : typeof rawInit === \"boolean\" ? \"boolean\" : typeof rawInit === \"string\" ? \"string\" : typeof rawInit === \"object\" ? \"object\" : !Number.isNaN(rawInit) ? \"number\" : \"any\";\n}\n\nconst StorageSerializers = {\n boolean: {\n read: (v) => v === \"true\",\n write: (v) => String(v)\n },\n object: {\n read: (v) => JSON.parse(v),\n write: (v) => JSON.stringify(v)\n },\n number: {\n read: (v) => Number.parseFloat(v),\n write: (v) => String(v)\n },\n any: {\n read: (v) => v,\n write: (v) => String(v)\n },\n string: {\n read: (v) => v,\n write: (v) => String(v)\n },\n map: {\n read: (v) => new Map(JSON.parse(v)),\n write: (v) => JSON.stringify(Array.from(v.entries()))\n },\n set: {\n read: (v) => new Set(JSON.parse(v)),\n write: (v) => JSON.stringify(Array.from(v))\n },\n date: {\n read: (v) => new Date(v),\n write: (v) => v.toISOString()\n }\n};\nconst customStorageEventName = \"vueuse-storage\";\nfunction useStorage(key, defaults, storage, options = {}) {\n var _a;\n const {\n flush = \"pre\",\n deep = true,\n listenToStorageChanges = true,\n writeDefaults = true,\n mergeDefaults = false,\n shallow,\n window = defaultWindow,\n eventFilter,\n onError = (e) => {\n console.error(e);\n },\n initOnMounted\n } = options;\n const data = (shallow ? shallowRef : ref)(typeof defaults === \"function\" ? defaults() : defaults);\n const keyComputed = computed(() => toValue(key));\n if (!storage) {\n try {\n storage = getSSRHandler(\"getDefaultStorage\", () => {\n var _a2;\n return (_a2 = defaultWindow) == null ? void 0 : _a2.localStorage;\n })();\n } catch (e) {\n onError(e);\n }\n }\n if (!storage)\n return data;\n const rawInit = toValue(defaults);\n const type = guessSerializerType(rawInit);\n const serializer = (_a = options.serializer) != null ? _a : StorageSerializers[type];\n const { pause: pauseWatch, resume: resumeWatch } = pausableWatch(\n data,\n () => write(data.value),\n { flush, deep, eventFilter }\n );\n watch(keyComputed, () => update(), { flush });\n if (window && listenToStorageChanges) {\n tryOnMounted(() => {\n if (storage instanceof Storage)\n useEventListener(window, \"storage\", update, { passive: true });\n else\n useEventListener(window, customStorageEventName, updateFromCustomEvent);\n if (initOnMounted)\n update();\n });\n }\n if (!initOnMounted)\n update();\n function dispatchWriteEvent(oldValue, newValue) {\n if (window) {\n const payload = {\n key: keyComputed.value,\n oldValue,\n newValue,\n storageArea: storage\n };\n window.dispatchEvent(storage instanceof Storage ? new StorageEvent(\"storage\", payload) : new CustomEvent(customStorageEventName, {\n detail: payload\n }));\n }\n }\n function write(v) {\n try {\n const oldValue = storage.getItem(keyComputed.value);\n if (v == null) {\n dispatchWriteEvent(oldValue, null);\n storage.removeItem(keyComputed.value);\n } else {\n const serialized = serializer.write(v);\n if (oldValue !== serialized) {\n storage.setItem(keyComputed.value, serialized);\n dispatchWriteEvent(oldValue, serialized);\n }\n }\n } catch (e) {\n onError(e);\n }\n }\n function read(event) {\n const rawValue = event ? event.newValue : storage.getItem(keyComputed.value);\n if (rawValue == null) {\n if (writeDefaults && rawInit != null)\n storage.setItem(keyComputed.value, serializer.write(rawInit));\n return rawInit;\n } else if (!event && mergeDefaults) {\n const value = serializer.read(rawValue);\n if (typeof mergeDefaults === \"function\")\n return mergeDefaults(value, rawInit);\n else if (type === \"object\" && !Array.isArray(value))\n return { ...rawInit, ...value };\n return value;\n } else if (typeof rawValue !== \"string\") {\n return rawValue;\n } else {\n return serializer.read(rawValue);\n }\n }\n function update(event) {\n if (event && event.storageArea !== storage)\n return;\n if (event && event.key == null) {\n data.value = rawInit;\n return;\n }\n if (event && event.key !== keyComputed.value)\n return;\n pauseWatch();\n try {\n if ((event == null ? void 0 : event.newValue) !== serializer.write(data.value))\n data.value = read(event);\n } catch (e) {\n onError(e);\n } finally {\n if (event)\n nextTick(resumeWatch);\n else\n resumeWatch();\n }\n }\n function updateFromCustomEvent(event) {\n update(event.detail);\n }\n return data;\n}\n\nconst CSS_DISABLE_TRANS = \"*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}\";\nfunction useColorMode(options = {}) {\n const {\n selector = \"html\",\n attribute = \"class\",\n initialValue = \"auto\",\n window = defaultWindow,\n storage,\n storageKey = \"vueuse-color-scheme\",\n listenToStorageChanges = true,\n storageRef,\n emitAuto,\n disableTransition = true\n } = options;\n const modes = {\n auto: \"\",\n light: \"light\",\n dark: \"dark\",\n ...options.modes || {}\n };\n const preferredDark = usePreferredDark({ window });\n const system = computed(() => preferredDark.value ? \"dark\" : \"light\");\n const store = storageRef || (storageKey == null ? toRef(initialValue) : useStorage(storageKey, initialValue, storage, { window, listenToStorageChanges }));\n const state = computed(() => store.value === \"auto\" ? system.value : store.value);\n const updateHTMLAttrs = getSSRHandler(\n \"updateHTMLAttrs\",\n (selector2, attribute2, value) => {\n const el = typeof selector2 === \"string\" ? window == null ? void 0 : window.document.querySelector(selector2) : unrefElement(selector2);\n if (!el)\n return;\n const classesToAdd = /* @__PURE__ */ new Set();\n const classesToRemove = /* @__PURE__ */ new Set();\n let attributeToChange = null;\n if (attribute2 === \"class\") {\n const current = value.split(/\\s/g);\n Object.values(modes).flatMap((i) => (i || \"\").split(/\\s/g)).filter(Boolean).forEach((v) => {\n if (current.includes(v))\n classesToAdd.add(v);\n else\n classesToRemove.add(v);\n });\n } else {\n attributeToChange = { key: attribute2, value };\n }\n if (classesToAdd.size === 0 && classesToRemove.size === 0 && attributeToChange === null)\n return;\n let style;\n if (disableTransition) {\n style = window.document.createElement(\"style\");\n style.appendChild(document.createTextNode(CSS_DISABLE_TRANS));\n window.document.head.appendChild(style);\n }\n for (const c of classesToAdd) {\n el.classList.add(c);\n }\n for (const c of classesToRemove) {\n el.classList.remove(c);\n }\n if (attributeToChange) {\n el.setAttribute(attributeToChange.key, attributeToChange.value);\n }\n if (disableTransition) {\n window.getComputedStyle(style).opacity;\n document.head.removeChild(style);\n }\n }\n );\n function defaultOnChanged(mode) {\n var _a;\n updateHTMLAttrs(selector, attribute, (_a = modes[mode]) != null ? _a : mode);\n }\n function onChanged(mode) {\n if (options.onChanged)\n options.onChanged(mode, defaultOnChanged);\n else\n defaultOnChanged(mode);\n }\n watch(state, onChanged, { flush: \"post\", immediate: true });\n tryOnMounted(() => onChanged(state.value));\n const auto = computed({\n get() {\n return emitAuto ? store.value : state.value;\n },\n set(v) {\n store.value = v;\n }\n });\n return Object.assign(auto, { store, system, state });\n}\n\nfunction useConfirmDialog(revealed = shallowRef(false)) {\n const confirmHook = createEventHook();\n const cancelHook = createEventHook();\n const revealHook = createEventHook();\n let _resolve = noop;\n const reveal = (data) => {\n revealHook.trigger(data);\n revealed.value = true;\n return new Promise((resolve) => {\n _resolve = resolve;\n });\n };\n const confirm = (data) => {\n revealed.value = false;\n confirmHook.trigger(data);\n _resolve({ data, isCanceled: false });\n };\n const cancel = (data) => {\n revealed.value = false;\n cancelHook.trigger(data);\n _resolve({ data, isCanceled: true });\n };\n return {\n isRevealed: computed(() => revealed.value),\n reveal,\n confirm,\n cancel,\n onReveal: revealHook.on,\n onConfirm: confirmHook.on,\n onCancel: cancelHook.on\n };\n}\n\nfunction useCountdown(initialCountdown, options) {\n var _a, _b;\n const remaining = shallowRef(toValue(initialCountdown));\n const intervalController = useIntervalFn(() => {\n var _a2, _b2;\n const value = remaining.value - 1;\n remaining.value = value < 0 ? 0 : value;\n (_a2 = options == null ? void 0 : options.onTick) == null ? void 0 : _a2.call(options);\n if (remaining.value <= 0) {\n intervalController.pause();\n (_b2 = options == null ? void 0 : options.onComplete) == null ? void 0 : _b2.call(options);\n }\n }, (_a = options == null ? void 0 : options.interval) != null ? _a : 1e3, { immediate: (_b = options == null ? void 0 : options.immediate) != null ? _b : false });\n const reset = (countdown) => {\n var _a2;\n remaining.value = (_a2 = toValue(countdown)) != null ? _a2 : toValue(initialCountdown);\n };\n const stop = () => {\n intervalController.pause();\n reset();\n };\n const resume = () => {\n if (!intervalController.isActive.value) {\n if (remaining.value > 0) {\n intervalController.resume();\n }\n }\n };\n const start = (countdown) => {\n reset(countdown);\n intervalController.resume();\n };\n return {\n remaining,\n reset,\n stop,\n start,\n pause: intervalController.pause,\n resume,\n isActive: intervalController.isActive\n };\n}\n\nfunction useCssVar(prop, target, options = {}) {\n const { window = defaultWindow, initialValue, observe = false } = options;\n const variable = shallowRef(initialValue);\n const elRef = computed(() => {\n var _a;\n return unrefElement(target) || ((_a = window == null ? void 0 : window.document) == null ? void 0 : _a.documentElement);\n });\n function updateCssVar() {\n var _a;\n const key = toValue(prop);\n const el = toValue(elRef);\n if (el && window && key) {\n const value = (_a = window.getComputedStyle(el).getPropertyValue(key)) == null ? void 0 : _a.trim();\n variable.value = value || variable.value || initialValue;\n }\n }\n if (observe) {\n useMutationObserver(elRef, updateCssVar, {\n attributeFilter: [\"style\", \"class\"],\n window\n });\n }\n watch(\n [elRef, () => toValue(prop)],\n (_, old) => {\n if (old[0] && old[1])\n old[0].style.removeProperty(old[1]);\n updateCssVar();\n },\n { immediate: true }\n );\n watch(\n [variable, elRef],\n ([val, el]) => {\n const raw_prop = toValue(prop);\n if ((el == null ? void 0 : el.style) && raw_prop) {\n if (val == null)\n el.style.removeProperty(raw_prop);\n else\n el.style.setProperty(raw_prop, val);\n }\n },\n { immediate: true }\n );\n return variable;\n}\n\nfunction useCurrentElement(rootComponent) {\n const vm = getCurrentInstance();\n const currentElement = computedWithControl(\n () => null,\n () => rootComponent ? unrefElement(rootComponent) : vm.proxy.$el\n );\n onUpdated(currentElement.trigger);\n onMounted(currentElement.trigger);\n return currentElement;\n}\n\nfunction useCycleList(list, options) {\n const state = shallowRef(getInitialValue());\n const listRef = toRef(list);\n const index = computed({\n get() {\n var _a;\n const targetList = listRef.value;\n let index2 = (options == null ? void 0 : options.getIndexOf) ? options.getIndexOf(state.value, targetList) : targetList.indexOf(state.value);\n if (index2 < 0)\n index2 = (_a = options == null ? void 0 : options.fallbackIndex) != null ? _a : 0;\n return index2;\n },\n set(v) {\n set(v);\n }\n });\n function set(i) {\n const targetList = listRef.value;\n const length = targetList.length;\n const index2 = (i % length + length) % length;\n const value = targetList[index2];\n state.value = value;\n return value;\n }\n function shift(delta = 1) {\n return set(index.value + delta);\n }\n function next(n = 1) {\n return shift(n);\n }\n function prev(n = 1) {\n return shift(-n);\n }\n function getInitialValue() {\n var _a, _b;\n return (_b = toValue((_a = options == null ? void 0 : options.initialValue) != null ? _a : toValue(list)[0])) != null ? _b : void 0;\n }\n watch(listRef, () => set(index.value));\n return {\n state,\n index,\n next,\n prev,\n go: set\n };\n}\n\nfunction useDark(options = {}) {\n const {\n valueDark = \"dark\",\n valueLight = \"\"\n } = options;\n const mode = useColorMode({\n ...options,\n onChanged: (mode2, defaultHandler) => {\n var _a;\n if (options.onChanged)\n (_a = options.onChanged) == null ? void 0 : _a.call(options, mode2 === \"dark\", defaultHandler, mode2);\n else\n defaultHandler(mode2);\n },\n modes: {\n dark: valueDark,\n light: valueLight\n }\n });\n const system = computed(() => mode.system.value);\n const isDark = computed({\n get() {\n return mode.value === \"dark\";\n },\n set(v) {\n const modeVal = v ? \"dark\" : \"light\";\n if (system.value === modeVal)\n mode.value = \"auto\";\n else\n mode.value = modeVal;\n }\n });\n return isDark;\n}\n\nfunction fnBypass(v) {\n return v;\n}\nfunction fnSetSource(source, value) {\n return source.value = value;\n}\nfunction defaultDump(clone) {\n return clone ? typeof clone === \"function\" ? clone : cloneFnJSON : fnBypass;\n}\nfunction defaultParse(clone) {\n return clone ? typeof clone === \"function\" ? clone : cloneFnJSON : fnBypass;\n}\nfunction useManualRefHistory(source, options = {}) {\n const {\n clone = false,\n dump = defaultDump(clone),\n parse = defaultParse(clone),\n setSource = fnSetSource\n } = options;\n function _createHistoryRecord() {\n return markRaw({\n snapshot: dump(source.value),\n timestamp: timestamp()\n });\n }\n const last = ref(_createHistoryRecord());\n const undoStack = ref([]);\n const redoStack = ref([]);\n const _setSource = (record) => {\n setSource(source, parse(record.snapshot));\n last.value = record;\n };\n const commit = () => {\n undoStack.value.unshift(last.value);\n last.value = _createHistoryRecord();\n if (options.capacity && undoStack.value.length > options.capacity)\n undoStack.value.splice(options.capacity, Number.POSITIVE_INFINITY);\n if (redoStack.value.length)\n redoStack.value.splice(0, redoStack.value.length);\n };\n const clear = () => {\n undoStack.value.splice(0, undoStack.value.length);\n redoStack.value.splice(0, redoStack.value.length);\n };\n const undo = () => {\n const state = undoStack.value.shift();\n if (state) {\n redoStack.value.unshift(last.value);\n _setSource(state);\n }\n };\n const redo = () => {\n const state = redoStack.value.shift();\n if (state) {\n undoStack.value.unshift(last.value);\n _setSource(state);\n }\n };\n const reset = () => {\n _setSource(last.value);\n };\n const history = computed(() => [last.value, ...undoStack.value]);\n const canUndo = computed(() => undoStack.value.length > 0);\n const canRedo = computed(() => redoStack.value.length > 0);\n return {\n source,\n undoStack,\n redoStack,\n last,\n history,\n canUndo,\n canRedo,\n clear,\n commit,\n reset,\n undo,\n redo\n };\n}\n\nfunction useRefHistory(source, options = {}) {\n const {\n deep = false,\n flush = \"pre\",\n eventFilter\n } = options;\n const {\n eventFilter: composedFilter,\n pause,\n resume: resumeTracking,\n isActive: isTracking\n } = pausableFilter(eventFilter);\n const {\n ignoreUpdates,\n ignorePrevAsyncUpdates,\n stop\n } = watchIgnorable(\n source,\n commit,\n { deep, flush, eventFilter: composedFilter }\n );\n function setSource(source2, value) {\n ignorePrevAsyncUpdates();\n ignoreUpdates(() => {\n source2.value = value;\n });\n }\n const manualHistory = useManualRefHistory(source, { ...options, clone: options.clone || deep, setSource });\n const { clear, commit: manualCommit } = manualHistory;\n function commit() {\n ignorePrevAsyncUpdates();\n manualCommit();\n }\n function resume(commitNow) {\n resumeTracking();\n if (commitNow)\n commit();\n }\n function batch(fn) {\n let canceled = false;\n const cancel = () => canceled = true;\n ignoreUpdates(() => {\n fn(cancel);\n });\n if (!canceled)\n commit();\n }\n function dispose() {\n stop();\n clear();\n }\n return {\n ...manualHistory,\n isTracking,\n pause,\n resume,\n commit,\n batch,\n dispose\n };\n}\n\nfunction useDebouncedRefHistory(source, options = {}) {\n const filter = options.debounce ? debounceFilter(options.debounce) : void 0;\n const history = useRefHistory(source, { ...options, eventFilter: filter });\n return {\n ...history\n };\n}\n\nfunction useDeviceMotion(options = {}) {\n const {\n window = defaultWindow,\n requestPermissions = false,\n eventFilter = bypassFilter\n } = options;\n const isSupported = useSupported(() => typeof DeviceMotionEvent !== \"undefined\");\n const requirePermissions = useSupported(() => isSupported.value && \"requestPermission\" in DeviceMotionEvent && typeof DeviceMotionEvent.requestPermission === \"function\");\n const permissionGranted = shallowRef(false);\n const acceleration = ref({ x: null, y: null, z: null });\n const rotationRate = ref({ alpha: null, beta: null, gamma: null });\n const interval = shallowRef(0);\n const accelerationIncludingGravity = ref({\n x: null,\n y: null,\n z: null\n });\n function init() {\n if (window) {\n const onDeviceMotion = createFilterWrapper(\n eventFilter,\n (event) => {\n var _a, _b, _c, _d, _e, _f, _g, _h, _i;\n acceleration.value = {\n x: ((_a = event.acceleration) == null ? void 0 : _a.x) || null,\n y: ((_b = event.acceleration) == null ? void 0 : _b.y) || null,\n z: ((_c = event.acceleration) == null ? void 0 : _c.z) || null\n };\n accelerationIncludingGravity.value = {\n x: ((_d = event.accelerationIncludingGravity) == null ? void 0 : _d.x) || null,\n y: ((_e = event.accelerationIncludingGravity) == null ? void 0 : _e.y) || null,\n z: ((_f = event.accelerationIncludingGravity) == null ? void 0 : _f.z) || null\n };\n rotationRate.value = {\n alpha: ((_g = event.rotationRate) == null ? void 0 : _g.alpha) || null,\n beta: ((_h = event.rotationRate) == null ? void 0 : _h.beta) || null,\n gamma: ((_i = event.rotationRate) == null ? void 0 : _i.gamma) || null\n };\n interval.value = event.interval;\n }\n );\n useEventListener(window, \"devicemotion\", onDeviceMotion, { passive: true });\n }\n }\n const ensurePermissions = async () => {\n if (!requirePermissions.value)\n permissionGranted.value = true;\n if (permissionGranted.value)\n return;\n if (requirePermissions.value) {\n const requestPermission = DeviceMotionEvent.requestPermission;\n try {\n const response = await requestPermission();\n if (response === \"granted\") {\n permissionGranted.value = true;\n init();\n }\n } catch (error) {\n console.error(error);\n }\n }\n };\n if (isSupported.value) {\n if (requestPermissions && requirePermissions.value) {\n ensurePermissions().then(() => init());\n } else {\n init();\n }\n }\n return {\n acceleration,\n accelerationIncludingGravity,\n rotationRate,\n interval,\n isSupported,\n requirePermissions,\n ensurePermissions,\n permissionGranted\n };\n}\n\nfunction useDeviceOrientation(options = {}) {\n const { window = defaultWindow } = options;\n const isSupported = useSupported(() => window && \"DeviceOrientationEvent\" in window);\n const isAbsolute = shallowRef(false);\n const alpha = shallowRef(null);\n const beta = shallowRef(null);\n const gamma = shallowRef(null);\n if (window && isSupported.value) {\n useEventListener(window, \"deviceorientation\", (event) => {\n isAbsolute.value = event.absolute;\n alpha.value = event.alpha;\n beta.value = event.beta;\n gamma.value = event.gamma;\n }, { passive: true });\n }\n return {\n isSupported,\n isAbsolute,\n alpha,\n beta,\n gamma\n };\n}\n\nfunction useDevicePixelRatio(options = {}) {\n const {\n window = defaultWindow\n } = options;\n const pixelRatio = shallowRef(1);\n const query = useMediaQuery(() => `(resolution: ${pixelRatio.value}dppx)`, options);\n let stop = noop;\n if (window) {\n stop = watchImmediate(query, () => pixelRatio.value = window.devicePixelRatio);\n }\n return {\n pixelRatio: readonly(pixelRatio),\n stop\n };\n}\n\nfunction useDevicesList(options = {}) {\n const {\n navigator = defaultNavigator,\n requestPermissions = false,\n constraints = { audio: true, video: true },\n onUpdated\n } = options;\n const devices = ref([]);\n const videoInputs = computed(() => devices.value.filter((i) => i.kind === \"videoinput\"));\n const audioInputs = computed(() => devices.value.filter((i) => i.kind === \"audioinput\"));\n const audioOutputs = computed(() => devices.value.filter((i) => i.kind === \"audiooutput\"));\n const isSupported = useSupported(() => navigator && navigator.mediaDevices && navigator.mediaDevices.enumerateDevices);\n const permissionGranted = shallowRef(false);\n let stream;\n async function update() {\n if (!isSupported.value)\n return;\n devices.value = await navigator.mediaDevices.enumerateDevices();\n onUpdated == null ? void 0 : onUpdated(devices.value);\n if (stream) {\n stream.getTracks().forEach((t) => t.stop());\n stream = null;\n }\n }\n async function ensurePermissions() {\n const deviceName = constraints.video ? \"camera\" : \"microphone\";\n if (!isSupported.value)\n return false;\n if (permissionGranted.value)\n return true;\n const { state, query } = usePermission(deviceName, { controls: true });\n await query();\n if (state.value !== \"granted\") {\n let granted = true;\n try {\n stream = await navigator.mediaDevices.getUserMedia(constraints);\n } catch (e) {\n stream = null;\n granted = false;\n }\n update();\n permissionGranted.value = granted;\n } else {\n permissionGranted.value = true;\n }\n return permissionGranted.value;\n }\n if (isSupported.value) {\n if (requestPermissions)\n ensurePermissions();\n useEventListener(navigator.mediaDevices, \"devicechange\", update, { passive: true });\n update();\n }\n return {\n devices,\n ensurePermissions,\n permissionGranted,\n videoInputs,\n audioInputs,\n audioOutputs,\n isSupported\n };\n}\n\nfunction useDisplayMedia(options = {}) {\n var _a;\n const enabled = shallowRef((_a = options.enabled) != null ? _a : false);\n const video = options.video;\n const audio = options.audio;\n const { navigator = defaultNavigator } = options;\n const isSupported = useSupported(() => {\n var _a2;\n return (_a2 = navigator == null ? void 0 : navigator.mediaDevices) == null ? void 0 : _a2.getDisplayMedia;\n });\n const constraint = { audio, video };\n const stream = shallowRef();\n async function _start() {\n var _a2;\n if (!isSupported.value || stream.value)\n return;\n stream.value = await navigator.mediaDevices.getDisplayMedia(constraint);\n (_a2 = stream.value) == null ? void 0 : _a2.getTracks().forEach((t) => useEventListener(t, \"ended\", stop, { passive: true }));\n return stream.value;\n }\n async function _stop() {\n var _a2;\n (_a2 = stream.value) == null ? void 0 : _a2.getTracks().forEach((t) => t.stop());\n stream.value = void 0;\n }\n function stop() {\n _stop();\n enabled.value = false;\n }\n async function start() {\n await _start();\n if (stream.value)\n enabled.value = true;\n return stream.value;\n }\n watch(\n enabled,\n (v) => {\n if (v)\n _start();\n else\n _stop();\n },\n { immediate: true }\n );\n return {\n isSupported,\n stream,\n start,\n stop,\n enabled\n };\n}\n\nfunction useDocumentVisibility(options = {}) {\n const { document = defaultDocument } = options;\n if (!document)\n return shallowRef(\"visible\");\n const visibility = shallowRef(document.visibilityState);\n useEventListener(document, \"visibilitychange\", () => {\n visibility.value = document.visibilityState;\n }, { passive: true });\n return visibility;\n}\n\nfunction useDraggable(target, options = {}) {\n var _a;\n const {\n pointerTypes,\n preventDefault,\n stopPropagation,\n exact,\n onMove,\n onEnd,\n onStart,\n initialValue,\n axis = \"both\",\n draggingElement = defaultWindow,\n containerElement,\n handle: draggingHandle = target,\n buttons = [0]\n } = options;\n const position = ref(\n (_a = toValue(initialValue)) != null ? _a : { x: 0, y: 0 }\n );\n const pressedDelta = ref();\n const filterEvent = (e) => {\n if (pointerTypes)\n return pointerTypes.includes(e.pointerType);\n return true;\n };\n const handleEvent = (e) => {\n if (toValue(preventDefault))\n e.preventDefault();\n if (toValue(stopPropagation))\n e.stopPropagation();\n };\n const start = (e) => {\n var _a2;\n if (!toValue(buttons).includes(e.button))\n return;\n if (toValue(options.disabled) || !filterEvent(e))\n return;\n if (toValue(exact) && e.target !== toValue(target))\n return;\n const container = toValue(containerElement);\n const containerRect = (_a2 = container == null ? void 0 : container.getBoundingClientRect) == null ? void 0 : _a2.call(container);\n const targetRect = toValue(target).getBoundingClientRect();\n const pos = {\n x: e.clientX - (container ? targetRect.left - containerRect.left + container.scrollLeft : targetRect.left),\n y: e.clientY - (container ? targetRect.top - containerRect.top + container.scrollTop : targetRect.top)\n };\n if ((onStart == null ? void 0 : onStart(pos, e)) === false)\n return;\n pressedDelta.value = pos;\n handleEvent(e);\n };\n const move = (e) => {\n if (toValue(options.disabled) || !filterEvent(e))\n return;\n if (!pressedDelta.value)\n return;\n const container = toValue(containerElement);\n const targetRect = toValue(target).getBoundingClientRect();\n let { x, y } = position.value;\n if (axis === \"x\" || axis === \"both\") {\n x = e.clientX - pressedDelta.value.x;\n if (container)\n x = Math.min(Math.max(0, x), container.scrollWidth - targetRect.width);\n }\n if (axis === \"y\" || axis === \"both\") {\n y = e.clientY - pressedDelta.value.y;\n if (container)\n y = Math.min(Math.max(0, y), container.scrollHeight - targetRect.height);\n }\n position.value = {\n x,\n y\n };\n onMove == null ? void 0 : onMove(position.value, e);\n handleEvent(e);\n };\n const end = (e) => {\n if (toValue(options.disabled) || !filterEvent(e))\n return;\n if (!pressedDelta.value)\n return;\n pressedDelta.value = void 0;\n onEnd == null ? void 0 : onEnd(position.value, e);\n handleEvent(e);\n };\n if (isClient) {\n const config = () => {\n var _a2;\n return {\n capture: (_a2 = options.capture) != null ? _a2 : true,\n passive: !toValue(preventDefault)\n };\n };\n useEventListener(draggingHandle, \"pointerdown\", start, config);\n useEventListener(draggingElement, \"pointermove\", move, config);\n useEventListener(draggingElement, \"pointerup\", end, config);\n }\n return {\n ...toRefs(position),\n position,\n isDragging: computed(() => !!pressedDelta.value),\n style: computed(\n () => `left:${position.value.x}px;top:${position.value.y}px;`\n )\n };\n}\n\nfunction useDropZone(target, options = {}) {\n var _a, _b;\n const isOverDropZone = shallowRef(false);\n const files = shallowRef(null);\n let counter = 0;\n let isValid = true;\n if (isClient) {\n const _options = typeof options === \"function\" ? { onDrop: options } : options;\n const multiple = (_a = _options.multiple) != null ? _a : true;\n const preventDefaultForUnhandled = (_b = _options.preventDefaultForUnhandled) != null ? _b : false;\n const getFiles = (event) => {\n var _a2, _b2;\n const list = Array.from((_b2 = (_a2 = event.dataTransfer) == null ? void 0 : _a2.files) != null ? _b2 : []);\n return list.length === 0 ? null : multiple ? list : [list[0]];\n };\n const checkDataTypes = (types) => {\n const dataTypes = unref(_options.dataTypes);\n if (typeof dataTypes === \"function\")\n return dataTypes(types);\n if (!(dataTypes == null ? void 0 : dataTypes.length))\n return true;\n if (types.length === 0)\n return false;\n return types.every(\n (type) => dataTypes.some((allowedType) => type.includes(allowedType))\n );\n };\n const checkValidity = (items) => {\n const types = Array.from(items != null ? items : []).map((item) => item.type);\n const dataTypesValid = checkDataTypes(types);\n const multipleFilesValid = multiple || items.length <= 1;\n return dataTypesValid && multipleFilesValid;\n };\n const isSafari = () => /^(?:(?!chrome|android).)*safari/i.test(navigator.userAgent) && !(\"chrome\" in window);\n const handleDragEvent = (event, eventType) => {\n var _a2, _b2, _c, _d, _e, _f;\n const dataTransferItemList = (_a2 = event.dataTransfer) == null ? void 0 : _a2.items;\n isValid = (_b2 = dataTransferItemList && checkValidity(dataTransferItemList)) != null ? _b2 : false;\n if (preventDefaultForUnhandled) {\n event.preventDefault();\n }\n if (!isSafari() && !isValid) {\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = \"none\";\n }\n return;\n }\n event.preventDefault();\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = \"copy\";\n }\n const currentFiles = getFiles(event);\n switch (eventType) {\n case \"enter\":\n counter += 1;\n isOverDropZone.value = true;\n (_c = _options.onEnter) == null ? void 0 : _c.call(_options, null, event);\n break;\n case \"over\":\n (_d = _options.onOver) == null ? void 0 : _d.call(_options, null, event);\n break;\n case \"leave\":\n counter -= 1;\n if (counter === 0)\n isOverDropZone.value = false;\n (_e = _options.onLeave) == null ? void 0 : _e.call(_options, null, event);\n break;\n case \"drop\":\n counter = 0;\n isOverDropZone.value = false;\n if (isValid) {\n files.value = currentFiles;\n (_f = _options.onDrop) == null ? void 0 : _f.call(_options, currentFiles, event);\n }\n break;\n }\n };\n useEventListener(target, \"dragenter\", (event) => handleDragEvent(event, \"enter\"));\n useEventListener(target, \"dragover\", (event) => handleDragEvent(event, \"over\"));\n useEventListener(target, \"dragleave\", (event) => handleDragEvent(event, \"leave\"));\n useEventListener(target, \"drop\", (event) => handleDragEvent(event, \"drop\"));\n }\n return {\n files,\n isOverDropZone\n };\n}\n\nfunction useResizeObserver(target, callback, options = {}) {\n const { window = defaultWindow, ...observerOptions } = options;\n let observer;\n const isSupported = useSupported(() => window && \"ResizeObserver\" in window);\n const cleanup = () => {\n if (observer) {\n observer.disconnect();\n observer = void 0;\n }\n };\n const targets = computed(() => {\n const _targets = toValue(target);\n return Array.isArray(_targets) ? _targets.map((el) => unrefElement(el)) : [unrefElement(_targets)];\n });\n const stopWatch = watch(\n targets,\n (els) => {\n cleanup();\n if (isSupported.value && window) {\n observer = new ResizeObserver(callback);\n for (const _el of els) {\n if (_el)\n observer.observe(_el, observerOptions);\n }\n }\n },\n { immediate: true, flush: \"post\" }\n );\n const stop = () => {\n cleanup();\n stopWatch();\n };\n tryOnScopeDispose(stop);\n return {\n isSupported,\n stop\n };\n}\n\nfunction useElementBounding(target, options = {}) {\n const {\n reset = true,\n windowResize = true,\n windowScroll = true,\n immediate = true,\n updateTiming = \"sync\"\n } = options;\n const height = shallowRef(0);\n const bottom = shallowRef(0);\n const left = shallowRef(0);\n const right = shallowRef(0);\n const top = shallowRef(0);\n const width = shallowRef(0);\n const x = shallowRef(0);\n const y = shallowRef(0);\n function recalculate() {\n const el = unrefElement(target);\n if (!el) {\n if (reset) {\n height.value = 0;\n bottom.value = 0;\n left.value = 0;\n right.value = 0;\n top.value = 0;\n width.value = 0;\n x.value = 0;\n y.value = 0;\n }\n return;\n }\n const rect = el.getBoundingClientRect();\n height.value = rect.height;\n bottom.value = rect.bottom;\n left.value = rect.left;\n right.value = rect.right;\n top.value = rect.top;\n width.value = rect.width;\n x.value = rect.x;\n y.value = rect.y;\n }\n function update() {\n if (updateTiming === \"sync\")\n recalculate();\n else if (updateTiming === \"next-frame\")\n requestAnimationFrame(() => recalculate());\n }\n useResizeObserver(target, update);\n watch(() => unrefElement(target), (ele) => !ele && update());\n useMutationObserver(target, update, {\n attributeFilter: [\"style\", \"class\"]\n });\n if (windowScroll)\n useEventListener(\"scroll\", update, { capture: true, passive: true });\n if (windowResize)\n useEventListener(\"resize\", update, { passive: true });\n tryOnMounted(() => {\n if (immediate)\n update();\n });\n return {\n height,\n bottom,\n left,\n right,\n top,\n width,\n x,\n y,\n update\n };\n}\n\nfunction useElementByPoint(options) {\n const {\n x,\n y,\n document = defaultDocument,\n multiple,\n interval = \"requestAnimationFrame\",\n immediate = true\n } = options;\n const isSupported = useSupported(() => {\n if (toValue(multiple))\n return document && \"elementsFromPoint\" in document;\n return document && \"elementFromPoint\" in document;\n });\n const element = shallowRef(null);\n const cb = () => {\n var _a, _b;\n element.value = toValue(multiple) ? (_a = document == null ? void 0 : document.elementsFromPoint(toValue(x), toValue(y))) != null ? _a : [] : (_b = document == null ? void 0 : document.elementFromPoint(toValue(x), toValue(y))) != null ? _b : null;\n };\n const controls = interval === \"requestAnimationFrame\" ? useRafFn(cb, { immediate }) : useIntervalFn(cb, interval, { immediate });\n return {\n isSupported,\n element,\n ...controls\n };\n}\n\nfunction useElementHover(el, options = {}) {\n const {\n delayEnter = 0,\n delayLeave = 0,\n triggerOnRemoval = false,\n window = defaultWindow\n } = options;\n const isHovered = shallowRef(false);\n let timer;\n const toggle = (entering) => {\n const delay = entering ? delayEnter : delayLeave;\n if (timer) {\n clearTimeout(timer);\n timer = void 0;\n }\n if (delay)\n timer = setTimeout(() => isHovered.value = entering, delay);\n else\n isHovered.value = entering;\n };\n if (!window)\n return isHovered;\n useEventListener(el, \"mouseenter\", () => toggle(true), { passive: true });\n useEventListener(el, \"mouseleave\", () => toggle(false), { passive: true });\n if (triggerOnRemoval) {\n onElementRemoval(\n computed(() => unrefElement(el)),\n () => toggle(false)\n );\n }\n return isHovered;\n}\n\nfunction useElementSize(target, initialSize = { width: 0, height: 0 }, options = {}) {\n const { window = defaultWindow, box = \"content-box\" } = options;\n const isSVG = computed(() => {\n var _a, _b;\n return (_b = (_a = unrefElement(target)) == null ? void 0 : _a.namespaceURI) == null ? void 0 : _b.includes(\"svg\");\n });\n const width = shallowRef(initialSize.width);\n const height = shallowRef(initialSize.height);\n const { stop: stop1 } = useResizeObserver(\n target,\n ([entry]) => {\n const boxSize = box === \"border-box\" ? entry.borderBoxSize : box === \"content-box\" ? entry.contentBoxSize : entry.devicePixelContentBoxSize;\n if (window && isSVG.value) {\n const $elem = unrefElement(target);\n if ($elem) {\n const rect = $elem.getBoundingClientRect();\n width.value = rect.width;\n height.value = rect.height;\n }\n } else {\n if (boxSize) {\n const formatBoxSize = toArray(boxSize);\n width.value = formatBoxSize.reduce((acc, { inlineSize }) => acc + inlineSize, 0);\n height.value = formatBoxSize.reduce((acc, { blockSize }) => acc + blockSize, 0);\n } else {\n width.value = entry.contentRect.width;\n height.value = entry.contentRect.height;\n }\n }\n },\n options\n );\n tryOnMounted(() => {\n const ele = unrefElement(target);\n if (ele) {\n width.value = \"offsetWidth\" in ele ? ele.offsetWidth : initialSize.width;\n height.value = \"offsetHeight\" in ele ? ele.offsetHeight : initialSize.height;\n }\n });\n const stop2 = watch(\n () => unrefElement(target),\n (ele) => {\n width.value = ele ? initialSize.width : 0;\n height.value = ele ? initialSize.height : 0;\n }\n );\n function stop() {\n stop1();\n stop2();\n }\n return {\n width,\n height,\n stop\n };\n}\n\nfunction useIntersectionObserver(target, callback, options = {}) {\n const {\n root,\n rootMargin = \"0px\",\n threshold = 0,\n window = defaultWindow,\n immediate = true\n } = options;\n const isSupported = useSupported(() => window && \"IntersectionObserver\" in window);\n const targets = computed(() => {\n const _target = toValue(target);\n return toArray(_target).map(unrefElement).filter(notNullish);\n });\n let cleanup = noop;\n const isActive = shallowRef(immediate);\n const stopWatch = isSupported.value ? watch(\n () => [targets.value, unrefElement(root), isActive.value],\n ([targets2, root2]) => {\n cleanup();\n if (!isActive.value)\n return;\n if (!targets2.length)\n return;\n const observer = new IntersectionObserver(\n callback,\n {\n root: unrefElement(root2),\n rootMargin,\n threshold\n }\n );\n targets2.forEach((el) => el && observer.observe(el));\n cleanup = () => {\n observer.disconnect();\n cleanup = noop;\n };\n },\n { immediate, flush: \"post\" }\n ) : noop;\n const stop = () => {\n cleanup();\n stopWatch();\n isActive.value = false;\n };\n tryOnScopeDispose(stop);\n return {\n isSupported,\n isActive,\n pause() {\n cleanup();\n isActive.value = false;\n },\n resume() {\n isActive.value = true;\n },\n stop\n };\n}\n\nfunction useElementVisibility(element, options = {}) {\n const {\n window = defaultWindow,\n scrollTarget,\n threshold = 0,\n rootMargin,\n once = false\n } = options;\n const elementIsVisible = shallowRef(false);\n const { stop } = useIntersectionObserver(\n element,\n (intersectionObserverEntries) => {\n let isIntersecting = elementIsVisible.value;\n let latestTime = 0;\n for (const entry of intersectionObserverEntries) {\n if (entry.time >= latestTime) {\n latestTime = entry.time;\n isIntersecting = entry.isIntersecting;\n }\n }\n elementIsVisible.value = isIntersecting;\n if (once) {\n watchOnce(elementIsVisible, () => {\n stop();\n });\n }\n },\n {\n root: scrollTarget,\n window,\n threshold,\n rootMargin: toValue(rootMargin)\n }\n );\n return elementIsVisible;\n}\n\nconst events = /* @__PURE__ */ new Map();\n\nfunction useEventBus(key) {\n const scope = getCurrentScope();\n function on(listener) {\n var _a;\n const listeners = events.get(key) || /* @__PURE__ */ new Set();\n listeners.add(listener);\n events.set(key, listeners);\n const _off = () => off(listener);\n (_a = scope == null ? void 0 : scope.cleanups) == null ? void 0 : _a.push(_off);\n return _off;\n }\n function once(listener) {\n function _listener(...args) {\n off(_listener);\n listener(...args);\n }\n return on(_listener);\n }\n function off(listener) {\n const listeners = events.get(key);\n if (!listeners)\n return;\n listeners.delete(listener);\n if (!listeners.size)\n reset();\n }\n function reset() {\n events.delete(key);\n }\n function emit(event, payload) {\n var _a;\n (_a = events.get(key)) == null ? void 0 : _a.forEach((v) => v(event, payload));\n }\n return { on, once, off, emit, reset };\n}\n\nfunction resolveNestedOptions$1(options) {\n if (options === true)\n return {};\n return options;\n}\nfunction useEventSource(url, events = [], options = {}) {\n const event = shallowRef(null);\n const data = shallowRef(null);\n const status = shallowRef(\"CONNECTING\");\n const eventSource = ref(null);\n const error = shallowRef(null);\n const urlRef = toRef(url);\n const lastEventId = shallowRef(null);\n let explicitlyClosed = false;\n let retried = 0;\n const {\n withCredentials = false,\n immediate = true,\n autoConnect = true,\n autoReconnect\n } = options;\n const close = () => {\n if (isClient && eventSource.value) {\n eventSource.value.close();\n eventSource.value = null;\n status.value = \"CLOSED\";\n explicitlyClosed = true;\n }\n };\n const _init = () => {\n if (explicitlyClosed || typeof urlRef.value === \"undefined\")\n return;\n const es = new EventSource(urlRef.value, { withCredentials });\n status.value = \"CONNECTING\";\n eventSource.value = es;\n es.onopen = () => {\n status.value = \"OPEN\";\n error.value = null;\n };\n es.onerror = (e) => {\n status.value = \"CLOSED\";\n error.value = e;\n if (es.readyState === 2 && !explicitlyClosed && autoReconnect) {\n es.close();\n const {\n retries = -1,\n delay = 1e3,\n onFailed\n } = resolveNestedOptions$1(autoReconnect);\n retried += 1;\n if (typeof retries === \"number\" && (retries < 0 || retried < retries))\n setTimeout(_init, delay);\n else if (typeof retries === \"function\" && retries())\n setTimeout(_init, delay);\n else\n onFailed == null ? void 0 : onFailed();\n }\n };\n es.onmessage = (e) => {\n event.value = null;\n data.value = e.data;\n lastEventId.value = e.lastEventId;\n };\n for (const event_name of events) {\n useEventListener(es, event_name, (e) => {\n event.value = event_name;\n data.value = e.data || null;\n }, { passive: true });\n }\n };\n const open = () => {\n if (!isClient)\n return;\n close();\n explicitlyClosed = false;\n retried = 0;\n _init();\n };\n if (immediate)\n open();\n if (autoConnect)\n watch(urlRef, open);\n tryOnScopeDispose(close);\n return {\n eventSource,\n event,\n data,\n status,\n error,\n open,\n close,\n lastEventId\n };\n}\n\nfunction useEyeDropper(options = {}) {\n const { initialValue = \"\" } = options;\n const isSupported = useSupported(() => typeof window !== \"undefined\" && \"EyeDropper\" in window);\n const sRGBHex = shallowRef(initialValue);\n async function open(openOptions) {\n if (!isSupported.value)\n return;\n const eyeDropper = new window.EyeDropper();\n const result = await eyeDropper.open(openOptions);\n sRGBHex.value = result.sRGBHex;\n return result;\n }\n return { isSupported, sRGBHex, open };\n}\n\nfunction useFavicon(newIcon = null, options = {}) {\n const {\n baseUrl = \"\",\n rel = \"icon\",\n document = defaultDocument\n } = options;\n const favicon = toRef(newIcon);\n const applyIcon = (icon) => {\n const elements = document == null ? void 0 : document.head.querySelectorAll(`link[rel*=\"${rel}\"]`);\n if (!elements || elements.length === 0) {\n const link = document == null ? void 0 : document.createElement(\"link\");\n if (link) {\n link.rel = rel;\n link.href = `${baseUrl}${icon}`;\n link.type = `image/${icon.split(\".\").pop()}`;\n document == null ? void 0 : document.head.append(link);\n }\n return;\n }\n elements == null ? void 0 : elements.forEach((el) => el.href = `${baseUrl}${icon}`);\n };\n watch(\n favicon,\n (i, o) => {\n if (typeof i === \"string\" && i !== o)\n applyIcon(i);\n },\n { immediate: true }\n );\n return favicon;\n}\n\nconst payloadMapping = {\n json: \"application/json\",\n text: \"text/plain\"\n};\nfunction isFetchOptions(obj) {\n return obj && containsProp(obj, \"immediate\", \"refetch\", \"initialData\", \"timeout\", \"beforeFetch\", \"afterFetch\", \"onFetchError\", \"fetch\", \"updateDataOnError\");\n}\nconst reAbsolute = /^(?:[a-z][a-z\\d+\\-.]*:)?\\/\\//i;\nfunction isAbsoluteURL(url) {\n return reAbsolute.test(url);\n}\nfunction headersToObject(headers) {\n if (typeof Headers !== \"undefined\" && headers instanceof Headers)\n return Object.fromEntries(headers.entries());\n return headers;\n}\nfunction combineCallbacks(combination, ...callbacks) {\n if (combination === \"overwrite\") {\n return async (ctx) => {\n let callback;\n for (let i = callbacks.length - 1; i >= 0; i--) {\n if (callbacks[i] != null) {\n callback = callbacks[i];\n break;\n }\n }\n if (callback)\n return { ...ctx, ...await callback(ctx) };\n return ctx;\n };\n } else {\n return async (ctx) => {\n for (const callback of callbacks) {\n if (callback)\n ctx = { ...ctx, ...await callback(ctx) };\n }\n return ctx;\n };\n }\n}\nfunction createFetch(config = {}) {\n const _combination = config.combination || \"chain\";\n const _options = config.options || {};\n const _fetchOptions = config.fetchOptions || {};\n function useFactoryFetch(url, ...args) {\n const computedUrl = computed(() => {\n const baseUrl = toValue(config.baseUrl);\n const targetUrl = toValue(url);\n return baseUrl && !isAbsoluteURL(targetUrl) ? joinPaths(baseUrl, targetUrl) : targetUrl;\n });\n let options = _options;\n let fetchOptions = _fetchOptions;\n if (args.length > 0) {\n if (isFetchOptions(args[0])) {\n options = {\n ...options,\n ...args[0],\n beforeFetch: combineCallbacks(_combination, _options.beforeFetch, args[0].beforeFetch),\n afterFetch: combineCallbacks(_combination, _options.afterFetch, args[0].afterFetch),\n onFetchError: combineCallbacks(_combination, _options.onFetchError, args[0].onFetchError)\n };\n } else {\n fetchOptions = {\n ...fetchOptions,\n ...args[0],\n headers: {\n ...headersToObject(fetchOptions.headers) || {},\n ...headersToObject(args[0].headers) || {}\n }\n };\n }\n }\n if (args.length > 1 && isFetchOptions(args[1])) {\n options = {\n ...options,\n ...args[1],\n beforeFetch: combineCallbacks(_combination, _options.beforeFetch, args[1].beforeFetch),\n afterFetch: combineCallbacks(_combination, _options.afterFetch, args[1].afterFetch),\n onFetchError: combineCallbacks(_combination, _options.onFetchError, args[1].onFetchError)\n };\n }\n return useFetch(computedUrl, fetchOptions, options);\n }\n return useFactoryFetch;\n}\nfunction useFetch(url, ...args) {\n var _a;\n const supportsAbort = typeof AbortController === \"function\";\n let fetchOptions = {};\n let options = {\n immediate: true,\n refetch: false,\n timeout: 0,\n updateDataOnError: false\n };\n const config = {\n method: \"GET\",\n type: \"text\",\n payload: void 0\n };\n if (args.length > 0) {\n if (isFetchOptions(args[0]))\n options = { ...options, ...args[0] };\n else\n fetchOptions = args[0];\n }\n if (args.length > 1) {\n if (isFetchOptions(args[1]))\n options = { ...options, ...args[1] };\n }\n const {\n fetch = (_a = defaultWindow) == null ? void 0 : _a.fetch,\n initialData,\n timeout\n } = options;\n const responseEvent = createEventHook();\n const errorEvent = createEventHook();\n const finallyEvent = createEventHook();\n const isFinished = shallowRef(false);\n const isFetching = shallowRef(false);\n const aborted = shallowRef(false);\n const statusCode = shallowRef(null);\n const response = shallowRef(null);\n const error = shallowRef(null);\n const data = shallowRef(initialData || null);\n const canAbort = computed(() => supportsAbort && isFetching.value);\n let controller;\n let timer;\n const abort = () => {\n if (supportsAbort) {\n controller == null ? void 0 : controller.abort();\n controller = new AbortController();\n controller.signal.onabort = () => aborted.value = true;\n fetchOptions = {\n ...fetchOptions,\n signal: controller.signal\n };\n }\n };\n const loading = (isLoading) => {\n isFetching.value = isLoading;\n isFinished.value = !isLoading;\n };\n if (timeout)\n timer = useTimeoutFn(abort, timeout, { immediate: false });\n let executeCounter = 0;\n const execute = async (throwOnFailed = false) => {\n var _a2, _b;\n abort();\n loading(true);\n error.value = null;\n statusCode.value = null;\n aborted.value = false;\n executeCounter += 1;\n const currentExecuteCounter = executeCounter;\n const defaultFetchOptions = {\n method: config.method,\n headers: {}\n };\n const payload = toValue(config.payload);\n if (payload) {\n const headers = headersToObject(defaultFetchOptions.headers);\n const proto = Object.getPrototypeOf(payload);\n if (!config.payloadType && payload && (proto === Object.prototype || Array.isArray(proto)) && !(payload instanceof FormData))\n config.payloadType = \"json\";\n if (config.payloadType)\n headers[\"Content-Type\"] = (_a2 = payloadMapping[config.payloadType]) != null ? _a2 : config.payloadType;\n defaultFetchOptions.body = config.payloadType === \"json\" ? JSON.stringify(payload) : payload;\n }\n let isCanceled = false;\n const context = {\n url: toValue(url),\n options: {\n ...defaultFetchOptions,\n ...fetchOptions\n },\n cancel: () => {\n isCanceled = true;\n }\n };\n if (options.beforeFetch)\n Object.assign(context, await options.beforeFetch(context));\n if (isCanceled || !fetch) {\n loading(false);\n return Promise.resolve(null);\n }\n let responseData = null;\n if (timer)\n timer.start();\n return fetch(\n context.url,\n {\n ...defaultFetchOptions,\n ...context.options,\n headers: {\n ...headersToObject(defaultFetchOptions.headers),\n ...headersToObject((_b = context.options) == null ? void 0 : _b.headers)\n }\n }\n ).then(async (fetchResponse) => {\n response.value = fetchResponse;\n statusCode.value = fetchResponse.status;\n responseData = await fetchResponse.clone()[config.type]();\n if (!fetchResponse.ok) {\n data.value = initialData || null;\n throw new Error(fetchResponse.statusText);\n }\n if (options.afterFetch) {\n ({ data: responseData } = await options.afterFetch({\n data: responseData,\n response: fetchResponse,\n context,\n execute\n }));\n }\n data.value = responseData;\n responseEvent.trigger(fetchResponse);\n return fetchResponse;\n }).catch(async (fetchError) => {\n let errorData = fetchError.message || fetchError.name;\n if (options.onFetchError) {\n ({ error: errorData, data: responseData } = await options.onFetchError({\n data: responseData,\n error: fetchError,\n response: response.value,\n context,\n execute\n }));\n }\n error.value = errorData;\n if (options.updateDataOnError)\n data.value = responseData;\n errorEvent.trigger(fetchError);\n if (throwOnFailed)\n throw fetchError;\n return null;\n }).finally(() => {\n if (currentExecuteCounter === executeCounter)\n loading(false);\n if (timer)\n timer.stop();\n finallyEvent.trigger(null);\n });\n };\n const refetch = toRef(options.refetch);\n watch(\n [\n refetch,\n toRef(url)\n ],\n ([refetch2]) => refetch2 && execute(),\n { deep: true }\n );\n const shell = {\n isFinished: readonly(isFinished),\n isFetching: readonly(isFetching),\n statusCode,\n response,\n error,\n data,\n canAbort,\n aborted,\n abort,\n execute,\n onFetchResponse: responseEvent.on,\n onFetchError: errorEvent.on,\n onFetchFinally: finallyEvent.on,\n // method\n get: setMethod(\"GET\"),\n put: setMethod(\"PUT\"),\n post: setMethod(\"POST\"),\n delete: setMethod(\"DELETE\"),\n patch: setMethod(\"PATCH\"),\n head: setMethod(\"HEAD\"),\n options: setMethod(\"OPTIONS\"),\n // type\n json: setType(\"json\"),\n text: setType(\"text\"),\n blob: setType(\"blob\"),\n arrayBuffer: setType(\"arrayBuffer\"),\n formData: setType(\"formData\")\n };\n function setMethod(method) {\n return (payload, payloadType) => {\n if (!isFetching.value) {\n config.method = method;\n config.payload = payload;\n config.payloadType = payloadType;\n if (isRef(config.payload)) {\n watch(\n [\n refetch,\n toRef(config.payload)\n ],\n ([refetch2]) => refetch2 && execute(),\n { deep: true }\n );\n }\n return {\n ...shell,\n then(onFulfilled, onRejected) {\n return waitUntilFinished().then(onFulfilled, onRejected);\n }\n };\n }\n return void 0;\n };\n }\n function waitUntilFinished() {\n return new Promise((resolve, reject) => {\n until(isFinished).toBe(true).then(() => resolve(shell)).catch(reject);\n });\n }\n function setType(type) {\n return () => {\n if (!isFetching.value) {\n config.type = type;\n return {\n ...shell,\n then(onFulfilled, onRejected) {\n return waitUntilFinished().then(onFulfilled, onRejected);\n }\n };\n }\n return void 0;\n };\n }\n if (options.immediate)\n Promise.resolve().then(() => execute());\n return {\n ...shell,\n then(onFulfilled, onRejected) {\n return waitUntilFinished().then(onFulfilled, onRejected);\n }\n };\n}\nfunction joinPaths(start, end) {\n if (!start.endsWith(\"/\") && !end.startsWith(\"/\")) {\n return `${start}/${end}`;\n }\n if (start.endsWith(\"/\") && end.startsWith(\"/\")) {\n return `${start.slice(0, -1)}${end}`;\n }\n return `${start}${end}`;\n}\n\nconst DEFAULT_OPTIONS = {\n multiple: true,\n accept: \"*\",\n reset: false,\n directory: false\n};\nfunction prepareInitialFiles(files) {\n if (!files)\n return null;\n if (files instanceof FileList)\n return files;\n const dt = new DataTransfer();\n for (const file of files) {\n dt.items.add(file);\n }\n return dt.files;\n}\nfunction useFileDialog(options = {}) {\n const {\n document = defaultDocument\n } = options;\n const files = ref(prepareInitialFiles(options.initialFiles));\n const { on: onChange, trigger: changeTrigger } = createEventHook();\n const { on: onCancel, trigger: cancelTrigger } = createEventHook();\n let input;\n if (document) {\n input = document.createElement(\"input\");\n input.type = \"file\";\n input.onchange = (event) => {\n const result = event.target;\n files.value = result.files;\n changeTrigger(files.value);\n };\n input.oncancel = () => {\n cancelTrigger();\n };\n }\n const reset = () => {\n files.value = null;\n if (input && input.value) {\n input.value = \"\";\n changeTrigger(null);\n }\n };\n const open = (localOptions) => {\n if (!input)\n return;\n const _options = {\n ...DEFAULT_OPTIONS,\n ...options,\n ...localOptions\n };\n input.multiple = _options.multiple;\n input.accept = _options.accept;\n input.webkitdirectory = _options.directory;\n if (hasOwn(_options, \"capture\"))\n input.capture = _options.capture;\n if (_options.reset)\n reset();\n input.click();\n };\n return {\n files: readonly(files),\n open,\n reset,\n onCancel,\n onChange\n };\n}\n\nfunction useFileSystemAccess(options = {}) {\n const {\n window: _window = defaultWindow,\n dataType = \"Text\"\n } = options;\n const window = _window;\n const isSupported = useSupported(() => window && \"showSaveFilePicker\" in window && \"showOpenFilePicker\" in window);\n const fileHandle = shallowRef();\n const data = shallowRef();\n const file = shallowRef();\n const fileName = computed(() => {\n var _a, _b;\n return (_b = (_a = file.value) == null ? void 0 : _a.name) != null ? _b : \"\";\n });\n const fileMIME = computed(() => {\n var _a, _b;\n return (_b = (_a = file.value) == null ? void 0 : _a.type) != null ? _b : \"\";\n });\n const fileSize = computed(() => {\n var _a, _b;\n return (_b = (_a = file.value) == null ? void 0 : _a.size) != null ? _b : 0;\n });\n const fileLastModified = computed(() => {\n var _a, _b;\n return (_b = (_a = file.value) == null ? void 0 : _a.lastModified) != null ? _b : 0;\n });\n async function open(_options = {}) {\n if (!isSupported.value)\n return;\n const [handle] = await window.showOpenFilePicker({ ...toValue(options), ..._options });\n fileHandle.value = handle;\n await updateData();\n }\n async function create(_options = {}) {\n if (!isSupported.value)\n return;\n fileHandle.value = await window.showSaveFilePicker({ ...options, ..._options });\n data.value = void 0;\n await updateData();\n }\n async function save(_options = {}) {\n if (!isSupported.value)\n return;\n if (!fileHandle.value)\n return saveAs(_options);\n if (data.value) {\n const writableStream = await fileHandle.value.createWritable();\n await writableStream.write(data.value);\n await writableStream.close();\n }\n await updateFile();\n }\n async function saveAs(_options = {}) {\n if (!isSupported.value)\n return;\n fileHandle.value = await window.showSaveFilePicker({ ...options, ..._options });\n if (data.value) {\n const writableStream = await fileHandle.value.createWritable();\n await writableStream.write(data.value);\n await writableStream.close();\n }\n await updateFile();\n }\n async function updateFile() {\n var _a;\n file.value = await ((_a = fileHandle.value) == null ? void 0 : _a.getFile());\n }\n async function updateData() {\n var _a, _b;\n await updateFile();\n const type = toValue(dataType);\n if (type === \"Text\")\n data.value = await ((_a = file.value) == null ? void 0 : _a.text());\n else if (type === \"ArrayBuffer\")\n data.value = await ((_b = file.value) == null ? void 0 : _b.arrayBuffer());\n else if (type === \"Blob\")\n data.value = file.value;\n }\n watch(() => toValue(dataType), updateData);\n return {\n isSupported,\n data,\n file,\n fileName,\n fileMIME,\n fileSize,\n fileLastModified,\n open,\n create,\n save,\n saveAs,\n updateData\n };\n}\n\nfunction useFocus(target, options = {}) {\n const { initialValue = false, focusVisible = false, preventScroll = false } = options;\n const innerFocused = shallowRef(false);\n const targetElement = computed(() => unrefElement(target));\n const listenerOptions = { passive: true };\n useEventListener(targetElement, \"focus\", (event) => {\n var _a, _b;\n if (!focusVisible || ((_b = (_a = event.target).matches) == null ? void 0 : _b.call(_a, \":focus-visible\")))\n innerFocused.value = true;\n }, listenerOptions);\n useEventListener(targetElement, \"blur\", () => innerFocused.value = false, listenerOptions);\n const focused = computed({\n get: () => innerFocused.value,\n set(value) {\n var _a, _b;\n if (!value && innerFocused.value)\n (_a = targetElement.value) == null ? void 0 : _a.blur();\n else if (value && !innerFocused.value)\n (_b = targetElement.value) == null ? void 0 : _b.focus({ preventScroll });\n }\n });\n watch(\n targetElement,\n () => {\n focused.value = initialValue;\n },\n { immediate: true, flush: \"post\" }\n );\n return { focused };\n}\n\nconst EVENT_FOCUS_IN = \"focusin\";\nconst EVENT_FOCUS_OUT = \"focusout\";\nconst PSEUDO_CLASS_FOCUS_WITHIN = \":focus-within\";\nfunction useFocusWithin(target, options = {}) {\n const { window = defaultWindow } = options;\n const targetElement = computed(() => unrefElement(target));\n const _focused = shallowRef(false);\n const focused = computed(() => _focused.value);\n const activeElement = useActiveElement(options);\n if (!window || !activeElement.value) {\n return { focused };\n }\n const listenerOptions = { passive: true };\n useEventListener(targetElement, EVENT_FOCUS_IN, () => _focused.value = true, listenerOptions);\n useEventListener(targetElement, EVENT_FOCUS_OUT, () => {\n var _a, _b, _c;\n return _focused.value = (_c = (_b = (_a = targetElement.value) == null ? void 0 : _a.matches) == null ? void 0 : _b.call(_a, PSEUDO_CLASS_FOCUS_WITHIN)) != null ? _c : false;\n }, listenerOptions);\n return { focused };\n}\n\nfunction useFps(options) {\n var _a;\n const fps = shallowRef(0);\n if (typeof performance === \"undefined\")\n return fps;\n const every = (_a = options == null ? void 0 : options.every) != null ? _a : 10;\n let last = performance.now();\n let ticks = 0;\n useRafFn(() => {\n ticks += 1;\n if (ticks >= every) {\n const now = performance.now();\n const diff = now - last;\n fps.value = Math.round(1e3 / (diff / ticks));\n last = now;\n ticks = 0;\n }\n });\n return fps;\n}\n\nconst eventHandlers = [\n \"fullscreenchange\",\n \"webkitfullscreenchange\",\n \"webkitendfullscreen\",\n \"mozfullscreenchange\",\n \"MSFullscreenChange\"\n];\nfunction useFullscreen(target, options = {}) {\n const {\n document = defaultDocument,\n autoExit = false\n } = options;\n const targetRef = computed(() => {\n var _a;\n return (_a = unrefElement(target)) != null ? _a : document == null ? void 0 : document.documentElement;\n });\n const isFullscreen = shallowRef(false);\n const requestMethod = computed(() => {\n return [\n \"requestFullscreen\",\n \"webkitRequestFullscreen\",\n \"webkitEnterFullscreen\",\n \"webkitEnterFullScreen\",\n \"webkitRequestFullScreen\",\n \"mozRequestFullScreen\",\n \"msRequestFullscreen\"\n ].find((m) => document && m in document || targetRef.value && m in targetRef.value);\n });\n const exitMethod = computed(() => {\n return [\n \"exitFullscreen\",\n \"webkitExitFullscreen\",\n \"webkitExitFullScreen\",\n \"webkitCancelFullScreen\",\n \"mozCancelFullScreen\",\n \"msExitFullscreen\"\n ].find((m) => document && m in document || targetRef.value && m in targetRef.value);\n });\n const fullscreenEnabled = computed(() => {\n return [\n \"fullScreen\",\n \"webkitIsFullScreen\",\n \"webkitDisplayingFullscreen\",\n \"mozFullScreen\",\n \"msFullscreenElement\"\n ].find((m) => document && m in document || targetRef.value && m in targetRef.value);\n });\n const fullscreenElementMethod = [\n \"fullscreenElement\",\n \"webkitFullscreenElement\",\n \"mozFullScreenElement\",\n \"msFullscreenElement\"\n ].find((m) => document && m in document);\n const isSupported = useSupported(() => targetRef.value && document && requestMethod.value !== void 0 && exitMethod.value !== void 0 && fullscreenEnabled.value !== void 0);\n const isCurrentElementFullScreen = () => {\n if (fullscreenElementMethod)\n return (document == null ? void 0 : document[fullscreenElementMethod]) === targetRef.value;\n return false;\n };\n const isElementFullScreen = () => {\n if (fullscreenEnabled.value) {\n if (document && document[fullscreenEnabled.value] != null) {\n return document[fullscreenEnabled.value];\n } else {\n const target2 = targetRef.value;\n if ((target2 == null ? void 0 : target2[fullscreenEnabled.value]) != null) {\n return Boolean(target2[fullscreenEnabled.value]);\n }\n }\n }\n return false;\n };\n async function exit() {\n if (!isSupported.value || !isFullscreen.value)\n return;\n if (exitMethod.value) {\n if ((document == null ? void 0 : document[exitMethod.value]) != null) {\n await document[exitMethod.value]();\n } else {\n const target2 = targetRef.value;\n if ((target2 == null ? void 0 : target2[exitMethod.value]) != null)\n await target2[exitMethod.value]();\n }\n }\n isFullscreen.value = false;\n }\n async function enter() {\n if (!isSupported.value || isFullscreen.value)\n return;\n if (isElementFullScreen())\n await exit();\n const target2 = targetRef.value;\n if (requestMethod.value && (target2 == null ? void 0 : target2[requestMethod.value]) != null) {\n await target2[requestMethod.value]();\n isFullscreen.value = true;\n }\n }\n async function toggle() {\n await (isFullscreen.value ? exit() : enter());\n }\n const handlerCallback = () => {\n const isElementFullScreenValue = isElementFullScreen();\n if (!isElementFullScreenValue || isElementFullScreenValue && isCurrentElementFullScreen())\n isFullscreen.value = isElementFullScreenValue;\n };\n const listenerOptions = { capture: false, passive: true };\n useEventListener(document, eventHandlers, handlerCallback, listenerOptions);\n useEventListener(() => unrefElement(targetRef), eventHandlers, handlerCallback, listenerOptions);\n if (autoExit)\n tryOnScopeDispose(exit);\n return {\n isSupported,\n isFullscreen,\n enter,\n exit,\n toggle\n };\n}\n\nfunction mapGamepadToXbox360Controller(gamepad) {\n return computed(() => {\n if (gamepad.value) {\n return {\n buttons: {\n a: gamepad.value.buttons[0],\n b: gamepad.value.buttons[1],\n x: gamepad.value.buttons[2],\n y: gamepad.value.buttons[3]\n },\n bumper: {\n left: gamepad.value.buttons[4],\n right: gamepad.value.buttons[5]\n },\n triggers: {\n left: gamepad.value.buttons[6],\n right: gamepad.value.buttons[7]\n },\n stick: {\n left: {\n horizontal: gamepad.value.axes[0],\n vertical: gamepad.value.axes[1],\n button: gamepad.value.buttons[10]\n },\n right: {\n horizontal: gamepad.value.axes[2],\n vertical: gamepad.value.axes[3],\n button: gamepad.value.buttons[11]\n }\n },\n dpad: {\n up: gamepad.value.buttons[12],\n down: gamepad.value.buttons[13],\n left: gamepad.value.buttons[14],\n right: gamepad.value.buttons[15]\n },\n back: gamepad.value.buttons[8],\n start: gamepad.value.buttons[9]\n };\n }\n return null;\n });\n}\nfunction useGamepad(options = {}) {\n const {\n navigator = defaultNavigator\n } = options;\n const isSupported = useSupported(() => navigator && \"getGamepads\" in navigator);\n const gamepads = ref([]);\n const onConnectedHook = createEventHook();\n const onDisconnectedHook = createEventHook();\n const stateFromGamepad = (gamepad) => {\n const hapticActuators = [];\n const vibrationActuator = \"vibrationActuator\" in gamepad ? gamepad.vibrationActuator : null;\n if (vibrationActuator)\n hapticActuators.push(vibrationActuator);\n if (gamepad.hapticActuators)\n hapticActuators.push(...gamepad.hapticActuators);\n return {\n id: gamepad.id,\n index: gamepad.index,\n connected: gamepad.connected,\n mapping: gamepad.mapping,\n timestamp: gamepad.timestamp,\n vibrationActuator: gamepad.vibrationActuator,\n hapticActuators,\n axes: gamepad.axes.map((axes) => axes),\n buttons: gamepad.buttons.map((button) => ({ pressed: button.pressed, touched: button.touched, value: button.value }))\n };\n };\n const updateGamepadState = () => {\n const _gamepads = (navigator == null ? void 0 : navigator.getGamepads()) || [];\n for (const gamepad of _gamepads) {\n if (gamepad && gamepads.value[gamepad.index])\n gamepads.value[gamepad.index] = stateFromGamepad(gamepad);\n }\n };\n const { isActive, pause, resume } = useRafFn(updateGamepadState);\n const onGamepadConnected = (gamepad) => {\n if (!gamepads.value.some(({ index }) => index === gamepad.index)) {\n gamepads.value.push(stateFromGamepad(gamepad));\n onConnectedHook.trigger(gamepad.index);\n }\n resume();\n };\n const onGamepadDisconnected = (gamepad) => {\n gamepads.value = gamepads.value.filter((x) => x.index !== gamepad.index);\n onDisconnectedHook.trigger(gamepad.index);\n };\n const listenerOptions = { passive: true };\n useEventListener(\"gamepadconnected\", (e) => onGamepadConnected(e.gamepad), listenerOptions);\n useEventListener(\"gamepaddisconnected\", (e) => onGamepadDisconnected(e.gamepad), listenerOptions);\n tryOnMounted(() => {\n const _gamepads = (navigator == null ? void 0 : navigator.getGamepads()) || [];\n for (const gamepad of _gamepads) {\n if (gamepad && gamepads.value[gamepad.index])\n onGamepadConnected(gamepad);\n }\n });\n pause();\n return {\n isSupported,\n onConnected: onConnectedHook.on,\n onDisconnected: onDisconnectedHook.on,\n gamepads,\n pause,\n resume,\n isActive\n };\n}\n\nfunction useGeolocation(options = {}) {\n const {\n enableHighAccuracy = true,\n maximumAge = 3e4,\n timeout = 27e3,\n navigator = defaultNavigator,\n immediate = true\n } = options;\n const isSupported = useSupported(() => navigator && \"geolocation\" in navigator);\n const locatedAt = shallowRef(null);\n const error = shallowRef(null);\n const coords = ref({\n accuracy: 0,\n latitude: Number.POSITIVE_INFINITY,\n longitude: Number.POSITIVE_INFINITY,\n altitude: null,\n altitudeAccuracy: null,\n heading: null,\n speed: null\n });\n function updatePosition(position) {\n locatedAt.value = position.timestamp;\n coords.value = position.coords;\n error.value = null;\n }\n let watcher;\n function resume() {\n if (isSupported.value) {\n watcher = navigator.geolocation.watchPosition(\n updatePosition,\n (err) => error.value = err,\n {\n enableHighAccuracy,\n maximumAge,\n timeout\n }\n );\n }\n }\n if (immediate)\n resume();\n function pause() {\n if (watcher && navigator)\n navigator.geolocation.clearWatch(watcher);\n }\n tryOnScopeDispose(() => {\n pause();\n });\n return {\n isSupported,\n coords,\n locatedAt,\n error,\n resume,\n pause\n };\n}\n\nconst defaultEvents$1 = [\"mousemove\", \"mousedown\", \"resize\", \"keydown\", \"touchstart\", \"wheel\"];\nconst oneMinute = 6e4;\nfunction useIdle(timeout = oneMinute, options = {}) {\n const {\n initialState = false,\n listenForVisibilityChange = true,\n events = defaultEvents$1,\n window = defaultWindow,\n eventFilter = throttleFilter(50)\n } = options;\n const idle = shallowRef(initialState);\n const lastActive = shallowRef(timestamp());\n let timer;\n const reset = () => {\n idle.value = false;\n clearTimeout(timer);\n timer = setTimeout(() => idle.value = true, timeout);\n };\n const onEvent = createFilterWrapper(\n eventFilter,\n () => {\n lastActive.value = timestamp();\n reset();\n }\n );\n if (window) {\n const document = window.document;\n const listenerOptions = { passive: true };\n for (const event of events)\n useEventListener(window, event, onEvent, listenerOptions);\n if (listenForVisibilityChange) {\n useEventListener(document, \"visibilitychange\", () => {\n if (!document.hidden)\n onEvent();\n }, listenerOptions);\n }\n reset();\n }\n return {\n idle,\n lastActive,\n reset\n };\n}\n\nasync function loadImage(options) {\n return new Promise((resolve, reject) => {\n const img = new Image();\n const { src, srcset, sizes, class: clazz, loading, crossorigin, referrerPolicy, width, height, decoding, fetchPriority, ismap, usemap } = options;\n img.src = src;\n if (srcset != null)\n img.srcset = srcset;\n if (sizes != null)\n img.sizes = sizes;\n if (clazz != null)\n img.className = clazz;\n if (loading != null)\n img.loading = loading;\n if (crossorigin != null)\n img.crossOrigin = crossorigin;\n if (referrerPolicy != null)\n img.referrerPolicy = referrerPolicy;\n if (width != null)\n img.width = width;\n if (height != null)\n img.height = height;\n if (decoding != null)\n img.decoding = decoding;\n if (fetchPriority != null)\n img.fetchPriority = fetchPriority;\n if (ismap != null)\n img.isMap = ismap;\n if (usemap != null)\n img.useMap = usemap;\n img.onload = () => resolve(img);\n img.onerror = reject;\n });\n}\nfunction useImage(options, asyncStateOptions = {}) {\n const state = useAsyncState(\n () => loadImage(toValue(options)),\n void 0,\n {\n resetOnExecute: true,\n ...asyncStateOptions\n }\n );\n watch(\n () => toValue(options),\n () => state.execute(asyncStateOptions.delay),\n { deep: true }\n );\n return state;\n}\n\nfunction resolveElement(el) {\n if (typeof Window !== \"undefined\" && el instanceof Window)\n return el.document.documentElement;\n if (typeof Document !== \"undefined\" && el instanceof Document)\n return el.documentElement;\n return el;\n}\n\nconst ARRIVED_STATE_THRESHOLD_PIXELS = 1;\nfunction useScroll(element, options = {}) {\n const {\n throttle = 0,\n idle = 200,\n onStop = noop,\n onScroll = noop,\n offset = {\n left: 0,\n right: 0,\n top: 0,\n bottom: 0\n },\n eventListenerOptions = {\n capture: false,\n passive: true\n },\n behavior = \"auto\",\n window = defaultWindow,\n onError = (e) => {\n console.error(e);\n }\n } = options;\n const internalX = shallowRef(0);\n const internalY = shallowRef(0);\n const x = computed({\n get() {\n return internalX.value;\n },\n set(x2) {\n scrollTo(x2, void 0);\n }\n });\n const y = computed({\n get() {\n return internalY.value;\n },\n set(y2) {\n scrollTo(void 0, y2);\n }\n });\n function scrollTo(_x, _y) {\n var _a, _b, _c, _d;\n if (!window)\n return;\n const _element = toValue(element);\n if (!_element)\n return;\n (_c = _element instanceof Document ? window.document.body : _element) == null ? void 0 : _c.scrollTo({\n top: (_a = toValue(_y)) != null ? _a : y.value,\n left: (_b = toValue(_x)) != null ? _b : x.value,\n behavior: toValue(behavior)\n });\n const scrollContainer = ((_d = _element == null ? void 0 : _element.document) == null ? void 0 : _d.documentElement) || (_element == null ? void 0 : _element.documentElement) || _element;\n if (x != null)\n internalX.value = scrollContainer.scrollLeft;\n if (y != null)\n internalY.value = scrollContainer.scrollTop;\n }\n const isScrolling = shallowRef(false);\n const arrivedState = reactive({\n left: true,\n right: false,\n top: true,\n bottom: false\n });\n const directions = reactive({\n left: false,\n right: false,\n top: false,\n bottom: false\n });\n const onScrollEnd = (e) => {\n if (!isScrolling.value)\n return;\n isScrolling.value = false;\n directions.left = false;\n directions.right = false;\n directions.top = false;\n directions.bottom = false;\n onStop(e);\n };\n const onScrollEndDebounced = useDebounceFn(onScrollEnd, throttle + idle);\n const setArrivedState = (target) => {\n var _a;\n if (!window)\n return;\n const el = ((_a = target == null ? void 0 : target.document) == null ? void 0 : _a.documentElement) || (target == null ? void 0 : target.documentElement) || unrefElement(target);\n const { display, flexDirection, direction } = getComputedStyle(el);\n const directionMultipler = direction === \"rtl\" ? -1 : 1;\n const scrollLeft = el.scrollLeft;\n directions.left = scrollLeft < internalX.value;\n directions.right = scrollLeft > internalX.value;\n const left = Math.abs(scrollLeft * directionMultipler) <= (offset.left || 0);\n const right = Math.abs(scrollLeft * directionMultipler) + el.clientWidth >= el.scrollWidth - (offset.right || 0) - ARRIVED_STATE_THRESHOLD_PIXELS;\n if (display === \"flex\" && flexDirection === \"row-reverse\") {\n arrivedState.left = right;\n arrivedState.right = left;\n } else {\n arrivedState.left = left;\n arrivedState.right = right;\n }\n internalX.value = scrollLeft;\n let scrollTop = el.scrollTop;\n if (target === window.document && !scrollTop)\n scrollTop = window.document.body.scrollTop;\n directions.top = scrollTop < internalY.value;\n directions.bottom = scrollTop > internalY.value;\n const top = Math.abs(scrollTop) <= (offset.top || 0);\n const bottom = Math.abs(scrollTop) + el.clientHeight >= el.scrollHeight - (offset.bottom || 0) - ARRIVED_STATE_THRESHOLD_PIXELS;\n if (display === \"flex\" && flexDirection === \"column-reverse\") {\n arrivedState.top = bottom;\n arrivedState.bottom = top;\n } else {\n arrivedState.top = top;\n arrivedState.bottom = bottom;\n }\n internalY.value = scrollTop;\n };\n const onScrollHandler = (e) => {\n var _a;\n if (!window)\n return;\n const eventTarget = (_a = e.target.documentElement) != null ? _a : e.target;\n setArrivedState(eventTarget);\n isScrolling.value = true;\n onScrollEndDebounced(e);\n onScroll(e);\n };\n useEventListener(\n element,\n \"scroll\",\n throttle ? useThrottleFn(onScrollHandler, throttle, true, false) : onScrollHandler,\n eventListenerOptions\n );\n tryOnMounted(() => {\n try {\n const _element = toValue(element);\n if (!_element)\n return;\n setArrivedState(_element);\n } catch (e) {\n onError(e);\n }\n });\n useEventListener(\n element,\n \"scrollend\",\n onScrollEnd,\n eventListenerOptions\n );\n return {\n x,\n y,\n isScrolling,\n arrivedState,\n directions,\n measure() {\n const _element = toValue(element);\n if (window && _element)\n setArrivedState(_element);\n }\n };\n}\n\nfunction useInfiniteScroll(element, onLoadMore, options = {}) {\n var _a;\n const {\n direction = \"bottom\",\n interval = 100,\n canLoadMore = () => true\n } = options;\n const state = reactive(useScroll(\n element,\n {\n ...options,\n offset: {\n [direction]: (_a = options.distance) != null ? _a : 0,\n ...options.offset\n }\n }\n ));\n const promise = ref();\n const isLoading = computed(() => !!promise.value);\n const observedElement = computed(() => {\n return resolveElement(toValue(element));\n });\n const isElementVisible = useElementVisibility(observedElement);\n function checkAndLoad() {\n state.measure();\n if (!observedElement.value || !isElementVisible.value || !canLoadMore(observedElement.value))\n return;\n const { scrollHeight, clientHeight, scrollWidth, clientWidth } = observedElement.value;\n const isNarrower = direction === \"bottom\" || direction === \"top\" ? scrollHeight <= clientHeight : scrollWidth <= clientWidth;\n if (state.arrivedState[direction] || isNarrower) {\n if (!promise.value) {\n promise.value = Promise.all([\n onLoadMore(state),\n new Promise((resolve) => setTimeout(resolve, interval))\n ]).finally(() => {\n promise.value = null;\n nextTick(() => checkAndLoad());\n });\n }\n }\n }\n const stop = watch(\n () => [state.arrivedState[direction], isElementVisible.value],\n checkAndLoad,\n { immediate: true }\n );\n tryOnUnmounted(stop);\n return {\n isLoading,\n reset() {\n nextTick(() => checkAndLoad());\n }\n };\n}\n\nconst defaultEvents = [\"mousedown\", \"mouseup\", \"keydown\", \"keyup\"];\nfunction useKeyModifier(modifier, options = {}) {\n const {\n events = defaultEvents,\n document = defaultDocument,\n initial = null\n } = options;\n const state = shallowRef(initial);\n if (document) {\n events.forEach((listenerEvent) => {\n useEventListener(document, listenerEvent, (evt) => {\n if (typeof evt.getModifierState === \"function\")\n state.value = evt.getModifierState(modifier);\n }, { passive: true });\n });\n }\n return state;\n}\n\nfunction useLocalStorage(key, initialValue, options = {}) {\n const { window = defaultWindow } = options;\n return useStorage(key, initialValue, window == null ? void 0 : window.localStorage, options);\n}\n\nconst DefaultMagicKeysAliasMap = {\n ctrl: \"control\",\n command: \"meta\",\n cmd: \"meta\",\n option: \"alt\",\n up: \"arrowup\",\n down: \"arrowdown\",\n left: \"arrowleft\",\n right: \"arrowright\"\n};\n\nfunction useMagicKeys(options = {}) {\n const {\n reactive: useReactive = false,\n target = defaultWindow,\n aliasMap = DefaultMagicKeysAliasMap,\n passive = true,\n onEventFired = noop\n } = options;\n const current = reactive(/* @__PURE__ */ new Set());\n const obj = {\n toJSON() {\n return {};\n },\n current\n };\n const refs = useReactive ? reactive(obj) : obj;\n const metaDeps = /* @__PURE__ */ new Set();\n const usedKeys = /* @__PURE__ */ new Set();\n function setRefs(key, value) {\n if (key in refs) {\n if (useReactive)\n refs[key] = value;\n else\n refs[key].value = value;\n }\n }\n function reset() {\n current.clear();\n for (const key of usedKeys)\n setRefs(key, false);\n }\n function updateRefs(e, value) {\n var _a, _b;\n const key = (_a = e.key) == null ? void 0 : _a.toLowerCase();\n const code = (_b = e.code) == null ? void 0 : _b.toLowerCase();\n const values = [code, key].filter(Boolean);\n if (key) {\n if (value)\n current.add(key);\n else\n current.delete(key);\n }\n for (const key2 of values) {\n usedKeys.add(key2);\n setRefs(key2, value);\n }\n if (key === \"meta\" && !value) {\n metaDeps.forEach((key2) => {\n current.delete(key2);\n setRefs(key2, false);\n });\n metaDeps.clear();\n } else if (typeof e.getModifierState === \"function\" && e.getModifierState(\"Meta\") && value) {\n [...current, ...values].forEach((key2) => metaDeps.add(key2));\n }\n }\n useEventListener(target, \"keydown\", (e) => {\n updateRefs(e, true);\n return onEventFired(e);\n }, { passive });\n useEventListener(target, \"keyup\", (e) => {\n updateRefs(e, false);\n return onEventFired(e);\n }, { passive });\n useEventListener(\"blur\", reset, { passive });\n useEventListener(\"focus\", reset, { passive });\n const proxy = new Proxy(\n refs,\n {\n get(target2, prop, rec) {\n if (typeof prop !== \"string\")\n return Reflect.get(target2, prop, rec);\n prop = prop.toLowerCase();\n if (prop in aliasMap)\n prop = aliasMap[prop];\n if (!(prop in refs)) {\n if (/[+_-]/.test(prop)) {\n const keys = prop.split(/[+_-]/g).map((i) => i.trim());\n refs[prop] = computed(() => keys.map((key) => toValue(proxy[key])).every(Boolean));\n } else {\n refs[prop] = shallowRef(false);\n }\n }\n const r = Reflect.get(target2, prop, rec);\n return useReactive ? toValue(r) : r;\n }\n }\n );\n return proxy;\n}\n\nfunction usingElRef(source, cb) {\n if (toValue(source))\n cb(toValue(source));\n}\nfunction timeRangeToArray(timeRanges) {\n let ranges = [];\n for (let i = 0; i < timeRanges.length; ++i)\n ranges = [...ranges, [timeRanges.start(i), timeRanges.end(i)]];\n return ranges;\n}\nfunction tracksToArray(tracks) {\n return Array.from(tracks).map(({ label, kind, language, mode, activeCues, cues, inBandMetadataTrackDispatchType }, id) => ({ id, label, kind, language, mode, activeCues, cues, inBandMetadataTrackDispatchType }));\n}\nconst defaultOptions = {\n src: \"\",\n tracks: []\n};\nfunction useMediaControls(target, options = {}) {\n target = toRef(target);\n options = {\n ...defaultOptions,\n ...options\n };\n const {\n document = defaultDocument\n } = options;\n const listenerOptions = { passive: true };\n const currentTime = shallowRef(0);\n const duration = shallowRef(0);\n const seeking = shallowRef(false);\n const volume = shallowRef(1);\n const waiting = shallowRef(false);\n const ended = shallowRef(false);\n const playing = shallowRef(false);\n const rate = shallowRef(1);\n const stalled = shallowRef(false);\n const buffered = ref([]);\n const tracks = ref([]);\n const selectedTrack = shallowRef(-1);\n const isPictureInPicture = shallowRef(false);\n const muted = shallowRef(false);\n const supportsPictureInPicture = document && \"pictureInPictureEnabled\" in document;\n const sourceErrorEvent = createEventHook();\n const playbackErrorEvent = createEventHook();\n const disableTrack = (track) => {\n usingElRef(target, (el) => {\n if (track) {\n const id = typeof track === \"number\" ? track : track.id;\n el.textTracks[id].mode = \"disabled\";\n } else {\n for (let i = 0; i < el.textTracks.length; ++i)\n el.textTracks[i].mode = \"disabled\";\n }\n selectedTrack.value = -1;\n });\n };\n const enableTrack = (track, disableTracks = true) => {\n usingElRef(target, (el) => {\n const id = typeof track === \"number\" ? track : track.id;\n if (disableTracks)\n disableTrack();\n el.textTracks[id].mode = \"showing\";\n selectedTrack.value = id;\n });\n };\n const togglePictureInPicture = () => {\n return new Promise((resolve, reject) => {\n usingElRef(target, async (el) => {\n if (supportsPictureInPicture) {\n if (!isPictureInPicture.value) {\n el.requestPictureInPicture().then(resolve).catch(reject);\n } else {\n document.exitPictureInPicture().then(resolve).catch(reject);\n }\n }\n });\n });\n };\n watchEffect(() => {\n if (!document)\n return;\n const el = toValue(target);\n if (!el)\n return;\n const src = toValue(options.src);\n let sources = [];\n if (!src)\n return;\n if (typeof src === \"string\")\n sources = [{ src }];\n else if (Array.isArray(src))\n sources = src;\n else if (isObject(src))\n sources = [src];\n el.querySelectorAll(\"source\").forEach((e) => {\n e.remove();\n });\n sources.forEach(({ src: src2, type, media }) => {\n const source = document.createElement(\"source\");\n source.setAttribute(\"src\", src2);\n source.setAttribute(\"type\", type || \"\");\n source.setAttribute(\"media\", media || \"\");\n useEventListener(source, \"error\", sourceErrorEvent.trigger, listenerOptions);\n el.appendChild(source);\n });\n el.load();\n });\n watch([target, volume], () => {\n const el = toValue(target);\n if (!el)\n return;\n el.volume = volume.value;\n });\n watch([target, muted], () => {\n const el = toValue(target);\n if (!el)\n return;\n el.muted = muted.value;\n });\n watch([target, rate], () => {\n const el = toValue(target);\n if (!el)\n return;\n el.playbackRate = rate.value;\n });\n watchEffect(() => {\n if (!document)\n return;\n const textTracks = toValue(options.tracks);\n const el = toValue(target);\n if (!textTracks || !textTracks.length || !el)\n return;\n el.querySelectorAll(\"track\").forEach((e) => e.remove());\n textTracks.forEach(({ default: isDefault, kind, label, src, srcLang }, i) => {\n const track = document.createElement(\"track\");\n track.default = isDefault || false;\n track.kind = kind;\n track.label = label;\n track.src = src;\n track.srclang = srcLang;\n if (track.default)\n selectedTrack.value = i;\n el.appendChild(track);\n });\n });\n const { ignoreUpdates: ignoreCurrentTimeUpdates } = watchIgnorable(currentTime, (time) => {\n const el = toValue(target);\n if (!el)\n return;\n el.currentTime = time;\n });\n const { ignoreUpdates: ignorePlayingUpdates } = watchIgnorable(playing, (isPlaying) => {\n const el = toValue(target);\n if (!el)\n return;\n if (isPlaying) {\n el.play().catch((e) => {\n playbackErrorEvent.trigger(e);\n throw e;\n });\n } else {\n el.pause();\n }\n });\n useEventListener(\n target,\n \"timeupdate\",\n () => ignoreCurrentTimeUpdates(() => currentTime.value = toValue(target).currentTime),\n listenerOptions\n );\n useEventListener(\n target,\n \"durationchange\",\n () => duration.value = toValue(target).duration,\n listenerOptions\n );\n useEventListener(\n target,\n \"progress\",\n () => buffered.value = timeRangeToArray(toValue(target).buffered),\n listenerOptions\n );\n useEventListener(\n target,\n \"seeking\",\n () => seeking.value = true,\n listenerOptions\n );\n useEventListener(\n target,\n \"seeked\",\n () => seeking.value = false,\n listenerOptions\n );\n useEventListener(\n target,\n [\"waiting\", \"loadstart\"],\n () => {\n waiting.value = true;\n ignorePlayingUpdates(() => playing.value = false);\n },\n listenerOptions\n );\n useEventListener(\n target,\n \"loadeddata\",\n () => waiting.value = false,\n listenerOptions\n );\n useEventListener(\n target,\n \"playing\",\n () => {\n waiting.value = false;\n ended.value = false;\n ignorePlayingUpdates(() => playing.value = true);\n },\n listenerOptions\n );\n useEventListener(\n target,\n \"ratechange\",\n () => rate.value = toValue(target).playbackRate,\n listenerOptions\n );\n useEventListener(\n target,\n \"stalled\",\n () => stalled.value = true,\n listenerOptions\n );\n useEventListener(\n target,\n \"ended\",\n () => ended.value = true,\n listenerOptions\n );\n useEventListener(\n target,\n \"pause\",\n () => ignorePlayingUpdates(() => playing.value = false),\n listenerOptions\n );\n useEventListener(\n target,\n \"play\",\n () => ignorePlayingUpdates(() => playing.value = true),\n listenerOptions\n );\n useEventListener(\n target,\n \"enterpictureinpicture\",\n () => isPictureInPicture.value = true,\n listenerOptions\n );\n useEventListener(\n target,\n \"leavepictureinpicture\",\n () => isPictureInPicture.value = false,\n listenerOptions\n );\n useEventListener(\n target,\n \"volumechange\",\n () => {\n const el = toValue(target);\n if (!el)\n return;\n volume.value = el.volume;\n muted.value = el.muted;\n },\n listenerOptions\n );\n const listeners = [];\n const stop = watch([target], () => {\n const el = toValue(target);\n if (!el)\n return;\n stop();\n listeners[0] = useEventListener(el.textTracks, \"addtrack\", () => tracks.value = tracksToArray(el.textTracks), listenerOptions);\n listeners[1] = useEventListener(el.textTracks, \"removetrack\", () => tracks.value = tracksToArray(el.textTracks), listenerOptions);\n listeners[2] = useEventListener(el.textTracks, \"change\", () => tracks.value = tracksToArray(el.textTracks), listenerOptions);\n });\n tryOnScopeDispose(() => listeners.forEach((listener) => listener()));\n return {\n currentTime,\n duration,\n waiting,\n seeking,\n ended,\n stalled,\n buffered,\n playing,\n rate,\n // Volume\n volume,\n muted,\n // Tracks\n tracks,\n selectedTrack,\n enableTrack,\n disableTrack,\n // Picture in Picture\n supportsPictureInPicture,\n togglePictureInPicture,\n isPictureInPicture,\n // Events\n onSourceError: sourceErrorEvent.on,\n onPlaybackError: playbackErrorEvent.on\n };\n}\n\nfunction useMemoize(resolver, options) {\n const initCache = () => {\n if (options == null ? void 0 : options.cache)\n return shallowReactive(options.cache);\n return shallowReactive(/* @__PURE__ */ new Map());\n };\n const cache = initCache();\n const generateKey = (...args) => (options == null ? void 0 : options.getKey) ? options.getKey(...args) : JSON.stringify(args);\n const _loadData = (key, ...args) => {\n cache.set(key, resolver(...args));\n return cache.get(key);\n };\n const loadData = (...args) => _loadData(generateKey(...args), ...args);\n const deleteData = (...args) => {\n cache.delete(generateKey(...args));\n };\n const clearData = () => {\n cache.clear();\n };\n const memoized = (...args) => {\n const key = generateKey(...args);\n if (cache.has(key))\n return cache.get(key);\n return _loadData(key, ...args);\n };\n memoized.load = loadData;\n memoized.delete = deleteData;\n memoized.clear = clearData;\n memoized.generateKey = generateKey;\n memoized.cache = cache;\n return memoized;\n}\n\nfunction useMemory(options = {}) {\n const memory = ref();\n const isSupported = useSupported(() => typeof performance !== \"undefined\" && \"memory\" in performance);\n if (isSupported.value) {\n const { interval = 1e3 } = options;\n useIntervalFn(() => {\n memory.value = performance.memory;\n }, interval, { immediate: options.immediate, immediateCallback: options.immediateCallback });\n }\n return { isSupported, memory };\n}\n\nconst UseMouseBuiltinExtractors = {\n page: (event) => [event.pageX, event.pageY],\n client: (event) => [event.clientX, event.clientY],\n screen: (event) => [event.screenX, event.screenY],\n movement: (event) => event instanceof MouseEvent ? [event.movementX, event.movementY] : null\n};\nfunction useMouse(options = {}) {\n const {\n type = \"page\",\n touch = true,\n resetOnTouchEnds = false,\n initialValue = { x: 0, y: 0 },\n window = defaultWindow,\n target = window,\n scroll = true,\n eventFilter\n } = options;\n let _prevMouseEvent = null;\n let _prevScrollX = 0;\n let _prevScrollY = 0;\n const x = shallowRef(initialValue.x);\n const y = shallowRef(initialValue.y);\n const sourceType = shallowRef(null);\n const extractor = typeof type === \"function\" ? type : UseMouseBuiltinExtractors[type];\n const mouseHandler = (event) => {\n const result = extractor(event);\n _prevMouseEvent = event;\n if (result) {\n [x.value, y.value] = result;\n sourceType.value = \"mouse\";\n }\n if (window) {\n _prevScrollX = window.scrollX;\n _prevScrollY = window.scrollY;\n }\n };\n const touchHandler = (event) => {\n if (event.touches.length > 0) {\n const result = extractor(event.touches[0]);\n if (result) {\n [x.value, y.value] = result;\n sourceType.value = \"touch\";\n }\n }\n };\n const scrollHandler = () => {\n if (!_prevMouseEvent || !window)\n return;\n const pos = extractor(_prevMouseEvent);\n if (_prevMouseEvent instanceof MouseEvent && pos) {\n x.value = pos[0] + window.scrollX - _prevScrollX;\n y.value = pos[1] + window.scrollY - _prevScrollY;\n }\n };\n const reset = () => {\n x.value = initialValue.x;\n y.value = initialValue.y;\n };\n const mouseHandlerWrapper = eventFilter ? (event) => eventFilter(() => mouseHandler(event), {}) : (event) => mouseHandler(event);\n const touchHandlerWrapper = eventFilter ? (event) => eventFilter(() => touchHandler(event), {}) : (event) => touchHandler(event);\n const scrollHandlerWrapper = eventFilter ? () => eventFilter(() => scrollHandler(), {}) : () => scrollHandler();\n if (target) {\n const listenerOptions = { passive: true };\n useEventListener(target, [\"mousemove\", \"dragover\"], mouseHandlerWrapper, listenerOptions);\n if (touch && type !== \"movement\") {\n useEventListener(target, [\"touchstart\", \"touchmove\"], touchHandlerWrapper, listenerOptions);\n if (resetOnTouchEnds)\n useEventListener(target, \"touchend\", reset, listenerOptions);\n }\n if (scroll && type === \"page\")\n useEventListener(window, \"scroll\", scrollHandlerWrapper, listenerOptions);\n }\n return {\n x,\n y,\n sourceType\n };\n}\n\nfunction useMouseInElement(target, options = {}) {\n const {\n handleOutside = true,\n window = defaultWindow\n } = options;\n const type = options.type || \"page\";\n const { x, y, sourceType } = useMouse(options);\n const targetRef = shallowRef(target != null ? target : window == null ? void 0 : window.document.body);\n const elementX = shallowRef(0);\n const elementY = shallowRef(0);\n const elementPositionX = shallowRef(0);\n const elementPositionY = shallowRef(0);\n const elementHeight = shallowRef(0);\n const elementWidth = shallowRef(0);\n const isOutside = shallowRef(true);\n let stop = () => {\n };\n if (window) {\n stop = watch(\n [targetRef, x, y],\n () => {\n const el = unrefElement(targetRef);\n if (!el || !(el instanceof Element))\n return;\n const {\n left,\n top,\n width,\n height\n } = el.getBoundingClientRect();\n elementPositionX.value = left + (type === \"page\" ? window.pageXOffset : 0);\n elementPositionY.value = top + (type === \"page\" ? window.pageYOffset : 0);\n elementHeight.value = height;\n elementWidth.value = width;\n const elX = x.value - elementPositionX.value;\n const elY = y.value - elementPositionY.value;\n isOutside.value = width === 0 || height === 0 || elX < 0 || elY < 0 || elX > width || elY > height;\n if (handleOutside || !isOutside.value) {\n elementX.value = elX;\n elementY.value = elY;\n }\n },\n { immediate: true }\n );\n useEventListener(\n document,\n \"mouseleave\",\n () => isOutside.value = true,\n { passive: true }\n );\n }\n return {\n x,\n y,\n sourceType,\n elementX,\n elementY,\n elementPositionX,\n elementPositionY,\n elementHeight,\n elementWidth,\n isOutside,\n stop\n };\n}\n\nfunction useMousePressed(options = {}) {\n const {\n touch = true,\n drag = true,\n capture = false,\n initialValue = false,\n window = defaultWindow\n } = options;\n const pressed = shallowRef(initialValue);\n const sourceType = shallowRef(null);\n if (!window) {\n return {\n pressed,\n sourceType\n };\n }\n const onPressed = (srcType) => (event) => {\n var _a;\n pressed.value = true;\n sourceType.value = srcType;\n (_a = options.onPressed) == null ? void 0 : _a.call(options, event);\n };\n const onReleased = (event) => {\n var _a;\n pressed.value = false;\n sourceType.value = null;\n (_a = options.onReleased) == null ? void 0 : _a.call(options, event);\n };\n const target = computed(() => unrefElement(options.target) || window);\n const listenerOptions = { passive: true, capture };\n useEventListener(target, \"mousedown\", onPressed(\"mouse\"), listenerOptions);\n useEventListener(window, \"mouseleave\", onReleased, listenerOptions);\n useEventListener(window, \"mouseup\", onReleased, listenerOptions);\n if (drag) {\n useEventListener(target, \"dragstart\", onPressed(\"mouse\"), listenerOptions);\n useEventListener(window, \"drop\", onReleased, listenerOptions);\n useEventListener(window, \"dragend\", onReleased, listenerOptions);\n }\n if (touch) {\n useEventListener(target, \"touchstart\", onPressed(\"touch\"), listenerOptions);\n useEventListener(window, \"touchend\", onReleased, listenerOptions);\n useEventListener(window, \"touchcancel\", onReleased, listenerOptions);\n }\n return {\n pressed,\n sourceType\n };\n}\n\nfunction useNavigatorLanguage(options = {}) {\n const { window = defaultWindow } = options;\n const navigator = window == null ? void 0 : window.navigator;\n const isSupported = useSupported(() => navigator && \"language\" in navigator);\n const language = shallowRef(navigator == null ? void 0 : navigator.language);\n useEventListener(window, \"languagechange\", () => {\n if (navigator)\n language.value = navigator.language;\n }, { passive: true });\n return {\n isSupported,\n language\n };\n}\n\nfunction useNetwork(options = {}) {\n const { window = defaultWindow } = options;\n const navigator = window == null ? void 0 : window.navigator;\n const isSupported = useSupported(() => navigator && \"connection\" in navigator);\n const isOnline = shallowRef(true);\n const saveData = shallowRef(false);\n const offlineAt = shallowRef(void 0);\n const onlineAt = shallowRef(void 0);\n const downlink = shallowRef(void 0);\n const downlinkMax = shallowRef(void 0);\n const rtt = shallowRef(void 0);\n const effectiveType = shallowRef(void 0);\n const type = shallowRef(\"unknown\");\n const connection = isSupported.value && navigator.connection;\n function updateNetworkInformation() {\n if (!navigator)\n return;\n isOnline.value = navigator.onLine;\n offlineAt.value = isOnline.value ? void 0 : Date.now();\n onlineAt.value = isOnline.value ? Date.now() : void 0;\n if (connection) {\n downlink.value = connection.downlink;\n downlinkMax.value = connection.downlinkMax;\n effectiveType.value = connection.effectiveType;\n rtt.value = connection.rtt;\n saveData.value = connection.saveData;\n type.value = connection.type;\n }\n }\n const listenerOptions = { passive: true };\n if (window) {\n useEventListener(window, \"offline\", () => {\n isOnline.value = false;\n offlineAt.value = Date.now();\n }, listenerOptions);\n useEventListener(window, \"online\", () => {\n isOnline.value = true;\n onlineAt.value = Date.now();\n }, listenerOptions);\n }\n if (connection)\n useEventListener(connection, \"change\", updateNetworkInformation, listenerOptions);\n updateNetworkInformation();\n return {\n isSupported,\n isOnline: readonly(isOnline),\n saveData: readonly(saveData),\n offlineAt: readonly(offlineAt),\n onlineAt: readonly(onlineAt),\n downlink: readonly(downlink),\n downlinkMax: readonly(downlinkMax),\n effectiveType: readonly(effectiveType),\n rtt: readonly(rtt),\n type: readonly(type)\n };\n}\n\nfunction useNow(options = {}) {\n const {\n controls: exposeControls = false,\n interval = \"requestAnimationFrame\"\n } = options;\n const now = ref(/* @__PURE__ */ new Date());\n const update = () => now.value = /* @__PURE__ */ new Date();\n const controls = interval === \"requestAnimationFrame\" ? useRafFn(update, { immediate: true }) : useIntervalFn(update, interval, { immediate: true });\n if (exposeControls) {\n return {\n now,\n ...controls\n };\n } else {\n return now;\n }\n}\n\nfunction useObjectUrl(object) {\n const url = shallowRef();\n const release = () => {\n if (url.value)\n URL.revokeObjectURL(url.value);\n url.value = void 0;\n };\n watch(\n () => toValue(object),\n (newObject) => {\n release();\n if (newObject)\n url.value = URL.createObjectURL(newObject);\n },\n { immediate: true }\n );\n tryOnScopeDispose(release);\n return readonly(url);\n}\n\nfunction useClamp(value, min, max) {\n if (typeof value === \"function\" || isReadonly(value))\n return computed(() => clamp(toValue(value), toValue(min), toValue(max)));\n const _value = ref(value);\n return computed({\n get() {\n return _value.value = clamp(_value.value, toValue(min), toValue(max));\n },\n set(value2) {\n _value.value = clamp(value2, toValue(min), toValue(max));\n }\n });\n}\n\nfunction useOffsetPagination(options) {\n const {\n total = Number.POSITIVE_INFINITY,\n pageSize = 10,\n page = 1,\n onPageChange = noop,\n onPageSizeChange = noop,\n onPageCountChange = noop\n } = options;\n const currentPageSize = useClamp(pageSize, 1, Number.POSITIVE_INFINITY);\n const pageCount = computed(() => Math.max(\n 1,\n Math.ceil(toValue(total) / toValue(currentPageSize))\n ));\n const currentPage = useClamp(page, 1, pageCount);\n const isFirstPage = computed(() => currentPage.value === 1);\n const isLastPage = computed(() => currentPage.value === pageCount.value);\n if (isRef(page)) {\n syncRef(page, currentPage, {\n direction: isReadonly(page) ? \"ltr\" : \"both\"\n });\n }\n if (isRef(pageSize)) {\n syncRef(pageSize, currentPageSize, {\n direction: isReadonly(pageSize) ? \"ltr\" : \"both\"\n });\n }\n function prev() {\n currentPage.value--;\n }\n function next() {\n currentPage.value++;\n }\n const returnValue = {\n currentPage,\n currentPageSize,\n pageCount,\n isFirstPage,\n isLastPage,\n prev,\n next\n };\n watch(currentPage, () => {\n onPageChange(reactive(returnValue));\n });\n watch(currentPageSize, () => {\n onPageSizeChange(reactive(returnValue));\n });\n watch(pageCount, () => {\n onPageCountChange(reactive(returnValue));\n });\n return returnValue;\n}\n\nfunction useOnline(options = {}) {\n const { isOnline } = useNetwork(options);\n return isOnline;\n}\n\nfunction usePageLeave(options = {}) {\n const { window = defaultWindow } = options;\n const isLeft = shallowRef(false);\n const handler = (event) => {\n if (!window)\n return;\n event = event || window.event;\n const from = event.relatedTarget || event.toElement;\n isLeft.value = !from;\n };\n if (window) {\n const listenerOptions = { passive: true };\n useEventListener(window, \"mouseout\", handler, listenerOptions);\n useEventListener(window.document, \"mouseleave\", handler, listenerOptions);\n useEventListener(window.document, \"mouseenter\", handler, listenerOptions);\n }\n return isLeft;\n}\n\nfunction useScreenOrientation(options = {}) {\n const {\n window = defaultWindow\n } = options;\n const isSupported = useSupported(() => window && \"screen\" in window && \"orientation\" in window.screen);\n const screenOrientation = isSupported.value ? window.screen.orientation : {};\n const orientation = ref(screenOrientation.type);\n const angle = shallowRef(screenOrientation.angle || 0);\n if (isSupported.value) {\n useEventListener(window, \"orientationchange\", () => {\n orientation.value = screenOrientation.type;\n angle.value = screenOrientation.angle;\n }, { passive: true });\n }\n const lockOrientation = (type) => {\n if (isSupported.value && typeof screenOrientation.lock === \"function\")\n return screenOrientation.lock(type);\n return Promise.reject(new Error(\"Not supported\"));\n };\n const unlockOrientation = () => {\n if (isSupported.value && typeof screenOrientation.unlock === \"function\")\n screenOrientation.unlock();\n };\n return {\n isSupported,\n orientation,\n angle,\n lockOrientation,\n unlockOrientation\n };\n}\n\nfunction useParallax(target, options = {}) {\n const {\n deviceOrientationTiltAdjust = (i) => i,\n deviceOrientationRollAdjust = (i) => i,\n mouseTiltAdjust = (i) => i,\n mouseRollAdjust = (i) => i,\n window = defaultWindow\n } = options;\n const orientation = reactive(useDeviceOrientation({ window }));\n const screenOrientation = reactive(useScreenOrientation({ window }));\n const {\n elementX: x,\n elementY: y,\n elementWidth: width,\n elementHeight: height\n } = useMouseInElement(target, { handleOutside: false, window });\n const source = computed(() => {\n if (orientation.isSupported && (orientation.alpha != null && orientation.alpha !== 0 || orientation.gamma != null && orientation.gamma !== 0)) {\n return \"deviceOrientation\";\n }\n return \"mouse\";\n });\n const roll = computed(() => {\n if (source.value === \"deviceOrientation\") {\n let value;\n switch (screenOrientation.orientation) {\n case \"landscape-primary\":\n value = orientation.gamma / 90;\n break;\n case \"landscape-secondary\":\n value = -orientation.gamma / 90;\n break;\n case \"portrait-primary\":\n value = -orientation.beta / 90;\n break;\n case \"portrait-secondary\":\n value = orientation.beta / 90;\n break;\n default:\n value = -orientation.beta / 90;\n }\n return deviceOrientationRollAdjust(value);\n } else {\n const value = -(y.value - height.value / 2) / height.value;\n return mouseRollAdjust(value);\n }\n });\n const tilt = computed(() => {\n if (source.value === \"deviceOrientation\") {\n let value;\n switch (screenOrientation.orientation) {\n case \"landscape-primary\":\n value = orientation.beta / 90;\n break;\n case \"landscape-secondary\":\n value = -orientation.beta / 90;\n break;\n case \"portrait-primary\":\n value = orientation.gamma / 90;\n break;\n case \"portrait-secondary\":\n value = -orientation.gamma / 90;\n break;\n default:\n value = orientation.gamma / 90;\n }\n return deviceOrientationTiltAdjust(value);\n } else {\n const value = (x.value - width.value / 2) / width.value;\n return mouseTiltAdjust(value);\n }\n });\n return { roll, tilt, source };\n}\n\nfunction useParentElement(element = useCurrentElement()) {\n const parentElement = shallowRef();\n const update = () => {\n const el = unrefElement(element);\n if (el)\n parentElement.value = el.parentElement;\n };\n tryOnMounted(update);\n watch(() => toValue(element), update);\n return parentElement;\n}\n\nfunction usePerformanceObserver(options, callback) {\n const {\n window = defaultWindow,\n immediate = true,\n ...performanceOptions\n } = options;\n const isSupported = useSupported(() => window && \"PerformanceObserver\" in window);\n let observer;\n const stop = () => {\n observer == null ? void 0 : observer.disconnect();\n };\n const start = () => {\n if (isSupported.value) {\n stop();\n observer = new PerformanceObserver(callback);\n observer.observe(performanceOptions);\n }\n };\n tryOnScopeDispose(stop);\n if (immediate)\n start();\n return {\n isSupported,\n start,\n stop\n };\n}\n\nconst defaultState = {\n x: 0,\n y: 0,\n pointerId: 0,\n pressure: 0,\n tiltX: 0,\n tiltY: 0,\n width: 0,\n height: 0,\n twist: 0,\n pointerType: null\n};\nconst keys = /* @__PURE__ */ Object.keys(defaultState);\nfunction usePointer(options = {}) {\n const {\n target = defaultWindow\n } = options;\n const isInside = shallowRef(false);\n const state = ref(options.initialValue || {});\n Object.assign(state.value, defaultState, state.value);\n const handler = (event) => {\n isInside.value = true;\n if (options.pointerTypes && !options.pointerTypes.includes(event.pointerType))\n return;\n state.value = objectPick(event, keys, false);\n };\n if (target) {\n const listenerOptions = { passive: true };\n useEventListener(target, [\"pointerdown\", \"pointermove\", \"pointerup\"], handler, listenerOptions);\n useEventListener(target, \"pointerleave\", () => isInside.value = false, listenerOptions);\n }\n return {\n ...toRefs(state),\n isInside\n };\n}\n\nfunction usePointerLock(target, options = {}) {\n const { document = defaultDocument } = options;\n const isSupported = useSupported(() => document && \"pointerLockElement\" in document);\n const element = shallowRef();\n const triggerElement = shallowRef();\n let targetElement;\n if (isSupported.value) {\n const listenerOptions = { passive: true };\n useEventListener(document, \"pointerlockchange\", () => {\n var _a;\n const currentElement = (_a = document.pointerLockElement) != null ? _a : element.value;\n if (targetElement && currentElement === targetElement) {\n element.value = document.pointerLockElement;\n if (!element.value)\n targetElement = triggerElement.value = null;\n }\n }, listenerOptions);\n useEventListener(document, \"pointerlockerror\", () => {\n var _a;\n const currentElement = (_a = document.pointerLockElement) != null ? _a : element.value;\n if (targetElement && currentElement === targetElement) {\n const action = document.pointerLockElement ? \"release\" : \"acquire\";\n throw new Error(`Failed to ${action} pointer lock.`);\n }\n }, listenerOptions);\n }\n async function lock(e) {\n var _a;\n if (!isSupported.value)\n throw new Error(\"Pointer Lock API is not supported by your browser.\");\n triggerElement.value = e instanceof Event ? e.currentTarget : null;\n targetElement = e instanceof Event ? (_a = unrefElement(target)) != null ? _a : triggerElement.value : unrefElement(e);\n if (!targetElement)\n throw new Error(\"Target element undefined.\");\n targetElement.requestPointerLock();\n return await until(element).toBe(targetElement);\n }\n async function unlock() {\n if (!element.value)\n return false;\n document.exitPointerLock();\n await until(element).toBeNull();\n return true;\n }\n return {\n isSupported,\n element,\n triggerElement,\n lock,\n unlock\n };\n}\n\nfunction usePointerSwipe(target, options = {}) {\n const targetRef = toRef(target);\n const {\n threshold = 50,\n onSwipe,\n onSwipeEnd,\n onSwipeStart,\n disableTextSelect = false\n } = options;\n const posStart = reactive({ x: 0, y: 0 });\n const updatePosStart = (x, y) => {\n posStart.x = x;\n posStart.y = y;\n };\n const posEnd = reactive({ x: 0, y: 0 });\n const updatePosEnd = (x, y) => {\n posEnd.x = x;\n posEnd.y = y;\n };\n const distanceX = computed(() => posStart.x - posEnd.x);\n const distanceY = computed(() => posStart.y - posEnd.y);\n const { max, abs } = Math;\n const isThresholdExceeded = computed(() => max(abs(distanceX.value), abs(distanceY.value)) >= threshold);\n const isSwiping = shallowRef(false);\n const isPointerDown = shallowRef(false);\n const direction = computed(() => {\n if (!isThresholdExceeded.value)\n return \"none\";\n if (abs(distanceX.value) > abs(distanceY.value)) {\n return distanceX.value > 0 ? \"left\" : \"right\";\n } else {\n return distanceY.value > 0 ? \"up\" : \"down\";\n }\n });\n const eventIsAllowed = (e) => {\n var _a, _b, _c;\n const isReleasingButton = e.buttons === 0;\n const isPrimaryButton = e.buttons === 1;\n return (_c = (_b = (_a = options.pointerTypes) == null ? void 0 : _a.includes(e.pointerType)) != null ? _b : isReleasingButton || isPrimaryButton) != null ? _c : true;\n };\n const listenerOptions = { passive: true };\n const stops = [\n useEventListener(target, \"pointerdown\", (e) => {\n if (!eventIsAllowed(e))\n return;\n isPointerDown.value = true;\n const eventTarget = e.target;\n eventTarget == null ? void 0 : eventTarget.setPointerCapture(e.pointerId);\n const { clientX: x, clientY: y } = e;\n updatePosStart(x, y);\n updatePosEnd(x, y);\n onSwipeStart == null ? void 0 : onSwipeStart(e);\n }, listenerOptions),\n useEventListener(target, \"pointermove\", (e) => {\n if (!eventIsAllowed(e))\n return;\n if (!isPointerDown.value)\n return;\n const { clientX: x, clientY: y } = e;\n updatePosEnd(x, y);\n if (!isSwiping.value && isThresholdExceeded.value)\n isSwiping.value = true;\n if (isSwiping.value)\n onSwipe == null ? void 0 : onSwipe(e);\n }, listenerOptions),\n useEventListener(target, \"pointerup\", (e) => {\n if (!eventIsAllowed(e))\n return;\n if (isSwiping.value)\n onSwipeEnd == null ? void 0 : onSwipeEnd(e, direction.value);\n isPointerDown.value = false;\n isSwiping.value = false;\n }, listenerOptions)\n ];\n tryOnMounted(() => {\n var _a, _b, _c, _d, _e, _f, _g, _h;\n (_b = (_a = targetRef.value) == null ? void 0 : _a.style) == null ? void 0 : _b.setProperty(\"touch-action\", \"none\");\n if (disableTextSelect) {\n (_d = (_c = targetRef.value) == null ? void 0 : _c.style) == null ? void 0 : _d.setProperty(\"-webkit-user-select\", \"none\");\n (_f = (_e = targetRef.value) == null ? void 0 : _e.style) == null ? void 0 : _f.setProperty(\"-ms-user-select\", \"none\");\n (_h = (_g = targetRef.value) == null ? void 0 : _g.style) == null ? void 0 : _h.setProperty(\"user-select\", \"none\");\n }\n });\n const stop = () => stops.forEach((s) => s());\n return {\n isSwiping: readonly(isSwiping),\n direction: readonly(direction),\n posStart: readonly(posStart),\n posEnd: readonly(posEnd),\n distanceX,\n distanceY,\n stop\n };\n}\n\nfunction usePreferredColorScheme(options) {\n const isLight = useMediaQuery(\"(prefers-color-scheme: light)\", options);\n const isDark = useMediaQuery(\"(prefers-color-scheme: dark)\", options);\n return computed(() => {\n if (isDark.value)\n return \"dark\";\n if (isLight.value)\n return \"light\";\n return \"no-preference\";\n });\n}\n\nfunction usePreferredContrast(options) {\n const isMore = useMediaQuery(\"(prefers-contrast: more)\", options);\n const isLess = useMediaQuery(\"(prefers-contrast: less)\", options);\n const isCustom = useMediaQuery(\"(prefers-contrast: custom)\", options);\n return computed(() => {\n if (isMore.value)\n return \"more\";\n if (isLess.value)\n return \"less\";\n if (isCustom.value)\n return \"custom\";\n return \"no-preference\";\n });\n}\n\nfunction usePreferredLanguages(options = {}) {\n const { window = defaultWindow } = options;\n if (!window)\n return ref([\"en\"]);\n const navigator = window.navigator;\n const value = ref(navigator.languages);\n useEventListener(window, \"languagechange\", () => {\n value.value = navigator.languages;\n }, { passive: true });\n return value;\n}\n\nfunction usePreferredReducedMotion(options) {\n const isReduced = useMediaQuery(\"(prefers-reduced-motion: reduce)\", options);\n return computed(() => {\n if (isReduced.value)\n return \"reduce\";\n return \"no-preference\";\n });\n}\n\nfunction usePreferredReducedTransparency(options) {\n const isReduced = useMediaQuery(\"(prefers-reduced-transparency: reduce)\", options);\n return computed(() => {\n if (isReduced.value)\n return \"reduce\";\n return \"no-preference\";\n });\n}\n\nfunction usePrevious(value, initialValue) {\n const previous = shallowRef(initialValue);\n watch(\n toRef(value),\n (_, oldValue) => {\n previous.value = oldValue;\n },\n { flush: \"sync\" }\n );\n return readonly(previous);\n}\n\nconst topVarName = \"--vueuse-safe-area-top\";\nconst rightVarName = \"--vueuse-safe-area-right\";\nconst bottomVarName = \"--vueuse-safe-area-bottom\";\nconst leftVarName = \"--vueuse-safe-area-left\";\nfunction useScreenSafeArea() {\n const top = shallowRef(\"\");\n const right = shallowRef(\"\");\n const bottom = shallowRef(\"\");\n const left = shallowRef(\"\");\n if (isClient) {\n const topCssVar = useCssVar(topVarName);\n const rightCssVar = useCssVar(rightVarName);\n const bottomCssVar = useCssVar(bottomVarName);\n const leftCssVar = useCssVar(leftVarName);\n topCssVar.value = \"env(safe-area-inset-top, 0px)\";\n rightCssVar.value = \"env(safe-area-inset-right, 0px)\";\n bottomCssVar.value = \"env(safe-area-inset-bottom, 0px)\";\n leftCssVar.value = \"env(safe-area-inset-left, 0px)\";\n update();\n useEventListener(\"resize\", useDebounceFn(update), { passive: true });\n }\n function update() {\n top.value = getValue(topVarName);\n right.value = getValue(rightVarName);\n bottom.value = getValue(bottomVarName);\n left.value = getValue(leftVarName);\n }\n return {\n top,\n right,\n bottom,\n left,\n update\n };\n}\nfunction getValue(position) {\n return getComputedStyle(document.documentElement).getPropertyValue(position);\n}\n\nfunction useScriptTag(src, onLoaded = noop, options = {}) {\n const {\n immediate = true,\n manual = false,\n type = \"text/javascript\",\n async = true,\n crossOrigin,\n referrerPolicy,\n noModule,\n defer,\n document = defaultDocument,\n attrs = {}\n } = options;\n const scriptTag = shallowRef(null);\n let _promise = null;\n const loadScript = (waitForScriptLoad) => new Promise((resolve, reject) => {\n const resolveWithElement = (el2) => {\n scriptTag.value = el2;\n resolve(el2);\n return el2;\n };\n if (!document) {\n resolve(false);\n return;\n }\n let shouldAppend = false;\n let el = document.querySelector(`script[src=\"${toValue(src)}\"]`);\n if (!el) {\n el = document.createElement(\"script\");\n el.type = type;\n el.async = async;\n el.src = toValue(src);\n if (defer)\n el.defer = defer;\n if (crossOrigin)\n el.crossOrigin = crossOrigin;\n if (noModule)\n el.noModule = noModule;\n if (referrerPolicy)\n el.referrerPolicy = referrerPolicy;\n Object.entries(attrs).forEach(([name, value]) => el == null ? void 0 : el.setAttribute(name, value));\n shouldAppend = true;\n } else if (el.hasAttribute(\"data-loaded\")) {\n resolveWithElement(el);\n }\n const listenerOptions = {\n passive: true\n };\n useEventListener(el, \"error\", (event) => reject(event), listenerOptions);\n useEventListener(el, \"abort\", (event) => reject(event), listenerOptions);\n useEventListener(el, \"load\", () => {\n el.setAttribute(\"data-loaded\", \"true\");\n onLoaded(el);\n resolveWithElement(el);\n }, listenerOptions);\n if (shouldAppend)\n el = document.head.appendChild(el);\n if (!waitForScriptLoad)\n resolveWithElement(el);\n });\n const load = (waitForScriptLoad = true) => {\n if (!_promise)\n _promise = loadScript(waitForScriptLoad);\n return _promise;\n };\n const unload = () => {\n if (!document)\n return;\n _promise = null;\n if (scriptTag.value)\n scriptTag.value = null;\n const el = document.querySelector(`script[src=\"${toValue(src)}\"]`);\n if (el)\n document.head.removeChild(el);\n };\n if (immediate && !manual)\n tryOnMounted(load);\n if (!manual)\n tryOnUnmounted(unload);\n return { scriptTag, load, unload };\n}\n\nfunction checkOverflowScroll(ele) {\n const style = window.getComputedStyle(ele);\n if (style.overflowX === \"scroll\" || style.overflowY === \"scroll\" || style.overflowX === \"auto\" && ele.clientWidth < ele.scrollWidth || style.overflowY === \"auto\" && ele.clientHeight < ele.scrollHeight) {\n return true;\n } else {\n const parent = ele.parentNode;\n if (!parent || parent.tagName === \"BODY\")\n return false;\n return checkOverflowScroll(parent);\n }\n}\nfunction preventDefault(rawEvent) {\n const e = rawEvent || window.event;\n const _target = e.target;\n if (checkOverflowScroll(_target))\n return false;\n if (e.touches.length > 1)\n return true;\n if (e.preventDefault)\n e.preventDefault();\n return false;\n}\nconst elInitialOverflow = /* @__PURE__ */ new WeakMap();\nfunction useScrollLock(element, initialState = false) {\n const isLocked = shallowRef(initialState);\n let stopTouchMoveListener = null;\n let initialOverflow = \"\";\n watch(toRef(element), (el) => {\n const target = resolveElement(toValue(el));\n if (target) {\n const ele = target;\n if (!elInitialOverflow.get(ele))\n elInitialOverflow.set(ele, ele.style.overflow);\n if (ele.style.overflow !== \"hidden\")\n initialOverflow = ele.style.overflow;\n if (ele.style.overflow === \"hidden\")\n return isLocked.value = true;\n if (isLocked.value)\n return ele.style.overflow = \"hidden\";\n }\n }, {\n immediate: true\n });\n const lock = () => {\n const el = resolveElement(toValue(element));\n if (!el || isLocked.value)\n return;\n if (isIOS) {\n stopTouchMoveListener = useEventListener(\n el,\n \"touchmove\",\n (e) => {\n preventDefault(e);\n },\n { passive: false }\n );\n }\n el.style.overflow = \"hidden\";\n isLocked.value = true;\n };\n const unlock = () => {\n const el = resolveElement(toValue(element));\n if (!el || !isLocked.value)\n return;\n if (isIOS)\n stopTouchMoveListener == null ? void 0 : stopTouchMoveListener();\n el.style.overflow = initialOverflow;\n elInitialOverflow.delete(el);\n isLocked.value = false;\n };\n tryOnScopeDispose(unlock);\n return computed({\n get() {\n return isLocked.value;\n },\n set(v) {\n if (v)\n lock();\n else unlock();\n }\n });\n}\n\nfunction useSessionStorage(key, initialValue, options = {}) {\n const { window = defaultWindow } = options;\n return useStorage(key, initialValue, window == null ? void 0 : window.sessionStorage, options);\n}\n\nfunction useShare(shareOptions = {}, options = {}) {\n const { navigator = defaultNavigator } = options;\n const _navigator = navigator;\n const isSupported = useSupported(() => _navigator && \"canShare\" in _navigator);\n const share = async (overrideOptions = {}) => {\n if (isSupported.value) {\n const data = {\n ...toValue(shareOptions),\n ...toValue(overrideOptions)\n };\n let granted = true;\n if (data.files && _navigator.canShare)\n granted = _navigator.canShare({ files: data.files });\n if (granted)\n return _navigator.share(data);\n }\n };\n return {\n isSupported,\n share\n };\n}\n\nconst defaultSortFn = (source, compareFn) => source.sort(compareFn);\nconst defaultCompare = (a, b) => a - b;\nfunction useSorted(...args) {\n var _a, _b, _c, _d;\n const [source] = args;\n let compareFn = defaultCompare;\n let options = {};\n if (args.length === 2) {\n if (typeof args[1] === \"object\") {\n options = args[1];\n compareFn = (_a = options.compareFn) != null ? _a : defaultCompare;\n } else {\n compareFn = (_b = args[1]) != null ? _b : defaultCompare;\n }\n } else if (args.length > 2) {\n compareFn = (_c = args[1]) != null ? _c : defaultCompare;\n options = (_d = args[2]) != null ? _d : {};\n }\n const {\n dirty = false,\n sortFn = defaultSortFn\n } = options;\n if (!dirty)\n return computed(() => sortFn([...toValue(source)], compareFn));\n watchEffect(() => {\n const result = sortFn(toValue(source), compareFn);\n if (isRef(source))\n source.value = result;\n else\n source.splice(0, source.length, ...result);\n });\n return source;\n}\n\nfunction useSpeechRecognition(options = {}) {\n const {\n interimResults = true,\n continuous = true,\n maxAlternatives = 1,\n window = defaultWindow\n } = options;\n const lang = toRef(options.lang || \"en-US\");\n const isListening = shallowRef(false);\n const isFinal = shallowRef(false);\n const result = shallowRef(\"\");\n const error = shallowRef(void 0);\n let recognition;\n const start = () => {\n isListening.value = true;\n };\n const stop = () => {\n isListening.value = false;\n };\n const toggle = (value = !isListening.value) => {\n if (value) {\n start();\n } else {\n stop();\n }\n };\n const SpeechRecognition = window && (window.SpeechRecognition || window.webkitSpeechRecognition);\n const isSupported = useSupported(() => SpeechRecognition);\n if (isSupported.value) {\n recognition = new SpeechRecognition();\n recognition.continuous = continuous;\n recognition.interimResults = interimResults;\n recognition.lang = toValue(lang);\n recognition.maxAlternatives = maxAlternatives;\n recognition.onstart = () => {\n isListening.value = true;\n isFinal.value = false;\n };\n watch(lang, (lang2) => {\n if (recognition && !isListening.value)\n recognition.lang = lang2;\n });\n recognition.onresult = (event) => {\n const currentResult = event.results[event.resultIndex];\n const { transcript } = currentResult[0];\n isFinal.value = currentResult.isFinal;\n result.value = transcript;\n error.value = void 0;\n };\n recognition.onerror = (event) => {\n error.value = event;\n };\n recognition.onend = () => {\n isListening.value = false;\n recognition.lang = toValue(lang);\n };\n watch(isListening, (newValue, oldValue) => {\n if (newValue === oldValue)\n return;\n if (newValue)\n recognition.start();\n else\n recognition.stop();\n });\n }\n tryOnScopeDispose(() => {\n stop();\n });\n return {\n isSupported,\n isListening,\n isFinal,\n recognition,\n result,\n error,\n toggle,\n start,\n stop\n };\n}\n\nfunction useSpeechSynthesis(text, options = {}) {\n const {\n pitch = 1,\n rate = 1,\n volume = 1,\n window = defaultWindow\n } = options;\n const synth = window && window.speechSynthesis;\n const isSupported = useSupported(() => synth);\n const isPlaying = shallowRef(false);\n const status = shallowRef(\"init\");\n const spokenText = toRef(text || \"\");\n const lang = toRef(options.lang || \"en-US\");\n const error = shallowRef(void 0);\n const toggle = (value = !isPlaying.value) => {\n isPlaying.value = value;\n };\n const bindEventsForUtterance = (utterance2) => {\n utterance2.lang = toValue(lang);\n utterance2.voice = toValue(options.voice) || null;\n utterance2.pitch = toValue(pitch);\n utterance2.rate = toValue(rate);\n utterance2.volume = volume;\n utterance2.onstart = () => {\n isPlaying.value = true;\n status.value = \"play\";\n };\n utterance2.onpause = () => {\n isPlaying.value = false;\n status.value = \"pause\";\n };\n utterance2.onresume = () => {\n isPlaying.value = true;\n status.value = \"play\";\n };\n utterance2.onend = () => {\n isPlaying.value = false;\n status.value = \"end\";\n };\n utterance2.onerror = (event) => {\n error.value = event;\n };\n };\n const utterance = computed(() => {\n isPlaying.value = false;\n status.value = \"init\";\n const newUtterance = new SpeechSynthesisUtterance(spokenText.value);\n bindEventsForUtterance(newUtterance);\n return newUtterance;\n });\n const speak = () => {\n synth.cancel();\n if (utterance)\n synth.speak(utterance.value);\n };\n const stop = () => {\n synth.cancel();\n isPlaying.value = false;\n };\n if (isSupported.value) {\n bindEventsForUtterance(utterance.value);\n watch(lang, (lang2) => {\n if (utterance.value && !isPlaying.value)\n utterance.value.lang = lang2;\n });\n if (options.voice) {\n watch(options.voice, () => {\n synth.cancel();\n });\n }\n watch(isPlaying, () => {\n if (isPlaying.value)\n synth.resume();\n else\n synth.pause();\n });\n }\n tryOnScopeDispose(() => {\n isPlaying.value = false;\n });\n return {\n isSupported,\n isPlaying,\n status,\n utterance,\n error,\n stop,\n toggle,\n speak\n };\n}\n\nfunction useStepper(steps, initialStep) {\n const stepsRef = ref(steps);\n const stepNames = computed(() => Array.isArray(stepsRef.value) ? stepsRef.value : Object.keys(stepsRef.value));\n const index = ref(stepNames.value.indexOf(initialStep != null ? initialStep : stepNames.value[0]));\n const current = computed(() => at(index.value));\n const isFirst = computed(() => index.value === 0);\n const isLast = computed(() => index.value === stepNames.value.length - 1);\n const next = computed(() => stepNames.value[index.value + 1]);\n const previous = computed(() => stepNames.value[index.value - 1]);\n function at(index2) {\n if (Array.isArray(stepsRef.value))\n return stepsRef.value[index2];\n return stepsRef.value[stepNames.value[index2]];\n }\n function get(step) {\n if (!stepNames.value.includes(step))\n return;\n return at(stepNames.value.indexOf(step));\n }\n function goTo(step) {\n if (stepNames.value.includes(step))\n index.value = stepNames.value.indexOf(step);\n }\n function goToNext() {\n if (isLast.value)\n return;\n index.value++;\n }\n function goToPrevious() {\n if (isFirst.value)\n return;\n index.value--;\n }\n function goBackTo(step) {\n if (isAfter(step))\n goTo(step);\n }\n function isNext(step) {\n return stepNames.value.indexOf(step) === index.value + 1;\n }\n function isPrevious(step) {\n return stepNames.value.indexOf(step) === index.value - 1;\n }\n function isCurrent(step) {\n return stepNames.value.indexOf(step) === index.value;\n }\n function isBefore(step) {\n return index.value < stepNames.value.indexOf(step);\n }\n function isAfter(step) {\n return index.value > stepNames.value.indexOf(step);\n }\n return {\n steps: stepsRef,\n stepNames,\n index,\n current,\n next,\n previous,\n isFirst,\n isLast,\n at,\n get,\n goTo,\n goToNext,\n goToPrevious,\n goBackTo,\n isNext,\n isPrevious,\n isCurrent,\n isBefore,\n isAfter\n };\n}\n\nfunction useStorageAsync(key, initialValue, storage, options = {}) {\n var _a;\n const {\n flush = \"pre\",\n deep = true,\n listenToStorageChanges = true,\n writeDefaults = true,\n mergeDefaults = false,\n shallow,\n window = defaultWindow,\n eventFilter,\n onError = (e) => {\n console.error(e);\n }\n } = options;\n const rawInit = toValue(initialValue);\n const type = guessSerializerType(rawInit);\n const data = (shallow ? shallowRef : ref)(toValue(initialValue));\n const serializer = (_a = options.serializer) != null ? _a : StorageSerializers[type];\n if (!storage) {\n try {\n storage = getSSRHandler(\"getDefaultStorageAsync\", () => {\n var _a2;\n return (_a2 = defaultWindow) == null ? void 0 : _a2.localStorage;\n })();\n } catch (e) {\n onError(e);\n }\n }\n async function read(event) {\n if (!storage || event && event.key !== key)\n return;\n try {\n const rawValue = event ? event.newValue : await storage.getItem(key);\n if (rawValue == null) {\n data.value = rawInit;\n if (writeDefaults && rawInit !== null)\n await storage.setItem(key, await serializer.write(rawInit));\n } else if (mergeDefaults) {\n const value = await serializer.read(rawValue);\n if (typeof mergeDefaults === \"function\")\n data.value = mergeDefaults(value, rawInit);\n else if (type === \"object\" && !Array.isArray(value))\n data.value = { ...rawInit, ...value };\n else data.value = value;\n } else {\n data.value = await serializer.read(rawValue);\n }\n } catch (e) {\n onError(e);\n }\n }\n read();\n if (window && listenToStorageChanges)\n useEventListener(window, \"storage\", (e) => Promise.resolve().then(() => read(e)), { passive: true });\n if (storage) {\n watchWithFilter(\n data,\n async () => {\n try {\n if (data.value == null)\n await storage.removeItem(key);\n else\n await storage.setItem(key, await serializer.write(data.value));\n } catch (e) {\n onError(e);\n }\n },\n {\n flush,\n deep,\n eventFilter\n }\n );\n }\n return data;\n}\n\nlet _id = 0;\nfunction useStyleTag(css, options = {}) {\n const isLoaded = shallowRef(false);\n const {\n document = defaultDocument,\n immediate = true,\n manual = false,\n id = `vueuse_styletag_${++_id}`\n } = options;\n const cssRef = shallowRef(css);\n let stop = () => {\n };\n const load = () => {\n if (!document)\n return;\n const el = document.getElementById(id) || document.createElement(\"style\");\n if (!el.isConnected) {\n el.id = id;\n if (options.media)\n el.media = options.media;\n document.head.appendChild(el);\n }\n if (isLoaded.value)\n return;\n stop = watch(\n cssRef,\n (value) => {\n el.textContent = value;\n },\n { immediate: true }\n );\n isLoaded.value = true;\n };\n const unload = () => {\n if (!document || !isLoaded.value)\n return;\n stop();\n document.head.removeChild(document.getElementById(id));\n isLoaded.value = false;\n };\n if (immediate && !manual)\n tryOnMounted(load);\n if (!manual)\n tryOnScopeDispose(unload);\n return {\n id,\n css: cssRef,\n unload,\n load,\n isLoaded: readonly(isLoaded)\n };\n}\n\nfunction useSwipe(target, options = {}) {\n const {\n threshold = 50,\n onSwipe,\n onSwipeEnd,\n onSwipeStart,\n passive = true\n } = options;\n const coordsStart = reactive({ x: 0, y: 0 });\n const coordsEnd = reactive({ x: 0, y: 0 });\n const diffX = computed(() => coordsStart.x - coordsEnd.x);\n const diffY = computed(() => coordsStart.y - coordsEnd.y);\n const { max, abs } = Math;\n const isThresholdExceeded = computed(() => max(abs(diffX.value), abs(diffY.value)) >= threshold);\n const isSwiping = shallowRef(false);\n const direction = computed(() => {\n if (!isThresholdExceeded.value)\n return \"none\";\n if (abs(diffX.value) > abs(diffY.value)) {\n return diffX.value > 0 ? \"left\" : \"right\";\n } else {\n return diffY.value > 0 ? \"up\" : \"down\";\n }\n });\n const getTouchEventCoords = (e) => [e.touches[0].clientX, e.touches[0].clientY];\n const updateCoordsStart = (x, y) => {\n coordsStart.x = x;\n coordsStart.y = y;\n };\n const updateCoordsEnd = (x, y) => {\n coordsEnd.x = x;\n coordsEnd.y = y;\n };\n const listenerOptions = { passive, capture: !passive };\n const onTouchEnd = (e) => {\n if (isSwiping.value)\n onSwipeEnd == null ? void 0 : onSwipeEnd(e, direction.value);\n isSwiping.value = false;\n };\n const stops = [\n useEventListener(target, \"touchstart\", (e) => {\n if (e.touches.length !== 1)\n return;\n const [x, y] = getTouchEventCoords(e);\n updateCoordsStart(x, y);\n updateCoordsEnd(x, y);\n onSwipeStart == null ? void 0 : onSwipeStart(e);\n }, listenerOptions),\n useEventListener(target, \"touchmove\", (e) => {\n if (e.touches.length !== 1)\n return;\n const [x, y] = getTouchEventCoords(e);\n updateCoordsEnd(x, y);\n if (listenerOptions.capture && !listenerOptions.passive && Math.abs(diffX.value) > Math.abs(diffY.value))\n e.preventDefault();\n if (!isSwiping.value && isThresholdExceeded.value)\n isSwiping.value = true;\n if (isSwiping.value)\n onSwipe == null ? void 0 : onSwipe(e);\n }, listenerOptions),\n useEventListener(target, [\"touchend\", \"touchcancel\"], onTouchEnd, listenerOptions)\n ];\n const stop = () => stops.forEach((s) => s());\n return {\n isSwiping,\n direction,\n coordsStart,\n coordsEnd,\n lengthX: diffX,\n lengthY: diffY,\n stop,\n // TODO: Remove in the next major version\n isPassiveEventSupported: true\n };\n}\n\nfunction useTemplateRefsList() {\n const refs = ref([]);\n refs.value.set = (el) => {\n if (el)\n refs.value.push(el);\n };\n onBeforeUpdate(() => {\n refs.value.length = 0;\n });\n return refs;\n}\n\nfunction useTextDirection(options = {}) {\n const {\n document = defaultDocument,\n selector = \"html\",\n observe = false,\n initialValue = \"ltr\"\n } = options;\n function getValue() {\n var _a, _b;\n return (_b = (_a = document == null ? void 0 : document.querySelector(selector)) == null ? void 0 : _a.getAttribute(\"dir\")) != null ? _b : initialValue;\n }\n const dir = ref(getValue());\n tryOnMounted(() => dir.value = getValue());\n if (observe && document) {\n useMutationObserver(\n document.querySelector(selector),\n () => dir.value = getValue(),\n { attributes: true }\n );\n }\n return computed({\n get() {\n return dir.value;\n },\n set(v) {\n var _a, _b;\n dir.value = v;\n if (!document)\n return;\n if (dir.value)\n (_a = document.querySelector(selector)) == null ? void 0 : _a.setAttribute(\"dir\", dir.value);\n else\n (_b = document.querySelector(selector)) == null ? void 0 : _b.removeAttribute(\"dir\");\n }\n });\n}\n\nfunction getRangesFromSelection(selection) {\n var _a;\n const rangeCount = (_a = selection.rangeCount) != null ? _a : 0;\n return Array.from({ length: rangeCount }, (_, i) => selection.getRangeAt(i));\n}\nfunction useTextSelection(options = {}) {\n const {\n window = defaultWindow\n } = options;\n const selection = ref(null);\n const text = computed(() => {\n var _a, _b;\n return (_b = (_a = selection.value) == null ? void 0 : _a.toString()) != null ? _b : \"\";\n });\n const ranges = computed(() => selection.value ? getRangesFromSelection(selection.value) : []);\n const rects = computed(() => ranges.value.map((range) => range.getBoundingClientRect()));\n function onSelectionChange() {\n selection.value = null;\n if (window)\n selection.value = window.getSelection();\n }\n if (window)\n useEventListener(window.document, \"selectionchange\", onSelectionChange, { passive: true });\n return {\n text,\n rects,\n ranges,\n selection\n };\n}\n\nfunction tryRequestAnimationFrame(window = defaultWindow, fn) {\n if (window && typeof window.requestAnimationFrame === \"function\") {\n window.requestAnimationFrame(fn);\n } else {\n fn();\n }\n}\nfunction useTextareaAutosize(options = {}) {\n var _a, _b;\n const { window = defaultWindow } = options;\n const textarea = toRef(options == null ? void 0 : options.element);\n const input = toRef((_a = options == null ? void 0 : options.input) != null ? _a : \"\");\n const styleProp = (_b = options == null ? void 0 : options.styleProp) != null ? _b : \"height\";\n const textareaScrollHeight = shallowRef(1);\n const textareaOldWidth = shallowRef(0);\n function triggerResize() {\n var _a2;\n if (!textarea.value)\n return;\n let height = \"\";\n textarea.value.style[styleProp] = \"1px\";\n textareaScrollHeight.value = (_a2 = textarea.value) == null ? void 0 : _a2.scrollHeight;\n const _styleTarget = toValue(options == null ? void 0 : options.styleTarget);\n if (_styleTarget)\n _styleTarget.style[styleProp] = `${textareaScrollHeight.value}px`;\n else\n height = `${textareaScrollHeight.value}px`;\n textarea.value.style[styleProp] = height;\n }\n watch([input, textarea], () => nextTick(triggerResize), { immediate: true });\n watch(textareaScrollHeight, () => {\n var _a2;\n return (_a2 = options == null ? void 0 : options.onResize) == null ? void 0 : _a2.call(options);\n });\n useResizeObserver(textarea, ([{ contentRect }]) => {\n if (textareaOldWidth.value === contentRect.width)\n return;\n tryRequestAnimationFrame(window, () => {\n textareaOldWidth.value = contentRect.width;\n triggerResize();\n });\n });\n if (options == null ? void 0 : options.watch)\n watch(options.watch, triggerResize, { immediate: true, deep: true });\n return {\n textarea,\n input,\n triggerResize\n };\n}\n\nfunction useThrottledRefHistory(source, options = {}) {\n const { throttle = 200, trailing = true } = options;\n const filter = throttleFilter(throttle, trailing);\n const history = useRefHistory(source, { ...options, eventFilter: filter });\n return {\n ...history\n };\n}\n\nconst DEFAULT_UNITS = [\n { max: 6e4, value: 1e3, name: \"second\" },\n { max: 276e4, value: 6e4, name: \"minute\" },\n { max: 72e6, value: 36e5, name: \"hour\" },\n { max: 5184e5, value: 864e5, name: \"day\" },\n { max: 24192e5, value: 6048e5, name: \"week\" },\n { max: 28512e6, value: 2592e6, name: \"month\" },\n { max: Number.POSITIVE_INFINITY, value: 31536e6, name: \"year\" }\n];\nconst DEFAULT_MESSAGES = {\n justNow: \"just now\",\n past: (n) => n.match(/\\d/) ? `${n} ago` : n,\n future: (n) => n.match(/\\d/) ? `in ${n}` : n,\n month: (n, past) => n === 1 ? past ? \"last month\" : \"next month\" : `${n} month${n > 1 ? \"s\" : \"\"}`,\n year: (n, past) => n === 1 ? past ? \"last year\" : \"next year\" : `${n} year${n > 1 ? \"s\" : \"\"}`,\n day: (n, past) => n === 1 ? past ? \"yesterday\" : \"tomorrow\" : `${n} day${n > 1 ? \"s\" : \"\"}`,\n week: (n, past) => n === 1 ? past ? \"last week\" : \"next week\" : `${n} week${n > 1 ? \"s\" : \"\"}`,\n hour: (n) => `${n} hour${n > 1 ? \"s\" : \"\"}`,\n minute: (n) => `${n} minute${n > 1 ? \"s\" : \"\"}`,\n second: (n) => `${n} second${n > 1 ? \"s\" : \"\"}`,\n invalid: \"\"\n};\nfunction DEFAULT_FORMATTER(date) {\n return date.toISOString().slice(0, 10);\n}\nfunction useTimeAgo(time, options = {}) {\n const {\n controls: exposeControls = false,\n updateInterval = 3e4\n } = options;\n const { now, ...controls } = useNow({ interval: updateInterval, controls: true });\n const timeAgo = computed(() => formatTimeAgo(new Date(toValue(time)), options, toValue(now)));\n if (exposeControls) {\n return {\n timeAgo,\n ...controls\n };\n } else {\n return timeAgo;\n }\n}\nfunction formatTimeAgo(from, options = {}, now = Date.now()) {\n var _a;\n const {\n max,\n messages = DEFAULT_MESSAGES,\n fullDateFormatter = DEFAULT_FORMATTER,\n units = DEFAULT_UNITS,\n showSecond = false,\n rounding = \"round\"\n } = options;\n const roundFn = typeof rounding === \"number\" ? (n) => +n.toFixed(rounding) : Math[rounding];\n const diff = +now - +from;\n const absDiff = Math.abs(diff);\n function getValue(diff2, unit) {\n return roundFn(Math.abs(diff2) / unit.value);\n }\n function format(diff2, unit) {\n const val = getValue(diff2, unit);\n const past = diff2 > 0;\n const str = applyFormat(unit.name, val, past);\n return applyFormat(past ? \"past\" : \"future\", str, past);\n }\n function applyFormat(name, val, isPast) {\n const formatter = messages[name];\n if (typeof formatter === \"function\")\n return formatter(val, isPast);\n return formatter.replace(\"{0}\", val.toString());\n }\n if (absDiff < 6e4 && !showSecond)\n return messages.justNow;\n if (typeof max === \"number\" && absDiff > max)\n return fullDateFormatter(new Date(from));\n if (typeof max === \"string\") {\n const unitMax = (_a = units.find((i) => i.name === max)) == null ? void 0 : _a.max;\n if (unitMax && absDiff > unitMax)\n return fullDateFormatter(new Date(from));\n }\n for (const [idx, unit] of units.entries()) {\n const val = getValue(diff, unit);\n if (val <= 0 && units[idx - 1])\n return format(diff, units[idx - 1]);\n if (absDiff < unit.max)\n return format(diff, unit);\n }\n return messages.invalid;\n}\n\nfunction useTimeoutPoll(fn, interval, options = {}) {\n const {\n immediate = true,\n immediateCallback = false\n } = options;\n const { start } = useTimeoutFn(loop, interval, { immediate });\n const isActive = shallowRef(false);\n async function loop() {\n if (!isActive.value)\n return;\n await fn();\n start();\n }\n function resume() {\n if (!isActive.value) {\n isActive.value = true;\n if (immediateCallback)\n fn();\n start();\n }\n }\n function pause() {\n isActive.value = false;\n }\n if (immediate && isClient)\n resume();\n tryOnScopeDispose(pause);\n return {\n isActive,\n pause,\n resume\n };\n}\n\nfunction useTimestamp(options = {}) {\n const {\n controls: exposeControls = false,\n offset = 0,\n immediate = true,\n interval = \"requestAnimationFrame\",\n callback\n } = options;\n const ts = shallowRef(timestamp() + offset);\n const update = () => ts.value = timestamp() + offset;\n const cb = callback ? () => {\n update();\n callback(ts.value);\n } : update;\n const controls = interval === \"requestAnimationFrame\" ? useRafFn(cb, { immediate }) : useIntervalFn(cb, interval, { immediate });\n if (exposeControls) {\n return {\n timestamp: ts,\n ...controls\n };\n } else {\n return ts;\n }\n}\n\nfunction useTitle(newTitle = null, options = {}) {\n var _a, _b, _c;\n const {\n document = defaultDocument,\n restoreOnUnmount = (t) => t\n } = options;\n const originalTitle = (_a = document == null ? void 0 : document.title) != null ? _a : \"\";\n const title = toRef((_b = newTitle != null ? newTitle : document == null ? void 0 : document.title) != null ? _b : null);\n const isReadonly = !!(newTitle && typeof newTitle === \"function\");\n function format(t) {\n if (!(\"titleTemplate\" in options))\n return t;\n const template = options.titleTemplate || \"%s\";\n return typeof template === \"function\" ? template(t) : toValue(template).replace(/%s/g, t);\n }\n watch(\n title,\n (newValue, oldValue) => {\n if (newValue !== oldValue && document)\n document.title = format(newValue != null ? newValue : \"\");\n },\n { immediate: true }\n );\n if (options.observe && !options.titleTemplate && document && !isReadonly) {\n useMutationObserver(\n (_c = document.head) == null ? void 0 : _c.querySelector(\"title\"),\n () => {\n if (document && document.title !== title.value)\n title.value = format(document.title);\n },\n { childList: true }\n );\n }\n tryOnScopeDispose(() => {\n if (restoreOnUnmount) {\n const restoredTitle = restoreOnUnmount(originalTitle, title.value || \"\");\n if (restoredTitle != null && document)\n document.title = restoredTitle;\n }\n });\n return title;\n}\n\nconst _TransitionPresets = {\n easeInSine: [0.12, 0, 0.39, 0],\n easeOutSine: [0.61, 1, 0.88, 1],\n easeInOutSine: [0.37, 0, 0.63, 1],\n easeInQuad: [0.11, 0, 0.5, 0],\n easeOutQuad: [0.5, 1, 0.89, 1],\n easeInOutQuad: [0.45, 0, 0.55, 1],\n easeInCubic: [0.32, 0, 0.67, 0],\n easeOutCubic: [0.33, 1, 0.68, 1],\n easeInOutCubic: [0.65, 0, 0.35, 1],\n easeInQuart: [0.5, 0, 0.75, 0],\n easeOutQuart: [0.25, 1, 0.5, 1],\n easeInOutQuart: [0.76, 0, 0.24, 1],\n easeInQuint: [0.64, 0, 0.78, 0],\n easeOutQuint: [0.22, 1, 0.36, 1],\n easeInOutQuint: [0.83, 0, 0.17, 1],\n easeInExpo: [0.7, 0, 0.84, 0],\n easeOutExpo: [0.16, 1, 0.3, 1],\n easeInOutExpo: [0.87, 0, 0.13, 1],\n easeInCirc: [0.55, 0, 1, 0.45],\n easeOutCirc: [0, 0.55, 0.45, 1],\n easeInOutCirc: [0.85, 0, 0.15, 1],\n easeInBack: [0.36, 0, 0.66, -0.56],\n easeOutBack: [0.34, 1.56, 0.64, 1],\n easeInOutBack: [0.68, -0.6, 0.32, 1.6]\n};\nconst TransitionPresets = /* @__PURE__ */ Object.assign({}, { linear: identity }, _TransitionPresets);\nfunction createEasingFunction([p0, p1, p2, p3]) {\n const a = (a1, a2) => 1 - 3 * a2 + 3 * a1;\n const b = (a1, a2) => 3 * a2 - 6 * a1;\n const c = (a1) => 3 * a1;\n const calcBezier = (t, a1, a2) => ((a(a1, a2) * t + b(a1, a2)) * t + c(a1)) * t;\n const getSlope = (t, a1, a2) => 3 * a(a1, a2) * t * t + 2 * b(a1, a2) * t + c(a1);\n const getTforX = (x) => {\n let aGuessT = x;\n for (let i = 0; i < 4; ++i) {\n const currentSlope = getSlope(aGuessT, p0, p2);\n if (currentSlope === 0)\n return aGuessT;\n const currentX = calcBezier(aGuessT, p0, p2) - x;\n aGuessT -= currentX / currentSlope;\n }\n return aGuessT;\n };\n return (x) => p0 === p1 && p2 === p3 ? x : calcBezier(getTforX(x), p1, p3);\n}\nfunction lerp(a, b, alpha) {\n return a + alpha * (b - a);\n}\nfunction toVec(t) {\n return (typeof t === \"number\" ? [t] : t) || [];\n}\nfunction executeTransition(source, from, to, options = {}) {\n var _a, _b;\n const fromVal = toValue(from);\n const toVal = toValue(to);\n const v1 = toVec(fromVal);\n const v2 = toVec(toVal);\n const duration = (_a = toValue(options.duration)) != null ? _a : 1e3;\n const startedAt = Date.now();\n const endAt = Date.now() + duration;\n const trans = typeof options.transition === \"function\" ? options.transition : (_b = toValue(options.transition)) != null ? _b : identity;\n const ease = typeof trans === \"function\" ? trans : createEasingFunction(trans);\n return new Promise((resolve) => {\n source.value = fromVal;\n const tick = () => {\n var _a2;\n if ((_a2 = options.abort) == null ? void 0 : _a2.call(options)) {\n resolve();\n return;\n }\n const now = Date.now();\n const alpha = ease((now - startedAt) / duration);\n const arr = toVec(source.value).map((n, i) => lerp(v1[i], v2[i], alpha));\n if (Array.isArray(source.value))\n source.value = arr.map((n, i) => {\n var _a3, _b2;\n return lerp((_a3 = v1[i]) != null ? _a3 : 0, (_b2 = v2[i]) != null ? _b2 : 0, alpha);\n });\n else if (typeof source.value === \"number\")\n source.value = arr[0];\n if (now < endAt) {\n requestAnimationFrame(tick);\n } else {\n source.value = toVal;\n resolve();\n }\n };\n tick();\n });\n}\nfunction useTransition(source, options = {}) {\n let currentId = 0;\n const sourceVal = () => {\n const v = toValue(source);\n return typeof v === \"number\" ? v : v.map(toValue);\n };\n const outputRef = ref(sourceVal());\n watch(sourceVal, async (to) => {\n var _a, _b;\n if (toValue(options.disabled))\n return;\n const id = ++currentId;\n if (options.delay)\n await promiseTimeout(toValue(options.delay));\n if (id !== currentId)\n return;\n const toVal = Array.isArray(to) ? to.map(toValue) : toValue(to);\n (_a = options.onStarted) == null ? void 0 : _a.call(options);\n await executeTransition(outputRef, outputRef.value, toVal, {\n ...options,\n abort: () => {\n var _a2;\n return id !== currentId || ((_a2 = options.abort) == null ? void 0 : _a2.call(options));\n }\n });\n (_b = options.onFinished) == null ? void 0 : _b.call(options);\n }, { deep: true });\n watch(() => toValue(options.disabled), (disabled) => {\n if (disabled) {\n currentId++;\n outputRef.value = sourceVal();\n }\n });\n tryOnScopeDispose(() => {\n currentId++;\n });\n return computed(() => toValue(options.disabled) ? sourceVal() : outputRef.value);\n}\n\nfunction useUrlSearchParams(mode = \"history\", options = {}) {\n const {\n initialValue = {},\n removeNullishValues = true,\n removeFalsyValues = false,\n write: enableWrite = true,\n writeMode = \"replace\",\n window = defaultWindow\n } = options;\n if (!window)\n return reactive(initialValue);\n const state = reactive({});\n function getRawParams() {\n if (mode === \"history\") {\n return window.location.search || \"\";\n } else if (mode === \"hash\") {\n const hash = window.location.hash || \"\";\n const index = hash.indexOf(\"?\");\n return index > 0 ? hash.slice(index) : \"\";\n } else {\n return (window.location.hash || \"\").replace(/^#/, \"\");\n }\n }\n function constructQuery(params) {\n const stringified = params.toString();\n if (mode === \"history\")\n return `${stringified ? `?${stringified}` : \"\"}${window.location.hash || \"\"}`;\n if (mode === \"hash-params\")\n return `${window.location.search || \"\"}${stringified ? `#${stringified}` : \"\"}`;\n const hash = window.location.hash || \"#\";\n const index = hash.indexOf(\"?\");\n if (index > 0)\n return `${window.location.search || \"\"}${hash.slice(0, index)}${stringified ? `?${stringified}` : \"\"}`;\n return `${window.location.search || \"\"}${hash}${stringified ? `?${stringified}` : \"\"}`;\n }\n function read() {\n return new URLSearchParams(getRawParams());\n }\n function updateState(params) {\n const unusedKeys = new Set(Object.keys(state));\n for (const key of params.keys()) {\n const paramsForKey = params.getAll(key);\n state[key] = paramsForKey.length > 1 ? paramsForKey : params.get(key) || \"\";\n unusedKeys.delete(key);\n }\n Array.from(unusedKeys).forEach((key) => delete state[key]);\n }\n const { pause, resume } = pausableWatch(\n state,\n () => {\n const params = new URLSearchParams(\"\");\n Object.keys(state).forEach((key) => {\n const mapEntry = state[key];\n if (Array.isArray(mapEntry))\n mapEntry.forEach((value) => params.append(key, value));\n else if (removeNullishValues && mapEntry == null)\n params.delete(key);\n else if (removeFalsyValues && !mapEntry)\n params.delete(key);\n else\n params.set(key, mapEntry);\n });\n write(params, false);\n },\n { deep: true }\n );\n function write(params, shouldUpdate) {\n pause();\n if (shouldUpdate)\n updateState(params);\n if (writeMode === \"replace\") {\n window.history.replaceState(\n window.history.state,\n window.document.title,\n window.location.pathname + constructQuery(params)\n );\n } else {\n window.history.pushState(\n window.history.state,\n window.document.title,\n window.location.pathname + constructQuery(params)\n );\n }\n resume();\n }\n function onChanged() {\n if (!enableWrite)\n return;\n write(read(), true);\n }\n const listenerOptions = { passive: true };\n useEventListener(window, \"popstate\", onChanged, listenerOptions);\n if (mode !== \"history\")\n useEventListener(window, \"hashchange\", onChanged, listenerOptions);\n const initial = read();\n if (initial.keys().next().value)\n updateState(initial);\n else\n Object.assign(state, initialValue);\n return state;\n}\n\nfunction useUserMedia(options = {}) {\n var _a, _b;\n const enabled = shallowRef((_a = options.enabled) != null ? _a : false);\n const autoSwitch = shallowRef((_b = options.autoSwitch) != null ? _b : true);\n const constraints = ref(options.constraints);\n const { navigator = defaultNavigator } = options;\n const isSupported = useSupported(() => {\n var _a2;\n return (_a2 = navigator == null ? void 0 : navigator.mediaDevices) == null ? void 0 : _a2.getUserMedia;\n });\n const stream = shallowRef();\n function getDeviceOptions(type) {\n switch (type) {\n case \"video\": {\n if (constraints.value)\n return constraints.value.video || false;\n break;\n }\n case \"audio\": {\n if (constraints.value)\n return constraints.value.audio || false;\n break;\n }\n }\n }\n async function _start() {\n if (!isSupported.value || stream.value)\n return;\n stream.value = await navigator.mediaDevices.getUserMedia({\n video: getDeviceOptions(\"video\"),\n audio: getDeviceOptions(\"audio\")\n });\n return stream.value;\n }\n function _stop() {\n var _a2;\n (_a2 = stream.value) == null ? void 0 : _a2.getTracks().forEach((t) => t.stop());\n stream.value = void 0;\n }\n function stop() {\n _stop();\n enabled.value = false;\n }\n async function start() {\n await _start();\n if (stream.value)\n enabled.value = true;\n return stream.value;\n }\n async function restart() {\n _stop();\n return await start();\n }\n watch(\n enabled,\n (v) => {\n if (v)\n _start();\n else _stop();\n },\n { immediate: true }\n );\n watch(\n constraints,\n () => {\n if (autoSwitch.value && stream.value)\n restart();\n },\n { immediate: true }\n );\n tryOnScopeDispose(() => {\n stop();\n });\n return {\n isSupported,\n stream,\n start,\n stop,\n restart,\n constraints,\n enabled,\n autoSwitch\n };\n}\n\nfunction useVModel(props, key, emit, options = {}) {\n var _a, _b, _c;\n const {\n clone = false,\n passive = false,\n eventName,\n deep = false,\n defaultValue,\n shouldEmit\n } = options;\n const vm = getCurrentInstance();\n const _emit = emit || (vm == null ? void 0 : vm.emit) || ((_a = vm == null ? void 0 : vm.$emit) == null ? void 0 : _a.bind(vm)) || ((_c = (_b = vm == null ? void 0 : vm.proxy) == null ? void 0 : _b.$emit) == null ? void 0 : _c.bind(vm == null ? void 0 : vm.proxy));\n let event = eventName;\n if (!key) {\n key = \"modelValue\";\n }\n event = event || `update:${key.toString()}`;\n const cloneFn = (val) => !clone ? val : typeof clone === \"function\" ? clone(val) : cloneFnJSON(val);\n const getValue = () => isDef(props[key]) ? cloneFn(props[key]) : defaultValue;\n const triggerEmit = (value) => {\n if (shouldEmit) {\n if (shouldEmit(value))\n _emit(event, value);\n } else {\n _emit(event, value);\n }\n };\n if (passive) {\n const initialValue = getValue();\n const proxy = ref(initialValue);\n let isUpdating = false;\n watch(\n () => props[key],\n (v) => {\n if (!isUpdating) {\n isUpdating = true;\n proxy.value = cloneFn(v);\n nextTick(() => isUpdating = false);\n }\n }\n );\n watch(\n proxy,\n (v) => {\n if (!isUpdating && (v !== props[key] || deep))\n triggerEmit(v);\n },\n { deep }\n );\n return proxy;\n } else {\n return computed({\n get() {\n return getValue();\n },\n set(value) {\n triggerEmit(value);\n }\n });\n }\n}\n\nfunction useVModels(props, emit, options = {}) {\n const ret = {};\n for (const key in props) {\n ret[key] = useVModel(\n props,\n key,\n emit,\n options\n );\n }\n return ret;\n}\n\nfunction useVibrate(options) {\n const {\n pattern = [],\n interval = 0,\n navigator = defaultNavigator\n } = options || {};\n const isSupported = useSupported(() => typeof navigator !== \"undefined\" && \"vibrate\" in navigator);\n const patternRef = toRef(pattern);\n let intervalControls;\n const vibrate = (pattern2 = patternRef.value) => {\n if (isSupported.value)\n navigator.vibrate(pattern2);\n };\n const stop = () => {\n if (isSupported.value)\n navigator.vibrate(0);\n intervalControls == null ? void 0 : intervalControls.pause();\n };\n if (interval > 0) {\n intervalControls = useIntervalFn(\n vibrate,\n interval,\n {\n immediate: false,\n immediateCallback: false\n }\n );\n }\n return {\n isSupported,\n pattern,\n intervalControls,\n vibrate,\n stop\n };\n}\n\nfunction useVirtualList(list, options) {\n const { containerStyle, wrapperProps, scrollTo, calculateRange, currentList, containerRef } = \"itemHeight\" in options ? useVerticalVirtualList(options, list) : useHorizontalVirtualList(options, list);\n return {\n list: currentList,\n scrollTo,\n containerProps: {\n ref: containerRef,\n onScroll: () => {\n calculateRange();\n },\n style: containerStyle\n },\n wrapperProps\n };\n}\nfunction useVirtualListResources(list) {\n const containerRef = shallowRef(null);\n const size = useElementSize(containerRef);\n const currentList = ref([]);\n const source = shallowRef(list);\n const state = ref({ start: 0, end: 10 });\n return { state, source, currentList, size, containerRef };\n}\nfunction createGetViewCapacity(state, source, itemSize) {\n return (containerSize) => {\n if (typeof itemSize === \"number\")\n return Math.ceil(containerSize / itemSize);\n const { start = 0 } = state.value;\n let sum = 0;\n let capacity = 0;\n for (let i = start; i < source.value.length; i++) {\n const size = itemSize(i);\n sum += size;\n capacity = i;\n if (sum > containerSize)\n break;\n }\n return capacity - start;\n };\n}\nfunction createGetOffset(source, itemSize) {\n return (scrollDirection) => {\n if (typeof itemSize === \"number\")\n return Math.floor(scrollDirection / itemSize) + 1;\n let sum = 0;\n let offset = 0;\n for (let i = 0; i < source.value.length; i++) {\n const size = itemSize(i);\n sum += size;\n if (sum >= scrollDirection) {\n offset = i;\n break;\n }\n }\n return offset + 1;\n };\n}\nfunction createCalculateRange(type, overscan, getOffset, getViewCapacity, { containerRef, state, currentList, source }) {\n return () => {\n const element = containerRef.value;\n if (element) {\n const offset = getOffset(type === \"vertical\" ? element.scrollTop : element.scrollLeft);\n const viewCapacity = getViewCapacity(type === \"vertical\" ? element.clientHeight : element.clientWidth);\n const from = offset - overscan;\n const to = offset + viewCapacity + overscan;\n state.value = {\n start: from < 0 ? 0 : from,\n end: to > source.value.length ? source.value.length : to\n };\n currentList.value = source.value.slice(state.value.start, state.value.end).map((ele, index) => ({\n data: ele,\n index: index + state.value.start\n }));\n }\n };\n}\nfunction createGetDistance(itemSize, source) {\n return (index) => {\n if (typeof itemSize === \"number\") {\n const size2 = index * itemSize;\n return size2;\n }\n const size = source.value.slice(0, index).reduce((sum, _, i) => sum + itemSize(i), 0);\n return size;\n };\n}\nfunction useWatchForSizes(size, list, containerRef, calculateRange) {\n watch([size.width, size.height, list, containerRef], () => {\n calculateRange();\n });\n}\nfunction createComputedTotalSize(itemSize, source) {\n return computed(() => {\n if (typeof itemSize === \"number\")\n return source.value.length * itemSize;\n return source.value.reduce((sum, _, index) => sum + itemSize(index), 0);\n });\n}\nconst scrollToDictionaryForElementScrollKey = {\n horizontal: \"scrollLeft\",\n vertical: \"scrollTop\"\n};\nfunction createScrollTo(type, calculateRange, getDistance, containerRef) {\n return (index) => {\n if (containerRef.value) {\n containerRef.value[scrollToDictionaryForElementScrollKey[type]] = getDistance(index);\n calculateRange();\n }\n };\n}\nfunction useHorizontalVirtualList(options, list) {\n const resources = useVirtualListResources(list);\n const { state, source, currentList, size, containerRef } = resources;\n const containerStyle = { overflowX: \"auto\" };\n const { itemWidth, overscan = 5 } = options;\n const getViewCapacity = createGetViewCapacity(state, source, itemWidth);\n const getOffset = createGetOffset(source, itemWidth);\n const calculateRange = createCalculateRange(\"horizontal\", overscan, getOffset, getViewCapacity, resources);\n const getDistanceLeft = createGetDistance(itemWidth, source);\n const offsetLeft = computed(() => getDistanceLeft(state.value.start));\n const totalWidth = createComputedTotalSize(itemWidth, source);\n useWatchForSizes(size, list, containerRef, calculateRange);\n const scrollTo = createScrollTo(\"horizontal\", calculateRange, getDistanceLeft, containerRef);\n const wrapperProps = computed(() => {\n return {\n style: {\n height: \"100%\",\n width: `${totalWidth.value - offsetLeft.value}px`,\n marginLeft: `${offsetLeft.value}px`,\n display: \"flex\"\n }\n };\n });\n return {\n scrollTo,\n calculateRange,\n wrapperProps,\n containerStyle,\n currentList,\n containerRef\n };\n}\nfunction useVerticalVirtualList(options, list) {\n const resources = useVirtualListResources(list);\n const { state, source, currentList, size, containerRef } = resources;\n const containerStyle = { overflowY: \"auto\" };\n const { itemHeight, overscan = 5 } = options;\n const getViewCapacity = createGetViewCapacity(state, source, itemHeight);\n const getOffset = createGetOffset(source, itemHeight);\n const calculateRange = createCalculateRange(\"vertical\", overscan, getOffset, getViewCapacity, resources);\n const getDistanceTop = createGetDistance(itemHeight, source);\n const offsetTop = computed(() => getDistanceTop(state.value.start));\n const totalHeight = createComputedTotalSize(itemHeight, source);\n useWatchForSizes(size, list, containerRef, calculateRange);\n const scrollTo = createScrollTo(\"vertical\", calculateRange, getDistanceTop, containerRef);\n const wrapperProps = computed(() => {\n return {\n style: {\n width: \"100%\",\n height: `${totalHeight.value - offsetTop.value}px`,\n marginTop: `${offsetTop.value}px`\n }\n };\n });\n return {\n calculateRange,\n scrollTo,\n containerStyle,\n wrapperProps,\n currentList,\n containerRef\n };\n}\n\nfunction useWakeLock(options = {}) {\n const {\n navigator = defaultNavigator,\n document = defaultDocument\n } = options;\n const requestedType = shallowRef(false);\n const sentinel = shallowRef(null);\n const documentVisibility = useDocumentVisibility({ document });\n const isSupported = useSupported(() => navigator && \"wakeLock\" in navigator);\n const isActive = computed(() => !!sentinel.value && documentVisibility.value === \"visible\");\n if (isSupported.value) {\n useEventListener(sentinel, \"release\", () => {\n var _a, _b;\n requestedType.value = (_b = (_a = sentinel.value) == null ? void 0 : _a.type) != null ? _b : false;\n }, { passive: true });\n whenever(\n () => documentVisibility.value === \"visible\" && (document == null ? void 0 : document.visibilityState) === \"visible\" && requestedType.value,\n (type) => {\n requestedType.value = false;\n forceRequest(type);\n }\n );\n }\n async function forceRequest(type) {\n var _a;\n await ((_a = sentinel.value) == null ? void 0 : _a.release());\n sentinel.value = isSupported.value ? await navigator.wakeLock.request(type) : null;\n }\n async function request(type) {\n if (documentVisibility.value === \"visible\")\n await forceRequest(type);\n else\n requestedType.value = type;\n }\n async function release() {\n requestedType.value = false;\n const s = sentinel.value;\n sentinel.value = null;\n await (s == null ? void 0 : s.release());\n }\n return {\n sentinel,\n isSupported,\n isActive,\n request,\n forceRequest,\n release\n };\n}\n\nfunction useWebNotification(options = {}) {\n const {\n window = defaultWindow,\n requestPermissions: _requestForPermissions = true\n } = options;\n const defaultWebNotificationOptions = options;\n const isSupported = useSupported(() => {\n if (!window || !(\"Notification\" in window))\n return false;\n if (Notification.permission === \"granted\")\n return true;\n try {\n const notification2 = new Notification(\"\");\n notification2.onshow = () => {\n notification2.close();\n };\n } catch (e) {\n if (e.name === \"TypeError\")\n return false;\n }\n return true;\n });\n const permissionGranted = shallowRef(isSupported.value && \"permission\" in Notification && Notification.permission === \"granted\");\n const notification = ref(null);\n const ensurePermissions = async () => {\n if (!isSupported.value)\n return;\n if (!permissionGranted.value && Notification.permission !== \"denied\") {\n const result = await Notification.requestPermission();\n if (result === \"granted\")\n permissionGranted.value = true;\n }\n return permissionGranted.value;\n };\n const { on: onClick, trigger: clickTrigger } = createEventHook();\n const { on: onShow, trigger: showTrigger } = createEventHook();\n const { on: onError, trigger: errorTrigger } = createEventHook();\n const { on: onClose, trigger: closeTrigger } = createEventHook();\n const show = async (overrides) => {\n if (!isSupported.value || !permissionGranted.value)\n return;\n const options2 = Object.assign({}, defaultWebNotificationOptions, overrides);\n notification.value = new Notification(options2.title || \"\", options2);\n notification.value.onclick = clickTrigger;\n notification.value.onshow = showTrigger;\n notification.value.onerror = errorTrigger;\n notification.value.onclose = closeTrigger;\n return notification.value;\n };\n const close = () => {\n if (notification.value)\n notification.value.close();\n notification.value = null;\n };\n if (_requestForPermissions)\n tryOnMounted(ensurePermissions);\n tryOnScopeDispose(close);\n if (isSupported.value && window) {\n const document = window.document;\n useEventListener(document, \"visibilitychange\", (e) => {\n e.preventDefault();\n if (document.visibilityState === \"visible\") {\n close();\n }\n });\n }\n return {\n isSupported,\n notification,\n ensurePermissions,\n permissionGranted,\n show,\n close,\n onClick,\n onShow,\n onError,\n onClose\n };\n}\n\nconst DEFAULT_PING_MESSAGE = \"ping\";\nfunction resolveNestedOptions(options) {\n if (options === true)\n return {};\n return options;\n}\nfunction useWebSocket(url, options = {}) {\n const {\n onConnected,\n onDisconnected,\n onError,\n onMessage,\n immediate = true,\n autoConnect = true,\n autoClose = true,\n protocols = []\n } = options;\n const data = ref(null);\n const status = shallowRef(\"CLOSED\");\n const wsRef = ref();\n const urlRef = toRef(url);\n let heartbeatPause;\n let heartbeatResume;\n let explicitlyClosed = false;\n let retried = 0;\n let bufferedData = [];\n let retryTimeout;\n let pongTimeoutWait;\n const _sendBuffer = () => {\n if (bufferedData.length && wsRef.value && status.value === \"OPEN\") {\n for (const buffer of bufferedData)\n wsRef.value.send(buffer);\n bufferedData = [];\n }\n };\n const resetRetry = () => {\n if (retryTimeout != null) {\n clearTimeout(retryTimeout);\n retryTimeout = void 0;\n }\n };\n const resetHeartbeat = () => {\n clearTimeout(pongTimeoutWait);\n pongTimeoutWait = void 0;\n };\n const close = (code = 1e3, reason) => {\n resetRetry();\n if (!isClient && !isWorker || !wsRef.value)\n return;\n explicitlyClosed = true;\n resetHeartbeat();\n heartbeatPause == null ? void 0 : heartbeatPause();\n wsRef.value.close(code, reason);\n wsRef.value = void 0;\n };\n const send = (data2, useBuffer = true) => {\n if (!wsRef.value || status.value !== \"OPEN\") {\n if (useBuffer)\n bufferedData.push(data2);\n return false;\n }\n _sendBuffer();\n wsRef.value.send(data2);\n return true;\n };\n const _init = () => {\n if (explicitlyClosed || typeof urlRef.value === \"undefined\")\n return;\n const ws = new WebSocket(urlRef.value, protocols);\n wsRef.value = ws;\n status.value = \"CONNECTING\";\n ws.onopen = () => {\n status.value = \"OPEN\";\n retried = 0;\n onConnected == null ? void 0 : onConnected(ws);\n heartbeatResume == null ? void 0 : heartbeatResume();\n _sendBuffer();\n };\n ws.onclose = (ev) => {\n status.value = \"CLOSED\";\n resetHeartbeat();\n heartbeatPause == null ? void 0 : heartbeatPause();\n onDisconnected == null ? void 0 : onDisconnected(ws, ev);\n if (!explicitlyClosed && options.autoReconnect && (wsRef.value == null || ws === wsRef.value)) {\n const {\n retries = -1,\n delay = 1e3,\n onFailed\n } = resolveNestedOptions(options.autoReconnect);\n const checkRetires = typeof retries === \"function\" ? retries : () => typeof retries === \"number\" && (retries < 0 || retried < retries);\n if (checkRetires(retried)) {\n retried += 1;\n retryTimeout = setTimeout(_init, delay);\n } else {\n onFailed == null ? void 0 : onFailed();\n }\n }\n };\n ws.onerror = (e) => {\n onError == null ? void 0 : onError(ws, e);\n };\n ws.onmessage = (e) => {\n if (options.heartbeat) {\n resetHeartbeat();\n const {\n message = DEFAULT_PING_MESSAGE,\n responseMessage = message\n } = resolveNestedOptions(options.heartbeat);\n if (e.data === toValue(responseMessage))\n return;\n }\n data.value = e.data;\n onMessage == null ? void 0 : onMessage(ws, e);\n };\n };\n if (options.heartbeat) {\n const {\n message = DEFAULT_PING_MESSAGE,\n interval = 1e3,\n pongTimeout = 1e3\n } = resolveNestedOptions(options.heartbeat);\n const { pause, resume } = useIntervalFn(\n () => {\n send(toValue(message), false);\n if (pongTimeoutWait != null)\n return;\n pongTimeoutWait = setTimeout(() => {\n close();\n explicitlyClosed = false;\n }, pongTimeout);\n },\n interval,\n { immediate: false }\n );\n heartbeatPause = pause;\n heartbeatResume = resume;\n }\n if (autoClose) {\n if (isClient)\n useEventListener(\"beforeunload\", () => close(), { passive: true });\n tryOnScopeDispose(close);\n }\n const open = () => {\n if (!isClient && !isWorker)\n return;\n close();\n explicitlyClosed = false;\n retried = 0;\n _init();\n };\n if (immediate)\n open();\n if (autoConnect)\n watch(urlRef, open);\n return {\n data,\n status,\n close,\n send,\n open,\n ws: wsRef\n };\n}\n\nfunction useWebWorker(arg0, workerOptions, options) {\n const {\n window = defaultWindow\n } = options != null ? options : {};\n const data = ref(null);\n const worker = shallowRef();\n const post = (...args) => {\n if (!worker.value)\n return;\n worker.value.postMessage(...args);\n };\n const terminate = function terminate2() {\n if (!worker.value)\n return;\n worker.value.terminate();\n };\n if (window) {\n if (typeof arg0 === \"string\")\n worker.value = new Worker(arg0, workerOptions);\n else if (typeof arg0 === \"function\")\n worker.value = arg0();\n else\n worker.value = arg0;\n worker.value.onmessage = (e) => {\n data.value = e.data;\n };\n tryOnScopeDispose(() => {\n if (worker.value)\n worker.value.terminate();\n });\n }\n return {\n data,\n post,\n terminate,\n worker\n };\n}\n\nfunction depsParser(deps, localDeps) {\n if (deps.length === 0 && localDeps.length === 0)\n return \"\";\n const depsString = deps.map((dep) => `'${dep}'`).toString();\n const depsFunctionString = localDeps.filter((dep) => typeof dep === \"function\").map((fn) => {\n const str = fn.toString();\n if (str.trim().startsWith(\"function\")) {\n return str;\n } else {\n const name = fn.name;\n return `const ${name} = ${str}`;\n }\n }).join(\";\");\n const importString = `importScripts(${depsString});`;\n return `${depsString.trim() === \"\" ? \"\" : importString} ${depsFunctionString}`;\n}\n\nfunction jobRunner(userFunc) {\n return (e) => {\n const userFuncArgs = e.data[0];\n return Promise.resolve(userFunc.apply(void 0, userFuncArgs)).then((result) => {\n postMessage([\"SUCCESS\", result]);\n }).catch((error) => {\n postMessage([\"ERROR\", error]);\n });\n };\n}\n\nfunction createWorkerBlobUrl(fn, deps, localDeps) {\n const blobCode = `${depsParser(deps, localDeps)}; onmessage=(${jobRunner})(${fn})`;\n const blob = new Blob([blobCode], { type: \"text/javascript\" });\n const url = URL.createObjectURL(blob);\n return url;\n}\n\nfunction useWebWorkerFn(fn, options = {}) {\n const {\n dependencies = [],\n localDependencies = [],\n timeout,\n window = defaultWindow\n } = options;\n const worker = ref();\n const workerStatus = shallowRef(\"PENDING\");\n const promise = ref({});\n const timeoutId = shallowRef();\n const workerTerminate = (status = \"PENDING\") => {\n if (worker.value && worker.value._url && window) {\n worker.value.terminate();\n URL.revokeObjectURL(worker.value._url);\n promise.value = {};\n worker.value = void 0;\n window.clearTimeout(timeoutId.value);\n workerStatus.value = status;\n }\n };\n workerTerminate();\n tryOnScopeDispose(workerTerminate);\n const generateWorker = () => {\n const blobUrl = createWorkerBlobUrl(fn, dependencies, localDependencies);\n const newWorker = new Worker(blobUrl);\n newWorker._url = blobUrl;\n newWorker.onmessage = (e) => {\n const { resolve = () => {\n }, reject = () => {\n } } = promise.value;\n const [status, result] = e.data;\n switch (status) {\n case \"SUCCESS\":\n resolve(result);\n workerTerminate(status);\n break;\n default:\n reject(result);\n workerTerminate(\"ERROR\");\n break;\n }\n };\n newWorker.onerror = (e) => {\n const { reject = () => {\n } } = promise.value;\n e.preventDefault();\n reject(e);\n workerTerminate(\"ERROR\");\n };\n if (timeout) {\n timeoutId.value = setTimeout(\n () => workerTerminate(\"TIMEOUT_EXPIRED\"),\n timeout\n );\n }\n return newWorker;\n };\n const callWorker = (...fnArgs) => new Promise((resolve, reject) => {\n var _a;\n promise.value = {\n resolve,\n reject\n };\n (_a = worker.value) == null ? void 0 : _a.postMessage([[...fnArgs]]);\n workerStatus.value = \"RUNNING\";\n });\n const workerFn = (...fnArgs) => {\n if (workerStatus.value === \"RUNNING\") {\n console.error(\n \"[useWebWorkerFn] You can only run one instance of the worker at a time.\"\n );\n return Promise.reject();\n }\n worker.value = generateWorker();\n return callWorker(...fnArgs);\n };\n return {\n workerFn,\n workerStatus,\n workerTerminate\n };\n}\n\nfunction useWindowFocus(options = {}) {\n const { window = defaultWindow } = options;\n if (!window)\n return shallowRef(false);\n const focused = shallowRef(window.document.hasFocus());\n const listenerOptions = { passive: true };\n useEventListener(window, \"blur\", () => {\n focused.value = false;\n }, listenerOptions);\n useEventListener(window, \"focus\", () => {\n focused.value = true;\n }, listenerOptions);\n return focused;\n}\n\nfunction useWindowScroll(options = {}) {\n const { window = defaultWindow, ...rest } = options;\n return useScroll(window, rest);\n}\n\nfunction useWindowSize(options = {}) {\n const {\n window = defaultWindow,\n initialWidth = Number.POSITIVE_INFINITY,\n initialHeight = Number.POSITIVE_INFINITY,\n listenOrientation = true,\n includeScrollbar = true,\n type = \"inner\"\n } = options;\n const width = shallowRef(initialWidth);\n const height = shallowRef(initialHeight);\n const update = () => {\n if (window) {\n if (type === \"outer\") {\n width.value = window.outerWidth;\n height.value = window.outerHeight;\n } else if (type === \"visual\" && window.visualViewport) {\n const { width: visualViewportWidth, height: visualViewportHeight, scale } = window.visualViewport;\n width.value = Math.round(visualViewportWidth * scale);\n height.value = Math.round(visualViewportHeight * scale);\n } else if (includeScrollbar) {\n width.value = window.innerWidth;\n height.value = window.innerHeight;\n } else {\n width.value = window.document.documentElement.clientWidth;\n height.value = window.document.documentElement.clientHeight;\n }\n }\n };\n update();\n tryOnMounted(update);\n const listenerOptions = { passive: true };\n useEventListener(\"resize\", update, listenerOptions);\n if (window && type === \"visual\" && window.visualViewport) {\n useEventListener(window.visualViewport, \"resize\", update, listenerOptions);\n }\n if (listenOrientation) {\n const matches = useMediaQuery(\"(orientation: portrait)\");\n watch(matches, () => update());\n }\n return { width, height };\n}\n\nexport { DefaultMagicKeysAliasMap, StorageSerializers, TransitionPresets, computedAsync as asyncComputed, breakpointsAntDesign, breakpointsBootstrapV5, breakpointsElement, breakpointsMasterCss, breakpointsPrimeFlex, breakpointsQuasar, breakpointsSematic, breakpointsTailwind, breakpointsVuetify, breakpointsVuetifyV2, breakpointsVuetifyV3, cloneFnJSON, computedAsync, computedInject, createFetch, createReusableTemplate, createTemplatePromise, createUnrefFn, customStorageEventName, defaultDocument, defaultLocation, defaultNavigator, defaultWindow, executeTransition, formatTimeAgo, getSSRHandler, mapGamepadToXbox360Controller, onClickOutside, onElementRemoval, onKeyDown, onKeyPressed, onKeyStroke, onKeyUp, onLongPress, onStartTyping, provideSSRWidth, setSSRHandler, templateRef, unrefElement, useActiveElement, useAnimate, useAsyncQueue, useAsyncState, useBase64, useBattery, useBluetooth, useBreakpoints, useBroadcastChannel, useBrowserLocation, useCached, useClipboard, useClipboardItems, useCloned, useColorMode, useConfirmDialog, useCountdown, useCssVar, useCurrentElement, useCycleList, useDark, useDebouncedRefHistory, useDeviceMotion, useDeviceOrientation, useDevicePixelRatio, useDevicesList, useDisplayMedia, useDocumentVisibility, useDraggable, useDropZone, useElementBounding, useElementByPoint, useElementHover, useElementSize, useElementVisibility, useEventBus, useEventListener, useEventSource, useEyeDropper, useFavicon, useFetch, useFileDialog, useFileSystemAccess, useFocus, useFocusWithin, useFps, useFullscreen, useGamepad, useGeolocation, useIdle, useImage, useInfiniteScroll, useIntersectionObserver, useKeyModifier, useLocalStorage, useMagicKeys, useManualRefHistory, useMediaControls, useMediaQuery, useMemoize, useMemory, useMounted, useMouse, useMouseInElement, useMousePressed, useMutationObserver, useNavigatorLanguage, useNetwork, useNow, useObjectUrl, useOffsetPagination, useOnline, usePageLeave, useParallax, useParentElement, usePerformanceObserver, usePermission, usePointer, usePointerLock, usePointerSwipe, usePreferredColorScheme, usePreferredContrast, usePreferredDark, usePreferredLanguages, usePreferredReducedMotion, usePreferredReducedTransparency, usePrevious, useRafFn, useRefHistory, useResizeObserver, useSSRWidth, useScreenOrientation, useScreenSafeArea, useScriptTag, useScroll, useScrollLock, useSessionStorage, useShare, useSorted, useSpeechRecognition, useSpeechSynthesis, useStepper, useStorage, useStorageAsync, useStyleTag, useSupported, useSwipe, useTemplateRefsList, useTextDirection, useTextSelection, useTextareaAutosize, useThrottledRefHistory, useTimeAgo, useTimeoutPoll, useTimestamp, useTitle, useTransition, useUrlSearchParams, useUserMedia, useVModel, useVModels, useVibrate, useVirtualList, useWakeLock, useWebNotification, useWebSocket, useWebWorker, useWebWorkerFn, useWindowFocus, useWindowScroll, useWindowSize };\n"], - "mappings": "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", - "names": ["get", "set", "ref", "keys", "invoke", "toRef", "toRefs", "toValue", "window", "document", "timestamp", "navigator", "events", "getValue", "ref", "defaults", "toRef", "set", "onUpdated", "preventDefault", "toRefs", "now", "keys", "get", "isReadonly"] -} diff --git a/www/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js b/www/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js deleted file mode 100644 index 42925ac..0000000 --- a/www/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js +++ /dev/null @@ -1,12824 +0,0 @@ -// node_modules/@vue/shared/dist/shared.esm-bundler.js -function makeMap(str) { - const map2 = /* @__PURE__ */ Object.create(null); - for (const key of str.split(",")) map2[key] = 1; - return (val) => val in map2; -} -var EMPTY_OBJ = true ? Object.freeze({}) : {}; -var EMPTY_ARR = true ? Object.freeze([]) : []; -var NOOP = () => { -}; -var NO = () => false; -var isOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && // uppercase letter -(key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97); -var isModelListener = (key) => key.startsWith("onUpdate:"); -var extend = Object.assign; -var remove = (arr, el) => { - const i = arr.indexOf(el); - if (i > -1) { - arr.splice(i, 1); - } -}; -var hasOwnProperty = Object.prototype.hasOwnProperty; -var hasOwn = (val, key) => hasOwnProperty.call(val, key); -var isArray = Array.isArray; -var isMap = (val) => toTypeString(val) === "[object Map]"; -var isSet = (val) => toTypeString(val) === "[object Set]"; -var isDate = (val) => toTypeString(val) === "[object Date]"; -var isRegExp = (val) => toTypeString(val) === "[object RegExp]"; -var isFunction = (val) => typeof val === "function"; -var isString = (val) => typeof val === "string"; -var isSymbol = (val) => typeof val === "symbol"; -var isObject = (val) => val !== null && typeof val === "object"; -var isPromise = (val) => { - return (isObject(val) || isFunction(val)) && isFunction(val.then) && isFunction(val.catch); -}; -var objectToString = Object.prototype.toString; -var toTypeString = (value) => objectToString.call(value); -var toRawType = (value) => { - return toTypeString(value).slice(8, -1); -}; -var isPlainObject = (val) => toTypeString(val) === "[object Object]"; -var isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; -var isReservedProp = makeMap( - // the leading comma is intentional so empty string "" is also included - ",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted" -); -var isBuiltInDirective = makeMap( - "bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo" -); -var cacheStringFunction = (fn) => { - const cache = /* @__PURE__ */ Object.create(null); - return (str) => { - const hit = cache[str]; - return hit || (cache[str] = fn(str)); - }; -}; -var camelizeRE = /-\w/g; -var camelize = cacheStringFunction( - (str) => { - return str.replace(camelizeRE, (c) => c.slice(1).toUpperCase()); - } -); -var hyphenateRE = /\B([A-Z])/g; -var hyphenate = cacheStringFunction( - (str) => str.replace(hyphenateRE, "-$1").toLowerCase() -); -var capitalize = cacheStringFunction((str) => { - return str.charAt(0).toUpperCase() + str.slice(1); -}); -var toHandlerKey = cacheStringFunction( - (str) => { - const s = str ? `on${capitalize(str)}` : ``; - return s; - } -); -var hasChanged = (value, oldValue) => !Object.is(value, oldValue); -var invokeArrayFns = (fns, ...arg) => { - for (let i = 0; i < fns.length; i++) { - fns[i](...arg); - } -}; -var def = (obj, key, value, writable = false) => { - Object.defineProperty(obj, key, { - configurable: true, - enumerable: false, - writable, - value - }); -}; -var looseToNumber = (val) => { - const n = parseFloat(val); - return isNaN(n) ? val : n; -}; -var toNumber = (val) => { - const n = isString(val) ? Number(val) : NaN; - return isNaN(n) ? val : n; -}; -var _globalThis; -var getGlobalThis = () => { - return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); -}; -var GLOBALS_ALLOWED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error,Symbol"; -var isGloballyAllowed = makeMap(GLOBALS_ALLOWED); -function normalizeStyle(value) { - if (isArray(value)) { - const res = {}; - for (let i = 0; i < value.length; i++) { - const item = value[i]; - const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item); - if (normalized) { - for (const key in normalized) { - res[key] = normalized[key]; - } - } - } - return res; - } else if (isString(value) || isObject(value)) { - return value; - } -} -var listDelimiterRE = /;(?![^(]*\))/g; -var propertyDelimiterRE = /:([^]+)/; -var styleCommentRE = /\/\*[^]*?\*\//g; -function parseStringStyle(cssText) { - const ret = {}; - cssText.replace(styleCommentRE, "").split(listDelimiterRE).forEach((item) => { - if (item) { - const tmp = item.split(propertyDelimiterRE); - tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); - } - }); - return ret; -} -function stringifyStyle(styles) { - if (!styles) return ""; - if (isString(styles)) return styles; - let ret = ""; - for (const key in styles) { - const value = styles[key]; - if (isString(value) || typeof value === "number") { - const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key); - ret += `${normalizedKey}:${value};`; - } - } - return ret; -} -function normalizeClass(value) { - let res = ""; - if (isString(value)) { - res = value; - } else if (isArray(value)) { - for (let i = 0; i < value.length; i++) { - const normalized = normalizeClass(value[i]); - if (normalized) { - res += normalized + " "; - } - } - } else if (isObject(value)) { - for (const name in value) { - if (value[name]) { - res += name + " "; - } - } - } - return res.trim(); -} -function normalizeProps(props) { - if (!props) return null; - let { class: klass, style } = props; - if (klass && !isString(klass)) { - props.class = normalizeClass(klass); - } - if (style) { - props.style = normalizeStyle(style); - } - return props; -} -var HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot"; -var SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view"; -var MATH_TAGS = "annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics"; -var VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr"; -var isHTMLTag = makeMap(HTML_TAGS); -var isSVGTag = makeMap(SVG_TAGS); -var isMathMLTag = makeMap(MATH_TAGS); -var isVoidTag = makeMap(VOID_TAGS); -var specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; -var isSpecialBooleanAttr = makeMap(specialBooleanAttrs); -var isBooleanAttr = makeMap( - specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected` -); -function includeBooleanAttr(value) { - return !!value || value === ""; -} -var isKnownHtmlAttr = makeMap( - `accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,inert,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap` -); -var isKnownSvgAttr = makeMap( - `xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xmlns:xlink,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan` -); -var isKnownMathMLAttr = makeMap( - `accent,accentunder,actiontype,align,alignmentscope,altimg,altimg-height,altimg-valign,altimg-width,alttext,bevelled,close,columnsalign,columnlines,columnspan,denomalign,depth,dir,display,displaystyle,encoding,equalcolumns,equalrows,fence,fontstyle,fontweight,form,frame,framespacing,groupalign,height,href,id,indentalign,indentalignfirst,indentalignlast,indentshift,indentshiftfirst,indentshiftlast,indextype,justify,largetop,largeop,lquote,lspace,mathbackground,mathcolor,mathsize,mathvariant,maxsize,minlabelspacing,mode,other,overflow,position,rowalign,rowlines,rowspan,rquote,rspace,scriptlevel,scriptminsize,scriptsizemultiplier,selection,separator,separators,shift,side,src,stackalign,stretchy,subscriptshift,superscriptshift,symmetric,voffset,width,widths,xlink:href,xlink:show,xlink:type,xmlns` -); -function isRenderableAttrValue(value) { - if (value == null) { - return false; - } - const type = typeof value; - return type === "string" || type === "number" || type === "boolean"; -} -var cssVarNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g; -function getEscapedCssVarName(key, doubleEscape) { - return key.replace( - cssVarNameEscapeSymbolsRE, - (s) => doubleEscape ? s === '"' ? '\\\\\\"' : `\\\\${s}` : `\\${s}` - ); -} -function looseCompareArrays(a, b) { - if (a.length !== b.length) return false; - let equal = true; - for (let i = 0; equal && i < a.length; i++) { - equal = looseEqual(a[i], b[i]); - } - return equal; -} -function looseEqual(a, b) { - if (a === b) return true; - let aValidType = isDate(a); - let bValidType = isDate(b); - if (aValidType || bValidType) { - return aValidType && bValidType ? a.getTime() === b.getTime() : false; - } - aValidType = isSymbol(a); - bValidType = isSymbol(b); - if (aValidType || bValidType) { - return a === b; - } - aValidType = isArray(a); - bValidType = isArray(b); - if (aValidType || bValidType) { - return aValidType && bValidType ? looseCompareArrays(a, b) : false; - } - aValidType = isObject(a); - bValidType = isObject(b); - if (aValidType || bValidType) { - if (!aValidType || !bValidType) { - return false; - } - const aKeysCount = Object.keys(a).length; - const bKeysCount = Object.keys(b).length; - if (aKeysCount !== bKeysCount) { - return false; - } - for (const key in a) { - const aHasKey = a.hasOwnProperty(key); - const bHasKey = b.hasOwnProperty(key); - if (aHasKey && !bHasKey || !aHasKey && bHasKey || !looseEqual(a[key], b[key])) { - return false; - } - } - } - return String(a) === String(b); -} -function looseIndexOf(arr, val) { - return arr.findIndex((item) => looseEqual(item, val)); -} -var isRef = (val) => { - return !!(val && val["__v_isRef"] === true); -}; -var toDisplayString = (val) => { - return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? isRef(val) ? toDisplayString(val.value) : JSON.stringify(val, replacer, 2) : String(val); -}; -var replacer = (_key, val) => { - if (isRef(val)) { - return replacer(_key, val.value); - } else if (isMap(val)) { - return { - [`Map(${val.size})`]: [...val.entries()].reduce( - (entries, [key, val2], i) => { - entries[stringifySymbol(key, i) + " =>"] = val2; - return entries; - }, - {} - ) - }; - } else if (isSet(val)) { - return { - [`Set(${val.size})`]: [...val.values()].map((v) => stringifySymbol(v)) - }; - } else if (isSymbol(val)) { - return stringifySymbol(val); - } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) { - return String(val); - } - return val; -}; -var stringifySymbol = (v, i = "") => { - var _a; - return ( - // Symbol.description in es2019+ so we need to cast here to pass - // the lib: es2016 check - isSymbol(v) ? `Symbol(${(_a = v.description) != null ? _a : i})` : v - ); -}; -function normalizeCssVarValue(value) { - if (value == null) { - return "initial"; - } - if (typeof value === "string") { - return value === "" ? " " : value; - } - if (typeof value !== "number" || !Number.isFinite(value)) { - if (true) { - console.warn( - "[Vue warn] Invalid value used for CSS binding. Expected a string or a finite number but received:", - value - ); - } - } - return String(value); -} - -// node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js -function warn(msg, ...args) { - console.warn(`[Vue warn] ${msg}`, ...args); -} -var activeEffectScope; -var EffectScope = class { - constructor(detached = false) { - this.detached = detached; - this._active = true; - this._on = 0; - this.effects = []; - this.cleanups = []; - this._isPaused = false; - this.parent = activeEffectScope; - if (!detached && activeEffectScope) { - this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push( - this - ) - 1; - } - } - get active() { - return this._active; - } - pause() { - if (this._active) { - this._isPaused = true; - let i, l; - if (this.scopes) { - for (i = 0, l = this.scopes.length; i < l; i++) { - this.scopes[i].pause(); - } - } - for (i = 0, l = this.effects.length; i < l; i++) { - this.effects[i].pause(); - } - } - } - /** - * Resumes the effect scope, including all child scopes and effects. - */ - resume() { - if (this._active) { - if (this._isPaused) { - this._isPaused = false; - let i, l; - if (this.scopes) { - for (i = 0, l = this.scopes.length; i < l; i++) { - this.scopes[i].resume(); - } - } - for (i = 0, l = this.effects.length; i < l; i++) { - this.effects[i].resume(); - } - } - } - } - run(fn) { - if (this._active) { - const currentEffectScope = activeEffectScope; - try { - activeEffectScope = this; - return fn(); - } finally { - activeEffectScope = currentEffectScope; - } - } else if (true) { - warn(`cannot run an inactive effect scope.`); - } - } - /** - * This should only be called on non-detached scopes - * @internal - */ - on() { - if (++this._on === 1) { - this.prevScope = activeEffectScope; - activeEffectScope = this; - } - } - /** - * This should only be called on non-detached scopes - * @internal - */ - off() { - if (this._on > 0 && --this._on === 0) { - activeEffectScope = this.prevScope; - this.prevScope = void 0; - } - } - stop(fromParent) { - if (this._active) { - this._active = false; - let i, l; - for (i = 0, l = this.effects.length; i < l; i++) { - this.effects[i].stop(); - } - this.effects.length = 0; - for (i = 0, l = this.cleanups.length; i < l; i++) { - this.cleanups[i](); - } - this.cleanups.length = 0; - if (this.scopes) { - for (i = 0, l = this.scopes.length; i < l; i++) { - this.scopes[i].stop(true); - } - this.scopes.length = 0; - } - if (!this.detached && this.parent && !fromParent) { - const last = this.parent.scopes.pop(); - if (last && last !== this) { - this.parent.scopes[this.index] = last; - last.index = this.index; - } - } - this.parent = void 0; - } - } -}; -function effectScope(detached) { - return new EffectScope(detached); -} -function getCurrentScope() { - return activeEffectScope; -} -function onScopeDispose(fn, failSilently = false) { - if (activeEffectScope) { - activeEffectScope.cleanups.push(fn); - } else if (!failSilently) { - warn( - `onScopeDispose() is called when there is no active effect scope to be associated with.` - ); - } -} -var activeSub; -var pausedQueueEffects = /* @__PURE__ */ new WeakSet(); -var ReactiveEffect = class { - constructor(fn) { - this.fn = fn; - this.deps = void 0; - this.depsTail = void 0; - this.flags = 1 | 4; - this.next = void 0; - this.cleanup = void 0; - this.scheduler = void 0; - if (activeEffectScope && activeEffectScope.active) { - activeEffectScope.effects.push(this); - } - } - pause() { - this.flags |= 64; - } - resume() { - if (this.flags & 64) { - this.flags &= -65; - if (pausedQueueEffects.has(this)) { - pausedQueueEffects.delete(this); - this.trigger(); - } - } - } - /** - * @internal - */ - notify() { - if (this.flags & 2 && !(this.flags & 32)) { - return; - } - if (!(this.flags & 8)) { - batch(this); - } - } - run() { - if (!(this.flags & 1)) { - return this.fn(); - } - this.flags |= 2; - cleanupEffect(this); - prepareDeps(this); - const prevEffect = activeSub; - const prevShouldTrack = shouldTrack; - activeSub = this; - shouldTrack = true; - try { - return this.fn(); - } finally { - if (activeSub !== this) { - warn( - "Active effect was not restored correctly - this is likely a Vue internal bug." - ); - } - cleanupDeps(this); - activeSub = prevEffect; - shouldTrack = prevShouldTrack; - this.flags &= -3; - } - } - stop() { - if (this.flags & 1) { - for (let link = this.deps; link; link = link.nextDep) { - removeSub(link); - } - this.deps = this.depsTail = void 0; - cleanupEffect(this); - this.onStop && this.onStop(); - this.flags &= -2; - } - } - trigger() { - if (this.flags & 64) { - pausedQueueEffects.add(this); - } else if (this.scheduler) { - this.scheduler(); - } else { - this.runIfDirty(); - } - } - /** - * @internal - */ - runIfDirty() { - if (isDirty(this)) { - this.run(); - } - } - get dirty() { - return isDirty(this); - } -}; -var batchDepth = 0; -var batchedSub; -var batchedComputed; -function batch(sub, isComputed = false) { - sub.flags |= 8; - if (isComputed) { - sub.next = batchedComputed; - batchedComputed = sub; - return; - } - sub.next = batchedSub; - batchedSub = sub; -} -function startBatch() { - batchDepth++; -} -function endBatch() { - if (--batchDepth > 0) { - return; - } - if (batchedComputed) { - let e = batchedComputed; - batchedComputed = void 0; - while (e) { - const next = e.next; - e.next = void 0; - e.flags &= -9; - e = next; - } - } - let error; - while (batchedSub) { - let e = batchedSub; - batchedSub = void 0; - while (e) { - const next = e.next; - e.next = void 0; - e.flags &= -9; - if (e.flags & 1) { - try { - ; - e.trigger(); - } catch (err) { - if (!error) error = err; - } - } - e = next; - } - } - if (error) throw error; -} -function prepareDeps(sub) { - for (let link = sub.deps; link; link = link.nextDep) { - link.version = -1; - link.prevActiveLink = link.dep.activeLink; - link.dep.activeLink = link; - } -} -function cleanupDeps(sub) { - let head; - let tail = sub.depsTail; - let link = tail; - while (link) { - const prev = link.prevDep; - if (link.version === -1) { - if (link === tail) tail = prev; - removeSub(link); - removeDep(link); - } else { - head = link; - } - link.dep.activeLink = link.prevActiveLink; - link.prevActiveLink = void 0; - link = prev; - } - sub.deps = head; - sub.depsTail = tail; -} -function isDirty(sub) { - for (let link = sub.deps; link; link = link.nextDep) { - if (link.dep.version !== link.version || link.dep.computed && (refreshComputed(link.dep.computed) || link.dep.version !== link.version)) { - return true; - } - } - if (sub._dirty) { - return true; - } - return false; -} -function refreshComputed(computed3) { - if (computed3.flags & 4 && !(computed3.flags & 16)) { - return; - } - computed3.flags &= -17; - if (computed3.globalVersion === globalVersion) { - return; - } - computed3.globalVersion = globalVersion; - if (!computed3.isSSR && computed3.flags & 128 && (!computed3.deps && !computed3._dirty || !isDirty(computed3))) { - return; - } - computed3.flags |= 2; - const dep = computed3.dep; - const prevSub = activeSub; - const prevShouldTrack = shouldTrack; - activeSub = computed3; - shouldTrack = true; - try { - prepareDeps(computed3); - const value = computed3.fn(computed3._value); - if (dep.version === 0 || hasChanged(value, computed3._value)) { - computed3.flags |= 128; - computed3._value = value; - dep.version++; - } - } catch (err) { - dep.version++; - throw err; - } finally { - activeSub = prevSub; - shouldTrack = prevShouldTrack; - cleanupDeps(computed3); - computed3.flags &= -3; - } -} -function removeSub(link, soft = false) { - const { dep, prevSub, nextSub } = link; - if (prevSub) { - prevSub.nextSub = nextSub; - link.prevSub = void 0; - } - if (nextSub) { - nextSub.prevSub = prevSub; - link.nextSub = void 0; - } - if (dep.subsHead === link) { - dep.subsHead = nextSub; - } - if (dep.subs === link) { - dep.subs = prevSub; - if (!prevSub && dep.computed) { - dep.computed.flags &= -5; - for (let l = dep.computed.deps; l; l = l.nextDep) { - removeSub(l, true); - } - } - } - if (!soft && !--dep.sc && dep.map) { - dep.map.delete(dep.key); - } -} -function removeDep(link) { - const { prevDep, nextDep } = link; - if (prevDep) { - prevDep.nextDep = nextDep; - link.prevDep = void 0; - } - if (nextDep) { - nextDep.prevDep = prevDep; - link.nextDep = void 0; - } -} -function effect(fn, options) { - if (fn.effect instanceof ReactiveEffect) { - fn = fn.effect.fn; - } - const e = new ReactiveEffect(fn); - if (options) { - extend(e, options); - } - try { - e.run(); - } catch (err) { - e.stop(); - throw err; - } - const runner = e.run.bind(e); - runner.effect = e; - return runner; -} -function stop(runner) { - runner.effect.stop(); -} -var shouldTrack = true; -var trackStack = []; -function pauseTracking() { - trackStack.push(shouldTrack); - shouldTrack = false; -} -function resetTracking() { - const last = trackStack.pop(); - shouldTrack = last === void 0 ? true : last; -} -function cleanupEffect(e) { - const { cleanup } = e; - e.cleanup = void 0; - if (cleanup) { - const prevSub = activeSub; - activeSub = void 0; - try { - cleanup(); - } finally { - activeSub = prevSub; - } - } -} -var globalVersion = 0; -var Link = class { - constructor(sub, dep) { - this.sub = sub; - this.dep = dep; - this.version = dep.version; - this.nextDep = this.prevDep = this.nextSub = this.prevSub = this.prevActiveLink = void 0; - } -}; -var Dep = class { - // TODO isolatedDeclarations "__v_skip" - constructor(computed3) { - this.computed = computed3; - this.version = 0; - this.activeLink = void 0; - this.subs = void 0; - this.map = void 0; - this.key = void 0; - this.sc = 0; - this.__v_skip = true; - if (true) { - this.subsHead = void 0; - } - } - track(debugInfo) { - if (!activeSub || !shouldTrack || activeSub === this.computed) { - return; - } - let link = this.activeLink; - if (link === void 0 || link.sub !== activeSub) { - link = this.activeLink = new Link(activeSub, this); - if (!activeSub.deps) { - activeSub.deps = activeSub.depsTail = link; - } else { - link.prevDep = activeSub.depsTail; - activeSub.depsTail.nextDep = link; - activeSub.depsTail = link; - } - addSub(link); - } else if (link.version === -1) { - link.version = this.version; - if (link.nextDep) { - const next = link.nextDep; - next.prevDep = link.prevDep; - if (link.prevDep) { - link.prevDep.nextDep = next; - } - link.prevDep = activeSub.depsTail; - link.nextDep = void 0; - activeSub.depsTail.nextDep = link; - activeSub.depsTail = link; - if (activeSub.deps === link) { - activeSub.deps = next; - } - } - } - if (activeSub.onTrack) { - activeSub.onTrack( - extend( - { - effect: activeSub - }, - debugInfo - ) - ); - } - return link; - } - trigger(debugInfo) { - this.version++; - globalVersion++; - this.notify(debugInfo); - } - notify(debugInfo) { - startBatch(); - try { - if (true) { - for (let head = this.subsHead; head; head = head.nextSub) { - if (head.sub.onTrigger && !(head.sub.flags & 8)) { - head.sub.onTrigger( - extend( - { - effect: head.sub - }, - debugInfo - ) - ); - } - } - } - for (let link = this.subs; link; link = link.prevSub) { - if (link.sub.notify()) { - ; - link.sub.dep.notify(); - } - } - } finally { - endBatch(); - } - } -}; -function addSub(link) { - link.dep.sc++; - if (link.sub.flags & 4) { - const computed3 = link.dep.computed; - if (computed3 && !link.dep.subs) { - computed3.flags |= 4 | 16; - for (let l = computed3.deps; l; l = l.nextDep) { - addSub(l); - } - } - const currentTail = link.dep.subs; - if (currentTail !== link) { - link.prevSub = currentTail; - if (currentTail) currentTail.nextSub = link; - } - if (link.dep.subsHead === void 0) { - link.dep.subsHead = link; - } - link.dep.subs = link; - } -} -var targetMap = /* @__PURE__ */ new WeakMap(); -var ITERATE_KEY = Symbol( - true ? "Object iterate" : "" -); -var MAP_KEY_ITERATE_KEY = Symbol( - true ? "Map keys iterate" : "" -); -var ARRAY_ITERATE_KEY = Symbol( - true ? "Array iterate" : "" -); -function track(target, type, key) { - if (shouldTrack && activeSub) { - let depsMap = targetMap.get(target); - if (!depsMap) { - targetMap.set(target, depsMap = /* @__PURE__ */ new Map()); - } - let dep = depsMap.get(key); - if (!dep) { - depsMap.set(key, dep = new Dep()); - dep.map = depsMap; - dep.key = key; - } - if (true) { - dep.track({ - target, - type, - key - }); - } else { - dep.track(); - } - } -} -function trigger(target, type, key, newValue, oldValue, oldTarget) { - const depsMap = targetMap.get(target); - if (!depsMap) { - globalVersion++; - return; - } - const run = (dep) => { - if (dep) { - if (true) { - dep.trigger({ - target, - type, - key, - newValue, - oldValue, - oldTarget - }); - } else { - dep.trigger(); - } - } - }; - startBatch(); - if (type === "clear") { - depsMap.forEach(run); - } else { - const targetIsArray = isArray(target); - const isArrayIndex = targetIsArray && isIntegerKey(key); - if (targetIsArray && key === "length") { - const newLength = Number(newValue); - depsMap.forEach((dep, key2) => { - if (key2 === "length" || key2 === ARRAY_ITERATE_KEY || !isSymbol(key2) && key2 >= newLength) { - run(dep); - } - }); - } else { - if (key !== void 0 || depsMap.has(void 0)) { - run(depsMap.get(key)); - } - if (isArrayIndex) { - run(depsMap.get(ARRAY_ITERATE_KEY)); - } - switch (type) { - case "add": - if (!targetIsArray) { - run(depsMap.get(ITERATE_KEY)); - if (isMap(target)) { - run(depsMap.get(MAP_KEY_ITERATE_KEY)); - } - } else if (isArrayIndex) { - run(depsMap.get("length")); - } - break; - case "delete": - if (!targetIsArray) { - run(depsMap.get(ITERATE_KEY)); - if (isMap(target)) { - run(depsMap.get(MAP_KEY_ITERATE_KEY)); - } - } - break; - case "set": - if (isMap(target)) { - run(depsMap.get(ITERATE_KEY)); - } - break; - } - } - } - endBatch(); -} -function getDepFromReactive(object, key) { - const depMap = targetMap.get(object); - return depMap && depMap.get(key); -} -function reactiveReadArray(array) { - const raw = toRaw(array); - if (raw === array) return raw; - track(raw, "iterate", ARRAY_ITERATE_KEY); - return isShallow(array) ? raw : raw.map(toReactive); -} -function shallowReadArray(arr) { - track(arr = toRaw(arr), "iterate", ARRAY_ITERATE_KEY); - return arr; -} -function toWrapped(target, item) { - if (isReadonly(target)) { - return isReactive(target) ? toReadonly(toReactive(item)) : toReadonly(item); - } - return toReactive(item); -} -var arrayInstrumentations = { - __proto__: null, - [Symbol.iterator]() { - return iterator(this, Symbol.iterator, (item) => toWrapped(this, item)); - }, - concat(...args) { - return reactiveReadArray(this).concat( - ...args.map((x) => isArray(x) ? reactiveReadArray(x) : x) - ); - }, - entries() { - return iterator(this, "entries", (value) => { - value[1] = toWrapped(this, value[1]); - return value; - }); - }, - every(fn, thisArg) { - return apply(this, "every", fn, thisArg, void 0, arguments); - }, - filter(fn, thisArg) { - return apply( - this, - "filter", - fn, - thisArg, - (v) => v.map((item) => toWrapped(this, item)), - arguments - ); - }, - find(fn, thisArg) { - return apply( - this, - "find", - fn, - thisArg, - (item) => toWrapped(this, item), - arguments - ); - }, - findIndex(fn, thisArg) { - return apply(this, "findIndex", fn, thisArg, void 0, arguments); - }, - findLast(fn, thisArg) { - return apply( - this, - "findLast", - fn, - thisArg, - (item) => toWrapped(this, item), - arguments - ); - }, - findLastIndex(fn, thisArg) { - return apply(this, "findLastIndex", fn, thisArg, void 0, arguments); - }, - // flat, flatMap could benefit from ARRAY_ITERATE but are not straight-forward to implement - forEach(fn, thisArg) { - return apply(this, "forEach", fn, thisArg, void 0, arguments); - }, - includes(...args) { - return searchProxy(this, "includes", args); - }, - indexOf(...args) { - return searchProxy(this, "indexOf", args); - }, - join(separator) { - return reactiveReadArray(this).join(separator); - }, - // keys() iterator only reads `length`, no optimization required - lastIndexOf(...args) { - return searchProxy(this, "lastIndexOf", args); - }, - map(fn, thisArg) { - return apply(this, "map", fn, thisArg, void 0, arguments); - }, - pop() { - return noTracking(this, "pop"); - }, - push(...args) { - return noTracking(this, "push", args); - }, - reduce(fn, ...args) { - return reduce(this, "reduce", fn, args); - }, - reduceRight(fn, ...args) { - return reduce(this, "reduceRight", fn, args); - }, - shift() { - return noTracking(this, "shift"); - }, - // slice could use ARRAY_ITERATE but also seems to beg for range tracking - some(fn, thisArg) { - return apply(this, "some", fn, thisArg, void 0, arguments); - }, - splice(...args) { - return noTracking(this, "splice", args); - }, - toReversed() { - return reactiveReadArray(this).toReversed(); - }, - toSorted(comparer) { - return reactiveReadArray(this).toSorted(comparer); - }, - toSpliced(...args) { - return reactiveReadArray(this).toSpliced(...args); - }, - unshift(...args) { - return noTracking(this, "unshift", args); - }, - values() { - return iterator(this, "values", (item) => toWrapped(this, item)); - } -}; -function iterator(self2, method, wrapValue) { - const arr = shallowReadArray(self2); - const iter = arr[method](); - if (arr !== self2 && !isShallow(self2)) { - iter._next = iter.next; - iter.next = () => { - const result = iter._next(); - if (!result.done) { - result.value = wrapValue(result.value); - } - return result; - }; - } - return iter; -} -var arrayProto = Array.prototype; -function apply(self2, method, fn, thisArg, wrappedRetFn, args) { - const arr = shallowReadArray(self2); - const needsWrap = arr !== self2 && !isShallow(self2); - const methodFn = arr[method]; - if (methodFn !== arrayProto[method]) { - const result2 = methodFn.apply(self2, args); - return needsWrap ? toReactive(result2) : result2; - } - let wrappedFn = fn; - if (arr !== self2) { - if (needsWrap) { - wrappedFn = function(item, index) { - return fn.call(this, toWrapped(self2, item), index, self2); - }; - } else if (fn.length > 2) { - wrappedFn = function(item, index) { - return fn.call(this, item, index, self2); - }; - } - } - const result = methodFn.call(arr, wrappedFn, thisArg); - return needsWrap && wrappedRetFn ? wrappedRetFn(result) : result; -} -function reduce(self2, method, fn, args) { - const arr = shallowReadArray(self2); - let wrappedFn = fn; - if (arr !== self2) { - if (!isShallow(self2)) { - wrappedFn = function(acc, item, index) { - return fn.call(this, acc, toWrapped(self2, item), index, self2); - }; - } else if (fn.length > 3) { - wrappedFn = function(acc, item, index) { - return fn.call(this, acc, item, index, self2); - }; - } - } - return arr[method](wrappedFn, ...args); -} -function searchProxy(self2, method, args) { - const arr = toRaw(self2); - track(arr, "iterate", ARRAY_ITERATE_KEY); - const res = arr[method](...args); - if ((res === -1 || res === false) && isProxy(args[0])) { - args[0] = toRaw(args[0]); - return arr[method](...args); - } - return res; -} -function noTracking(self2, method, args = []) { - pauseTracking(); - startBatch(); - const res = toRaw(self2)[method].apply(self2, args); - endBatch(); - resetTracking(); - return res; -} -var isNonTrackableKeys = makeMap(`__proto__,__v_isRef,__isVue`); -var builtInSymbols = new Set( - Object.getOwnPropertyNames(Symbol).filter((key) => key !== "arguments" && key !== "caller").map((key) => Symbol[key]).filter(isSymbol) -); -function hasOwnProperty2(key) { - if (!isSymbol(key)) key = String(key); - const obj = toRaw(this); - track(obj, "has", key); - return obj.hasOwnProperty(key); -} -var BaseReactiveHandler = class { - constructor(_isReadonly = false, _isShallow = false) { - this._isReadonly = _isReadonly; - this._isShallow = _isShallow; - } - get(target, key, receiver) { - if (key === "__v_skip") return target["__v_skip"]; - const isReadonly2 = this._isReadonly, isShallow2 = this._isShallow; - if (key === "__v_isReactive") { - return !isReadonly2; - } else if (key === "__v_isReadonly") { - return isReadonly2; - } else if (key === "__v_isShallow") { - return isShallow2; - } else if (key === "__v_raw") { - if (receiver === (isReadonly2 ? isShallow2 ? shallowReadonlyMap : readonlyMap : isShallow2 ? shallowReactiveMap : reactiveMap).get(target) || // receiver is not the reactive proxy, but has the same prototype - // this means the receiver is a user proxy of the reactive proxy - Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)) { - return target; - } - return; - } - const targetIsArray = isArray(target); - if (!isReadonly2) { - let fn; - if (targetIsArray && (fn = arrayInstrumentations[key])) { - return fn; - } - if (key === "hasOwnProperty") { - return hasOwnProperty2; - } - } - const res = Reflect.get( - target, - key, - // if this is a proxy wrapping a ref, return methods using the raw ref - // as receiver so that we don't have to call `toRaw` on the ref in all - // its class methods - isRef2(target) ? target : receiver - ); - if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { - return res; - } - if (!isReadonly2) { - track(target, "get", key); - } - if (isShallow2) { - return res; - } - if (isRef2(res)) { - const value = targetIsArray && isIntegerKey(key) ? res : res.value; - return isReadonly2 && isObject(value) ? readonly(value) : value; - } - if (isObject(res)) { - return isReadonly2 ? readonly(res) : reactive(res); - } - return res; - } -}; -var MutableReactiveHandler = class extends BaseReactiveHandler { - constructor(isShallow2 = false) { - super(false, isShallow2); - } - set(target, key, value, receiver) { - let oldValue = target[key]; - const isArrayWithIntegerKey = isArray(target) && isIntegerKey(key); - if (!this._isShallow) { - const isOldValueReadonly = isReadonly(oldValue); - if (!isShallow(value) && !isReadonly(value)) { - oldValue = toRaw(oldValue); - value = toRaw(value); - } - if (!isArrayWithIntegerKey && isRef2(oldValue) && !isRef2(value)) { - if (isOldValueReadonly) { - if (true) { - warn( - `Set operation on key "${String(key)}" failed: target is readonly.`, - target[key] - ); - } - return true; - } else { - oldValue.value = value; - return true; - } - } - } - const hadKey = isArrayWithIntegerKey ? Number(key) < target.length : hasOwn(target, key); - const result = Reflect.set( - target, - key, - value, - isRef2(target) ? target : receiver - ); - if (target === toRaw(receiver)) { - if (!hadKey) { - trigger(target, "add", key, value); - } else if (hasChanged(value, oldValue)) { - trigger(target, "set", key, value, oldValue); - } - } - return result; - } - deleteProperty(target, key) { - const hadKey = hasOwn(target, key); - const oldValue = target[key]; - const result = Reflect.deleteProperty(target, key); - if (result && hadKey) { - trigger(target, "delete", key, void 0, oldValue); - } - return result; - } - has(target, key) { - const result = Reflect.has(target, key); - if (!isSymbol(key) || !builtInSymbols.has(key)) { - track(target, "has", key); - } - return result; - } - ownKeys(target) { - track( - target, - "iterate", - isArray(target) ? "length" : ITERATE_KEY - ); - return Reflect.ownKeys(target); - } -}; -var ReadonlyReactiveHandler = class extends BaseReactiveHandler { - constructor(isShallow2 = false) { - super(true, isShallow2); - } - set(target, key) { - if (true) { - warn( - `Set operation on key "${String(key)}" failed: target is readonly.`, - target - ); - } - return true; - } - deleteProperty(target, key) { - if (true) { - warn( - `Delete operation on key "${String(key)}" failed: target is readonly.`, - target - ); - } - return true; - } -}; -var mutableHandlers = new MutableReactiveHandler(); -var readonlyHandlers = new ReadonlyReactiveHandler(); -var shallowReactiveHandlers = new MutableReactiveHandler(true); -var shallowReadonlyHandlers = new ReadonlyReactiveHandler(true); -var toShallow = (value) => value; -var getProto = (v) => Reflect.getPrototypeOf(v); -function createIterableMethod(method, isReadonly2, isShallow2) { - return function(...args) { - const target = this["__v_raw"]; - const rawTarget = toRaw(target); - const targetIsMap = isMap(rawTarget); - const isPair = method === "entries" || method === Symbol.iterator && targetIsMap; - const isKeyOnly = method === "keys" && targetIsMap; - const innerIterator = target[method](...args); - const wrap = isShallow2 ? toShallow : isReadonly2 ? toReadonly : toReactive; - !isReadonly2 && track( - rawTarget, - "iterate", - isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY - ); - return { - // iterator protocol - next() { - const { value, done } = innerIterator.next(); - return done ? { value, done } : { - value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value), - done - }; - }, - // iterable protocol - [Symbol.iterator]() { - return this; - } - }; - }; -} -function createReadonlyMethod(type) { - return function(...args) { - if (true) { - const key = args[0] ? `on key "${args[0]}" ` : ``; - warn( - `${capitalize(type)} operation ${key}failed: target is readonly.`, - toRaw(this) - ); - } - return type === "delete" ? false : type === "clear" ? void 0 : this; - }; -} -function createInstrumentations(readonly2, shallow) { - const instrumentations = { - get(key) { - const target = this["__v_raw"]; - const rawTarget = toRaw(target); - const rawKey = toRaw(key); - if (!readonly2) { - if (hasChanged(key, rawKey)) { - track(rawTarget, "get", key); - } - track(rawTarget, "get", rawKey); - } - const { has } = getProto(rawTarget); - const wrap = shallow ? toShallow : readonly2 ? toReadonly : toReactive; - if (has.call(rawTarget, key)) { - return wrap(target.get(key)); - } else if (has.call(rawTarget, rawKey)) { - return wrap(target.get(rawKey)); - } else if (target !== rawTarget) { - target.get(key); - } - }, - get size() { - const target = this["__v_raw"]; - !readonly2 && track(toRaw(target), "iterate", ITERATE_KEY); - return target.size; - }, - has(key) { - const target = this["__v_raw"]; - const rawTarget = toRaw(target); - const rawKey = toRaw(key); - if (!readonly2) { - if (hasChanged(key, rawKey)) { - track(rawTarget, "has", key); - } - track(rawTarget, "has", rawKey); - } - return key === rawKey ? target.has(key) : target.has(key) || target.has(rawKey); - }, - forEach(callback, thisArg) { - const observed = this; - const target = observed["__v_raw"]; - const rawTarget = toRaw(target); - const wrap = shallow ? toShallow : readonly2 ? toReadonly : toReactive; - !readonly2 && track(rawTarget, "iterate", ITERATE_KEY); - return target.forEach((value, key) => { - return callback.call(thisArg, wrap(value), wrap(key), observed); - }); - } - }; - extend( - instrumentations, - readonly2 ? { - add: createReadonlyMethod("add"), - set: createReadonlyMethod("set"), - delete: createReadonlyMethod("delete"), - clear: createReadonlyMethod("clear") - } : { - add(value) { - if (!shallow && !isShallow(value) && !isReadonly(value)) { - value = toRaw(value); - } - const target = toRaw(this); - const proto = getProto(target); - const hadKey = proto.has.call(target, value); - if (!hadKey) { - target.add(value); - trigger(target, "add", value, value); - } - return this; - }, - set(key, value) { - if (!shallow && !isShallow(value) && !isReadonly(value)) { - value = toRaw(value); - } - const target = toRaw(this); - const { has, get } = getProto(target); - let hadKey = has.call(target, key); - if (!hadKey) { - key = toRaw(key); - hadKey = has.call(target, key); - } else if (true) { - checkIdentityKeys(target, has, key); - } - const oldValue = get.call(target, key); - target.set(key, value); - if (!hadKey) { - trigger(target, "add", key, value); - } else if (hasChanged(value, oldValue)) { - trigger(target, "set", key, value, oldValue); - } - return this; - }, - delete(key) { - const target = toRaw(this); - const { has, get } = getProto(target); - let hadKey = has.call(target, key); - if (!hadKey) { - key = toRaw(key); - hadKey = has.call(target, key); - } else if (true) { - checkIdentityKeys(target, has, key); - } - const oldValue = get ? get.call(target, key) : void 0; - const result = target.delete(key); - if (hadKey) { - trigger(target, "delete", key, void 0, oldValue); - } - return result; - }, - clear() { - const target = toRaw(this); - const hadItems = target.size !== 0; - const oldTarget = true ? isMap(target) ? new Map(target) : new Set(target) : void 0; - const result = target.clear(); - if (hadItems) { - trigger( - target, - "clear", - void 0, - void 0, - oldTarget - ); - } - return result; - } - } - ); - const iteratorMethods = [ - "keys", - "values", - "entries", - Symbol.iterator - ]; - iteratorMethods.forEach((method) => { - instrumentations[method] = createIterableMethod(method, readonly2, shallow); - }); - return instrumentations; -} -function createInstrumentationGetter(isReadonly2, shallow) { - const instrumentations = createInstrumentations(isReadonly2, shallow); - return (target, key, receiver) => { - if (key === "__v_isReactive") { - return !isReadonly2; - } else if (key === "__v_isReadonly") { - return isReadonly2; - } else if (key === "__v_raw") { - return target; - } - return Reflect.get( - hasOwn(instrumentations, key) && key in target ? instrumentations : target, - key, - receiver - ); - }; -} -var mutableCollectionHandlers = { - get: createInstrumentationGetter(false, false) -}; -var shallowCollectionHandlers = { - get: createInstrumentationGetter(false, true) -}; -var readonlyCollectionHandlers = { - get: createInstrumentationGetter(true, false) -}; -var shallowReadonlyCollectionHandlers = { - get: createInstrumentationGetter(true, true) -}; -function checkIdentityKeys(target, has, key) { - const rawKey = toRaw(key); - if (rawKey !== key && has.call(target, rawKey)) { - const type = toRawType(target); - warn( - `Reactive ${type} contains both the raw and reactive versions of the same object${type === `Map` ? ` as keys` : ``}, which can lead to inconsistencies. Avoid differentiating between the raw and reactive versions of an object and only use the reactive version if possible.` - ); - } -} -var reactiveMap = /* @__PURE__ */ new WeakMap(); -var shallowReactiveMap = /* @__PURE__ */ new WeakMap(); -var readonlyMap = /* @__PURE__ */ new WeakMap(); -var shallowReadonlyMap = /* @__PURE__ */ new WeakMap(); -function targetTypeMap(rawType) { - switch (rawType) { - case "Object": - case "Array": - return 1; - case "Map": - case "Set": - case "WeakMap": - case "WeakSet": - return 2; - default: - return 0; - } -} -function getTargetType(value) { - return value["__v_skip"] || !Object.isExtensible(value) ? 0 : targetTypeMap(toRawType(value)); -} -function reactive(target) { - if (isReadonly(target)) { - return target; - } - return createReactiveObject( - target, - false, - mutableHandlers, - mutableCollectionHandlers, - reactiveMap - ); -} -function shallowReactive(target) { - return createReactiveObject( - target, - false, - shallowReactiveHandlers, - shallowCollectionHandlers, - shallowReactiveMap - ); -} -function readonly(target) { - return createReactiveObject( - target, - true, - readonlyHandlers, - readonlyCollectionHandlers, - readonlyMap - ); -} -function shallowReadonly(target) { - return createReactiveObject( - target, - true, - shallowReadonlyHandlers, - shallowReadonlyCollectionHandlers, - shallowReadonlyMap - ); -} -function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) { - if (!isObject(target)) { - if (true) { - warn( - `value cannot be made ${isReadonly2 ? "readonly" : "reactive"}: ${String( - target - )}` - ); - } - return target; - } - if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) { - return target; - } - const targetType = getTargetType(target); - if (targetType === 0) { - return target; - } - const existingProxy = proxyMap.get(target); - if (existingProxy) { - return existingProxy; - } - const proxy = new Proxy( - target, - targetType === 2 ? collectionHandlers : baseHandlers - ); - proxyMap.set(target, proxy); - return proxy; -} -function isReactive(value) { - if (isReadonly(value)) { - return isReactive(value["__v_raw"]); - } - return !!(value && value["__v_isReactive"]); -} -function isReadonly(value) { - return !!(value && value["__v_isReadonly"]); -} -function isShallow(value) { - return !!(value && value["__v_isShallow"]); -} -function isProxy(value) { - return value ? !!value["__v_raw"] : false; -} -function toRaw(observed) { - const raw = observed && observed["__v_raw"]; - return raw ? toRaw(raw) : observed; -} -function markRaw(value) { - if (!hasOwn(value, "__v_skip") && Object.isExtensible(value)) { - def(value, "__v_skip", true); - } - return value; -} -var toReactive = (value) => isObject(value) ? reactive(value) : value; -var toReadonly = (value) => isObject(value) ? readonly(value) : value; -function isRef2(r) { - return r ? r["__v_isRef"] === true : false; -} -function ref(value) { - return createRef(value, false); -} -function shallowRef(value) { - return createRef(value, true); -} -function createRef(rawValue, shallow) { - if (isRef2(rawValue)) { - return rawValue; - } - return new RefImpl(rawValue, shallow); -} -var RefImpl = class { - constructor(value, isShallow2) { - this.dep = new Dep(); - this["__v_isRef"] = true; - this["__v_isShallow"] = false; - this._rawValue = isShallow2 ? value : toRaw(value); - this._value = isShallow2 ? value : toReactive(value); - this["__v_isShallow"] = isShallow2; - } - get value() { - if (true) { - this.dep.track({ - target: this, - type: "get", - key: "value" - }); - } else { - this.dep.track(); - } - return this._value; - } - set value(newValue) { - const oldValue = this._rawValue; - const useDirectValue = this["__v_isShallow"] || isShallow(newValue) || isReadonly(newValue); - newValue = useDirectValue ? newValue : toRaw(newValue); - if (hasChanged(newValue, oldValue)) { - this._rawValue = newValue; - this._value = useDirectValue ? newValue : toReactive(newValue); - if (true) { - this.dep.trigger({ - target: this, - type: "set", - key: "value", - newValue, - oldValue - }); - } else { - this.dep.trigger(); - } - } - } -}; -function triggerRef(ref2) { - if (ref2.dep) { - if (true) { - ref2.dep.trigger({ - target: ref2, - type: "set", - key: "value", - newValue: ref2._value - }); - } else { - ref2.dep.trigger(); - } - } -} -function unref(ref2) { - return isRef2(ref2) ? ref2.value : ref2; -} -function toValue(source) { - return isFunction(source) ? source() : unref(source); -} -var shallowUnwrapHandlers = { - get: (target, key, receiver) => key === "__v_raw" ? target : unref(Reflect.get(target, key, receiver)), - set: (target, key, value, receiver) => { - const oldValue = target[key]; - if (isRef2(oldValue) && !isRef2(value)) { - oldValue.value = value; - return true; - } else { - return Reflect.set(target, key, value, receiver); - } - } -}; -function proxyRefs(objectWithRefs) { - return isReactive(objectWithRefs) ? objectWithRefs : new Proxy(objectWithRefs, shallowUnwrapHandlers); -} -var CustomRefImpl = class { - constructor(factory) { - this["__v_isRef"] = true; - this._value = void 0; - const dep = this.dep = new Dep(); - const { get, set } = factory(dep.track.bind(dep), dep.trigger.bind(dep)); - this._get = get; - this._set = set; - } - get value() { - return this._value = this._get(); - } - set value(newVal) { - this._set(newVal); - } -}; -function customRef(factory) { - return new CustomRefImpl(factory); -} -function toRefs(object) { - if (!isProxy(object)) { - warn(`toRefs() expects a reactive object but received a plain one.`); - } - const ret = isArray(object) ? new Array(object.length) : {}; - for (const key in object) { - ret[key] = propertyToRef(object, key); - } - return ret; -} -var ObjectRefImpl = class { - constructor(_object, _key, _defaultValue) { - this._object = _object; - this._key = _key; - this._defaultValue = _defaultValue; - this["__v_isRef"] = true; - this._value = void 0; - this._raw = toRaw(_object); - let shallow = true; - let obj = _object; - if (!isArray(_object) || !isIntegerKey(String(_key))) { - do { - shallow = !isProxy(obj) || isShallow(obj); - } while (shallow && (obj = obj["__v_raw"])); - } - this._shallow = shallow; - } - get value() { - let val = this._object[this._key]; - if (this._shallow) { - val = unref(val); - } - return this._value = val === void 0 ? this._defaultValue : val; - } - set value(newVal) { - if (this._shallow && isRef2(this._raw[this._key])) { - const nestedRef = this._object[this._key]; - if (isRef2(nestedRef)) { - nestedRef.value = newVal; - return; - } - } - this._object[this._key] = newVal; - } - get dep() { - return getDepFromReactive(this._raw, this._key); - } -}; -var GetterRefImpl = class { - constructor(_getter) { - this._getter = _getter; - this["__v_isRef"] = true; - this["__v_isReadonly"] = true; - this._value = void 0; - } - get value() { - return this._value = this._getter(); - } -}; -function toRef(source, key, defaultValue) { - if (isRef2(source)) { - return source; - } else if (isFunction(source)) { - return new GetterRefImpl(source); - } else if (isObject(source) && arguments.length > 1) { - return propertyToRef(source, key, defaultValue); - } else { - return ref(source); - } -} -function propertyToRef(source, key, defaultValue) { - return new ObjectRefImpl(source, key, defaultValue); -} -var ComputedRefImpl = class { - constructor(fn, setter, isSSR) { - this.fn = fn; - this.setter = setter; - this._value = void 0; - this.dep = new Dep(this); - this.__v_isRef = true; - this.deps = void 0; - this.depsTail = void 0; - this.flags = 16; - this.globalVersion = globalVersion - 1; - this.next = void 0; - this.effect = this; - this["__v_isReadonly"] = !setter; - this.isSSR = isSSR; - } - /** - * @internal - */ - notify() { - this.flags |= 16; - if (!(this.flags & 8) && // avoid infinite self recursion - activeSub !== this) { - batch(this, true); - return true; - } else if (true) ; - } - get value() { - const link = true ? this.dep.track({ - target: this, - type: "get", - key: "value" - }) : this.dep.track(); - refreshComputed(this); - if (link) { - link.version = this.dep.version; - } - return this._value; - } - set value(newValue) { - if (this.setter) { - this.setter(newValue); - } else if (true) { - warn("Write operation failed: computed value is readonly"); - } - } -}; -function computed(getterOrOptions, debugOptions, isSSR = false) { - let getter; - let setter; - if (isFunction(getterOrOptions)) { - getter = getterOrOptions; - } else { - getter = getterOrOptions.get; - setter = getterOrOptions.set; - } - const cRef = new ComputedRefImpl(getter, setter, isSSR); - if (debugOptions && !isSSR) { - cRef.onTrack = debugOptions.onTrack; - cRef.onTrigger = debugOptions.onTrigger; - } - return cRef; -} -var TrackOpTypes = { - "GET": "get", - "HAS": "has", - "ITERATE": "iterate" -}; -var TriggerOpTypes = { - "SET": "set", - "ADD": "add", - "DELETE": "delete", - "CLEAR": "clear" -}; -var INITIAL_WATCHER_VALUE = {}; -var cleanupMap = /* @__PURE__ */ new WeakMap(); -var activeWatcher = void 0; -function getCurrentWatcher() { - return activeWatcher; -} -function onWatcherCleanup(cleanupFn, failSilently = false, owner = activeWatcher) { - if (owner) { - let cleanups = cleanupMap.get(owner); - if (!cleanups) cleanupMap.set(owner, cleanups = []); - cleanups.push(cleanupFn); - } else if (!failSilently) { - warn( - `onWatcherCleanup() was called when there was no active watcher to associate with.` - ); - } -} -function watch(source, cb, options = EMPTY_OBJ) { - const { immediate, deep, once, scheduler, augmentJob, call } = options; - const warnInvalidSource = (s) => { - (options.onWarn || warn)( - `Invalid watch source: `, - s, - `A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.` - ); - }; - const reactiveGetter = (source2) => { - if (deep) return source2; - if (isShallow(source2) || deep === false || deep === 0) - return traverse(source2, 1); - return traverse(source2); - }; - let effect2; - let getter; - let cleanup; - let boundCleanup; - let forceTrigger = false; - let isMultiSource = false; - if (isRef2(source)) { - getter = () => source.value; - forceTrigger = isShallow(source); - } else if (isReactive(source)) { - getter = () => reactiveGetter(source); - forceTrigger = true; - } else if (isArray(source)) { - isMultiSource = true; - forceTrigger = source.some((s) => isReactive(s) || isShallow(s)); - getter = () => source.map((s) => { - if (isRef2(s)) { - return s.value; - } else if (isReactive(s)) { - return reactiveGetter(s); - } else if (isFunction(s)) { - return call ? call(s, 2) : s(); - } else { - warnInvalidSource(s); - } - }); - } else if (isFunction(source)) { - if (cb) { - getter = call ? () => call(source, 2) : source; - } else { - getter = () => { - if (cleanup) { - pauseTracking(); - try { - cleanup(); - } finally { - resetTracking(); - } - } - const currentEffect = activeWatcher; - activeWatcher = effect2; - try { - return call ? call(source, 3, [boundCleanup]) : source(boundCleanup); - } finally { - activeWatcher = currentEffect; - } - }; - } - } else { - getter = NOOP; - warnInvalidSource(source); - } - if (cb && deep) { - const baseGetter = getter; - const depth = deep === true ? Infinity : deep; - getter = () => traverse(baseGetter(), depth); - } - const scope = getCurrentScope(); - const watchHandle = () => { - effect2.stop(); - if (scope && scope.active) { - remove(scope.effects, effect2); - } - }; - if (once && cb) { - const _cb = cb; - cb = (...args) => { - _cb(...args); - watchHandle(); - }; - } - let oldValue = isMultiSource ? new Array(source.length).fill(INITIAL_WATCHER_VALUE) : INITIAL_WATCHER_VALUE; - const job = (immediateFirstRun) => { - if (!(effect2.flags & 1) || !effect2.dirty && !immediateFirstRun) { - return; - } - if (cb) { - const newValue = effect2.run(); - if (deep || forceTrigger || (isMultiSource ? newValue.some((v, i) => hasChanged(v, oldValue[i])) : hasChanged(newValue, oldValue))) { - if (cleanup) { - cleanup(); - } - const currentWatcher = activeWatcher; - activeWatcher = effect2; - try { - const args = [ - newValue, - // pass undefined as the old value when it's changed for the first time - oldValue === INITIAL_WATCHER_VALUE ? void 0 : isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE ? [] : oldValue, - boundCleanup - ]; - oldValue = newValue; - call ? call(cb, 3, args) : ( - // @ts-expect-error - cb(...args) - ); - } finally { - activeWatcher = currentWatcher; - } - } - } else { - effect2.run(); - } - }; - if (augmentJob) { - augmentJob(job); - } - effect2 = new ReactiveEffect(getter); - effect2.scheduler = scheduler ? () => scheduler(job, false) : job; - boundCleanup = (fn) => onWatcherCleanup(fn, false, effect2); - cleanup = effect2.onStop = () => { - const cleanups = cleanupMap.get(effect2); - if (cleanups) { - if (call) { - call(cleanups, 4); - } else { - for (const cleanup2 of cleanups) cleanup2(); - } - cleanupMap.delete(effect2); - } - }; - if (true) { - effect2.onTrack = options.onTrack; - effect2.onTrigger = options.onTrigger; - } - if (cb) { - if (immediate) { - job(true); - } else { - oldValue = effect2.run(); - } - } else if (scheduler) { - scheduler(job.bind(null, true), true); - } else { - effect2.run(); - } - watchHandle.pause = effect2.pause.bind(effect2); - watchHandle.resume = effect2.resume.bind(effect2); - watchHandle.stop = watchHandle; - return watchHandle; -} -function traverse(value, depth = Infinity, seen) { - if (depth <= 0 || !isObject(value) || value["__v_skip"]) { - return value; - } - seen = seen || /* @__PURE__ */ new Map(); - if ((seen.get(value) || 0) >= depth) { - return value; - } - seen.set(value, depth); - depth--; - if (isRef2(value)) { - traverse(value.value, depth, seen); - } else if (isArray(value)) { - for (let i = 0; i < value.length; i++) { - traverse(value[i], depth, seen); - } - } else if (isSet(value) || isMap(value)) { - value.forEach((v) => { - traverse(v, depth, seen); - }); - } else if (isPlainObject(value)) { - for (const key in value) { - traverse(value[key], depth, seen); - } - for (const key of Object.getOwnPropertySymbols(value)) { - if (Object.prototype.propertyIsEnumerable.call(value, key)) { - traverse(value[key], depth, seen); - } - } - } - return value; -} - -// node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js -var stack = []; -function pushWarningContext(vnode) { - stack.push(vnode); -} -function popWarningContext() { - stack.pop(); -} -var isWarning = false; -function warn$1(msg, ...args) { - if (isWarning) return; - isWarning = true; - pauseTracking(); - const instance = stack.length ? stack[stack.length - 1].component : null; - const appWarnHandler = instance && instance.appContext.config.warnHandler; - const trace = getComponentTrace(); - if (appWarnHandler) { - callWithErrorHandling( - appWarnHandler, - instance, - 11, - [ - // eslint-disable-next-line no-restricted-syntax - msg + args.map((a) => { - var _a, _b; - return (_b = (_a = a.toString) == null ? void 0 : _a.call(a)) != null ? _b : JSON.stringify(a); - }).join(""), - instance && instance.proxy, - trace.map( - ({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>` - ).join("\n"), - trace - ] - ); - } else { - const warnArgs = [`[Vue warn]: ${msg}`, ...args]; - if (trace.length && // avoid spamming console during tests - true) { - warnArgs.push(` -`, ...formatTrace(trace)); - } - console.warn(...warnArgs); - } - resetTracking(); - isWarning = false; -} -function getComponentTrace() { - let currentVNode = stack[stack.length - 1]; - if (!currentVNode) { - return []; - } - const normalizedStack = []; - while (currentVNode) { - const last = normalizedStack[0]; - if (last && last.vnode === currentVNode) { - last.recurseCount++; - } else { - normalizedStack.push({ - vnode: currentVNode, - recurseCount: 0 - }); - } - const parentInstance = currentVNode.component && currentVNode.component.parent; - currentVNode = parentInstance && parentInstance.vnode; - } - return normalizedStack; -} -function formatTrace(trace) { - const logs = []; - trace.forEach((entry, i) => { - logs.push(...i === 0 ? [] : [` -`], ...formatTraceEntry(entry)); - }); - return logs; -} -function formatTraceEntry({ vnode, recurseCount }) { - const postfix = recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``; - const isRoot = vnode.component ? vnode.component.parent == null : false; - const open = ` at <${formatComponentName( - vnode.component, - vnode.type, - isRoot - )}`; - const close = `>` + postfix; - return vnode.props ? [open, ...formatProps(vnode.props), close] : [open + close]; -} -function formatProps(props) { - const res = []; - const keys = Object.keys(props); - keys.slice(0, 3).forEach((key) => { - res.push(...formatProp(key, props[key])); - }); - if (keys.length > 3) { - res.push(` ...`); - } - return res; -} -function formatProp(key, value, raw) { - if (isString(value)) { - value = JSON.stringify(value); - return raw ? value : [`${key}=${value}`]; - } else if (typeof value === "number" || typeof value === "boolean" || value == null) { - return raw ? value : [`${key}=${value}`]; - } else if (isRef2(value)) { - value = formatProp(key, toRaw(value.value), true); - return raw ? value : [`${key}=Ref<`, value, `>`]; - } else if (isFunction(value)) { - return [`${key}=fn${value.name ? `<${value.name}>` : ``}`]; - } else { - value = toRaw(value); - return raw ? value : [`${key}=`, value]; - } -} -function assertNumber(val, type) { - if (false) return; - if (val === void 0) { - return; - } else if (typeof val !== "number") { - warn$1(`${type} is not a valid number - got ${JSON.stringify(val)}.`); - } else if (isNaN(val)) { - warn$1(`${type} is NaN - the duration expression might be incorrect.`); - } -} -var ErrorCodes = { - "SETUP_FUNCTION": 0, - "0": "SETUP_FUNCTION", - "RENDER_FUNCTION": 1, - "1": "RENDER_FUNCTION", - "NATIVE_EVENT_HANDLER": 5, - "5": "NATIVE_EVENT_HANDLER", - "COMPONENT_EVENT_HANDLER": 6, - "6": "COMPONENT_EVENT_HANDLER", - "VNODE_HOOK": 7, - "7": "VNODE_HOOK", - "DIRECTIVE_HOOK": 8, - "8": "DIRECTIVE_HOOK", - "TRANSITION_HOOK": 9, - "9": "TRANSITION_HOOK", - "APP_ERROR_HANDLER": 10, - "10": "APP_ERROR_HANDLER", - "APP_WARN_HANDLER": 11, - "11": "APP_WARN_HANDLER", - "FUNCTION_REF": 12, - "12": "FUNCTION_REF", - "ASYNC_COMPONENT_LOADER": 13, - "13": "ASYNC_COMPONENT_LOADER", - "SCHEDULER": 14, - "14": "SCHEDULER", - "COMPONENT_UPDATE": 15, - "15": "COMPONENT_UPDATE", - "APP_UNMOUNT_CLEANUP": 16, - "16": "APP_UNMOUNT_CLEANUP" -}; -var ErrorTypeStrings$1 = { - ["sp"]: "serverPrefetch hook", - ["bc"]: "beforeCreate hook", - ["c"]: "created hook", - ["bm"]: "beforeMount hook", - ["m"]: "mounted hook", - ["bu"]: "beforeUpdate hook", - ["u"]: "updated", - ["bum"]: "beforeUnmount hook", - ["um"]: "unmounted hook", - ["a"]: "activated hook", - ["da"]: "deactivated hook", - ["ec"]: "errorCaptured hook", - ["rtc"]: "renderTracked hook", - ["rtg"]: "renderTriggered hook", - [0]: "setup function", - [1]: "render function", - [2]: "watcher getter", - [3]: "watcher callback", - [4]: "watcher cleanup function", - [5]: "native event handler", - [6]: "component event handler", - [7]: "vnode hook", - [8]: "directive hook", - [9]: "transition hook", - [10]: "app errorHandler", - [11]: "app warnHandler", - [12]: "ref function", - [13]: "async component loader", - [14]: "scheduler flush", - [15]: "component update", - [16]: "app unmount cleanup function" -}; -function callWithErrorHandling(fn, instance, type, args) { - try { - return args ? fn(...args) : fn(); - } catch (err) { - handleError(err, instance, type); - } -} -function callWithAsyncErrorHandling(fn, instance, type, args) { - if (isFunction(fn)) { - const res = callWithErrorHandling(fn, instance, type, args); - if (res && isPromise(res)) { - res.catch((err) => { - handleError(err, instance, type); - }); - } - return res; - } - if (isArray(fn)) { - const values = []; - for (let i = 0; i < fn.length; i++) { - values.push(callWithAsyncErrorHandling(fn[i], instance, type, args)); - } - return values; - } else if (true) { - warn$1( - `Invalid value type passed to callWithAsyncErrorHandling(): ${typeof fn}` - ); - } -} -function handleError(err, instance, type, throwInDev = true) { - const contextVNode = instance ? instance.vnode : null; - const { errorHandler, throwUnhandledErrorInProduction } = instance && instance.appContext.config || EMPTY_OBJ; - if (instance) { - let cur = instance.parent; - const exposedInstance = instance.proxy; - const errorInfo = true ? ErrorTypeStrings$1[type] : `https://vuejs.org/error-reference/#runtime-${type}`; - while (cur) { - const errorCapturedHooks = cur.ec; - if (errorCapturedHooks) { - for (let i = 0; i < errorCapturedHooks.length; i++) { - if (errorCapturedHooks[i](err, exposedInstance, errorInfo) === false) { - return; - } - } - } - cur = cur.parent; - } - if (errorHandler) { - pauseTracking(); - callWithErrorHandling(errorHandler, null, 10, [ - err, - exposedInstance, - errorInfo - ]); - resetTracking(); - return; - } - } - logError(err, type, contextVNode, throwInDev, throwUnhandledErrorInProduction); -} -function logError(err, type, contextVNode, throwInDev = true, throwInProd = false) { - if (true) { - const info = ErrorTypeStrings$1[type]; - if (contextVNode) { - pushWarningContext(contextVNode); - } - warn$1(`Unhandled error${info ? ` during execution of ${info}` : ``}`); - if (contextVNode) { - popWarningContext(); - } - if (throwInDev) { - throw err; - } else { - console.error(err); - } - } else if (throwInProd) { - throw err; - } else { - console.error(err); - } -} -var queue = []; -var flushIndex = -1; -var pendingPostFlushCbs = []; -var activePostFlushCbs = null; -var postFlushIndex = 0; -var resolvedPromise = Promise.resolve(); -var currentFlushPromise = null; -var RECURSION_LIMIT = 100; -function nextTick(fn) { - const p2 = currentFlushPromise || resolvedPromise; - return fn ? p2.then(this ? fn.bind(this) : fn) : p2; -} -function findInsertionIndex(id) { - let start = flushIndex + 1; - let end = queue.length; - while (start < end) { - const middle = start + end >>> 1; - const middleJob = queue[middle]; - const middleJobId = getId(middleJob); - if (middleJobId < id || middleJobId === id && middleJob.flags & 2) { - start = middle + 1; - } else { - end = middle; - } - } - return start; -} -function queueJob(job) { - if (!(job.flags & 1)) { - const jobId = getId(job); - const lastJob = queue[queue.length - 1]; - if (!lastJob || // fast path when the job id is larger than the tail - !(job.flags & 2) && jobId >= getId(lastJob)) { - queue.push(job); - } else { - queue.splice(findInsertionIndex(jobId), 0, job); - } - job.flags |= 1; - queueFlush(); - } -} -function queueFlush() { - if (!currentFlushPromise) { - currentFlushPromise = resolvedPromise.then(flushJobs); - } -} -function queuePostFlushCb(cb) { - if (!isArray(cb)) { - if (activePostFlushCbs && cb.id === -1) { - activePostFlushCbs.splice(postFlushIndex + 1, 0, cb); - } else if (!(cb.flags & 1)) { - pendingPostFlushCbs.push(cb); - cb.flags |= 1; - } - } else { - pendingPostFlushCbs.push(...cb); - } - queueFlush(); -} -function flushPreFlushCbs(instance, seen, i = flushIndex + 1) { - if (true) { - seen = seen || /* @__PURE__ */ new Map(); - } - for (; i < queue.length; i++) { - const cb = queue[i]; - if (cb && cb.flags & 2) { - if (instance && cb.id !== instance.uid) { - continue; - } - if (checkRecursiveUpdates(seen, cb)) { - continue; - } - queue.splice(i, 1); - i--; - if (cb.flags & 4) { - cb.flags &= -2; - } - cb(); - if (!(cb.flags & 4)) { - cb.flags &= -2; - } - } - } -} -function flushPostFlushCbs(seen) { - if (pendingPostFlushCbs.length) { - const deduped = [...new Set(pendingPostFlushCbs)].sort( - (a, b) => getId(a) - getId(b) - ); - pendingPostFlushCbs.length = 0; - if (activePostFlushCbs) { - activePostFlushCbs.push(...deduped); - return; - } - activePostFlushCbs = deduped; - if (true) { - seen = seen || /* @__PURE__ */ new Map(); - } - for (postFlushIndex = 0; postFlushIndex < activePostFlushCbs.length; postFlushIndex++) { - const cb = activePostFlushCbs[postFlushIndex]; - if (checkRecursiveUpdates(seen, cb)) { - continue; - } - if (cb.flags & 4) { - cb.flags &= -2; - } - if (!(cb.flags & 8)) cb(); - cb.flags &= -2; - } - activePostFlushCbs = null; - postFlushIndex = 0; - } -} -var getId = (job) => job.id == null ? job.flags & 2 ? -1 : Infinity : job.id; -function flushJobs(seen) { - if (true) { - seen = seen || /* @__PURE__ */ new Map(); - } - const check = true ? (job) => checkRecursiveUpdates(seen, job) : NOOP; - try { - for (flushIndex = 0; flushIndex < queue.length; flushIndex++) { - const job = queue[flushIndex]; - if (job && !(job.flags & 8)) { - if (check(job)) { - continue; - } - if (job.flags & 4) { - job.flags &= ~1; - } - callWithErrorHandling( - job, - job.i, - job.i ? 15 : 14 - ); - if (!(job.flags & 4)) { - job.flags &= ~1; - } - } - } - } finally { - for (; flushIndex < queue.length; flushIndex++) { - const job = queue[flushIndex]; - if (job) { - job.flags &= -2; - } - } - flushIndex = -1; - queue.length = 0; - flushPostFlushCbs(seen); - currentFlushPromise = null; - if (queue.length || pendingPostFlushCbs.length) { - flushJobs(seen); - } - } -} -function checkRecursiveUpdates(seen, fn) { - const count = seen.get(fn) || 0; - if (count > RECURSION_LIMIT) { - const instance = fn.i; - const componentName = instance && getComponentName(instance.type); - handleError( - `Maximum recursive updates exceeded${componentName ? ` in component <${componentName}>` : ``}. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.`, - null, - 10 - ); - return true; - } - seen.set(fn, count + 1); - return false; -} -var isHmrUpdating = false; -var hmrDirtyComponents = /* @__PURE__ */ new Map(); -if (true) { - getGlobalThis().__VUE_HMR_RUNTIME__ = { - createRecord: tryWrap(createRecord), - rerender: tryWrap(rerender), - reload: tryWrap(reload) - }; -} -var map = /* @__PURE__ */ new Map(); -function registerHMR(instance) { - const id = instance.type.__hmrId; - let record = map.get(id); - if (!record) { - createRecord(id, instance.type); - record = map.get(id); - } - record.instances.add(instance); -} -function unregisterHMR(instance) { - map.get(instance.type.__hmrId).instances.delete(instance); -} -function createRecord(id, initialDef) { - if (map.has(id)) { - return false; - } - map.set(id, { - initialDef: normalizeClassComponent(initialDef), - instances: /* @__PURE__ */ new Set() - }); - return true; -} -function normalizeClassComponent(component) { - return isClassComponent(component) ? component.__vccOpts : component; -} -function rerender(id, newRender) { - const record = map.get(id); - if (!record) { - return; - } - record.initialDef.render = newRender; - [...record.instances].forEach((instance) => { - if (newRender) { - instance.render = newRender; - normalizeClassComponent(instance.type).render = newRender; - } - instance.renderCache = []; - isHmrUpdating = true; - if (!(instance.job.flags & 8)) { - instance.update(); - } - isHmrUpdating = false; - }); -} -function reload(id, newComp) { - const record = map.get(id); - if (!record) return; - newComp = normalizeClassComponent(newComp); - updateComponentDef(record.initialDef, newComp); - const instances = [...record.instances]; - for (let i = 0; i < instances.length; i++) { - const instance = instances[i]; - const oldComp = normalizeClassComponent(instance.type); - let dirtyInstances = hmrDirtyComponents.get(oldComp); - if (!dirtyInstances) { - if (oldComp !== record.initialDef) { - updateComponentDef(oldComp, newComp); - } - hmrDirtyComponents.set(oldComp, dirtyInstances = /* @__PURE__ */ new Set()); - } - dirtyInstances.add(instance); - instance.appContext.propsCache.delete(instance.type); - instance.appContext.emitsCache.delete(instance.type); - instance.appContext.optionsCache.delete(instance.type); - if (instance.ceReload) { - dirtyInstances.add(instance); - instance.ceReload(newComp.styles); - dirtyInstances.delete(instance); - } else if (instance.parent) { - queueJob(() => { - if (!(instance.job.flags & 8)) { - isHmrUpdating = true; - instance.parent.update(); - isHmrUpdating = false; - dirtyInstances.delete(instance); - } - }); - } else if (instance.appContext.reload) { - instance.appContext.reload(); - } else if (typeof window !== "undefined") { - window.location.reload(); - } else { - console.warn( - "[HMR] Root or manually mounted instance modified. Full reload required." - ); - } - if (instance.root.ce && instance !== instance.root) { - instance.root.ce._removeChildStyle(oldComp); - } - } - queuePostFlushCb(() => { - hmrDirtyComponents.clear(); - }); -} -function updateComponentDef(oldComp, newComp) { - extend(oldComp, newComp); - for (const key in oldComp) { - if (key !== "__file" && !(key in newComp)) { - delete oldComp[key]; - } - } -} -function tryWrap(fn) { - return (id, arg) => { - try { - return fn(id, arg); - } catch (e) { - console.error(e); - console.warn( - `[HMR] Something went wrong during Vue component hot-reload. Full reload required.` - ); - } - }; -} -var devtools$1; -var buffer = []; -var devtoolsNotInstalled = false; -function emit$1(event, ...args) { - if (devtools$1) { - devtools$1.emit(event, ...args); - } else if (!devtoolsNotInstalled) { - buffer.push({ event, args }); - } -} -function setDevtoolsHook$1(hook, target) { - var _a, _b; - devtools$1 = hook; - if (devtools$1) { - devtools$1.enabled = true; - buffer.forEach(({ event, args }) => devtools$1.emit(event, ...args)); - buffer = []; - } else if ( - // handle late devtools injection - only do this if we are in an actual - // browser environment to avoid the timer handle stalling test runner exit - // (#4815) - typeof window !== "undefined" && // some envs mock window but not fully - window.HTMLElement && // also exclude jsdom - // eslint-disable-next-line no-restricted-syntax - !((_b = (_a = window.navigator) == null ? void 0 : _a.userAgent) == null ? void 0 : _b.includes("jsdom")) - ) { - const replay = target.__VUE_DEVTOOLS_HOOK_REPLAY__ = target.__VUE_DEVTOOLS_HOOK_REPLAY__ || []; - replay.push((newHook) => { - setDevtoolsHook$1(newHook, target); - }); - setTimeout(() => { - if (!devtools$1) { - target.__VUE_DEVTOOLS_HOOK_REPLAY__ = null; - devtoolsNotInstalled = true; - buffer = []; - } - }, 3e3); - } else { - devtoolsNotInstalled = true; - buffer = []; - } -} -function devtoolsInitApp(app, version2) { - emit$1("app:init", app, version2, { - Fragment, - Text, - Comment, - Static - }); -} -function devtoolsUnmountApp(app) { - emit$1("app:unmount", app); -} -var devtoolsComponentAdded = createDevtoolsComponentHook( - "component:added" - /* COMPONENT_ADDED */ -); -var devtoolsComponentUpdated = createDevtoolsComponentHook( - "component:updated" - /* COMPONENT_UPDATED */ -); -var _devtoolsComponentRemoved = createDevtoolsComponentHook( - "component:removed" - /* COMPONENT_REMOVED */ -); -var devtoolsComponentRemoved = (component) => { - if (devtools$1 && typeof devtools$1.cleanupBuffer === "function" && // remove the component if it wasn't buffered - !devtools$1.cleanupBuffer(component)) { - _devtoolsComponentRemoved(component); - } -}; -function createDevtoolsComponentHook(hook) { - return (component) => { - emit$1( - hook, - component.appContext.app, - component.uid, - component.parent ? component.parent.uid : void 0, - component - ); - }; -} -var devtoolsPerfStart = createDevtoolsPerformanceHook( - "perf:start" - /* PERFORMANCE_START */ -); -var devtoolsPerfEnd = createDevtoolsPerformanceHook( - "perf:end" - /* PERFORMANCE_END */ -); -function createDevtoolsPerformanceHook(hook) { - return (component, type, time) => { - emit$1(hook, component.appContext.app, component.uid, component, type, time); - }; -} -function devtoolsComponentEmit(component, event, params) { - emit$1( - "component:emit", - component.appContext.app, - component, - event, - params - ); -} -var currentRenderingInstance = null; -var currentScopeId = null; -function setCurrentRenderingInstance(instance) { - const prev = currentRenderingInstance; - currentRenderingInstance = instance; - currentScopeId = instance && instance.type.__scopeId || null; - return prev; -} -function pushScopeId(id) { - currentScopeId = id; -} -function popScopeId() { - currentScopeId = null; -} -var withScopeId = (_id) => withCtx; -function withCtx(fn, ctx = currentRenderingInstance, isNonScopedSlot) { - if (!ctx) return fn; - if (fn._n) { - return fn; - } - const renderFnWithContext = (...args) => { - if (renderFnWithContext._d) { - setBlockTracking(-1); - } - const prevInstance = setCurrentRenderingInstance(ctx); - let res; - try { - res = fn(...args); - } finally { - setCurrentRenderingInstance(prevInstance); - if (renderFnWithContext._d) { - setBlockTracking(1); - } - } - if (true) { - devtoolsComponentUpdated(ctx); - } - return res; - }; - renderFnWithContext._n = true; - renderFnWithContext._c = true; - renderFnWithContext._d = true; - return renderFnWithContext; -} -function validateDirectiveName(name) { - if (isBuiltInDirective(name)) { - warn$1("Do not use built-in directive ids as custom directive id: " + name); - } -} -function withDirectives(vnode, directives) { - if (currentRenderingInstance === null) { - warn$1(`withDirectives can only be used inside render functions.`); - return vnode; - } - const instance = getComponentPublicInstance(currentRenderingInstance); - const bindings = vnode.dirs || (vnode.dirs = []); - for (let i = 0; i < directives.length; i++) { - let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]; - if (dir) { - if (isFunction(dir)) { - dir = { - mounted: dir, - updated: dir - }; - } - if (dir.deep) { - traverse(value); - } - bindings.push({ - dir, - instance, - value, - oldValue: void 0, - arg, - modifiers - }); - } - } - return vnode; -} -function invokeDirectiveHook(vnode, prevVNode, instance, name) { - const bindings = vnode.dirs; - const oldBindings = prevVNode && prevVNode.dirs; - for (let i = 0; i < bindings.length; i++) { - const binding = bindings[i]; - if (oldBindings) { - binding.oldValue = oldBindings[i].value; - } - let hook = binding.dir[name]; - if (hook) { - pauseTracking(); - callWithAsyncErrorHandling(hook, instance, 8, [ - vnode.el, - binding, - vnode, - prevVNode - ]); - resetTracking(); - } - } -} -function provide(key, value) { - if (true) { - if (!currentInstance || currentInstance.isMounted) { - warn$1(`provide() can only be used inside setup().`); - } - } - if (currentInstance) { - let provides = currentInstance.provides; - const parentProvides = currentInstance.parent && currentInstance.parent.provides; - if (parentProvides === provides) { - provides = currentInstance.provides = Object.create(parentProvides); - } - provides[key] = value; - } -} -function inject(key, defaultValue, treatDefaultAsFactory = false) { - const instance = getCurrentInstance(); - if (instance || currentApp) { - let provides = currentApp ? currentApp._context.provides : instance ? instance.parent == null || instance.ce ? instance.vnode.appContext && instance.vnode.appContext.provides : instance.parent.provides : void 0; - if (provides && key in provides) { - return provides[key]; - } else if (arguments.length > 1) { - return treatDefaultAsFactory && isFunction(defaultValue) ? defaultValue.call(instance && instance.proxy) : defaultValue; - } else if (true) { - warn$1(`injection "${String(key)}" not found.`); - } - } else if (true) { - warn$1(`inject() can only be used inside setup() or functional components.`); - } -} -function hasInjectionContext() { - return !!(getCurrentInstance() || currentApp); -} -var ssrContextKey = Symbol.for("v-scx"); -var useSSRContext = () => { - { - const ctx = inject(ssrContextKey); - if (!ctx) { - warn$1( - `Server rendering context not provided. Make sure to only call useSSRContext() conditionally in the server build.` - ); - } - return ctx; - } -}; -function watchEffect(effect2, options) { - return doWatch(effect2, null, options); -} -function watchPostEffect(effect2, options) { - return doWatch( - effect2, - null, - true ? extend({}, options, { flush: "post" }) : { flush: "post" } - ); -} -function watchSyncEffect(effect2, options) { - return doWatch( - effect2, - null, - true ? extend({}, options, { flush: "sync" }) : { flush: "sync" } - ); -} -function watch2(source, cb, options) { - if (!isFunction(cb)) { - warn$1( - `\`watch(fn, options?)\` signature has been moved to a separate API. Use \`watchEffect(fn, options?)\` instead. \`watch\` now only supports \`watch(source, cb, options?) signature.` - ); - } - return doWatch(source, cb, options); -} -function doWatch(source, cb, options = EMPTY_OBJ) { - const { immediate, deep, flush, once } = options; - if (!cb) { - if (immediate !== void 0) { - warn$1( - `watch() "immediate" option is only respected when using the watch(source, callback, options?) signature.` - ); - } - if (deep !== void 0) { - warn$1( - `watch() "deep" option is only respected when using the watch(source, callback, options?) signature.` - ); - } - if (once !== void 0) { - warn$1( - `watch() "once" option is only respected when using the watch(source, callback, options?) signature.` - ); - } - } - const baseWatchOptions = extend({}, options); - if (true) baseWatchOptions.onWarn = warn$1; - const runsImmediately = cb && immediate || !cb && flush !== "post"; - let ssrCleanup; - if (isInSSRComponentSetup) { - if (flush === "sync") { - const ctx = useSSRContext(); - ssrCleanup = ctx.__watcherHandles || (ctx.__watcherHandles = []); - } else if (!runsImmediately) { - const watchStopHandle = () => { - }; - watchStopHandle.stop = NOOP; - watchStopHandle.resume = NOOP; - watchStopHandle.pause = NOOP; - return watchStopHandle; - } - } - const instance = currentInstance; - baseWatchOptions.call = (fn, type, args) => callWithAsyncErrorHandling(fn, instance, type, args); - let isPre = false; - if (flush === "post") { - baseWatchOptions.scheduler = (job) => { - queuePostRenderEffect(job, instance && instance.suspense); - }; - } else if (flush !== "sync") { - isPre = true; - baseWatchOptions.scheduler = (job, isFirstRun) => { - if (isFirstRun) { - job(); - } else { - queueJob(job); - } - }; - } - baseWatchOptions.augmentJob = (job) => { - if (cb) { - job.flags |= 4; - } - if (isPre) { - job.flags |= 2; - if (instance) { - job.id = instance.uid; - job.i = instance; - } - } - }; - const watchHandle = watch(source, cb, baseWatchOptions); - if (isInSSRComponentSetup) { - if (ssrCleanup) { - ssrCleanup.push(watchHandle); - } else if (runsImmediately) { - watchHandle(); - } - } - return watchHandle; -} -function instanceWatch(source, value, options) { - const publicThis = this.proxy; - const getter = isString(source) ? source.includes(".") ? createPathGetter(publicThis, source) : () => publicThis[source] : source.bind(publicThis, publicThis); - let cb; - if (isFunction(value)) { - cb = value; - } else { - cb = value.handler; - options = value; - } - const reset = setCurrentInstance(this); - const res = doWatch(getter, cb.bind(publicThis), options); - reset(); - return res; -} -function createPathGetter(ctx, path) { - const segments = path.split("."); - return () => { - let cur = ctx; - for (let i = 0; i < segments.length && cur; i++) { - cur = cur[segments[i]]; - } - return cur; - }; -} -var TeleportEndKey = Symbol("_vte"); -var isTeleport = (type) => type.__isTeleport; -var isTeleportDisabled = (props) => props && (props.disabled || props.disabled === ""); -var isTeleportDeferred = (props) => props && (props.defer || props.defer === ""); -var isTargetSVG = (target) => typeof SVGElement !== "undefined" && target instanceof SVGElement; -var isTargetMathML = (target) => typeof MathMLElement === "function" && target instanceof MathMLElement; -var resolveTarget = (props, select) => { - const targetSelector = props && props.to; - if (isString(targetSelector)) { - if (!select) { - warn$1( - `Current renderer does not support string target for Teleports. (missing querySelector renderer option)` - ); - return null; - } else { - const target = select(targetSelector); - if (!target && !isTeleportDisabled(props)) { - warn$1( - `Failed to locate Teleport target with selector "${targetSelector}". Note the target element must exist before the component is mounted - i.e. the target cannot be rendered by the component itself, and ideally should be outside of the entire Vue component tree.` - ); - } - return target; - } - } else { - if (!targetSelector && !isTeleportDisabled(props)) { - warn$1(`Invalid Teleport target: ${targetSelector}`); - } - return targetSelector; - } -}; -var TeleportImpl = { - name: "Teleport", - __isTeleport: true, - process(n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, internals) { - const { - mc: mountChildren, - pc: patchChildren, - pbc: patchBlockChildren, - o: { insert, querySelector, createText, createComment } - } = internals; - const disabled = isTeleportDisabled(n2.props); - let { shapeFlag, children, dynamicChildren } = n2; - if (isHmrUpdating) { - optimized = false; - dynamicChildren = null; - } - if (n1 == null) { - const placeholder = n2.el = true ? createComment("teleport start") : createText(""); - const mainAnchor = n2.anchor = true ? createComment("teleport end") : createText(""); - insert(placeholder, container, anchor); - insert(mainAnchor, container, anchor); - const mount = (container2, anchor2) => { - if (shapeFlag & 16) { - mountChildren( - children, - container2, - anchor2, - parentComponent, - parentSuspense, - namespace, - slotScopeIds, - optimized - ); - } - }; - const mountToTarget = () => { - const target = n2.target = resolveTarget(n2.props, querySelector); - const targetAnchor = prepareAnchor(target, n2, createText, insert); - if (target) { - if (namespace !== "svg" && isTargetSVG(target)) { - namespace = "svg"; - } else if (namespace !== "mathml" && isTargetMathML(target)) { - namespace = "mathml"; - } - if (parentComponent && parentComponent.isCE) { - (parentComponent.ce._teleportTargets || (parentComponent.ce._teleportTargets = /* @__PURE__ */ new Set())).add(target); - } - if (!disabled) { - mount(target, targetAnchor); - updateCssVars(n2, false); - } - } else if (!disabled) { - warn$1( - "Invalid Teleport target on mount:", - target, - `(${typeof target})` - ); - } - }; - if (disabled) { - mount(container, mainAnchor); - updateCssVars(n2, true); - } - if (isTeleportDeferred(n2.props)) { - n2.el.__isMounted = false; - queuePostRenderEffect(() => { - mountToTarget(); - delete n2.el.__isMounted; - }, parentSuspense); - } else { - mountToTarget(); - } - } else { - if (isTeleportDeferred(n2.props) && n1.el.__isMounted === false) { - queuePostRenderEffect(() => { - TeleportImpl.process( - n1, - n2, - container, - anchor, - parentComponent, - parentSuspense, - namespace, - slotScopeIds, - optimized, - internals - ); - }, parentSuspense); - return; - } - n2.el = n1.el; - n2.targetStart = n1.targetStart; - const mainAnchor = n2.anchor = n1.anchor; - const target = n2.target = n1.target; - const targetAnchor = n2.targetAnchor = n1.targetAnchor; - const wasDisabled = isTeleportDisabled(n1.props); - const currentContainer = wasDisabled ? container : target; - const currentAnchor = wasDisabled ? mainAnchor : targetAnchor; - if (namespace === "svg" || isTargetSVG(target)) { - namespace = "svg"; - } else if (namespace === "mathml" || isTargetMathML(target)) { - namespace = "mathml"; - } - if (dynamicChildren) { - patchBlockChildren( - n1.dynamicChildren, - dynamicChildren, - currentContainer, - parentComponent, - parentSuspense, - namespace, - slotScopeIds - ); - traverseStaticChildren(n1, n2, false); - } else if (!optimized) { - patchChildren( - n1, - n2, - currentContainer, - currentAnchor, - parentComponent, - parentSuspense, - namespace, - slotScopeIds, - false - ); - } - if (disabled) { - if (!wasDisabled) { - moveTeleport( - n2, - container, - mainAnchor, - internals, - 1 - ); - } else { - if (n2.props && n1.props && n2.props.to !== n1.props.to) { - n2.props.to = n1.props.to; - } - } - } else { - if ((n2.props && n2.props.to) !== (n1.props && n1.props.to)) { - const nextTarget = n2.target = resolveTarget( - n2.props, - querySelector - ); - if (nextTarget) { - moveTeleport( - n2, - nextTarget, - null, - internals, - 0 - ); - } else if (true) { - warn$1( - "Invalid Teleport target on update:", - target, - `(${typeof target})` - ); - } - } else if (wasDisabled) { - moveTeleport( - n2, - target, - targetAnchor, - internals, - 1 - ); - } - } - updateCssVars(n2, disabled); - } - }, - remove(vnode, parentComponent, parentSuspense, { um: unmount, o: { remove: hostRemove } }, doRemove) { - const { - shapeFlag, - children, - anchor, - targetStart, - targetAnchor, - target, - props - } = vnode; - if (target) { - hostRemove(targetStart); - hostRemove(targetAnchor); - } - doRemove && hostRemove(anchor); - if (shapeFlag & 16) { - const shouldRemove = doRemove || !isTeleportDisabled(props); - for (let i = 0; i < children.length; i++) { - const child = children[i]; - unmount( - child, - parentComponent, - parentSuspense, - shouldRemove, - !!child.dynamicChildren - ); - } - } - }, - move: moveTeleport, - hydrate: hydrateTeleport -}; -function moveTeleport(vnode, container, parentAnchor, { o: { insert }, m: move }, moveType = 2) { - if (moveType === 0) { - insert(vnode.targetAnchor, container, parentAnchor); - } - const { el, anchor, shapeFlag, children, props } = vnode; - const isReorder = moveType === 2; - if (isReorder) { - insert(el, container, parentAnchor); - } - if (!isReorder || isTeleportDisabled(props)) { - if (shapeFlag & 16) { - for (let i = 0; i < children.length; i++) { - move( - children[i], - container, - parentAnchor, - 2 - ); - } - } - } - if (isReorder) { - insert(anchor, container, parentAnchor); - } -} -function hydrateTeleport(node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized, { - o: { nextSibling, parentNode, querySelector, insert, createText } -}, hydrateChildren) { - function hydrateDisabledTeleport(node2, vnode2, targetStart, targetAnchor) { - vnode2.anchor = hydrateChildren( - nextSibling(node2), - vnode2, - parentNode(node2), - parentComponent, - parentSuspense, - slotScopeIds, - optimized - ); - vnode2.targetStart = targetStart; - vnode2.targetAnchor = targetAnchor; - } - const target = vnode.target = resolveTarget( - vnode.props, - querySelector - ); - const disabled = isTeleportDisabled(vnode.props); - if (target) { - const targetNode = target._lpa || target.firstChild; - if (vnode.shapeFlag & 16) { - if (disabled) { - hydrateDisabledTeleport( - node, - vnode, - targetNode, - targetNode && nextSibling(targetNode) - ); - } else { - vnode.anchor = nextSibling(node); - let targetAnchor = targetNode; - while (targetAnchor) { - if (targetAnchor && targetAnchor.nodeType === 8) { - if (targetAnchor.data === "teleport start anchor") { - vnode.targetStart = targetAnchor; - } else if (targetAnchor.data === "teleport anchor") { - vnode.targetAnchor = targetAnchor; - target._lpa = vnode.targetAnchor && nextSibling(vnode.targetAnchor); - break; - } - } - targetAnchor = nextSibling(targetAnchor); - } - if (!vnode.targetAnchor) { - prepareAnchor(target, vnode, createText, insert); - } - hydrateChildren( - targetNode && nextSibling(targetNode), - vnode, - target, - parentComponent, - parentSuspense, - slotScopeIds, - optimized - ); - } - } - updateCssVars(vnode, disabled); - } else if (disabled) { - if (vnode.shapeFlag & 16) { - hydrateDisabledTeleport(node, vnode, node, nextSibling(node)); - } - } - return vnode.anchor && nextSibling(vnode.anchor); -} -var Teleport = TeleportImpl; -function updateCssVars(vnode, isDisabled) { - const ctx = vnode.ctx; - if (ctx && ctx.ut) { - let node, anchor; - if (isDisabled) { - node = vnode.el; - anchor = vnode.anchor; - } else { - node = vnode.targetStart; - anchor = vnode.targetAnchor; - } - while (node && node !== anchor) { - if (node.nodeType === 1) node.setAttribute("data-v-owner", ctx.uid); - node = node.nextSibling; - } - ctx.ut(); - } -} -function prepareAnchor(target, vnode, createText, insert) { - const targetStart = vnode.targetStart = createText(""); - const targetAnchor = vnode.targetAnchor = createText(""); - targetStart[TeleportEndKey] = targetAnchor; - if (target) { - insert(targetStart, target); - insert(targetAnchor, target); - } - return targetAnchor; -} -var leaveCbKey = Symbol("_leaveCb"); -var enterCbKey = Symbol("_enterCb"); -function useTransitionState() { - const state = { - isMounted: false, - isLeaving: false, - isUnmounting: false, - leavingVNodes: /* @__PURE__ */ new Map() - }; - onMounted(() => { - state.isMounted = true; - }); - onBeforeUnmount(() => { - state.isUnmounting = true; - }); - return state; -} -var TransitionHookValidator = [Function, Array]; -var BaseTransitionPropsValidators = { - mode: String, - appear: Boolean, - persisted: Boolean, - // enter - onBeforeEnter: TransitionHookValidator, - onEnter: TransitionHookValidator, - onAfterEnter: TransitionHookValidator, - onEnterCancelled: TransitionHookValidator, - // leave - onBeforeLeave: TransitionHookValidator, - onLeave: TransitionHookValidator, - onAfterLeave: TransitionHookValidator, - onLeaveCancelled: TransitionHookValidator, - // appear - onBeforeAppear: TransitionHookValidator, - onAppear: TransitionHookValidator, - onAfterAppear: TransitionHookValidator, - onAppearCancelled: TransitionHookValidator -}; -var recursiveGetSubtree = (instance) => { - const subTree = instance.subTree; - return subTree.component ? recursiveGetSubtree(subTree.component) : subTree; -}; -var BaseTransitionImpl = { - name: `BaseTransition`, - props: BaseTransitionPropsValidators, - setup(props, { slots }) { - const instance = getCurrentInstance(); - const state = useTransitionState(); - return () => { - const children = slots.default && getTransitionRawChildren(slots.default(), true); - if (!children || !children.length) { - return; - } - const child = findNonCommentChild(children); - const rawProps = toRaw(props); - const { mode } = rawProps; - if (mode && mode !== "in-out" && mode !== "out-in" && mode !== "default") { - warn$1(`invalid mode: ${mode}`); - } - if (state.isLeaving) { - return emptyPlaceholder(child); - } - const innerChild = getInnerChild$1(child); - if (!innerChild) { - return emptyPlaceholder(child); - } - let enterHooks = resolveTransitionHooks( - innerChild, - rawProps, - state, - instance, - // #11061, ensure enterHooks is fresh after clone - (hooks) => enterHooks = hooks - ); - if (innerChild.type !== Comment) { - setTransitionHooks(innerChild, enterHooks); - } - let oldInnerChild = instance.subTree && getInnerChild$1(instance.subTree); - if (oldInnerChild && oldInnerChild.type !== Comment && !isSameVNodeType(oldInnerChild, innerChild) && recursiveGetSubtree(instance).type !== Comment) { - let leavingHooks = resolveTransitionHooks( - oldInnerChild, - rawProps, - state, - instance - ); - setTransitionHooks(oldInnerChild, leavingHooks); - if (mode === "out-in" && innerChild.type !== Comment) { - state.isLeaving = true; - leavingHooks.afterLeave = () => { - state.isLeaving = false; - if (!(instance.job.flags & 8)) { - instance.update(); - } - delete leavingHooks.afterLeave; - oldInnerChild = void 0; - }; - return emptyPlaceholder(child); - } else if (mode === "in-out" && innerChild.type !== Comment) { - leavingHooks.delayLeave = (el, earlyRemove, delayedLeave) => { - const leavingVNodesCache = getLeavingNodesForType( - state, - oldInnerChild - ); - leavingVNodesCache[String(oldInnerChild.key)] = oldInnerChild; - el[leaveCbKey] = () => { - earlyRemove(); - el[leaveCbKey] = void 0; - delete enterHooks.delayedLeave; - oldInnerChild = void 0; - }; - enterHooks.delayedLeave = () => { - delayedLeave(); - delete enterHooks.delayedLeave; - oldInnerChild = void 0; - }; - }; - } else { - oldInnerChild = void 0; - } - } else if (oldInnerChild) { - oldInnerChild = void 0; - } - return child; - }; - } -}; -function findNonCommentChild(children) { - let child = children[0]; - if (children.length > 1) { - let hasFound = false; - for (const c of children) { - if (c.type !== Comment) { - if (hasFound) { - warn$1( - " can only be used on a single element or component. Use for lists." - ); - break; - } - child = c; - hasFound = true; - if (false) break; - } - } - } - return child; -} -var BaseTransition = BaseTransitionImpl; -function getLeavingNodesForType(state, vnode) { - const { leavingVNodes } = state; - let leavingVNodesCache = leavingVNodes.get(vnode.type); - if (!leavingVNodesCache) { - leavingVNodesCache = /* @__PURE__ */ Object.create(null); - leavingVNodes.set(vnode.type, leavingVNodesCache); - } - return leavingVNodesCache; -} -function resolveTransitionHooks(vnode, props, state, instance, postClone) { - const { - appear, - mode, - persisted = false, - onBeforeEnter, - onEnter, - onAfterEnter, - onEnterCancelled, - onBeforeLeave, - onLeave, - onAfterLeave, - onLeaveCancelled, - onBeforeAppear, - onAppear, - onAfterAppear, - onAppearCancelled - } = props; - const key = String(vnode.key); - const leavingVNodesCache = getLeavingNodesForType(state, vnode); - const callHook3 = (hook, args) => { - hook && callWithAsyncErrorHandling( - hook, - instance, - 9, - args - ); - }; - const callAsyncHook = (hook, args) => { - const done = args[1]; - callHook3(hook, args); - if (isArray(hook)) { - if (hook.every((hook2) => hook2.length <= 1)) done(); - } else if (hook.length <= 1) { - done(); - } - }; - const hooks = { - mode, - persisted, - beforeEnter(el) { - let hook = onBeforeEnter; - if (!state.isMounted) { - if (appear) { - hook = onBeforeAppear || onBeforeEnter; - } else { - return; - } - } - if (el[leaveCbKey]) { - el[leaveCbKey]( - true - /* cancelled */ - ); - } - const leavingVNode = leavingVNodesCache[key]; - if (leavingVNode && isSameVNodeType(vnode, leavingVNode) && leavingVNode.el[leaveCbKey]) { - leavingVNode.el[leaveCbKey](); - } - callHook3(hook, [el]); - }, - enter(el) { - let hook = onEnter; - let afterHook = onAfterEnter; - let cancelHook = onEnterCancelled; - if (!state.isMounted) { - if (appear) { - hook = onAppear || onEnter; - afterHook = onAfterAppear || onAfterEnter; - cancelHook = onAppearCancelled || onEnterCancelled; - } else { - return; - } - } - let called = false; - const done = el[enterCbKey] = (cancelled) => { - if (called) return; - called = true; - if (cancelled) { - callHook3(cancelHook, [el]); - } else { - callHook3(afterHook, [el]); - } - if (hooks.delayedLeave) { - hooks.delayedLeave(); - } - el[enterCbKey] = void 0; - }; - if (hook) { - callAsyncHook(hook, [el, done]); - } else { - done(); - } - }, - leave(el, remove2) { - const key2 = String(vnode.key); - if (el[enterCbKey]) { - el[enterCbKey]( - true - /* cancelled */ - ); - } - if (state.isUnmounting) { - return remove2(); - } - callHook3(onBeforeLeave, [el]); - let called = false; - const done = el[leaveCbKey] = (cancelled) => { - if (called) return; - called = true; - remove2(); - if (cancelled) { - callHook3(onLeaveCancelled, [el]); - } else { - callHook3(onAfterLeave, [el]); - } - el[leaveCbKey] = void 0; - if (leavingVNodesCache[key2] === vnode) { - delete leavingVNodesCache[key2]; - } - }; - leavingVNodesCache[key2] = vnode; - if (onLeave) { - callAsyncHook(onLeave, [el, done]); - } else { - done(); - } - }, - clone(vnode2) { - const hooks2 = resolveTransitionHooks( - vnode2, - props, - state, - instance, - postClone - ); - if (postClone) postClone(hooks2); - return hooks2; - } - }; - return hooks; -} -function emptyPlaceholder(vnode) { - if (isKeepAlive(vnode)) { - vnode = cloneVNode(vnode); - vnode.children = null; - return vnode; - } -} -function getInnerChild$1(vnode) { - if (!isKeepAlive(vnode)) { - if (isTeleport(vnode.type) && vnode.children) { - return findNonCommentChild(vnode.children); - } - return vnode; - } - if (vnode.component) { - return vnode.component.subTree; - } - const { shapeFlag, children } = vnode; - if (children) { - if (shapeFlag & 16) { - return children[0]; - } - if (shapeFlag & 32 && isFunction(children.default)) { - return children.default(); - } - } -} -function setTransitionHooks(vnode, hooks) { - if (vnode.shapeFlag & 6 && vnode.component) { - vnode.transition = hooks; - setTransitionHooks(vnode.component.subTree, hooks); - } else if (vnode.shapeFlag & 128) { - vnode.ssContent.transition = hooks.clone(vnode.ssContent); - vnode.ssFallback.transition = hooks.clone(vnode.ssFallback); - } else { - vnode.transition = hooks; - } -} -function getTransitionRawChildren(children, keepComment = false, parentKey) { - let ret = []; - let keyedFragmentCount = 0; - for (let i = 0; i < children.length; i++) { - let child = children[i]; - const key = parentKey == null ? child.key : String(parentKey) + String(child.key != null ? child.key : i); - if (child.type === Fragment) { - if (child.patchFlag & 128) keyedFragmentCount++; - ret = ret.concat( - getTransitionRawChildren(child.children, keepComment, key) - ); - } else if (keepComment || child.type !== Comment) { - ret.push(key != null ? cloneVNode(child, { key }) : child); - } - } - if (keyedFragmentCount > 1) { - for (let i = 0; i < ret.length; i++) { - ret[i].patchFlag = -2; - } - } - return ret; -} -function defineComponent(options, extraOptions) { - return isFunction(options) ? ( - // #8236: extend call and options.name access are considered side-effects - // by Rollup, so we have to wrap it in a pure-annotated IIFE. - (() => extend({ name: options.name }, extraOptions, { setup: options }))() - ) : options; -} -function useId() { - const i = getCurrentInstance(); - if (i) { - return (i.appContext.config.idPrefix || "v") + "-" + i.ids[0] + i.ids[1]++; - } else if (true) { - warn$1( - `useId() is called when there is no active component instance to be associated with.` - ); - } - return ""; -} -function markAsyncBoundary(instance) { - instance.ids = [instance.ids[0] + instance.ids[2]++ + "-", 0, 0]; -} -var knownTemplateRefs = /* @__PURE__ */ new WeakSet(); -function useTemplateRef(key) { - const i = getCurrentInstance(); - const r = shallowRef(null); - if (i) { - const refs = i.refs === EMPTY_OBJ ? i.refs = {} : i.refs; - let desc; - if ((desc = Object.getOwnPropertyDescriptor(refs, key)) && !desc.configurable) { - warn$1(`useTemplateRef('${key}') already exists.`); - } else { - Object.defineProperty(refs, key, { - enumerable: true, - get: () => r.value, - set: (val) => r.value = val - }); - } - } else if (true) { - warn$1( - `useTemplateRef() is called when there is no active component instance to be associated with.` - ); - } - const ret = true ? readonly(r) : r; - if (true) { - knownTemplateRefs.add(ret); - } - return ret; -} -var pendingSetRefMap = /* @__PURE__ */ new WeakMap(); -function setRef(rawRef, oldRawRef, parentSuspense, vnode, isUnmount = false) { - if (isArray(rawRef)) { - rawRef.forEach( - (r, i) => setRef( - r, - oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef), - parentSuspense, - vnode, - isUnmount - ) - ); - return; - } - if (isAsyncWrapper(vnode) && !isUnmount) { - if (vnode.shapeFlag & 512 && vnode.type.__asyncResolved && vnode.component.subTree.component) { - setRef(rawRef, oldRawRef, parentSuspense, vnode.component.subTree); - } - return; - } - const refValue = vnode.shapeFlag & 4 ? getComponentPublicInstance(vnode.component) : vnode.el; - const value = isUnmount ? null : refValue; - const { i: owner, r: ref2 } = rawRef; - if (!owner) { - warn$1( - `Missing ref owner context. ref cannot be used on hoisted vnodes. A vnode with ref must be created inside the render function.` - ); - return; - } - const oldRef = oldRawRef && oldRawRef.r; - const refs = owner.refs === EMPTY_OBJ ? owner.refs = {} : owner.refs; - const setupState = owner.setupState; - const rawSetupState = toRaw(setupState); - const canSetSetupRef = setupState === EMPTY_OBJ ? NO : (key) => { - if (true) { - if (hasOwn(rawSetupState, key) && !isRef2(rawSetupState[key])) { - warn$1( - `Template ref "${key}" used on a non-ref value. It will not work in the production build.` - ); - } - if (knownTemplateRefs.has(rawSetupState[key])) { - return false; - } - } - return hasOwn(rawSetupState, key); - }; - const canSetRef = (ref22) => { - return !knownTemplateRefs.has(ref22); - }; - if (oldRef != null && oldRef !== ref2) { - invalidatePendingSetRef(oldRawRef); - if (isString(oldRef)) { - refs[oldRef] = null; - if (canSetSetupRef(oldRef)) { - setupState[oldRef] = null; - } - } else if (isRef2(oldRef)) { - if (canSetRef(oldRef)) { - oldRef.value = null; - } - const oldRawRefAtom = oldRawRef; - if (oldRawRefAtom.k) refs[oldRawRefAtom.k] = null; - } - } - if (isFunction(ref2)) { - callWithErrorHandling(ref2, owner, 12, [value, refs]); - } else { - const _isString = isString(ref2); - const _isRef = isRef2(ref2); - if (_isString || _isRef) { - const doSet = () => { - if (rawRef.f) { - const existing = _isString ? canSetSetupRef(ref2) ? setupState[ref2] : refs[ref2] : canSetRef(ref2) || !rawRef.k ? ref2.value : refs[rawRef.k]; - if (isUnmount) { - isArray(existing) && remove(existing, refValue); - } else { - if (!isArray(existing)) { - if (_isString) { - refs[ref2] = [refValue]; - if (canSetSetupRef(ref2)) { - setupState[ref2] = refs[ref2]; - } - } else { - const newVal = [refValue]; - if (canSetRef(ref2)) { - ref2.value = newVal; - } - if (rawRef.k) refs[rawRef.k] = newVal; - } - } else if (!existing.includes(refValue)) { - existing.push(refValue); - } - } - } else if (_isString) { - refs[ref2] = value; - if (canSetSetupRef(ref2)) { - setupState[ref2] = value; - } - } else if (_isRef) { - if (canSetRef(ref2)) { - ref2.value = value; - } - if (rawRef.k) refs[rawRef.k] = value; - } else if (true) { - warn$1("Invalid template ref type:", ref2, `(${typeof ref2})`); - } - }; - if (value) { - const job = () => { - doSet(); - pendingSetRefMap.delete(rawRef); - }; - job.id = -1; - pendingSetRefMap.set(rawRef, job); - queuePostRenderEffect(job, parentSuspense); - } else { - invalidatePendingSetRef(rawRef); - doSet(); - } - } else if (true) { - warn$1("Invalid template ref type:", ref2, `(${typeof ref2})`); - } - } -} -function invalidatePendingSetRef(rawRef) { - const pendingSetRef = pendingSetRefMap.get(rawRef); - if (pendingSetRef) { - pendingSetRef.flags |= 8; - pendingSetRefMap.delete(rawRef); - } -} -var hasLoggedMismatchError = false; -var logMismatchError = () => { - if (hasLoggedMismatchError) { - return; - } - console.error("Hydration completed but contains mismatches."); - hasLoggedMismatchError = true; -}; -var isSVGContainer = (container) => container.namespaceURI.includes("svg") && container.tagName !== "foreignObject"; -var isMathMLContainer = (container) => container.namespaceURI.includes("MathML"); -var getContainerType = (container) => { - if (container.nodeType !== 1) return void 0; - if (isSVGContainer(container)) return "svg"; - if (isMathMLContainer(container)) return "mathml"; - return void 0; -}; -var isComment = (node) => node.nodeType === 8; -function createHydrationFunctions(rendererInternals) { - const { - mt: mountComponent, - p: patch, - o: { - patchProp: patchProp2, - createText, - nextSibling, - parentNode, - remove: remove2, - insert, - createComment - } - } = rendererInternals; - const hydrate2 = (vnode, container) => { - if (!container.hasChildNodes()) { - warn$1( - `Attempting to hydrate existing markup but container is empty. Performing full mount instead.` - ); - patch(null, vnode, container); - flushPostFlushCbs(); - container._vnode = vnode; - return; - } - hydrateNode(container.firstChild, vnode, null, null, null); - flushPostFlushCbs(); - container._vnode = vnode; - }; - const hydrateNode = (node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized = false) => { - optimized = optimized || !!vnode.dynamicChildren; - const isFragmentStart = isComment(node) && node.data === "["; - const onMismatch = () => handleMismatch( - node, - vnode, - parentComponent, - parentSuspense, - slotScopeIds, - isFragmentStart - ); - const { type, ref: ref2, shapeFlag, patchFlag } = vnode; - let domType = node.nodeType; - vnode.el = node; - if (true) { - def(node, "__vnode", vnode, true); - def(node, "__vueParentComponent", parentComponent, true); - } - if (patchFlag === -2) { - optimized = false; - vnode.dynamicChildren = null; - } - let nextNode = null; - switch (type) { - case Text: - if (domType !== 3) { - if (vnode.children === "") { - insert(vnode.el = createText(""), parentNode(node), node); - nextNode = node; - } else { - nextNode = onMismatch(); - } - } else { - if (node.data !== vnode.children) { - warn$1( - `Hydration text mismatch in`, - node.parentNode, - ` - - rendered on server: ${JSON.stringify( - node.data - )} - - expected on client: ${JSON.stringify(vnode.children)}` - ); - logMismatchError(); - node.data = vnode.children; - } - nextNode = nextSibling(node); - } - break; - case Comment: - if (isTemplateNode(node)) { - nextNode = nextSibling(node); - replaceNode( - vnode.el = node.content.firstChild, - node, - parentComponent - ); - } else if (domType !== 8 || isFragmentStart) { - nextNode = onMismatch(); - } else { - nextNode = nextSibling(node); - } - break; - case Static: - if (isFragmentStart) { - node = nextSibling(node); - domType = node.nodeType; - } - if (domType === 1 || domType === 3) { - nextNode = node; - const needToAdoptContent = !vnode.children.length; - for (let i = 0; i < vnode.staticCount; i++) { - if (needToAdoptContent) - vnode.children += nextNode.nodeType === 1 ? nextNode.outerHTML : nextNode.data; - if (i === vnode.staticCount - 1) { - vnode.anchor = nextNode; - } - nextNode = nextSibling(nextNode); - } - return isFragmentStart ? nextSibling(nextNode) : nextNode; - } else { - onMismatch(); - } - break; - case Fragment: - if (!isFragmentStart) { - nextNode = onMismatch(); - } else { - nextNode = hydrateFragment( - node, - vnode, - parentComponent, - parentSuspense, - slotScopeIds, - optimized - ); - } - break; - default: - if (shapeFlag & 1) { - if ((domType !== 1 || vnode.type.toLowerCase() !== node.tagName.toLowerCase()) && !isTemplateNode(node)) { - nextNode = onMismatch(); - } else { - nextNode = hydrateElement( - node, - vnode, - parentComponent, - parentSuspense, - slotScopeIds, - optimized - ); - } - } else if (shapeFlag & 6) { - vnode.slotScopeIds = slotScopeIds; - const container = parentNode(node); - if (isFragmentStart) { - nextNode = locateClosingAnchor(node); - } else if (isComment(node) && node.data === "teleport start") { - nextNode = locateClosingAnchor(node, node.data, "teleport end"); - } else { - nextNode = nextSibling(node); - } - mountComponent( - vnode, - container, - null, - parentComponent, - parentSuspense, - getContainerType(container), - optimized - ); - if (isAsyncWrapper(vnode) && !vnode.type.__asyncResolved) { - let subTree; - if (isFragmentStart) { - subTree = createVNode(Fragment); - subTree.anchor = nextNode ? nextNode.previousSibling : container.lastChild; - } else { - subTree = node.nodeType === 3 ? createTextVNode("") : createVNode("div"); - } - subTree.el = node; - vnode.component.subTree = subTree; - } - } else if (shapeFlag & 64) { - if (domType !== 8) { - nextNode = onMismatch(); - } else { - nextNode = vnode.type.hydrate( - node, - vnode, - parentComponent, - parentSuspense, - slotScopeIds, - optimized, - rendererInternals, - hydrateChildren - ); - } - } else if (shapeFlag & 128) { - nextNode = vnode.type.hydrate( - node, - vnode, - parentComponent, - parentSuspense, - getContainerType(parentNode(node)), - slotScopeIds, - optimized, - rendererInternals, - hydrateNode - ); - } else if (true) { - warn$1("Invalid HostVNode type:", type, `(${typeof type})`); - } - } - if (ref2 != null) { - setRef(ref2, null, parentSuspense, vnode); - } - return nextNode; - }; - const hydrateElement = (el, vnode, parentComponent, parentSuspense, slotScopeIds, optimized) => { - optimized = optimized || !!vnode.dynamicChildren; - const { type, props, patchFlag, shapeFlag, dirs, transition } = vnode; - const forcePatch = type === "input" || type === "option"; - if (true) { - if (dirs) { - invokeDirectiveHook(vnode, null, parentComponent, "created"); - } - let needCallTransitionHooks = false; - if (isTemplateNode(el)) { - needCallTransitionHooks = needTransition( - null, - // no need check parentSuspense in hydration - transition - ) && parentComponent && parentComponent.vnode.props && parentComponent.vnode.props.appear; - const content = el.content.firstChild; - if (needCallTransitionHooks) { - const cls = content.getAttribute("class"); - if (cls) content.$cls = cls; - transition.beforeEnter(content); - } - replaceNode(content, el, parentComponent); - vnode.el = el = content; - } - if (shapeFlag & 16 && // skip if element has innerHTML / textContent - !(props && (props.innerHTML || props.textContent))) { - let next = hydrateChildren( - el.firstChild, - vnode, - el, - parentComponent, - parentSuspense, - slotScopeIds, - optimized - ); - let hasWarned2 = false; - while (next) { - if (!isMismatchAllowed( - el, - 1 - /* CHILDREN */ - )) { - if (!hasWarned2) { - warn$1( - `Hydration children mismatch on`, - el, - ` -Server rendered element contains more child nodes than client vdom.` - ); - hasWarned2 = true; - } - logMismatchError(); - } - const cur = next; - next = next.nextSibling; - remove2(cur); - } - } else if (shapeFlag & 8) { - let clientText = vnode.children; - if (clientText[0] === "\n" && (el.tagName === "PRE" || el.tagName === "TEXTAREA")) { - clientText = clientText.slice(1); - } - const { textContent } = el; - if (textContent !== clientText && // innerHTML normalize \r\n or \r into a single \n in the DOM - textContent !== clientText.replace(/\r\n|\r/g, "\n")) { - if (!isMismatchAllowed( - el, - 0 - /* TEXT */ - )) { - warn$1( - `Hydration text content mismatch on`, - el, - ` - - rendered on server: ${textContent} - - expected on client: ${clientText}` - ); - logMismatchError(); - } - el.textContent = vnode.children; - } - } - if (props) { - if (true) { - const isCustomElement = el.tagName.includes("-"); - for (const key in props) { - if (// #11189 skip if this node has directives that have created hooks - // as it could have mutated the DOM in any possible way - !(dirs && dirs.some((d) => d.dir.created)) && propHasMismatch(el, key, props[key], vnode, parentComponent)) { - logMismatchError(); - } - if (forcePatch && (key.endsWith("value") || key === "indeterminate") || isOn(key) && !isReservedProp(key) || // force hydrate v-bind with .prop modifiers - key[0] === "." || isCustomElement) { - patchProp2(el, key, null, props[key], void 0, parentComponent); - } - } - } else if (props.onClick) { - patchProp2( - el, - "onClick", - null, - props.onClick, - void 0, - parentComponent - ); - } else if (patchFlag & 4 && isReactive(props.style)) { - for (const key in props.style) props.style[key]; - } - } - let vnodeHooks; - if (vnodeHooks = props && props.onVnodeBeforeMount) { - invokeVNodeHook(vnodeHooks, parentComponent, vnode); - } - if (dirs) { - invokeDirectiveHook(vnode, null, parentComponent, "beforeMount"); - } - if ((vnodeHooks = props && props.onVnodeMounted) || dirs || needCallTransitionHooks) { - queueEffectWithSuspense(() => { - vnodeHooks && invokeVNodeHook(vnodeHooks, parentComponent, vnode); - needCallTransitionHooks && transition.enter(el); - dirs && invokeDirectiveHook(vnode, null, parentComponent, "mounted"); - }, parentSuspense); - } - } - return el.nextSibling; - }; - const hydrateChildren = (node, parentVNode, container, parentComponent, parentSuspense, slotScopeIds, optimized) => { - optimized = optimized || !!parentVNode.dynamicChildren; - const children = parentVNode.children; - const l = children.length; - let hasWarned2 = false; - for (let i = 0; i < l; i++) { - const vnode = optimized ? children[i] : children[i] = normalizeVNode(children[i]); - const isText = vnode.type === Text; - if (node) { - if (isText && !optimized) { - if (i + 1 < l && normalizeVNode(children[i + 1]).type === Text) { - insert( - createText( - node.data.slice(vnode.children.length) - ), - container, - nextSibling(node) - ); - node.data = vnode.children; - } - } - node = hydrateNode( - node, - vnode, - parentComponent, - parentSuspense, - slotScopeIds, - optimized - ); - } else if (isText && !vnode.children) { - insert(vnode.el = createText(""), container); - } else { - if (!isMismatchAllowed( - container, - 1 - /* CHILDREN */ - )) { - if (!hasWarned2) { - warn$1( - `Hydration children mismatch on`, - container, - ` -Server rendered element contains fewer child nodes than client vdom.` - ); - hasWarned2 = true; - } - logMismatchError(); - } - patch( - null, - vnode, - container, - null, - parentComponent, - parentSuspense, - getContainerType(container), - slotScopeIds - ); - } - } - return node; - }; - const hydrateFragment = (node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized) => { - const { slotScopeIds: fragmentSlotScopeIds } = vnode; - if (fragmentSlotScopeIds) { - slotScopeIds = slotScopeIds ? slotScopeIds.concat(fragmentSlotScopeIds) : fragmentSlotScopeIds; - } - const container = parentNode(node); - const next = hydrateChildren( - nextSibling(node), - vnode, - container, - parentComponent, - parentSuspense, - slotScopeIds, - optimized - ); - if (next && isComment(next) && next.data === "]") { - return nextSibling(vnode.anchor = next); - } else { - logMismatchError(); - insert(vnode.anchor = createComment(`]`), container, next); - return next; - } - }; - const handleMismatch = (node, vnode, parentComponent, parentSuspense, slotScopeIds, isFragment) => { - if (!isMismatchAllowed( - node.parentElement, - 1 - /* CHILDREN */ - )) { - warn$1( - `Hydration node mismatch: -- rendered on server:`, - node, - node.nodeType === 3 ? `(text)` : isComment(node) && node.data === "[" ? `(start of fragment)` : ``, - ` -- expected on client:`, - vnode.type - ); - logMismatchError(); - } - vnode.el = null; - if (isFragment) { - const end = locateClosingAnchor(node); - while (true) { - const next2 = nextSibling(node); - if (next2 && next2 !== end) { - remove2(next2); - } else { - break; - } - } - } - const next = nextSibling(node); - const container = parentNode(node); - remove2(node); - patch( - null, - vnode, - container, - next, - parentComponent, - parentSuspense, - getContainerType(container), - slotScopeIds - ); - if (parentComponent) { - parentComponent.vnode.el = vnode.el; - updateHOCHostEl(parentComponent, vnode.el); - } - return next; - }; - const locateClosingAnchor = (node, open = "[", close = "]") => { - let match = 0; - while (node) { - node = nextSibling(node); - if (node && isComment(node)) { - if (node.data === open) match++; - if (node.data === close) { - if (match === 0) { - return nextSibling(node); - } else { - match--; - } - } - } - } - return node; - }; - const replaceNode = (newNode, oldNode, parentComponent) => { - const parentNode2 = oldNode.parentNode; - if (parentNode2) { - parentNode2.replaceChild(newNode, oldNode); - } - let parent = parentComponent; - while (parent) { - if (parent.vnode.el === oldNode) { - parent.vnode.el = parent.subTree.el = newNode; - } - parent = parent.parent; - } - }; - const isTemplateNode = (node) => { - return node.nodeType === 1 && node.tagName === "TEMPLATE"; - }; - return [hydrate2, hydrateNode]; -} -function propHasMismatch(el, key, clientValue, vnode, instance) { - let mismatchType; - let mismatchKey; - let actual; - let expected; - if (key === "class") { - if (el.$cls) { - actual = el.$cls; - delete el.$cls; - } else { - actual = el.getAttribute("class"); - } - expected = normalizeClass(clientValue); - if (!isSetEqual(toClassSet(actual || ""), toClassSet(expected))) { - mismatchType = 2; - mismatchKey = `class`; - } - } else if (key === "style") { - actual = el.getAttribute("style") || ""; - expected = isString(clientValue) ? clientValue : stringifyStyle(normalizeStyle(clientValue)); - const actualMap = toStyleMap(actual); - const expectedMap = toStyleMap(expected); - if (vnode.dirs) { - for (const { dir, value } of vnode.dirs) { - if (dir.name === "show" && !value) { - expectedMap.set("display", "none"); - } - } - } - if (instance) { - resolveCssVars(instance, vnode, expectedMap); - } - if (!isMapEqual(actualMap, expectedMap)) { - mismatchType = 3; - mismatchKey = "style"; - } - } else if (el instanceof SVGElement && isKnownSvgAttr(key) || el instanceof HTMLElement && (isBooleanAttr(key) || isKnownHtmlAttr(key))) { - if (isBooleanAttr(key)) { - actual = el.hasAttribute(key); - expected = includeBooleanAttr(clientValue); - } else if (clientValue == null) { - actual = el.hasAttribute(key); - expected = false; - } else { - if (el.hasAttribute(key)) { - actual = el.getAttribute(key); - } else if (key === "value" && el.tagName === "TEXTAREA") { - actual = el.value; - } else { - actual = false; - } - expected = isRenderableAttrValue(clientValue) ? String(clientValue) : false; - } - if (actual !== expected) { - mismatchType = 4; - mismatchKey = key; - } - } - if (mismatchType != null && !isMismatchAllowed(el, mismatchType)) { - const format = (v) => v === false ? `(not rendered)` : `${mismatchKey}="${v}"`; - const preSegment = `Hydration ${MismatchTypeString[mismatchType]} mismatch on`; - const postSegment = ` - - rendered on server: ${format(actual)} - - expected on client: ${format(expected)} - Note: this mismatch is check-only. The DOM will not be rectified in production due to performance overhead. - You should fix the source of the mismatch.`; - { - warn$1(preSegment, el, postSegment); - } - return true; - } - return false; -} -function toClassSet(str) { - return new Set(str.trim().split(/\s+/)); -} -function isSetEqual(a, b) { - if (a.size !== b.size) { - return false; - } - for (const s of a) { - if (!b.has(s)) { - return false; - } - } - return true; -} -function toStyleMap(str) { - const styleMap = /* @__PURE__ */ new Map(); - for (const item of str.split(";")) { - let [key, value] = item.split(":"); - key = key.trim(); - value = value && value.trim(); - if (key && value) { - styleMap.set(key, value); - } - } - return styleMap; -} -function isMapEqual(a, b) { - if (a.size !== b.size) { - return false; - } - for (const [key, value] of a) { - if (value !== b.get(key)) { - return false; - } - } - return true; -} -function resolveCssVars(instance, vnode, expectedMap) { - const root = instance.subTree; - if (instance.getCssVars && (vnode === root || root && root.type === Fragment && root.children.includes(vnode))) { - const cssVars = instance.getCssVars(); - for (const key in cssVars) { - const value = normalizeCssVarValue(cssVars[key]); - expectedMap.set(`--${getEscapedCssVarName(key, false)}`, value); - } - } - if (vnode === root && instance.parent) { - resolveCssVars(instance.parent, instance.vnode, expectedMap); - } -} -var allowMismatchAttr = "data-allow-mismatch"; -var MismatchTypeString = { - [ - 0 - /* TEXT */ - ]: "text", - [ - 1 - /* CHILDREN */ - ]: "children", - [ - 2 - /* CLASS */ - ]: "class", - [ - 3 - /* STYLE */ - ]: "style", - [ - 4 - /* ATTRIBUTE */ - ]: "attribute" -}; -function isMismatchAllowed(el, allowedType) { - if (allowedType === 0 || allowedType === 1) { - while (el && !el.hasAttribute(allowMismatchAttr)) { - el = el.parentElement; - } - } - const allowedAttr = el && el.getAttribute(allowMismatchAttr); - if (allowedAttr == null) { - return false; - } else if (allowedAttr === "") { - return true; - } else { - const list = allowedAttr.split(","); - if (allowedType === 0 && list.includes("children")) { - return true; - } - return list.includes(MismatchTypeString[allowedType]); - } -} -var requestIdleCallback = getGlobalThis().requestIdleCallback || ((cb) => setTimeout(cb, 1)); -var cancelIdleCallback = getGlobalThis().cancelIdleCallback || ((id) => clearTimeout(id)); -var hydrateOnIdle = (timeout = 1e4) => (hydrate2) => { - const id = requestIdleCallback(hydrate2, { timeout }); - return () => cancelIdleCallback(id); -}; -function elementIsVisibleInViewport(el) { - const { top, left, bottom, right } = el.getBoundingClientRect(); - const { innerHeight, innerWidth } = window; - return (top > 0 && top < innerHeight || bottom > 0 && bottom < innerHeight) && (left > 0 && left < innerWidth || right > 0 && right < innerWidth); -} -var hydrateOnVisible = (opts) => (hydrate2, forEach) => { - const ob = new IntersectionObserver((entries) => { - for (const e of entries) { - if (!e.isIntersecting) continue; - ob.disconnect(); - hydrate2(); - break; - } - }, opts); - forEach((el) => { - if (!(el instanceof Element)) return; - if (elementIsVisibleInViewport(el)) { - hydrate2(); - ob.disconnect(); - return false; - } - ob.observe(el); - }); - return () => ob.disconnect(); -}; -var hydrateOnMediaQuery = (query) => (hydrate2) => { - if (query) { - const mql = matchMedia(query); - if (mql.matches) { - hydrate2(); - } else { - mql.addEventListener("change", hydrate2, { once: true }); - return () => mql.removeEventListener("change", hydrate2); - } - } -}; -var hydrateOnInteraction = (interactions = []) => (hydrate2, forEach) => { - if (isString(interactions)) interactions = [interactions]; - let hasHydrated = false; - const doHydrate = (e) => { - if (!hasHydrated) { - hasHydrated = true; - teardown(); - hydrate2(); - e.target.dispatchEvent(new e.constructor(e.type, e)); - } - }; - const teardown = () => { - forEach((el) => { - for (const i of interactions) { - el.removeEventListener(i, doHydrate); - } - }); - }; - forEach((el) => { - for (const i of interactions) { - el.addEventListener(i, doHydrate, { once: true }); - } - }); - return teardown; -}; -function forEachElement(node, cb) { - if (isComment(node) && node.data === "[") { - let depth = 1; - let next = node.nextSibling; - while (next) { - if (next.nodeType === 1) { - const result = cb(next); - if (result === false) { - break; - } - } else if (isComment(next)) { - if (next.data === "]") { - if (--depth === 0) break; - } else if (next.data === "[") { - depth++; - } - } - next = next.nextSibling; - } - } else { - cb(node); - } -} -var isAsyncWrapper = (i) => !!i.type.__asyncLoader; -function defineAsyncComponent(source) { - if (isFunction(source)) { - source = { loader: source }; - } - const { - loader, - loadingComponent, - errorComponent, - delay = 200, - hydrate: hydrateStrategy, - timeout, - // undefined = never times out - suspensible = true, - onError: userOnError - } = source; - let pendingRequest = null; - let resolvedComp; - let retries = 0; - const retry = () => { - retries++; - pendingRequest = null; - return load(); - }; - const load = () => { - let thisRequest; - return pendingRequest || (thisRequest = pendingRequest = loader().catch((err) => { - err = err instanceof Error ? err : new Error(String(err)); - if (userOnError) { - return new Promise((resolve2, reject) => { - const userRetry = () => resolve2(retry()); - const userFail = () => reject(err); - userOnError(err, userRetry, userFail, retries + 1); - }); - } else { - throw err; - } - }).then((comp) => { - if (thisRequest !== pendingRequest && pendingRequest) { - return pendingRequest; - } - if (!comp) { - warn$1( - `Async component loader resolved to undefined. If you are using retry(), make sure to return its return value.` - ); - } - if (comp && (comp.__esModule || comp[Symbol.toStringTag] === "Module")) { - comp = comp.default; - } - if (comp && !isObject(comp) && !isFunction(comp)) { - throw new Error(`Invalid async component load result: ${comp}`); - } - resolvedComp = comp; - return comp; - })); - }; - return defineComponent({ - name: "AsyncComponentWrapper", - __asyncLoader: load, - __asyncHydrate(el, instance, hydrate2) { - let patched = false; - (instance.bu || (instance.bu = [])).push(() => patched = true); - const performHydrate = () => { - if (patched) { - if (true) { - warn$1( - `Skipping lazy hydration for component '${getComponentName(resolvedComp) || resolvedComp.__file}': it was updated before lazy hydration performed.` - ); - } - return; - } - hydrate2(); - }; - const doHydrate = hydrateStrategy ? () => { - const teardown = hydrateStrategy( - performHydrate, - (cb) => forEachElement(el, cb) - ); - if (teardown) { - (instance.bum || (instance.bum = [])).push(teardown); - } - } : performHydrate; - if (resolvedComp) { - doHydrate(); - } else { - load().then(() => !instance.isUnmounted && doHydrate()); - } - }, - get __asyncResolved() { - return resolvedComp; - }, - setup() { - const instance = currentInstance; - markAsyncBoundary(instance); - if (resolvedComp) { - return () => createInnerComp(resolvedComp, instance); - } - const onError = (err) => { - pendingRequest = null; - handleError( - err, - instance, - 13, - !errorComponent - ); - }; - if (suspensible && instance.suspense || isInSSRComponentSetup) { - return load().then((comp) => { - return () => createInnerComp(comp, instance); - }).catch((err) => { - onError(err); - return () => errorComponent ? createVNode(errorComponent, { - error: err - }) : null; - }); - } - const loaded = ref(false); - const error = ref(); - const delayed = ref(!!delay); - if (delay) { - setTimeout(() => { - delayed.value = false; - }, delay); - } - if (timeout != null) { - setTimeout(() => { - if (!loaded.value && !error.value) { - const err = new Error( - `Async component timed out after ${timeout}ms.` - ); - onError(err); - error.value = err; - } - }, timeout); - } - load().then(() => { - loaded.value = true; - if (instance.parent && isKeepAlive(instance.parent.vnode)) { - instance.parent.update(); - } - }).catch((err) => { - onError(err); - error.value = err; - }); - return () => { - if (loaded.value && resolvedComp) { - return createInnerComp(resolvedComp, instance); - } else if (error.value && errorComponent) { - return createVNode(errorComponent, { - error: error.value - }); - } else if (loadingComponent && !delayed.value) { - return createInnerComp( - loadingComponent, - instance - ); - } - }; - } - }); -} -function createInnerComp(comp, parent) { - const { ref: ref2, props, children, ce } = parent.vnode; - const vnode = createVNode(comp, props, children); - vnode.ref = ref2; - vnode.ce = ce; - delete parent.vnode.ce; - return vnode; -} -var isKeepAlive = (vnode) => vnode.type.__isKeepAlive; -var KeepAliveImpl = { - name: `KeepAlive`, - // Marker for special handling inside the renderer. We are not using a === - // check directly on KeepAlive in the renderer, because importing it directly - // would prevent it from being tree-shaken. - __isKeepAlive: true, - props: { - include: [String, RegExp, Array], - exclude: [String, RegExp, Array], - max: [String, Number] - }, - setup(props, { slots }) { - const instance = getCurrentInstance(); - const sharedContext = instance.ctx; - if (!sharedContext.renderer) { - return () => { - const children = slots.default && slots.default(); - return children && children.length === 1 ? children[0] : children; - }; - } - const cache = /* @__PURE__ */ new Map(); - const keys = /* @__PURE__ */ new Set(); - let current = null; - if (true) { - instance.__v_cache = cache; - } - const parentSuspense = instance.suspense; - const { - renderer: { - p: patch, - m: move, - um: _unmount, - o: { createElement } - } - } = sharedContext; - const storageContainer = createElement("div"); - sharedContext.activate = (vnode, container, anchor, namespace, optimized) => { - const instance2 = vnode.component; - move(vnode, container, anchor, 0, parentSuspense); - patch( - instance2.vnode, - vnode, - container, - anchor, - instance2, - parentSuspense, - namespace, - vnode.slotScopeIds, - optimized - ); - queuePostRenderEffect(() => { - instance2.isDeactivated = false; - if (instance2.a) { - invokeArrayFns(instance2.a); - } - const vnodeHook = vnode.props && vnode.props.onVnodeMounted; - if (vnodeHook) { - invokeVNodeHook(vnodeHook, instance2.parent, vnode); - } - }, parentSuspense); - if (true) { - devtoolsComponentAdded(instance2); - } - }; - sharedContext.deactivate = (vnode) => { - const instance2 = vnode.component; - invalidateMount(instance2.m); - invalidateMount(instance2.a); - move(vnode, storageContainer, null, 1, parentSuspense); - queuePostRenderEffect(() => { - if (instance2.da) { - invokeArrayFns(instance2.da); - } - const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted; - if (vnodeHook) { - invokeVNodeHook(vnodeHook, instance2.parent, vnode); - } - instance2.isDeactivated = true; - }, parentSuspense); - if (true) { - devtoolsComponentAdded(instance2); - } - if (true) { - instance2.__keepAliveStorageContainer = storageContainer; - } - }; - function unmount(vnode) { - resetShapeFlag(vnode); - _unmount(vnode, instance, parentSuspense, true); - } - function pruneCache(filter) { - cache.forEach((vnode, key) => { - const name = getComponentName( - isAsyncWrapper(vnode) ? vnode.type.__asyncResolved || {} : vnode.type - ); - if (name && !filter(name)) { - pruneCacheEntry(key); - } - }); - } - function pruneCacheEntry(key) { - const cached = cache.get(key); - if (cached && (!current || !isSameVNodeType(cached, current))) { - unmount(cached); - } else if (current) { - resetShapeFlag(current); - } - cache.delete(key); - keys.delete(key); - } - watch2( - () => [props.include, props.exclude], - ([include, exclude]) => { - include && pruneCache((name) => matches(include, name)); - exclude && pruneCache((name) => !matches(exclude, name)); - }, - // prune post-render after `current` has been updated - { flush: "post", deep: true } - ); - let pendingCacheKey = null; - const cacheSubtree = () => { - if (pendingCacheKey != null) { - if (isSuspense(instance.subTree.type)) { - queuePostRenderEffect(() => { - cache.set(pendingCacheKey, getInnerChild(instance.subTree)); - }, instance.subTree.suspense); - } else { - cache.set(pendingCacheKey, getInnerChild(instance.subTree)); - } - } - }; - onMounted(cacheSubtree); - onUpdated(cacheSubtree); - onBeforeUnmount(() => { - cache.forEach((cached) => { - const { subTree, suspense } = instance; - const vnode = getInnerChild(subTree); - if (cached.type === vnode.type && cached.key === vnode.key) { - resetShapeFlag(vnode); - const da = vnode.component.da; - da && queuePostRenderEffect(da, suspense); - return; - } - unmount(cached); - }); - }); - return () => { - pendingCacheKey = null; - if (!slots.default) { - return current = null; - } - const children = slots.default(); - const rawVNode = children[0]; - if (children.length > 1) { - if (true) { - warn$1(`KeepAlive should contain exactly one component child.`); - } - current = null; - return children; - } else if (!isVNode(rawVNode) || !(rawVNode.shapeFlag & 4) && !(rawVNode.shapeFlag & 128)) { - current = null; - return rawVNode; - } - let vnode = getInnerChild(rawVNode); - if (vnode.type === Comment) { - current = null; - return vnode; - } - const comp = vnode.type; - const name = getComponentName( - isAsyncWrapper(vnode) ? vnode.type.__asyncResolved || {} : comp - ); - const { include, exclude, max } = props; - if (include && (!name || !matches(include, name)) || exclude && name && matches(exclude, name)) { - vnode.shapeFlag &= -257; - current = vnode; - return rawVNode; - } - const key = vnode.key == null ? comp : vnode.key; - const cachedVNode = cache.get(key); - if (vnode.el) { - vnode = cloneVNode(vnode); - if (rawVNode.shapeFlag & 128) { - rawVNode.ssContent = vnode; - } - } - pendingCacheKey = key; - if (cachedVNode) { - vnode.el = cachedVNode.el; - vnode.component = cachedVNode.component; - if (vnode.transition) { - setTransitionHooks(vnode, vnode.transition); - } - vnode.shapeFlag |= 512; - keys.delete(key); - keys.add(key); - } else { - keys.add(key); - if (max && keys.size > parseInt(max, 10)) { - pruneCacheEntry(keys.values().next().value); - } - } - vnode.shapeFlag |= 256; - current = vnode; - return isSuspense(rawVNode.type) ? rawVNode : vnode; - }; - } -}; -var KeepAlive = KeepAliveImpl; -function matches(pattern, name) { - if (isArray(pattern)) { - return pattern.some((p2) => matches(p2, name)); - } else if (isString(pattern)) { - return pattern.split(",").includes(name); - } else if (isRegExp(pattern)) { - pattern.lastIndex = 0; - return pattern.test(name); - } - return false; -} -function onActivated(hook, target) { - registerKeepAliveHook(hook, "a", target); -} -function onDeactivated(hook, target) { - registerKeepAliveHook(hook, "da", target); -} -function registerKeepAliveHook(hook, type, target = currentInstance) { - const wrappedHook = hook.__wdc || (hook.__wdc = () => { - let current = target; - while (current) { - if (current.isDeactivated) { - return; - } - current = current.parent; - } - return hook(); - }); - injectHook(type, wrappedHook, target); - if (target) { - let current = target.parent; - while (current && current.parent) { - if (isKeepAlive(current.parent.vnode)) { - injectToKeepAliveRoot(wrappedHook, type, target, current); - } - current = current.parent; - } - } -} -function injectToKeepAliveRoot(hook, type, target, keepAliveRoot) { - const injected = injectHook( - type, - hook, - keepAliveRoot, - true - /* prepend */ - ); - onUnmounted(() => { - remove(keepAliveRoot[type], injected); - }, target); -} -function resetShapeFlag(vnode) { - vnode.shapeFlag &= -257; - vnode.shapeFlag &= -513; -} -function getInnerChild(vnode) { - return vnode.shapeFlag & 128 ? vnode.ssContent : vnode; -} -function injectHook(type, hook, target = currentInstance, prepend = false) { - if (target) { - const hooks = target[type] || (target[type] = []); - const wrappedHook = hook.__weh || (hook.__weh = (...args) => { - pauseTracking(); - const reset = setCurrentInstance(target); - const res = callWithAsyncErrorHandling(hook, target, type, args); - reset(); - resetTracking(); - return res; - }); - if (prepend) { - hooks.unshift(wrappedHook); - } else { - hooks.push(wrappedHook); - } - return wrappedHook; - } else if (true) { - const apiName = toHandlerKey(ErrorTypeStrings$1[type].replace(/ hook$/, "")); - warn$1( - `${apiName} is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup(). If you are using async setup(), make sure to register lifecycle hooks before the first await statement.` - ); - } -} -var createHook = (lifecycle) => (hook, target = currentInstance) => { - if (!isInSSRComponentSetup || lifecycle === "sp") { - injectHook(lifecycle, (...args) => hook(...args), target); - } -}; -var onBeforeMount = createHook("bm"); -var onMounted = createHook("m"); -var onBeforeUpdate = createHook( - "bu" -); -var onUpdated = createHook("u"); -var onBeforeUnmount = createHook( - "bum" -); -var onUnmounted = createHook("um"); -var onServerPrefetch = createHook( - "sp" -); -var onRenderTriggered = createHook("rtg"); -var onRenderTracked = createHook("rtc"); -function onErrorCaptured(hook, target = currentInstance) { - injectHook("ec", hook, target); -} -var COMPONENTS = "components"; -var DIRECTIVES = "directives"; -function resolveComponent(name, maybeSelfReference) { - return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name; -} -var NULL_DYNAMIC_COMPONENT = Symbol.for("v-ndc"); -function resolveDynamicComponent(component) { - if (isString(component)) { - return resolveAsset(COMPONENTS, component, false) || component; - } else { - return component || NULL_DYNAMIC_COMPONENT; - } -} -function resolveDirective(name) { - return resolveAsset(DIRECTIVES, name); -} -function resolveAsset(type, name, warnMissing = true, maybeSelfReference = false) { - const instance = currentRenderingInstance || currentInstance; - if (instance) { - const Component = instance.type; - if (type === COMPONENTS) { - const selfName = getComponentName( - Component, - false - ); - if (selfName && (selfName === name || selfName === camelize(name) || selfName === capitalize(camelize(name)))) { - return Component; - } - } - const res = ( - // local registration - // check instance[type] first which is resolved for options API - resolve(instance[type] || Component[type], name) || // global registration - resolve(instance.appContext[type], name) - ); - if (!res && maybeSelfReference) { - return Component; - } - if (warnMissing && !res) { - const extra = type === COMPONENTS ? ` -If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.` : ``; - warn$1(`Failed to resolve ${type.slice(0, -1)}: ${name}${extra}`); - } - return res; - } else if (true) { - warn$1( - `resolve${capitalize(type.slice(0, -1))} can only be used in render() or setup().` - ); - } -} -function resolve(registry, name) { - return registry && (registry[name] || registry[camelize(name)] || registry[capitalize(camelize(name))]); -} -function renderList(source, renderItem, cache, index) { - let ret; - const cached = cache && cache[index]; - const sourceIsArray = isArray(source); - if (sourceIsArray || isString(source)) { - const sourceIsReactiveArray = sourceIsArray && isReactive(source); - let needsWrap = false; - let isReadonlySource = false; - if (sourceIsReactiveArray) { - needsWrap = !isShallow(source); - isReadonlySource = isReadonly(source); - source = shallowReadArray(source); - } - ret = new Array(source.length); - for (let i = 0, l = source.length; i < l; i++) { - ret[i] = renderItem( - needsWrap ? isReadonlySource ? toReadonly(toReactive(source[i])) : toReactive(source[i]) : source[i], - i, - void 0, - cached && cached[i] - ); - } - } else if (typeof source === "number") { - if (!Number.isInteger(source)) { - warn$1(`The v-for range expect an integer value but got ${source}.`); - } - ret = new Array(source); - for (let i = 0; i < source; i++) { - ret[i] = renderItem(i + 1, i, void 0, cached && cached[i]); - } - } else if (isObject(source)) { - if (source[Symbol.iterator]) { - ret = Array.from( - source, - (item, i) => renderItem(item, i, void 0, cached && cached[i]) - ); - } else { - const keys = Object.keys(source); - ret = new Array(keys.length); - for (let i = 0, l = keys.length; i < l; i++) { - const key = keys[i]; - ret[i] = renderItem(source[key], key, i, cached && cached[i]); - } - } - } else { - ret = []; - } - if (cache) { - cache[index] = ret; - } - return ret; -} -function createSlots(slots, dynamicSlots) { - for (let i = 0; i < dynamicSlots.length; i++) { - const slot = dynamicSlots[i]; - if (isArray(slot)) { - for (let j = 0; j < slot.length; j++) { - slots[slot[j].name] = slot[j].fn; - } - } else if (slot) { - slots[slot.name] = slot.key ? (...args) => { - const res = slot.fn(...args); - if (res) res.key = slot.key; - return res; - } : slot.fn; - } - } - return slots; -} -function renderSlot(slots, name, props = {}, fallback, noSlotted) { - if (currentRenderingInstance.ce || currentRenderingInstance.parent && isAsyncWrapper(currentRenderingInstance.parent) && currentRenderingInstance.parent.ce) { - const hasProps = Object.keys(props).length > 0; - if (name !== "default") props.name = name; - return openBlock(), createBlock( - Fragment, - null, - [createVNode("slot", props, fallback && fallback())], - hasProps ? -2 : 64 - ); - } - let slot = slots[name]; - if (slot && slot.length > 1) { - warn$1( - `SSR-optimized slot function detected in a non-SSR-optimized render function. You need to mark this component with $dynamic-slots in the parent template.` - ); - slot = () => []; - } - if (slot && slot._c) { - slot._d = false; - } - openBlock(); - const validSlotContent = slot && ensureValidVNode(slot(props)); - const slotKey = props.key || // slot content array of a dynamic conditional slot may have a branch - // key attached in the `createSlots` helper, respect that - validSlotContent && validSlotContent.key; - const rendered = createBlock( - Fragment, - { - key: (slotKey && !isSymbol(slotKey) ? slotKey : `_${name}`) + // #7256 force differentiate fallback content from actual content - (!validSlotContent && fallback ? "_fb" : "") - }, - validSlotContent || (fallback ? fallback() : []), - validSlotContent && slots._ === 1 ? 64 : -2 - ); - if (!noSlotted && rendered.scopeId) { - rendered.slotScopeIds = [rendered.scopeId + "-s"]; - } - if (slot && slot._c) { - slot._d = true; - } - return rendered; -} -function ensureValidVNode(vnodes) { - return vnodes.some((child) => { - if (!isVNode(child)) return true; - if (child.type === Comment) return false; - if (child.type === Fragment && !ensureValidVNode(child.children)) - return false; - return true; - }) ? vnodes : null; -} -function toHandlers(obj, preserveCaseIfNecessary) { - const ret = {}; - if (!isObject(obj)) { - warn$1(`v-on with no argument expects an object value.`); - return ret; - } - for (const key in obj) { - ret[preserveCaseIfNecessary && /[A-Z]/.test(key) ? `on:${key}` : toHandlerKey(key)] = obj[key]; - } - return ret; -} -var getPublicInstance = (i) => { - if (!i) return null; - if (isStatefulComponent(i)) return getComponentPublicInstance(i); - return getPublicInstance(i.parent); -}; -var publicPropertiesMap = ( - // Move PURE marker to new line to workaround compiler discarding it - // due to type annotation - extend(/* @__PURE__ */ Object.create(null), { - $: (i) => i, - $el: (i) => i.vnode.el, - $data: (i) => i.data, - $props: (i) => true ? shallowReadonly(i.props) : i.props, - $attrs: (i) => true ? shallowReadonly(i.attrs) : i.attrs, - $slots: (i) => true ? shallowReadonly(i.slots) : i.slots, - $refs: (i) => true ? shallowReadonly(i.refs) : i.refs, - $parent: (i) => getPublicInstance(i.parent), - $root: (i) => getPublicInstance(i.root), - $host: (i) => i.ce, - $emit: (i) => i.emit, - $options: (i) => __VUE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type, - $forceUpdate: (i) => i.f || (i.f = () => { - queueJob(i.update); - }), - $nextTick: (i) => i.n || (i.n = nextTick.bind(i.proxy)), - $watch: (i) => __VUE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP - }) -); -var isReservedPrefix = (key) => key === "_" || key === "$"; -var hasSetupBinding = (state, key) => state !== EMPTY_OBJ && !state.__isScriptSetup && hasOwn(state, key); -var PublicInstanceProxyHandlers = { - get({ _: instance }, key) { - if (key === "__v_skip") { - return true; - } - const { ctx, setupState, data, props, accessCache, type, appContext } = instance; - if (key === "__isVue") { - return true; - } - if (key[0] !== "$") { - const n = accessCache[key]; - if (n !== void 0) { - switch (n) { - case 1: - return setupState[key]; - case 2: - return data[key]; - case 4: - return ctx[key]; - case 3: - return props[key]; - } - } else if (hasSetupBinding(setupState, key)) { - accessCache[key] = 1; - return setupState[key]; - } else if (__VUE_OPTIONS_API__ && data !== EMPTY_OBJ && hasOwn(data, key)) { - accessCache[key] = 2; - return data[key]; - } else if (hasOwn(props, key)) { - accessCache[key] = 3; - return props[key]; - } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) { - accessCache[key] = 4; - return ctx[key]; - } else if (!__VUE_OPTIONS_API__ || shouldCacheAccess) { - accessCache[key] = 0; - } - } - const publicGetter = publicPropertiesMap[key]; - let cssModule, globalProperties; - if (publicGetter) { - if (key === "$attrs") { - track(instance.attrs, "get", ""); - markAttrsAccessed(); - } else if (key === "$slots") { - track(instance, "get", key); - } - return publicGetter(instance); - } else if ( - // css module (injected by vue-loader) - (cssModule = type.__cssModules) && (cssModule = cssModule[key]) - ) { - return cssModule; - } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) { - accessCache[key] = 4; - return ctx[key]; - } else if ( - // global properties - globalProperties = appContext.config.globalProperties, hasOwn(globalProperties, key) - ) { - { - return globalProperties[key]; - } - } else if (currentRenderingInstance && (!isString(key) || // #1091 avoid internal isRef/isVNode checks on component instance leading - // to infinite warning loop - key.indexOf("__v") !== 0)) { - if (data !== EMPTY_OBJ && isReservedPrefix(key[0]) && hasOwn(data, key)) { - warn$1( - `Property ${JSON.stringify( - key - )} must be accessed via $data because it starts with a reserved character ("$" or "_") and is not proxied on the render context.` - ); - } else if (instance === currentRenderingInstance) { - warn$1( - `Property ${JSON.stringify(key)} was accessed during render but is not defined on instance.` - ); - } - } - }, - set({ _: instance }, key, value) { - const { data, setupState, ctx } = instance; - if (hasSetupBinding(setupState, key)) { - setupState[key] = value; - return true; - } else if (setupState.__isScriptSetup && hasOwn(setupState, key)) { - warn$1(`Cannot mutate - - diff --git a/www/docs/.vitepress/theme/components/HeroInstall.vue b/www/docs/.vitepress/theme/components/HeroInstall.vue deleted file mode 100644 index af77098..0000000 --- a/www/docs/.vitepress/theme/components/HeroInstall.vue +++ /dev/null @@ -1,196 +0,0 @@ - - - - - diff --git a/www/docs/.vitepress/theme/components/NavBarVersion.vue b/www/docs/.vitepress/theme/components/NavBarVersion.vue deleted file mode 100644 index 415a77a..0000000 --- a/www/docs/.vitepress/theme/components/NavBarVersion.vue +++ /dev/null @@ -1,182 +0,0 @@ - - - - - diff --git a/www/docs/.vitepress/theme/custom.css b/www/docs/.vitepress/theme/custom.css deleted file mode 100644 index 0b865fa..0000000 --- a/www/docs/.vitepress/theme/custom.css +++ /dev/null @@ -1,1038 +0,0 @@ -/** - * Custom theme matching the Mist dashboard - * Color scheme: Dark purple theme with OKLCH colors - * Modern design with subtle shadows inspired by Bun - */ - -:root { - /* Light mode colors */ - --vp-c-brand-1: oklch(0.488 0.243 264.376); - --vp-c-brand-2: oklch(0.623 0.214 259.815); - --vp-c-brand-3: oklch(0.546 0.245 262.881); - --vp-c-brand-soft: oklch(0.424 0.199 265.638); - - /* Light Background Colors */ - --vp-c-bg: oklch(1 0 0); - --vp-c-bg-soft: oklch(0.98 0 0); - --vp-c-bg-mute: oklch(0.96 0 0); - --vp-c-bg-alt: oklch(0.98 0 0); - - /* Light Text Colors */ - --vp-c-text-1: oklch(0.141 0.005 285.823); - --vp-c-text-2: oklch(0.461 0.015 286.067); - --vp-c-text-3: oklch(0.556 0 0); - - /* Custom blocks text colors for light mode */ - --vp-custom-block-info-text: oklch(0.141 0.005 285.823); - --vp-custom-block-tip-text: oklch(0.141 0.005 285.823); - --vp-custom-block-warning-text: oklch(0.141 0.005 285.823); - --vp-custom-block-danger-text: oklch(0.141 0.005 285.823); - - /* Light Border Colors */ - --vp-c-border: oklch(0 0 0 / 10%); - --vp-c-divider: oklch(0 0 0 / 10%); - --vp-c-gutter: oklch(0 0 0 / 10%); - - /* Light Component Colors */ - --vp-c-default-1: oklch(0.96 0 0); - --vp-c-default-2: oklch(0.98 0 0); - --vp-c-default-3: oklch(1 0 0); - --vp-c-default-soft: oklch(0.96 0 0); - - /* Code Colors */ - --vp-code-bg: oklch(0.98 0 0); - --vp-code-color: oklch(0.488 0.243 264.376); - - /* Sidebar Colors */ - --vp-sidebar-bg-color: oklch(0.98 0 0); - - /* Custom Accent */ - --vp-c-accent: oklch(0.488 0.243 264.376); - - /* Button Colors */ - --vp-button-brand-bg: oklch(0.488 0.243 264.376); - --vp-button-brand-text: oklch(0.97 0.014 254.604); - --vp-button-brand-hover-bg: oklch(0.623 0.214 259.815); - --vp-button-brand-hover-text: oklch(0.97 0.014 254.604); - --vp-button-brand-active-bg: oklch(0.546 0.245 262.881); - - /* Border Radius */ - --vp-border-radius: 0.65rem; - - /* Shadows */ - --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.04); - --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.08); - --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12); - --shadow-xl: 0 12px 32px rgba(0, 0, 0, 0.16); -} - -.dark { - /* Dark mode colors */ - --vp-c-bg: oklch(0.141 0.005 285.823); - --vp-c-bg-soft: oklch(0.21 0.006 285.885); - --vp-c-bg-mute: oklch(0.274 0.006 286.033); - --vp-c-bg-alt: oklch(0.21 0.006 285.885); - - /* Dark Text Colors */ - --vp-c-text-1: oklch(0.985 0 0); - --vp-c-text-2: oklch(0.705 0.015 286.067); - --vp-c-text-3: oklch(0.556 0 0); - - /* Dark Border Colors */ - --vp-c-border: oklch(1 0 0 / 10%); - --vp-c-divider: oklch(1 0 0 / 10%); - --vp-c-gutter: oklch(1 0 0 / 10%); - - /* Dark Component Colors */ - --vp-c-default-1: oklch(0.274 0.006 286.033); - --vp-c-default-2: oklch(0.21 0.006 285.885); - --vp-c-default-3: oklch(0.141 0.005 285.823); - --vp-c-default-soft: oklch(0.274 0.006 286.033); - - /* Dark Code Colors */ - --vp-code-bg: oklch(0.21 0.006 285.885); - --vp-code-color: oklch(0.809 0.105 251.813); - - /* Dark Sidebar Colors */ - --vp-sidebar-bg-color: oklch(0.21 0.006 285.885); - - /* Shadows for dark mode */ - --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.3); - --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.4); - --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5); - --shadow-xl: 0 12px 32px rgba(0, 0, 0, 0.6); -} - -/* Custom Styling */ -.VPNavBar { - background-color: var(--vp-c-bg-soft) !important; - border-bottom: 1px solid var(--vp-c-border); - backdrop-filter: blur(8px); - box-shadow: var(--shadow-sm); -} - -/* Fix navbar color consistency - remove default VitePress gradient/transition effects */ -.VPNavBar.has-sidebar { - background-color: var(--vp-c-bg-soft) !important; -} - -.VPNavBar:not(.home) { - background-color: var(--vp-c-bg-soft) !important; -} - -.VPNavBar.filled { - background-color: var(--vp-c-bg-soft) !important; -} - -/* Ensure navbar content has consistent colors */ -.VPNavBar .content { - background-color: transparent !important; -} - -.VPNavBar .content-body { - background-color: transparent !important; -} - -.VPNavBar .divider { - background-color: transparent !important; -} - -/* Navbar logo and title sizing */ -.VPNavBarTitle { - display: flex; - align-items: center; - gap: 0; -} - -/* Increase logo size */ -.VPNavBar .logo { - width: 32px !important; - height: 32px !important; - margin-right: 12px !important; -} - -/* Increase title/name size */ -.VPNavBar .title { - font-size: 1.25rem !important; - font-weight: 700 !important; - letter-spacing: -0.01em !important; -} - -/* Navbar version positioning - keep it attached to title */ -.navbar-version { - flex-shrink: 0; - margin-left: 0.5rem !important; - display: inline-flex !important; - align-items: center !important; -} - -/* Ensure the VPNavBarTitle area contains both title and version */ -.VPNavBar .VPNavBarTitle .title-container { - display: flex; - align-items: center; - gap: 0.5rem; -} - -@media (max-width: 960px) { - .VPNavBar .logo { - width: 28px !important; - height: 28px !important; - } - - .VPNavBar .title { - font-size: 1.1rem !important; - } - - .navbar-version { - margin-left: 0.25rem !important; - } -} - -.VPSidebar { - background-color: var(--vp-c-bg-soft) !important; - box-shadow: var(--shadow-md); -} - -.VPContent { - background-color: var(--vp-c-bg) !important; -} - -/* Hero Section Enhancement */ -.VPHero { - padding-top: 8rem !important; - padding-bottom: 3rem !important; - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; -} - -.VPHero .container { - max-width: 1200px; - margin: 0 auto !important; -} - -.VPHero .main { - max-width: 800px; - margin: 0 auto !important; - /* text-align: center; */ -} - -.VPHero .name { - background: linear-gradient(135deg, oklch(0.809 0.105 251.813), oklch(0.488 0.243 264.376)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - font-weight: 800; - letter-spacing: -0.02em; -} - -.VPHero .text { - font-weight: 600; - font-size: 3rem; - line-height: 1.2; - letter-spacing: -0.01em; -} - -.VPHero .tagline { - font-size: 1.25rem; - line-height: 1.6; - color: var(--vp-c-text-2); - max-width: 600px; - margin: 0 auto; -} - -.VPHero .image { - max-width: 250px !important; - filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.12)); - margin: 0 auto !important; -} - -.VPHero .image-container { - display: flex; - justify-content: center; -} - -@media (max-width: 768px) { - .VPHero { - padding-top: 3rem !important; - } - - .VPHero .text { - font-size: 2rem; - } - - .VPHero .tagline { - font-size: 1rem; - } - - .VPHero .image { - max-width: 200px !important; - width: 100% !important; - height: auto !important; - display: block !important; - } - - .VPHero .image img { - width: 100% !important; - height: auto !important; - display: block !important; - margin: 0 auto !important; - } - - .VPHero .image-container { - display: flex !important; - justify-content: center !important; - align-items: center !important; - width: 100% !important; - margin-bottom: 1.5rem !important; - } -} - -/* Custom badge for "Coming Soon" */ -.coming-soon-badge { - display: inline-block; - background: linear-gradient(135deg, oklch(0.488 0.243 264.376), oklch(0.623 0.214 259.815)); - color: white !important; - padding: 0.25rem 0.75rem; - border-radius: 0.5rem; - font-size: 0.875rem; - font-weight: 600; - margin-left: 0.5rem; - vertical-align: middle; -} - -/* Custom card styling */ -.custom-card { - background: var(--vp-c-bg-soft); - border: 1px solid var(--vp-c-border); - border-radius: 12px; - padding: 1.5rem; - margin: 1rem 0; - box-shadow: var(--shadow-md); - transition: all 0.3s ease; -} - -.custom-card:hover { - box-shadow: var(--shadow-lg); - transform: translateY(-2px); -} - -.custom-card h3 { - margin-top: 0; - color: var(--vp-c-brand-1); -} - -/* Feature grid */ -.feature-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 1.5rem; - margin: 2rem 0; -} - -.feature-card { - background: var(--vp-c-bg-soft); - border: 1px solid var(--vp-c-border); - border-radius: 12px; - padding: 1.5rem; - transition: all 0.3s ease; - box-shadow: var(--shadow-sm); -} - -.feature-card:hover { - transform: translateY(-4px); - border-color: var(--vp-c-brand-1); - box-shadow: var(--shadow-lg); -} - -.feature-card h3 { - margin-top: 0; - color: var(--vp-c-brand-2); - display: flex; - align-items: center; - gap: 0.5rem; -} - -.feature-icon { - font-size: 1.5rem; -} - -/* VitePress Features Section Enhancement */ -.VPFeatures { - padding: 3rem 1.5rem !important; - max-width: 1200px; - margin: 0 auto; -} - -.VPFeature { - background: var(--vp-c-bg-soft); - border: 1px solid var(--vp-c-border); - border-radius: 12px; - padding: 1.5rem; - transition: all 0.3s ease; - box-shadow: var(--shadow-sm); -} - -.VPFeature:hover { - transform: translateY(-4px); - box-shadow: var(--shadow-lg); - border-color: var(--vp-c-brand-1); -} - -/* Coming Soon Banner */ -.coming-soon-banner { - background: linear-gradient(135deg, oklch(0.488 0.243 264.376) 0%, oklch(0.424 0.199 265.638) 100%); - border: 1px solid oklch(0.623 0.214 259.815); - border-radius: var(--vp-border-radius); - padding: 1rem 1.5rem; - margin: 1.5rem 0; - color: white !important; -} - -.coming-soon-banner h4 { - margin: 0 0 0.5rem 0; - color: white !important; -} - -.coming-soon-banner p { - margin: 0; - color: white !important; - opacity: 0.95; -} - -.coming-soon-banner ul, -.coming-soon-banner ol { - color: white !important; -} - -.coming-soon-banner li { - color: white !important; -} - -.coming-soon-banner strong { - color: white !important; -} - -.coming-soon-banner code { - color: white !important; - background: rgba(0, 0, 0, 0.25) !important; - padding: 0.2em 0.4em; - border-radius: 4px; -} - -.coming-soon-banner a { - color: white !important; - text-decoration: underline; - font-weight: 600; -} - -.coming-soon-banner a:hover { - color: rgba(255, 255, 255, 0.85) !important; -} - -/* Code blocks */ -.vp-code-group { - margin: 1rem 0; - border-radius: 12px; - overflow: hidden; - box-shadow: var(--shadow-md); -} - -div[class*='language-'] { - border-radius: 12px; - box-shadow: var(--shadow-sm); - border: 1px solid var(--vp-c-border); -} - -div[class*='language-']:hover { - box-shadow: var(--shadow-md); -} - -/* Code copy button */ -.vp-code-group button, -div[class*='language-'] button { - transition: all 0.2s ease; -} - -.vp-code-group button:hover, -div[class*='language-'] button:hover { - transform: scale(1.05); -} - -/* Home hero customization */ -.VPHero .name { - background: linear-gradient(135deg, oklch(0.809 0.105 251.813), oklch(0.488 0.243 264.376)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -/* Content sections */ -.vp-doc { - max-width: 1200px !important; - margin: 0 auto; -} - -.vp-doc .content-container { - max-width: 1200px !important; -} - -.vp-doc h2 { - border-top: 1px solid var(--vp-c-divider); - padding-top: 2rem; - margin-top: 3rem; - font-weight: 700; - letter-spacing: -0.01em; -} - -.vp-doc h3 { - font-weight: 600; - letter-spacing: -0.005em; - color: var(--vp-c-brand-1); -} - -/* Cards in content */ -.vp-doc .custom-block { - border-radius: 12px; - box-shadow: var(--shadow-sm); - transition: all 0.3s ease; -} - -.vp-doc .custom-block:hover { - box-shadow: var(--shadow-md); - transform: translateY(-2px); -} - -/* Links */ -a { - color: var(--vp-c-brand-1); - text-decoration: none; -} - -a:hover { - color: var(--vp-c-brand-2); -} - -/* Fix link visibility in code blocks and inline code */ -.vp-doc a code, -code a { - color: var(--vp-c-brand-1) !important; -} - -.dark .vp-doc a code, -.dark code a { - color: var(--vp-c-brand-2) !important; -} - -/* Fix text visibility in custom blocks (info, tip, warning, danger) */ -/* Keep white text on blue/colored backgrounds in BOTH light and dark modes */ -.vp-doc .custom-block.info, -.vp-doc .custom-block.tip, -.vp-doc .custom-block.warning, -.vp-doc .custom-block.danger, -.custom-block.info, -.custom-block.tip, -.custom-block.warning, -.custom-block.danger { - color: white !important; -} - -.vp-doc .custom-block.info .custom-block-title, -.vp-doc .custom-block.tip .custom-block-title, -.vp-doc .custom-block.warning .custom-block-title, -.vp-doc .custom-block.danger .custom-block-title, -.custom-block.info .custom-block-title, -.custom-block.tip .custom-block-title, -.custom-block.warning .custom-block-title, -.custom-block.danger .custom-block-title { - color: white !important; -} - -.vp-doc .custom-block.info p, -.vp-doc .custom-block.tip p, -.vp-doc .custom-block.warning p, -.vp-doc .custom-block.danger p, -.custom-block.info p, -.custom-block.tip p, -.custom-block.warning p, -.custom-block.danger p { - color: white !important; -} - -.vp-doc .custom-block.info ul, -.vp-doc .custom-block.tip ul, -.vp-doc .custom-block.warning ul, -.vp-doc .custom-block.danger ul, -.vp-doc .custom-block.info ol, -.vp-doc .custom-block.tip ol, -.vp-doc .custom-block.warning ol, -.vp-doc .custom-block.danger ol, -.custom-block.info ul, -.custom-block.tip ul, -.custom-block.warning ul, -.custom-block.danger ul, -.custom-block.info ol, -.custom-block.tip ol, -.custom-block.warning ol, -.custom-block.danger ol { - color: white !important; -} - -.vp-doc .custom-block.info li, -.vp-doc .custom-block.tip li, -.vp-doc .custom-block.warning li, -.vp-doc .custom-block.danger li, -.custom-block.info li, -.custom-block.tip li, -.custom-block.warning li, -.custom-block.danger li { - color: white !important; -} - -.vp-doc .custom-block.info strong, -.vp-doc .custom-block.tip strong, -.vp-doc .custom-block.warning strong, -.vp-doc .custom-block.danger strong, -.custom-block.info strong, -.custom-block.tip strong, -.custom-block.warning strong, -.custom-block.danger strong { - color: white !important; -} - -.vp-doc .custom-block.info code, -.vp-doc .custom-block.tip code, -.vp-doc .custom-block.warning code, -.vp-doc .custom-block.danger code, -.custom-block.info code, -.custom-block.tip code, -.custom-block.warning code, -.custom-block.danger code { - color: white !important; - background: rgba(0, 0, 0, 0.25) !important; - padding: 0.2em 0.4em; - border-radius: 4px; -} - -.dark .vp-doc .custom-block.info code, -.dark .vp-doc .custom-block.tip code, -.dark .vp-doc .custom-block.warning code, -.dark .vp-doc .custom-block.danger code, -.dark .custom-block.info code, -.dark .custom-block.tip code, -.dark .custom-block.warning code, -.dark .custom-block.danger code { - color: white !important; - background: rgba(0, 0, 0, 0.3) !important; - padding: 0.2em 0.4em; - border-radius: 4px; -} - -/* Fix links in custom blocks - keep white with underline */ -.vp-doc .custom-block a, -.custom-block a { - color: white !important; - text-decoration: underline; - font-weight: 600; -} - -.dark .vp-doc .custom-block a, -.dark .custom-block a { - color: white !important; -} - -.vp-doc .custom-block a:hover, -.custom-block a:hover { - color: rgba(255, 255, 255, 0.85) !important; - text-decoration: underline; -} - -.dark .vp-doc .custom-block a:hover, -.dark .custom-block a:hover { - color: rgba(255, 255, 255, 0.85) !important; -} - -/* Fix inline code visibility in light mode */ -:not(pre) > code { - color: oklch(0.35 0.25 264.376) !important; - background-color: oklch(0.96 0 0) !important; -} - -.dark :not(pre) > code { - color: oklch(0.809 0.105 251.813) !important; - background-color: oklch(0.21 0.006 285.885) !important; -} - -/* Tables */ -table { - border-collapse: collapse; - width: 100%; - margin: 1rem 0; -} - -th, td { - border: 1px solid var(--vp-c-border); - padding: 0.75rem; - text-align: left; -} - -th { - background: var(--vp-c-bg-soft); - font-weight: 600; -} - -tr:hover { - background: var(--vp-c-bg-mute); -} - -/* Comparison Table Styling */ -.comparison-section { - margin: 3rem 0; - width: 100%; -} - -.comparison-section h3 { - text-align: center; - margin-bottom: 2rem; -} - -.comparison-table-wrapper { - margin: 2rem auto; - border-radius: 12px; - overflow: hidden; - box-shadow: var(--shadow-lg); - border: 1px solid var(--vp-c-border); - max-width: 100%; -} - -.comparison-table-wrapper table { - margin: 0 auto; - box-shadow: none; - width: 100%; - table-layout: fixed; -} - -.comparison-table-wrapper th { - background: var(--vp-c-brand-1); - color: white; - font-weight: 700; - padding: 1rem 0.75rem; - text-align: center; - font-size: 0.9rem; - width: 16.66%; -} - -.comparison-table-wrapper th:first-child { - text-align: left; - padding-left: 1.5rem; - width: 20%; -} - -.comparison-table-wrapper td { - padding: 0.875rem 0.5rem; - vertical-align: middle; - font-size: 0.85rem; - width: 16.66%; -} - -.comparison-table-wrapper td:first-child { - font-weight: 600; - color: var(--vp-c-text-1); - padding-left: 1.5rem; - white-space: nowrap; - width: 20%; -} - -.comparison-table-wrapper td:not(:first-child) { - text-align: center; -} - -.comparison-table-wrapper tr:hover { - background: var(--vp-c-bg-soft); -} - -.comparison-table-wrapper tbody tr { - transition: all 0.2s ease; - border-bottom: 1px solid var(--vp-c-border); -} - -.comparison-table-wrapper tbody tr:last-child { - border-bottom: none; -} - -/* Features comparison lists */ -.features-comparison { - background: var(--vp-c-bg-soft); - border: 1px solid var(--vp-c-border); - border-left: 4px solid #10b981; - border-radius: 8px; - padding: 1.5rem 1.5rem 1.5rem 2rem; - margin: 1.5rem 0; - box-shadow: var(--shadow-sm); -} - -.features-comparison.limitations { - border-left-color: #f59e0b; -} - -.features-comparison ul { - margin: 0; - padding: 0; - list-style: none; -} - -.features-comparison li { - padding: 0.5rem 0; - position: relative; - padding-left: 1.5rem; - line-height: 1.7; -} - -.features-comparison li:before { - content: "●"; - position: absolute; - left: 0; - color: #10b981; - font-size: 0.8rem; - top: 0.6rem; -} - -.features-comparison.limitations li:before { - color: #f59e0b; -} - -/* Important Notes Section */ -.important-notes { - margin: 3rem 0; -} - -.important-notes .custom-block { - margin: 2rem 0; - padding: 1.5rem 2rem; - border-radius: 12px; - box-shadow: var(--shadow-lg); - transition: all 0.3s ease; - font-size: 1.05rem; - line-height: 1.8; -} - -.important-notes .custom-block:hover { - box-shadow: var(--shadow-xl); - transform: translateY(-3px); -} - -.important-notes .custom-block.info { - border-left: 6px solid #3b82f6; - background: linear-gradient(to right, rgba(59, 130, 246, 0.05), transparent); -} - -.important-notes .custom-block.tip { - border-left: 6px solid #10b981; - background: linear-gradient(to right, rgba(16, 185, 129, 0.05), transparent); -} - -.important-notes .custom-block-title { - font-size: 1.25rem; - font-weight: 700; - margin-bottom: 0.75rem; -} - -.important-notes .custom-block p { - font-size: 1.05rem; - line-height: 1.8; - margin: 0.5rem 0; -} - -.important-notes .custom-block strong { - font-weight: 700; - font-size: 1.1rem; -} - -/* Professional Features Grid */ -.features-grid-professional { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: 1.5rem; - margin: 2.5rem auto; - max-width: 1200px; -} - -.feature-card-pro { - background: var(--vp-c-bg-soft); - border: 1px solid var(--vp-c-border); - border-radius: 12px; - padding: 2rem; - transition: all 0.3s ease; - box-shadow: var(--shadow-sm); - position: relative; - overflow: hidden; -} - -.feature-card-pro::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 3px; - background: linear-gradient(90deg, var(--vp-c-brand-1), var(--vp-c-brand-2)); - transform: scaleX(0); - transition: transform 0.3s ease; -} - -.feature-card-pro:hover { - transform: translateY(-4px); - box-shadow: var(--shadow-lg); - border-color: var(--vp-c-brand-1); -} - -.feature-card-pro:hover::before { - transform: scaleX(1); -} - -.feature-icon-pro { - width: 48px; - height: 48px; - background: var(--vp-c-brand-1); - border-radius: 10px; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 1.25rem; - color: white; - transition: all 0.3s ease; -} - -.feature-card-pro:hover .feature-icon-pro { - background: var(--vp-c-brand-2); - color: white; - transform: scale(1.1) rotate(5deg); -} - -.feature-card-pro h3 { - margin: 0 0 0.75rem 0; - font-size: 1.15rem; - font-weight: 600; - color: var(--vp-c-text-1); - letter-spacing: -0.01em; -} - -.feature-card-pro p { - margin: 0; - color: var(--vp-c-text-2); - font-size: 0.95rem; - line-height: 1.6; -} - -@media (max-width: 768px) { - .features-grid-professional { - grid-template-columns: 1fr; - gap: 1rem; - } - - .feature-card-pro { - padding: 1.5rem; - } -} - -/* Coming Soon Section Styling */ -.vp-doc h3 + p { - line-height: 1.8; - color: var(--vp-c-text-2); -} - -/* Responsive Table for Mobile */ -@media (max-width: 768px) { - .comparison-table-wrapper { - overflow-x: auto; - margin: 2rem 0; - } - - .comparison-table-wrapper table { - min-width: 900px; - } - - .features-grid-professional { - grid-template-columns: 1fr; - } - - .features-comparison { - padding: 1rem 1rem 1rem 1.5rem; - } - - .comparison-section { - margin: 2rem 0; - } -} - -@media (max-width: 1024px) { - .comparison-table-wrapper { - overflow-x: auto; - } - - .comparison-table-wrapper table { - min-width: 900px; - } -} -.status-badge { - display: inline-block; - padding: 0.25rem 0.75rem; - border-radius: 0.5rem; - font-size: 0.875rem; - font-weight: 600; - margin: 0.25rem; -} - -.status-implemented { - background: oklch(0.488 0.243 264.376); - color: white !important; -} - -.status-planned { - background: oklch(0.274 0.006 286.033); - color: white !important; -} - -.status-coming-soon { - background: linear-gradient(135deg, oklch(0.488 0.243 264.376), oklch(0.623 0.214 259.815)); - color: white !important; -} - -/* Ultra high specificity override for custom blocks in light mode */ -:root .vp-doc .custom-block.info *, -:root .vp-doc .custom-block.tip *, -:root .vp-doc .custom-block.warning *, -:root .vp-doc .custom-block.danger *, -:root .custom-block.info *, -:root .custom-block.tip *, -:root .custom-block.warning *, -:root .custom-block.danger * { - color: white !important; -} - -/* Ensure custom block backgrounds are visible */ -:root .vp-doc .custom-block.info, -:root .custom-block.info { - background-color: #3b82f6 !important; -} - -:root .vp-doc .custom-block.tip, -:root .custom-block.tip { - background-color: #10b981 !important; -} - -:root .vp-doc .custom-block.warning, -:root .custom-block.warning { - background-color: #f59e0b !important; -} - -:root .vp-doc .custom-block.danger, -:root .custom-block.danger { - background-color: #ef4444 !important; -} - -/* Code blocks inside custom blocks */ -:root .vp-doc .custom-block code, -:root .custom-block code { - background: rgba(0, 0, 0, 0.25) !important; - color: white !important; -} diff --git a/www/docs/.vitepress/theme/index.js b/www/docs/.vitepress/theme/index.js deleted file mode 100644 index e48e9a8..0000000 --- a/www/docs/.vitepress/theme/index.js +++ /dev/null @@ -1,14 +0,0 @@ -import DefaultTheme from 'vitepress/theme' -import './custom.css' -import HeroInstall from './components/HeroInstall.vue' -import NavBarVersion from './components/NavBarVersion.vue' -import Layout from './Layout.vue' - -export default { - extends: DefaultTheme, - Layout, - enhanceApp({ app }) { - app.component('HeroInstall', HeroInstall) - app.component('NavBarVersion', NavBarVersion) - } -} diff --git a/www/docs/api/applications.md b/www/docs/api/applications.md deleted file mode 100644 index cbf2609..0000000 --- a/www/docs/api/applications.md +++ /dev/null @@ -1,366 +0,0 @@ -# Applications API - -Manage applications programmatically. All endpoints require authentication via JWT cookie. - -## Create Application - -`POST /api/apps/create` - -Creates a new application in a project. Supports three app types: `web`, `service`, and `database`. - -### Request Body - -```json -{ - "name": "my-app", - "description": "My application description", - "projectId": 1, - "appType": "web", - "port": 3000, - "envVars": { - "NODE_ENV": "production", - "API_KEY": "secret" - } -} -``` - -**For database apps**, use: - -```json -{ - "name": "my-postgres", - "description": "PostgreSQL database", - "projectId": 1, - "appType": "database", - "templateName": "postgresql", - "envVars": { - "POSTGRES_PASSWORD": "secret123" - } -} -``` - -### Parameters - -- `name` (required): Application name -- `description` (optional): Application description -- `projectId` (required): ID of the project -- `appType` (optional): One of `web`, `service`, or `database` (defaults to `web`) -- `port` (optional): Port number for web apps (defaults to 3000) -- `templateName` (required for database apps): Template name (e.g., `postgresql`, `mysql`, `redis`, `mongodb`) -- `envVars` (optional): Key-value map of environment variables - -### Response - -```json -{ - "success": true, - "message": "Application created successfully", - "data": { - "id": 1, - "name": "my-app", - "description": "My application description", - "project_id": 1, - "app_type": "web", - "port": 3000, - "status": "stopped", - "created_by": 1, - "created_at": "2025-01-15T10:30:00Z" - } -} -``` - -::: tip -For web apps, Mist automatically generates a domain if wildcard DNS is configured. -::: - -## List Applications - -`POST /api/apps/get` - -Retrieves all applications in a project that the authenticated user has access to. - -### Request Body - -```json -{ - "projectId": 1 -} -``` - -### Response - -```json -{ - "success": true, - "message": "Applications retrieved successfully", - "data": [ - { - "id": 1, - "name": "my-app", - "app_type": "web", - "status": "running", - "port": 3000, - "git_repository": "https://github.com/user/repo", - "git_branch": "main", - "created_at": "2025-01-15T10:30:00Z" - } - ] -} -``` - -## Get Application by ID - -`POST /api/apps/get/id` - -Retrieves a single application by its ID. - -### Request Body - -```json -{ - "appId": 1 -} -``` - -### Response - -```json -{ - "success": true, - "message": "Application retrieved successfully", - "data": { - "id": 1, - "name": "my-app", - "description": "My application", - "project_id": 1, - "app_type": "web", - "port": 3000, - "status": "running", - "git_repository": "https://github.com/user/repo", - "git_branch": "main", - "root_directory": "", - "dockerfile_path": null, - "deployment_strategy": "rolling", - "restart_policy": "unless-stopped", - "cpu_limit": 1.0, - "memory_limit": 512, - "created_at": "2025-01-15T10:30:00Z", - "updated_at": "2025-01-15T12:00:00Z" - } -} -``` - -## Update Application - -`POST /api/apps/update` - -Updates an application's configuration. Only the application owner can update settings. - -### Request Body - -All fields are optional. Only include fields you want to update: - -```json -{ - "appId": 1, - "name": "updated-app", - "description": "Updated description", - "gitRepository": "https://github.com/user/new-repo", - "gitBranch": "develop", - "port": 8080, - "rootDirectory": "/app", - "dockerfilePath": "docker/Dockerfile", - "deploymentStrategy": "rolling", - "status": "running", - "cpuLimit": 2.0, - "memoryLimit": 1024, - "restartPolicy": "always" -} -``` - -### Parameters - -- `appId` (required): Application ID to update -- `name`: Application name -- `description`: Application description -- `gitRepository`: Git repository URL -- `gitBranch`: Git branch name -- `port`: Application port -- `rootDirectory`: Root directory for builds -- `dockerfilePath`: Path to Dockerfile -- `deploymentStrategy`: Deployment strategy -- `status`: Application status (`stopped`, `running`, `error`, `building`, `deploying`) -- `cpuLimit`: CPU limit in cores (e.g., 1.0, 2.0) -- `memoryLimit`: Memory limit in MB (e.g., 512, 1024) -- `restartPolicy`: One of `no`, `always`, `on-failure`, `unless-stopped` - -### Response - -```json -{ - "success": true, - "message": "Application updated successfully", - "data": { - "id": 1, - "name": "updated-app", - "git_branch": "develop", - "updated_at": "2025-01-15T14:00:00Z" - } -} -``` - -## Container Controls - -### Stop Container - -`POST /api/apps/container/stop?appId=1` - -Stops a running container. - -### Start Container - -`POST /api/apps/container/start?appId=1` - -Starts a stopped container. - -### Restart Container - -`POST /api/apps/container/restart?appId=1` - -Restarts a container. - -### Response (all control endpoints) - -```json -{ - "success": true, - "message": "Container stopped successfully", - "data": { - "message": "Container stopped successfully" - } -} -``` - -## Get Container Status - -`GET /api/apps/container/status?appId=1` - -Retrieves the current status of a container. - -### Response - -```json -{ - "success": true, - "message": "Container status retrieved successfully", - "data": { - "state": "running", - "status": "Up 2 hours", - "id": "abc123def456" - } -} -``` - -## Get Container Logs - -`GET /api/apps/container/logs?appId=1&tail=100` - -Retrieves container logs. - -### Query Parameters - -- `appId` (required): Application ID -- `tail` (optional): Number of lines to retrieve (default: 100) - -### Response - -```json -{ - "success": true, - "message": "Container logs retrieved successfully", - "data": { - "logs": "2025-01-15T10:30:00Z Server started\n2025-01-15T10:30:01Z Listening on port 3000" - } -} -``` - -## cURL Examples - -### Create a Web Application - -```bash -curl -X POST https://mist.example.com/api/apps/create \ - -H "Content-Type: application/json" \ - -b cookies.txt \ - -d '{ - "name": "my-web-app", - "description": "My Node.js application", - "projectId": 1, - "appType": "web", - "port": 3000, - "envVars": { - "NODE_ENV": "production" - } - }' -``` - -### Create a Database Application - -```bash -curl -X POST https://mist.example.com/api/apps/create \ - -H "Content-Type: application/json" \ - -b cookies.txt \ - -d '{ - "name": "my-postgres", - "description": "PostgreSQL database", - "projectId": 1, - "appType": "database", - "templateName": "postgresql", - "envVars": { - "POSTGRES_PASSWORD": "mysecretpassword", - "POSTGRES_DB": "mydb" - } - }' -``` - -### Update Application - -```bash -curl -X POST https://mist.example.com/api/apps/update \ - -H "Content-Type: application/json" \ - -b cookies.txt \ - -d '{ - "appId": 1, - "name": "updated-app", - "gitBranch": "develop", - "cpuLimit": 2.0, - "memoryLimit": 1024 - }' -``` - -### Stop Container - -```bash -curl -X POST 'https://mist.example.com/api/apps/container/stop?appId=1' \ - -b cookies.txt -``` - -### Get Container Logs - -```bash -curl -X GET 'https://mist.example.com/api/apps/container/logs?appId=1&tail=50' \ - -b cookies.txt -``` - -::: tip Authentication -All endpoints require authentication via JWT cookie. Use `-b cookies.txt` with cURL to persist cookies after login. -::: - -## Related Endpoints - -- [Environment Variables API](/api/environment-variables) - Manage app environment variables -- [Domains API](/api/domains) - Manage app domains -- [Deployments API](/api/deployments) - Deploy applications -- [WebSocket API](/api/websockets) - Real-time container logs - -For the complete implementation, see [server/api/handlers/applications](https://github.com/corecollectives/mist/tree/main/server/api/handlers/applications). diff --git a/www/docs/api/authentication.md b/www/docs/api/authentication.md deleted file mode 100644 index 6d47028..0000000 --- a/www/docs/api/authentication.md +++ /dev/null @@ -1,58 +0,0 @@ -# Authentication API - -Manage user authentication via API. - -## Login - -`POST /api/auth/login` - -**Request:** -```json -{ - "email": "user@example.com", - "password": "password" -} -``` - -**Response:** -```json -{ - "success": true, - "data": { - "user": { - "id": 1, - "email": "user@example.com", - "role": "admin" - } - } -} -``` - -Sets HTTP-only cookie with JWT token. - -## Logout - -`POST /api/auth/logout` - -Clears authentication cookie. - -## Get Current User - -`GET /api/auth/me` - -Returns currently authenticated user. - -**Response:** -```json -{ - "success": true, - "data": { - "id": 1, - "email": "user@example.com", - "role": "admin", - "createdAt": "2025-01-15T10:00:00Z" - } -} -``` - -See [server/api/handlers/auth](https://github.com/corecollectives/mist/tree/main/server/api/handlers/auth) for implementation details. diff --git a/www/docs/api/deployments.md b/www/docs/api/deployments.md deleted file mode 100644 index f3b86b5..0000000 --- a/www/docs/api/deployments.md +++ /dev/null @@ -1,240 +0,0 @@ -# Deployments API - -Trigger and manage deployments programmatically. All endpoints require authentication. - -## Trigger Deployment - -`POST /api/deployment/add` - -Triggers a new deployment for an application. For web/service apps, it fetches the latest commit from the configured Git repository. For database apps, it deploys using the service template's Docker image. - -### Request Body - -```json -{ - "appId": 1 -} -``` - -### Parameters - -- `appId` (required): The ID of the application to deploy - -### Response - -```json -{ - "id": 15, - "app_id": 1, - "commit_hash": "a1b2c3d4e5f6", - "commit_message": "Fix user authentication bug", - "status": "pending", - "stage": "", - "progress": 0, - "created_at": "2025-01-15T10:30:00Z" -} -``` - -::: tip Deployment Queue -Deployments are added to a queue and processed by background workers. The deployment status updates from `pending` → `building` → `deploying` → `success` (or `failed`). -::: - -## List Deployments - -`POST /api/deployment/getByAppId` - -Retrieves deployment history for an application, including all previous deployments with their statuses. - -### Request Body - -```json -{ - "appId": 1 -} -``` - -### Response - -```json -{ - "success": true, - "message": "Deployments retrieved successfully", - "data": [ - { - "id": 15, - "app_id": 1, - "deployment_number": 15, - "commit_hash": "a1b2c3d4e5f6", - "commit_message": "Fix user authentication bug", - "status": "success", - "stage": "completed", - "progress": 100, - "duration": 45, - "is_active": true, - "created_at": "2025-01-15T10:30:00Z", - "updated_at": "2025-01-15T10:31:00Z" - }, - { - "id": 14, - "app_id": 1, - "deployment_number": 14, - "commit_hash": "f6e5d4c3b2a1", - "commit_message": "Add user profile page", - "status": "success", - "stage": "completed", - "progress": 100, - "duration": 38, - "is_active": false, - "rolled_back_from": 15, - "created_at": "2025-01-14T16:20:00Z", - "updated_at": "2025-01-14T16:21:00Z" - } - ] -} -``` - -### Deployment Statuses - -- `pending`: Deployment is queued and waiting to start -- `building`: Application is being built (cloning repo, building Docker image) -- `deploying`: Container is being deployed -- `success`: Deployment completed successfully -- `failed`: Deployment failed (check error message and logs) -- `stopped`: Deployment was manually stopped -- `rolled_back`: Deployment was rolled back to a previous version - -## Get Deployment Logs - -`GET /api/deployment/logs/:deploymentId` - -Retrieves build logs for a specific deployment. These are the logs generated during the build process. - -### Response - -``` -Cloning repository... -Cloning into '/tmp/mist-build-123'... -Building Docker image... -Step 1/8 : FROM node:18-alpine - ---> 1a2b3c4d5e6f -Step 2/8 : WORKDIR /app - ---> Running in 7f8e9d0c1b2a - ---> 3c4d5e6f7a8b -... -Successfully built abc123def456 -Successfully tagged mist-app-1:latest -Deployment completed successfully -``` - -## WebSocket: Watch Deployment Status - -`WS /ws/deployment/logs?deploymentId=15` - -Connect to a WebSocket to receive real-time deployment status updates and logs. - -### Connection - -```javascript -const ws = new WebSocket('wss://mist.example.com/ws/deployment/logs?deploymentId=15'); - -ws.onmessage = (event) => { - const data = JSON.parse(event.data); - - if (data.type === 'status') { - console.log('Status:', data.data.status); - console.log('Progress:', data.data.progress + '%'); - console.log('Stage:', data.data.stage); - } else if (data.type === 'log') { - console.log('Log:', data.data.line); - } -}; -``` - -### Message Types - -**Status Update:** -```json -{ - "type": "status", - "timestamp": "2025-01-15T10:30:15Z", - "data": { - "deployment_id": 15, - "status": "building", - "stage": "cloning_repo", - "progress": 25, - "message": "Cloning repository...", - "error_message": "" - } -} -``` - -**Log Line:** -```json -{ - "type": "log", - "timestamp": "2025-01-15T10:30:16Z", - "data": { - "line": "Step 1/8 : FROM node:18-alpine" - } -} -``` - -**Error:** -```json -{ - "type": "error", - "timestamp": "2025-01-15T10:30:30Z", - "data": { - "message": "Failed to build Docker image: exit code 1" - } -} -``` - -## Deployment Stages - -During a deployment, the system goes through these stages: - -1. **cloning_repo** - Cloning Git repository -2. **building_image** - Building Docker image -3. **stopping_old** - Stopping previous container -4. **starting_new** - Starting new container -5. **completed** - Deployment finished successfully - -## cURL Examples - -### Trigger Deployment - -```bash -curl -X POST https://mist.example.com/api/deployment/add \ - -H "Content-Type: application/json" \ - -b cookies.txt \ - -d '{"appId": 1}' -``` - -### List Deployments - -```bash -curl -X POST https://mist.example.com/api/deployment/getByAppId \ - -H "Content-Type: application/json" \ - -b cookies.txt \ - -d '{"appId": 1}' -``` - -### Get Deployment Logs - -```bash -curl https://mist.example.com/api/deployment/logs/15 \ - -b cookies.txt -``` - -::: tip Real-time Updates -For real-time deployment progress, use the WebSocket endpoint. The REST API logs endpoint only returns completed logs after the deployment finishes. -::: - -## Related Endpoints - -- [Applications API](/api/applications) - Manage applications -- [WebSocket API](/api/websockets) - Real-time updates -- [GitHub API](/api/github) - GitHub integration - -For the complete implementation, see [server/api/handlers/deployments](https://github.com/corecollectives/mist/tree/main/server/api/handlers/deployments) and [server/docker/deployer.go](https://github.com/corecollectives/mist/blob/main/server/docker/deployer.go). diff --git a/www/docs/api/domains.md b/www/docs/api/domains.md deleted file mode 100644 index 97d07d1..0000000 --- a/www/docs/api/domains.md +++ /dev/null @@ -1,39 +0,0 @@ -# Domains API - -Manage application domains. - -## Add Domain - -`POST /api/apps/domains/create` - -**Request:** -```json -{ - "appId": 1, - "domain": "app.example.com" -} -``` - -## List Domains - -`POST /api/apps/domains/get` - -**Request:** -```json -{ - "appId": 1 -} -``` - -## Delete Domain - -`DELETE /api/apps/domains/delete` - -**Request:** -```json -{ - "id": 1 -} -``` - -See [server/api/handlers/applications/domains.go](https://github.com/corecollectives/mist/blob/main/server/api/handlers/applications/domains.go) for implementation. diff --git a/www/docs/api/environment-variables.md b/www/docs/api/environment-variables.md deleted file mode 100644 index 463fa5f..0000000 --- a/www/docs/api/environment-variables.md +++ /dev/null @@ -1,109 +0,0 @@ -# Environment Variables API - -Manage environment variables via API. - -## Create Variable - -`POST /api/apps/envs/create` - -### Request Body - -```json -{ - "appId": 1, - "key": "API_KEY", - "value": "secret-value" -} -``` - -### Response - -```json -{ - "success": true, - "data": { - "id": 1, - "appId": 1, - "key": "API_KEY", - "value": "secret-value", - "createdAt": "2025-01-15T10:30:00Z" - } -} -``` - -## List Variables - -`POST /api/apps/envs/get` - -### Request Body - -```json -{ - "appId": 1 -} -``` - -### Response - -```json -{ - "success": true, - "data": [ - { - "id": 1, - "key": "API_KEY", - "value": "secret-value", - "createdAt": "2025-01-15T10:30:00Z" - } - ] -} -``` - -## Update Variable - -`PUT /api/apps/envs/update` - -### Request Body - -```json -{ - "id": 1, - "key": "API_KEY", - "value": "new-secret-value" -} -``` - -## Delete Variable - -`DELETE /api/apps/envs/delete` - -### Request Body - -```json -{ - "id": 1 -} -``` - -## Example: Bulk Import - -```bash -#!/bin/bash - -APP_ID=1 - -# Read .env file and create variables -while IFS='=' read -r key value; do - # Skip comments and empty lines - [[ $key =~ ^#.*$ ]] && continue - [[ -z $key ]] && continue - - # Create variable - curl -X POST https://mist.example.com/api/apps/envs/create \ - -H "Content-Type: application/json" \ - -b cookies.txt \ - -d "{\"appId\": $APP_ID, \"key\": \"$key\", \"value\": \"$value\"}" -done < .env -``` - -For complete API reference, see [server/api/handlers/applications/envs.go](https://github.com/corecollectives/mist/blob/main/server/api/handlers/applications/envs.go). diff --git a/www/docs/api/github.md b/www/docs/api/github.md deleted file mode 100644 index 0663031..0000000 --- a/www/docs/api/github.md +++ /dev/null @@ -1,38 +0,0 @@ -# GitHub API - -GitHub integration endpoints. - -## List Repositories - -`POST /api/github/repos` - -Returns repositories accessible via installed GitHub App. - -**Request:** -```json -{ - "installationId": 12345 -} -``` - -## List Branches - -`POST /api/github/branches` - -**Request:** -```json -{ - "repoFullName": "user/repo", - "installationId": 12345 -} -``` - -Returns branches for the specified repository. - -## Webhook Endpoint - -`POST /api/webhooks/github` - -Receives GitHub webhook events for auto-deployment. - -See [server/api/handlers/github](https://github.com/corecollectives/mist/tree/main/server/api/handlers/github) for implementation. diff --git a/www/docs/api/overview.md b/www/docs/api/overview.md deleted file mode 100644 index ef7cfb3..0000000 --- a/www/docs/api/overview.md +++ /dev/null @@ -1,151 +0,0 @@ -# API Overview - -Mist provides a comprehensive REST API for programmatic access to all features. - -## Base URL - -``` -https://your-mist-instance.com/api -``` - -## Authentication - -All API requests require authentication using JWT tokens stored in HTTP-only cookies. - -### Via Browser - -Cookies are set automatically after login. - -### Via cURL/API Client - -1. Login to get session cookie: - -```bash -curl -X POST https://mist.example.com/api/auth/login \ - -H "Content-Type: application/json" \ - -d '{"email": "user@example.com", "password": "password"}' \ - -c cookies.txt -``` - -2. Use cookie in subsequent requests: - -```bash -curl https://mist.example.com/api/apps \ - -b cookies.txt -``` - -## API Endpoints - -### Authentication -- `POST /api/auth/login` - Login -- `POST /api/auth/logout` - Logout -- `GET /api/auth/me` - Get current user - -### Projects -- `GET /api/projects` - List projects -- `POST /api/projects/create` - Create project -- `GET /api/projects/:id` - Get project -- `PUT /api/projects/update` - Update project -- `DELETE /api/projects/delete` - Delete project - -### Applications -- `POST /api/apps/get` - List applications -- `POST /api/apps/create` - Create application -- `POST /api/apps/update` - Update application -- `POST /api/apps/delete` - Delete application - -### Deployments -- `POST /api/deployments/create` - Trigger deployment -- `POST /api/deployments/get` - List deployments -- `GET /api/deployments/:id` - Get deployment details - -### Environment Variables -- `POST /api/apps/envs/create` - Create variable -- `POST /api/apps/envs/get` - List variables -- `PUT /api/apps/envs/update` - Update variable -- `DELETE /api/apps/envs/delete` - Delete variable - -### Domains -- `POST /api/apps/domains/create` - Add domain -- `POST /api/apps/domains/get` - List domains -- `DELETE /api/apps/domains/delete` - Remove domain - -### Users (Admin Only) -- `POST /api/users/get` - List users -- `POST /api/users/create` - Create user -- `DELETE /api/users/delete` - Delete user - -### GitHub Integration -- `POST /api/github/repos` - List repositories -- `POST /api/github/branches` - List branches -- `POST /api/webhooks/github` - Webhook endpoint - -## WebSocket Endpoints - -### Container Logs -``` -ws://your-mist-instance.com/api/ws/logs/:containerId -``` - -### System Metrics -``` -ws://your-mist-instance.com/api/ws/metrics -``` - -### Deployment Status -``` -ws://your-mist-instance.com/api/ws/deployment/:deploymentId -``` - -## Response Format - -### Success Response - -```json -{ - "success": true, - "data": { ... } -} -``` - -### Error Response - -```json -{ - "success": false, - "error": "Error message" -} -``` - -## Rate Limiting - -Currently no rate limiting is enforced. This will be added in future releases. - -## API Versioning - -The API is currently unversioned. Breaking changes will be avoided when possible. - -## Coming Soon - -
-

🚧 API Improvements

-
- -- **API Tokens** - Generate tokens for non-browser clients -- **OpenAPI Specification** - Interactive API documentation -- **API Versioning** - v1, v2, etc. -- **Rate Limiting** - Prevent API abuse -- **Webhooks** - Subscribe to events -- **GraphQL API** - Alternative to REST - -## Detailed Documentation - -- [Authentication API](./authentication) -- [Projects API](./projects) -- [Applications API](./applications) -- [Deployments API](./deployments) -- [Environment Variables API](./environment-variables) -- [Domains API](./domains) -- [Users API](./users) -- [GitHub API](./github) -- [WebSockets API](./websockets) diff --git a/www/docs/api/projects.md b/www/docs/api/projects.md deleted file mode 100644 index 6c38bb1..0000000 --- a/www/docs/api/projects.md +++ /dev/null @@ -1,54 +0,0 @@ -# Projects API - -Manage projects via API. - -## List Projects - -`GET /api/projects` - -Returns all projects accessible to the authenticated user. - -## Create Project - -`POST /api/projects/create` - -**Request:** -```json -{ - "name": "My Project", - "description": "Project description", - "tags": "production,web" -} -``` - -## Get Project - -`GET /api/projects/:id` - -Returns project details including applications and members. - -## Update Project - -`PUT /api/projects/update` - -**Request:** -```json -{ - "id": 1, - "name": "Updated Name", - "description": "New description" -} -``` - -## Delete Project - -`DELETE /api/projects/delete` - -**Request:** -```json -{ - "id": 1 -} -``` - -See [server/api/handlers/projects](https://github.com/corecollectives/mist/tree/main/server/api/handlers/projects) for details. diff --git a/www/docs/api/users.md b/www/docs/api/users.md deleted file mode 100644 index d110bf6..0000000 --- a/www/docs/api/users.md +++ /dev/null @@ -1,39 +0,0 @@ -# Users API - -Manage users (Admin only). - -## List Users - -`POST /api/users/get` - -Returns all users in the system. - -## Create User - -`POST /api/users/create` - -**Request:** -```json -{ - "email": "newuser@example.com", - "password": "securepassword", - "role": "user" -} -``` - -## Delete User - -`DELETE /api/users/delete` - -**Request:** -```json -{ - "id": 1 -} -``` - -::: warning Admin Only -These endpoints require admin role. -::: - -See [server/api/handlers/auth/users.go](https://github.com/corecollectives/mist/blob/main/server/api/handlers/auth/users.go) for details. diff --git a/www/docs/api/websockets.md b/www/docs/api/websockets.md deleted file mode 100644 index eefd3ff..0000000 --- a/www/docs/api/websockets.md +++ /dev/null @@ -1,327 +0,0 @@ -# WebSockets API - -Real-time data streaming via WebSocket connections. Mist provides WebSocket endpoints for live container logs, deployment status, and system metrics. - -## Container Logs (Real-time) - -`WS /ws/container/logs?appId={appId}` - -Streams live container logs from a running application. This provides real-time log output as the container generates it. - -### Connection - -```javascript -const ws = new WebSocket('wss://mist.example.com/ws/container/logs?appId=1'); - -ws.onopen = () => { - console.log('Connected to container logs'); -}; - -ws.onmessage = (event) => { - const message = JSON.parse(event.data); - - switch(message.type) { - case 'status': - console.log('Container status:', message.data); - break; - case 'log': - console.log('Log:', message.data.line); - break; - case 'error': - console.error('Error:', message.data.message); - break; - case 'end': - console.log('Log stream ended'); - break; - } -}; - -ws.onerror = (error) => { - console.error('WebSocket error:', error); -}; - -ws.onclose = () => { - console.log('Disconnected from container logs'); -}; -``` - -### Message Types - -**Status Update (initial connection):** -```json -{ - "type": "status", - "timestamp": "2025-01-15T10:30:00Z", - "data": { - "container": "mist-app-1", - "state": "running", - "status": "Up 2 hours" - } -} -``` - -**Log Line:** -```json -{ - "type": "log", - "timestamp": "2025-01-15T10:30:01Z", - "data": { - "line": "2025-01-15 10:30:01 [INFO] Server listening on port 3000" - } -} -``` - -**Error:** -```json -{ - "type": "error", - "timestamp": "2025-01-15T10:30:05Z", - "data": { - "message": "Container not found" - } -} -``` - -**Stream End:** -```json -{ - "type": "end", - "timestamp": "2025-01-15T10:35:00Z", - "data": { - "message": "Log stream ended" - } -} -``` - -### Features - -- **Auto-reconnect**: Includes ping/pong messages every 30 seconds to keep connection alive -- **Buffer size**: Handles large log lines up to 1MB -- **Tail logs**: Shows last 100 lines on connection -- **Real-time**: Streams logs as they're generated - -### Notes - -::: warning Container Must Be Running -The WebSocket connection requires the container to be in a `running` state. If the container is stopped, you'll receive an error message. -::: - -## Deployment Logs & Status - -`WS /ws/deployment/logs?deploymentId={deploymentId}` - -Streams real-time deployment progress, build logs, and status updates during a deployment. - -### Connection - -```javascript -const ws = new WebSocket('wss://mist.example.com/ws/deployment/logs?deploymentId=15'); - -ws.onmessage = (event) => { - const message = JSON.parse(event.data); - - if (message.type === 'status') { - updateProgressBar(message.data.progress); - updateStage(message.data.stage); - } else if (message.type === 'log') { - appendLog(message.data.line); - } else if (message.type === 'error') { - showError(message.data.message); - } -}; -``` - -### Message Types - -**Status Update:** -```json -{ - "type": "status", - "timestamp": "2025-01-15T10:30:15Z", - "data": { - "deployment_id": 15, - "status": "building", - "stage": "building_image", - "progress": 50, - "message": "Building Docker image...", - "error_message": "" - } -} -``` - -**Log Line:** -```json -{ - "type": "log", - "timestamp": "2025-01-15T10:30:16Z", - "data": { - "line": "Step 3/8 : COPY package*.json ./" - } -} -``` - -**Error:** -```json -{ - "type": "error", - "timestamp": "2025-01-15T10:30:30Z", - "data": { - "message": "Docker build failed: exit code 1" - } -} -``` - -### Status Flow - -1. **pending** (0%) - Deployment queued -2. **building** (25-75%) - Building Docker image -3. **deploying** (75-90%) - Starting container -4. **success** (100%) - Deployment complete -5. **failed** (error) - Deployment failed - -### Stages - -- `cloning_repo` - Cloning Git repository -- `building_image` - Building Docker image -- `stopping_old` - Stopping previous container -- `starting_new` - Starting new container -- `completed` - Deployment finished - -## System Metrics - -`WS /ws/metrics` - -Streams real-time system metrics including CPU usage, memory, disk space, and Docker stats. - -### Connection - -```javascript -const ws = new WebSocket('wss://mist.example.com/ws/metrics'); - -ws.onmessage = (event) => { - const metrics = JSON.parse(event.data); - updateDashboard(metrics); -}; -``` - -### Message Format - -```json -{ - "timestamp": "2025-01-15T10:30:00Z", - "cpu": { - "usage": 25.5, - "cores": 4 - }, - "memory": { - "used": 4294967296, - "total": 17179869184, - "percent": 25.0 - }, - "disk": { - "used": 53687091200, - "total": 536870912000, - "percent": 10.0 - }, - "docker": { - "containers_running": 5, - "containers_total": 8, - "images": 12 - } -} -``` - -### Update Frequency - -Metrics are broadcast every **1 second** to all connected clients. - -## React/TypeScript Example - -Here's a complete example using React hooks: - -```typescript -import { useEffect, useState } from 'react'; - -interface LogMessage { - type: 'log' | 'status' | 'error' | 'end'; - timestamp: string; - data: any; -} - -export function useContainerLogs(appId: number) { - const [logs, setLogs] = useState([]); - const [status, setStatus] = useState('connecting'); - const [error, setError] = useState(null); - - useEffect(() => { - const ws = new WebSocket( - `wss://mist.example.com/ws/container/logs?appId=${appId}` - ); - - ws.onopen = () => { - setStatus('connected'); - }; - - ws.onmessage = (event) => { - const message: LogMessage = JSON.parse(event.data); - - switch (message.type) { - case 'log': - setLogs(prev => [...prev, message.data.line]); - break; - case 'status': - setStatus(message.data.state); - break; - case 'error': - setError(message.data.message); - break; - case 'end': - setStatus('ended'); - break; - } - }; - - ws.onerror = () => { - setError('WebSocket connection error'); - setStatus('error'); - }; - - ws.onclose = () => { - setStatus('disconnected'); - }; - - return () => { - ws.close(); - }; - }, [appId]); - - return { logs, status, error }; -} -``` - -## Authentication - -WebSocket connections inherit authentication from HTTP cookies. Make sure the user is authenticated before establishing WebSocket connections. - -::: tip Secure Connections -In production, always use `wss://` (WebSocket Secure) instead of `ws://` for encrypted connections. -::: - -## Error Handling - -Common error scenarios: - -- **Container not found**: Application doesn't have a running container -- **Permission denied**: User doesn't have access to the application -- **Connection timeout**: Network issues or firewall blocking WebSocket -- **Container not running**: Container is stopped or in error state - -## Browser Compatibility - -WebSocket API is supported in all modern browsers: -- Chrome/Edge 16+ -- Firefox 11+ -- Safari 7+ -- Opera 12.1+ - -For the complete implementation, see [server/websockets](https://github.com/corecollectives/mist/tree/main/server/websockets). diff --git a/www/docs/deployment/backup.md b/www/docs/deployment/backup.md deleted file mode 100644 index 092f6b3..0000000 --- a/www/docs/deployment/backup.md +++ /dev/null @@ -1,527 +0,0 @@ -# Backup & Recovery - -Backup and restore your Mist installation. - -## What to Backup - -### 1. SQLite Database (Critical) - -The database contains all your Mist data: -- Users, sessions, and authentication tokens -- Projects and team members -- Applications and configurations -- Deployments and deployment history -- Environment variables (including secrets) -- Domains and SSL certificate tracking -- GitHub App credentials -- Audit logs - -**Location**: `/var/lib/mist/mist.db` - -**Size**: Typically 10-100 MB depending on usage - -::: danger Critical Data -This is the **single most important file** to backup. Without it, you lose all configuration and deployment history. -::: - -### 2. SSL Certificates (Important) - -Let's Encrypt SSL certificates for your domains. - -**Location**: `/opt/mist/letsencrypt/acme.json` - -**Note**: Certificates can be re-issued by Let's Encrypt, but backing them up prevents rate limiting and downtime during recovery. - -### 3. Traefik Configuration (Auto-Generated) - -Dynamic Traefik routing configuration. - -**Location**: `/var/lib/mist/traefik/dynamic.yml` - -**Note**: This file is auto-generated from the database, so restoring the database will recreate it. - -### 4. User Uploads (Optional) - -User avatars and uploaded files. - -**Location**: `/var/lib/mist/uploads/` - -**Size**: Usually small (< 100 MB) - -### 5. Application Data (Important for Databases) - -If you're running databases (PostgreSQL, MySQL, MongoDB, Redis) as applications in Mist, their data is stored in Docker volumes. - -**List volumes:** -```bash -docker volume ls | grep mist -``` - -**Backup application volumes separately** if they contain important data. - -### What NOT to Backup - -- `/var/lib/mist/logs/` - Build logs (can be large, usually not needed) -- Git repositories - Cloned from source, can be re-cloned -- Docker images - Can be rebuilt -- `/var/lib/mist/traefik/dynamic.yml` - Auto-generated from database - -## Manual Backup - -### Quick Database Backup - -```bash -# Stop Mist service for consistency -sudo systemctl stop mist - -# Backup database with timestamp -sudo cp /var/lib/mist/mist.db /var/backups/mist_$(date +%Y%m%d_%H%M%S).db - -# Start Mist service -sudo systemctl start mist - -# Verify backup -ls -lh /var/backups/mist_*.db -``` - -::: tip Hot Backup (No Downtime) -SQLite supports online backups. You can backup without stopping Mist: - -```bash -# Create backup directory -sudo mkdir -p /var/backups/mist - -# Hot backup using SQLite -sqlite3 /var/lib/mist/mist.db ".backup /var/backups/mist_$(date +%Y%m%d_%H%M%S).db" -``` -::: - -### Full System Backup - -Backup all Mist data including database, certificates, and uploads: - -```bash -# Create backup directory -sudo mkdir -p /var/backups/mist - -# Stop Mist for consistency (optional for hot backup) -sudo systemctl stop mist - -# Backup everything -sudo tar -czf /var/backups/mist_full_$(date +%Y%m%d_%H%M%S).tar.gz \ - /var/lib/mist/mist.db \ - /var/lib/mist/uploads \ - /opt/mist/letsencrypt/acme.json \ - /opt/mist/traefik-static.yml - -# Start Mist -sudo systemctl start mist - -# Verify backup -ls -lh /var/backups/mist_full_*.tar.gz -``` - -### Backup Database Volumes (for Database Applications) - -If you run databases (PostgreSQL, MySQL, etc.) as applications in Mist: - -```bash -# List all Docker volumes -docker volume ls - -# Backup a specific volume -docker run --rm -v app-123_data:/data -v /var/backups:/backup \ - alpine tar -czf /backup/app-123_data_$(date +%Y%m%d).tar.gz -C /data . -``` - -## Automated Backup Script - -Create `/usr/local/bin/mist-backup.sh`: - -```bash -#!/bin/bash - -# Mist Automated Backup Script -BACKUP_DIR="/var/backups/mist" -DATE=$(date +%Y%m%d_%H%M%S) -RETENTION_DAYS=30 -DB_PATH="/var/lib/mist/mist.db" -CERT_PATH="/opt/mist/letsencrypt/acme.json" - -# Create backup directory -mkdir -p $BACKUP_DIR - -# Hot backup database using SQLite (no downtime) -if [ -f "$DB_PATH" ]; then - sqlite3 $DB_PATH ".backup $BACKUP_DIR/mist_$DATE.db" - echo "✅ Database backed up: mist_$DATE.db" -else - echo "❌ Database not found at $DB_PATH" - exit 1 -fi - -# Backup SSL certificates -if [ -f "$CERT_PATH" ]; then - cp $CERT_PATH $BACKUP_DIR/acme_$DATE.json - echo "✅ Certificates backed up: acme_$DATE.json" -fi - -# Backup user uploads (if exists) -if [ -d "/var/lib/mist/uploads" ]; then - tar -czf $BACKUP_DIR/uploads_$DATE.tar.gz -C /var/lib/mist uploads - echo "✅ Uploads backed up: uploads_$DATE.tar.gz" -fi - -# Remove old backups (keep last N days) -find $BACKUP_DIR -name "mist_*.db" -mtime +$RETENTION_DAYS -delete -find $BACKUP_DIR -name "acme_*.json" -mtime +$RETENTION_DAYS -delete -find $BACKUP_DIR -name "uploads_*.tar.gz" -mtime +$RETENTION_DAYS -delete - -echo "✅ Backup completed at $DATE" -echo "📁 Backup location: $BACKUP_DIR" - -# Optional: Copy to remote server -# rsync -avz $BACKUP_DIR/ user@backup-server:/backups/mist/ -``` - -Make executable and schedule: - -```bash -# Make script executable -sudo chmod +x /usr/local/bin/mist-backup.sh - -# Test the script -sudo /usr/local/bin/mist-backup.sh - -# Schedule daily backups at 2 AM -(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/mist-backup.sh") | crontab - - -# Verify cron job -crontab -l -``` - -::: tip Backup Frequency -- **Development**: Daily backups with 7-day retention -- **Production**: Daily backups with 30-day retention + offsite copies -- **Critical systems**: Multiple daily backups + real-time replication -::: - -## Backup to Cloud Storage - -### AWS S3 - -```bash -# Install AWS CLI -curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" -unzip awscliv2.zip -sudo ./aws/install - -# Configure credentials -aws configure -# Enter: Access Key ID, Secret Access Key, Region - -# Backup to S3 -aws s3 cp /var/backups/mist/mist_$(date +%Y%m%d_%H%M%S).db \ - s3://your-bucket/mist-backups/ - -# Automated S3 backup (add to backup script) -aws s3 sync /var/backups/mist/ s3://your-bucket/mist-backups/ --delete -``` - -### Backblaze B2 - -```bash -# Install B2 CLI -sudo pip3 install b2 - -# Authenticate -b2 authorize-account - -# Create bucket (one-time) -b2 create-bucket mist-backups allPrivate - -# Upload backup -b2 upload-file mist-backups /var/backups/mist/mist_latest.db mist_$(date +%Y%m%d).db -``` - -### Rsync to Remote Server - -```bash -# Setup SSH key (one-time, run as backup user) -ssh-keygen -t ed25519 -f ~/.ssh/backup_key -N "" -ssh-copy-id -i ~/.ssh/backup_key user@backup-server - -# Sync backups -rsync -avz -e "ssh -i ~/.ssh/backup_key" \ - /var/backups/mist/ \ - user@backup-server:/backups/mist/ - -# Add to automated script -rsync -avz --delete \ - /var/backups/mist/ \ - user@backup-server:/backups/mist/ -``` - -### Rclone (Universal Cloud Backup) - -Rclone supports 40+ cloud providers (S3, Google Drive, Dropbox, OneDrive, etc.): - -```bash -# Install rclone -curl https://rclone.org/install.sh | sudo bash - -# Configure cloud provider (interactive) -rclone config - -# Backup to cloud -rclone copy /var/backups/mist/ remote:mist-backups/ -v - -# Automated sync (add to backup script) -rclone sync /var/backups/mist/ remote:mist-backups/ --delete-excluded -``` - -## Restore from Backup - -### Database Restore - -```bash -# Stop Mist service -sudo systemctl stop mist - -# Backup current database (just in case) -sudo cp /var/lib/mist/mist.db /var/lib/mist/mist.db.before-restore - -# Restore database from backup -sudo cp /var/backups/mist/mist_20250122_140000.db /var/lib/mist/mist.db - -# Fix permissions (important!) -sudo chown $USER:$USER /var/lib/mist/mist.db -sudo chmod 644 /var/lib/mist/mist.db - -# Start Mist service -sudo systemctl start mist - -# Verify service is running -sudo systemctl status mist - -# Check logs for errors -journalctl -u mist -f -``` - -::: warning Permission Issues -Make sure the database file is owned by the user running the Mist service (usually the user who installed Mist, not `mist` or `root`). -::: - -### Full System Restore - -```bash -# Stop Mist service -sudo systemctl stop mist - -# Extract full backup -sudo tar -xzf /var/backups/mist_full_20250122.tar.gz -C / - -# Or restore individual files -sudo tar -xzf /var/backups/mist_full_20250122.tar.gz \ - -C / \ - var/lib/mist/mist.db \ - opt/mist/letsencrypt/acme.json - -# Fix permissions -sudo chown -R $USER:$USER /var/lib/mist -sudo chmod 600 /opt/mist/letsencrypt/acme.json - -# Restart services -sudo systemctl start mist -docker compose -f /opt/mist/traefik-compose.yml restart - -# Verify everything is running -sudo systemctl status mist -docker ps | grep traefik -``` - -### Restore SSL Certificates - -```bash -# Stop Traefik -docker compose -f /opt/mist/traefik-compose.yml down - -# Restore certificates -sudo cp /var/backups/mist/acme_20250122.json /opt/mist/letsencrypt/acme.json -sudo chmod 600 /opt/mist/letsencrypt/acme.json - -# Start Traefik -docker compose -f /opt/mist/traefik-compose.yml up -d - -# Verify certificates are loaded -docker logs traefik 2>&1 | grep -i certificate -``` - -### Restore Docker Volumes (Database Applications) - -```bash -# Stop the application container first -docker stop container-name - -# Restore volume from backup -docker run --rm -v app-123_data:/data -v /var/backups:/backup \ - alpine sh -c "cd /data && tar -xzf /backup/app-123_data_20250122.tar.gz" - -# Start the application container -docker start container-name -``` - -## Disaster Recovery - -### Complete Server Loss - -Follow these steps to recover from total server failure: - -1. **Provision New Server** - ```bash - # Same OS as original (Ubuntu 20.04+ recommended) - # Install Docker first - curl -fsSL https://get.docker.com | sh - ``` - -2. **Install Mist** - ```bash - curl -fsSL https://raw.githubusercontent.com/corecollectives/mist/main/install.sh | bash - ``` - -3. **Stop Mist Service** - ```bash - sudo systemctl stop mist - ``` - -4. **Restore Database** - ```bash - # Copy backup to new server - scp backup-server:/backups/mist_latest.db /tmp/ - - # Restore database - sudo cp /tmp/mist_latest.db /var/lib/mist/mist.db - sudo chown $USER:$USER /var/lib/mist/mist.db - ``` - -5. **Restore SSL Certificates (optional, but recommended)** - ```bash - # Copy certificates - scp backup-server:/backups/acme.json /tmp/ - - # Restore - sudo cp /tmp/acme.json /opt/mist/letsencrypt/acme.json - sudo chmod 600 /opt/mist/letsencrypt/acme.json - ``` - -6. **Start Services** - ```bash - sudo systemctl start mist - docker compose -f /opt/mist/traefik-compose.yml restart - ``` - -7. **Verify Recovery** - ```bash - # Check Mist service - sudo systemctl status mist - - # Check Traefik - docker ps | grep traefik - - # Access web interface - curl http://localhost:8080/health # or your domain - ``` - -8. **Update DNS** (if IP changed) - - Update A records to point to new server IP - - Wait for DNS propagation (5-60 minutes) - -### Database Corruption - -Check and recover from database corruption: - -```bash -# Check database integrity -sqlite3 /var/lib/mist/mist.db "PRAGMA integrity_check;" - -# If output is "ok", database is fine -# If errors appear, restore from backup: - -sudo systemctl stop mist -sudo cp /var/lib/mist/mist.db /var/lib/mist/mist.db.corrupted -sudo cp /var/backups/mist/mist_LATEST.db /var/lib/mist/mist.db -sudo systemctl start mist -``` - -### Accidental Data Deletion - -If you accidentally delete data through the UI: - -```bash -# Stop Mist immediately -sudo systemctl stop mist - -# Restore from most recent backup -sudo cp /var/backups/mist/mist_$(ls -t /var/backups/mist/mist_*.db | head -1) \ - /var/lib/mist/mist.db - -# Start Mist -sudo systemctl start mist -``` - -::: tip Recovery Point Objective (RPO) -Your RPO = time between backups. If you backup daily at 2 AM and disaster strikes at 1 PM, you lose up to 11 hours of data. For critical systems, increase backup frequency or use real-time replication. -::: - -## Backup Best Practices - -### The 3-2-1 Rule - -- **3** copies of data -- **2** different storage types -- **1** offsite copy - -### Backup Checklist - -- [ ] Automated daily backups configured -- [ ] Backups stored on separate disk/server -- [ ] Offsite/cloud backup configured -- [ ] Backup retention policy defined (7-30 days) -- [ ] Backup integrity tested regularly -- [ ] Restore procedure documented -- [ ] Recovery time objective (RTO) defined -- [ ] Recovery point objective (RPO) defined - -### Test Your Backups - -Regularly test restore procedures: - -```bash -# Test restore on separate server -scp backup-server:/backups/mist_latest.db /tmp/test-restore.db -sqlite3 /tmp/test-restore.db "SELECT COUNT(*) FROM users;" -``` - -## Coming Soon - -The following backup features are planned for future releases: - -- **Built-in Backup UI** - Configure and manage backups from the dashboard -- **One-Click Restore** - Restore from backup through the UI -- **Automated S3/Cloud Backup** - Built-in integration with cloud storage -- **Scheduled Backups** - Configure backup schedules per project or system-wide -- **Backup Verification** - Automatic integrity checks and restore testing -- **Point-in-Time Recovery** - Restore to a specific moment in time -- **Incremental Backups** - Save storage with differential backups -- **Database Replication** - Real-time database replication to standby server -- **Automated Application Volume Backups** - Backup database volumes automatically -- **Backup Encryption** - Encrypt backups at rest and in transit -- **Retention Policies** - Flexible retention rules (hourly, daily, weekly, monthly) -- **Webhook Notifications** - Get notified on backup success/failure - -## Getting Help - -If you encounter issues with backup or recovery: - -- [GitHub Issues](https://github.com/corecollectives/mist/issues) -- [GitHub Discussions](https://github.com/corecollectives/mist/discussions) diff --git a/www/docs/deployment/configuration.md b/www/docs/deployment/configuration.md deleted file mode 100644 index 45231df..0000000 --- a/www/docs/deployment/configuration.md +++ /dev/null @@ -1,120 +0,0 @@ -# Configuration - -Configure Mist for your environment. - -## Configuration System - -Mist uses a **database-driven configuration system**. Most settings are stored in the SQLite database at `/var/lib/mist/mist.db` and managed through the web UI or API. - -::: tip -There is no `/etc/mist/config.yml` file. System settings are configured through the **Settings → System** page in the web interface. -::: - -## Fixed Configuration Values - -Some values are hardcoded in the application: - -### Server Port - -- **Port:** `8080` (hardcoded) -- **Location:** `server/api/main.go:33` -- **Cannot be changed** via environment variables - -### Session Configuration - -- **JWT Expiry:** 31 days (hardcoded) -- **Location:** `server/api/middleware/auth.go:21` - -### File Paths - -- **Root Path:** `/var/lib/mist` -- **Logs:** `/var/lib/mist/logs` -- **Avatars:** `/var/lib/mist/uploads/avatar` -- **Traefik Config:** `/var/lib/mist/traefik` -- **Certificates:** `/opt/mist/letsencrypt/acme.json` - -## Database Configuration - -All system settings and credentials are stored in the SQLite database. - -### GitHub App Configuration - -GitHub App credentials are stored in the `github_app` table: -- App ID -- Client ID -- Client Secret -- Webhook Secret -- Private Key (RSA) - -Configure through: **Settings → Integrations → GitHub App** - -See [GitHub App Setup](./github-app) for configuration instructions. - -## Docker Configuration - -Mist uses the Docker socket at `/var/run/docker.sock`. - -Ensure the user running Mist has Docker permissions: - -```bash -sudo usermod -aG docker mist-user -``` - -## Traefik Integration - -Mist automatically generates Traefik configuration in `/var/lib/mist/traefik/`: -- `traefik.yml` - Static configuration -- `dynamic.yml` - Dynamic routing rules (auto-generated) - -See [Traefik Setup](./traefik) for detailed configuration. - -## System Settings - -System-wide settings are configured through the Mist UI (requires owner role). - -### Wildcard Domain - -Configure automatic domain generation for web applications: - -1. Navigate to **Settings** → **System** -2. Enter your wildcard domain (e.g., `example.com` or `*.example.com`) -3. Optionally configure the Mist dashboard subdomain name (default: `mist`) - -When configured, new web applications automatically receive domains in the format: -``` -{project-name}-{app-name}.{wildcard-domain} -``` - -**Example:** -- Wildcard domain: `apps.example.com` -- Project: `production`, App: `api` -- Auto-generated domain: `production-api.apps.example.com` - -**DNS Requirements:** -Configure a wildcard DNS record: -``` -Type: A -Name: * -Value: YOUR_SERVER_IP -``` - -Or for nested subdomains: -``` -Type: A -Name: *.apps -Value: YOUR_SERVER_IP -``` - -[Learn more about wildcard domains →](../guide/domains#wildcard-domain-configuration) - -## Coming Soon - -The following configuration features are planned: - -- **Configurable Server Port** - Change port via UI or configuration -- **Configurable JWT Secret** - Set JWT secret during installation -- **SMTP Configuration** - Email settings for notifications -- **Backup Settings** - Automatic backup configuration -- **Resource Limits** - Global resource limits per project/application -- **Logging Configuration** - Log levels and destinations -- **Custom SSL Certificates** - Upload custom TLS certificates diff --git a/www/docs/deployment/github-app.md b/www/docs/deployment/github-app.md deleted file mode 100644 index cf2a491..0000000 --- a/www/docs/deployment/github-app.md +++ /dev/null @@ -1,363 +0,0 @@ -# GitHub App Setup - -Configure GitHub App integration for automatic deployments from GitHub repositories. - -## Overview - -Mist uses **GitHub App integration** (not OAuth Apps or Personal Access Tokens) to: -- Access your repositories -- Receive webhook notifications for code pushes -- Trigger automatic deployments when you push code -- Clone private repositories securely - -## Automatic GitHub App Creation - -Mist provides a streamlined process to create a GitHub App directly from the dashboard. - -### Step 1: Access GitHub App Setup - -1. Log in to Mist as **Owner** (only Owners can create GitHub Apps) -2. Navigate to **Settings → Integrations → GitHub** -3. Click **"Create GitHub App"** or **"Connect GitHub"** - -### Step 2: Create GitHub App - -1. Log in to Mist as **Owner** (only Owners can create GitHub Apps) -2. Navigate to **Settings → Integrations → GitHub** -3. Click **"Create GitHub App"** or **"Connect GitHub"** - -### Step 3: Automatic GitHub App Creation - -When you click "Create GitHub App" in Mist: - -1. **Mist generates a manifest** with: - - App name: `Mist-{random-5-digit-number}` (e.g., `Mist-12345`) - - Webhook URL: Auto-configured based on your Mist installation - - Callback URLs for OAuth - - Required permissions (contents, metadata, webhooks) - - Event subscriptions (push, pull_request, deployment_status) - -2. **You're redirected to GitHub** with pre-filled configuration - -3. **GitHub creates the app** and redirects back to Mist - -4. **Mist stores credentials** in the database: - - App ID - - Client ID - - Client Secret - - Private Key (RSA) - - Webhook Secret - -::: tip Fully Automated -You don't need to manually configure permissions, generate keys, or copy credentials. Mist handles everything automatically. -::: - -## GitHub App Permissions - -The automatically created GitHub App requests these permissions: - -**Repository Permissions:** -- **Contents**: Read - Access repository files and commits -- **Metadata**: Read - Basic repository information -- **Pull Requests**: Read - View pull requests -- **Deployments**: Read - Deployment status -- **Administration**: Write - Manage repository settings -- **Webhooks**: Write - Create and manage webhooks - -**Subscribed Events:** -- **Push** - Triggered when code is pushed to repository -- **Pull Request** - Triggered on PR events -- **Deployment Status** - Triggered on deployment updates - -**Webhook Security:** -- Webhook signatures are validated using HMAC-SHA256 -- Only requests with valid signatures are processed -- Webhook secret is automatically generated during app creation - -## Install GitHub App on Repositories - -After creating the GitHub App, you need to install it on the repositories you want to deploy: - -### Install on Your Account/Organization - -1. **From Mist Dashboard**: - - After successful app creation, you're redirected to installation page - - Or go to GitHub → Settings → Applications → Your Mist App → Install - -2. **Select Installation Target**: - - Personal account - - Or organization (if you have access) - -3. **Choose Repository Access**: - - **All repositories**: Mist can access all current and future repos - - **Only select repositories**: Choose specific repos to grant access - -4. **Complete Installation**: - - Click **"Install"** - - You'll be redirected back to Mist - -### Verify Installation - -In Mist dashboard: - -1. **Create a new application** or edit existing -2. Go to **Git Configuration** section -3. **Repository dropdown** should show your installed repositories -4. Select repository and branch -5. Configure Dockerfile path -6. Deploy! - -::: tip Multiple Organizations -If you have multiple GitHub organizations, you can install the same GitHub App on each organization separately. Each installation is independent. -::: - -## Configure Webhook URL for Production - -If you need to update the GitHub App webhook URL after initial creation: - -1. Go to [GitHub Apps Settings](https://github.com/settings/apps) -2. Select your Mist app -3. Update **Webhook URL** to: `https://mist.yourdomain.com/api/github/webhook` -4. Update **Callback URL** to: `https://mist.yourdomain.com/api/github/callback` -5. Update **Setup URL** to: `https://mist.yourdomain.com/api/github/installation/callback` -6. Click **"Save changes"** - -## Automatic Deployments - -Once configured, Mist automatically deploys when you push code: - -### How it Works - -1. **You push code** to a branch configured in Mist -2. **GitHub sends webhook** to Mist -3. **Mist receives push event** and starts deployment -4. **Clones repository** using GitHub App authentication -5. **Builds Docker image** from Dockerfile -6. **Deploys container** with configured settings -7. **Updates deployment status** in Mist dashboard - -### Monitor Webhook Deliveries - -Check if webhooks are being delivered successfully: - -1. Go to [GitHub Apps Settings](https://github.com/settings/apps) -2. Select your Mist app -3. Click **"Advanced" tab** -4. View **Recent Deliveries** - -Look for: -- ✅ **200 OK**: Webhook received successfully -- ❌ **Failed**: Check error details and Mist logs - -## Test Webhook Integration - -Verify that automatic deployments work correctly: - -### Method 1: Push to Repository - -1. **Configure application** in Mist with GitHub repository -2. **Make a change** to your code -3. **Commit and push**: - ```bash - git add . - git commit -m "Test deployment" - git push origin main - ``` -4. **Check Mist dashboard**: - - Navigate to application → Deployments - - You should see a new deployment starting automatically - - Monitor build logs in real-time - -### Method 2: Redeliver Webhook - -Test without pushing new code: - -1. Go to [GitHub Apps Settings](https://github.com/settings/apps) -2. Click your Mist app → **Advanced** tab -3. Find a recent **push** event in Recent Deliveries -4. Click **"Redeliver"** -5. Check Mist dashboard for new deployment - -### Method 3: Check Mist Logs - -Monitor webhook reception in real-time: - -```bash -# Watch for incoming webhooks -sudo journalctl -u mist -f | grep -i "webhook\|github" - -# Check recent webhook activity -sudo journalctl -u mist -n 100 | grep -i webhook -``` - -Look for log messages like: -- `Received GitHub webhook` -- `Processing push event for repository: owner/repo` -- `Starting deployment for application: app-name` - -## Troubleshooting - -### Webhooks Not Being Received - -**Check webhook deliveries in GitHub:** - -1. GitHub Apps → Your App → Advanced tab -2. Look at Recent Deliveries -3. If failed: - - ❌ **Connection timeout**: Firewall blocking GitHub IPs - - ❌ **Connection refused**: Mist not running or port 8080 not accessible - - ❌ **404 Not Found**: Wrong webhook URL - - ❌ **500 Internal Server Error**: Check Mist logs for errors - -**Verify webhook URL is accessible:** - -```bash -# Test from external server (or use webhook.site for testing) -curl -X POST https://your-mist-domain.com/api/github/webhook \ - -H "Content-Type: application/json" \ - -d '{"test": true}' - -# Should return 200 or similar (not 404/timeout) -``` - -**Check firewall rules:** - -```bash -# Verify port 80/443 are open -sudo ufw status | grep -E "80|443" - -# If using Mist API directly (port 8080), verify it's accessible -curl http://your-domain:8080/health # Should work -``` - -**Allow GitHub webhook IPs (optional, for security):** - -```bash -# Get GitHub's current IP ranges -curl https://api.github.com/meta | jq -r '.hooks[]' - -# Example: Allow GitHub IPs for webhook endpoint -# (Implement via nginx/firewall rules) -``` - -### Repositories Not Showing in Dropdown - -**Verify GitHub App installation:** - -1. Go to GitHub → Settings → Applications → Installed GitHub Apps -2. Find your Mist app -3. Verify it's installed on the correct account/organization -4. Check repository access (All repositories or Selected ones) - -**Reinstall GitHub App:** - -If repositories still don't appear: -1. Uninstall the GitHub App from GitHub settings -2. Go back to Mist → Settings → Integrations -3. Reinstall the app -4. Select repositories to grant access - -**Check Mist logs:** - -```bash -sudo journalctl -u mist -n 100 | grep -i "github\|repository" -``` - -### Private Key Errors - -If you see errors related to GitHub authentication: - -**Verify GitHub App exists in database:** - -```bash -sqlite3 /var/lib/mist/mist.db "SELECT app_id, client_id FROM github_app;" -``` - -**Check private key format:** - -The private key should be stored as RSA format in the database. If corrupted: -1. Delete the GitHub App from Mist settings -2. Recreate it using the automatic setup process - -### Deployment Not Triggering - -**Verify branch configuration:** - -- Make sure you're pushing to the **branch configured in Mist** -- Default is often `main` but could be `master`, `develop`, etc. - -**Check deployment logs:** - -```bash -# View recent deployments -sudo journalctl -u mist | grep -i "deployment\|deploy" - -# Check for errors -sudo journalctl -u mist -p err -``` - -**Manually trigger deployment:** - -From Mist dashboard: -1. Go to application details -2. Click **"Deploy"** button -3. Monitor build logs - -### Webhook URL Changed - -If you change your Mist domain or IP: - -1. Update webhook URL in GitHub App settings (see "Configure Webhook URL for Production" above) -2. Or delete and recreate the GitHub App through Mist UI - -## Security Best Practices - -### Webhook Security - -Mist validates all incoming webhook requests using HMAC-SHA256 signatures. Only authenticated requests from GitHub are processed. - -**Additional Security (Optional):** - -If you want an extra layer of security, restrict webhook endpoint access to GitHub's IP ranges: - -**Option 1: IP Whitelist (Nginx)** - -```nginx -# /etc/nginx/sites-available/mist -location /api/github/webhook { - # GitHub webhook IP ranges (update periodically from https://api.github.com/meta) - allow 140.82.112.0/20; - allow 143.55.64.0/20; - allow 185.199.108.0/22; - allow 192.30.252.0/22; - deny all; - - proxy_pass http://localhost:8080; -} -``` - -**Option 2: Firewall Rules** - -Use UFW or iptables to allow only GitHub IP ranges for the webhook endpoint. - -## Advanced Configuration - -### Multiple GitHub Apps - -You can only have **one GitHub App per Mist instance** currently. If you need multiple: - -- Use different Mist instances for different organizations -- Or install the same GitHub App on multiple organizations - -### GitHub Enterprise Server - -Mist uses GitHub.com APIs. For GitHub Enterprise Server support: -- Currently not supported -- Coming soon: Configurable GitHub API base URL - -## Further Reading - -- [GitHub Apps Documentation](https://docs.github.com/en/apps) -- [GitHub Webhooks](https://docs.github.com/en/webhooks) -- [GitHub API Meta Endpoint](https://docs.github.com/en/rest/meta) - Get webhook IP ranges - diff --git a/www/docs/deployment/installation.md b/www/docs/deployment/installation.md deleted file mode 100644 index f00e440..0000000 --- a/www/docs/deployment/installation.md +++ /dev/null @@ -1,430 +0,0 @@ -# Installation - -Install Mist on your server with a single command. - -## Prerequisites - -### System Requirements - -- **OS**: Ubuntu 20.04+, Debian 11+, Fedora, RHEL/CentOS, Arch Linux (any Linux with systemd) -- **RAM**: Minimum 1GB, recommended 2GB+ -- **Disk**: Minimum 10GB free space -- **Docker**: Version 20.10 or later (**REQUIRED** - must be installed first) -- **Docker Compose**: v2 (usually bundled with Docker) -- **Ports**: 80, 443, 8080 must be available - -### Docker Installation - -::: warning Install Docker First -The Mist installation script does NOT install Docker. You must install Docker before running the script. -::: - -```bash -# Ubuntu/Debian -curl -fsSL https://get.docker.com | sh -sudo usermod -aG docker $USER - -# Log out and back in for group changes to take effect - -# Verify installation -docker --version -docker compose version -``` - -[Official Docker Installation Guide →](https://docs.docker.com/engine/install/) - -## Quick Install - -### One-Line Installation - -```bash -curl -fsSL https://trymist.cloud/install.sh | bash -``` - -During installation, you'll be prompted for: -- **Let's Encrypt Email**: Email for SSL certificate notifications (default: admin@example.com) - -### What the Installation Script Does - -The script performs the following steps: - -1. **System Detection** - - Detects package manager (apt/dnf/yum/pacman) - - Checks for Docker installation - -2. **Install Dependencies** - - Git, curl, wget, unzip - - Build tools (gcc, g++, make) - -3. **Install Runtimes** (if not present) - - Go 1.22.11 → `/usr/local/go` - - Bun → `~/.bun` - -4. **Clone Repository** - - Clones Mist from GitHub → `/opt/mist` - - Uses `main` branch - -5. **Create Docker Network** - - Creates `traefik-net` Docker bridge network - -6. **Configure Traefik** - - Updates `/opt/mist/traefik-static.yml` with your email - - Starts Traefik via Docker Compose - -7. **Build Frontend** - - Runs `bun install` and `bun run build` in `/opt/mist/dash` - - Copies built files to `/opt/mist/server/static/` - -8. **Build Backend** - - Runs `go mod tidy` and `go build` in `/opt/mist/server` - - Creates binary at `/opt/mist/server/mist` - -9. **Create Data Directory** - - Creates `/var/lib/mist/` - - Creates empty database file - - Creates traefik config directory - -10. **Configure Firewall** - - Opens port 8080 (UFW/firewalld/iptables) - -11. **Create Systemd Service** - - Creates `/etc/systemd/system/mist.service` - - Enables and starts service - -::: tip Installation Time -On a 1 vCPU server, installation takes 5-10 minutes due to Go and frontend builds. On 2+ vCPUs, it takes 2-5 minutes. -::: - -### Installation Paths - -After installation, files are located at: - -``` -/opt/mist/ # Main installation -├── server/ -│ ├── mist # Go binary (executable) -│ └── static/ # Frontend build files -├── dash/ # Frontend source (kept) -├── traefik-static.yml # Traefik configuration -├── traefik-compose.yml # Docker Compose file -└── letsencrypt/ # SSL certificates - -/var/lib/mist/ # Data directory -├── mist.db # SQLite database -├── traefik/ # Dynamic configs -│ └── dynamic.yml # Generated at runtime -├── logs/ # Build logs (created at runtime) -├── uploads/ -│ └── avatar/ # User avatars (created at runtime) -└── repos/ # Git clones (temporary) - -/etc/systemd/system/mist.service # Service definition - -/usr/local/go/ # Go installation -~/.bun/ # Bun installation -``` - -## Post-Installation - -### 1. Verify Installation - -Check that Mist is running: - -```bash -# Check service status -sudo systemctl status mist - -# View logs -sudo journalctl -u mist -n 50 -f -``` - -### 2. Access Dashboard - -Navigate to `http://your-server-ip:8080` in your browser. - -::: tip First-Time Setup -On first visit, you'll create the owner account. This account has full system privileges. -::: - -### 3. Create Admin Account - -On the setup page: -1. Enter email address -2. Create a strong password -3. Click "Create Admin Account" -4. You'll be automatically logged in - -### 4. Configure Wildcard Domain (Optional) - -For automatic domain generation: -1. Go to **Settings** → **System** -2. Enter your wildcard domain (e.g., `example.com`) -3. Configure DNS with wildcard A record pointing `*.example.com` to your server -4. New web applications will automatically get domains like `{project}-{app}.example.com` - -[Learn more about wildcard domains →](../guide/domains#wildcard-domain-configuration) - -### 5. Configure GitHub Integration - -For Git-based deployments: -1. Go to **Settings** → **Git** -2. Follow instructions to create a GitHub App -3. Install the app on your repositories - -[Learn more about GitHub setup →](./github-app) - -## Verification - -### Check Components - -Verify all components are running: - -```bash -# Mist service -sudo systemctl status mist - -# Traefik container -docker ps | grep traefik - -# Docker network -docker network ls | grep traefik-net - -# Database file -ls -lh /var/lib/mist/mist.db -``` - -### Test API - -```bash -# Health check (should return system info) -curl http://localhost:8080/api/health -``` - -### Check Ports - -Verify ports are accessible: - -```bash -# Check if ports are listening -sudo netstat -tulpn | grep -E ':(80|443|8080) ' - -# Or with ss -sudo ss -tulpn | grep -E ':(80|443|8080) ' -``` - -## Updating Installation - -To update to the latest version: - -```bash -# Re-run installation script -curl -sSL https://raw.githubusercontent.com/corecollectives/mist/main/install.sh | bash -``` - -::: warning Destructive Update -The installation script performs `git reset --hard origin/main`, which will overwrite any local changes. Always backup your data before updating. -::: - -[Learn more about upgrading →](./upgrading) - -## Uninstallation - -To completely remove Mist: - -```bash -curl -sSL https://raw.githubusercontent.com/corecollectives/mist/main/uninstall.sh | bash -``` - -This will: -- Stop and disable the systemd service -- Remove the service file -- Delete `/opt/mist/` directory -- Delete `/var/lib/mist/mist.db` database -- Remove firewall rules for port 8080 -- Remove Go and Bun PATH entries from `~/.bashrc` - -::: warning Data Preservation -The uninstall script does NOT remove: -- Traefik container (still running) -- Docker network `traefik-net` -- SSL certificates in `/opt/mist/letsencrypt/` -- `/var/lib/mist/traefik/` directory -- Go and Bun installations - -Manually remove these if desired. -::: - -## Troubleshooting - -### Installation Fails - -**Docker not installed:** -``` -Error: Docker is not installed -``` -Install Docker first using the official installation guide. - -**Port already in use:** -``` -Error: Port 8080 is already in use -``` -Check what's using the port: -```bash -sudo lsof -i :8080 -# or -sudo netstat -tulpn | grep :8080 -``` - -**Build fails:** -``` -Error: Go build failed -``` -Check system resources and try again. Build requires significant CPU and RAM. - -### Service Won't Start - -Check logs for errors: - -```bash -sudo journalctl -u mist -n 100 --no-pager -``` - -Common issues: -- Database file permissions -- Port 8080 already in use -- Docker socket not accessible -- Missing dependencies - -### Docker Socket Permission Denied - -If Mist can't access Docker: - -```bash -# Add user to docker group -sudo usermod -aG docker $(whoami) - -# Log out and back in, or: -newgrp docker - -# Restart service -sudo systemctl restart mist -``` - -### Traefik Not Running - -Check Traefik status: - -```bash -docker ps | grep traefik -docker logs traefik -``` - -Restart Traefik: - -```bash -cd /opt/mist -docker compose -f traefik-compose.yml down -docker compose -f traefik-compose.yml up -d -``` - -### Can't Access Dashboard - -1. **Check firewall**: - ```bash - sudo ufw status - sudo ufw allow 8080/tcp - ``` - -2. **Check service**: - ```bash - sudo systemctl status mist - ``` - -3. **Check if port is listening**: - ```bash - curl http://localhost:8080 - ``` - -4. **Check from external**: - ```bash - curl http://YOUR_SERVER_IP:8080 - ``` - -### Database Migration Errors - -If database migrations fail: - -```bash -# Backup database -sudo cp /var/lib/mist/mist.db /var/lib/mist/mist.db.backup - -# Delete and recreate (CAUTION: loses all data) -sudo rm /var/lib/mist/mist.db -sudo touch /var/lib/mist/mist.db -sudo chown $(whoami):$(whoami) /var/lib/mist/mist.db - -# Restart service -sudo systemctl restart mist -``` - -## Advanced Installation - -### Custom Installation Path - -The installation script uses `/opt/mist` by default. To use a custom path, clone and build manually: - -```bash -# Clone to custom location -git clone https://github.com/corecollectives/mist.git /your/custom/path - -# Follow manual build steps -cd /your/custom/path -# ... (frontend and backend build) - -# Update systemd service WorkingDirectory -sudo nano /etc/systemd/system/mist.service -``` - -### Development Installation - -For local development: - -```bash -# Clone repository -git clone https://github.com/corecollectives/mist.git -cd mist - -# Frontend -cd dash -bun install -bun run dev # Runs on port 5173 - -# Backend (separate terminal) -cd server -go mod tidy -go run main.go # Runs on port 8080 -``` - -[Learn more about development setup →](https://github.com/corecollectives/mist#development) - -## Security Recommendations - -After installation: - -1. **Change default firewall rules** - Only allow necessary ports -2. **Set strong password** - Use a password manager -3. **Configure GitHub webhook secret** - Secure webhook endpoint -4. **Regular backups** - Backup `/var/lib/mist/mist.db` -5. **Update regularly** - Keep Mist up to date -6. **Disable Traefik dashboard** - In production, disable port 8081 -7. **Monitor logs** - Watch for suspicious activity - -[Learn more about security →](./security) - -## Next Steps - -- [Configure GitHub Integration](./github-app) -- [Set Up Custom Domains](../guide/domains) -- [Create Your First Application](../guide/applications) -- [Configure Backups](./backup) -- [Review Security Best Practices](./security) diff --git a/www/docs/deployment/requirements.md b/www/docs/deployment/requirements.md deleted file mode 100644 index ee19eaa..0000000 --- a/www/docs/deployment/requirements.md +++ /dev/null @@ -1,192 +0,0 @@ -# System Requirements - -Hardware and software requirements for running Mist. - -## Minimum Requirements - -### Small Deployments (1-5 applications) - -- **CPU**: 1 vCPU -- **RAM**: 1GB minimum, 2GB recommended -- **Disk**: 10GB SSD -- **Network**: 100 Mbps -- **Cost**: ~$5-10/month VPS - -::: tip Build Performance -The build process (compiling Go and bundling frontend) requires significant CPU. During installation, 1 vCPU may take 5-10 minutes to build. -::: - -### Medium Deployments (5-20 applications) - -- **CPU**: 2 vCPU -- **RAM**: 2-4GB -- **Disk**: 20-50GB SSD -- **Network**: 500 Mbps -- **Cost**: ~$12-24/month VPS - -### Large Deployments (20+ applications) - -- **CPU**: 4+ vCPU -- **RAM**: 8GB+ -- **Disk**: 100GB+ SSD -- **Network**: 1 Gbps -- **Cost**: ~$40+/month VPS - -## Software Requirements - -### Operating System - -- Ubuntu 20.04 LTS or later -- Debian 11 or later -- Any Linux distribution with: - - systemd - - Docker support - - Modern kernel (5.x+) - -### Docker - -**Required** - Must be installed before running Mist installation script - -- Docker Engine 20.10 or later -- Docker Compose v2 (for Traefik) -- Docker socket accessible at `/var/run/docker.sock` - -::: warning Install Docker First -The Mist installation script does NOT install Docker. You must install Docker manually before running the script. - -[Install Docker →](https://docs.docker.com/engine/install/) -::: - -### Build Dependencies - -Automatically installed by the installation script: -- Go 1.22.11 -- Bun (JavaScript runtime) -- Build essentials (gcc, g++, make) -- Git, curl, wget, unzip - -### Network - -- **Required Ports**: 80, 443, 8080 must be available -- Port 80: HTTP entry point (Traefik) -- Port 443: HTTPS entry point (Traefik) -- Port 8080: Mist API and Dashboard -- Port 8081: Traefik Dashboard (optional) - -- **Outbound internet access** for: - - GitHub API (api.github.com) - - Docker Hub (hub.docker.com) - - Package downloads (Go, Bun, system packages) - - Let's Encrypt ACME challenges (acme-v02.api.letsencrypt.org) - -## Recommended Cloud Providers - -### DigitalOcean - -- **Droplet**: Basic ($12/month, 2GB RAM) -- Pre-installed Docker images available -- Good for most use cases - -### Hetzner Cloud - -- **CX21**: (€5/month, 2GB RAM) -- Excellent price/performance -- European data centers - -### Linode - -- **Nanode**: ($5/month, 1GB RAM) -- Good for small deployments -- Global data centers - -### Vultr - -- **Regular Performance**: ($6/month, 1GB RAM) -- Multiple locations worldwide - -## Storage Considerations - -### Installation Size - -- **Mist binary**: 8-15MB -- **Frontend assets**: ~5MB -- **Go installation**: ~150MB (if not already installed) -- **Bun installation**: ~50MB (if not already installed) -- **Build dependencies**: ~100MB -- **Total installation**: ~500MB-1GB - -### Database - -- SQLite database grows slowly (~10MB per 1000 deployments) -- Initial database: ~100KB -- Recommend at least 1GB for database growth - -### Git Repositories - -- Cloned during deployment to `/var/lib/mist/repos/{app-id}/` -- Average repo: 50-500MB -- **Temporary** - Cleaned up after successful builds -- Multiple concurrent clones possible - -### Docker Images - -- Images stored by Docker Engine -- Size varies: 100MB - 2GB per image -- Each deployment creates new image tagged with commit hash -- Old images remain until manually pruned -- Recommendation: - - Small deployments: 10-20GB - - Medium deployments: 50-100GB - - Large deployments: 100GB+ - -### Logs - -- Build logs stored in filesystem: `/var/lib/mist/logs/{deployment-id}/build.log` -- Average log file: 1-5MB -- Not automatically cleaned up -- Recommend 5-10GB for logs - -### User Uploads - -- Avatars stored in `/var/lib/mist/uploads/avatar/` -- Maximum size: 5MB per avatar -- Minimal storage impact - -## Performance Benchmarks - -### API Response Time - -- Average: 20-50ms -- p95: < 100ms -- p99: < 200ms - -### Concurrent Deployments - -- Small (1 CPU): 1-2 concurrent -- Medium (2 CPU): 3-5 concurrent -- Large (4+ CPU): 10+ concurrent - -### WebSocket Connections - -- Hundreds of concurrent connections supported -- Minimal resource overhead - -## Scaling Considerations - -
-

🚧 Multi-Node Support Coming Soon

-

Horizontal scaling planned for Phase 4.

-
- -### Current Limitations - -- Single server deployment only -- Vertical scaling (upgrade server) required -- No load balancing across nodes - -### Future Plans - -- Docker Swarm support -- Kubernetes option -- Multi-server deployments -- Shared storage for builds diff --git a/www/docs/deployment/security.md b/www/docs/deployment/security.md deleted file mode 100644 index 77b32ab..0000000 --- a/www/docs/deployment/security.md +++ /dev/null @@ -1,686 +0,0 @@ -# Security Best Practices - -Secure your Mist installation in production. - -## Server Security - -### Firewall Configuration - -Configure firewall to allow only necessary ports: - -```bash -# UFW example (Ubuntu/Debian) -sudo ufw default deny incoming -sudo ufw default allow outgoing - -# Allow SSH (change 22 if using custom port) -sudo ufw allow 22/tcp - -# Allow HTTP and HTTPS (required for web access and SSL challenges) -sudo ufw allow 80/tcp -sudo ufw allow 443/tcp - -# Allow Mist API (if accessing directly, otherwise only localhost) -sudo ufw allow 8080/tcp - -# Optional: Allow Traefik dashboard (restrict to specific IPs in production) -sudo ufw allow 8081/tcp - -# Enable firewall -sudo ufw enable - -# Verify rules -sudo ufw status numbered -``` - -**Production Firewall Best Practices:** - -```bash -# Restrict Mist API to localhost only (recommended if using Traefik) -sudo ufw delete allow 8080/tcp -sudo ufw allow from 127.0.0.1 to any port 8080 - -# Restrict Traefik dashboard to localhost only -sudo ufw delete allow 8081/tcp -sudo ufw allow from 127.0.0.1 to any port 8081 - -# Or allow only from specific admin IP -sudo ufw allow from YOUR_ADMIN_IP to any port 8081 - -# Allow SSH only from specific IP (very secure) -sudo ufw delete allow 22/tcp -sudo ufw allow from YOUR_ADMIN_IP to any port 22 -``` - -### SSH Hardening - -```bash -# Disable root login -sudo sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config - -# Disable password auth (use SSH keys only) -sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config - -# Restart SSH -sudo systemctl restart sshd -``` - -### Keep System Updated - -```bash -# Ubuntu/Debian -sudo apt update && sudo apt upgrade -y - -# Enable automatic security updates -sudo apt install unattended-upgrades -sudo dpkg-reconfigure -plow unattended-upgrades -``` - -## Mist Security - -### Strong Passwords - -Enforce strong password requirements for all users: - -- **Minimum length**: 12 characters (16+ recommended) -- **Complexity**: Mix of uppercase, lowercase, numbers, and symbols -- **No common passwords**: Avoid dictionary words, names, dates -- **Use password manager**: 1Password, Bitwarden, LastPass -- **Unique per account**: Never reuse passwords - -**Owner Account Security:** -- The first user account becomes the Owner with full system access -- Use an extremely strong password for the Owner account -- Consider using a hardware security key (coming soon) - -### JWT Secret - -::: warning Hardcoded JWT Secret -Currently, the JWT secret is **hardcoded** in the application (`server/api/middleware/auth.go:14`). This is a **security risk** for production deployments. - -**Workaround:** Modify the code before deployment: -```bash -cd /opt/mist/server/api/middleware -# Edit auth.go and change line 14 to use a strong random value -# Then rebuild: cd /opt/mist/server && go build -o ../bin/server main.go -``` - -**Upcoming:** Configurable JWT secret via environment variable. -::: - -Generate a strong JWT secret: - -```bash -# Generate random 64-character secret -openssl rand -hex 32 - -# Or use this one-liner -openssl rand -base64 48 | tr -d "=+/" | cut -c1-64 -``` - -### Session Security - -- **Session duration**: 31 days (hardcoded) -- **Sessions** are stored as JWT tokens -- **Token expiry**: Automatically logged out after 31 days -- **No session revocation**: Currently, there's no way to force-logout users (coming soon) - -### GitHub Webhook Secret - -GitHub webhook requests are validated using HMAC-SHA256 signatures. - -**How it works:** -- Webhook secret is automatically generated when creating GitHub App -- Stored securely in Mist database -- All incoming webhook requests are validated before processing -- Invalid signatures are rejected - -Generate a strong webhook secret if creating manually: - -```bash -# Generate webhook secret (only needed for manual setup) -openssl rand -hex 32 -``` - -Configure in: **Settings → Integrations → GitHub App** (automatic during app creation) - -## Docker Security - -### Socket Permissions - -The Mist backend requires access to the Docker socket to manage containers: - -```bash -# Verify Docker socket permissions -ls -la /var/run/docker.sock - -# Should show: srw-rw---- 1 root docker - -# Add your user to docker group (done during installation) -sudo usermod -aG docker $USER - -# Log out and back in for group changes to take effect -``` - -::: danger Security Consideration -Access to the Docker socket = root-level access to the host system. The user running Mist can: -- Create privileged containers -- Mount host filesystem -- Escape container isolation - -**Mitigations:** -- Run Mist as a non-root user (recommended, default in install script) -- Never expose Mist API publicly without authentication -- Use Docker rootless mode (advanced, requires configuration) -- Monitor Docker events for suspicious activity -::: - -### Container Isolation - -Applications deployed by Mist run in Docker containers with: - -- **Network isolation**: Apps on `traefik-net` for controlled routing -- **Resource limits**: (coming soon - CPU/memory limits) -- **Read-only filesystems**: (coming soon) -- **Non-root users**: Depends on Dockerfile used - -**Best Practices for Application Dockerfiles:** - -```dockerfile -# Use specific versions, not :latest -FROM node:20-alpine - -# Run as non-root user -RUN addgroup -g 1001 -S appuser && \ - adduser -S appuser -u 1001 -USER appuser - -# Use multi-stage builds to reduce attack surface -# Copy only necessary files -# Scan images for vulnerabilities -``` - -### Image Security - -- **Official base images**: Use official Docker images (node, python, golang, etc.) -- **Image scanning**: (coming soon - automatic vulnerability scanning) -- **Keep images updated**: Rebuild periodically with latest base images -- **Minimize layers**: Combine RUN commands to reduce image size -- **No secrets in images**: Never `COPY` or embed secrets in Dockerfiles - -**Scan images manually:** - -```bash -# Install Trivy (vulnerability scanner) -sudo apt install wget apt-transport-https gnupg lsb-release -wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add - -echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list -sudo apt update -sudo apt install trivy - -# Scan an image -trivy image your-app-image:latest -``` - -## Network Security - -### Reverse Proxy - -- Always use Traefik as reverse proxy -- Never expose application ports directly -- Configure proper CORS headers - -### SSL/TLS - -Mist provides automatic SSL/TLS certificate provisioning and management. - -**Current Features:** -- ✅ Automatic SSL certificate issuance via Let's Encrypt -- ✅ Automatic certificate renewal (90-day certificates, renewed at 60 days) -- ✅ HTTP to HTTPS redirect enabled by default for domains -- ✅ TLS 1.2+ encryption (configured by Traefik v3.1) -- ✅ Modern cipher suites -- ✅ Perfect Forward Secrecy (PFS) - -**How it Works:** -1. User adds domain to application in Mist UI -2. Mist updates Traefik configuration with domain routing -3. Traefik automatically requests certificate from Let's Encrypt -4. Let's Encrypt verifies domain ownership via HTTP-01 challenge (port 80) -5. Certificate stored in `/opt/mist/letsencrypt/acme.json` -6. Traefik serves traffic over HTTPS with automatic HTTP redirect - -**Certificate Storage:** -```bash -# View certificate file -sudo ls -la /opt/mist/letsencrypt/acme.json - -# Should show: -rw------- (600 permissions) - -# Backup certificates -sudo cp /opt/mist/letsencrypt/acme.json /var/backups/acme_$(date +%Y%m%d).json -``` - -**Verify SSL Configuration:** - -```bash -# Test SSL connection -openssl s_client -connect your-domain.com:443 -servername your-domain.com - -# Check certificate expiry -echo | openssl s_client -servername your-domain.com -connect your-domain.com:443 2>/dev/null | openssl x509 -noout -dates - -# Use SSL Labs for comprehensive analysis -# https://www.ssllabs.com/ssltest/analyze.html?d=your-domain.com -``` - -[Learn more about SSL automation →](../guide/ssl-automation) - -**Coming Soon:** -- Wildcard SSL certificates (`*.example.com`) -- Custom certificate upload -- HSTS headers and security policies -- Certificate expiry notifications -- mTLS (mutual TLS) for service-to-service communication - -## Database Security - -### SQLite File Permissions - -The SQLite database contains ALL Mist data including secrets. Protect it carefully: - -```bash -# Check current permissions -ls -la /var/lib/mist/mist.db - -# Set restrictive permissions (read/write for owner only) -sudo chmod 600 /var/lib/mist/mist.db - -# Ensure owned by the user running Mist (not root!) -sudo chown $USER:$USER /var/lib/mist/mist.db - -# Verify -ls -la /var/lib/mist/mist.db -# Should show: -rw------- 1 youruser youruser -``` - -### Database Encryption - -::: warning Database Not Encrypted -SQLite database is currently stored in **plaintext** on disk. This means: -- Anyone with file access can read the database -- All secrets (API keys, tokens, env vars) are visible -- Backups should be encrypted separately - -**Mitigations:** -- Encrypt the entire disk/volume (LUKS, dm-crypt) -- Encrypt backups before uploading to cloud storage -- Restrict server access to trusted administrators only -- Use encrypted backup destinations (encrypted S3 buckets, etc.) -::: - -**Encrypt backups:** - -```bash -# Using GPG -gpg --symmetric --cipher-algo AES256 /var/backups/mist_backup.db - -# Using OpenSSL -openssl enc -aes-256-cbc -salt -in /var/backups/mist_backup.db -out /var/backups/mist_backup.db.enc - -# Decrypt when needed -openssl enc -d -aes-256-cbc -in /var/backups/mist_backup.db.enc -out /var/backups/mist_backup.db -``` - -### Backup Security - -```bash -# Automated encrypted backup script -#!/bin/bash -DATE=$(date +%Y%m%d_%H%M%S) -BACKUP_DIR="/var/backups/mist" -DB_FILE="/var/lib/mist/mist.db" -ENCRYPTION_KEY="your-encryption-passphrase" # Store securely! - -# Hot backup -sqlite3 $DB_FILE ".backup $BACKUP_DIR/mist_$DATE.db" - -# Encrypt backup -openssl enc -aes-256-cbc -salt -pbkdf2 \ - -in $BACKUP_DIR/mist_$DATE.db \ - -out $BACKUP_DIR/mist_$DATE.db.enc \ - -pass pass:"$ENCRYPTION_KEY" - -# Remove unencrypted backup -rm $BACKUP_DIR/mist_$DATE.db - -# Keep last 30 days -find $BACKUP_DIR -name "mist_*.db.enc" -mtime +30 -delete -``` - -See [Backup & Recovery](./backup) for complete backup procedures. - -## Application Security - -### Environment Variables & Secrets - -Environment variables can contain sensitive data (API keys, database passwords, etc.): - -**Security Best Practices:** - -```bash -# Never commit secrets to Git repositories -# Add .env to .gitignore - -# Never log secrets -# Avoid console.log() or print() statements with env vars - -# Rotate secrets regularly -# Change API keys every 90 days or after team member leaves - -# Use strong random values -openssl rand -hex 32 -``` - -**How Mist Handles Secrets:** - -- Environment variables stored in SQLite database (plaintext) -- Passed to containers at deployment time via Docker `-e` flag -- Also passed as `--build-arg` during image builds -- Visible in `docker inspect` output on the host - -::: warning Secrets Visibility -Environment variables are: -- **Stored in plaintext** in the database -- **Visible to anyone** with access to the Mist database or Docker host -- **Passed to build process** (avoid using secrets during build) - -**Upcoming**: Secret encryption at rest and secret management integration (Vault, AWS Secrets Manager) -::: - -**Minimize Secret Exposure:** - -1. **Don't use secrets in Dockerfile ARG**: They're stored in image layers - ```dockerfile - # BAD - Secret visible in image history - ARG API_KEY - RUN curl -H "Authorization: $API_KEY" https://api.example.com/setup - - # GOOD - Fetch at runtime - CMD node app.js # App reads API_KEY from process.env at runtime - ``` - -2. **Use secret management for databases**: Connect to external secret managers at runtime - -3. **Limit access**: Only give secrets to apps that need them - -### Audit Logging - -Mist tracks important actions in the audit log: - -**What's Logged:** -- User authentication (login/logout) -- Application creation/deletion -- Deployments -- Domain changes -- Environment variable changes -- Project member changes -- System settings changes - -**Access Audit Logs:** -- Navigate to **Settings → Audit Logs** in the UI -- Or query database: `SELECT * FROM audit_logs ORDER BY created_at DESC LIMIT 100` - -**Security Monitoring:** - -```bash -# View recent audit logs via database -sqlite3 /var/lib/mist/mist.db "SELECT user_id, action, resource_type, created_at FROM audit_logs ORDER BY created_at DESC LIMIT 50;" -``` - -**What to Monitor:** -- Failed login attempts (possible brute force) -- Unusual deployment times (after hours activity) -- Unexpected user creations -- Permission changes -- Bulk deletions - -::: tip -Set up alerts for critical actions (coming soon). For now, periodically review audit logs manually. -::: - -## Monitoring & Incident Response - -### Log Monitoring - -Monitor system and application logs for security events: - -```bash -# Watch Mist backend logs in real-time -sudo journalctl -u mist -f - -# Filter for authentication events -sudo journalctl -u mist | grep -i "login\|auth\|failed" - -# Watch Traefik access logs -docker logs -f traefik - -# Monitor all Docker container activity -docker events - -# Check for unauthorized container creation -docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.CreatedAt}}" -``` - -### System Monitoring - -Monitor resource usage for anomalies: - -```bash -# Monitor disk space (database growth, logs, containers) -df -h -du -sh /var/lib/mist/* -docker system df - -# Monitor memory usage -free -h -docker stats --no-stream - -# Monitor CPU usage -top -bn1 | head -20 - -# Check Docker daemon resource usage -ps aux | grep dockerd -``` - -### Security Alerts - -Set up alerts for critical events (manual for now, automated coming soon): - -**Monitor for:** -- Multiple failed login attempts -- Unusual deployment times -- High resource usage (potential crypto mining) -- New user creations -- Permission escalations -- Database file modifications -- Firewall rule changes - -**Simple alerting script example:** - -```bash -#!/bin/bash -# /usr/local/bin/mist-security-alert.sh - -# Check for failed logins in last hour -FAILED_LOGINS=$(sudo journalctl -u mist --since "1 hour ago" | grep -c "authentication failed") - -if [ $FAILED_LOGINS -gt 10 ]; then - echo "WARNING: $FAILED_LOGINS failed login attempts in the last hour" | \ - mail -s "Mist Security Alert" admin@example.com -fi - -# Check database size growth (possible data exfiltration) -DB_SIZE=$(stat -f%z /var/lib/mist/mist.db 2>/dev/null || stat -c%s /var/lib/mist/mist.db) -if [ $DB_SIZE -gt 1073741824 ]; then # 1GB - echo "WARNING: Database size exceeds 1GB" | \ - mail -s "Mist Database Alert" admin@example.com -fi -``` - -### Incident Response - -If you suspect a security incident: - -1. **Isolate**: Stop Mist and Traefik immediately - ```bash - sudo systemctl stop mist - docker compose -f /opt/mist/traefik-compose.yml down - ``` - -2. **Preserve Evidence**: Backup current state before changes - ```bash - sqlite3 /var/lib/mist/mist.db ".backup /tmp/incident_db_$(date +%Y%m%d_%H%M%S).db" - sudo journalctl -u mist > /tmp/incident_logs_$(date +%Y%m%d_%H%M%S).log - docker ps -a > /tmp/incident_containers_$(date +%Y%m%d_%H%M%S).txt - ``` - -3. **Investigate**: Check audit logs, system logs, container logs - -4. **Remediate**: Change passwords, rotate secrets, patch vulnerabilities - -5. **Restore**: From clean backup if compromised - -6. **Report**: If vulnerability in Mist itself, report to maintainers - -## Coming Soon - -The following security features are planned for future releases: - -**Authentication & Authorization:** -- Two-Factor Authentication (TOTP/WebAuthn) -- SSO/SAML integration -- OAuth2 provider support -- API key management with scopes -- Session management (force logout, view active sessions) - -**Secrets Management:** -- Environment variable encryption at rest -- Integration with HashiCorp Vault -- AWS Secrets Manager integration -- Secret rotation policies -- Secret scanning in code repositories - -**Network Security:** -- Rate limiting per IP/user -- IP whitelisting for admin access -- Custom security headers (CSP, HSTS, X-Frame-Options) -- Web Application Firewall (WAF) integration -- DDoS protection configuration - -**Application Security:** -- Automated vulnerability scanning for Docker images -- Security policy enforcement (required base images, etc.) -- Container resource limits (CPU, memory, network) -- Read-only container filesystems -- Seccomp profiles and AppArmor - -**Monitoring & Compliance:** -- Security dashboard and metrics -- Automated security alerts (email, Slack, webhook) -- Compliance reporting (SOC 2, ISO 27001) -- Security audit trail export -- Real-time threat detection - -**Data Protection:** -- Database encryption at rest -- Backup encryption by default -- Automated backup integrity verification -- Point-in-time recovery -- Data retention policies - -## Security Checklist - -Use this checklist to ensure your Mist installation is properly secured: - -### Server Security -- [ ] Firewall configured and enabled (UFW/iptables) -- [ ] Only necessary ports open (22, 80, 443, 8080, 8081) -- [ ] Mist API (8080) and Traefik dashboard (8081) restricted to localhost or specific IPs -- [ ] SSH hardened (key-only auth, no root login) -- [ ] Automatic security updates enabled -- [ ] System packages up to date -- [ ] Disk encryption enabled (optional but recommended for sensitive data) - -### Mist Security -- [ ] Strong passwords enforced for all users (12+ characters) -- [ ] Owner account uses extremely strong password -- [ ] JWT secret changed from hardcoded value (requires code modification) -- [ ] GitHub webhook secret configured and validated -- [ ] Regular security updates applied to Mist - -### Docker Security -- [ ] Mist running as non-root user -- [ ] User in docker group (but aware of security implications) -- [ ] Docker socket permissions restricted (660) -- [ ] Containers use official base images only -- [ ] Images periodically scanned for vulnerabilities -- [ ] Unnecessary containers removed regularly - -### Database Security -- [ ] Database file permissions locked down (600) -- [ ] Database owned by correct user (not root) -- [ ] Automated backups configured -- [ ] Backups stored securely (encrypted and offsite) -- [ ] Backup restoration tested regularly - -### Network Security -- [ ] SSL/TLS enabled for all public domains -- [ ] Certificates automatically renewing -- [ ] HTTP automatically redirects to HTTPS -- [ ] Traefik dashboard access restricted -- [ ] No application ports exposed directly (all through Traefik) - -### Application Security -- [ ] No secrets committed to Git repositories -- [ ] Environment variables contain only necessary data -- [ ] Secrets rotated regularly (90 days) -- [ ] Audit logs reviewed periodically -- [ ] Suspicious activity monitored - -### Monitoring & Response -- [ ] Log monitoring configured -- [ ] Disk space alerts set up -- [ ] Resource usage monitored -- [ ] Incident response plan documented -- [ ] Emergency contacts updated - -### Compliance (if applicable) -- [ ] Data retention policy defined -- [ ] User data handling documented -- [ ] Privacy policy published -- [ ] Terms of service published -- [ ] GDPR/compliance requirements met - -## Reporting Security Issues - -Found a security vulnerability in Mist? - -**Please report responsibly:** - -1. **DO NOT** open a public GitHub issue for security vulnerabilities -2. **DO** report via [GitHub Security Advisories](https://github.com/corecollectives/mist/security/advisories/new) -3. **OR** email: security@corecollectives.com (if available) -4. Include: - - Description of the vulnerability - - Steps to reproduce - - Potential impact - - Suggested fix (if any) - -**We will:** -- Acknowledge receipt within 48 hours -- Provide estimated fix timeline -- Credit you in security advisory (if desired) -- Keep you updated on progress - -Thank you for helping keep Mist secure! diff --git a/www/docs/deployment/traefik.md b/www/docs/deployment/traefik.md deleted file mode 100644 index 4283355..0000000 --- a/www/docs/deployment/traefik.md +++ /dev/null @@ -1,318 +0,0 @@ -# Traefik Setup - -Configure Traefik reverse proxy for Mist. - -## Overview - -Traefik v3.1 is installed automatically with the Mist installation script. It provides: -- Automatic SSL/TLS certificate management via Let's Encrypt -- Reverse proxy for all deployed applications -- HTTP to HTTPS redirection -- Docker label-based routing -- Dynamic configuration updates - -## Installation Location - -Traefik is installed in `/opt/mist/` with the following structure: - -``` -/opt/mist/ -├── traefik-compose.yml # Docker Compose configuration -├── traefik-static.yml # Static Traefik configuration -└── letsencrypt/ - └── acme.json # SSL certificates storage -``` - -## Docker Compose Configuration - -`/opt/mist/traefik-compose.yml`: - -```yaml -services: - traefik: - image: traefik:v3.1 - container_name: traefik - restart: unless-stopped - ports: - - "80:80" # HTTP - - "443:443" # HTTPS - - "8081:8080" # Traefik dashboard - volumes: - - "/var/run/docker.sock:/var/run/docker.sock:ro" - - "./letsencrypt:/letsencrypt" - - "/var/lib/mist/traefik:/etc/traefik/dynamic:ro" - - "./traefik-static.yml:/etc/traefik/traefik.yml:ro" - networks: - - traefik-net - -networks: - traefik-net: - external: true -``` - -## Static Configuration - -`/opt/mist/traefik-static.yml`: - -```yaml -api: - dashboard: true - insecure: true - -providers: - docker: - exposedByDefault: false - network: traefik-net - endpoint: "unix:///var/run/docker.sock" - file: - directory: /etc/traefik/dynamic - watch: true - -entryPoints: - web: - address: ":80" - websecure: - address: ":443" - -certificatesResolvers: - le: - acme: - email: admin@example.com # Set during installation - storage: /letsencrypt/acme.json - httpChallenge: - entryPoint: web - -log: - level: INFO -``` - -## Dynamic Configuration - -Mist automatically generates dynamic Traefik configuration in `/var/lib/mist/traefik/dynamic.yml` based on deployed applications and domains. - -This file is auto-generated and updated when: -- Applications are deployed -- Domains are added or removed -- SSL certificates are issued - -::: warning -Do not manually edit files in `/var/lib/mist/traefik/`. They are auto-generated by Mist. -::: - -## SSL/TLS Configuration - -Mist automatically configures Traefik for SSL/TLS with Let's Encrypt integration. - -### Automatic SSL Setup - -When you add a domain to your application, Mist automatically: -1. Updates dynamic Traefik configuration -2. Adds Docker labels to application containers -3. Traefik requests SSL certificate from Let's Encrypt via HTTP-01 challenge -4. Sets up HTTP to HTTPS redirect -5. Enables automatic certificate renewal (every 60 days) - -### Certificate Resolver - -The Let's Encrypt certificate resolver is configured in `/opt/mist/traefik-static.yml`: - -```yaml -certificatesResolvers: - le: - acme: - email: your-email@example.com # Set during installation - storage: /letsencrypt/acme.json - httpChallenge: - entryPoint: web -``` - -### Certificate Storage - -Certificates are stored in `/opt/mist/letsencrypt/acme.json`. - -```bash -# View certificate file -ls -la /opt/mist/letsencrypt/acme.json - -# Backup certificates -sudo cp /opt/mist/letsencrypt/acme.json /backup/acme.json.$(date +%Y%m%d) -``` - -::: warning Important -The `acme.json` file must have `600` permissions for security: -```bash -sudo chmod 600 /opt/mist/letsencrypt/acme.json -``` -::: - -### How SSL Works in Mist - -1. **Domain Added**: User adds domain to application via Mist UI -2. **Container Deployed**: Mist deploys container with Traefik labels: - ```yaml - traefik.enable=true - traefik.http.routers.{app-id}.rule=Host(`domain.com`) - traefik.http.routers.{app-id}.entrypoints=websecure - traefik.http.routers.{app-id}.tls.certresolver=le - ``` -3. **Certificate Request**: Traefik automatically requests certificate from Let's Encrypt -4. **HTTP Challenge**: Let's Encrypt verifies domain ownership via HTTP-01 challenge -5. **Certificate Issued**: Certificate stored in `acme.json` and served by Traefik -6. **Auto-Renewal**: Traefik automatically renews certificates before expiry - -### Verifying SSL Configuration - -Check Traefik logs for certificate operations: - -```bash -docker logs traefik 2>&1 | grep -i certificate -docker logs traefik 2>&1 | grep -i acme -``` - -Look for messages like: -- `Serving default certificate` -- `Certificate obtained for domain` -- `Renewing certificate` - -Check certificate status in Mist UI: -- Navigate to application **Domains** tab -- Certificate status shows as "Active" when issued - -[Learn more about SSL automation →](../guide/ssl-automation) - -## Dashboard Access - -Traefik dashboard is available at: -``` -http://your-server-ip:8081 -``` - -The dashboard shows: -- Active routers and services -- Middleware configuration -- SSL certificate status -- Real-time metrics - -::: warning Security -The dashboard is configured with `insecure: true` for easy access. In production environments: -- Restrict access via firewall rules -- Use SSH tunneling for remote access -- Or disable the dashboard entirely - -```bash -# Restrict to localhost only (recommended) -sudo ufw deny 8081 -sudo ufw allow from 127.0.0.1 to any port 8081 - -# Access via SSH tunnel -ssh -L 8081:localhost:8081 user@your-server -# Then browse to http://localhost:8081 -``` -::: - -## Network Configuration - -Mist uses the `traefik-net` Docker network for routing: - -```bash -# Verify network exists -docker network inspect traefik-net - -# View connected containers -docker network inspect traefik-net --format='{{range .Containers}}{{.Name}} {{end}}' -``` - -All application containers are automatically connected to `traefik-net` when deployed with domains. - -## Troubleshooting - -### Check Traefik Status - -```bash -# Verify Traefik is running -docker ps | grep traefik - -# Check container health -docker inspect traefik --format='{{.State.Status}}' -``` - -### View Logs - -```bash -# All logs -docker logs traefik - -# Follow logs in real-time -docker logs -f traefik - -# Search for errors -docker logs traefik 2>&1 | grep -i error - -# Certificate-related logs -docker logs traefik 2>&1 | grep -i acme -docker logs traefik 2>&1 | grep -i certificate -``` - -### Verify Configuration - -```bash -# Check Traefik version -docker exec traefik traefik version - -# Verify static configuration -cat /opt/mist/traefik-static.yml - -# Check dynamic configuration -cat /var/lib/mist/traefik/dynamic.yml -``` - -### Common Issues - -**Port conflicts:** -```bash -# Check if ports 80, 443, 8081 are in use -sudo netstat -tulpn | grep -E ':(80|443|8081)' -``` - -**Certificate issues:** -```bash -# Check acme.json permissions -ls -la /opt/mist/letsencrypt/acme.json -# Should be: -rw------- (600) - -# Reset certificates (if needed) -sudo rm /opt/mist/letsencrypt/acme.json -sudo touch /opt/mist/letsencrypt/acme.json -sudo chmod 600 /opt/mist/letsencrypt/acme.json -docker restart traefik -``` - -**Network issues:** -```bash -# Verify traefik-net exists -docker network ls | grep traefik-net - -# Recreate network if needed -docker network create traefik-net -docker restart traefik -``` - -### Restart Traefik - -```bash -cd /opt/mist -docker compose -f traefik-compose.yml restart -``` - -For more details, see [Traefik Documentation](https://doc.traefik.io/traefik/). - -## Coming Soon - -The following Traefik features are planned: - -- **Wildcard SSL Certificates** - Single certificate for `*.domain.com` -- **Custom Certificate Upload** - Use your own SSL certificates -- **Advanced Rate Limiting** - Request throttling per domain -- **IP Whitelisting** - Restrict access by IP address -- **Basic Authentication** - Password-protect applications via Traefik -- **Custom Headers** - Set security headers (HSTS, CSP, etc.) diff --git a/www/docs/deployment/upgrading.md b/www/docs/deployment/upgrading.md deleted file mode 100644 index 5e45c39..0000000 --- a/www/docs/deployment/upgrading.md +++ /dev/null @@ -1,469 +0,0 @@ -# Upgrading Mist - -Keep your Mist installation up to date with the latest features and security patches. - -## Before You Upgrade - -::: danger Always Backup First -**NEVER upgrade without a recent backup.** Database migrations can fail or cause data loss. - -```bash -# Quick backup before upgrading -sudo systemctl stop mist -sqlite3 /var/lib/mist/mist.db ".backup /var/backups/mist_pre_upgrade_$(date +%Y%m%d_%H%M%S).db" -sudo systemctl start mist -``` - -See [Backup & Recovery](./backup) for complete backup procedures. -::: - -### Pre-Upgrade Checklist - -- [ ] Backup database (`/var/lib/mist/mist.db`) -- [ ] Backup SSL certificates (`/opt/mist/letsencrypt/acme.json`) -- [ ] Check [CHANGELOG.md](https://github.com/corecollectives/mist/blob/main/CHANGELOG.md) for breaking changes -- [ ] Note current deployed applications and their status -- [ ] Have rollback plan ready -- [ ] Schedule upgrade during low-traffic window - -## Check Current Version - -Check your current Mist version: - -**Via Dashboard:** -1. Navigate to **Extras → Updates** -2. View current version at the top -3. Click "Check for Updates" to see if updates are available - -**Via API:** -```bash -curl http://localhost:8080/api/updates/version -``` - -**Via CLI:** -```bash -# Check git commit (if available) -cd /opt/mist && git log -1 --oneline - -# Check systemd service status -sudo systemctl status mist -``` - -## Upgrade Methods - -### Method 1: Dashboard One-Click Update (Recommended) - -Mist includes a built-in update system accessible from the dashboard: - -1. Navigate to **Extras → Updates** in the dashboard -2. Click **"Check for Updates"** to see if a new version is available -3. Review the release notes and changes -4. Click **"Update Now"** to trigger the automatic update -5. The system will: - - Download and install the latest version - - Restart the Mist service - - Run database migrations automatically - - Mark the update as complete - -::: tip Auto-Recovery -If an update appears stuck (rare), simply restart the Mist service: -```bash -sudo systemctl restart mist -``` - -The system automatically detects incomplete updates on startup and resolves them based on the installed version. -::: - -::: warning Manual Clearing -Owners can manually clear stuck updates via the dashboard or API if needed: -```bash -curl -X POST http://localhost:8080/api/updates/clear \ - -H "Authorization: Bearer YOUR_TOKEN" -``` -::: - -### Method 2: Re-run Installation Script - -The installation script is idempotent and will update your existing installation: - -```bash -# 1. Backup database -sudo systemctl stop mist -sqlite3 /var/lib/mist/mist.db ".backup /var/backups/mist_backup_$(date +%Y%m%d).db" -sudo systemctl start mist - -# 2. Re-run installation script -curl -fsSL https://raw.githubusercontent.com/corecollectives/mist/main/install.sh | bash - -# 3. Verify upgrade -sudo systemctl status mist -sudo journalctl -u mist -n 50 -``` - -**What it does:** -- Pulls latest code from GitHub (`main` branch) -- Rebuilds Go backend -- Rebuilds Vite frontend -- Restarts Mist service -- Preserves database and all data -- Runs database migrations automatically - -### Method 3: Manual Git Pull and Rebuild - -If you want more control over the upgrade process: - -```bash -# 1. Backup database -sudo systemctl stop mist -sqlite3 /var/lib/mist/mist.db ".backup /var/backups/mist_backup_$(date +%Y%m%d).db" - -# 2. Pull latest code -cd /opt/mist -git fetch origin -git pull origin main - -# 3. Rebuild backend -cd server -go build -o ../bin/server main.go - -# 4. Rebuild frontend -cd ../dash -npm install -npm run build - -# 5. Restart service -sudo systemctl start mist - -# 6. Verify -sudo systemctl status mist -sudo journalctl -u mist -n 50 -``` - -### Method 4: Upgrade to Specific Version - -To upgrade to a specific release or branch: - -```bash -# 1. Backup -sudo systemctl stop mist -sqlite3 /var/lib/mist/mist.db ".backup /var/backups/mist_backup_$(date +%Y%m%d).db" - -# 2. Checkout specific version -cd /opt/mist -git fetch --all --tags -git checkout tags/v1.2.3 # or specific commit hash - -# 3. Rebuild (same as Method 2) -cd server && go build -o ../bin/server main.go -cd ../dash && npm install && npm run build - -# 4. Restart -sudo systemctl start mist -``` - -## Database Migrations - -Mist automatically runs database migrations on startup. The migration system is located in `server/db/migrations/`. - -### How Migrations Work - -1. **On Startup**: Mist checks which migrations have been applied -2. **Auto-Execute**: Runs any pending migrations in order -3. **Track Applied**: Stores migration history in the database -4. **Continue**: Starts normally after migrations complete - -### Monitoring Migrations - -Check migration status in logs: - -```bash -# View recent Mist logs -sudo journalctl -u mist -n 100 - -# Follow logs in real-time during upgrade -sudo journalctl -u mist -f - -# Search for migration-related logs -sudo journalctl -u mist | grep -i migration -sudo journalctl -u mist | grep -i "Running migration" -``` - -Look for messages like: -- `Running migrations...` -- `Migration 001_Create_User.sql completed` -- `All migrations applied successfully` - -### Migration Locations - -Migrations are stored in: -``` -/opt/mist/server/db/migrations/ -├── 001_Create_User.sql -├── 002_Create_Projects.sql -├── 003_Create_Project_members.sql -├── 004_Create_GitProviders.sql -└── ... -``` - -### If Migrations Fail - -If a migration fails during upgrade: - -```bash -# Stop Mist -sudo systemctl stop mist - -# Check logs for error details -sudo journalctl -u mist -n 100 | grep -i error - -# Restore from backup -sudo cp /var/backups/mist_backup_YYYYMMDD.db /var/lib/mist/mist.db - -# Try again or report issue on GitHub -sudo systemctl start mist -``` - -::: warning -Never manually modify the database schema. Always use the built-in migration system or report schema issues on GitHub. -::: - -## Post-Upgrade Verification - -After upgrading, verify everything works correctly: - -### 1. Check Service Status - -```bash -# Verify Mist is running -sudo systemctl status mist - -# Check for errors in recent logs -sudo journalctl -u mist -n 50 -``` - -### 2. Test Web Interface and Version - -```bash -# Test localhost access -curl http://localhost:8080/health - -# Check updated version -curl http://localhost:8080/api/updates/version - -# Or browse to your domain -# https://your-mist-domain.com -``` - -**Via Dashboard:** -- Log in and check the sidebar shows the new version -- Navigate to **Extras → Updates** to verify the update was successful - -### 3. Verify Applications - -- Log in to Mist dashboard -- Check all applications are showing as "Running" -- Test one or two applications to ensure they're accessible -- Check recent deployments in audit logs - -### 4. Check Traefik - -```bash -# Verify Traefik is running -docker ps | grep traefik - -# Check Traefik logs for errors -docker logs traefik 2>&1 | tail -50 -``` - -### 5. Test SSL Certificates - -```bash -# Check certificate status for your domains -curl -I https://your-app-domain.com - -# Verify SSL in browser (no warnings) -``` - -## Rollback Procedure - -If the upgrade causes issues and you need to rollback: - -### Quick Rollback - -```bash -# 1. Stop Mist -sudo systemctl stop mist - -# 2. Restore previous code (if available) -cd /opt/mist -git log --oneline -10 # Find previous commit -git reset --hard - -# 3. Restore database backup -sudo cp /var/backups/mist_backup_YYYYMMDD.db /var/lib/mist/mist.db - -# 4. Rebuild (if code changed) -cd server && go build -o ../bin/server main.go - -# 5. Restart -sudo systemctl start mist - -# 6. Verify -sudo systemctl status mist -``` - -### Full Rollback - -If you need to completely restore to pre-upgrade state: - -```bash -# Stop services -sudo systemctl stop mist -docker compose -f /opt/mist/traefik-compose.yml down - -# Restore database -sudo cp /var/backups/mist_backup_YYYYMMDD.db /var/lib/mist/mist.db - -# Restore certificates (if needed) -sudo cp /var/backups/acme_backup_YYYYMMDD.json /opt/mist/letsencrypt/acme.json - -# Restore code to previous version -cd /opt/mist -git reset --hard - -# Rebuild -cd server && go build -o ../bin/server main.go -cd ../dash && npm install && npm run build - -# Restart services -sudo systemctl start mist -docker compose -f /opt/mist/traefik-compose.yml up -d - -# Verify -sudo systemctl status mist -``` - -::: danger Report Issues -If you need to rollback, please report the issue on [GitHub Issues](https://github.com/corecollectives/mist/issues) with: -- Error logs from `journalctl -u mist` -- What version you upgraded from/to -- Steps that led to the problem -::: - -## Upgrade Best Practices - -### Scheduling Upgrades - -- **Development**: Upgrade anytime -- **Staging**: Upgrade and test thoroughly before production -- **Production**: Schedule during maintenance windows or low-traffic periods - -### Testing Strategy - -1. **Test in Staging First**: If possible, test upgrade on staging server -2. **Read Changelog**: Check for breaking changes and new features -3. **Backup Everything**: Database, certificates, uploads -4. **Gradual Rollout**: Test one application before full deployment -5. **Monitor Closely**: Watch logs and metrics for 24-48 hours after upgrade - -### Stay Updated - -- Watch [GitHub Releases](https://github.com/corecollectives/mist/releases) -- Subscribe to [GitHub Discussions](https://github.com/corecollectives/mist/discussions) -- Check for security updates regularly - -### Recommended Upgrade Frequency - -- **Security patches**: Apply immediately -- **Minor versions**: Monthly or when new features needed -- **Major versions**: Test thoroughly in staging first - -## Troubleshooting Upgrades - -### Common Issues - -**Build fails during upgrade:** -```bash -# Clear build cache and retry -cd /opt/mist/server -go clean -cache -go build -o ../bin/server main.go - -cd /opt/mist/dash -rm -rf node_modules package-lock.json -npm install -npm run build -``` - -**Service won't start after upgrade:** -```bash -# Check detailed logs -sudo journalctl -u mist -n 100 --no-pager - -# Check for port conflicts -sudo netstat -tulpn | grep 8080 - -# Verify file permissions -ls -la /opt/mist/bin/server -``` - -**Database migration stuck:** -```bash -# Check if database is locked -sudo lsof /var/lib/mist/mist.db - -# If locked, stop all processes accessing it -sudo systemctl stop mist -# Wait a few seconds -sudo systemctl start mist -``` - -**Update appears stuck in dashboard:** -```bash -# The system auto-recovers on restart -sudo systemctl restart mist - -# Check logs to verify resolution -sudo journalctl -u mist -n 50 - -# Or manually clear via API -curl -X POST http://localhost:8080/api/updates/clear \ - -H "Authorization: Bearer YOUR_TOKEN" -``` - -See the [Troubleshooting Guide](../troubleshooting/) for more update-related issues. - -**Applications not accessible after upgrade:** -```bash -# Restart Traefik -docker compose -f /opt/mist/traefik-compose.yml restart - -# Check Traefik logs -docker logs traefik 2>&1 | tail -100 - -# Verify dynamic config -cat /var/lib/mist/traefik/dynamic.yml -``` - -## Available Now - -The following upgrade features are currently available: - -- ✅ **One-Click Dashboard Updates** - Update directly from **Extras → Updates** -- ✅ **Version Display** - View current version in sidebar and updates page -- ✅ **Update Status Tracking** - Monitor update progress and history -- ✅ **Automatic Recovery** - System auto-resolves stuck updates on restart -- ✅ **Manual Update Clear** - Owners can manually clear stuck updates -- ✅ **Check for Updates** - View available updates from dashboard -- ✅ **Database Auto-Migrations** - Migrations run automatically on update - -## Coming Soon - -The following upgrade features are planned for future releases: - -- **In-App Update Notifications** - Push notifications when updates are available -- **Automatic Scheduled Updates** - Opt-in automatic updates with configurable schedule -- **Update Channels** - Choose stable, beta, or nightly update channel -- **Pre-Upgrade Health Check** - Automatic system checks before upgrade -- **One-Click Rollback** - Built-in rollback from dashboard -- **Staged Rollouts** - Upgrade one server at a time in multi-server setup diff --git a/www/docs/guide/applications.md b/www/docs/guide/applications.md deleted file mode 100644 index 413f3f6..0000000 --- a/www/docs/guide/applications.md +++ /dev/null @@ -1,720 +0,0 @@ -# Applications - -Applications are the core deployable units in Mist. Each application represents a containerized service that can be a web application, background service, or managed database. - -## Application Types - -Mist supports three distinct application types: - -### Web Applications (`web`) - -Web applications are services exposed via HTTP/HTTPS through Traefik reverse proxy: - -- **Public access** via custom domains -- **Auto-generated domains** (if wildcard DNS configured) -- **SSL certificates** managed automatically -- **Port configuration** (default: 3000) -- **Git-based deployments** from GitHub repositories - -**Use cases**: Websites, web apps, REST APIs, GraphQL servers - -### Service Applications (`service`) - -Background services that run without external HTTP access: - -- **Internal only** - Not exposed to public internet -- **Port 3000** (internal, not routable) -- **Git-based deployments** from GitHub repositories -- **Same deployment flow** as web apps - -**Use cases**: Workers, queue processors, scheduled tasks, internal services - -### Database Applications (`database`) - -Managed database services using pre-configured Docker templates: - -- **Template-based** deployment (PostgreSQL, MySQL, Redis, MongoDB) -- **No Git repository** required -- **Pre-configured** CPU and memory limits from template -- **Version control** via Docker image tags -- **Environment variables** for configuration (passwords, databases, etc.) - -**Use cases**: PostgreSQL, MySQL, Redis, MongoDB, or any other database service - -## Creating an Application - -### Via Dashboard - -1. Navigate to your project -2. Click **"New Application"** -3. Select application type: - - **Web** - For public-facing applications - - **Service** - For background workers - - **Database** - For managed database services -4. Fill in the required fields based on type - -### Web/Service Configuration - -**Initial Creation (Required):** -- **Name**: Unique identifier within project (lowercase, numbers, hyphens only) -- **Description**: Optional application description -- **Port**: Port your app listens on (default: 3000) - -**After Creation (Configure in App Settings):** -- **Git Repository**: Connect GitHub repository -- **Git Branch**: Select branch to deploy from -- **Dockerfile Path**: Path to your Dockerfile (default: `./Dockerfile`) -- **Root Directory**: Root directory for builds (default: `/`) -- **Environment Variables**: Add key-value pairs in Environment tab - -::: tip Build & Start Commands Not Yet Available -Build and start commands are coming soon. Currently, you must provide a Dockerfile for your application. -::: - -### Database Configuration - -- **Name**: Database instance name -- **Description**: Optional description -- **Template**: Select from available templates: - - PostgreSQL - - MySQL - - Redis - - MongoDB -- **Environment Variables**: Database credentials and settings - -::: tip Auto-Generated Domains -For web applications, Mist can automatically generate a subdomain if wildcard DNS is configured in system settings. The format is `{project-name}-{app-name}.{wildcard-domain}`. - -Example: If wildcard domain is `example.com`, project is `production`, and app is `api`, the auto-generated domain will be `production-api.example.com`. - -This only applies to web applications. Service and database apps do not get auto-generated domains. -::: - -## Resource Configuration - -### CPU Limits - -Set CPU core limits for your application: - -- **Default**: No limit (uses available CPU) -- **Range**: 0.1 to N cores (e.g., 0.5, 1.0, 2.0) -- **Database defaults**: Applied automatically from templates - -```javascript -// API example -{ - "appId": 1, - "cpuLimit": 1.0 // 1 CPU core -} -``` - -### Memory Limits - -Set memory limits in megabytes: - -- **Default**: No limit (uses available memory) -- **Range**: 128MB to system maximum -- **Database defaults**: Applied automatically from templates - -```javascript -// API example -{ - "appId": 1, - "memoryLimit": 512 // 512 MB -} -``` - -### Restart Policies - -Control container restart behavior: - -- **`no`**: Never restart automatically -- **`always`**: Always restart if stopped -- **`on-failure`**: Restart only on failure -- **`unless-stopped`**: Restart unless manually stopped (default) - -```javascript -// API example -{ - "appId": 1, - "restartPolicy": "unless-stopped" -} -``` - -## Container Configuration - -### Dockerfile Requirements - -**All web and service applications require a Dockerfile.** Mist builds your application using Docker. - -#### Basic Dockerfile Structure - -Your repository must include a Dockerfile with: -1. **Base image** (FROM statement) -2. **Working directory** (WORKDIR) -3. **Dependency installation** (COPY package files, RUN install commands) -4. **Application code** (COPY source files) -5. **Port exposure** (EXPOSE - optional but recommended) -6. **Start command** (CMD or ENTRYPOINT) - -Example for Node.js: -```dockerfile -FROM node:18-alpine -WORKDIR /app - -# Copy dependency files -COPY package*.json ./ - -# Install dependencies -RUN npm ci --production - -# Copy application code -COPY . . - -# Expose port (should match port in Mist config) -EXPOSE 3000 - -# Start command -CMD ["node", "server.js"] -``` - -Example for Python: -```dockerfile -FROM python:3.11-slim -WORKDIR /app - -# Copy requirements -COPY requirements.txt . - -# Install dependencies -RUN pip install --no-cache-dir -r requirements.txt - -# Copy application -COPY . . - -# Expose port -EXPOSE 8000 - -# Start command -CMD ["python", "app.py"] -``` - -Example for Go: -```dockerfile -FROM golang:1.21-alpine AS builder -WORKDIR /app -COPY go.* ./ -RUN go mod download -COPY . . -RUN go build -o main . - -FROM alpine:latest -WORKDIR /app -COPY --from=builder /app/main . -EXPOSE 8080 -CMD ["./main"] -``` - -::: tip Dockerfile Path -If your Dockerfile is not in the root directory, specify the path in the app settings (e.g., `./docker/Dockerfile` or `./backend/Dockerfile`). -::: - -### Dockerfile Support - -### Dockerfile Support - -Mist supports custom Dockerfiles in your repository: - -1. **Default location**: `./Dockerfile` in repository root -2. **Custom path**: Specify via `dockerfilePath` setting (e.g., `./docker/Dockerfile.prod`) -3. **Root directory**: Set `rootDirectory` for builds in subdirectories (e.g., `/services/api`) - -The build process: -- Uses your specified Dockerfile -- Passes all environment variables as `--build-arg` -- Tags image with commit hash for version tracking -- Stores build logs for debugging - -```dockerfile -# Example multi-service monorepo structure -# Repository structure: -# /services -# /api -# Dockerfile -# ... -# /worker -# Dockerfile -# ... - -# For API service, set: -# Root Directory: /services/api -# Dockerfile Path: ./Dockerfile - -FROM node:18-alpine -WORKDIR /app -COPY package*.json ./ -RUN npm ci --production -COPY . . -EXPOSE 3000 -CMD ["node", "server.js"] -``` - -::: tip Multi-Stage Builds -Use multi-stage Dockerfiles to keep production images small and secure. Copy only necessary artifacts from build stage to runtime stage. -::: - -### Health Checks - -::: warning Coming Soon -Health checks are an upcoming feature. Container health monitoring is not yet implemented. -::: - -Health checks will allow automatic container monitoring: -- HTTP endpoint checks (e.g., `/health`) -- Configurable check intervals and timeouts -- Automatic restart on health check failures -- Status tracking in dashboard - -## Git Integration - -### Connecting a Repository - -For web and service applications: - -1. **Install GitHub App** on your GitHub account/organization -2. **Grant access** to repositories in Mist settings -3. **Select repository** when creating application -4. **Choose branch** to deploy from - -### Configuration Settings - -After creating your application, configure Git integration in the app settings: - -- **Git Repository**: Repository name format `owner/repo` (e.g., `myorg/myapp`) -- **Git Branch**: Branch name to deploy (e.g., `main`, `develop`) -- **Root Directory**: Subdirectory for monorepos (optional, default: `/`) -- **Dockerfile Path**: Path to Dockerfile (optional, default: `./Dockerfile`) - -::: warning Dockerfile Required -Your repository must contain a valid Dockerfile. Build and start commands are not yet supported - all applications are deployed using Docker. -::: - -```javascript -// API example -{ - "appId": 1, - "gitRepository": "myorg/myapp", - "gitBranch": "main", - "rootDirectory": "/", - "dockerfilePath": "./Dockerfile" -} -``` - -## Environment Variables - -Applications can have unlimited environment variables for configuration. See the [Environment Variables guide](./environment-variables) for details. - -### Adding Variables - -1. Go to **Environment Variables** tab -2. Click **"Add Variable"** or use **"Bulk Paste"** -3. Enter key-value pairs -4. Variables are applied on next deployment - -### Build-time & Runtime - -All environment variables are available in both phases: - -- **Build-time**: Passed as `--build-arg` during Docker image build -- **Runtime**: Passed as `-e` flags when container starts -- **Both**: All variables are automatically available in both phases - -Example Dockerfile using build args: -```dockerfile -FROM node:18-alpine - -# Build arg accessible during build -ARG NODE_ENV -ARG API_URL - -WORKDIR /app -COPY package*.json ./ -RUN npm ci --production=$NODE_ENV - -COPY . . - -# Runtime environment variables -ENV NODE_ENV=$NODE_ENV -ENV API_URL=$API_URL - -CMD ["node", "server.js"] -``` - -::: warning Secrets Management -Never commit secrets to Git. Always use environment variables for sensitive data like API keys, passwords, and tokens. -::: - -## Deployment - -### Deployment Process - -When you deploy an application, Mist follows this pipeline: - -**For Web/Service Apps:** -1. **Repository Clone**: Clones your Git repository to `/var/lib/mist/projects/{projectId}/apps/{appName}` -2. **Image Build**: Builds Docker image using your Dockerfile with environment variables as build args -3. **Container Stop**: Gracefully stops existing container (if running) -4. **Container Start**: Starts new container with updated configuration -5. **Domain Routing**: Updates Traefik routes (for web apps with domains) - -**For Database Apps:** -1. **Image Pull**: Pulls the specified Docker image from Docker Hub -2. **Container Stop**: Stops existing container (if running) -3. **Container Start**: Starts new container with template configuration - -### Manual Deployment - -Trigger deployment from dashboard: - -1. Click **"Deploy"** button -2. Latest commit is fetched automatically -3. Deployment added to queue -4. Build starts when worker is available -5. Watch real-time progress and logs - -For databases, deployment uses the configured Docker image version from the template. - -### Automatic Deployment - -With GitHub App configured: - -- **Push events**: Auto-deploy on push to configured branch -- **Webhooks**: GitHub triggers deployment automatically -- **Status updates**: Real-time deployment progress - -[Learn more about deployments →](./deployments) - -## Deployment Strategies - -Mist uses a rolling deployment strategy: - -- **Rolling** (default): Stops old container, then starts new container -- Zero-downtime deployments coming soon - -```javascript -{ - "deploymentStrategy": "rolling" -} -``` - -## Domains - -Add custom domains to web applications: - -1. Go to **Domains** tab -2. Click **"Add Domain"** -3. Enter domain name (e.g., `app.example.com`) -4. Configure DNS A record to point to your server -5. Wait for DNS verification -6. SSL certificate issued automatically via Let's Encrypt - -[Learn more about domains →](./domains) - -## Container Controls - -### Networking - -All containers are connected to the `traefik-net` Docker bridge network for communication: - -**Web Apps with Domains:** -- Connected to `traefik-net` -- Routed through Traefik reverse proxy -- Automatic SSL/TLS via Let's Encrypt -- HTTP to HTTPS redirect enabled -- Accessible via custom domains - -**Web Apps without Domains:** -- Exposed directly on host port (e.g., `-p 3000:3000`) -- Accessible via `http://server-ip:port` -- No SSL by default - -**Service Apps:** -- Connected to `traefik-net` -- No external port exposure -- Accessible by other containers via container name: `app-{appId}` - -**Database Apps:** -- Connected to `traefik-net` -- Internal DNS resolution -- Accessible by other apps via container name: `app-{appId}` -- Example connection: `postgres://user:pass@app-123:5432/dbname` - -::: tip Inter-Container Communication -Applications can communicate with each other using the container name format `app-{appId}` where `{appId}` is the application ID shown in the dashboard. -::: - -### Start/Stop/Restart - -Control container lifecycle from dashboard or API: - -- **Stop**: Gracefully stops container -- **Start**: Starts stopped container -- **Restart**: Restarts running container - -### Application Status - -Container states: - -- **`stopped`**: Container is not running -- **`running`**: Container is active and healthy -- **`error`**: Container failed to start or crashed -- **`building`**: Docker image is being built -- **`deploying`**: Container is being deployed - -## Monitoring - -### Real-time Logs - -View live container logs via WebSocket: - -- **Real-time streaming**: See logs as they're generated -- **Last 100 lines**: Initial connection shows tail -- **Auto-reconnect**: Maintains connection with ping/pong -- **Timestamps**: Each log line includes timestamp - -[View logs tab in dashboard or use WebSocket API](/api/websockets) - -### Deployment History - -Track all deployments: - -- **Deployment number**: Sequential numbering -- **Commit info**: Hash and message (for Git apps) -- **Status**: pending → building → deploying → success/failed -- **Duration**: Build and deployment time -- **Build logs**: Complete build output -- **Active deployment**: Currently running version - -### Container Statistics - -Monitor basic container information: - -- Container name and ID -- Container state (running, stopped, exited) -- Container uptime - -::: tip Coming Soon -Advanced metrics including CPU usage, memory consumption, and network I/O are coming soon. -::: - -## Application Settings - -Update application configuration: - -- **General**: Name, description -- **Git**: Repository URL, branch, paths -- **Resources**: CPU, memory limits -- **Container**: Restart policy -- **Status**: Control running state - -::: tip Audit Logging -All configuration changes are logged in the audit log with user information and timestamps. -::: - -## Rollback - -::: warning Coming Soon -Rollback functionality is an upcoming feature. You cannot currently rollback to previous deployments. -::: - -When available, you'll be able to: -1. View deployment history -2. Select a previous successful deployment -3. Click "Rollback" to redeploy that version -4. Previous Docker images will be reused (no rebuild needed) - -## Service Templates - -Database applications use pre-configured service templates: - -### How Templates Work - -1. **Template Selection**: Choose from available database/service templates -2. **Auto-Configuration**: CPU, memory, port, and environment variables set from template -3. **Docker Image**: Pre-defined image and version from template -4. **One-Click Deploy**: No Dockerfile or Git repository required - -### Template Properties - -Each template defines: -- `dockerImage`: Docker Hub image name -- `dockerImageVersion`: Image tag (e.g., `latest`, `14-alpine`) -- `defaultPort`: Container port -- `recommendedCpu`: Suggested CPU cores -- `recommendedMemory`: Suggested memory in MB -- `defaultEnvVars`: Pre-configured environment variables -- `volumeRequired`: Whether persistent volume is needed - -### Available Templates - -Common templates include: -- **PostgreSQL**: Full-featured relational database -- **MySQL**: Popular open-source database -- **Redis**: In-memory data store and cache -- **MongoDB**: Document-oriented database -- **MariaDB**: MySQL-compatible database - -::: tip Coming Soon -Custom template creation and management by administrators is an upcoming feature. Currently, only pre-defined templates are available. -::: - -## Best Practices - -### Application Structure - -Organize your repository: - -``` -my-app/ -├── src/ # Source code -├── Dockerfile # Production Dockerfile -├── .dockerignore # Exclude unnecessary files -├── package.json # Dependencies -├── .env.example # Document required env vars -└── README.md # Setup instructions -``` - -### Resource Planning - -- **Start small**: Begin with default limits -- **Database memory**: Databases need sufficient RAM -- **CPU limits**: Set realistic limits for consistent performance - -::: tip Coming Soon -Once metrics are available, you'll be able to monitor usage and scale accordingly. -::: - -### Environment Variables - -- Document all required variables in README -- Use descriptive key names (e.g., `DATABASE_URL` not `DB`) -- Separate development and production configs -- Rotate secrets regularly - -### Build Optimization - -Use `.dockerignore` to exclude files: - -``` -node_modules -.git -.env -*.log -*.md -.vscode -.idea -``` - -Use multi-stage builds: - -```dockerfile -# Build stage -FROM node:18-alpine AS builder -WORKDIR /app -COPY package*.json ./ -RUN npm ci -COPY . . -RUN npm run build - -# Production stage -FROM node:18-alpine -WORKDIR /app -COPY --from=builder /app/dist ./dist -COPY --from=builder /app/node_modules ./node_modules -CMD ["node", "dist/server.js"] -``` - -### Health Check Implementation - -Add health check endpoint to your application: - -```javascript -// Express.js example -app.get('/health', (req, res) => { - // Check database connection - // Check external dependencies - res.status(200).json({ - status: 'healthy', - timestamp: new Date().toISOString() - }); -}); -``` - -```python -# Flask example -@app.route('/health') -def health(): - return {'status': 'healthy'}, 200 -``` - -## Troubleshooting - -### Build Failures - -Check deployment logs for errors: - -- **Missing dependencies**: Check your package manager files (package.json, requirements.txt, etc.) -- **Dockerfile errors**: Verify Dockerfile syntax and commands -- **Build args missing**: Ensure required environment variables are set -- **Out of disk space**: Clean up old Docker images with `docker system prune` -- **Permission denied**: Check file permissions and COPY commands in Dockerfile -- **Git clone failed**: Verify GitHub App installation and repository access - -### Container Won't Start - -Common issues: - -- **Dockerfile CMD missing**: Container needs a command to run -- **Port mismatch**: Dockerfile EXPOSE port should match app configuration -- **Missing env vars**: Required environment variables not set -- **Application crashes**: Check container logs for runtime errors -- **Resource limits**: Container may be OOM killed if memory limit too low - -### Application Not Accessible - -Checklist: - -- ✅ Container status is `running` (green indicator) -- ✅ Domain has A record pointing to server -- ✅ DNS has propagated (check with `dig` or `nslookup`) -- ✅ Firewall allows ports 80/443 -- ✅ Application is listening on correct port -- ✅ No errors in container logs - -### Performance Issues - -Investigate: - -- Check CPU/memory usage in metrics -- Review container logs for errors -- Increase resource limits if needed -- Check database query performance -- Review application profiling data - -## API Reference - -See the [Applications API documentation](/api/applications) for programmatic access to: - -- Create applications -- Update configuration -- Control containers -- Get status and logs -- Manage resources - -## Related Topics - -- [Deployments](./deployments) - Deploy your applications -- [Environment Variables](./environment-variables) - Configure applications -- [Domains](./domains) - Add custom domains -- [Git Integration](./git-integration) - Connect GitHub repositories -- [Logs](./logs) - Monitor application logs diff --git a/www/docs/guide/architecture.md b/www/docs/guide/architecture.md deleted file mode 100644 index 161a2ad..0000000 --- a/www/docs/guide/architecture.md +++ /dev/null @@ -1,337 +0,0 @@ -# Architecture - -Mist is designed as a lightweight, monolithic application with a focus on simplicity and resource efficiency. - -## System Design - -### High-Level Architecture - -``` -Internet Traffic - ↓ - [Traefik Proxy] ← SSL/TLS, Domain Routing - ↓ - [App Containers] ← Your deployed applications - ↑ - [Mist Backend] ← Go API Server - ↑ - [SQLite DB] ← Application state - ↑ - [Frontend SPA] ← React dashboard -``` - -## Core Components - -### 1. Backend API Server (Go) - -**Location**: `/server` - -The backend is a single Go binary that handles: - -- **REST API** - HTTP endpoints for CRUD operations -- **WebSocket Server** - Real-time updates for logs and metrics -- **Authentication** - JWT-based auth with HTTP-only cookies -- **Deployment Queue** - In-memory queue for build jobs -- **Deployment Workers** - Goroutines that process deployments -- **Docker Integration** - Direct socket communication -- **GitHub Webhooks** - Auto-deploy on push events - -**Key Features:** -- Single compiled binary -- No external process managers needed -- Embedded migrations -- Graceful shutdown handling - -### 2. Frontend (React + Vite) - -**Location**: `/dash` - -Modern single-page application providing: - -- Dashboard with real-time updates -- Application management interface -- Deployment monitoring -- Log viewer with WebSocket streaming -- System metrics visualization - -**Tech Stack:** -- React 18 -- Vite for build tooling -- Tailwind CSS for styling -- Shadcn/ui components - -### 3. Database (SQLite) - -**Location**: `/var/lib/mist/mist.db` - -Embedded file-based database storing: - -- Users and sessions -- Projects and members -- Applications -- Deployments -- Environment variables -- Domains -- Audit logs -- System settings - -**Benefits:** -- No separate database server -- Automatic backups (just copy the file) -- ACID transactions -- Zero configuration - -### 4. Docker Engine - -Mist communicates with Docker via the socket (`/var/run/docker.sock`) to: - -- Build images from Dockerfiles -- Create and manage containers -- Stream container logs -- Monitor container status -- Configure networks - -### 5. Reverse Proxy (Traefik) - -**Location**: `docker-compose.yml` - -Traefik handles: - -- HTTP/HTTPS traffic routing -- Domain-based routing to containers -- SSL/TLS termination -- Load balancing (future) - -**Configuration:** -- Dynamic configuration via Docker labels -- File-based static configuration -- Automatic service discovery - -## Deployment Workflow - -### 1. User Triggers Deployment - -```mermaid -User → Frontend → API → Deployment Queue -``` - -### 2. Queue Processing - -```go -// Simplified deployment worker -func processDeployment(job DeploymentJob) { - // 1. Clone Git repository - cloneRepo(job.RepoURL, job.Branch) - - // 2. Build Docker image - buildImage(job.Dockerfile, job.BuildArgs) - - // 3. Stop old container (if exists) - stopContainer(job.AppID) - - // 4. Start new container - startContainer(job.Image, job.EnvVars, job.Port) - - // 5. Update Traefik routes - updateProxy(job.Domain, job.ContainerName) - - // 6. Update database - markDeploymentComplete(job.ID) -} -``` - -### 3. Real-time Updates - -WebSocket connections stream: -- Build logs during image creation -- Container logs after deployment -- System metrics every second -- Mist backend logs via journalctl integration - -## Data Flow - -### REST API Request - -``` -Frontend → API Endpoint → Middleware (Auth) → Handler → Database → Response -``` - -### WebSocket Connection - -``` -Frontend → WebSocket Upgrade → Auth Check → Stream Data → Client -``` - -### Deployment Flow - -``` -1. Git Push -2. GitHub Webhook → Mist API -3. Create Deployment Record -4. Add to Queue -5. Worker Picks Up Job -6. Clone Repo -7. Build Docker Image (stream logs via WebSocket) -8. Start Container -9. Update Traefik Config -10. Mark Deployment Complete -11. Notify Frontend via WebSocket -``` - -## Security Architecture - -### Authentication - -- JWT tokens stored in HTTP-only cookies -- Refresh token rotation -- Session management -- Password hashing with bcrypt - -### Authorization - -- Role-based access control (admin, user) -- Project-level permissions -- Owner-only operations - -### API Security - -- Rate limiting (planned) -- CORS configuration -- Input validation -- SQL injection prevention (parameterized queries) -- XSS protection - -## Monitoring & Observability - -### Logging Architecture - -**Container Logs:** -- Streamed from Docker via socket connection -- Real-time delivery via WebSocket to frontend -- Last 100 lines buffered on connection - -**System Logs:** -- Mist backend logs collected via systemd journal -- Accessed through `journalctl` integration -- Live streaming to `/logs` page -- Includes all Go backend output (API, deployments, errors) - -**Build Logs:** -- Written to filesystem during Docker builds -- Path: `/var/lib/mist/logs/.log` -- Accessible through deployment details - -### Metrics Collection - -**System Metrics:** -- CPU usage (percentage) -- Memory usage (used/total) -- Disk usage (used/total) -- Updated every second via WebSocket - -**Container Metrics:** -- Container state (running/stopped/error) -- Container uptime -- Basic container information - -::: tip -Advanced per-container metrics (CPU, memory, network I/O) are planned for future releases. -::: - -## Scalability Considerations - -### Current Design (Single Server) - -✅ **Strengths:** -- Simple deployment -- No complex networking -- Easy to debug -- Low resource usage - -⚠️ **Limitations:** -- Single point of failure -- Limited to one server's resources -- No horizontal scaling - -### Future Improvements - -🚧 **Planned:** -- Multi-node support with Docker Swarm -- Database replication -- Load balancing -- Shared storage for builds - -## File System Layout - -``` -/var/lib/mist/ -├── mist.db # SQLite database -├── repos/ # Cloned Git repositories -│ └── / -│ └── / -├── builds/ # Build artifacts -│ └── / -└── logs/ # Build logs - └── .log - -/etc/mist/ -├── config.yml # Mist configuration -└── traefik/ # Traefik configs - ├── traefik.yml - └── dynamic/ - -/usr/local/bin/ -└── mist # Mist binary -``` - -## Performance Characteristics - -### Resource Usage - -**Idle State:** -- CPU: < 1% -- RAM: ~50-100MB -- Disk: ~100MB - -**Under Load (10 concurrent deployments):** -- CPU: 10-30% -- RAM: ~200-500MB -- Disk: Depends on app sizes - -### Benchmarks - -- API Response Time: < 50ms (p95) -- WebSocket Latency: < 10ms -- Concurrent Users: 100+ -- Apps per Instance: 1000+ - -## Technology Choices - -### Why Go? - -- Fast compilation and execution -- Excellent concurrency (goroutines) -- Single binary deployment -- Strong standard library -- Great Docker SDK - -### Why SQLite? - -- Zero configuration -- Embedded database -- ACID compliance -- Perfect for single-server deployments -- Easy backups (copy file) - -### Why Traefik? - -- Dynamic configuration -- Docker integration -- Automatic service discovery -- Let's Encrypt support -- Modern and actively maintained - -## Learn More - -- [Deployment Process](./deployments) -- [Traefik Configuration](/deployment/traefik) diff --git a/www/docs/guide/audit-logs.md b/www/docs/guide/audit-logs.md deleted file mode 100644 index ba9590e..0000000 --- a/www/docs/guide/audit-logs.md +++ /dev/null @@ -1,74 +0,0 @@ -# Audit Logs - -Track all user actions in your Mist instance. - -## Overview - -Audit logs record: -- User authentication events -- Application deployments -- Configuration changes -- User management actions -- Project modifications - -## Viewing Audit Logs - -
-

🚧 Audit Log UI Coming Soon

-

Audit logs are currently stored in the database but UI for viewing is under development.

-
- -## What's Logged - -### Authentication -- User login attempts -- Session creation -- Logout events - -### Applications -- Application creation -- Application updates -- Application deletion -- Deployment triggers - -### Environment Variables -- Variable creation -- Variable updates -- Variable deletion - -### Projects -- Project creation -- Member addition/removal -- Project settings changes - -### Domains -- Domain addition -- Domain removal - -### Users (Admin) -- User creation -- Role changes -- User deletion - -## Log Structure - -Each audit log entry includes: -- **Timestamp** - When action occurred -- **User** - Who performed the action -- **Action** - What was done -- **Resource** - What was affected -- **Details** - Additional context -- **IP Address** - Source IP - -## Coming Soon - -
-

🚧 Audit Log Features

-
- -- **Audit Log Viewer UI** - Browse logs in dashboard -- **Search & Filter** - Find specific events -- **Export Logs** - Download as CSV/JSON -- **Retention Policies** - Configure log retention -- **Compliance Reports** - Generate audit reports -- **Real-time Alerts** - Notify on suspicious activity diff --git a/www/docs/guide/authentication.md b/www/docs/guide/authentication.md deleted file mode 100644 index d743b0f..0000000 --- a/www/docs/guide/authentication.md +++ /dev/null @@ -1,198 +0,0 @@ -# Authentication & Users - -Manage users and authentication in your Mist instance. - -## User Roles - -Mist implements a hierarchical role-based access control (RBAC) system with three distinct roles: - -### Owner - -The highest privilege level in Mist with complete system control. - -**Characteristics:** -- **Unique**: Only ONE owner can exist in the system -- **Created**: Automatically assigned during first-time setup -- **Cannot be duplicated**: No API or UI allows creating additional owners - -**Permissions:** -- ✅ Full system access to all features -- ✅ View and update system settings (wildcard domain, etc.) -- ✅ Create and delete projects -- ✅ Create users (admin and user roles only) -- ✅ Delete any user including admins -- ✅ View all audit logs -- ✅ Access all projects and applications -- ✅ Manage Traefik configuration - -**Special Considerations:** -- Owner can delete themselves, which resets the system to setup mode -- If the owner is deleted, a new owner can be created via signup - -### Admin - -High-level administrative privileges, second to owner. - -**Permissions:** -- ✅ Create and manage projects -- ✅ Create users (admin and user roles only) -- ✅ Delete users (except owners and other admins) -- ✅ View all audit logs -- ✅ Access assigned projects as member -- ✅ Manage applications in assigned projects -- ❌ Cannot view or update system settings -- ❌ Cannot delete owners -- ❌ Cannot delete other admins -- ❌ Cannot create owner accounts - -### User - -Standard user role with limited permissions. - -**Permissions:** -- ✅ Access assigned projects as member -- ✅ Deploy applications in assigned projects -- ✅ View logs and metrics for assigned projects -- ✅ Manage application settings in assigned projects -- ✅ View own profile -- ❌ Cannot create projects -- ❌ Cannot create users -- ❌ Cannot view system settings -- ❌ Cannot view audit logs - -## Authentication - -### Login - -Access Mist at `http://your-server-ip:8080`: - -1. Enter email and password -2. Click **"Sign In"** -3. JWT token stored in HTTP-only cookie - -### Session Management - -- Sessions expire after 31 days -- Tokens stored in secure HTTP-only cookies -- Role embedded in JWT for fast authorization -- Role verified against database on each request - -### First-Time Setup - -On first access, create the owner account: - -1. Navigate to `http://your-server-ip:8080` -2. You'll see the setup page -3. Enter email and password -4. Click **"Create Admin Account"** -5. You'll be automatically logged in as **owner** - -::: tip -The first user is always assigned the owner role. After the first user is created, the signup page is disabled. -::: - -## User Management - -### Creating Users - -**Who can create users**: Owner and Admin roles only - -1. Go to **Settings** → **Users** -2. Click **"Add User"** or **"New User"** -3. Enter user details: - - Username - - Email - - Password -4. Select role: - - **Admin**: Full administrative privileges (cannot create owners) - - **User**: Standard user access -5. Click **"Create"** - -::: warning Cannot Create Owners -The owner role cannot be assigned when creating users. Only one owner exists per Mist instance. -::: - -### Updating Users - -**Who can update users**: Owner and Admin - -Edit user details: -- Update username -- Update email -- Reset password -- Change role (owner/admin only) - -All user updates are logged in the audit log. - -### Deleting Users - -**Who can delete users**: -- **Owner**: Can delete any user including admins -- **Admin**: Can only delete users (not owners or other admins) -- **User**: Cannot delete any users - -To delete a user: -1. Navigate to **Settings** → **Users** -2. Click delete icon next to the user -3. Confirm deletion - -::: warning -- Admins cannot delete owners or other admins -- Deleting a user removes their access but preserves projects and applications they created -- If the owner deletes themselves, the system resets to setup mode -::: - -## Role Hierarchy - -The role hierarchy is strictly enforced: - -``` -Owner (highest) - ↓ -Admin - ↓ -User (lowest) -``` - -**Deletion Rules:** -- Owner can delete: Admin, User -- Admin can delete: User only -- User cannot delete anyone - -## Security Features - -### Password Security - -- Minimum 8 characters required -- Hashed with bcrypt algorithm -- Salted for additional security -- Never stored in plain text - -### JWT Tokens - -- Signed with HS256 algorithm -- Includes user ID, email, and role -- 31-day expiration -- Stored in HTTP-only cookies (prevents XSS attacks) - -### Session Security - -- HTTP-only cookies prevent JavaScript access -- Secure flag enabled in production (HTTPS) -- Role verified on every API request -- Invalid tokens result in automatic logout - -## Coming Soon - -
-

🚧 Authentication Enhancements

-
- -- **Password Reset** - Email-based password recovery -- **Email Verification** - Verify email addresses -- **Two-Factor Authentication** - TOTP/Authenticator app support -- **SSO Integration** - OAuth2, SAML support -- **API Tokens** - Generate tokens for CLI/API access -- **Session Management UI** - View and revoke active sessions -- **User Invitations** - Invite users via email -- **Fine-grained Permissions** - Custom permission sets diff --git a/www/docs/guide/cli.md b/www/docs/guide/cli.md deleted file mode 100644 index f83ce02..0000000 --- a/www/docs/guide/cli.md +++ /dev/null @@ -1,260 +0,0 @@ -# CLI Tool - -Command-line interface for managing Mist users and system settings. - -The Mist CLI (`mist-cli`) is a powerful command-line tool that allows administrators to manage their Mist installation directly from the terminal. It's automatically installed when you install Mist using the standard installation script. - -## Installation - -The CLI tool is automatically installed during Mist installation and is available at `/usr/local/bin/mist-cli`. - -To verify installation: - -```bash -mist-cli version -``` - -## Available Commands - -## Available Commands - -The CLI supports the following main commands: - -- `user` - Manage users -- `settings` - Manage system settings -- `version` - Show CLI version -- `help` - Show help message - -Use `mist-cli --help` for more information about each command. - ---- - -## User Management - -Manage user accounts including password changes and listing users. - -### Change User Password - -Change a user's password with optional interactive or non-interactive mode. - -**Interactive mode (recommended):** -```bash -mist-cli user change-password --username admin -``` - -This will prompt you to enter and confirm the new password securely. - -**Non-interactive mode:** -```bash -mist-cli user change-password --username admin --password newpass123 -``` - -**Examples:** -```bash -# Change admin password (interactive) -mist-cli user change-password --username admin - -# Change user password directly -mist-cli user change-password --username john --password SecurePass123 -``` - -### List Users - -Display all users in the system with their details. - -```bash -mist-cli user list -``` - -**Output:** -``` -Users: ----------------------------------------------- -ID Username Email Role ----------------------------------------------- -1 admin admin@example.com owner -2 developer dev@example.com member ----------------------------------------------- -Total: 2 users -``` - ---- - -## System Settings Management - -Manage system-wide settings that control Mist behavior. - -### List All Settings - -View all current system settings: - -```bash -mist-cli settings list -``` - -**Output:** -``` -System Settings: ----------------------------------------------- -Setting Value ----------------------------------------------- -wildcard_domain example.com -mist_app_name mist -production_mode true -secure_cookies true -auto_cleanup_containers false -auto_cleanup_images false ----------------------------------------------- -``` - -### Get Specific Setting - -Retrieve the value of a specific setting: - -```bash -mist-cli settings get --key wildcard_domain -``` - -**Available setting keys:** -- `wildcard_domain` - Wildcard domain for auto-generated app domains -- `mist_app_name` - Subdomain name for Mist dashboard -- `production_mode` - Enable production mode (true/false) -- `secure_cookies` - Enable secure cookies for HTTPS (true/false) -- `auto_cleanup_containers` - Auto cleanup stopped containers (true/false) -- `auto_cleanup_images` - Auto cleanup dangling images (true/false) - -### Set Setting Value - -Update a system setting: - -```bash -mist-cli settings set --key --value -``` - -**Examples:** - -```bash -# Set wildcard domain -mist-cli settings set --key wildcard_domain --value apps.example.com - -# Enable production mode -mist-cli settings set --key production_mode --value true - -# Enable secure cookies -mist-cli settings set --key secure_cookies --value true - -# Enable auto cleanup -mist-cli settings set --key auto_cleanup_containers --value true -mist-cli settings set --key auto_cleanup_images --value true - -# Change Mist dashboard subdomain -mist-cli settings set --key mist_app_name --value dashboard -``` - ---- - -## Common Use Cases - -### Initial Setup - -After installing Mist, set up your wildcard domain: - -```bash -# Configure wildcard domain -mist-cli settings set --key wildcard_domain --value apps.example.com - -# Set Mist dashboard subdomain -mist-cli settings set --key mist_app_name --value mist - -# Enable production mode -mist-cli settings set --key production_mode --value true -``` - -### Password Recovery - -If you've forgotten the admin password: - -```bash -# Reset admin password -sudo mist-cli user change-password --username admin -``` - -### Enable Auto Cleanup - -To automatically clean up Docker resources: - -```bash -# Enable container cleanup -mist-cli settings set --key auto_cleanup_containers --value true - -# Enable image cleanup -mist-cli settings set --key auto_cleanup_images --value true -``` - -### Check Current Configuration - -View all settings and users: - -```bash -# List all settings -mist-cli settings list - -# List all users -mist-cli user list -``` - ---- - -## Permissions - -The CLI requires direct access to the Mist database at `/var/lib/mist/mist.db`. You may need to run commands with `sudo`: - -```bash -sudo mist-cli user change-password --username admin -``` - ---- - -## Troubleshooting - -### Database Not Found - -**Error:** -``` -Error: database file not found at /var/lib/mist/mist.db. Please ensure Mist is installed and running -``` - -**Solution:** Ensure Mist is properly installed and the service is running: -```bash -sudo systemctl status mist -``` - -### Permission Denied - -**Error:** Permission issues when accessing the database - -**Solution:** Run the command with sudo: -```bash -sudo mist-cli settings list -``` - -### Unknown Setting Key - -**Error:** -``` -Error: Unknown setting key 'invalid_key' -``` - -**Solution:** Use `mist-cli settings --help` to see available setting keys. - ---- - -## Help & Support - -For more information on any command, use the `--help` flag: - -```bash -mist-cli --help -mist-cli user --help -mist-cli settings --help -``` diff --git a/www/docs/guide/databases.md b/www/docs/guide/databases.md deleted file mode 100644 index 71c11f2..0000000 --- a/www/docs/guide/databases.md +++ /dev/null @@ -1,232 +0,0 @@ -# Database Services - -One-click database provisioning for your applications. - -Mist provides managed database services through pre-configured Docker templates, allowing you to quickly deploy databases without manual configuration. - -## Available Databases - -### PostgreSQL -- Latest PostgreSQL version -- Pre-configured with recommended settings -- Automatic environment variable setup -- Internal network access via `app-{appId}` hostname -- Template-driven CPU and memory limits - -### MySQL -- Latest MySQL version -- Optimized configuration -- User and password management via environment variables -- Internal network connectivity - -### MariaDB -- MySQL-compatible database -- Drop-in replacement for MySQL -- Same connection interface - -### Redis -- In-memory data store and cache -- Latest Redis version -- Password protection via environment variables -- Pub/sub and caching support - -### MongoDB -- Document-oriented database -- Latest MongoDB version -- User authentication support -- Internal DNS resolution - -## Creating a Database - -1. Navigate to your project -2. Click **"New Application"** -3. Select **"Database"** type -4. Choose your database template: - - PostgreSQL - - MySQL - - MariaDB - - Redis - - MongoDB -5. Enter instance name and description -6. Configure environment variables (passwords, database names) -7. Click **"Create"** - -The database will be provisioned automatically with: -- Pre-configured port from template -- Recommended CPU and memory limits -- Default environment variables -- Connected to `traefik-net` network - -## Connecting to Databases - -### From Your Applications - -Databases are accessible via internal hostname using the format `app-{appId}`: - -**Example Connection Strings:** - -```bash -# PostgreSQL -DATABASE_URL=postgresql://user:password@app-123:5432/myapp - -# MySQL -DATABASE_URL=mysql://root:password@app-124:3306/myapp - -# Redis -REDIS_URL=redis://:password@app-125:6379 - -# MongoDB -MONGODB_URI=mongodb://admin:password@app-126:27017/myapp -``` - -Replace `app-{appId}` with your actual database application ID shown in the dashboard. - -### Configuration via Environment Variables - -Add connection details to your web/service application's environment variables: - -1. Go to your application's **Environment** tab -2. Add database connection variables -3. Use the database's container name (`app-{appId}`) -4. Deploy your application - -## Database Management - -### Environment Variables - -Configure your database through environment variables: - -**PostgreSQL:** -``` -POSTGRES_USER=myuser -POSTGRES_PASSWORD=mypassword -POSTGRES_DB=mydatabase -``` - -**MySQL:** -``` -MYSQL_ROOT_PASSWORD=rootpassword -MYSQL_DATABASE=mydatabase -MYSQL_USER=myuser -MYSQL_PASSWORD=mypassword -``` - -**MongoDB:** -``` -MONGO_INITDB_ROOT_USERNAME=admin -MONGO_INITDB_ROOT_PASSWORD=password -``` - -**Redis:** -``` -REDIS_PASSWORD=yourpassword -``` - -### Resource Limits - -Template-based defaults are applied: -- **CPU**: 1.0 cores (adjustable) -- **Memory**: 512MB (adjustable) -- **Restart Policy**: unless-stopped - -You can modify these in the application settings. - -### Container Management - -- **Start/Stop**: Control database lifecycle from dashboard -- **Restart**: Restart database containers -- **Logs**: View database logs in real-time -- **Status**: Monitor container state and uptime - -## Networking - -All databases are connected to the `traefik-net` network: -- Internal DNS resolution -- Accessible by container name (`app-{appId}`) -- Isolated from public internet -- Secure inter-container communication - -## Best Practices - -### Security - -- **Strong Passwords**: Use complex passwords for database users -- **Environment Variables**: Never hardcode credentials -- **Least Privilege**: Create database users with minimal required permissions -- **Network Isolation**: Databases are not exposed externally by default - -### Performance - -- **Resource Allocation**: Monitor usage and adjust CPU/memory limits -- **Connection Pooling**: Use connection pools in your applications -- **Indexing**: Create appropriate indexes for query performance -- **Monitoring**: Check logs regularly for errors or warnings - -### Backup - -::: warning Manual Backups Required -Automatic backup scheduling is not yet available. Manually backup your databases regularly using database-specific tools. -::: - -**Backup Methods:** - -**PostgreSQL:** -```bash -docker exec app-{appId} pg_dump -U user dbname > backup.sql -``` - -**MySQL:** -```bash -docker exec app-{appId} mysqldump -u root -p dbname > backup.sql -``` - -**MongoDB:** -```bash -docker exec app-{appId} mongodump --out /backup -``` - -## Coming Soon - -
-

🚧 Upcoming Features

-
- -- **External Access** - Public database access with security controls -- **Management UIs** - Built-in admin interfaces (pgAdmin, phpMyAdmin, Mongo Express, Redis Commander) -- **Automated Backups** - Scheduled backup and restore -- **Connection Pooling** - Optimize database connections -- **Replication** - High availability setups -- **Multiple Versions** - Choose specific database versions -- **Volume Management** - Persistent volume configuration -- **Database Cloning** - Duplicate databases for staging/testing -- **Performance Metrics** - Database-specific monitoring -- **Auto-Configuration** - Automatic environment variable injection to apps - -## Troubleshooting - -### Connection Refused - -- Verify database container is running -- Check container logs for errors -- Ensure correct hostname format (`app-{appId}`) -- Verify port matches database default - -### Authentication Failed - -- Check environment variables are set correctly -- Verify username and password -- Ensure database user has been created -- Check database logs for auth errors - -### Application Can't Connect - -- Verify both containers are on `traefik-net` network -- Check firewall rules aren't blocking internal traffic -- Test connection from application container logs -- Verify DNS resolution of container name - -## Related Documentation - -- [Applications](./applications) - Application types and management -- [Environment Variables](./environment-variables) - Managing configuration -- [Service Templates](./applications#service-templates) - How templates work diff --git a/www/docs/guide/deployments.md b/www/docs/guide/deployments.md deleted file mode 100644 index 63af2b3..0000000 --- a/www/docs/guide/deployments.md +++ /dev/null @@ -1,118 +0,0 @@ -# Deployments - -Learn how to deploy and manage your applications. - -## Deployment Process - -When you trigger a deployment, Mist: - -1. **Clones** your Git repository (for web/service apps) -2. **Builds** Docker image using your Dockerfile with environment variables as build args -3. **Stops** the previous container (if exists) -4. **Starts** new container with updated code -5. **Updates** Traefik routing configuration (for web apps with domains) -6. **Streams** real-time logs to your dashboard - -For database applications, Mist pulls the Docker image instead of building from source. - -## Triggering Deployments - -### Manual Deployment - -Click the **"Deploy"** button in your application dashboard. - -### Automatic Deployment - -Configure GitHub webhooks to auto-deploy on push: - -1. Install Mist GitHub App on your repository -2. Enable webhooks in application settings -3. Push to your configured branch -4. Deployment triggers automatically - -[Learn more about Git integration →](./git-integration) - -## Deployment Status - -Monitor deployment progress in real-time: - -- 🟡 **Queued** - Waiting in deployment queue -- 🔵 **Building** - Docker image being built -- 🟢 **Running** - Container started successfully -- 🔴 **Failed** - Deployment encountered an error - -## Build Logs - -View detailed build logs during and after deployment: - -1. Go to **Deployments** tab -2. Click on a deployment -3. View streaming logs in real-time - -Logs include: -- Git clone output -- Build command execution -- Docker image creation -- Container startup - -## Deployment History - -Track all deployments with full history: - -- Deployment timestamp -- Commit hash and message -- Build duration -- Status (success/failed) -- Triggering user or webhook - -## Coming Soon - -
-

🚧 Deployment Features

-
- -- **Rollback** - Revert to previous deployments with one click -- **Blue-Green Deployments** - Zero-downtime deployments -- **Canary Releases** - Gradual traffic shifting -- **Deployment Hooks** - Pre/post deploy scripts -- **Manual Approval** - Require approval before production deploys -- **Scheduled Deployments** - Deploy at specific times -- **Preview Environments** - Auto-deploy pull requests - -## Troubleshooting - -### Build Failures - -Common causes: -- Missing dependencies -- Incorrect build command -- Dockerfile errors -- Insufficient permissions - -**Solution**: Check build logs for specific error messages - -### Container Won't Start - -Possible issues: -- Wrong start command -- Port configuration mismatch -- Missing environment variables -- Application crashes on startup - -**Solution**: Review container logs for errors - -### Slow Builds - -Optimization tips: -- Use `.dockerignore` to exclude unnecessary files -- Implement Docker layer caching -- Use multi-stage builds -- Optimize dependency installation - -## Best Practices - -- ✅ Test locally before deploying -- ✅ Use feature branches for experimentation -- ✅ Monitor logs during deployment -- ✅ Set up health checks -- ✅ Keep deployment logs for debugging diff --git a/www/docs/guide/domains.md b/www/docs/guide/domains.md deleted file mode 100644 index 458e2e6..0000000 --- a/www/docs/guide/domains.md +++ /dev/null @@ -1,188 +0,0 @@ -# Domains & SSL - -Configure custom domains for your applications. - -## Wildcard Domain Configuration - -Mist supports automatic domain generation for web applications using a wildcard domain configuration. - -### What is Wildcard Domain? - -When configured, Mist automatically generates a subdomain for every new web application using the pattern: -``` -{project-name}-{app-name}.{wildcard-domain} -``` - -### Setting Up Wildcard Domain - -1. Go to **Settings** (requires owner role) -2. Enter your wildcard domain (e.g., `example.com` or `*.example.com`) -3. Optionally configure the Mist dashboard subdomain name (default: `mist`) -4. Click **"Save"** - -### DNS Configuration for Wildcard - -To use wildcard domains, configure a wildcard DNS record: - -``` -Type: A -Name: * -Value: YOUR_SERVER_IP -TTL: 3600 -``` - -Or for subdomains: -``` -Type: A -Name: *.apps -Value: YOUR_SERVER_IP -TTL: 3600 -``` - -This allows any subdomain (e.g., `production-api.example.com`, `staging-web.example.com`) to route to your server. - -### Benefits - -- **Automatic domains**: No manual domain configuration needed for new apps -- **Consistent naming**: Standardized domain format across all applications -- **SSL automation**: Auto-generated domains get automatic Let's Encrypt certificates -- **Easy management**: Change the base domain in one place - -### Examples - -**Wildcard domain**: `example.com` -- Project: `production`, App: `api` → `production-api.example.com` -- Project: `staging`, App: `frontend` → `staging-frontend.example.com` - -**Wildcard domain**: `apps.mysite.com` -- Project: `personal`, App: `blog` → `personal-blog.apps.mysite.com` - -::: tip -Auto-generated domains only apply to web applications. Service and database applications do not receive automatic domains. -::: - -## Adding a Domain - -1. Navigate to your application -2. Go to **Domains** tab -3. Click **"Add Domain"** -4. Enter your domain name (e.g., `app.example.com`) -5. Click **"Add"** - -## DNS Configuration - -Point your domain to your Mist server: - -### A Record - -``` -Type: A -Name: app (or @ for root) -Value: YOUR_SERVER_IP -TTL: 3600 -``` - -### CNAME Record (Subdomain) - -``` -Type: CNAME -Name: app -Value: yourdomain.com -TTL: 3600 -``` - -## Verify DNS Propagation - -Check if DNS has propagated: - -```bash -dig app.example.com -nslookup app.example.com -``` - -Propagation can take 5 minutes to 48 hours. - -## SSL/TLS Certificates - -Mist automatically provisions and manages SSL/TLS certificates for your domains using Let's Encrypt and Traefik. - -### Automatic SSL - -When you add a domain to your application: - -1. **Domain Verification**: Mist verifies your DNS is correctly configured -2. **Certificate Request**: Traefik automatically requests a certificate from Let's Encrypt -3. **HTTPS Enabled**: Your domain is secured with SSL/TLS -4. **HTTP Redirect**: HTTP traffic is automatically redirected to HTTPS - -### Auto-Renewal - -Certificates are automatically renewed by Traefik: -- Renewal happens before expiration (typically 30 days before) -- Zero downtime during renewal -- No manual intervention required - -### Requirements - -For automatic SSL to work: -- Domain DNS must point to your Mist server (A record) -- Ports 80 and 443 must be open and accessible -- Domain must be publicly accessible for Let's Encrypt verification - -::: tip Certificate Status -You can view the SSL status of your domains in the Domains tab of your application. Status will show as "pending" during issuance and "active" once the certificate is installed. -::: - -### Coming Soon - -- **Wildcard Support** - `*.example.com` certificates -- **Custom Certificates** - Upload your own SSL certs - -## Multiple Domains - -Add multiple domains to the same application: - -``` -app.example.com -www.app.example.com -app.yourdomain.com -``` - -All domains will route to the same container. - -## Domain Management - -### Edit Domain - -Update domain configuration in the domains list. - -### Delete Domain - -Remove a domain from your application: - -1. Click the delete icon next to the domain -2. Confirm deletion - -## Troubleshooting - -### Domain Not Resolving - -- Verify DNS records are correct -- Wait for DNS propagation (can take up to 48 hours) -- Check DNS with: `dig app.example.com` or `nslookup app.example.com` -- Clear local DNS cache: `sudo systemd-resolve --flush-caches` - -### SSL Certificate Not Issuing - -- Verify domain DNS points to your server IP -- Ensure ports 80 and 443 are open in your firewall -- Check that domain is publicly accessible -- View Traefik logs for errors: `docker logs traefik` -- Let's Encrypt has rate limits - wait if you've made many requests - -### HTTPS Not Working - -- Wait a few minutes for certificate issuance -- Check SSL status in Domains tab (should show "active") -- Verify Traefik container is running: `docker ps | grep traefik` -- Check Traefik configuration in `/etc/traefik/` diff --git a/www/docs/guide/environment-variables.md b/www/docs/guide/environment-variables.md deleted file mode 100644 index e032037..0000000 --- a/www/docs/guide/environment-variables.md +++ /dev/null @@ -1,279 +0,0 @@ -# Environment Variables - -Environment variables allow you to configure your applications without hardcoding values in your code. - -## Overview - -Environment variables in Mist are: - -- **Secure**: Values are masked in the UI -- **Flexible**: Support both build-time and runtime variables -- **Easy to Manage**: Single and bulk import modes - -## Adding Environment Variables - -### Single Variable - -1. Navigate to your application -2. Go to **Environment** tab -3. Click **"Add Variable"** -4. Select **"Single"** mode -5. Enter **Key** and **Value** -6. Click **"Add"** - -### Bulk Import New - -Add multiple variables at once by pasting them in `KEY=VALUE` format: - -1. Click **"Add Variable"** -2. Select **"Bulk Paste"** mode -3. Paste your environment variables: - -```bash -NODE_ENV=production -DATABASE_URL=postgresql://user:pass@host:5432/db -API_KEY=your-api-key-here -REDIS_URL=redis://localhost:6379 -PORT=3000 -``` - -4. Preview detected variables -5. Click **"Add X Variables"** - -#### Supported Formats - -The parser supports: - -```bash -# Standard format -KEY=value - -# With quotes -KEY="value with spaces" -KEY='single quoted value' - -# Comments (ignored) -# This is a comment -DATABASE_URL=postgres://localhost - -# Empty lines (ignored) - -NEXT_PUBLIC_API=https://api.example.com -``` - -## Variable Types - -### Runtime Variables - -Available when your container runs: - -```javascript -// Node.js -const apiKey = process.env.API_KEY; - -// Python -import os -api_key = os.environ.get('API_KEY') -``` - -### Build-time Variables - -Available during the build process: - -```dockerfile -ARG NODE_ENV -RUN npm run build -``` - -## Editing Variables - -1. Find the variable in the list -2. Click the **Edit** icon (pencil) -3. Modify key or value -4. Click **"Save"** - -## Deleting Variables - -1. Find the variable in the list -2. Click the **Delete** icon (trash) -3. Confirm deletion - -::: warning -Changes to environment variables require redeployment to take effect. -::: - -## Common Use Cases - -### Database Connection - -```bash -DATABASE_URL=postgresql://user:password@host:5432/dbname -DB_HOST=localhost -DB_PORT=5432 -DB_NAME=myapp -DB_USER=appuser -DB_PASSWORD=secret123 -``` - -### API Keys - -```bash -STRIPE_API_KEY=sk_test_xxxxx -SENDGRID_API_KEY=SG.xxxxx -AWS_ACCESS_KEY_ID=AKIAXXXXX -AWS_SECRET_ACCESS_KEY=xxxxx -``` - -### Application Configuration - -```bash -NODE_ENV=production -PORT=3000 -LOG_LEVEL=info -SESSION_SECRET=random-secret-string -``` - -### Feature Flags - -```bash -ENABLE_ANALYTICS=true -ENABLE_BETA_FEATURES=false -MAINTENANCE_MODE=false -``` - -## Best Practices - -### Security - -- ✅ Never commit secrets to Git -- ✅ Use environment variables for all sensitive data -- ✅ Rotate API keys regularly -- ✅ Use different values for staging and production - -### Naming Conventions - -```bash -# Use UPPER_SNAKE_CASE -DATABASE_URL=... - -# Prefix public variables (Next.js, Vite, etc.) -NEXT_PUBLIC_API_URL=... -VITE_API_URL=... - -# Group related variables -AWS_REGION=us-east-1 -AWS_BUCKET=my-bucket -AWS_ACCESS_KEY_ID=... -``` - -### Organization - -```bash -# Database -DATABASE_URL=... -DATABASE_POOL_SIZE=10 - -# Redis -REDIS_URL=... -REDIS_TTL=3600 - -# External Services -STRIPE_KEY=... -SENDGRID_KEY=... - -# App Config -NODE_ENV=production -PORT=3000 -``` - -## Coming Soon - -
-

🚧 Planned Features

-

Future enhancements for environment variables:

-
- -- **Encryption at Rest** - Encrypted storage for sensitive values -- **Variable Groups** - Organize variables into reusable groups -- **Templates** - Pre-defined variable sets for common stacks -- **Version History** - Track changes to variables over time -- **Import/Export** - Download as `.env` file -- **Secrets Management** - Integration with Vault or similar -- **Variable Validation** - Ensure required variables are set - -## Troubleshooting - -### Variables Not Available - -**Problem**: Environment variables not accessible in application - -**Solutions**: -- Redeploy the application after adding variables -- Check variable names (case-sensitive) -- Verify the variable is not overridden elsewhere - -### Build Failing - -**Problem**: Build fails after adding variables - -**Solutions**: -- Check for special characters in values -- Use quotes for values with spaces -- Ensure variables don't conflict with system variables - -### Parsing Errors (Bulk Import) - -**Problem**: Some variables not detected - -**Solutions**: -- Ensure format is `KEY=VALUE` -- Keys must start with letter or underscore -- Keys can only contain letters, numbers, and underscores -- Remove invalid characters - -## Examples by Framework - -### Node.js / Express - -```bash -NODE_ENV=production -PORT=3000 -DATABASE_URL=postgresql://localhost/myapp -SESSION_SECRET=your-secret-key -``` - -### Next.js - -```bash -# Server-side -DATABASE_URL=postgresql://localhost/myapp - -# Client-side (must be prefixed) -NEXT_PUBLIC_API_URL=https://api.example.com -``` - -### Python / Django - -```bash -DJANGO_SECRET_KEY=your-secret-key -DEBUG=False -DATABASE_URL=postgresql://localhost/myapp -ALLOWED_HOSTS=example.com,www.example.com -``` - -### Go - -```bash -PORT=8080 -DATABASE_URL=postgresql://localhost/myapp -JWT_SECRET=your-jwt-secret -``` - -## API Reference - -Manage environment variables programmatically via the API: - -- [Create Variable](/api/environment-variables#create) -- [List Variables](/api/environment-variables#list) -- [Update Variable](/api/environment-variables#update) -- [Delete Variable](/api/environment-variables#delete) diff --git a/www/docs/guide/getting-started.md b/www/docs/guide/getting-started.md deleted file mode 100644 index 68f234a..0000000 --- a/www/docs/guide/getting-started.md +++ /dev/null @@ -1,162 +0,0 @@ -# Getting Started - -This guide will walk you through installing Mist and deploying your first application. - -## Prerequisites - -Before installing Mist, ensure you have: - -- A Linux server (Ubuntu 20.04+ or Debian 11+ recommended) -- Docker installed and running -- At least 256MB RAM and 2GB disk space -- Root or sudo access -- A domain name (optional, but recommended for production) - -## Installation - -### Quick Install - -The easiest way to install Mist is using the installation script: - -```bash -curl -sSL https://trymist.cloud/install.sh | bash -``` - -This script will: -1. Download the latest Mist binary -2. Set up Traefik reverse proxy -3. Create systemd service for auto-start -4. Configure firewall rules (ports 80, 443, 8080) - -[Learn more about installation →](/deployment/installation) - -## First-Time Setup - -After installation, Mist will be available at `http://your-server-ip:8080`. - -### 1. Create Admin Account - -On first visit, you'll see the setup page: - -1. Enter admin email and password -2. Click "Create Admin Account" -3. You'll be automatically logged in - -### 2. Configure Wildcard Domain (Optional) - -For automatic domain generation: - -1. Go to **Settings** → **System** -2. Enter your wildcard domain (e.g., `example.com`) -3. Configure DNS with a wildcard A record pointing `*.example.com` to your server -4. New web applications will automatically get domains like `{project}-{app}.example.com` - -[Learn more about wildcard domains →](./domains#wildcard-domain-configuration) - -### 3. Configure GitHub Integration (Optional) - -To enable Git deployments: - -1. Go to **Settings** → **Git** -2. Follow the instructions to create a GitHub App -3. Install the app on your repositories - -[Learn more about GitHub setup →](/deployment/github-app) - -## Deploy Your First Application - -### Step 1: Create a Project - -Projects organize your applications: - -1. Click **"New Project"** in the dashboard -2. Enter a name (e.g., "My Portfolio") -3. Add tags (optional) -4. Click **"Create Project"** - -### Step 2: Create an Application - -1. Open your project -2. Click **"New Application"** -3. Fill in the basic details: - - **Name**: Your app name - - **Description**: Brief description of your app - - **Port**: Application port (e.g., 3000) -4. Click **"Create Application"** -5. Configure additional settings inside the app: - - **Git Repository**: Select from connected repos - - **Branch**: Choose branch to deploy - - **Dockerfile Path**: Path to your Dockerfile (e.g., `./Dockerfile`) - -### Step 3: Add Environment Variables - -1. Go to the **Environment** tab -2. Click **"Add Variable"** or use **"Bulk Paste"** -3. Add your environment variables: - ``` - NODE_ENV=production - DATABASE_URL=your-database-url - API_KEY=your-api-key - ``` - -### Step 4: Deploy - -1. Click **"Deploy"** button -2. Watch real-time build logs -3. Wait for deployment to complete -4. Access your app via the generated URL - -## Configure Custom Domain - -### Step 1: Add Domain - -1. Go to **Domains** tab in your application -2. Click **"Add Domain"** -3. Enter your domain (e.g., `app.example.com`) - -### Step 2: Configure DNS - -Point your domain to your server: - -``` -Type: A Record -Name: app (or @ for root domain) -Value: YOUR_SERVER_IP -TTL: 3600 (or auto) -``` - -### Step 3: Wait for DNS Propagation - -DNS changes can take 5 minutes to 48 hours to propagate. Check with: - -```bash -dig app.example.com -``` - -::: tip SSL/TLS Certificate -SSL certificates are automatically provisioned using Traefik and Let's Encrypt. Once your DNS is configured and propagated, your application will automatically get an SSL certificate. -::: - -## Next Steps - -Now that you have Mist running, explore these topics: - -- [**Projects**](./projects) - Organize applications -- [**Deployments**](./deployments) - Deployment strategies -- [**Environment Variables**](./environment-variables) - Managing configuration -- [**Monitoring**](./logs) - Container and system logs -- [**Metrics**](./metrics) - System resource monitoring -- [**Git Integration**](./git-integration) - Auto-deploy on push - -## Getting Help - -- [GitHub Issues](https://github.com/corecollectives/mist/issues) - Report bugs -- [GitHub Discussions](https://github.com/corecollectives/mist/discussions) - Ask questions -- [Documentation](/) - Read more guides - -## What's Next? - -### Coming Soon Features - -- 🚧 Email notifications for deployments -- 🚧 CLI tool for terminal deployments diff --git a/www/docs/guide/git-integration.md b/www/docs/guide/git-integration.md deleted file mode 100644 index 98b64f2..0000000 --- a/www/docs/guide/git-integration.md +++ /dev/null @@ -1,98 +0,0 @@ -# Git Integration - -Connect Mist with GitHub to enable automatic deployments. - -## GitHub App Setup - -### 1. Create GitHub App - -Follow the [GitHub App Setup Guide](/deployment/github-app) to create and install the Mist GitHub App. - -### 2. Install on Repositories - -Install the GitHub App on repositories you want to deploy: - -1. Visit GitHub App settings -2. Click **"Install App"** -3. Select repositories -4. Grant required permissions - -### 3. Connect in Mist - -Once installed, repositories appear in the application creation form. - -## Auto-Deploy on Push - -Enable automatic deployments: - -1. Create application with GitHub repository -2. Select branch to deploy -3. Push code to that branch -4. Mist receives webhook and deploys automatically - -## Webhook Events - -Mist listens for these GitHub events: - -- **Push** - Code pushed to branch -- **Pull Request** (coming soon) - PR opened/updated - -## Commit Tracking - -Each deployment tracks: - -- Commit hash (SHA) -- Commit message -- Author information -- Timestamp - -View commit details in the deployments list. - -## Branch Management - -### Selecting Branch - -Choose which branch to deploy: - -- `main` - Production deployments -- `develop` - Staging environment -- `feature/*` - Feature testing - -### Changing Branch - -Update the deployment branch: - -1. Go to application settings -2. Change **Branch** field -3. Save changes -4. Next deployment uses new branch - -## Coming Soon - -
-

🚧 Git Features in Development

-
- -- **GitLab Integration** - Connect GitLab repositories -- **Bitbucket Support** - Bitbucket repositories -- **Pull Request Previews** - Deploy PRs to preview URLs -- **Commit Status Updates** - Update GitHub with deployment status -- **Multiple Git Providers** - Use multiple providers per app -- **Self-hosted Git** - Gitea, Gogs support - -## Troubleshooting - -### Webhook Not Triggering - -Check: -- GitHub App is installed on repository -- Webhook URL is correct -- Repository permissions are granted -- Firewall allows GitHub webhook IPs - -### Wrong Branch Deploying - -Verify: -- Branch name matches exactly (case-sensitive) -- Application settings have correct branch -- Webhook is configured for correct events diff --git a/www/docs/guide/logs.md b/www/docs/guide/logs.md deleted file mode 100644 index 8be67ac..0000000 --- a/www/docs/guide/logs.md +++ /dev/null @@ -1,132 +0,0 @@ -# Logs & Monitoring - -Monitor your applications and system with real-time logs and system metrics. - -## System Logs - -View live logs from the Mist backend server: - -1. Navigate to **Logs** page in the main navigation -2. System logs stream in real-time -3. Powered by `journalctl` under the hood - -### What's Included - -System logs show all output from the Mist Go backend, including: -- Application lifecycle events (startup, shutdown) -- Deployment processing -- API requests and responses -- Container operations -- Error messages and stack traces -- System events and notifications - -### Features - -- **Real-time streaming** - Logs appear instantly via WebSocket -- **Live updates** - See backend activity as it happens -- **journalctl integration** - Leverages systemd journal for reliable log collection -- **Filterable** - Search and filter system logs -- **Persistent** - Logs stored in systemd journal - -::: tip -System logs are useful for debugging deployment issues, monitoring backend activity, and troubleshooting system-level problems. -::: - -## Container Logs - -View live logs from your running containers: - -1. Navigate to your application -2. Go to **Logs** tab -3. Logs stream in real-time via WebSocket - -### Features - -- **Real-time streaming** - Logs appear instantly -- **Auto-scroll** - Automatically scrolls to latest logs -- **Color coding** - Error, warning, and info levels highlighted -- **Search** - Filter logs by keyword (coming soon) -- **Download** - Export logs to file (coming soon) - -## Build Logs - -View logs from deployment builds: - -1. Go to **Deployments** tab -2. Click on a deployment -3. View complete build output - -Build logs show: -- Git clone process -- Dependency installation -- Build command execution -- Docker image creation -- Container startup - -## System Metrics - -Monitor server resource usage: - -### CPU Usage - -Real-time CPU utilization displayed as percentage. - -### Memory Usage - -Current RAM usage and available memory. - -### Disk Usage - -Storage space used and available on server. - -### Update Frequency - -Metrics update every second via WebSocket. - -## Coming Soon - -
-

🚧 Advanced Monitoring Features

-
- -- **Log Aggregation** - Search across all logs -- **Log Retention** - Configure how long to keep logs -- **Log Export** - Download logs as files -- **Application Metrics** - Request count, response times -- **Alerting** - Email/Slack notifications for errors -- **Custom Dashboards** - Create metric visualizations -- **Error Tracking** - Sentry-like error monitoring -- **Uptime Monitoring** - Track application availability - -## Log Best Practices - -### Structured Logging - -Use JSON format for easier parsing: - -```javascript -console.log(JSON.stringify({ - level: 'info', - message: 'User logged in', - userId: 123, - timestamp: new Date().toISOString() -})); -``` - -### Log Levels - -Use appropriate log levels: - -```javascript -console.log('Info message'); // Info -console.warn('Warning message'); // Warning -console.error('Error message'); // Error -``` - -### Avoid Sensitive Data - -Never log: -- Passwords -- API keys -- Credit card numbers -- Personal identification diff --git a/www/docs/guide/metrics.md b/www/docs/guide/metrics.md deleted file mode 100644 index 848247d..0000000 --- a/www/docs/guide/metrics.md +++ /dev/null @@ -1,52 +0,0 @@ -# System Metrics - -Monitor your Mist server's resource usage in real-time. - -::: tip Application-Level Metrics -Currently, Mist displays system-level metrics only. Per-application metrics (CPU, memory, network usage per container) are coming soon. -::: - -## Available Metrics - -### CPU Usage - -- **Current Usage**: Real-time CPU percentage -- **Per-Core**: Individual core utilization -- **Load Average**: System load over time - -### Memory (RAM) - -- **Used**: Currently allocated memory -- **Free**: Available memory -- **Total**: Total system memory -- **Percentage**: Memory usage percentage - -### Disk Space - -- **Used**: Storage space consumed -- **Free**: Available storage -- **Total**: Total disk capacity -- **Percentage**: Disk usage percentage - -## Viewing Metrics - -Access system metrics from: - -1. **Dashboard** - Overview of all metrics -2. **System Status** page - Detailed metrics view - -Metrics update every second via WebSocket connection. - -## Coming Soon - -
-

🚧 Enhanced Metrics

-
- -- **Historical Data** - View metrics over time -- **Metric Charts** - Visualize trends with graphs -- **Per-Application Metrics** - Resource usage by app -- **Network Metrics** - Bandwidth monitoring -- **Disk I/O** - Read/write operations -- **Alerts** - Notifications when thresholds exceeded -- **Metric Export** - Prometheus integration diff --git a/www/docs/guide/notifications.md b/www/docs/guide/notifications.md deleted file mode 100644 index e9cf0d2..0000000 --- a/www/docs/guide/notifications.md +++ /dev/null @@ -1,84 +0,0 @@ -# Notifications Coming Soon - -Get notified about deployments, errors, and system events. - -
-

🚧 Feature in Development

-

Notification system is planned for Phase 1 (Medium Priority).

-
- -## Planned Channels - -### Email Notifications - -- SMTP configuration -- Customizable templates -- Per-user preferences -- Batch digest mode - -### Slack Integration - -- Webhook integration -- Channel selection -- Custom message formatting -- Thread support - -### Discord Webhooks - -- Rich embed messages -- Role mentions -- Server/channel selection - -### Custom Webhooks - -- HTTP POST to any URL -- JSON payload -- Custom headers -- Retry logic - -## Event Types - -### Deployment Events - -- ✅ Deployment started -- ✅ Deployment succeeded -- ❌ Deployment failed -- 🔄 Rollback performed - -### System Events - -- ⚠️ High CPU usage -- ⚠️ Low disk space -- ⚠️ Memory threshold exceeded -- 🔒 SSL certificate expiring soon - -### Application Events - -- 🐛 Application crashed -- 🔄 Application restarted -- ⏱️ Slow response times -- ❌ Health check failed - -## Notification Preferences - -```yaml -# Per-user configuration -notifications: - email: - enabled: true - events: - - deployment.success - - deployment.failed - slack: - enabled: true - webhook: https://hooks.slack.com/... - events: - - deployment.failed - - system.alert -``` - -## Expected Release - -Target: Q2 2025 - -[View implementation plan →](https://github.com/corecollectives/mist/blob/main/roadmap.md#notification-system) diff --git a/www/docs/guide/projects.md b/www/docs/guide/projects.md deleted file mode 100644 index 1cfc7ce..0000000 --- a/www/docs/guide/projects.md +++ /dev/null @@ -1,82 +0,0 @@ -# Projects - -Projects help you organize and group related applications together. - -## Overview - -A project is a container for multiple applications, allowing you to: - -- Group related applications (e.g., frontend, backend, worker) -- Manage team access and permissions -- Organize by client, environment, or purpose -- Track resources and deployments collectively - -## Creating a Project - -1. Click **"New Project"** from the dashboard -2. Enter project details: - - **Name**: Descriptive project name - - **Description**: Optional description - - **Tags**: Comma-separated tags for filtering - -3. Click **"Create"** - -## Project Members - -Add team members to collaborate on projects: - -1. Open your project -2. Go to **Members** tab -3. Click **"Add Member"** -4. Select user(s) from list -5. Click **"Save"** - -### Member Access - -All project members have equal access to: -- View all applications in the project -- Deploy applications -- View logs and metrics -- Manage application settings - -::: tip Coming Soon -Role-based permissions (Owner, Admin, Member) and ownership transfer are upcoming features. Currently, all members have full access to the project. -::: - -## Managing Applications - -All applications within a project are listed on the project page: - -- Create new applications -- View deployment status -- Access application details -- Monitor resource usage - -## Project Settings - -### General - -- Update project name and description -- Modify tags for organization -- View project statistics -- Manage project members - -### Danger Zone - -- Delete project (requires confirmation) - - Only the project owner can delete the project - - Deletes all applications and their data - -## Coming Soon - -
-

🚧 Upcoming Features

-
- -- **Role-Based Permissions** - Owner, Admin, and Member roles with different access levels -- **Project Ownership Transfer** - Transfer project ownership to another user -- **Project-level Environment Variables** - Share variables across apps -- **Project Templates** - Quick-start templates for common stacks -- **Resource Quotas** - Limit CPU, memory, and storage per project -- **Billing Integration** - Track costs per project -- **Project Archives** - Temporarily disable all apps diff --git a/www/docs/guide/rollback.md b/www/docs/guide/rollback.md deleted file mode 100644 index 79f59d8..0000000 --- a/www/docs/guide/rollback.md +++ /dev/null @@ -1,60 +0,0 @@ -# Deployment Rollback Coming Soon - -Instantly revert to previous deployments. - -
-

🚧 Feature in Development

-

Deployment rollback is a high-priority feature for Phase 1.

-
- -## Planned Features - -### One-Click Rollback - -- Revert to any previous deployment -- Instant container swap -- Zero configuration required -- Preserves deployment history - -### Deployment History - -- Keep last N deployments (configurable) -- View complete deployment timeline -- Compare deployments side-by-side -- Track rollback events - -### Image Management - -- Automatic image retention -- Configurable cleanup policy -- Disk space management -- Image size tracking - -## How It Will Work - -```bash -# In the dashboard: -1. Go to Deployments tab -2. Find previous successful deployment -3. Click "Rollback to this deployment" -4. Confirm rollback -5. Container switches to previous image instantly -``` - -## Rollback Strategies - -### Quick Rollback -- Keep previous container image -- Instant switch (< 10 seconds) -- Previous environment variables restored - -### Full Rollback -- Roll back code, config, and environment -- Complete state restoration -- Useful for major issues - -## Expected Release - -Target: Q1 2025 - Quick Win Priority - -[View implementation plan →](https://github.com/corecollectives/mist/blob/main/roadmap.md#deployment-rollback) diff --git a/www/docs/guide/ssl-automation.md b/www/docs/guide/ssl-automation.md deleted file mode 100644 index 60b10fa..0000000 --- a/www/docs/guide/ssl-automation.md +++ /dev/null @@ -1,191 +0,0 @@ -# SSL Automation - -Automatic SSL/TLS certificate provisioning with Let's Encrypt. - -Mist provides automatic SSL certificate management through Traefik and Let's Encrypt integration, ensuring your applications are always secured with HTTPS. - -## Features - -### Automatic Certificate Issuance - -- **Let's Encrypt Integration**: Free SSL certificates via ACME protocol -- **Automatic Generation**: Certificates issued when you add a domain -- **HTTP-01 Challenge**: Domain verification through HTTP -- **Zero Configuration**: No manual certificate management needed - -### Auto-Renewal - -- **Automatic Renewal**: Certificates renewed before expiry (typically 30 days before) -- **Zero Downtime**: Renewals happen seamlessly in the background -- **No Manual Intervention**: Traefik handles the entire renewal process - -### Security Features - -- **Force HTTPS**: Automatic HTTP to HTTPS redirect enabled by default -- **TLS 1.2+**: Modern encryption standards -- **Secure Headers**: Best practice security headers configured - -## How It Works - -### Adding a Domain - -1. **Add Domain**: Enter your domain name in the application's Domains tab -2. **DNS Verification**: Ensure your domain's A record points to your Mist server -3. **Automatic Issuance**: Traefik detects the new domain and requests a certificate -4. **Certificate Installation**: Let's Encrypt issues the certificate and Traefik installs it -5. **HTTPS Enabled**: Your application is now accessible via HTTPS - -The entire process is automatic and typically takes 1-2 minutes after DNS propagation. - -### Domain Labels - -Mist automatically configures Traefik labels on your containers: - -```yaml -traefik.enable=true -traefik.http.routers.app-{id}.rule=Host(`your-domain.com`) -traefik.http.routers.app-{id}.entrypoints=websecure -traefik.http.routers.app-{id}.tls=true -traefik.http.routers.app-{id}.tls.certresolver=le -traefik.http.routers.app-{id}-http.rule=Host(`your-domain.com`) -traefik.http.routers.app-{id}-http.entrypoints=web -traefik.http.routers.app-{id}-http.middlewares=https-redirect -traefik.http.middlewares.https-redirect.redirectscheme.scheme=https -``` - -### Certificate Storage - -Certificates are stored in Traefik's ACME storage: -- Location: `/etc/traefik/acme.json` -- Format: JSON file with encrypted certificates -- Backup: Recommended to backup this file regularly - -## Requirements - -For SSL automation to work properly: - -1. **Valid Domain**: Domain must be registered and active -2. **DNS Configuration**: A record pointing to your server's public IP -3. **Port Access**: Ports 80 (HTTP) and 443 (HTTPS) must be publicly accessible -4. **Public Server**: Server must be reachable from the internet for Let's Encrypt verification -5. **No Rate Limits**: Be aware of Let's Encrypt rate limits (50 certificates per domain per week) - -## Monitoring Certificate Status - -### In Dashboard - -View certificate status in the Domains tab: -- **Pending**: Certificate request in progress -- **Active**: Certificate successfully issued and installed -- **Failed**: Certificate issuance failed (check logs) - -### Via Logs - -Check Traefik logs for certificate operations: - -```bash -docker logs traefik -``` - -Look for messages like: -- `Obtaining certificate for domain` -- `Certificate obtained successfully` -- `Renewing certificate` - -## Troubleshooting - -### Certificate Not Issuing - -**DNS Not Propagated** -- Wait for DNS propagation (up to 48 hours) -- Verify with: `dig your-domain.com` - -**Port Not Accessible** -- Check firewall: `sudo ufw status` -- Ensure ports 80 and 443 are open -- Test accessibility: `curl -I http://your-domain.com` - -**Let's Encrypt Rate Limit** -- Let's Encrypt has limits: 50 certificates per registered domain per week -- Wait if you've hit the limit -- Use Let's Encrypt staging environment for testing - -**Domain Verification Failed** -- Ensure domain points to correct IP -- Check that no other service is using port 80 -- Verify Traefik is running: `docker ps | grep traefik` - -### Certificate Not Renewing - -**Check ACME Storage** -```bash -ls -la /etc/traefik/acme.json -``` - -**Check Traefik Configuration** -```bash -cat /etc/traefik/traefik.yml -``` - -Ensure certificate resolver is configured: -```yaml -certificatesResolvers: - le: - acme: - email: your-email@example.com - storage: /acme.json - httpChallenge: - entryPoint: web -``` - -### Debugging - -Enable debug logging in Traefik: -1. Edit Traefik configuration -2. Set log level to DEBUG -3. Restart Traefik: `docker restart traefik` -4. Monitor logs: `docker logs -f traefik` - -## Best Practices - -### Email Configuration - -Configure a valid email in Traefik for Let's Encrypt notifications: -- Notifies about certificate expiration -- Required for Let's Encrypt terms acceptance -- Used for important updates - -### Backup ACME Storage - -Regularly backup `/etc/traefik/acme.json`: -```bash -cp /etc/traefik/acme.json /backup/acme.json.$(date +%Y%m%d) -``` - -### Test Domains - -Use staging environment for testing: -- Avoids rate limits -- Tests certificate issuance without counting against production limits -- Configure in Traefik: `caServer: https://acme-staging-v02.api.letsencrypt.org/directory` - -### Multiple Domains - -You can add multiple domains to a single application: -- Each domain gets its own certificate -- All domains route to the same container -- Certificates managed independently - -## Coming Soon - -- **Wildcard Certificates**: Support for `*.example.com` domains -- **Custom Certificates**: Upload and manage your own SSL certificates -- **Certificate Dashboard**: View all certificates and expiration dates -- **DNS-01 Challenge**: Support for wildcard certificates via DNS validation -- **Certificate Notifications**: Email alerts before certificate expiration - -## Related Documentation - -- [Domains Guide](./domains) - Managing application domains -- [Traefik Configuration](/deployment/traefik) - Advanced Traefik setup -- [Applications](./applications) - Application management diff --git a/www/docs/guide/users.md b/www/docs/guide/users.md deleted file mode 100644 index 951dc80..0000000 --- a/www/docs/guide/users.md +++ /dev/null @@ -1,167 +0,0 @@ -# Users & Roles - -Manage users and their permissions in Mist. - -## System Roles - -Mist has a hierarchical role-based access control system with three levels: - -### Owner - -**Highest privilege level** - Complete system control - -- Only ONE owner exists per Mist instance -- Created automatically during first-time setup -- Cannot be duplicated through UI or API -- Full access to all features and settings -- Can view and update system settings -- Can create and delete users (including admins) -- Can view all audit logs -- Can delete themselves (resets system to setup mode) - -### Admin - -**High-level administrative privileges** - Second to owner - -- Can create and manage projects -- Can create users (admin and user roles) -- Can delete users (except owners and other admins) -- Can view all audit logs -- Access to assigned projects only -- **Cannot** view or modify system settings -- **Cannot** delete owners or other admins -- **Cannot** create owner accounts - -### User - -**Standard role** - Basic project access - -- Can access assigned projects -- Can deploy applications -- Can manage applications in assigned projects -- Can view logs and metrics -- **Cannot** create projects -- **Cannot** create other users -- **Cannot** view system settings -- **Cannot** view audit logs - -## Role Comparison - -| Permission | Owner | Admin | User | -|-----------|-------|-------|------| -| View system settings | ✅ | ❌ | ❌ | -| Update system settings | ✅ | ❌ | ❌ | -| Create projects | ✅ | ✅ | ❌ | -| Create users | ✅ | ✅ | ❌ | -| Delete owners | ✅ | ❌ | ❌ | -| Delete admins | ✅ | ❌ | ❌ | -| Delete users | ✅ | ✅ | ❌ | -| View audit logs | ✅ | ✅ | ❌ | -| Access assigned projects | ✅ | ✅ | ✅ | -| Manage applications | ✅ | ✅ | ✅ | - -## Managing Users - -### Creating Users - -**Required role**: Owner or Admin - -1. Navigate to **Settings** → **Users** -2. Click **"Add User"** or **"New User"** -3. Fill in user information: - - Username - - Email address - - Password -4. Select role: - - **Admin**: For administrative users - - **User**: For standard users -5. Click **"Create"** - -::: tip -You cannot create additional owner accounts. Only one owner exists per Mist instance. -::: - -### Viewing Users - -**Required role**: Owner or Admin - -View all users in the system: -- Username -- Email -- Role (displayed with color-coded badges) -- Account creation date - -**Role badge colors:** -- Owner: Purple -- Admin: Blue -- User: Gray - -### Updating Users - -**Required role**: Owner or Admin - -Modify existing user details: -1. Click on the user you want to edit -2. Update information: - - Username - - Email - - Password - - Role (owner/admin only) -3. Click **"Save"** - -All changes are logged in the audit system. - -### Deleting Users - -**Deletion permissions:** -- **Owner**: Can delete any user (admins and users) -- **Admin**: Can only delete users (not owners or other admins) -- **User**: Cannot delete anyone - -To delete a user: -1. Navigate to the Users page -2. Click the delete icon next to the user -3. Confirm the deletion - -::: warning Deletion Restrictions -- Admins cannot delete owners or other admins -- Projects and applications created by the deleted user are preserved -- If the owner deletes themselves, the system resets to setup mode -::: - -## Project Roles - -::: info Coming Soon -Project-level roles (Owner, Admin, Member) are not yet implemented. Currently, all project members have equal access to project resources. - -See [Projects documentation](./projects) for more details on project membership. -::: - -## Best Practices - -### Role Assignment - -- **Owner**: Reserve for the primary system administrator -- **Admin**: For trusted team members who need to manage users and projects -- **User**: For team members who only need to work on specific projects - -### Security Recommendations - -1. **Limit Admins**: Only assign admin role to users who need administrative privileges -2. **Use Users**: Most team members should have the user role -3. **Protect Owner**: The owner account should have a strong password and be well-protected -4. **Regular Audits**: Review user list regularly and remove inactive accounts -5. **Audit Logs**: Monitor audit logs for suspicious activity - -### Account Management - -- Set strong password requirements for all users -- Remove users who no longer need access -- Review role assignments periodically -- Document who has owner and admin access - -## Related Documentation - -- [Authentication](./authentication) - Detailed authentication information -- [Audit Logs](./audit-logs) - Track user actions -- [Projects](./projects) - Project membership management diff --git a/www/docs/guide/what-is-mist.md b/www/docs/guide/what-is-mist.md deleted file mode 100644 index ad0b6dd..0000000 --- a/www/docs/guide/what-is-mist.md +++ /dev/null @@ -1,146 +0,0 @@ -# What is Mist? - -Mist is a **self-hostable Platform-as-a-Service (PaaS)** that allows you to deploy and manage Docker-based applications on your own infrastructure. Think of it as your own private Heroku, but with more control and zero vendor lock-in. - -## Overview - -Mist simplifies the deployment workflow by providing: - -- **Automated Deployments** - Push to Git and deploy automatically -- **Container Management** - Docker-based isolation for applications -- **Real-time Monitoring** - WebSocket-powered live logs and metrics -- **Team Collaboration** - Projects with multi-user access -- **Domain Management** - Custom domains with automatic SSL and wildcard domain support - -## Architecture - -Mist is built with a **monolithic Go backend** and a **React + Vite frontend**, designed to run efficiently on a single VPS with minimal resources. - -### Core Components - -``` -┌─────────────────────────────────────────────┐ -│ Frontend (React + Vite) │ -│ Modern UI with Real-time Updates │ -└──────────────────┬──────────────────────────┘ - │ REST API + WebSocket -┌──────────────────▼──────────────────────────┐ -│ Backend API (Go) │ -│ • Authentication & Authorization │ -│ • Project & Application Management │ -│ • Deployment Queue & Workers │ -│ • Real-time Log Streaming │ -└──────────┬────────────────┬──────────────────┘ - │ │ - │ │ -┌──────────▼────────┐ ┌────▼──────────────────┐ -│ SQLite Database │ │ Docker Engine │ -│ • Users │ │ • Build Images │ -│ • Projects │ │ • Run Containers │ -│ • Apps │ │ • Manage Networks │ -│ • Deployments │ │ │ -└───────────────────┘ └───────────────────────┘ - │ - ┌─────────▼─────────┐ - │ Traefik Proxy │ - │ • Route Traffic │ - │ • SSL/TLS │ - └────────────────────┘ -``` - -[Learn more about architecture →](./architecture) - -## Key Features - -### Deployment Workflow - -1. **Connect Repository** - Link your GitHub repository -2. **Configure Build** - Set build and start commands -3. **Add Environment Variables** - Configure runtime environment -4. **Deploy** - Push to Git or manually trigger deployment -5. **Monitor** - Watch real-time logs and metrics - -### Technology Stack - -**Backend:** -- Go (1.21+) - Fast, compiled language -- SQLite - Embedded database -- Traefik - Reverse proxy and load balancer -- Docker - Container runtime - -**Frontend:** -- React 18 - UI framework -- Vite - Build tool and dev server -- Tailwind CSS - Styling -- Shadcn/ui - Component library - -## Why Choose Mist? - -### 🪶 Ultra Lightweight - -- **Single Binary** - No complex installation process -- **No External Database** - SQLite embedded -- **Minimal Dependencies** - Just Docker required -- **Low Resource Usage** - Runs on basic VPS ($5-10/month) - -### ⚡ Real-time Experience - -- **WebSocket-First** - Instant feedback for all operations -- **Live Logs** - Stream container and system logs in real-time -- **Live Metrics** - CPU, RAM, and disk usage updated every second -- **Deployment Status** - Watch builds progress in real-time -- **System Monitoring** - View Mist backend logs via journalctl integration - -### 🔧 Developer Friendly - -- **Simple Setup** - One-line installation script -- **Intuitive UI** - Clean, modern dashboard -- **Comprehensive API** - RESTful endpoints for automation -- **CLI Tool** - (Coming Soon) Deploy from terminal - -### 🔐 Secure - -- **JWT Authentication** - Secure token-based auth -- **HTTP-Only Cookies** - Protection against XSS -- **Audit Logging** - Track all user actions -- **Role-Based Access** - Admin and user roles -- **Environment Encryption** - (Coming Soon) Secrets at rest - -## Use Cases - -### Personal Projects - -Host your side projects, portfolios, and experimental apps without paying for expensive hosting. - -### Small Teams - -Collaborate with team members on multiple projects with organized workspaces and access control. - -### Staging Environments - -Run isolated staging environments alongside production apps for testing before deployment. - -### Microservices - -Deploy and manage multiple microservices with custom domains and networking. - -## Comparison with Other PaaS - -| Feature | Mist | Coolify | Dokploy | CapRover | Heroku | -|---------|------|---------|---------|----------|--------| -| **Self-hosted** | ✅ | ✅ | ✅ | ✅ | ❌ | -| **Open Source** | ✅ | ✅ | ✅ | ✅ | ❌ | -| **Real-time Monitoring** | ✅ | ✅ | ✅ | ❌ | ✅ | -| **No External DB** | ✅ (SQLite) | ❌ (Postgres) | ❌ (Postgres) | ❌ (Mongo) | N/A | -| **Go Backend** | ✅ | ❌ (Node) | ❌ (Node) | ❌ (Node) | N/A | -| **Single Binary** | ✅ | ❌ | ❌ | ❌ | N/A | -| **Git Integration** | ✅ GitHub | ✅ Multiple | ✅ Multiple | ✅ Multiple | ✅ Multiple | -| **Managed Databases** | ✅ | ✅ | ✅ | ✅ | ✅ | -| **SSL Automation** | ✅ | ✅ | ✅ | ✅ | ✅ | -| **Auto Domain Generation** | ✅ | ❌ | ❌ | ✅ | ✅ | - -## Getting Started - -Ready to deploy your first application with Mist? - -[Get Started →](./getting-started) diff --git a/www/docs/index.md b/www/docs/index.md deleted file mode 100644 index 5683885..0000000 --- a/www/docs/index.md +++ /dev/null @@ -1,187 +0,0 @@ ---- -layout: home - -hero: - name: Mist - text: | - Self-hostable - Platform-as-a-Service - tagline: Deploy applications with ease. Lightweight, fast, and built for developers. - image: - src: /mist.png - alt: Mist Logo ---- - - - - - -## Core Features - -
- -
-
- -
-

Easy Deployment

-

Deploy Docker apps from Git with automatic builds and zero-downtime updates

-
- -
-
- -
-

Git Integration

-

Native GitHub integration with webhooks for automatic deployments on push

-
- -
-
- -
-

Domains & SSL

-

Custom domains with automatic Let's Encrypt SSL certificate provisioning

-
- -
-
- -
-

Real-time Monitoring

-

Live deployment logs, system metrics, and container status with WebSocket updates

-
- -
-
- -
-

Ultra Lightweight

-

Single Go binary with embedded SQLite. No external dependencies required

-
- -
-
- -
-

Secure by Default

-

JWT authentication, audit logs, and role-based access control built-in

-
- -
-
- -
-

Database Services

-

One-click provisioning for PostgreSQL, MySQL, Redis, and MongoDB

-
- -
-
- -
-

Multi-User Projects

-

Team collaboration with project organization and role-based permissions

-
- -
-
- -
-

Environment Variables

-

Manage build-time and runtime environment variables with bulk import support

-
- -
- -## Why Mist? - -Mist is a **lightweight, self-hostable Platform-as-a-Service** designed for developers and small teams who want the simplicity of Heroku with the control of self-hosting. - -
- -### Platform Comparison - -
- -| Feature | Mist | Coolify | Dokploy | CapRover | Dokku | -|---------|------|---------|---------|----------|-------| -| **Installation** | Single command | Docker Compose | Single command | Docker Compose | Single command | -| **Dependencies** | Docker only | Docker + PostgreSQL/Redis | Docker only | Docker + PostgreSQL | Docker only | -| **Memory Usage** | ~50MB | ~500MB+ | ~100MB | ~300MB+ | Minimal | -| **Database** | SQLite (embedded) | PostgreSQL | PostgreSQL | PostgreSQL | File-based | -| **Real-time Updates** | ✓ WebSocket | ~ Polling | ✓ WebSocket | ~ Limited | ✗ No | -| **Multi-User** | ✓ Built-in | ✓ Built-in | ✓ Built-in | ~ Basic | ✗ Single user | -| **UI Framework** | React | Vue.js | React | Angular | Web + CLI | -| **Git Providers** | GitHub only | Multi-provider | Multi-provider | Multi-provider | Multi-provider | -| **One-click Upgrade** | ✓ Built-in | ✓ Built-in | ✓ Built-in | ✓ Built-in | ~ CLI only | -| **Docker Compose** | ✗ Not supported | ✓ Full support | ✓ Full support | ~ Limited | ~ Via plugins | -| **S3 Backups** | ✗ Not available | ✓ Automated | ✓ Automated | ~ Manual | ✗ Not available | -| **Template Library** | ✗ Coming soon | ✓ Extensive | ✓ Available | ✓ One-click apps | ~ Limited | -| **GitHub Integration** | ✓ Native | ✓ Native | ✓ Native | ~ Basic | ~ Via plugins | -| **Auto SSL** | ✓ Let's Encrypt | ✓ Let's Encrypt | ✓ Let's Encrypt | ✓ Let's Encrypt | ~ Via plugins | -| **Learning Curve** | Easy | Moderate | Easy | Moderate | Steep | -| **High Availability** | ✗ Single node | ✗ Single node | ✗ Single node | ✓ Cluster support | ✗ Single node | -| **Container Orchestration** | Docker | Docker | Docker | Docker Swarm | Docker/Kubernetes | -| **Maturity** | New (2025) | Established | New (2024) | Mature | Very mature | - -
- -
- -### What Mist Does Well - -
- -- **Minimal Resource Footprint** - Runs efficiently on a single VPS with just Docker installed (~50MB RAM) -- **Real-time Everything** - WebSocket-first architecture for instant feedback on logs, metrics, and deployments -- **Zero External Dependencies** - No PostgreSQL, Redis, or other services required. Just Docker. -- **Fast Setup** - Single command installation with automatic configuration -- **One-Click Updates** - Seamless upgrades from the dashboard with automatic rollback on failure -- **Modern UI/UX** - Clean, intuitive React interface with excellent developer experience - -
- -### Current Limitations - -
- -- **No High Availability** - Single node deployment only. Not suitable for mission-critical production workloads requiring 99.99% uptime -- **Limited Container Orchestration** - No Kubernetes support. Uses basic Docker containers without advanced orchestration -- **Single Git Provider** - Currently only supports GitHub. GitLab and Bitbucket support planned for future releases -- **No Docker Compose Support** - Cannot deploy docker-compose.yml files directly. Requires Dockerfile-based deployments only -- **No Automated Backups** - Missing S3/cloud storage integration for automatic database and application backups -- **Limited Template Library** - No pre-built application templates or one-click deployments (coming soon) -- **No Database Clustering** - Provisioned databases run as single containers without replication or clustering -- **Early Stage Project** - New platform (2024) with smaller community compared to established alternatives like Dokku or CapRover -- **Manual Scaling** - No auto-scaling capabilities. Manual intervention required for resource adjustments - -
- -
- -::: info Important Note -**Mist is not a drop-in replacement for Coolify, Dokploy, or other platforms.** We are a lightweight alternative focused on simplicity and minimal resource usage. If you need advanced features like Docker Compose support, multi-git providers, or high availability right now, consider using the more established alternatives above. However, these features are on our roadmap and will be available in Mist soon. -::: - -::: tip Fast Growing -**Mist is actively developed and rapidly evolving.** New features are being added continuously. All missing features listed above are on the roadmap and will be implemented soon. We're committed to making Mist a complete, production-ready PaaS solution. -::: - -
- - -## Community - -- [GitHub Repository](https://github.com/corecollectives/mist) -- [Discord Server](https://discord.gg/kxK8XHR6) -- [Report Issues](https://github.com/corecollectives/mist/issues) -- [Discussions](https://github.com/corecollectives/mist/discussions) - -## License - -Mist is open source software licensed under the [MIT License](https://github.com/corecollectives/mist/blob/main/LICENSE). diff --git a/www/docs/public/install.sh b/www/docs/public/install.sh deleted file mode 100755 index 91a6b79..0000000 --- a/www/docs/public/install.sh +++ /dev/null @@ -1,274 +0,0 @@ -#!/bin/bash -set -Eeo pipefail - - -LOG_FILE="/tmp/mist-install.log" -sudo rm -f "$LOG_FILE" 2>/dev/null || true -: > "$LOG_FILE" - -REAL_USER="${SUDO_USER:-$USER}" -REAL_HOME="$(getent passwd "$REAL_USER" | cut -d: -f6)" - -REPO="https://github.com/corecollectives/mist" -BRANCH="release" -APP_NAME="mist" -INSTALL_DIR="/opt/mist" -GO_BACKEND_DIR="server" -GO_BINARY_NAME="mist" -PORT=8080 -MIST_FILE="/var/lib/mist/mist.db" -SERVICE_FILE="/etc/systemd/system/$APP_NAME.service" - -SPINNER_PID="" -SUDO_KEEPALIVE_PID="" - -export PATH="/usr/local/go/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH" - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' - -log() { echo -e "${GREEN}[INFO]${NC} $1" | tee -a "$LOG_FILE"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOG_FILE"; } -error() { echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"; } - -spinner() { - local i=0 chars='|/-\' - while :; do - i=$(( (i + 1) % 4 )) - printf "\r⏳ %c" "${chars:$i:1}" - sleep 0.1 - done -} - -run_step() { - printf "\n▶ $1\n" - spinner & SPINNER_PID=$! - if bash -c "$2" >>"$LOG_FILE" 2>&1; then - kill "$SPINNER_PID" 2>/dev/null || true - wait "$SPINNER_PID" 2>/dev/null || true - printf "\r\033[K✔ Done\n" - return 0 - else - kill "$SPINNER_PID" 2>/dev/null || true - wait "$SPINNER_PID" 2>/dev/null || true - printf "\r\033[K✘ Failed\n" - return 1 - fi -} - -cleanup() { - kill "$SPINNER_PID" 2>/dev/null || true - kill "$SUDO_KEEPALIVE_PID" 2>/dev/null || true -} -trap cleanup EXIT - -rollback() { - error "Installation failed! Cleaning up..." - if [ -f "$SERVICE_FILE" ]; then - sudo systemctl stop "$APP_NAME" 2>>"$LOG_FILE" || true - sudo systemctl disable "$APP_NAME" 2>>"$LOG_FILE" || true - sudo rm -f "$SERVICE_FILE" 2>>"$LOG_FILE" || true - fi - error "Check logs: $LOG_FILE" - tail -30 "$LOG_FILE" 2>/dev/null - cleanup - exit 1 -} - -trap - ERR -log "Starting Mist installation..." - -if [ "$EUID" -ne 0 ] && [ -z "${SUDO_USER:-}" ]; then - error "This script requires sudo. Run: sudo bash install.sh" - exit 1 -fi - -echo "🔐 Verifying sudo access..." -sudo -v || exit 1 - -(while true; do sleep 60; sudo -n true || exit; done) 2>/dev/null & -SUDO_KEEPALIVE_PID=$! - -log "Checking disk space..." -AVAILABLE=$(df /opt 2>/dev/null | tail -1 | awk '{print $4}' || echo "0") -if [ "$AVAILABLE" -lt 2000000 ]; then - error "Need at least 2GB free in /opt" - exit 1 -fi - -log "Checking network..." -if ! curl -s --connect-timeout 10 https://github.com >/dev/null 2>&1; then - error "No network connectivity" - exit 1 -fi - -trap rollback ERR - -log "Installing dependencies..." -if command -v apt >/dev/null 2>&1; then - run_step "Installing packages (apt)" "sudo DEBIAN_FRONTEND=noninteractive apt update && sudo DEBIAN_FRONTEND=noninteractive apt install -y git curl build-essential wget unzip" || exit 1 -elif command -v dnf >/dev/null 2>&1; then - run_step "Installing packages (dnf)" "sudo dnf install -y git curl gcc make wget unzip" || exit 1 -elif command -v yum >/dev/null 2>&1; then - run_step "Installing packages (yum)" "sudo yum install -y git curl gcc make wget unzip" || exit 1 -elif command -v pacman >/dev/null 2>&1; then - run_step "Installing packages (pacman)" "sudo pacman -Sy --noconfirm git curl base-devel wget unzip" || exit 1 -else - error "Unsupported package manager" - exit 1 -fi - -if ! command -v docker >/dev/null 2>&1; then - error "Docker not found. Install from: https://docs.docker.com/engine/install/" - exit 1 -fi -if ! docker ps >/dev/null 2>&1; then - error "Docker not running or no permissions" - exit 1 -fi -log "Docker verified" - -if ! command -v go >/dev/null 2>&1; then - ARCH=$(uname -m) - case "$ARCH" in - x86_64) GO_ARCH="amd64";; - aarch64|arm64) GO_ARCH="arm64";; - armv7l) GO_ARCH="armv6l";; - *) error "Unsupported arch: $ARCH"; exit 1;; - esac - - run_step "Downloading Go" "wget -q https://go.dev/dl/go1.22.11.linux-${GO_ARCH}.tar.gz -O /tmp/go.tar.gz" || exit 1 - run_step "Installing Go" "sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf /tmp/go.tar.gz && rm -f /tmp/go.tar.gz" || exit 1 - - grep -q '/usr/local/go/bin' "$REAL_HOME/.bashrc" 2>/dev/null || echo 'export PATH=$PATH:/usr/local/go/bin' >>"$REAL_HOME/.bashrc" - export PATH="$PATH:/usr/local/go/bin" -fi -go version >>"$LOG_FILE" 2>&1 || { error "Go not working"; exit 1; } -log "Go ready" - -# ---------------- Repo ---------------- - -if [ -d "$INSTALL_DIR/.git" ]; then - log "Updating repository..." - cd "$INSTALL_DIR" - git config --local advice.detachedHead false 2>&1 || true - run_step "Fetching $BRANCH" "cd '$INSTALL_DIR' && git fetch origin '$BRANCH' && git reset --hard origin/'$BRANCH'" || exit 1 -else - run_step "Creating directory" "sudo mkdir -p '$INSTALL_DIR' && sudo chown '$REAL_USER:$REAL_USER' '$INSTALL_DIR'" || exit 1 - run_step "Cloning repository" "git clone -b '$BRANCH' --single-branch --depth 1 '$REPO' '$INSTALL_DIR'" || exit 1 -fi - -[ -d "$INSTALL_DIR/$GO_BACKEND_DIR" ] || { error "Server directory missing"; exit 1; } - -run_step "Setting ownership" "sudo chown -R root:root '$INSTALL_DIR'" || exit 1 - -sudo git config --global --add safe.directory "$INSTALL_DIR" >>"$LOG_FILE" 2>&1 || true - -log "Repository ready" - -run_step "Creating data directories" "sudo mkdir -p /var/lib/mist/{traefik,logs,backups} && sudo touch '$MIST_FILE' && sudo chown -R root:root /var/lib/mist && sudo chmod -R 755 /var/lib/mist" || exit 1 -run_step "Creating Traefik config" "sudo tee /var/lib/mist/traefik/dynamic.yml >/dev/null <<'EOF' -http: - routers: {} - services: {} -EOF -" || exit 1 - -cd "$INSTALL_DIR/$GO_BACKEND_DIR" -[ -f "go.mod" ] || { error "go.mod missing"; exit 1; } -run_step "Downloading dependencies" "cd '$INSTALL_DIR/$GO_BACKEND_DIR' && go mod download && go mod tidy" || exit 1 -run_step "Building backend" "cd '$INSTALL_DIR/$GO_BACKEND_DIR' && go build -v -o '$GO_BINARY_NAME'" || exit 1 -[ -f "$GO_BINARY_NAME" ] || { error "Binary not created"; exit 1; } -chmod +x "$GO_BINARY_NAME" -log "Build complete" - -# ---------------- CLI Tool ---------------- - -if [ -d "$INSTALL_DIR/cli" ]; then - if run_step "Building CLI tool" "cd '$INSTALL_DIR/cli' && go mod tidy && go build -o mist-cli"; then - if run_step "Installing CLI tool" "sudo cp '$INSTALL_DIR/cli/mist-cli' /usr/local/bin/mist-cli && sudo chmod +x /usr/local/bin/mist-cli"; then - log "CLI tool installed: mist-cli" - else - warn "Failed to install CLI tool, but continuing..." - fi - else - warn "Failed to build CLI tool, but continuing..." - fi -fi - -run_step "Creating systemd service" "sudo tee '$SERVICE_FILE' >/dev/null <<'EOF' -[Unit] -Description=Mist Service -After=network.target docker.service -Requires=docker.service - -[Service] -WorkingDirectory=/opt/mist/server -ExecStart=/opt/mist/server/mist -Restart=always -RestartSec=5 -User=root -Environment=PORT=8080 -Environment=PATH=/usr/local/go/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin - -[Install] -WantedBy=multi-user.target -EOF -" || exit 1 - -if sudo systemctl is-active --quiet "$APP_NAME" 2>/dev/null; then - log "Service already running, restarting..." - run_step "Restarting service" "sudo systemctl daemon-reload && sudo systemctl restart '$APP_NAME'" || exit 1 -else - run_step "Starting service" "sudo systemctl daemon-reload && sudo systemctl enable '$APP_NAME' && sudo systemctl start '$APP_NAME'" || exit 1 -fi -sleep 3 -sudo systemctl is-active --quiet "$APP_NAME" || { error "Service failed to start"; sudo journalctl -u "$APP_NAME" -n 20; exit 1; } -log "Service running" - -[ -f "$INSTALL_DIR/traefik-compose.yml" ] || { error "traefik-compose.yml missing"; exit 1; } -run_step "Creating Docker network" "docker network inspect traefik-net >/dev/null 2>&1 || docker network create traefik-net" || warn "Network creation failed" -run_step "Starting Traefik" "docker compose -f '$INSTALL_DIR/traefik-compose.yml' up -d" || warn "Traefik failed" - -if command -v ufw >/dev/null 2>&1; then - sudo ufw allow $PORT/tcp 2>&1 || true -elif command -v firewall-cmd >/dev/null 2>&1; then - sudo firewall-cmd --permanent --add-port=${PORT}/tcp 2>&1 || true - sudo firewall-cmd --reload 2>&1 || true -fi - -log "Running health check..." -sleep 5 -HTTP_OK=false -for i in {1..10}; do - if curl -f -s -o /dev/null "http://localhost:$PORT/api/health" 2>>"$LOG_FILE"; then - HTTP_OK=true - break - fi - sleep 2 -done -[ "$HTTP_OK" = true ] && log "HTTP check passed" || warn "HTTP check failed (may still be initializing)" - -SERVER_IP=$(curl -fsSL https://api.ipify.org 2>/dev/null || hostname -I | awk '{print $1}') -CLI_INSTALLED="" -if [ -f "/usr/local/bin/mist-cli" ]; then - CLI_INSTALLED="║ 💻 CLI Tool: mist-cli --help ║" -fi - -echo -echo "╔════════════════════════════════════════════╗" -echo "║ 🎉 Mist installation complete ║" -printf "║ 👉 %-40s║\n" "http://$SERVER_IP:$PORT" -if [ -n "$CLI_INSTALLED" ]; then - printf "║ %-42s ║\n" "$CLI_INSTALLED" -fi -echo "╚════════════════════════════════════════════╝" -echo -echo "📄 Logs: $LOG_FILE" -echo "🔧 Service: sudo systemctl status $APP_NAME" -echo "📋 Logs: sudo journalctl -u $APP_NAME -f" -if [ -f "/usr/local/bin/mist-cli" ]; then - echo "💻 CLI Tool: mist-cli --help" -fi diff --git a/www/docs/public/mist.png b/www/docs/public/mist.png deleted file mode 100644 index 0c9c10b..0000000 Binary files a/www/docs/public/mist.png and /dev/null differ diff --git a/www/docs/troubleshooting/forgot-password.md b/www/docs/troubleshooting/forgot-password.md deleted file mode 100644 index e376aac..0000000 --- a/www/docs/troubleshooting/forgot-password.md +++ /dev/null @@ -1,381 +0,0 @@ -# Forgot Password Recovery - -Step-by-step guide to recover access when you've forgotten your password. - ---- - -## Overview - -If you've forgotten your Mist password, you can reset it using the `mist-cli` command-line tool. This tool provides direct database access to change user passwords without requiring dashboard access. - -::: warning Server Access Required -You need SSH or direct access to the server where Mist is installed to use this method. -::: - ---- - -## Quick Recovery Steps - -### 1. SSH into Your Server - -Connect to your server where Mist is installed: - -```bash -ssh user@your-server-ip -``` - -### 2. Reset Password Using CLI - -Run the password reset command: - -```bash -sudo mist-cli user change-password --username YOUR_USERNAME -``` - -You'll be prompted to enter and confirm your new password: - -``` -Enter new password: -Confirm new password: -✓ Password changed successfully for user 'YOUR_USERNAME' -``` - -### 3. Log Back In - -Navigate to your Mist dashboard and log in with your new password: - -``` -https://your-mist-domain.com -``` - ---- - -## Detailed Instructions - -### For Admin Users - -If you're the admin and forgot the admin password: - -```bash -# SSH to server -ssh user@your-server-ip - -# Reset admin password -sudo mist-cli user change-password --username admin - -# Enter and confirm new password when prompted -``` - -**Example session:** -```bash -$ sudo mist-cli user change-password --username admin -Enter new password: **************** -Confirm new password: **************** -✓ Password changed successfully for user 'admin' -``` - -### For Other Users - -If you're helping another user who forgot their password: - -```bash -# First, list all users to verify the username -sudo mist-cli user list - -# Output: -# Users: -# ---------------------------------------------- -# ID Username Email Role -# ---------------------------------------------- -# 1 admin admin@example.com owner -# 2 developer dev@example.com member -# ---------------------------------------------- - -# Reset the specific user's password -sudo mist-cli user change-password --username developer -``` - ---- - -## Non-Interactive Mode - -If you need to reset a password in a script or automation, you can provide the password directly: - -```bash -sudo mist-cli user change-password --username admin --password NewSecurePass123 -``` - -::: warning Security Notice -Using the `--password` flag will expose the password in: -- Shell history -- Process list (visible via `ps` command) -- Log files - -Use this method only when necessary and clear your shell history afterward: -```bash -history -c -``` -::: - ---- - -## Troubleshooting - -### CLI Command Not Found - -**Error:** -```bash -mist-cli: command not found -``` - -**Solution:** - -Check if the CLI is installed: -```bash -ls -la /usr/local/bin/mist-cli -``` - -If not found, rebuild and install the CLI: -```bash -cd /opt/mist/cli -sudo go build -o mist-cli -sudo cp mist-cli /usr/local/bin/ -sudo chmod +x /usr/local/bin/mist-cli - -# Verify installation -mist-cli version -``` - -### Permission Denied - -**Error:** -```bash -Error: open /var/lib/mist/mist.db: permission denied -``` - -**Solution:** - -Run the command with `sudo`: -```bash -sudo mist-cli user change-password --username admin -``` - -### Database Not Found - -**Error:** -```bash -Error: database file not found at /var/lib/mist/mist.db -``` - -**Solution:** - -Verify Mist is installed and running: -```bash -# Check service status -sudo systemctl status mist - -# Check if database file exists -ls -la /var/lib/mist/mist.db - -# If service is not running, start it -sudo systemctl start mist -``` - -### User Not Found - -**Error:** -```bash -Error: User 'johndoe' not found -``` - -**Solution:** - -List all users to find the correct username: -```bash -sudo mist-cli user list -``` - -Usernames are case-sensitive, so ensure you're using the exact username as shown in the list. - -### Passwords Don't Match - -**Error:** -```bash -Error: Passwords do not match -``` - -**Solution:** - -When entering passwords in interactive mode, make sure you type the same password twice. If you keep getting this error: - -1. Copy your desired password to clipboard -2. Paste it when prompted for "Enter new password" -3. Paste it again when prompted for "Confirm new password" - -Or use non-interactive mode: -```bash -sudo mist-cli user change-password --username admin --password YourNewPassword123 -``` - ---- - -## Security Best Practices - -### After Password Reset - -1. **Clear shell history** if you used non-interactive mode: -```bash -history -c -``` - -2. **Use a strong password:** - - At least 12 characters - - Mix of uppercase, lowercase, numbers, and symbols - - Avoid common words or patterns - -3. **Consider using a password manager** like: - - Bitwarden - - 1Password - - KeePassXC - -### Prevent Future Lockouts - -1. **Store passwords securely:** - - Use a password manager - - Keep encrypted backup of credentials - - Document recovery procedures - -2. **Set up multiple admin accounts:** -```bash -# Create a backup admin account (via dashboard) -# Or add an additional user with owner role -``` - -3. **Keep SSH access secure:** - - Use SSH keys instead of passwords - - Document server access procedures - - Maintain backup access methods - ---- - -## Alternative Recovery Methods - -### If You Don't Have Server Access - -If you cannot access the server directly, you'll need to: - -1. **Contact your hosting provider** or system administrator -2. **Access the server console** through your cloud provider's dashboard (AWS, DigitalOcean, etc.) -3. **Use server recovery mode** if available - -### If CLI is Not Available - -If the CLI tool is not installed or not working, you can reset the password directly in the database: - -::: danger Advanced Users Only -Direct database manipulation can corrupt your data. Use the CLI method when possible. -::: - -```bash -# Connect to database -sqlite3 /var/lib/mist/mist.db - -# Generate password hash (use a temporary Go script or online bcrypt generator) -# Then update the database -UPDATE users SET password_hash = 'YOUR_BCRYPT_HASH' WHERE username = 'admin'; -.quit -``` - ---- - -## Testing the New Password - -After resetting your password: - -1. **Open your browser** and navigate to Mist dashboard -2. **Clear browser cookies** (recommended): - - Chrome: Settings → Privacy → Clear browsing data → Cookies - - Firefox: Settings → Privacy → Clear Data → Cookies -3. **Log in** with your username and new password -4. **Verify access** to all dashboard features - ---- - -## Common Scenarios - -### Scenario 1: Locked Out as Only Admin - -```bash -# SSH to server -ssh user@server - -# Reset your admin password -sudo mist-cli user change-password --username admin - -# Log back into dashboard -# https://your-mist-domain.com -``` - -### Scenario 2: Reset Password for Another User - -```bash -# List users first -sudo mist-cli user list - -# Reset specific user's password -sudo mist-cli user change-password --username developer - -# Notify the user of their password reset -``` - -### Scenario 3: Bulk Password Reset - -```bash -# Reset multiple users (requires bash loop) -for user in developer tester; do - sudo mist-cli user change-password --username $user --password TempPassword123 - echo "Reset password for $user" -done - -# Notify users to change their temporary passwords -``` - ---- - -## Getting Help - -If you're still having trouble recovering access: - -### Check Documentation -- [CLI Tool Guide](/guide/cli) - Complete CLI documentation -- [User Management](/guide/users) - User roles and permissions -- [Authentication](/guide/authentication) - Login and security - -### Community Support -- **GitHub Issues:** [github.com/corecollectives/mist/issues](https://github.com/corecollectives/mist/issues) -- **Discord:** [discord.gg/kxK8XHR6](https://discord.gg/kxK8XHR6) - -### Provide This Information -When asking for help, include: - -```bash -# Mist version -curl http://localhost:8080/api/updates/version - -# Service status -sudo systemctl status mist - -# CLI version -mist-cli version - -# User list (remove sensitive info) -sudo mist-cli user list -``` - ---- - -## Related Documentation - -- [CLI Tool Guide](/guide/cli) - Full CLI documentation -- [User Management](/guide/users) - Managing users and roles -- [Common Issues](/troubleshooting/) - Other troubleshooting guides diff --git a/www/docs/troubleshooting/index.md b/www/docs/troubleshooting/index.md deleted file mode 100644 index d481e3b..0000000 --- a/www/docs/troubleshooting/index.md +++ /dev/null @@ -1,645 +0,0 @@ -# Troubleshooting Guide - -Common issues and solutions for Mist deployments. - -::: tip Quick Links -- [Forgot Password Recovery](/troubleshooting/forgot-password) - Reset your password using CLI -- [Manual Update Guide](/troubleshooting/manual-update) - Update Mist manually if dashboard update fails -- [Community Support](https://discord.gg/kxK8XHR6) - Get help from the community -::: - ---- - -## Installation Issues - -### Installation Script Fails - -**Problem:** Installation script exits with errors - -**Solutions:** - -1. **Check system requirements:** -```bash -# Verify Docker is installed and running -docker --version -docker ps - -# Check disk space (need at least 2GB) -df -h /opt - -# Verify network connectivity -curl -s https://github.com -``` - -2. **Run with proper permissions:** -```bash -sudo bash install.sh -``` - -3. **Check the installation log:** -```bash -tail -50 /tmp/mist-install.log -``` - -### Go Installation Fails - -**Problem:** Go download or installation fails - -**Solution:** -```bash -# Manually install Go 1.22+ -wget https://go.dev/dl/go1.22.11.linux-amd64.tar.gz -sudo rm -rf /usr/local/go -sudo tar -C /usr/local -xzf go1.22.11.linux-amd64.tar.gz -export PATH=$PATH:/usr/local/go/bin -go version -``` - -### Docker Permission Denied - -**Problem:** Docker commands fail with permission errors - -**Solution:** -```bash -# Add current user to docker group -sudo usermod -aG docker $USER - -# Restart shell or run -newgrp docker - -# Verify -docker ps -``` - ---- - -## Service Issues - -### Mist Service Won't Start - -**Problem:** `systemctl start mist` fails - -**Diagnosis:** -```bash -# Check service status -sudo systemctl status mist - -# View recent logs -sudo journalctl -u mist -n 50 --no-pager - -# Check if port 8080 is already in use -sudo lsof -i :8080 -``` - -**Solutions:** - -1. **Port already in use:** -```bash -# Stop the conflicting service - -# Reload and restart -sudo systemctl daemon-reload -sudo systemctl restart mist -``` - -2. **Database issues:** -```bash -# Check database file permissions -ls -la /var/lib/mist/mist.db - -# Fix permissions if needed -sudo chown root:root /var/lib/mist/mist.db -sudo chmod 644 /var/lib/mist/mist.db -``` - -3. **Binary issues:** -```bash -# Rebuild the binary -cd /opt/mist/server -sudo go build -o mist -sudo systemctl restart mist -``` - -### Service Crashes After Update - -**Problem:** Service won't start after running update - -**Solution:** -```bash -# Check for stuck update logs -sudo journalctl -u mist -n 100 --no-pager | grep -i update - -# If update is stuck, manually clear it using CLI -sudo mist-cli settings list - -# View update logs via API or dashboard -curl http://localhost:8080/api/updates/history - -# Force restart the service -sudo systemctl daemon-reload -sudo systemctl restart mist -``` - ---- - -## Deployment Issues - -### Build Fails - -**Problem:** Application build fails during deployment - -**Diagnosis:** -```bash -# Check build logs in the dashboard -# Or check Docker logs -docker logs -``` - -**Common causes:** - -1. **Missing dependencies:** -```dockerfile -# Add missing dependencies to Dockerfile -RUN npm install -# or -RUN pip install -r requirements.txt -``` - -2. **Wrong build context:** -```dockerfile -# Ensure Dockerfile is in repository root -# Or specify custom path in deployment settings -``` - -3. **Build timeout:** -- Increase timeout in deployment settings -- Optimize Dockerfile with multi-stage builds -- Use build cache effectively - -### Container Keeps Restarting - -**Problem:** Deployed container restarts repeatedly - -**Diagnosis:** -```bash -# View container logs -docker logs - -# Check container status -docker ps -a | grep - -# Inspect container -docker inspect -``` - -**Common solutions:** - -1. **Application crashes on startup:** -- Check environment variables are set correctly -- Verify database connection strings -- Check port configuration - -2. **Health check failures:** -- Ensure health check endpoint returns 200 -- Increase health check timeout -- Verify health check path is correct - -3. **Missing environment variables:** -```bash -# Check via Mist dashboard or API -# Add missing variables in Environment Variables section -``` - -### Port Already in Use - -**Problem:** Deployment fails with "port already allocated" error - -**Solution:** -```bash -# Find conflicting container -docker ps | grep - -# Stop conflicting container -docker stop - -# Or change your app's port in environment variables -``` - ---- - -## Domain & SSL Issues - -### Domain Not Resolving - -**Problem:** Custom domain doesn't point to your application - -**Checklist:** - -1. **DNS Configuration:** -```bash -# Verify DNS records -dig yourdomain.com -nslookup yourdomain.com - -# Should point to your server IP -# A record: yourdomain.com -> your-server-ip -``` - -2. **Wildcard domain setup:** -```bash -# Verify wildcard domain setting -mist-cli settings get --key wildcard_domain - -# Should be set to your domain -mist-cli settings set --key wildcard_domain --value apps.example.com -``` - -3. **Traefik configuration:** -```bash -# Check Traefik is running -docker ps | grep traefik - -# Restart Traefik if needed -cd /opt/mist -docker compose -f traefik-compose.yml restart -``` - -### SSL Certificate Issues - -**Problem:** SSL certificate not generating or invalid - -**Diagnosis:** -```bash -# Check Traefik logs -docker logs traefik - -# Verify domain is accessible -curl -v https://yourdomain.com -``` - -**Solutions:** - -1. **Let's Encrypt rate limit:** -- Wait 1 hour (for failures) or 1 week (for cert limit) -- Use staging environment for testing - -2. **DNS not propagated:** -```bash -# Wait for DNS propagation (up to 48 hours) -# Check propagation status -dig +trace yourdomain.com -``` - -3. **Firewall blocking ports:** -```bash -# Ensure ports 80 and 443 are open -sudo ufw status -sudo ufw allow 80/tcp -sudo ufw allow 443/tcp -``` - ---- - -## Database Issues - -### Cannot Connect to Database - -**Problem:** Application can't connect to database service - -**Solutions:** - -1. **Verify database is running:** -```bash -docker ps | grep postgres -# or -docker ps | grep mysql -``` - -2. **Check connection string:** -- Ensure environment variables are correct -- Format: `postgresql://user:password@host:5432/dbname` -- Host should be the container name - -3. **Network issues:** -```bash -# Verify containers are on same network -docker network inspect traefik-net -``` - -### Database Data Lost After Restart - -**Problem:** Data disappears when container restarts - -**Solution:** -- Ensure volumes are properly configured -- Check volume mounts: -```bash -docker inspect | grep -A 10 Mounts -``` - ---- - -## GitHub Integration Issues - -### GitHub App Not Connecting - -**Problem:** Cannot connect GitHub repositories - -**Diagnosis:** -1. Verify GitHub App is installed on your organization/account -2. Check webhook delivery in GitHub App settings -3. Ensure webhook URL is accessible from internet - -**Solution:** -```bash -# Verify GitHub App settings in Mist dashboard -# Go to Settings -> Git - -# Test webhook: -curl -X POST https://your-mist-domain.com/api/github/webhook \ - -H "Content-Type: application/json" \ - -d '{"action": "ping"}' -``` - -### Webhook Delivery Fails - -**Problem:** GitHub webhooks not reaching Mist - -**Solutions:** - -1. **Firewall blocking webhooks:** -```bash -# Ensure Mist is accessible from internet -# Check if your server has public IP -curl ifconfig.me -``` - -2. **Wrong webhook URL:** -- Update in GitHub App settings -- Format: `https://your-mist-domain.com/api/github/webhook` - ---- - -## Performance Issues - -### Slow Dashboard Loading - -**Problem:** Dashboard takes long to load - -**Solutions:** - -1. **Check system resources:** -```bash -# View CPU and memory usage -htop - -# Check disk space -df -h - -# View container resource usage -docker stats -``` - -2. **Too many logs:** -```bash -# Clean up old logs -sudo find /var/lib/mist/logs -type f -mtime +7 -delete -``` - -3. **Database optimization:** -```bash -# Vacuum SQLite database -sqlite3 /var/lib/mist/mist.db "VACUUM;" -``` - -### High CPU Usage - -**Problem:** Server CPU constantly high - -**Diagnosis:** -```bash -# Check which process is using CPU -top -bn1 | head -20 - -# Check Docker container CPU -docker stats --no-stream -``` - -**Solutions:** - -1. **Too many containers:** -- Enable auto cleanup in settings -- Manually clean stopped containers: -```bash -docker container prune -f -``` - -2. **Container resource limits:** -- Set CPU/memory limits in deployment settings - ---- - -## Update Issues - -::: tip Manual Update Available -If you're experiencing issues with dashboard updates, you can [update manually using the install script](/troubleshooting/manual-update). -::: - -### Update Stuck in Progress - -**Problem:** Update shows "in progress" but nothing happens - -**Solution:** -```bash -# Check if update process is actually running -ps aux | grep install.sh - -# If not running, the startup check should auto-resolve it -# Force restart to trigger the check -sudo systemctl restart mist - -# Alternatively, manually clear stuck update via API -# (requires owner access) -curl -X POST http://localhost:8080/api/updates/clear?id= \ - --cookie "session_token=your_token" -``` - -**Or perform a manual update:** -```bash -# See full manual update guide -curl -fsSL https://trymist.cloud/install.sh | sudo bash -``` - -[→ Full Manual Update Guide](/troubleshooting/manual-update) - -### Update Failed - -**Problem:** Update failed and service won't start - -**Solution:** -```bash -# Check update logs -sudo journalctl -u mist -n 100 --no-pager - -# Revert to previous version if needed -cd /opt/mist -git log --oneline -10 -sudo git reset --hard -cd server -sudo go build -o mist -sudo systemctl restart mist -``` - -**Or perform a manual update with automatic recovery:** -```bash -curl -fsSL https://trymist.cloud/install.sh | sudo bash -``` - -[→ Full Manual Update Guide](/troubleshooting/manual-update) - ---- - -## CLI Issues - -### CLI Command Not Found - -**Problem:** `mist-cli` command not found - -**Solution:** -```bash -# Check if CLI is installed -ls -la /usr/local/bin/mist-cli - -# If not found, rebuild and install -cd /opt/mist/cli -sudo go build -o mist-cli -sudo cp mist-cli /usr/local/bin/ -sudo chmod +x /usr/local/bin/mist-cli -``` - -### CLI Database Access Denied - -**Problem:** CLI can't access database - -**Solution:** -```bash -# Run with sudo -sudo mist-cli user list - -# Or fix database permissions -sudo chown root:root /var/lib/mist/mist.db -sudo chmod 644 /var/lib/mist/mist.db -``` - ---- - -## Network Issues - -### Cannot Access Dashboard - -**Problem:** Cannot reach Mist dashboard in browser - -**Checklist:** - -1. **Service running:** -```bash -sudo systemctl status mist -curl http://localhost:8080/api/health -``` - -2. **Firewall:** -```bash -# Check firewall rules -sudo ufw status - -# Allow port 8080 -sudo ufw allow 8080/tcp -``` - -3. **Port forwarding:** -- If behind NAT, ensure port forwarding is configured -- Check router/cloud provider settings - -### Applications Not Accessible - -**Problem:** Deployed apps return 404 or 502 errors - -**Diagnosis:** -```bash -# Check Traefik logs -docker logs traefik - -# Verify Traefik config -cat /var/lib/mist/traefik/dynamic.yml - -# Check app container is running -docker ps | grep -``` - -**Solutions:** - -1. **Traefik not running:** -```bash -cd /opt/mist -docker compose -f traefik-compose.yml up -d -``` - -2. **Wrong domain configuration:** -- Verify domain is correctly set in Mist dashboard -- Check DNS points to your server - -3. **Container not in traefik-net:** -```bash -docker network connect traefik-net -``` - ---- - -## Getting More Help - -If you're still experiencing issues: - -1. **Check logs:** -```bash -# Service logs -sudo journalctl -u mist -n 100 --no-pager - -# Container logs -docker logs - -# Traefik logs -docker logs traefik -``` - -2. **Enable debug logging:** -```bash -# Edit service file -sudo nano /etc/systemd/system/mist.service - -# Add debug environment variable -Environment=LOG_LEVEL=debug - -# Restart -sudo systemctl daemon-reload -sudo systemctl restart mist -``` - -3. **Community support:** -- GitHub Issues: [github.com/corecollectives/mist/issues](https://github.com/corecollectives/mist/issues) -- Discord: [discord.gg/kxK8XHR6](https://discord.gg/kxK8XHR6) - -4. **Gather diagnostic information:** -```bash -# System info -uname -a -docker version -docker compose version - -# Mist version -curl http://localhost:8080/api/updates/version - -# Resource usage -free -h -df -h -docker stats --no-stream -``` diff --git a/www/docs/troubleshooting/manual-update.md b/www/docs/troubleshooting/manual-update.md deleted file mode 100644 index 71a4945..0000000 --- a/www/docs/troubleshooting/manual-update.md +++ /dev/null @@ -1,628 +0,0 @@ -# Manual Update Guide - -Step-by-step guide to manually update Mist when the dashboard update fails or gets stuck. - ---- - -## Overview - -If you're experiencing issues updating Mist through the dashboard, you can perform a manual update using the same installation script. This method is safe and will preserve all your data, applications, and settings. - -::: tip Safe Update Process -The installation script is designed to be idempotent - you can run it multiple times safely. It will: -- ✅ Preserve your database and all data -- ✅ Keep all applications and deployments running -- ✅ Maintain your settings and configurations -- ✅ Update only the Mist binary and system files -::: - ---- - -## When to Use Manual Update - -Use manual update when: -- Dashboard update gets stuck in "in progress" state -- Update fails with errors in the dashboard -- Service won't restart after an update -- You want to update from a specific Git branch -- You need to rollback to a previous version - ---- - -## Quick Update Steps - -### 1. SSH into Your Server - -```bash -ssh user@your-server-ip -``` - -### 2. Run the Installation Script - -The same script used for installation will update your existing installation: - -```bash -curl -fsSL https://trymist.cloud/install.sh | sudo bash -``` - -That's it! The script will: -1. Detect your existing installation -2. Update the repository to the latest version -3. Rebuild the binary -4. Restart the service -5. Verify everything is working - -### 3. Verify Update - -```bash -# Check service status -sudo systemctl status mist - -# Check version -curl http://localhost:8080/api/updates/version - -# Access dashboard -# https://your-mist-domain.com -``` - ---- - -## Detailed Manual Update Process - -### Step 1: Prepare for Update - -Before updating, it's good practice to check the current state: - -```bash -# Check current version -curl http://localhost:8080/api/updates/version - -# Check service status -sudo systemctl status mist - -# View current running containers -docker ps -``` - -### Step 2: Run the Update Script - -Execute the installation script with sudo: - -```bash -curl -fsSL https://trymist.cloud/install.sh | sudo bash -``` - -**What the script does:** -1. Checks system requirements -2. Updates Git repository from the `release` branch -3. Downloads Go dependencies -4. Builds the new binary -5. Restarts the Mist service -6. Verifies the service is running - -**Expected output:** -``` -Starting Mist installation... -▶ Updating repository... -✔ Done -▶ Downloading dependencies... -✔ Done -▶ Building backend... -✔ Done -▶ Restarting service... -✔ Done -╔════════════════════════════════════════════╗ -║ 🎉 Mist installation complete ║ -║ 👉 http://your-server-ip:8080 ║ -╚════════════════════════════════════════════╝ -``` - -### Step 3: Verify the Update - -After the script completes: - -1. **Check service is running:** -```bash -sudo systemctl status mist -``` - -Expected output: `Active: active (running)` - -2. **Verify new version:** -```bash -curl http://localhost:8080/api/updates/version -``` - -3. **Check service logs:** -```bash -sudo journalctl -u mist -n 50 --no-pager -``` - -4. **Test dashboard access:** - - Open your browser - - Navigate to your Mist dashboard - - Log in and verify functionality - -5. **Check applications:** - - Verify all applications are still running - - Check deployment history - - Test application access - ---- - -## Handling Update Issues - -### Update Script Fails - -If the update script encounters errors: - -**1. Check the installation log:** -```bash -tail -100 /tmp/mist-install.log -``` - -**2. Common issues:** - -#### Port Already in Use -```bash -# Stop Mist service first -sudo systemctl stop mist - -# Run update script -curl -fsSL https://trymist.cloud/install.sh | sudo bash -``` - -#### Git Conflicts -```bash -# Navigate to installation directory -cd /opt/mist - -# Reset any local changes -sudo git reset --hard - -# Run update script again -curl -fsSL https://trymist.cloud/install.sh | sudo bash -``` - -#### Build Failures -```bash -# Clear Go cache -sudo go clean -cache -modcache - -# Rebuild manually -cd /opt/mist/server -sudo go mod tidy -sudo go build -o mist - -# Restart service -sudo systemctl restart mist -``` - -### Service Won't Start After Update - -If Mist service fails to start after update: - -**1. Check service status and logs:** -```bash -sudo systemctl status mist -sudo journalctl -u mist -n 100 --no-pager -``` - -**2. Check for common issues:** - -#### Database Locked -```bash -# Stop service -sudo systemctl stop mist - -# Check for processes using database -sudo lsof /var/lib/mist/mist.db - -# Kill any stale processes if found -sudo pkill -9 mist - -# Start service -sudo systemctl start mist -``` - -#### Binary Not Executable -```bash -# Make binary executable -sudo chmod +x /opt/mist/server/mist - -# Restart service -sudo systemctl restart mist -``` - -#### Missing Dependencies -```bash -# Reinstall dependencies -cd /opt/mist/server -sudo go mod download -sudo go build -o mist -sudo systemctl restart mist -``` - -### Applications Not Working After Update - -If your applications are inaccessible after update: - -**1. Check Traefik is running:** -```bash -docker ps | grep traefik -``` - -**2. Restart Traefik if needed:** -```bash -cd /opt/mist -docker compose -f traefik-compose.yml restart -``` - -**3. Check application containers:** -```bash -docker ps -a -``` - -**4. Restart specific containers if needed:** -```bash -docker restart -``` - ---- - -## Update from Specific Version or Branch - -### Update from Specific Git Branch - -To update from a different branch (e.g., `main`, `develop`): - -```bash -# Navigate to installation directory -cd /opt/mist - -# Switch to desired branch -sudo git fetch origin -sudo git checkout -sudo git pull origin - -# Build and restart -cd server -sudo go mod tidy -sudo go build -o mist -sudo systemctl restart mist -``` - -### Rollback to Previous Version - -If the new update causes issues, you can rollback: - -**1. View recent versions:** -```bash -cd /opt/mist -git log --oneline -10 -``` - -**2. Rollback to specific commit:** -```bash -# Replace with desired version -sudo git reset --hard - -# Rebuild -cd server -sudo go build -o mist - -# Restart -sudo systemctl restart mist -``` - -**3. Rollback to previous release tag:** -```bash -# View available tags -git tag -l - -# Checkout specific tag -sudo git checkout tags/v1.0.0 - -# Rebuild and restart -cd server -sudo go build -o mist -sudo systemctl restart mist -``` - ---- - -## Clearing Stuck Updates - -If a dashboard update is stuck in "in progress" state: - -### Method 1: Restart Service (Automatic Fix) - -Simply restart the Mist service - the startup check will automatically resolve stuck updates: - -```bash -sudo systemctl restart mist -``` - -The system will: -- Check for stuck updates on startup -- Compare current version with target version -- Mark update as success if versions match -- Mark as failed if versions don't match - -### Method 2: Manual Fix Using CLI - -Clear the stuck update manually: - -**1. List update history:** -```bash -# Check update logs via dashboard or: -curl http://localhost:8080/api/updates/history -``` - -**2. Clear stuck update:** - -Via API (requires authentication): -```bash -# Note: You need to be logged in and get the update_log_id -curl -X POST "http://localhost:8080/api/updates/clear?id=" \ - --cookie "session_token=your_session_token" -``` - -### Method 3: Complete Manual Update - -Perform a complete manual update which will automatically fix the stuck state: - -```bash -# Run installation script -curl -fsSL https://trymist.cloud/install.sh | sudo bash - -# Service restart will resolve stuck update -``` - ---- - -## Update Best Practices - -### Before Updating - -1. **Backup database:** -```bash -# Create backup directory -sudo mkdir -p /var/lib/mist/backups - -# Copy database -sudo cp /var/lib/mist/mist.db /var/lib/mist/backups/mist.db.$(date +%Y%m%d_%H%M%S) -``` - -2. **Check disk space:** -```bash -df -h /opt -``` - -3. **Note current version:** -```bash -curl http://localhost:8080/api/updates/version -``` - -4. **Check running applications:** -```bash -docker ps -``` - -### During Update - -1. **Monitor the update process:** -```bash -# In a separate terminal, watch logs -sudo journalctl -u mist -f -``` - -2. **Don't interrupt the process:** - - Let the script complete - - Don't cancel or close the terminal - - Wait for confirmation message - -### After Update - -1. **Verify service health:** -```bash -# Check service -sudo systemctl status mist - -# Check logs for errors -sudo journalctl -u mist -n 50 --no-pager | grep -i error - -# Test health endpoint -curl http://localhost:8080/api/health -``` - -2. **Test critical functionality:** - - Log into dashboard - - Check applications are accessible - - Test deployment (optional) - - Verify domains are working - -3. **Clean up old builds (optional):** -```bash -# Clean Docker resources if needed -docker system prune -f -``` - ---- - -## Automated Update Script - -For convenience, you can create a custom update script: - -```bash -#!/bin/bash -# save as: /usr/local/bin/mist-update - -set -e - -echo "🔄 Starting Mist update..." - -# Backup database -echo "📦 Creating database backup..." -sudo mkdir -p /var/lib/mist/backups -sudo cp /var/lib/mist/mist.db /var/lib/mist/backups/mist.db.$(date +%Y%m%d_%H%M%S) - -# Run update -echo "⬇️ Downloading and installing update..." -curl -fsSL https://trymist.cloud/install.sh | sudo bash - -# Verify -echo "✅ Verifying update..." -sleep 3 -sudo systemctl status mist --no-pager - -echo "🎉 Update complete!" -echo "Current version:" -curl -s http://localhost:8080/api/updates/version | jq -r '.data.version' -``` - -**Make it executable and use it:** -```bash -sudo chmod +x /usr/local/bin/mist-update -sudo mist-update -``` - ---- - -## Update Frequency - -### Recommended Update Schedule - -- **Security updates:** As soon as available -- **Feature updates:** Monthly or as needed -- **Patch updates:** When issues are fixed - -### Check for Available Updates - -**Via Dashboard:** -- Navigate to Settings → Updates -- Click "Check for Updates" - -**Via API:** -```bash -curl http://localhost:8080/api/updates/check -``` - -**Via GitHub:** -- Check [releases page](https://github.com/corecollectives/mist/releases) -- Watch repository for notifications - ---- - -## Maintenance Mode (Optional) - -For minimal downtime during updates: - -**1. Create maintenance page:** -```bash -# Add to Traefik config to show maintenance page -# This is optional - most updates complete in under 30 seconds -``` - -**2. Notify users:** -- Post in team chat -- Send email notification -- Update status page - -**3. Schedule updates:** -- Choose low-traffic periods -- Plan for 5-10 minutes downtime -- Have rollback plan ready - ---- - -## Troubleshooting Specific Errors - -### Error: "go: command not found" - -```bash -# Install Go -wget https://go.dev/dl/go1.22.11.linux-amd64.tar.gz -sudo rm -rf /usr/local/go -sudo tar -C /usr/local -xzf go1.22.11.linux-amd64.tar.gz -export PATH=$PATH:/usr/local/go/bin - -# Run update again -curl -fsSL https://trymist.cloud/install.sh | sudo bash -``` - -### Error: "Permission denied" - -```bash -# Ensure you're using sudo -sudo bash install.sh - -# Or fix ownership -sudo chown -R root:root /opt/mist -sudo chmod -R 755 /opt/mist -``` - -### Error: "Database is locked" - -```bash -# Stop all processes -sudo systemctl stop mist - -# Check for lock -sudo lsof /var/lib/mist/mist.db - -# Kill if needed -sudo pkill -9 mist - -# Restart -sudo systemctl start mist -``` - ---- - -## Getting Help - -If manual update doesn't resolve your issue: - -### Gather Information - -```bash -# System info -uname -a - -# Mist version -curl http://localhost:8080/api/updates/version - -# Service status -sudo systemctl status mist - -# Recent logs -sudo journalctl -u mist -n 100 --no-pager - -# Installation log -tail -50 /tmp/mist-install.log -``` - -### Community Support - -- **GitHub Issues:** [Report update issues](https://github.com/corecollectives/mist/issues) -- **Discord:** [Get real-time help](https://discord.gg/kxK8XHR6) -- **Discussions:** [Ask questions](https://github.com/corecollectives/mist/discussions) - -### Include in Bug Report - -When reporting update issues, include: -- Current version and target version -- Complete error messages -- Installation log (`/tmp/mist-install.log`) -- Service logs (`sudo journalctl -u mist -n 100`) -- System information (`uname -a`) - ---- - -## Related Documentation - -- [Upgrading Guide](/deployment/upgrading) - Official upgrade documentation -- [Common Issues](/troubleshooting/) - General troubleshooting -- [Installation Guide](/deployment/installation) - Initial installation -- [Backup & Recovery](/deployment/backup) - Data backup procedures diff --git a/www/package-lock.json b/www/package-lock.json deleted file mode 100644 index e853a69..0000000 --- a/www/package-lock.json +++ /dev/null @@ -1,2475 +0,0 @@ -{ - "name": "www", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "www", - "version": "1.0.0", - "license": "ISC", - "devDependencies": { - "vitepress": "^1.6.4", - "vue": "^3.5.26" - } - }, - "node_modules/@algolia/abtesting": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.12.1.tgz", - "integrity": "sha512-Y+7e2uPe376OH5O73OB1+vR40ZhbV2kzGh/AR/dPCWguoBOp1IK0o+uZQLX+7i32RMMBEKl3pj6KVEav100Kvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1", - "@algolia/requester-browser-xhr": "5.46.1", - "@algolia/requester-fetch": "5.46.1", - "@algolia/requester-node-http": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/autocomplete-core": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", - "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", - "@algolia/autocomplete-shared": "1.17.7" - } - }, - "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", - "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-shared": "1.17.7" - }, - "peerDependencies": { - "search-insights": ">= 1 < 3" - } - }, - "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", - "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-shared": "1.17.7" - }, - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/autocomplete-shared": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", - "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/client-abtesting": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.46.1.tgz", - "integrity": "sha512-5SWfl0UGuKxMBYlU2Y9BnlIKKEyhFU5jHE9F9jAd8nbhxZNLk0y7fXE+AZeFtyK1lkVw6O4B/e6c3XIVVCkmqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1", - "@algolia/requester-browser-xhr": "5.46.1", - "@algolia/requester-fetch": "5.46.1", - "@algolia/requester-node-http": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-analytics": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.46.1.tgz", - "integrity": "sha512-496K6B1l/0Jvyp3MbW/YIgmm1a6nkTrKXBM7DoEy9YAOJ8GywGpa2UYjNCW1UrOTt+em1ECzDjRx7PIzTR9YvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1", - "@algolia/requester-browser-xhr": "5.46.1", - "@algolia/requester-fetch": "5.46.1", - "@algolia/requester-node-http": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-common": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.46.1.tgz", - "integrity": "sha512-3u6AuZ1Kiss6V5JPuZfVIUYfPi8im06QBCgKqLg82GUBJ3SwhiTdSZFIEgz2mzFuitFdW1PQi3c/65zE/3FgIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-insights": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.46.1.tgz", - "integrity": "sha512-LwuWjdO35HHl1rxtdn48t920Xl26Dl0SMxjxjFeAK/OwK/pIVfYjOZl/f3Pnm7Kixze+6HjpByVxEaqhTuAFaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1", - "@algolia/requester-browser-xhr": "5.46.1", - "@algolia/requester-fetch": "5.46.1", - "@algolia/requester-node-http": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-personalization": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.46.1.tgz", - "integrity": "sha512-6LvJAlfEsn9SVq63MYAFX2iUxztUK2Q7BVZtI1vN87lDiJ/tSVFKgKS/jBVO03A39ePxJQiFv6EKv7lmoGlWtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1", - "@algolia/requester-browser-xhr": "5.46.1", - "@algolia/requester-fetch": "5.46.1", - "@algolia/requester-node-http": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-query-suggestions": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.46.1.tgz", - "integrity": "sha512-9GLUCyGGo7YOXHcNqbzca82XYHJTbuiI6iT0FTGc0BrnV2N4OcrznUuVKic/duiLSun5gcy/G2Bciw5Sav9f9w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1", - "@algolia/requester-browser-xhr": "5.46.1", - "@algolia/requester-fetch": "5.46.1", - "@algolia/requester-node-http": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-search": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.46.1.tgz", - "integrity": "sha512-NL76o/BoEgU4ObY5oBEC3o6KSPpuXsnSta00tAxTm1iKUWOGR34DQEKhUt8xMHhMKleUNPM/rLPFiIVtfsGU8w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@algolia/client-common": "5.46.1", - "@algolia/requester-browser-xhr": "5.46.1", - "@algolia/requester-fetch": "5.46.1", - "@algolia/requester-node-http": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/ingestion": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.46.1.tgz", - "integrity": "sha512-52Nc8WKC1FFXsdlXlTMl1Re/pTAbd2DiJiNdYmgHiikZcfF96G+Opx4qKiLUG1q7zp9e+ahNwXF6ED0XChMywg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1", - "@algolia/requester-browser-xhr": "5.46.1", - "@algolia/requester-fetch": "5.46.1", - "@algolia/requester-node-http": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/monitoring": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.46.1.tgz", - "integrity": "sha512-1x2/2Y/eqz6l3QcEZ8u/zMhSCpjlhePyizJd3sXrmg031HjayYT5+IxikjpqkdF7TU/deCTd/TFUcxLJ2ZHXiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1", - "@algolia/requester-browser-xhr": "5.46.1", - "@algolia/requester-fetch": "5.46.1", - "@algolia/requester-node-http": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/recommend": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.46.1.tgz", - "integrity": "sha512-SSd3KlQuplxV3aRs5+Z09XilFesgpPjtCG7BGRxLTVje5hn9BLmhjO4W3gKw01INUt44Z1r0Fwx5uqnhAouunA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1", - "@algolia/requester-browser-xhr": "5.46.1", - "@algolia/requester-fetch": "5.46.1", - "@algolia/requester-node-http": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-browser-xhr": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.46.1.tgz", - "integrity": "sha512-3GfCwudeW6/3caKSdmOP6RXZEL4F3GiemCaXEStkTt2Re8f7NcGYAAZnGlHsCzvhlNEuDzPYdYxh4UweY8l/2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-fetch": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.46.1.tgz", - "integrity": "sha512-JUAxYfmnLYTVtAOFxVvXJ4GDHIhMuaP7JGyZXa/nCk3P8RrN5FCNTdRyftSnxyzwSIAd8qH3CjdBS9WwxxqcHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-node-http": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.46.1.tgz", - "integrity": "sha512-VwbhV1xvTGiek3d2pOS6vNBC4dtbNadyRT+i1niZpGhOJWz1XnfhxNboVbXPGAyMJYz7kDrolbDvEzIDT93uUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.5" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@docsearch/css": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", - "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@docsearch/js": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", - "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@docsearch/react": "3.8.2", - "preact": "^10.0.0" - } - }, - "node_modules/@docsearch/react": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", - "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-core": "1.17.7", - "@algolia/autocomplete-preset-algolia": "1.17.7", - "@docsearch/css": "3.8.2", - "algoliasearch": "^5.14.2" - }, - "peerDependencies": { - "@types/react": ">= 16.8.0 < 19.0.0", - "react": ">= 16.8.0 < 19.0.0", - "react-dom": ">= 16.8.0 < 19.0.0", - "search-insights": ">= 1 < 3" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "search-insights": { - "optional": true - } - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@iconify-json/simple-icons": { - "version": "1.2.63", - "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.63.tgz", - "integrity": "sha512-xZl2UWCwE58VlqZ+pDPmaUhE2tq8MVSTJRr4/9nzzHlDdjJ0Ud1VxNXPrwTSgESKY29iCQw3S0r2nJTSNNngHw==", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "@iconify/types": "*" - } - }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz", - "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz", - "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz", - "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz", - "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz", - "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz", - "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz", - "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz", - "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz", - "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz", - "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz", - "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz", - "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz", - "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz", - "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz", - "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz", - "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz", - "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz", - "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz", - "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz", - "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz", - "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz", - "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@shikijs/core": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", - "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/engine-javascript": "2.5.0", - "@shikijs/engine-oniguruma": "2.5.0", - "@shikijs/types": "2.5.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.4" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", - "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "2.5.0", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^3.1.0" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", - "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "2.5.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", - "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "2.5.0" - } - }, - "node_modules/@shikijs/themes": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", - "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "2.5.0" - } - }, - "node_modules/@shikijs/transformers": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", - "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/core": "2.5.0", - "@shikijs/types": "2.5.0" - } - }, - "node_modules/@shikijs/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", - "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/markdown-it": { - "version": "14.1.2", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", - "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/linkify-it": "^5", - "@types/mdurl": "^2" - } - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/web-bluetooth": { - "version": "0.0.21", - "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", - "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, - "node_modules/@vitejs/plugin-vue": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", - "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0", - "vue": "^3.2.25" - } - }, - "node_modules/@vue/compiler-core": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz", - "integrity": "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.5", - "@vue/shared": "3.5.26", - "entities": "^7.0.0", - "estree-walker": "^2.0.2", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.26.tgz", - "integrity": "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-core": "3.5.26", - "@vue/shared": "3.5.26" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.26.tgz", - "integrity": "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.5", - "@vue/compiler-core": "3.5.26", - "@vue/compiler-dom": "3.5.26", - "@vue/compiler-ssr": "3.5.26", - "@vue/shared": "3.5.26", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.21", - "postcss": "^8.5.6", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.26.tgz", - "integrity": "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.26", - "@vue/shared": "3.5.26" - } - }, - "node_modules/@vue/devtools-api": { - "version": "7.7.9", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", - "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/devtools-kit": "^7.7.9" - } - }, - "node_modules/@vue/devtools-kit": { - "version": "7.7.9", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", - "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/devtools-shared": "^7.7.9", - "birpc": "^2.3.0", - "hookable": "^5.5.3", - "mitt": "^3.0.1", - "perfect-debounce": "^1.0.0", - "speakingurl": "^14.0.1", - "superjson": "^2.2.2" - } - }, - "node_modules/@vue/devtools-shared": { - "version": "7.7.9", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", - "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "rfdc": "^1.4.1" - } - }, - "node_modules/@vue/reactivity": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.26.tgz", - "integrity": "sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/shared": "3.5.26" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.26.tgz", - "integrity": "sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.26", - "@vue/shared": "3.5.26" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.26.tgz", - "integrity": "sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.26", - "@vue/runtime-core": "3.5.26", - "@vue/shared": "3.5.26", - "csstype": "^3.2.3" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.26.tgz", - "integrity": "sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-ssr": "3.5.26", - "@vue/shared": "3.5.26" - }, - "peerDependencies": { - "vue": "3.5.26" - } - }, - "node_modules/@vue/shared": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz", - "integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vueuse/core": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", - "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/web-bluetooth": "^0.0.21", - "@vueuse/metadata": "12.8.2", - "@vueuse/shared": "12.8.2", - "vue": "^3.5.13" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vueuse/integrations": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", - "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vueuse/core": "12.8.2", - "@vueuse/shared": "12.8.2", - "vue": "^3.5.13" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "async-validator": "^4", - "axios": "^1", - "change-case": "^5", - "drauu": "^0.4", - "focus-trap": "^7", - "fuse.js": "^7", - "idb-keyval": "^6", - "jwt-decode": "^4", - "nprogress": "^0.2", - "qrcode": "^1.5", - "sortablejs": "^1", - "universal-cookie": "^7" - }, - "peerDependenciesMeta": { - "async-validator": { - "optional": true - }, - "axios": { - "optional": true - }, - "change-case": { - "optional": true - }, - "drauu": { - "optional": true - }, - "focus-trap": { - "optional": true - }, - "fuse.js": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "jwt-decode": { - "optional": true - }, - "nprogress": { - "optional": true - }, - "qrcode": { - "optional": true - }, - "sortablejs": { - "optional": true - }, - "universal-cookie": { - "optional": true - } - } - }, - "node_modules/@vueuse/metadata": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", - "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vueuse/shared": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", - "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "vue": "^3.5.13" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/algoliasearch": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.46.1.tgz", - "integrity": "sha512-39ol8Ulqb3MntofkXHlrcXKyU8BU0PXvQrXPBIX6eXj/EO4VT7651mhGVORI2oF8ydya9nFzT3fYDoqme/KL6w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@algolia/abtesting": "1.12.1", - "@algolia/client-abtesting": "5.46.1", - "@algolia/client-analytics": "5.46.1", - "@algolia/client-common": "5.46.1", - "@algolia/client-insights": "5.46.1", - "@algolia/client-personalization": "5.46.1", - "@algolia/client-query-suggestions": "5.46.1", - "@algolia/client-search": "5.46.1", - "@algolia/ingestion": "1.46.1", - "@algolia/monitoring": "1.46.1", - "@algolia/recommend": "5.46.1", - "@algolia/requester-browser-xhr": "5.46.1", - "@algolia/requester-fetch": "5.46.1", - "@algolia/requester-node-http": "5.46.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/birpc": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", - "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/copy-anything": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", - "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-what": "^5.2.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/emoji-regex-xs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", - "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", - "dev": true, - "license": "MIT" - }, - "node_modules/entities": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz", - "integrity": "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/focus-trap": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.6.tgz", - "integrity": "sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "tabbable": "^6.3.0" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hookable": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", - "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-what": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", - "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/mark.js": { - "version": "8.11.1", - "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", - "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/minisearch": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", - "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", - "dev": true, - "license": "MIT" - }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/oniguruma-to-es": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", - "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex-xs": "^1.0.0", - "regex": "^6.0.1", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/perfect-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", - "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/preact": { - "version": "10.28.0", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.0.tgz", - "integrity": "sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", - "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "dev": true, - "license": "MIT" - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/rollup": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz", - "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.54.0", - "@rollup/rollup-android-arm64": "4.54.0", - "@rollup/rollup-darwin-arm64": "4.54.0", - "@rollup/rollup-darwin-x64": "4.54.0", - "@rollup/rollup-freebsd-arm64": "4.54.0", - "@rollup/rollup-freebsd-x64": "4.54.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", - "@rollup/rollup-linux-arm-musleabihf": "4.54.0", - "@rollup/rollup-linux-arm64-gnu": "4.54.0", - "@rollup/rollup-linux-arm64-musl": "4.54.0", - "@rollup/rollup-linux-loong64-gnu": "4.54.0", - "@rollup/rollup-linux-ppc64-gnu": "4.54.0", - "@rollup/rollup-linux-riscv64-gnu": "4.54.0", - "@rollup/rollup-linux-riscv64-musl": "4.54.0", - "@rollup/rollup-linux-s390x-gnu": "4.54.0", - "@rollup/rollup-linux-x64-gnu": "4.54.0", - "@rollup/rollup-linux-x64-musl": "4.54.0", - "@rollup/rollup-openharmony-arm64": "4.54.0", - "@rollup/rollup-win32-arm64-msvc": "4.54.0", - "@rollup/rollup-win32-ia32-msvc": "4.54.0", - "@rollup/rollup-win32-x64-gnu": "4.54.0", - "@rollup/rollup-win32-x64-msvc": "4.54.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/search-insights": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", - "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/shiki": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", - "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/core": "2.5.0", - "@shikijs/engine-javascript": "2.5.0", - "@shikijs/engine-oniguruma": "2.5.0", - "@shikijs/langs": "2.5.0", - "@shikijs/themes": "2.5.0", - "@shikijs/types": "2.5.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/speakingurl": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", - "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "dev": true, - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/superjson": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", - "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "copy-anything": "^4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tabbable": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz", - "integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vitepress": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", - "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@docsearch/css": "3.8.2", - "@docsearch/js": "3.8.2", - "@iconify-json/simple-icons": "^1.2.21", - "@shikijs/core": "^2.1.0", - "@shikijs/transformers": "^2.1.0", - "@shikijs/types": "^2.1.0", - "@types/markdown-it": "^14.1.2", - "@vitejs/plugin-vue": "^5.2.1", - "@vue/devtools-api": "^7.7.0", - "@vue/shared": "^3.5.13", - "@vueuse/core": "^12.4.0", - "@vueuse/integrations": "^12.4.0", - "focus-trap": "^7.6.4", - "mark.js": "8.11.1", - "minisearch": "^7.1.1", - "shiki": "^2.1.0", - "vite": "^5.4.14", - "vue": "^3.5.13" - }, - "bin": { - "vitepress": "bin/vitepress.js" - }, - "peerDependencies": { - "markdown-it-mathjax3": "^4", - "postcss": "^8" - }, - "peerDependenciesMeta": { - "markdown-it-mathjax3": { - "optional": true - }, - "postcss": { - "optional": true - } - } - }, - "node_modules/vue": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz", - "integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@vue/compiler-dom": "3.5.26", - "@vue/compiler-sfc": "3.5.26", - "@vue/runtime-dom": "3.5.26", - "@vue/server-renderer": "3.5.26", - "@vue/shared": "3.5.26" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/www/package.json b/www/package.json deleted file mode 100644 index 0ff4650..0000000 --- a/www/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "mist-docs", - "version": "1.0.0", - "description": "Documentation for Mist - Self-hostable PaaS", - "type": "module", - "scripts": { - "docs:dev": "vitepress dev docs", - "docs:build": "vitepress build docs", - "docs:preview": "vitepress preview docs" - }, - "keywords": ["mist", "paas", "documentation", "self-hosted"], - "author": "", - "license": "MIT", - "devDependencies": { - "vitepress": "^1.6.4", - "vue": "^3.5.26" - } -}