@riligar/elysia-backup 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/assets/favicon.ico +0 -0
- package/src/assets/logo.png +0 -0
- package/src/core/config.js +70 -0
- package/src/core/scheduler.js +99 -0
- package/src/core/session.js +90 -0
- package/src/index.js +628 -3
- package/src/middleware/auth.middleware.js +41 -0
- package/src/services/backup.service.js +169 -0
- package/src/services/s3-client.js +28 -0
- package/src/views/DashboardPage.js +56 -0
- package/src/views/LoginPage.js +23 -0
- package/src/views/components/ActionArea.js +48 -0
- package/src/views/components/FilesTab.js +111 -0
- package/src/views/components/Head.js +56 -0
- package/src/views/components/Header.js +56 -0
- package/src/views/components/LoginCard.js +112 -0
- package/src/views/components/SecuritySection.js +166 -0
- package/src/views/components/SettingsTab.js +88 -0
- package/src/views/components/StatusCards.js +54 -0
- package/src/views/scripts/backupApp.js +324 -0
- package/src/views/scripts/loginApp.js +60 -0
- package/src/elysia-backup.js +0 -1736
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Login card component
|
|
3
|
+
* @param {{ totpEnabled: boolean }} props
|
|
4
|
+
* @returns {string} HTML string
|
|
5
|
+
*/
|
|
6
|
+
export const LoginCard = ({ totpEnabled }) => `
|
|
7
|
+
<div class="w-full max-w-md" x-data="loginApp()">
|
|
8
|
+
<!-- Login Card -->
|
|
9
|
+
<div class="bg-white rounded-2xl border border-gray-100 shadow-[0_8px_30px_rgba(0,0,0,0.08)] overflow-hidden">
|
|
10
|
+
<!-- Header -->
|
|
11
|
+
<div class="p-10 text-center border-b border-gray-100">
|
|
12
|
+
<img src="/backup/logo.png" alt="Backup Manager" class="w-20 h-20 mx-auto mb-4 rounded-xl">
|
|
13
|
+
<h1 class="text-2xl font-bold text-gray-900 mb-2">Backup Manager</h1>
|
|
14
|
+
<p class="text-sm text-gray-500">Access Control Panel</p>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- Form -->
|
|
18
|
+
<div class="p-10">
|
|
19
|
+
<form @submit.prevent="login" class="space-y-6">
|
|
20
|
+
<!-- Error Message -->
|
|
21
|
+
<div x-show="error" x-cloak class="bg-red-50 border border-red-200 rounded-lg p-4">
|
|
22
|
+
<div class="flex items-center gap-3">
|
|
23
|
+
<i data-lucide="alert-circle" class="w-5 h-5 text-red-600"></i>
|
|
24
|
+
<span class="text-sm text-red-800 font-medium" x-text="error"></span>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<!-- Username -->
|
|
29
|
+
<div>
|
|
30
|
+
<label class="block text-sm font-semibold text-gray-700 mb-2">Username</label>
|
|
31
|
+
<div class="relative">
|
|
32
|
+
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
|
|
33
|
+
<i data-lucide="user" class="w-5 h-5 text-gray-400"></i>
|
|
34
|
+
</div>
|
|
35
|
+
<input
|
|
36
|
+
type="text"
|
|
37
|
+
x-model="username"
|
|
38
|
+
required
|
|
39
|
+
class="w-full bg-gray-50 border border-gray-200 rounded-lg pl-12 pr-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium"
|
|
40
|
+
placeholder="Enter your username"
|
|
41
|
+
autofocus
|
|
42
|
+
>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<!-- Password -->
|
|
47
|
+
<div>
|
|
48
|
+
<label class="block text-sm font-semibold text-gray-700 mb-2">Password</label>
|
|
49
|
+
<div class="relative">
|
|
50
|
+
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
|
|
51
|
+
<i data-lucide="lock" class="w-5 h-5 text-gray-400"></i>
|
|
52
|
+
</div>
|
|
53
|
+
<input
|
|
54
|
+
type="password"
|
|
55
|
+
x-model="password"
|
|
56
|
+
required
|
|
57
|
+
class="w-full bg-gray-50 border border-gray-200 rounded-lg pl-12 pr-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium"
|
|
58
|
+
placeholder="Enter your password"
|
|
59
|
+
>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<!-- TOTP Code (only shown when TOTP is enabled) -->
|
|
64
|
+
<div x-show="totpEnabled" x-cloak>
|
|
65
|
+
<label class="block text-sm font-semibold text-gray-700 mb-2">Authenticator Code</label>
|
|
66
|
+
<div class="relative">
|
|
67
|
+
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
|
|
68
|
+
<i data-lucide="smartphone" class="w-5 h-5 text-gray-400"></i>
|
|
69
|
+
</div>
|
|
70
|
+
<input
|
|
71
|
+
type="text"
|
|
72
|
+
x-model="totpCode"
|
|
73
|
+
inputmode="numeric"
|
|
74
|
+
pattern="[0-9]*"
|
|
75
|
+
maxlength="6"
|
|
76
|
+
:required="totpEnabled"
|
|
77
|
+
class="w-full bg-gray-50 border border-gray-200 rounded-lg pl-12 pr-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium tracking-widest text-center text-lg"
|
|
78
|
+
placeholder="000000"
|
|
79
|
+
>
|
|
80
|
+
</div>
|
|
81
|
+
<p class="text-xs text-gray-500 mt-2 flex items-center gap-1">
|
|
82
|
+
<i data-lucide="info" class="w-3 h-3"></i>
|
|
83
|
+
Enter the 6-digit code from your authenticator app
|
|
84
|
+
</p>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<!-- Submit Button -->
|
|
88
|
+
<button
|
|
89
|
+
type="submit"
|
|
90
|
+
:disabled="loading"
|
|
91
|
+
class="w-full bg-primary-500 hover:bg-primary-600 text-white font-bold py-3.5 px-6 rounded-xl transition-all shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 flex items-center justify-center gap-2 disabled:opacity-70 disabled:cursor-not-allowed disabled:transform-none"
|
|
92
|
+
>
|
|
93
|
+
<span x-show="!loading" class="flex items-center gap-2">
|
|
94
|
+
<span>Sign In</span>
|
|
95
|
+
<i data-lucide="arrow-right" class="w-5 h-5"></i>
|
|
96
|
+
</span>
|
|
97
|
+
<span x-show="loading" class="flex items-center gap-2">
|
|
98
|
+
<i data-lucide="loader-2" class="w-5 h-5 animate-spin"></i>
|
|
99
|
+
<span>Authenticating...</span>
|
|
100
|
+
</span>
|
|
101
|
+
</button>
|
|
102
|
+
</form>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<!-- Footer -->
|
|
107
|
+
<div class="text-center mt-6 text-sm text-gray-500">
|
|
108
|
+
<i data-lucide="info" class="w-4 h-4 inline-block mr-1"></i>
|
|
109
|
+
Secure connection required for production use
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
`
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security section component with TOTP 2FA setup
|
|
3
|
+
* @returns {string} HTML string
|
|
4
|
+
*/
|
|
5
|
+
export const SecuritySection = () => `
|
|
6
|
+
<div class="bg-white rounded-2xl border border-gray-100 shadow-[0_2px_8px_rgba(0,0,0,0.04)] p-10 mt-8">
|
|
7
|
+
<h2 class="text-xl font-bold text-gray-900 mb-8 flex items-center gap-2">
|
|
8
|
+
<i data-lucide="shield" class="w-5 h-5"></i>
|
|
9
|
+
Security
|
|
10
|
+
</h2>
|
|
11
|
+
|
|
12
|
+
<!-- TOTP Setup -->
|
|
13
|
+
<div class="space-y-6">
|
|
14
|
+
<div class="flex items-start justify-between">
|
|
15
|
+
<div>
|
|
16
|
+
<h3 class="font-semibold text-gray-900 flex items-center gap-2">
|
|
17
|
+
<i data-lucide="smartphone" class="w-4 h-4"></i>
|
|
18
|
+
Two-Factor Authentication (2FA)
|
|
19
|
+
</h3>
|
|
20
|
+
<p class="text-sm text-gray-500 mt-1">
|
|
21
|
+
Add an extra layer of security using an authenticator app
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
<div x-show="!totpEnabled && !showTotpSetup">
|
|
25
|
+
<button
|
|
26
|
+
@click="generateTotp()"
|
|
27
|
+
class="bg-primary-500 hover:bg-primary-600 text-white font-bold py-2.5 px-5 rounded-lg transition-all flex items-center gap-2"
|
|
28
|
+
>
|
|
29
|
+
<i data-lucide="plus" class="w-4 h-4"></i>
|
|
30
|
+
Enable 2FA
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
<div x-show="totpEnabled && !showTotpSetup">
|
|
34
|
+
<span class="inline-flex items-center gap-2 px-4 py-2 bg-green-100 text-green-800 rounded-lg font-semibold text-sm">
|
|
35
|
+
<i data-lucide="check-circle" class="w-4 h-4"></i>
|
|
36
|
+
Enabled
|
|
37
|
+
</span>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<!-- TOTP Setup Flow -->
|
|
42
|
+
<div x-show="showTotpSetup" x-cloak class="border-t border-gray-100 pt-6 mt-6">
|
|
43
|
+
<!-- Loading -->
|
|
44
|
+
<div x-show="totpLoading" class="text-center py-8">
|
|
45
|
+
<i data-lucide="loader-2" class="w-8 h-8 animate-spin text-gray-400 mx-auto"></i>
|
|
46
|
+
<p class="text-sm text-gray-500 mt-2">Generating secure key...</p>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<!-- QR Code Display -->
|
|
50
|
+
<div x-show="!totpLoading && totpQrCode" class="space-y-6">
|
|
51
|
+
<div class="bg-gray-50 rounded-xl p-6 text-center">
|
|
52
|
+
<p class="text-sm font-medium text-gray-700 mb-4">
|
|
53
|
+
Scan this QR code with your authenticator app:
|
|
54
|
+
</p>
|
|
55
|
+
<img :src="totpQrCode" alt="TOTP QR Code" class="mx-auto w-48 h-48 rounded-lg shadow-sm">
|
|
56
|
+
|
|
57
|
+
<div class="mt-4 text-xs text-gray-500">
|
|
58
|
+
<p class="mb-2">Or enter this code manually:</p>
|
|
59
|
+
<code class="bg-white px-3 py-1.5 rounded border border-gray-200 font-mono text-gray-800 select-all" x-text="totpSecret"></code>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<!-- Verification -->
|
|
64
|
+
<div class="space-y-4">
|
|
65
|
+
<label class="block text-sm font-semibold text-gray-700">
|
|
66
|
+
Enter the 6-digit code from your authenticator app:
|
|
67
|
+
</label>
|
|
68
|
+
<div class="flex gap-4">
|
|
69
|
+
<input
|
|
70
|
+
type="text"
|
|
71
|
+
x-model="totpVerifyCode"
|
|
72
|
+
inputmode="numeric"
|
|
73
|
+
pattern="[0-9]*"
|
|
74
|
+
maxlength="6"
|
|
75
|
+
placeholder="000000"
|
|
76
|
+
class="flex-grow bg-gray-50 border border-gray-200 rounded-lg px-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium tracking-widest text-center text-lg"
|
|
77
|
+
>
|
|
78
|
+
<button
|
|
79
|
+
@click="verifyTotp()"
|
|
80
|
+
:disabled="totpVerifyCode.length !== 6 || totpVerifying"
|
|
81
|
+
class="bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-6 rounded-lg transition-all flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
82
|
+
>
|
|
83
|
+
<template x-if="!totpVerifying">
|
|
84
|
+
<span class="flex items-center gap-2">
|
|
85
|
+
<i data-lucide="check" class="w-4 h-4"></i>
|
|
86
|
+
Verify & Enable
|
|
87
|
+
</span>
|
|
88
|
+
</template>
|
|
89
|
+
<template x-if="totpVerifying">
|
|
90
|
+
<span class="flex items-center gap-2">
|
|
91
|
+
<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i>
|
|
92
|
+
Verifying...
|
|
93
|
+
</span>
|
|
94
|
+
</template>
|
|
95
|
+
</button>
|
|
96
|
+
</div>
|
|
97
|
+
<div x-show="totpError" x-cloak class="bg-red-50 border border-red-200 rounded-lg p-3 text-sm text-red-800 flex items-center gap-2">
|
|
98
|
+
<i data-lucide="alert-circle" class="w-4 h-4"></i>
|
|
99
|
+
<span x-text="totpError"></span>
|
|
100
|
+
</div>
|
|
101
|
+
<button
|
|
102
|
+
@click="cancelTotpSetup()"
|
|
103
|
+
class="text-sm text-gray-500 hover:text-gray-700 underline"
|
|
104
|
+
>
|
|
105
|
+
Cancel
|
|
106
|
+
</button>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<!-- Disable 2FA -->
|
|
112
|
+
<div x-show="totpEnabled && !showTotpSetup" x-cloak class="border-t border-gray-100 pt-6 mt-6">
|
|
113
|
+
<div x-show="!showDisableTotp">
|
|
114
|
+
<button
|
|
115
|
+
@click="showDisableTotp = true"
|
|
116
|
+
class="text-sm text-red-600 hover:text-red-700 font-medium flex items-center gap-2"
|
|
117
|
+
>
|
|
118
|
+
<i data-lucide="shield-off" class="w-4 h-4"></i>
|
|
119
|
+
Disable two-factor authentication
|
|
120
|
+
</button>
|
|
121
|
+
</div>
|
|
122
|
+
<div x-show="showDisableTotp" class="space-y-4">
|
|
123
|
+
<p class="text-sm text-gray-600">
|
|
124
|
+
Enter your current authenticator code to disable 2FA:
|
|
125
|
+
</p>
|
|
126
|
+
<div class="flex gap-4">
|
|
127
|
+
<input
|
|
128
|
+
type="text"
|
|
129
|
+
x-model="totpDisableCode"
|
|
130
|
+
inputmode="numeric"
|
|
131
|
+
pattern="[0-9]*"
|
|
132
|
+
maxlength="6"
|
|
133
|
+
placeholder="000000"
|
|
134
|
+
class="flex-grow bg-gray-50 border border-gray-200 rounded-lg px-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium tracking-widest text-center text-lg"
|
|
135
|
+
>
|
|
136
|
+
<button
|
|
137
|
+
@click="disableTotp()"
|
|
138
|
+
:disabled="totpDisableCode.length !== 6 || totpDisabling"
|
|
139
|
+
class="bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-6 rounded-lg transition-all flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
140
|
+
>
|
|
141
|
+
<template x-if="!totpDisabling">
|
|
142
|
+
<span>Disable 2FA</span>
|
|
143
|
+
</template>
|
|
144
|
+
<template x-if="totpDisabling">
|
|
145
|
+
<span class="flex items-center gap-2">
|
|
146
|
+
<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i>
|
|
147
|
+
Disabling...
|
|
148
|
+
</span>
|
|
149
|
+
</template>
|
|
150
|
+
</button>
|
|
151
|
+
</div>
|
|
152
|
+
<div x-show="totpError" x-cloak class="bg-red-50 border border-red-200 rounded-lg p-3 text-sm text-red-800 flex items-center gap-2">
|
|
153
|
+
<i data-lucide="alert-circle" class="w-4 h-4"></i>
|
|
154
|
+
<span x-text="totpError"></span>
|
|
155
|
+
</div>
|
|
156
|
+
<button
|
|
157
|
+
@click="showDisableTotp = false; totpDisableCode = ''; totpError = ''"
|
|
158
|
+
class="text-sm text-gray-500 hover:text-gray-700 underline"
|
|
159
|
+
>
|
|
160
|
+
Cancel
|
|
161
|
+
</button>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
`
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings tab component with configuration form
|
|
3
|
+
* @returns {string} HTML string
|
|
4
|
+
*/
|
|
5
|
+
export const SettingsTab = () => `
|
|
6
|
+
<div class="bg-white rounded-2xl border border-gray-100 shadow-[0_2px_8px_rgba(0,0,0,0.04)] p-10">
|
|
7
|
+
<h2 class="text-xl font-bold text-gray-900 mb-8 flex items-center gap-2">
|
|
8
|
+
<i data-lucide="sliders" class="w-5 h-5"></i>
|
|
9
|
+
Configuration
|
|
10
|
+
</h2>
|
|
11
|
+
<form @submit.prevent="saveConfig" class="space-y-8">
|
|
12
|
+
<div class="space-y-6">
|
|
13
|
+
<div>
|
|
14
|
+
<label class="block text-sm font-semibold text-gray-700 mb-2">Endpoint URL</label>
|
|
15
|
+
<input type="text" x-model="configForm.endpoint" class="w-full bg-gray-50 border border-gray-200 rounded-lg px-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium">
|
|
16
|
+
</div>
|
|
17
|
+
<div class="grid grid-cols-2 gap-6">
|
|
18
|
+
<div>
|
|
19
|
+
<label class="block text-sm font-semibold text-gray-700 mb-2">Bucket Name</label>
|
|
20
|
+
<input type="text" x-model="configForm.bucket" class="w-full bg-gray-50 border border-gray-200 rounded-lg px-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium">
|
|
21
|
+
</div>
|
|
22
|
+
<div>
|
|
23
|
+
<label class="block text-sm font-semibold text-gray-700 mb-2">Prefix (Folder)</label>
|
|
24
|
+
<input type="text" x-model="configForm.prefix" class="w-full bg-gray-50 border border-gray-200 rounded-lg px-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium">
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
<div>
|
|
28
|
+
<label class="block text-sm font-semibold text-gray-700 mb-2">Source Directory (Local)</label>
|
|
29
|
+
<input type="text" x-model="configForm.sourceDir" readonly class="w-full bg-gray-100 border border-gray-200 rounded-lg px-4 py-3 text-gray-500 cursor-not-allowed focus:outline-none font-medium">
|
|
30
|
+
<p class="text-xs text-gray-400 mt-1">Defined in server configuration</p>
|
|
31
|
+
</div>
|
|
32
|
+
<div>
|
|
33
|
+
<label class="block text-sm font-semibold text-gray-700 mb-2">Allowed Extensions (comma separated)</label>
|
|
34
|
+
<input type="text" x-model="configForm.extensions" placeholder=".db, .sqlite" class="w-full bg-gray-50 border border-gray-200 rounded-lg px-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium">
|
|
35
|
+
</div>
|
|
36
|
+
<div>
|
|
37
|
+
<label class="block text-sm font-semibold text-gray-700 mb-2">Cron Schedule</label>
|
|
38
|
+
<div class="flex gap-4 items-center">
|
|
39
|
+
<div class="relative flex-grow">
|
|
40
|
+
<input type="text" x-model="configForm.cronSchedule" placeholder="0 0 * * *" class="w-full bg-gray-50 border border-gray-200 rounded-lg px-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium">
|
|
41
|
+
<div class="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-gray-500">
|
|
42
|
+
<a href="https://crontab.guru/" target="_blank" class="underline hover:text-gray-800">Help</a>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="flex gap-2 shrink-0">
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
@click="configForm.cronEnabled = true; saveConfig()"
|
|
49
|
+
:class="configForm.cronEnabled !== false ? 'bg-green-600 text-white shadow-md' : 'bg-gray-100 text-gray-400 hover:bg-gray-200'"
|
|
50
|
+
class="px-4 py-3 rounded-lg font-bold text-sm transition-all flex items-center gap-2"
|
|
51
|
+
>
|
|
52
|
+
<i data-lucide="play" class="w-4 h-4"></i>
|
|
53
|
+
Start
|
|
54
|
+
</button>
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
@click="configForm.cronEnabled = false; saveConfig()"
|
|
58
|
+
:class="configForm.cronEnabled === false ? 'bg-red-600 text-white shadow-md' : 'bg-gray-100 text-gray-400 hover:bg-gray-200'"
|
|
59
|
+
class="px-4 py-3 rounded-lg font-bold text-sm transition-all flex items-center gap-2"
|
|
60
|
+
>
|
|
61
|
+
<i data-lucide="square" class="w-4 h-4"></i>
|
|
62
|
+
Stop
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<p class="text-xs text-gray-400 mt-1">Format: Minute Hour Day Month DayOfWeek (e.g., "0 0 * * *" for daily at midnight)</p>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="grid grid-cols-2 gap-6">
|
|
69
|
+
<div>
|
|
70
|
+
<label class="block text-sm font-semibold text-gray-700 mb-2">Access Key ID</label>
|
|
71
|
+
<input type="text" x-model="configForm.accessKeyId" class="w-full bg-gray-50 border border-gray-200 rounded-lg px-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium">
|
|
72
|
+
</div>
|
|
73
|
+
<div>
|
|
74
|
+
<label class="block text-sm font-semibold text-gray-700 mb-2">Secret Access Key</label>
|
|
75
|
+
<input type="password" x-model="configForm.secretAccessKey" class="w-full bg-gray-50 border border-gray-200 rounded-lg px-4 py-3 text-gray-900 focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-all font-medium">
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div class="pt-4 flex justify-end">
|
|
81
|
+
<button type="submit" class="bg-primary-500 hover:bg-primary-600 text-white font-bold py-3 px-8 rounded-xl transition-all shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 flex items-center gap-2">
|
|
82
|
+
<i data-lucide="save" class="w-4 h-4"></i>
|
|
83
|
+
Save Changes
|
|
84
|
+
</button>
|
|
85
|
+
</div>
|
|
86
|
+
</form>
|
|
87
|
+
</div>
|
|
88
|
+
`
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status cards component for dashboard overview
|
|
3
|
+
* @returns {string} HTML string
|
|
4
|
+
*/
|
|
5
|
+
export const StatusCards = () => `
|
|
6
|
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
7
|
+
<div class="bg-white p-8 rounded-2xl border border-gray-100 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
8
|
+
<div class="text-gray-500 text-sm font-medium mb-2 flex items-center gap-2">
|
|
9
|
+
<i data-lucide="folder" class="w-4 h-4"></i>
|
|
10
|
+
Local Source
|
|
11
|
+
</div>
|
|
12
|
+
<div class="text-gray-900 font-semibold truncate" x-text="config.sourceDir"></div>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="bg-white p-8 rounded-2xl border border-gray-100 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
15
|
+
<div class="text-gray-500 text-sm font-medium mb-2 flex items-center gap-2">
|
|
16
|
+
<i data-lucide="cloud" class="w-4 h-4"></i>
|
|
17
|
+
Target Bucket
|
|
18
|
+
</div>
|
|
19
|
+
<div class="text-gray-900 font-semibold truncate" x-text="config.bucket"></div>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="bg-white p-8 rounded-2xl border border-gray-100 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
22
|
+
<div class="text-gray-500 text-sm font-medium mb-2 flex items-center gap-2">
|
|
23
|
+
<i data-lucide="clock" class="w-4 h-4"></i>
|
|
24
|
+
Last Backup
|
|
25
|
+
</div>
|
|
26
|
+
<div class="text-gray-900 font-semibold" x-text="lastBackup || 'No backup recorded'"></div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="bg-white p-8 rounded-2xl border border-gray-100 shadow-[0_2px_8px_rgba(0,0,0,0.04)] relative overflow-hidden group">
|
|
29
|
+
<div class="text-gray-500 text-sm font-medium mb-3 flex items-center gap-2">
|
|
30
|
+
<i data-lucide="calendar-clock" class="w-4 h-4"></i>
|
|
31
|
+
Schedule Status
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<template x-if="cronStatus.isRunning">
|
|
35
|
+
<div class="flex items-center gap-3">
|
|
36
|
+
<span class="relative flex h-2.5 w-2.5 shrink-0" title="Active">
|
|
37
|
+
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
|
38
|
+
<span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-green-500"></span>
|
|
39
|
+
</span>
|
|
40
|
+
<div class="flex items-baseline gap-1.5 min-w-0">
|
|
41
|
+
<span class="text-gray-900 font-bold text-xs font-mono truncate" x-text="new Date(cronStatus.nextRun).toLocaleString(undefined, { dateStyle: 'short', timeStyle: 'medium' })"></span>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<template x-if="!cronStatus.isRunning">
|
|
47
|
+
<div class="flex items-center gap-3">
|
|
48
|
+
<span class="h-2.5 w-2.5 rounded-full bg-gray-300 shrink-0" title="Stopped"></span>
|
|
49
|
+
<span class="text-gray-400 text-sm italic">Scheduler paused</span>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
`
|