@sonicjs-cms/core 2.0.1 → 2.0.3
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/dist/{chunk-O46XKBFM.js → chunk-3LZ6TLPC.js} +14 -24
- package/dist/chunk-3LZ6TLPC.js.map +1 -0
- package/dist/{chunk-SRCY43RN.cjs → chunk-3NVJ6W27.cjs} +2 -2
- package/dist/chunk-3NVJ6W27.cjs.map +1 -0
- package/dist/{chunk-ALOS2CBJ.cjs → chunk-3SPQ3J4N.cjs} +14 -24
- package/dist/chunk-3SPQ3J4N.cjs.map +1 -0
- package/dist/chunk-4BJGEGX5.cjs +236 -0
- package/dist/chunk-4BJGEGX5.cjs.map +1 -0
- package/dist/{chunk-EGFHFM4N.cjs → chunk-5APKEYFK.cjs} +5 -5
- package/dist/{chunk-EGFHFM4N.cjs.map → chunk-5APKEYFK.cjs.map} +1 -1
- package/dist/{chunk-5FDDDD4J.cjs → chunk-5B3VMVEX.cjs} +559 -390
- package/dist/chunk-5B3VMVEX.cjs.map +1 -0
- package/dist/chunk-CDBVZEWR.js.map +1 -1
- package/dist/{chunk-BITQ4MFX.js → chunk-EAELJXRV.js} +93 -115
- package/dist/chunk-EAELJXRV.js.map +1 -0
- package/dist/chunk-FICTAGD4.js +59 -0
- package/dist/chunk-FICTAGD4.js.map +1 -0
- package/dist/{chunk-FVMV5DKA.cjs → chunk-HJZOA2O5.cjs} +93 -115
- package/dist/chunk-HJZOA2O5.cjs.map +1 -0
- package/dist/chunk-LEG4KNFP.cjs.map +1 -1
- package/dist/{chunk-5XTB4FE5.js → chunk-LH4Z7QID.js} +2 -2
- package/dist/chunk-LH4Z7QID.js.map +1 -0
- package/dist/chunk-M6FPVS7E.js +214 -0
- package/dist/chunk-M6FPVS7E.js.map +1 -0
- package/dist/{chunk-QSF34IYQ.js → chunk-PPUKPNTP.js} +401 -232
- package/dist/chunk-PPUKPNTP.js.map +1 -0
- package/dist/chunk-RCQ2HIQD.cjs +61 -0
- package/dist/chunk-RCQ2HIQD.cjs.map +1 -0
- package/dist/{chunk-P2PTTBO5.js → chunk-RYQCT2IV.js} +3 -3
- package/dist/{chunk-P2PTTBO5.js.map → chunk-RYQCT2IV.js.map} +1 -1
- package/dist/{chunk-NK6FN5R5.cjs → chunk-UL32L2KV.cjs} +3 -60
- package/dist/chunk-UL32L2KV.cjs.map +1 -0
- package/dist/{chunk-OL2OE3VJ.js → chunk-XJETEIRU.js} +4 -60
- package/dist/chunk-XJETEIRU.js.map +1 -0
- package/dist/index.cjs +145 -130
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +19 -15
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +26 -21
- package/dist/middleware.js +2 -1
- package/dist/plugins.cjs +7 -7
- package/dist/plugins.js +1 -1
- package/dist/routes.cjs +26 -25
- package/dist/routes.js +6 -5
- package/dist/services.cjs +7 -7
- package/dist/services.js +1 -1
- package/dist/templates.cjs +18 -18
- package/dist/templates.js +2 -2
- package/dist/utils.cjs +16 -15
- package/dist/utils.js +2 -1
- package/package.json +1 -1
- package/dist/chunk-5FDDDD4J.cjs.map +0 -1
- package/dist/chunk-5XTB4FE5.js.map +0 -1
- package/dist/chunk-ALOS2CBJ.cjs.map +0 -1
- package/dist/chunk-BITQ4MFX.js.map +0 -1
- package/dist/chunk-FVMV5DKA.cjs.map +0 -1
- package/dist/chunk-KM4AJFXI.cjs +0 -101
- package/dist/chunk-KM4AJFXI.cjs.map +0 -1
- package/dist/chunk-NK6FN5R5.cjs.map +0 -1
- package/dist/chunk-O46XKBFM.js.map +0 -1
- package/dist/chunk-OL2OE3VJ.js.map +0 -1
- package/dist/chunk-QSF34IYQ.js.map +0 -1
- package/dist/chunk-SRCY43RN.cjs.map +0 -1
- package/dist/chunk-TY3NHEBN.js +0 -80
- package/dist/chunk-TY3NHEBN.js.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { getCacheService, CACHE_CONFIGS, getLogger } from './chunk-
|
|
2
|
-
import { requireAuth, isPluginActive, requireRole, AuthManager, logActivity
|
|
1
|
+
import { getCacheService, CACHE_CONFIGS, getLogger } from './chunk-LH4Z7QID.js';
|
|
2
|
+
import { requireAuth, isPluginActive, requireRole, AuthManager, logActivity } from './chunk-M6FPVS7E.js';
|
|
3
3
|
import { PluginService, MigrationService } from './chunk-CDBVZEWR.js';
|
|
4
|
-
import { init_admin_layout_catalyst_template, renderDesignPage, renderCheckboxPage, renderFAQList, renderTestimonialsList, renderCodeExamplesList, renderAlert, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayoutCatalyst, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-
|
|
5
|
-
import { QueryFilterBuilder, sanitizeInput, getCoreVersion, escapeHtml } from './chunk-
|
|
4
|
+
import { init_admin_layout_catalyst_template, renderDesignPage, renderCheckboxPage, renderFAQList, renderTestimonialsList, renderCodeExamplesList, renderAlert, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayoutCatalyst, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-3LZ6TLPC.js';
|
|
5
|
+
import { QueryFilterBuilder, sanitizeInput, getCoreVersion, escapeHtml } from './chunk-XJETEIRU.js';
|
|
6
|
+
import { metricsTracker } from './chunk-FICTAGD4.js';
|
|
6
7
|
import { Hono } from 'hono';
|
|
7
8
|
import { cors } from 'hono/cors';
|
|
8
9
|
import { z } from 'zod';
|
|
@@ -2030,9 +2031,36 @@ function renderRegisterPage(data) {
|
|
|
2030
2031
|
</html>
|
|
2031
2032
|
`;
|
|
2032
2033
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2034
|
+
var authValidationService = {
|
|
2035
|
+
/**
|
|
2036
|
+
* Build registration schema dynamically based on auth settings
|
|
2037
|
+
* For now, returns a static schema with standard fields
|
|
2038
|
+
*/
|
|
2039
|
+
async buildRegistrationSchema(_db) {
|
|
2040
|
+
return z.object({
|
|
2041
|
+
email: z.string().email("Valid email is required"),
|
|
2042
|
+
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
2043
|
+
username: z.string().min(3, "Username must be at least 3 characters").optional(),
|
|
2044
|
+
firstName: z.string().min(1, "First name is required").optional(),
|
|
2045
|
+
lastName: z.string().min(1, "Last name is required").optional()
|
|
2046
|
+
});
|
|
2047
|
+
},
|
|
2048
|
+
/**
|
|
2049
|
+
* Generate default values for optional fields
|
|
2050
|
+
*/
|
|
2051
|
+
generateDefaultValue(field, data) {
|
|
2052
|
+
switch (field) {
|
|
2053
|
+
case "username":
|
|
2054
|
+
return data.email ? data.email.split("@")[0] : `user${Date.now()}`;
|
|
2055
|
+
case "firstName":
|
|
2056
|
+
return "User";
|
|
2057
|
+
case "lastName":
|
|
2058
|
+
return data.email ? data.email.split("@")[0] : "Account";
|
|
2059
|
+
default:
|
|
2060
|
+
return "";
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
};
|
|
2036
2064
|
|
|
2037
2065
|
// src/routes/auth.ts
|
|
2038
2066
|
var authRoutes = new Hono();
|
|
@@ -4704,6 +4732,7 @@ var isPluginActive2 = () => false;
|
|
|
4704
4732
|
|
|
4705
4733
|
// src/routes/admin-content.ts
|
|
4706
4734
|
var adminContentRoutes = new Hono();
|
|
4735
|
+
adminContentRoutes.use("*", requireAuth());
|
|
4707
4736
|
async function getCollectionFields(db, collectionId) {
|
|
4708
4737
|
const cache = getCacheService(CACHE_CONFIGS.collection);
|
|
4709
4738
|
return cache.getOrSet(
|
|
@@ -5836,170 +5865,157 @@ ${JSON.stringify(data, null, 2)}
|
|
|
5836
5865
|
var admin_content_default = adminContentRoutes;
|
|
5837
5866
|
|
|
5838
5867
|
// src/templates/pages/admin-profile.template.ts
|
|
5868
|
+
init_admin_layout_catalyst_template();
|
|
5839
5869
|
function renderProfilePage(data) {
|
|
5840
5870
|
const pageContent = `
|
|
5841
|
-
<div class="
|
|
5871
|
+
<div class="space-y-8">
|
|
5842
5872
|
<!-- Header -->
|
|
5843
|
-
<div class="
|
|
5873
|
+
<div class="sm:flex sm:items-center sm:justify-between">
|
|
5844
5874
|
<div>
|
|
5845
|
-
<h1 class="text-2xl font-semibold text-white">User Profile</h1>
|
|
5846
|
-
<p class="mt-2 text-sm text-
|
|
5875
|
+
<h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">User Profile</h1>
|
|
5876
|
+
<p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">
|
|
5877
|
+
Manage your account settings and preferences
|
|
5878
|
+
</p>
|
|
5847
5879
|
</div>
|
|
5848
5880
|
</div>
|
|
5849
5881
|
|
|
5850
|
-
<!-- Breadcrumb -->
|
|
5851
|
-
<nav class="flex mb-6" aria-label="Breadcrumb">
|
|
5852
|
-
<ol class="flex items-center space-x-3">
|
|
5853
|
-
<li>
|
|
5854
|
-
<a href="/admin" class="text-gray-300 hover:text-white transition-colors">
|
|
5855
|
-
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
|
|
5856
|
-
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
|
|
5857
|
-
</svg>
|
|
5858
|
-
</a>
|
|
5859
|
-
</li>
|
|
5860
|
-
<li class="flex items-center">
|
|
5861
|
-
<svg class="h-5 w-5 text-gray-400 mx-2" fill="currentColor" viewBox="0 0 20 20">
|
|
5862
|
-
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
|
|
5863
|
-
</svg>
|
|
5864
|
-
<span class="text-sm font-medium text-gray-200">Profile</span>
|
|
5865
|
-
</li>
|
|
5866
|
-
</ol>
|
|
5867
|
-
</nav>
|
|
5868
|
-
|
|
5869
5882
|
<!-- Alert Messages -->
|
|
5870
5883
|
${data.error ? renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
|
|
5871
5884
|
${data.success ? renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
|
|
5872
5885
|
|
|
5873
5886
|
<!-- Profile Form -->
|
|
5874
|
-
<div class="grid grid-cols-1 lg:grid-cols-3 gap-
|
|
5887
|
+
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
5875
5888
|
<!-- Main Profile Form -->
|
|
5876
5889
|
<div class="lg:col-span-2">
|
|
5877
|
-
<div class="
|
|
5890
|
+
<div class="rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10">
|
|
5878
5891
|
<!-- Form Header -->
|
|
5879
|
-
<div class="
|
|
5880
|
-
<div class="
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5892
|
+
<div class="px-6 py-5 border-b border-zinc-950/5 dark:border-white/5">
|
|
5893
|
+
<div class="flex items-center gap-x-3">
|
|
5894
|
+
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-zinc-950 dark:bg-white">
|
|
5895
|
+
<svg class="h-5 w-5 text-white dark:text-zinc-950" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
5884
5896
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
|
|
5885
5897
|
</svg>
|
|
5886
5898
|
</div>
|
|
5887
5899
|
<div>
|
|
5888
|
-
<h2 class="text-
|
|
5889
|
-
<p class="text-sm text-
|
|
5900
|
+
<h2 class="text-base font-semibold text-zinc-950 dark:text-white">Profile Information</h2>
|
|
5901
|
+
<p class="text-sm text-zinc-500 dark:text-zinc-400">Update your account details</p>
|
|
5890
5902
|
</div>
|
|
5891
5903
|
</div>
|
|
5892
5904
|
</div>
|
|
5893
5905
|
|
|
5894
5906
|
<!-- Form Content -->
|
|
5895
|
-
<form id="profile-form" hx-put="/admin/profile" hx-target="#form-messages" class="p-
|
|
5907
|
+
<form id="profile-form" hx-put="/admin/profile" hx-target="#form-messages" class="p-6 space-y-6">
|
|
5896
5908
|
<div id="form-messages"></div>
|
|
5897
5909
|
|
|
5898
5910
|
<!-- Basic Information -->
|
|
5899
5911
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
5900
5912
|
<div>
|
|
5901
|
-
<label class="block text-sm font-medium text-
|
|
5902
|
-
<input
|
|
5903
|
-
type="text"
|
|
5904
|
-
name="first_name"
|
|
5913
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">First Name</label>
|
|
5914
|
+
<input
|
|
5915
|
+
type="text"
|
|
5916
|
+
name="first_name"
|
|
5905
5917
|
value="${data.profile.first_name}"
|
|
5906
5918
|
required
|
|
5907
|
-
class="w-full px-
|
|
5919
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
5908
5920
|
placeholder="Enter your first name"
|
|
5909
5921
|
>
|
|
5910
5922
|
</div>
|
|
5911
5923
|
<div>
|
|
5912
|
-
<label class="block text-sm font-medium text-
|
|
5913
|
-
<input
|
|
5914
|
-
type="text"
|
|
5915
|
-
name="last_name"
|
|
5924
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Last Name</label>
|
|
5925
|
+
<input
|
|
5926
|
+
type="text"
|
|
5927
|
+
name="last_name"
|
|
5916
5928
|
value="${data.profile.last_name}"
|
|
5917
5929
|
required
|
|
5918
|
-
class="w-full px-
|
|
5930
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
5919
5931
|
placeholder="Enter your last name"
|
|
5920
5932
|
>
|
|
5921
5933
|
</div>
|
|
5922
5934
|
</div>
|
|
5923
5935
|
|
|
5924
5936
|
<div>
|
|
5925
|
-
<label class="block text-sm font-medium text-
|
|
5926
|
-
<input
|
|
5927
|
-
type="text"
|
|
5928
|
-
name="username"
|
|
5937
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Username</label>
|
|
5938
|
+
<input
|
|
5939
|
+
type="text"
|
|
5940
|
+
name="username"
|
|
5929
5941
|
value="${data.profile.username}"
|
|
5930
5942
|
required
|
|
5931
|
-
class="w-full px-
|
|
5943
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
5932
5944
|
placeholder="Enter your username"
|
|
5933
5945
|
>
|
|
5934
5946
|
</div>
|
|
5935
5947
|
|
|
5936
5948
|
<div>
|
|
5937
|
-
<label class="block text-sm font-medium text-
|
|
5938
|
-
<input
|
|
5939
|
-
type="email"
|
|
5940
|
-
name="email"
|
|
5949
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Email Address</label>
|
|
5950
|
+
<input
|
|
5951
|
+
type="email"
|
|
5952
|
+
name="email"
|
|
5941
5953
|
value="${data.profile.email}"
|
|
5942
5954
|
required
|
|
5943
|
-
class="w-full px-
|
|
5955
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
5944
5956
|
placeholder="Enter your email address"
|
|
5945
5957
|
>
|
|
5946
5958
|
</div>
|
|
5947
5959
|
|
|
5948
5960
|
<div>
|
|
5949
|
-
<label class="block text-sm font-medium text-
|
|
5950
|
-
<input
|
|
5951
|
-
type="tel"
|
|
5952
|
-
name="phone"
|
|
5961
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Phone Number</label>
|
|
5962
|
+
<input
|
|
5963
|
+
type="tel"
|
|
5964
|
+
name="phone"
|
|
5953
5965
|
value="${data.profile.phone || ""}"
|
|
5954
|
-
class="w-full px-
|
|
5966
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
5955
5967
|
placeholder="Enter your phone number"
|
|
5956
5968
|
>
|
|
5957
5969
|
</div>
|
|
5958
5970
|
|
|
5959
5971
|
<div>
|
|
5960
|
-
<label class="block text-sm font-medium text-
|
|
5961
|
-
<textarea
|
|
5962
|
-
name="bio"
|
|
5972
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Bio</label>
|
|
5973
|
+
<textarea
|
|
5974
|
+
name="bio"
|
|
5963
5975
|
rows="3"
|
|
5964
|
-
class="w-full px-
|
|
5976
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow resize-none"
|
|
5965
5977
|
placeholder="Tell us about yourself..."
|
|
5966
5978
|
>${data.profile.bio || ""}</textarea>
|
|
5967
5979
|
</div>
|
|
5968
5980
|
|
|
5969
5981
|
<!-- Preferences -->
|
|
5970
|
-
<div class="pt-6 border-t border-white/
|
|
5971
|
-
<h3 class="text-
|
|
5972
|
-
|
|
5982
|
+
<div class="pt-6 border-t border-zinc-950/5 dark:border-white/5">
|
|
5983
|
+
<h3 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Preferences</h3>
|
|
5984
|
+
|
|
5973
5985
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
5974
5986
|
<div>
|
|
5975
|
-
<label class="block text-sm font-medium text-
|
|
5976
|
-
<
|
|
5977
|
-
name="timezone"
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5987
|
+
<label for="timezone" class="block text-sm/6 font-medium text-zinc-950 dark:text-white mb-2">Timezone</label>
|
|
5988
|
+
<div class="grid grid-cols-1">
|
|
5989
|
+
<select id="timezone" name="timezone" class="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white/5 dark:bg-white/5 py-1.5 pl-3 pr-8 text-base text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-zinc-500/30 dark:outline-zinc-400/30 *:bg-white dark:*:bg-zinc-800 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-zinc-500 dark:focus-visible:outline-zinc-400 sm:text-sm/6">
|
|
5990
|
+
${data.timezones.map((tz) => `
|
|
5991
|
+
<option value="${tz.value}" ${tz.value === data.profile.timezone ? "selected" : ""}>${tz.label}</option>
|
|
5992
|
+
`).join("")}
|
|
5993
|
+
</select>
|
|
5994
|
+
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-zinc-600 dark:text-zinc-400 sm:size-4">
|
|
5995
|
+
<path d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd" />
|
|
5996
|
+
</svg>
|
|
5997
|
+
</div>
|
|
5984
5998
|
</div>
|
|
5985
5999
|
<div>
|
|
5986
|
-
<label class="block text-sm font-medium text-
|
|
5987
|
-
<
|
|
5988
|
-
name="language"
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
6000
|
+
<label for="language" class="block text-sm/6 font-medium text-zinc-950 dark:text-white mb-2">Language</label>
|
|
6001
|
+
<div class="grid grid-cols-1">
|
|
6002
|
+
<select id="language" name="language" class="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white/5 dark:bg-white/5 py-1.5 pl-3 pr-8 text-base text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-zinc-500/30 dark:outline-zinc-400/30 *:bg-white dark:*:bg-zinc-800 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-zinc-500 dark:focus-visible:outline-zinc-400 sm:text-sm/6">
|
|
6003
|
+
${data.languages.map((lang) => `
|
|
6004
|
+
<option value="${lang.value}" ${lang.value === data.profile.language ? "selected" : ""}>${lang.label}</option>
|
|
6005
|
+
`).join("")}
|
|
6006
|
+
</select>
|
|
6007
|
+
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-zinc-600 dark:text-zinc-400 sm:size-4">
|
|
6008
|
+
<path d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd" />
|
|
6009
|
+
</svg>
|
|
6010
|
+
</div>
|
|
5995
6011
|
</div>
|
|
5996
6012
|
</div>
|
|
5997
6013
|
</div>
|
|
5998
6014
|
|
|
5999
6015
|
<!-- Notifications -->
|
|
6000
|
-
<div class="pt-6 border-t border-white/
|
|
6001
|
-
<h3 class="text-
|
|
6002
|
-
|
|
6016
|
+
<div class="pt-6 border-t border-zinc-950/5 dark:border-white/5">
|
|
6017
|
+
<h3 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Notifications</h3>
|
|
6018
|
+
|
|
6003
6019
|
<div class="space-y-5">
|
|
6004
6020
|
<div class="flex gap-3">
|
|
6005
6021
|
<div class="flex h-6 shrink-0 items-center">
|
|
@@ -6010,27 +6026,30 @@ function renderProfilePage(data) {
|
|
|
6010
6026
|
name="email_notifications"
|
|
6011
6027
|
value="1"
|
|
6012
6028
|
${data.profile.email_notifications ? "checked" : ""}
|
|
6013
|
-
class="col-start-1 row-start-1 appearance-none rounded border border-zinc-950/10 dark:border-white/10 bg-white dark:bg-white/5 checked:border-indigo-500 checked:bg-indigo-500
|
|
6029
|
+
class="col-start-1 row-start-1 appearance-none rounded border border-zinc-950/10 dark:border-white/10 bg-white dark:bg-white/5 checked:border-indigo-500 checked:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500 disabled:border-zinc-950/5 dark:disabled:border-white/5 disabled:bg-zinc-950/10 dark:disabled:bg-white/10 forced-colors:appearance-auto"
|
|
6014
6030
|
/>
|
|
6015
6031
|
<svg viewBox="0 0 14 14" fill="none" class="pointer-events-none col-start-1 row-start-1 size-3.5 self-center justify-self-center stroke-white group-has-[:disabled]:stroke-zinc-950/25 dark:group-has-[:disabled]:stroke-white/25">
|
|
6016
6032
|
<path d="M3 8L6 11L11 3.5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="opacity-0 group-has-[:checked]:opacity-100" />
|
|
6017
|
-
<path d="M3 7H11" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="opacity-0 group-has-[:indeterminate]:opacity-100" />
|
|
6018
6033
|
</svg>
|
|
6019
6034
|
</div>
|
|
6020
6035
|
</div>
|
|
6021
6036
|
<div class="text-sm/6">
|
|
6022
6037
|
<label for="email_notifications" class="font-medium text-zinc-950 dark:text-white">Email notifications</label>
|
|
6038
|
+
<p class="text-zinc-500 dark:text-zinc-400">Receive email updates about new features and product announcements.</p>
|
|
6023
6039
|
</div>
|
|
6024
6040
|
</div>
|
|
6025
6041
|
</div>
|
|
6026
6042
|
</div>
|
|
6027
6043
|
|
|
6028
6044
|
<!-- Submit Button -->
|
|
6029
|
-
<div class="pt-6 border-t border-white/
|
|
6030
|
-
<button
|
|
6045
|
+
<div class="pt-6 border-t border-zinc-950/5 dark:border-white/5">
|
|
6046
|
+
<button
|
|
6031
6047
|
type="submit"
|
|
6032
|
-
class="
|
|
6048
|
+
class="inline-flex justify-center items-center gap-x-2 rounded-lg bg-zinc-950 dark:bg-white px-4 py-2.5 text-sm font-semibold text-white dark:text-zinc-950 hover:bg-zinc-800 dark:hover:bg-zinc-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-950 dark:focus-visible:outline-white transition-colors"
|
|
6033
6049
|
>
|
|
6050
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
6051
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
|
6052
|
+
</svg>
|
|
6034
6053
|
Update Profile
|
|
6035
6054
|
</button>
|
|
6036
6055
|
</div>
|
|
@@ -6041,86 +6060,96 @@ function renderProfilePage(data) {
|
|
|
6041
6060
|
<!-- Profile Sidebar -->
|
|
6042
6061
|
<div class="lg:col-span-1 space-y-6">
|
|
6043
6062
|
<!-- Avatar -->
|
|
6044
|
-
<div class="
|
|
6045
|
-
<h3 class="text-
|
|
6046
|
-
|
|
6063
|
+
<div class="rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 p-6">
|
|
6064
|
+
<h3 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Profile Picture</h3>
|
|
6065
|
+
|
|
6047
6066
|
<div class="text-center">
|
|
6048
|
-
<div class="w-24 h-24 rounded-full mx-auto mb-4 overflow-hidden bg-gradient-to-br from-
|
|
6067
|
+
<div class="w-24 h-24 rounded-full mx-auto mb-4 overflow-hidden bg-gradient-to-br from-cyan-400 to-purple-400 flex items-center justify-center ring-4 ring-zinc-950/5 dark:ring-white/10">
|
|
6049
6068
|
${data.profile.avatar_url ? `<img src="${data.profile.avatar_url}" alt="Profile picture" class="w-full h-full object-cover">` : `<span class="text-2xl font-bold text-white">${data.profile.first_name.charAt(0)}${data.profile.last_name.charAt(0)}</span>`}
|
|
6050
6069
|
</div>
|
|
6051
|
-
|
|
6070
|
+
|
|
6052
6071
|
<form id="avatar-form" hx-post="/admin/profile/avatar" hx-target="#avatar-messages" hx-encoding="multipart/form-data">
|
|
6053
|
-
<input
|
|
6054
|
-
type="file"
|
|
6055
|
-
name="avatar"
|
|
6072
|
+
<input
|
|
6073
|
+
type="file"
|
|
6074
|
+
name="avatar"
|
|
6056
6075
|
accept="image/*"
|
|
6057
6076
|
class="hidden"
|
|
6058
6077
|
id="avatar-input"
|
|
6059
6078
|
onchange="document.getElementById('avatar-form').dispatchEvent(new Event('submit'))"
|
|
6060
6079
|
>
|
|
6061
|
-
<label
|
|
6080
|
+
<label
|
|
6062
6081
|
for="avatar-input"
|
|
6063
|
-
class="inline-
|
|
6082
|
+
class="inline-flex items-center gap-x-2 rounded-lg bg-white dark:bg-zinc-800 px-4 py-2.5 text-sm font-semibold text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors cursor-pointer"
|
|
6064
6083
|
>
|
|
6084
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
6085
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"/>
|
|
6086
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
6087
|
+
</svg>
|
|
6065
6088
|
Change Picture
|
|
6066
6089
|
</label>
|
|
6067
6090
|
</form>
|
|
6068
|
-
|
|
6091
|
+
|
|
6069
6092
|
<div id="avatar-messages" class="mt-3"></div>
|
|
6070
6093
|
</div>
|
|
6071
6094
|
</div>
|
|
6072
6095
|
|
|
6073
6096
|
<!-- Account Info -->
|
|
6074
|
-
<div class="
|
|
6075
|
-
<h3 class="text-
|
|
6076
|
-
|
|
6077
|
-
<
|
|
6097
|
+
<div class="rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 p-6">
|
|
6098
|
+
<h3 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Account Information</h3>
|
|
6099
|
+
|
|
6100
|
+
<dl class="space-y-3 text-sm">
|
|
6078
6101
|
<div>
|
|
6079
|
-
<
|
|
6080
|
-
<
|
|
6102
|
+
<dt class="text-zinc-500 dark:text-zinc-400">Role</dt>
|
|
6103
|
+
<dd class="mt-1">
|
|
6104
|
+
<span class="inline-flex items-center rounded-md bg-blue-50 dark:bg-blue-500/10 px-2 py-1 text-xs font-medium text-blue-700 dark:text-blue-400 ring-1 ring-inset ring-blue-700/10 dark:ring-blue-400/20 capitalize">
|
|
6105
|
+
${data.profile.role}
|
|
6106
|
+
</span>
|
|
6107
|
+
</dd>
|
|
6081
6108
|
</div>
|
|
6082
6109
|
<div>
|
|
6083
|
-
<
|
|
6084
|
-
<
|
|
6110
|
+
<dt class="text-zinc-500 dark:text-zinc-400">Member Since</dt>
|
|
6111
|
+
<dd class="mt-1 text-zinc-950 dark:text-white">${new Date(data.profile.created_at).toLocaleDateString()}</dd>
|
|
6085
6112
|
</div>
|
|
6086
6113
|
${data.profile.last_login_at ? `
|
|
6087
6114
|
<div>
|
|
6088
|
-
<
|
|
6089
|
-
<
|
|
6115
|
+
<dt class="text-zinc-500 dark:text-zinc-400">Last Login</dt>
|
|
6116
|
+
<dd class="mt-1 text-zinc-950 dark:text-white">${new Date(data.profile.last_login_at).toLocaleDateString()}</dd>
|
|
6090
6117
|
</div>
|
|
6091
6118
|
` : ""}
|
|
6092
6119
|
<div>
|
|
6093
|
-
<
|
|
6094
|
-
<
|
|
6120
|
+
<dt class="text-zinc-500 dark:text-zinc-400">Two-Factor Auth</dt>
|
|
6121
|
+
<dd class="mt-1">
|
|
6122
|
+
${data.profile.two_factor_enabled ? '<span class="inline-flex items-center rounded-md bg-green-50 dark:bg-green-500/10 px-2 py-1 text-xs font-medium text-green-700 dark:text-green-400 ring-1 ring-inset ring-green-600/20 dark:ring-green-500/20">Enabled</span>' : '<span class="inline-flex items-center rounded-md bg-zinc-50 dark:bg-zinc-800 px-2 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-400 ring-1 ring-inset ring-zinc-500/10 dark:ring-zinc-400/20">Disabled</span>'}
|
|
6123
|
+
</dd>
|
|
6095
6124
|
</div>
|
|
6096
|
-
</
|
|
6125
|
+
</dl>
|
|
6097
6126
|
</div>
|
|
6098
6127
|
|
|
6099
6128
|
<!-- Security Actions -->
|
|
6100
|
-
<div class="
|
|
6101
|
-
<h3 class="text-
|
|
6102
|
-
|
|
6103
|
-
<div class="space-y-
|
|
6104
|
-
<button
|
|
6129
|
+
<div class="rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 p-6">
|
|
6130
|
+
<h3 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Security</h3>
|
|
6131
|
+
|
|
6132
|
+
<div class="space-y-2">
|
|
6133
|
+
<button
|
|
6105
6134
|
type="button"
|
|
6106
6135
|
onclick="showChangePasswordModal()"
|
|
6107
|
-
class="w-full text-left px-3 py-2 text-sm text-
|
|
6136
|
+
class="w-full text-left flex items-center gap-x-3 px-3 py-2 text-sm text-zinc-950 dark:text-white hover:bg-zinc-50 dark:hover:bg-zinc-800/50 rounded-lg transition-colors"
|
|
6108
6137
|
>
|
|
6109
|
-
<svg class="w-4 h-4
|
|
6138
|
+
<svg class="w-4 h-4 text-zinc-500 dark:text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
6110
6139
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-3.586l4.293-4.293A6 6 0 0119 9z"/>
|
|
6111
6140
|
</svg>
|
|
6112
|
-
Change Password
|
|
6141
|
+
<span class="font-medium">Change Password</span>
|
|
6113
6142
|
</button>
|
|
6114
|
-
|
|
6115
|
-
<button
|
|
6143
|
+
|
|
6144
|
+
<button
|
|
6116
6145
|
type="button"
|
|
6117
6146
|
onclick="toggle2FA()"
|
|
6118
|
-
class="w-full text-left px-3 py-2 text-sm text-
|
|
6147
|
+
class="w-full text-left flex items-center gap-x-3 px-3 py-2 text-sm text-zinc-950 dark:text-white hover:bg-zinc-50 dark:hover:bg-zinc-800/50 rounded-lg transition-colors"
|
|
6119
6148
|
>
|
|
6120
|
-
<svg class="w-4 h-4
|
|
6149
|
+
<svg class="w-4 h-4 text-zinc-500 dark:text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
6121
6150
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
|
|
6122
6151
|
</svg>
|
|
6123
|
-
|
|
6152
|
+
<span class="font-medium">${data.profile.two_factor_enabled ? "Disable" : "Enable"} 2FA</span>
|
|
6124
6153
|
</button>
|
|
6125
6154
|
</div>
|
|
6126
6155
|
</div>
|
|
@@ -6129,67 +6158,73 @@ function renderProfilePage(data) {
|
|
|
6129
6158
|
</div>
|
|
6130
6159
|
|
|
6131
6160
|
<!-- Change Password Modal -->
|
|
6132
|
-
<div id="password-modal" class="fixed inset-0 bg-
|
|
6133
|
-
<div class="
|
|
6134
|
-
<div class="
|
|
6135
|
-
<div class="
|
|
6136
|
-
|
|
6137
|
-
<
|
|
6138
|
-
|
|
6139
|
-
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
6161
|
+
<div id="password-modal" class="fixed inset-0 bg-zinc-950/50 backdrop-blur-sm flex items-center justify-center z-50 hidden">
|
|
6162
|
+
<div class="rounded-xl bg-white dark:bg-zinc-900 shadow-2xl ring-1 ring-zinc-950/5 dark:ring-white/10 w-full max-w-md mx-4">
|
|
6163
|
+
<div class="px-6 py-5 border-b border-zinc-950/5 dark:border-white/5">
|
|
6164
|
+
<div class="flex items-center justify-between">
|
|
6165
|
+
<h3 class="text-base font-semibold text-zinc-950 dark:text-white">Change Password</h3>
|
|
6166
|
+
<button onclick="closePasswordModal()" class="text-zinc-500 dark:text-zinc-400 hover:text-zinc-950 dark:hover:text-white transition-colors">
|
|
6167
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
6140
6168
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
6141
6169
|
</svg>
|
|
6142
6170
|
</button>
|
|
6143
6171
|
</div>
|
|
6144
6172
|
</div>
|
|
6145
|
-
|
|
6173
|
+
|
|
6146
6174
|
<form id="password-form" hx-post="/admin/profile/password" hx-target="#password-messages" class="p-6 space-y-4">
|
|
6147
6175
|
<div id="password-messages"></div>
|
|
6148
|
-
|
|
6176
|
+
|
|
6149
6177
|
<div>
|
|
6150
|
-
<label class="block text-sm font-medium text-
|
|
6151
|
-
<input
|
|
6152
|
-
type="password"
|
|
6153
|
-
name="current_password"
|
|
6178
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Current Password</label>
|
|
6179
|
+
<input
|
|
6180
|
+
type="password"
|
|
6181
|
+
name="current_password"
|
|
6154
6182
|
required
|
|
6155
|
-
class="w-full px-
|
|
6183
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
6184
|
+
placeholder="Enter current password"
|
|
6156
6185
|
>
|
|
6157
6186
|
</div>
|
|
6158
6187
|
|
|
6159
6188
|
<div>
|
|
6160
|
-
<label class="block text-sm font-medium text-
|
|
6161
|
-
<input
|
|
6162
|
-
type="password"
|
|
6163
|
-
name="new_password"
|
|
6189
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">New Password</label>
|
|
6190
|
+
<input
|
|
6191
|
+
type="password"
|
|
6192
|
+
name="new_password"
|
|
6164
6193
|
required
|
|
6165
6194
|
minlength="8"
|
|
6166
|
-
class="w-full px-
|
|
6195
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
6196
|
+
placeholder="Enter new password"
|
|
6167
6197
|
>
|
|
6198
|
+
<p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">Must be at least 8 characters</p>
|
|
6168
6199
|
</div>
|
|
6169
6200
|
|
|
6170
6201
|
<div>
|
|
6171
|
-
<label class="block text-sm font-medium text-
|
|
6172
|
-
<input
|
|
6173
|
-
type="password"
|
|
6174
|
-
name="confirm_password"
|
|
6202
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Confirm New Password</label>
|
|
6203
|
+
<input
|
|
6204
|
+
type="password"
|
|
6205
|
+
name="confirm_password"
|
|
6175
6206
|
required
|
|
6176
6207
|
minlength="8"
|
|
6177
|
-
class="w-full px-
|
|
6208
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
6209
|
+
placeholder="Confirm new password"
|
|
6178
6210
|
>
|
|
6179
6211
|
</div>
|
|
6180
6212
|
|
|
6181
|
-
<div class="flex justify-end
|
|
6182
|
-
<button
|
|
6183
|
-
type="button"
|
|
6213
|
+
<div class="flex justify-end gap-x-3 pt-4 border-t border-zinc-950/5 dark:border-white/5">
|
|
6214
|
+
<button
|
|
6215
|
+
type="button"
|
|
6184
6216
|
onclick="closePasswordModal()"
|
|
6185
|
-
class="px-4 py-2
|
|
6217
|
+
class="rounded-lg bg-white dark:bg-zinc-800 px-4 py-2.5 text-sm font-semibold text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors"
|
|
6186
6218
|
>
|
|
6187
6219
|
Cancel
|
|
6188
6220
|
</button>
|
|
6189
|
-
<button
|
|
6221
|
+
<button
|
|
6190
6222
|
type="submit"
|
|
6191
|
-
class="
|
|
6223
|
+
class="inline-flex items-center gap-x-2 rounded-lg bg-zinc-950 dark:bg-white px-4 py-2.5 text-sm font-semibold text-white dark:text-zinc-950 hover:bg-zinc-800 dark:hover:bg-zinc-100 transition-colors"
|
|
6192
6224
|
>
|
|
6225
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
6226
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
|
6227
|
+
</svg>
|
|
6193
6228
|
Update Password
|
|
6194
6229
|
</button>
|
|
6195
6230
|
</div>
|
|
@@ -6232,9 +6267,10 @@ function renderProfilePage(data) {
|
|
|
6232
6267
|
pageTitle: "Profile",
|
|
6233
6268
|
currentPath: "/admin/profile",
|
|
6234
6269
|
user: data.user,
|
|
6270
|
+
version: data.version,
|
|
6235
6271
|
content: pageContent
|
|
6236
6272
|
};
|
|
6237
|
-
return
|
|
6273
|
+
return renderAdminLayoutCatalyst(layoutData);
|
|
6238
6274
|
}
|
|
6239
6275
|
|
|
6240
6276
|
// src/templates/components/alert.template.ts
|
|
@@ -7653,6 +7689,9 @@ function renderUsersListPage(data) {
|
|
|
7653
7689
|
// src/routes/admin-users.ts
|
|
7654
7690
|
var userRoutes = new Hono();
|
|
7655
7691
|
userRoutes.use("*", requireAuth());
|
|
7692
|
+
userRoutes.get("/", (c) => {
|
|
7693
|
+
return c.redirect("/admin/dashboard");
|
|
7694
|
+
});
|
|
7656
7695
|
var TIMEZONES = [
|
|
7657
7696
|
{ value: "UTC", label: "UTC" },
|
|
7658
7697
|
{ value: "America/New_York", label: "Eastern Time" },
|
|
@@ -7975,7 +8014,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
7975
8014
|
}));
|
|
7976
8015
|
}
|
|
7977
8016
|
});
|
|
7978
|
-
userRoutes.get("/users",
|
|
8017
|
+
userRoutes.get("/users", async (c) => {
|
|
7979
8018
|
const db = c.env.DB;
|
|
7980
8019
|
const user = c.get("user");
|
|
7981
8020
|
try {
|
|
@@ -8094,7 +8133,7 @@ userRoutes.get("/users", requirePermission("users.read"), async (c) => {
|
|
|
8094
8133
|
}), 500);
|
|
8095
8134
|
}
|
|
8096
8135
|
});
|
|
8097
|
-
userRoutes.get("/users/new",
|
|
8136
|
+
userRoutes.get("/users/new", async (c) => {
|
|
8098
8137
|
const user = c.get("user");
|
|
8099
8138
|
try {
|
|
8100
8139
|
const pageData = {
|
|
@@ -8115,7 +8154,7 @@ userRoutes.get("/users/new", requirePermission("users.create"), async (c) => {
|
|
|
8115
8154
|
}), 500);
|
|
8116
8155
|
}
|
|
8117
8156
|
});
|
|
8118
|
-
userRoutes.post("/users/new",
|
|
8157
|
+
userRoutes.post("/users/new", async (c) => {
|
|
8119
8158
|
const db = c.env.DB;
|
|
8120
8159
|
const user = c.get("user");
|
|
8121
8160
|
try {
|
|
@@ -8215,7 +8254,7 @@ userRoutes.post("/users/new", requirePermission("users.create"), async (c) => {
|
|
|
8215
8254
|
}));
|
|
8216
8255
|
}
|
|
8217
8256
|
});
|
|
8218
|
-
userRoutes.get("/users/:id",
|
|
8257
|
+
userRoutes.get("/users/:id", async (c) => {
|
|
8219
8258
|
if (c.req.path.endsWith("/edit")) {
|
|
8220
8259
|
return c.notFound();
|
|
8221
8260
|
}
|
|
@@ -8266,7 +8305,7 @@ userRoutes.get("/users/:id", requirePermission("users.read"), async (c) => {
|
|
|
8266
8305
|
return c.json({ error: "Failed to fetch user" }, 500);
|
|
8267
8306
|
}
|
|
8268
8307
|
});
|
|
8269
|
-
userRoutes.get("/users/:id/edit",
|
|
8308
|
+
userRoutes.get("/users/:id/edit", async (c) => {
|
|
8270
8309
|
const db = c.env.DB;
|
|
8271
8310
|
const user = c.get("user");
|
|
8272
8311
|
const userId = c.req.param("id");
|
|
@@ -8320,7 +8359,7 @@ userRoutes.get("/users/:id/edit", requirePermission("users.update"), async (c) =
|
|
|
8320
8359
|
}), 500);
|
|
8321
8360
|
}
|
|
8322
8361
|
});
|
|
8323
|
-
userRoutes.put("/users/:id",
|
|
8362
|
+
userRoutes.put("/users/:id", async (c) => {
|
|
8324
8363
|
const db = c.env.DB;
|
|
8325
8364
|
const user = c.get("user");
|
|
8326
8365
|
const userId = c.req.param("id");
|
|
@@ -8406,7 +8445,7 @@ userRoutes.put("/users/:id", requirePermission("users.update"), async (c) => {
|
|
|
8406
8445
|
}));
|
|
8407
8446
|
}
|
|
8408
8447
|
});
|
|
8409
|
-
userRoutes.delete("/users/:id",
|
|
8448
|
+
userRoutes.delete("/users/:id", async (c) => {
|
|
8410
8449
|
const db = c.env.DB;
|
|
8411
8450
|
const user = c.get("user");
|
|
8412
8451
|
const userId = c.req.param("id");
|
|
@@ -8467,7 +8506,7 @@ userRoutes.delete("/users/:id", requirePermission("users.delete"), async (c) =>
|
|
|
8467
8506
|
return c.json({ error: "Failed to delete user" }, 500);
|
|
8468
8507
|
}
|
|
8469
8508
|
});
|
|
8470
|
-
userRoutes.post("/invite-user",
|
|
8509
|
+
userRoutes.post("/invite-user", async (c) => {
|
|
8471
8510
|
const db = c.env.DB;
|
|
8472
8511
|
const user = c.get("user");
|
|
8473
8512
|
try {
|
|
@@ -8543,7 +8582,7 @@ userRoutes.post("/invite-user", requirePermission("users.create"), async (c) =>
|
|
|
8543
8582
|
return c.json({ error: "Failed to send user invitation" }, 500);
|
|
8544
8583
|
}
|
|
8545
8584
|
});
|
|
8546
|
-
userRoutes.post("/resend-invitation/:id",
|
|
8585
|
+
userRoutes.post("/resend-invitation/:id", async (c) => {
|
|
8547
8586
|
const db = c.env.DB;
|
|
8548
8587
|
const user = c.get("user");
|
|
8549
8588
|
const userId = c.req.param("id");
|
|
@@ -8592,7 +8631,7 @@ userRoutes.post("/resend-invitation/:id", requirePermission("users.create"), asy
|
|
|
8592
8631
|
return c.json({ error: "Failed to resend invitation" }, 500);
|
|
8593
8632
|
}
|
|
8594
8633
|
});
|
|
8595
|
-
userRoutes.delete("/cancel-invitation/:id",
|
|
8634
|
+
userRoutes.delete("/cancel-invitation/:id", async (c) => {
|
|
8596
8635
|
const db = c.env.DB;
|
|
8597
8636
|
const user = c.get("user");
|
|
8598
8637
|
const userId = c.req.param("id");
|
|
@@ -8626,7 +8665,7 @@ userRoutes.delete("/cancel-invitation/:id", requirePermission("users.delete"), a
|
|
|
8626
8665
|
return c.json({ error: "Failed to cancel invitation" }, 500);
|
|
8627
8666
|
}
|
|
8628
8667
|
});
|
|
8629
|
-
userRoutes.get("/activity-logs",
|
|
8668
|
+
userRoutes.get("/activity-logs", async (c) => {
|
|
8630
8669
|
const db = c.env.DB;
|
|
8631
8670
|
const user = c.get("user");
|
|
8632
8671
|
try {
|
|
@@ -8732,7 +8771,7 @@ userRoutes.get("/activity-logs", requirePermission("activity.read"), async (c) =
|
|
|
8732
8771
|
return c.html(renderActivityLogsPage(pageData));
|
|
8733
8772
|
}
|
|
8734
8773
|
});
|
|
8735
|
-
userRoutes.get("/activity-logs/export",
|
|
8774
|
+
userRoutes.get("/activity-logs/export", async (c) => {
|
|
8736
8775
|
const db = c.env.DB;
|
|
8737
8776
|
const user = c.get("user");
|
|
8738
8777
|
try {
|
|
@@ -10133,6 +10172,7 @@ var fileValidationSchema2 = z.object({
|
|
|
10133
10172
|
// 50MB max
|
|
10134
10173
|
});
|
|
10135
10174
|
var adminMediaRoutes = new Hono();
|
|
10175
|
+
adminMediaRoutes.use("*", requireAuth());
|
|
10136
10176
|
adminMediaRoutes.get("/", async (c) => {
|
|
10137
10177
|
try {
|
|
10138
10178
|
const user = c.get("user");
|
|
@@ -12152,6 +12192,7 @@ function formatTimestamp(timestamp) {
|
|
|
12152
12192
|
|
|
12153
12193
|
// src/routes/admin-plugins.ts
|
|
12154
12194
|
var adminPluginRoutes = new Hono();
|
|
12195
|
+
adminPluginRoutes.use("*", requireAuth());
|
|
12155
12196
|
adminPluginRoutes.get("/", async (c) => {
|
|
12156
12197
|
try {
|
|
12157
12198
|
const user = c.get("user");
|
|
@@ -13261,6 +13302,7 @@ function renderLogConfigPage(data) {
|
|
|
13261
13302
|
|
|
13262
13303
|
// src/routes/admin-logs.ts
|
|
13263
13304
|
var adminLogsRoutes = new Hono();
|
|
13305
|
+
adminLogsRoutes.use("*", requireAuth());
|
|
13264
13306
|
adminLogsRoutes.get("/", async (c) => {
|
|
13265
13307
|
try {
|
|
13266
13308
|
const user = c.get("user");
|
|
@@ -15743,7 +15785,7 @@ function renderAnalyticsChart() {
|
|
|
15743
15785
|
|
|
15744
15786
|
<!-- Hidden div to trigger HTMX polling -->
|
|
15745
15787
|
<div
|
|
15746
|
-
hx-get="/admin/api/metrics"
|
|
15788
|
+
hx-get="/admin/dashboard/api/metrics"
|
|
15747
15789
|
hx-trigger="every 1s"
|
|
15748
15790
|
hx-swap="none"
|
|
15749
15791
|
style="display: none;"
|
|
@@ -15846,9 +15888,12 @@ function renderAnalyticsChart() {
|
|
|
15846
15888
|
|
|
15847
15889
|
// Listen for metrics updates from HTMX
|
|
15848
15890
|
window.addEventListener('htmx:afterRequest', function(event) {
|
|
15849
|
-
|
|
15891
|
+
console.log('[Dashboard] HTMX request completed:', event.detail.pathInfo.requestPath);
|
|
15892
|
+
|
|
15893
|
+
if (event.detail.pathInfo.requestPath === '/admin/dashboard/api/metrics') {
|
|
15850
15894
|
try {
|
|
15851
15895
|
const metrics = JSON.parse(event.detail.xhr.responseText);
|
|
15896
|
+
console.log('[Dashboard] Metrics received:', metrics);
|
|
15852
15897
|
|
|
15853
15898
|
// Update current RPS display
|
|
15854
15899
|
const rpsElement = document.getElementById('current-rps');
|
|
@@ -15869,8 +15914,9 @@ function renderAnalyticsChart() {
|
|
|
15869
15914
|
chart.data.labels = newLabels;
|
|
15870
15915
|
|
|
15871
15916
|
chart.update('none'); // Update without animation for smoother real-time updates
|
|
15917
|
+
console.log('[Dashboard] Chart updated with RPS:', metrics.requestsPerSecond);
|
|
15872
15918
|
} catch (e) {
|
|
15873
|
-
console.error('Error updating metrics:', e);
|
|
15919
|
+
console.error('[Dashboard] Error updating metrics:', e);
|
|
15874
15920
|
}
|
|
15875
15921
|
}
|
|
15876
15922
|
});
|
|
@@ -16217,7 +16263,7 @@ router.get("/", async (c) => {
|
|
|
16217
16263
|
return c.html(renderDashboardPage(pageData));
|
|
16218
16264
|
}
|
|
16219
16265
|
});
|
|
16220
|
-
router.get("/
|
|
16266
|
+
router.get("/stats", async (c) => {
|
|
16221
16267
|
try {
|
|
16222
16268
|
const db = c.env.DB;
|
|
16223
16269
|
let collectionsCount = 0;
|
|
@@ -16267,7 +16313,7 @@ router.get("/dashboard/stats", async (c) => {
|
|
|
16267
16313
|
return c.html('<div class="text-red-500">Failed to load statistics</div>');
|
|
16268
16314
|
}
|
|
16269
16315
|
});
|
|
16270
|
-
router.get("/
|
|
16316
|
+
router.get("/storage", async (c) => {
|
|
16271
16317
|
try {
|
|
16272
16318
|
const db = c.env.DB;
|
|
16273
16319
|
let databaseSize = 0;
|
|
@@ -16292,7 +16338,7 @@ router.get("/dashboard/storage", async (c) => {
|
|
|
16292
16338
|
return c.html('<div class="text-red-500">Failed to load storage information</div>');
|
|
16293
16339
|
}
|
|
16294
16340
|
});
|
|
16295
|
-
router.get("/
|
|
16341
|
+
router.get("/recent-activity", async (c) => {
|
|
16296
16342
|
try {
|
|
16297
16343
|
const db = c.env.DB;
|
|
16298
16344
|
const limit = parseInt(c.req.query("limit") || "5");
|
|
@@ -16344,25 +16390,14 @@ router.get("/dashboard/recent-activity", async (c) => {
|
|
|
16344
16390
|
}
|
|
16345
16391
|
});
|
|
16346
16392
|
router.get("/api/metrics", async (c) => {
|
|
16347
|
-
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
16354
|
-
});
|
|
16355
|
-
} catch (error) {
|
|
16356
|
-
console.error("Error fetching metrics:", error);
|
|
16357
|
-
return c.json({
|
|
16358
|
-
requestsPerSecond: 0,
|
|
16359
|
-
totalRequests: 0,
|
|
16360
|
-
averageRPS: 0,
|
|
16361
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
16362
|
-
});
|
|
16363
|
-
}
|
|
16393
|
+
return c.json({
|
|
16394
|
+
requestsPerSecond: metricsTracker.getRequestsPerSecond(),
|
|
16395
|
+
totalRequests: metricsTracker.getTotalRequests(),
|
|
16396
|
+
averageRPS: Number(metricsTracker.getAverageRPS().toFixed(2)),
|
|
16397
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
16398
|
+
});
|
|
16364
16399
|
});
|
|
16365
|
-
router.get("/
|
|
16400
|
+
router.get("/system-status", async (c) => {
|
|
16366
16401
|
try {
|
|
16367
16402
|
const html9 = `
|
|
16368
16403
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
@@ -17683,7 +17718,8 @@ function renderCollectionFormPage(data) {
|
|
|
17683
17718
|
|
|
17684
17719
|
// src/routes/admin-collections.ts
|
|
17685
17720
|
var adminCollectionsRoutes = new Hono();
|
|
17686
|
-
adminCollectionsRoutes.
|
|
17721
|
+
adminCollectionsRoutes.use("*", requireAuth());
|
|
17722
|
+
adminCollectionsRoutes.get("/", async (c) => {
|
|
17687
17723
|
try {
|
|
17688
17724
|
const user = c.get("user");
|
|
17689
17725
|
const db = c.env.DB;
|
|
@@ -17738,7 +17774,7 @@ adminCollectionsRoutes.get("/collections", async (c) => {
|
|
|
17738
17774
|
return c.html(html`<p>Error loading collections</p>`);
|
|
17739
17775
|
}
|
|
17740
17776
|
});
|
|
17741
|
-
adminCollectionsRoutes.get("/
|
|
17777
|
+
adminCollectionsRoutes.get("/new", (c) => {
|
|
17742
17778
|
const user = c.get("user");
|
|
17743
17779
|
const formData = {
|
|
17744
17780
|
isEdit: false,
|
|
@@ -17751,7 +17787,7 @@ adminCollectionsRoutes.get("/collections/new", (c) => {
|
|
|
17751
17787
|
};
|
|
17752
17788
|
return c.html(renderCollectionFormPage(formData));
|
|
17753
17789
|
});
|
|
17754
|
-
adminCollectionsRoutes.post("/
|
|
17790
|
+
adminCollectionsRoutes.post("/", async (c) => {
|
|
17755
17791
|
try {
|
|
17756
17792
|
const formData = await c.req.formData();
|
|
17757
17793
|
const name = formData.get("name");
|
|
@@ -17870,7 +17906,7 @@ adminCollectionsRoutes.post("/collections", async (c) => {
|
|
|
17870
17906
|
}
|
|
17871
17907
|
}
|
|
17872
17908
|
});
|
|
17873
|
-
adminCollectionsRoutes.get("
|
|
17909
|
+
adminCollectionsRoutes.get("/:id", async (c) => {
|
|
17874
17910
|
try {
|
|
17875
17911
|
const id = c.req.param("id");
|
|
17876
17912
|
const user = c.get("user");
|
|
@@ -17938,7 +17974,7 @@ adminCollectionsRoutes.get("/collections/:id", async (c) => {
|
|
|
17938
17974
|
return c.html(renderCollectionFormPage(formData));
|
|
17939
17975
|
}
|
|
17940
17976
|
});
|
|
17941
|
-
adminCollectionsRoutes.put("
|
|
17977
|
+
adminCollectionsRoutes.put("/:id", async (c) => {
|
|
17942
17978
|
try {
|
|
17943
17979
|
const id = c.req.param("id");
|
|
17944
17980
|
const formData = await c.req.formData();
|
|
@@ -17972,7 +18008,7 @@ adminCollectionsRoutes.put("/collections/:id", async (c) => {
|
|
|
17972
18008
|
`);
|
|
17973
18009
|
}
|
|
17974
18010
|
});
|
|
17975
|
-
adminCollectionsRoutes.delete("
|
|
18011
|
+
adminCollectionsRoutes.delete("/:id", async (c) => {
|
|
17976
18012
|
try {
|
|
17977
18013
|
const id = c.req.param("id");
|
|
17978
18014
|
const db = c.env.DB;
|
|
@@ -18003,7 +18039,7 @@ adminCollectionsRoutes.delete("/collections/:id", async (c) => {
|
|
|
18003
18039
|
`);
|
|
18004
18040
|
}
|
|
18005
18041
|
});
|
|
18006
|
-
adminCollectionsRoutes.post("
|
|
18042
|
+
adminCollectionsRoutes.post("/:id/fields", async (c) => {
|
|
18007
18043
|
try {
|
|
18008
18044
|
const collectionId = c.req.param("id");
|
|
18009
18045
|
const formData = await c.req.formData();
|
|
@@ -18056,7 +18092,7 @@ adminCollectionsRoutes.post("/collections/:id/fields", async (c) => {
|
|
|
18056
18092
|
return c.json({ success: false, error: "Failed to add field." });
|
|
18057
18093
|
}
|
|
18058
18094
|
});
|
|
18059
|
-
adminCollectionsRoutes.put("
|
|
18095
|
+
adminCollectionsRoutes.put("/:collectionId/fields/:fieldId", async (c) => {
|
|
18060
18096
|
try {
|
|
18061
18097
|
const fieldId = c.req.param("fieldId");
|
|
18062
18098
|
const formData = await c.req.formData();
|
|
@@ -18080,7 +18116,7 @@ adminCollectionsRoutes.put("/collections/:collectionId/fields/:fieldId", async (
|
|
|
18080
18116
|
return c.json({ success: false, error: "Failed to update field." });
|
|
18081
18117
|
}
|
|
18082
18118
|
});
|
|
18083
|
-
adminCollectionsRoutes.delete("
|
|
18119
|
+
adminCollectionsRoutes.delete("/:collectionId/fields/:fieldId", async (c) => {
|
|
18084
18120
|
try {
|
|
18085
18121
|
const fieldId = c.req.param("fieldId");
|
|
18086
18122
|
const db = c.env.DB;
|
|
@@ -18092,7 +18128,7 @@ adminCollectionsRoutes.delete("/collections/:collectionId/fields/:fieldId", asyn
|
|
|
18092
18128
|
return c.json({ success: false, error: "Failed to delete field." });
|
|
18093
18129
|
}
|
|
18094
18130
|
});
|
|
18095
|
-
adminCollectionsRoutes.post("
|
|
18131
|
+
adminCollectionsRoutes.post("/:collectionId/fields/reorder", async (c) => {
|
|
18096
18132
|
try {
|
|
18097
18133
|
const body = await c.req.json();
|
|
18098
18134
|
const fieldIds = body.fieldIds;
|
|
@@ -18213,7 +18249,7 @@ function renderSettingsPage(data) {
|
|
|
18213
18249
|
// Migration functions
|
|
18214
18250
|
window.refreshMigrationStatus = async function() {
|
|
18215
18251
|
try {
|
|
18216
|
-
const response = await fetch('/admin/api/migrations/status');
|
|
18252
|
+
const response = await fetch('/admin/settings/api/migrations/status');
|
|
18217
18253
|
const result = await response.json();
|
|
18218
18254
|
|
|
18219
18255
|
if (result.success) {
|
|
@@ -18241,7 +18277,7 @@ function renderSettingsPage(data) {
|
|
|
18241
18277
|
btn.innerHTML = 'Running...';
|
|
18242
18278
|
|
|
18243
18279
|
try {
|
|
18244
|
-
const response = await fetch('/admin/api/migrations/run', {
|
|
18280
|
+
const response = await fetch('/admin/settings/api/migrations/run', {
|
|
18245
18281
|
method: 'POST'
|
|
18246
18282
|
});
|
|
18247
18283
|
const result = await response.json();
|
|
@@ -18262,7 +18298,7 @@ function renderSettingsPage(data) {
|
|
|
18262
18298
|
|
|
18263
18299
|
window.validateSchema = async function() {
|
|
18264
18300
|
try {
|
|
18265
|
-
const response = await fetch('/admin/api/migrations/validate');
|
|
18301
|
+
const response = await fetch('/admin/settings/api/migrations/validate');
|
|
18266
18302
|
const result = await response.json();
|
|
18267
18303
|
|
|
18268
18304
|
if (result.success) {
|
|
@@ -18340,7 +18376,7 @@ function renderSettingsPage(data) {
|
|
|
18340
18376
|
// Database Tools functions
|
|
18341
18377
|
window.refreshDatabaseStats = async function() {
|
|
18342
18378
|
try {
|
|
18343
|
-
const response = await fetch('/admin/database-tools/
|
|
18379
|
+
const response = await fetch('/admin/settings/api/database-tools/stats');
|
|
18344
18380
|
const result = await response.json();
|
|
18345
18381
|
|
|
18346
18382
|
if (result.success) {
|
|
@@ -18361,7 +18397,7 @@ function renderSettingsPage(data) {
|
|
|
18361
18397
|
btn.innerHTML = 'Creating Backup...';
|
|
18362
18398
|
|
|
18363
18399
|
try {
|
|
18364
|
-
const response = await fetch('/admin/database-tools/
|
|
18400
|
+
const response = await fetch('/admin/settings/api/database-tools/backup', {
|
|
18365
18401
|
method: 'POST'
|
|
18366
18402
|
});
|
|
18367
18403
|
const result = await response.json();
|
|
@@ -18400,7 +18436,7 @@ function renderSettingsPage(data) {
|
|
|
18400
18436
|
btn.innerHTML = 'Truncating...';
|
|
18401
18437
|
|
|
18402
18438
|
try {
|
|
18403
|
-
const response = await fetch('/admin/database-tools/
|
|
18439
|
+
const response = await fetch('/admin/settings/api/database-tools/truncate', {
|
|
18404
18440
|
method: 'POST',
|
|
18405
18441
|
headers: {
|
|
18406
18442
|
'Content-Type': 'application/json'
|
|
@@ -18431,7 +18467,7 @@ function renderSettingsPage(data) {
|
|
|
18431
18467
|
|
|
18432
18468
|
window.validateDatabase = async function() {
|
|
18433
18469
|
try {
|
|
18434
|
-
const response = await fetch('/admin/database-tools/
|
|
18470
|
+
const response = await fetch('/admin/settings/api/database-tools/validate');
|
|
18435
18471
|
const result = await response.json();
|
|
18436
18472
|
|
|
18437
18473
|
if (result.success) {
|
|
@@ -19285,7 +19321,7 @@ function renderMigrationSettings(settings) {
|
|
|
19285
19321
|
if (typeof refreshMigrationStatus === 'undefined') {
|
|
19286
19322
|
window.refreshMigrationStatus = async function() {
|
|
19287
19323
|
try {
|
|
19288
|
-
const response = await fetch('/admin/api/migrations/status');
|
|
19324
|
+
const response = await fetch('/admin/settings/api/migrations/status');
|
|
19289
19325
|
const result = await response.json();
|
|
19290
19326
|
|
|
19291
19327
|
if (result.success) {
|
|
@@ -19539,6 +19575,7 @@ function renderDatabaseToolsSettings(settings) {
|
|
|
19539
19575
|
|
|
19540
19576
|
// src/routes/admin-settings.ts
|
|
19541
19577
|
var adminSettingsRoutes = new Hono();
|
|
19578
|
+
adminSettingsRoutes.use("*", requireAuth());
|
|
19542
19579
|
function getMockSettings(user) {
|
|
19543
19580
|
return {
|
|
19544
19581
|
general: {
|
|
@@ -19597,10 +19634,10 @@ function getMockSettings(user) {
|
|
|
19597
19634
|
}
|
|
19598
19635
|
};
|
|
19599
19636
|
}
|
|
19600
|
-
adminSettingsRoutes.get("/
|
|
19637
|
+
adminSettingsRoutes.get("/", (c) => {
|
|
19601
19638
|
return c.redirect("/admin/settings/general");
|
|
19602
19639
|
});
|
|
19603
|
-
adminSettingsRoutes.get("/
|
|
19640
|
+
adminSettingsRoutes.get("/general", (c) => {
|
|
19604
19641
|
const user = c.get("user");
|
|
19605
19642
|
const pageData = {
|
|
19606
19643
|
user: user ? {
|
|
@@ -19614,7 +19651,7 @@ adminSettingsRoutes.get("/settings/general", (c) => {
|
|
|
19614
19651
|
};
|
|
19615
19652
|
return c.html(renderSettingsPage(pageData));
|
|
19616
19653
|
});
|
|
19617
|
-
adminSettingsRoutes.get("/
|
|
19654
|
+
adminSettingsRoutes.get("/appearance", (c) => {
|
|
19618
19655
|
const user = c.get("user");
|
|
19619
19656
|
const pageData = {
|
|
19620
19657
|
user: user ? {
|
|
@@ -19628,7 +19665,7 @@ adminSettingsRoutes.get("/settings/appearance", (c) => {
|
|
|
19628
19665
|
};
|
|
19629
19666
|
return c.html(renderSettingsPage(pageData));
|
|
19630
19667
|
});
|
|
19631
|
-
adminSettingsRoutes.get("/
|
|
19668
|
+
adminSettingsRoutes.get("/security", (c) => {
|
|
19632
19669
|
const user = c.get("user");
|
|
19633
19670
|
const pageData = {
|
|
19634
19671
|
user: user ? {
|
|
@@ -19642,7 +19679,7 @@ adminSettingsRoutes.get("/settings/security", (c) => {
|
|
|
19642
19679
|
};
|
|
19643
19680
|
return c.html(renderSettingsPage(pageData));
|
|
19644
19681
|
});
|
|
19645
|
-
adminSettingsRoutes.get("/
|
|
19682
|
+
adminSettingsRoutes.get("/notifications", (c) => {
|
|
19646
19683
|
const user = c.get("user");
|
|
19647
19684
|
const pageData = {
|
|
19648
19685
|
user: user ? {
|
|
@@ -19656,7 +19693,7 @@ adminSettingsRoutes.get("/settings/notifications", (c) => {
|
|
|
19656
19693
|
};
|
|
19657
19694
|
return c.html(renderSettingsPage(pageData));
|
|
19658
19695
|
});
|
|
19659
|
-
adminSettingsRoutes.get("/
|
|
19696
|
+
adminSettingsRoutes.get("/storage", (c) => {
|
|
19660
19697
|
const user = c.get("user");
|
|
19661
19698
|
const pageData = {
|
|
19662
19699
|
user: user ? {
|
|
@@ -19670,7 +19707,7 @@ adminSettingsRoutes.get("/settings/storage", (c) => {
|
|
|
19670
19707
|
};
|
|
19671
19708
|
return c.html(renderSettingsPage(pageData));
|
|
19672
19709
|
});
|
|
19673
|
-
adminSettingsRoutes.get("/
|
|
19710
|
+
adminSettingsRoutes.get("/migrations", (c) => {
|
|
19674
19711
|
const user = c.get("user");
|
|
19675
19712
|
const pageData = {
|
|
19676
19713
|
user: user ? {
|
|
@@ -19684,7 +19721,7 @@ adminSettingsRoutes.get("/settings/migrations", (c) => {
|
|
|
19684
19721
|
};
|
|
19685
19722
|
return c.html(renderSettingsPage(pageData));
|
|
19686
19723
|
});
|
|
19687
|
-
adminSettingsRoutes.get("/
|
|
19724
|
+
adminSettingsRoutes.get("/database-tools", (c) => {
|
|
19688
19725
|
const user = c.get("user");
|
|
19689
19726
|
const pageData = {
|
|
19690
19727
|
user: user ? {
|
|
@@ -19757,7 +19794,139 @@ adminSettingsRoutes.get("/api/migrations/validate", async (c) => {
|
|
|
19757
19794
|
}, 500);
|
|
19758
19795
|
}
|
|
19759
19796
|
});
|
|
19760
|
-
adminSettingsRoutes.
|
|
19797
|
+
adminSettingsRoutes.get("/api/database-tools/stats", async (c) => {
|
|
19798
|
+
try {
|
|
19799
|
+
const db = c.env.DB;
|
|
19800
|
+
const tablesQuery = await db.prepare(`
|
|
19801
|
+
SELECT name FROM sqlite_master
|
|
19802
|
+
WHERE type='table'
|
|
19803
|
+
AND name NOT LIKE 'sqlite_%'
|
|
19804
|
+
AND name NOT LIKE '_cf_%'
|
|
19805
|
+
ORDER BY name
|
|
19806
|
+
`).all();
|
|
19807
|
+
const tables = tablesQuery.results || [];
|
|
19808
|
+
let totalRows = 0;
|
|
19809
|
+
const tableStats = await Promise.all(
|
|
19810
|
+
tables.map(async (table) => {
|
|
19811
|
+
try {
|
|
19812
|
+
const countResult = await db.prepare(`SELECT COUNT(*) as count FROM ${table.name}`).first();
|
|
19813
|
+
const rowCount = countResult?.count || 0;
|
|
19814
|
+
totalRows += rowCount;
|
|
19815
|
+
return {
|
|
19816
|
+
name: table.name,
|
|
19817
|
+
rowCount
|
|
19818
|
+
};
|
|
19819
|
+
} catch (error) {
|
|
19820
|
+
console.error(`Error counting rows in ${table.name}:`, error);
|
|
19821
|
+
return {
|
|
19822
|
+
name: table.name,
|
|
19823
|
+
rowCount: 0
|
|
19824
|
+
};
|
|
19825
|
+
}
|
|
19826
|
+
})
|
|
19827
|
+
);
|
|
19828
|
+
const estimatedSizeBytes = totalRows * 1024;
|
|
19829
|
+
const databaseSizeMB = (estimatedSizeBytes / (1024 * 1024)).toFixed(2);
|
|
19830
|
+
return c.json({
|
|
19831
|
+
success: true,
|
|
19832
|
+
data: {
|
|
19833
|
+
totalTables: tables.length,
|
|
19834
|
+
totalRows,
|
|
19835
|
+
databaseSize: `${databaseSizeMB} MB (estimated)`,
|
|
19836
|
+
tables: tableStats
|
|
19837
|
+
}
|
|
19838
|
+
});
|
|
19839
|
+
} catch (error) {
|
|
19840
|
+
console.error("Error fetching database stats:", error);
|
|
19841
|
+
return c.json({
|
|
19842
|
+
success: false,
|
|
19843
|
+
error: "Failed to fetch database statistics"
|
|
19844
|
+
}, 500);
|
|
19845
|
+
}
|
|
19846
|
+
});
|
|
19847
|
+
adminSettingsRoutes.get("/api/database-tools/validate", async (c) => {
|
|
19848
|
+
try {
|
|
19849
|
+
const db = c.env.DB;
|
|
19850
|
+
const integrityResult = await db.prepare("PRAGMA integrity_check").first();
|
|
19851
|
+
const isValid = integrityResult?.integrity_check === "ok";
|
|
19852
|
+
return c.json({
|
|
19853
|
+
success: true,
|
|
19854
|
+
data: {
|
|
19855
|
+
valid: isValid,
|
|
19856
|
+
message: isValid ? "Database integrity check passed" : "Database integrity check failed"
|
|
19857
|
+
}
|
|
19858
|
+
});
|
|
19859
|
+
} catch (error) {
|
|
19860
|
+
console.error("Error validating database:", error);
|
|
19861
|
+
return c.json({
|
|
19862
|
+
success: false,
|
|
19863
|
+
error: "Failed to validate database"
|
|
19864
|
+
}, 500);
|
|
19865
|
+
}
|
|
19866
|
+
});
|
|
19867
|
+
adminSettingsRoutes.post("/api/database-tools/backup", async (c) => {
|
|
19868
|
+
try {
|
|
19869
|
+
const user = c.get("user");
|
|
19870
|
+
if (!user || user.role !== "admin") {
|
|
19871
|
+
return c.json({
|
|
19872
|
+
success: false,
|
|
19873
|
+
error: "Unauthorized. Admin access required."
|
|
19874
|
+
}, 403);
|
|
19875
|
+
}
|
|
19876
|
+
return c.json({
|
|
19877
|
+
success: true,
|
|
19878
|
+
message: "Database backup feature coming soon. Use Cloudflare Dashboard for backups."
|
|
19879
|
+
});
|
|
19880
|
+
} catch (error) {
|
|
19881
|
+
console.error("Error creating backup:", error);
|
|
19882
|
+
return c.json({
|
|
19883
|
+
success: false,
|
|
19884
|
+
error: "Failed to create backup"
|
|
19885
|
+
}, 500);
|
|
19886
|
+
}
|
|
19887
|
+
});
|
|
19888
|
+
adminSettingsRoutes.post("/api/database-tools/truncate", async (c) => {
|
|
19889
|
+
try {
|
|
19890
|
+
const user = c.get("user");
|
|
19891
|
+
if (!user || user.role !== "admin") {
|
|
19892
|
+
return c.json({
|
|
19893
|
+
success: false,
|
|
19894
|
+
error: "Unauthorized. Admin access required."
|
|
19895
|
+
}, 403);
|
|
19896
|
+
}
|
|
19897
|
+
const body = await c.req.json();
|
|
19898
|
+
const tablesToTruncate = body.tables || [];
|
|
19899
|
+
if (!Array.isArray(tablesToTruncate) || tablesToTruncate.length === 0) {
|
|
19900
|
+
return c.json({
|
|
19901
|
+
success: false,
|
|
19902
|
+
error: "No tables specified for truncation"
|
|
19903
|
+
}, 400);
|
|
19904
|
+
}
|
|
19905
|
+
const db = c.env.DB;
|
|
19906
|
+
const results = [];
|
|
19907
|
+
for (const tableName of tablesToTruncate) {
|
|
19908
|
+
try {
|
|
19909
|
+
await db.prepare(`DELETE FROM ${tableName}`).run();
|
|
19910
|
+
results.push({ table: tableName, success: true });
|
|
19911
|
+
} catch (error) {
|
|
19912
|
+
console.error(`Error truncating ${tableName}:`, error);
|
|
19913
|
+
results.push({ table: tableName, success: false, error: String(error) });
|
|
19914
|
+
}
|
|
19915
|
+
}
|
|
19916
|
+
return c.json({
|
|
19917
|
+
success: true,
|
|
19918
|
+
message: `Truncated ${results.filter((r) => r.success).length} of ${tablesToTruncate.length} tables`,
|
|
19919
|
+
results
|
|
19920
|
+
});
|
|
19921
|
+
} catch (error) {
|
|
19922
|
+
console.error("Error truncating tables:", error);
|
|
19923
|
+
return c.json({
|
|
19924
|
+
success: false,
|
|
19925
|
+
error: "Failed to truncate tables"
|
|
19926
|
+
}, 500);
|
|
19927
|
+
}
|
|
19928
|
+
});
|
|
19929
|
+
adminSettingsRoutes.post("/", async (c) => {
|
|
19761
19930
|
try {
|
|
19762
19931
|
const formData = await c.req.formData();
|
|
19763
19932
|
return c.html(html`
|
|
@@ -19809,5 +19978,5 @@ var ROUTES_INFO = {
|
|
|
19809
19978
|
};
|
|
19810
19979
|
|
|
19811
19980
|
export { ROUTES_INFO, adminCheckboxRoutes, adminCollectionsRoutes, adminDesignRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, admin_api_default, admin_code_examples_default, admin_content_default, admin_faq_default, admin_testimonials_default, api_content_crud_default, api_default, api_media_default, api_system_default, auth_default, router, userRoutes };
|
|
19812
|
-
//# sourceMappingURL=chunk-
|
|
19813
|
-
//# sourceMappingURL=chunk-
|
|
19981
|
+
//# sourceMappingURL=chunk-PPUKPNTP.js.map
|
|
19982
|
+
//# sourceMappingURL=chunk-PPUKPNTP.js.map
|