kempo 4.7.0 → 4.8.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/README.md +3 -1
- package/bin/cli.js +15 -33
- package/dist/admin/components/EmailTemplates.js +25 -0
- package/dist/admin/components/EmailTemplates.js.map +1 -0
- package/dist/admin/components/Emails.js +49 -0
- package/dist/admin/components/Emails.js.map +1 -0
- package/dist/admin/components/settings/AdminSettings.js +7 -0
- package/dist/admin/components/settings/AdminSettings.js.map +1 -0
- package/dist/admin/components/settings/BooleanSetting.js +2 -0
- package/dist/admin/components/settings/BooleanSetting.js.map +1 -0
- package/dist/admin/components/settings/JsonSetting.js +2 -0
- package/dist/admin/components/settings/JsonSetting.js.map +1 -0
- package/dist/admin/components/settings/NumberSetting.js +2 -0
- package/dist/admin/components/settings/NumberSetting.js.map +1 -0
- package/dist/admin/components/settings/SecretSetting.js +2 -0
- package/dist/admin/components/settings/SecretSetting.js.map +1 -0
- package/dist/admin/components/settings/Setting.js +2 -0
- package/dist/admin/components/settings/Setting.js.map +1 -0
- package/dist/admin/components/settings/SettingEditor.js +2 -0
- package/dist/admin/components/settings/SettingEditor.js.map +1 -0
- package/dist/admin/components/settings/SettingsPanel.js +24 -0
- package/dist/admin/components/settings/SettingsPanel.js.map +1 -0
- package/dist/admin/components/settings/StringSetting.js +2 -0
- package/dist/admin/components/settings/StringSetting.js.map +1 -0
- package/dist/admin/components/settings/humanize.js +2 -0
- package/dist/admin/components/settings/humanize.js.map +1 -0
- package/dist/admin/emails/index.page.html +14 -0
- package/dist/admin/icons/mail.svg +1 -0
- package/dist/admin/nav.fragment.html +6 -0
- package/dist/admin/settings/[owner]/[name]/index.page.html +1 -1
- package/dist/admin/settings/index.page.html +6 -289
- package/dist/kempo/api/email-templates/GET.js +2 -0
- package/dist/kempo/api/email-templates/GET.js.map +1 -0
- package/dist/kempo/api/email-templates/file/DELETE.js +2 -0
- package/dist/kempo/api/email-templates/file/DELETE.js.map +1 -0
- package/dist/kempo/api/email-templates/file/GET.js +2 -0
- package/dist/kempo/api/email-templates/file/GET.js.map +1 -0
- package/dist/kempo/api/email-templates/file/PUT.js +2 -0
- package/dist/kempo/api/email-templates/file/PUT.js.map +1 -0
- package/dist/kempo/api/email-templates/preview/GET.js +2 -0
- package/dist/kempo/api/email-templates/preview/GET.js.map +1 -0
- package/dist/kempo/api/emails/GET.js +2 -0
- package/dist/kempo/api/emails/GET.js.map +1 -0
- package/dist/kempo/api/emails/[id]/GET.js +2 -0
- package/dist/kempo/api/emails/[id]/GET.js.map +1 -0
- package/dist/kempo/api/emails/[id]/retry/POST.js +2 -0
- package/dist/kempo/api/emails/[id]/retry/POST.js.map +1 -0
- package/dist/kempo/api/emails/test/POST.js +2 -0
- package/dist/kempo/api/emails/test/POST.js.map +1 -0
- package/dist/kempo/api/settings/[owner]/[name]/POST.js +1 -1
- package/dist/kempo/api/settings/[owner]/[name]/POST.js.map +1 -1
- package/dist/kempo/sdk.js +1 -1
- package/dist/kempo/sdk.js.map +1 -1
- package/middleware/kempo.js +21 -0
- package/package.json +2 -3
- package/server/db/schema.js +32 -0
- package/server/sdk.js +5 -0
- package/server/utils/email/defaultSender.js +2 -0
- package/server/utils/email/dispatchEmail.js +56 -0
- package/server/utils/email/emailSettings.js +21 -0
- package/server/utils/email/emailTemplatePaths.js +36 -0
- package/server/utils/email/getEmail.js +14 -0
- package/server/utils/email/getSenderEmail.js +11 -0
- package/server/utils/email/getSenderFrom.js +18 -0
- package/server/utils/email/isValidSender.js +4 -0
- package/server/utils/email/listEmailTemplates.js +23 -0
- package/server/utils/email/listEmails.js +35 -0
- package/server/utils/email/maintainEmails.js +46 -0
- package/server/utils/email/pruneEmails.js +29 -0
- package/server/utils/email/readEmailTemplate.js +28 -0
- package/server/utils/email/resetEmailTemplate.js +15 -0
- package/server/utils/email/retryEmail.js +21 -0
- package/server/utils/email/saveEmailTemplate.js +38 -0
- package/server/utils/email/sendEmail.js +22 -32
- package/server/utils/email/sendEmailFromTemplate.js +2 -2
- package/server/utils/hooks/triggerHook.js +17 -4
- package/server/utils/settings/listSettings.js +10 -5
- package/server/utils/settings/setSetting.js +4 -0
- package/server/utils/settings/settingRules.js +21 -0
- package/server/utils/settings/systemSettings.js +40 -0
- package/server/utils/setup/seedDatabase.js +5 -60
- package/server/utils/setup/systemPermissions.js +75 -0
package/dist/kempo/sdk.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"src\\kempo\\sdk.js","names":["get","post","put","patch","del","API_BASE","register","async","email","password","name","login","logout","getSession","forgotPassword","resetPassword","token","logoutAll","newPassword","changePassword","currentPassword","verifyEmail","currentUserHasPermission","permission","currentUserHasAllPermissions","permissions","currentUserHasSomePermissions","getAllCurrentUserPermissions","userHasPermission","userid","userHasAllPermissions","userHasSomePermissions","getAllUserPermissions","getUsers","limit","offset","searchUsers","q","inGroup","notInGroup","createUser","emailVerified","deleteUsers","ids","updateUser","id","createdAt","getUser","getUserSessions","deleteUserSession","sessionToken","deleteExpiredUserSessions","getUserGroups","addUserToGroup","groupName","removeUserFromGroup","listGroups","owner","createGroup","description","updateGroup","newName","deleteGroups","names","getGroup","encodeURIComponent","getGroupMembers","addMemberToGroup","userId","removeMemberFromGroup","getGroupPermissions","addPermissionToGroup","permissionName","removePermissionFromGroup","listPermissions","createPermissions","updatePermission","deletePermissions","getSetting","listSettings","setSetting","data","deleteSetting","listPages","showHidden","rescanPages","listTemplates","listDirectories","getPage","file","createPage","directory","template","updatePage","title","author","contents","extraMetadata","deletePages","files","disablePage","enablePage","movePage","newFile","searchPagesByMetadata","query","JSON","stringify","setPageLocked","locked","getTemplate","createTemplate","copyFrom","updateTemplate","markup","deleteTemplates","disableTemplate","enableTemplate","moveTemplate","setTemplateLocked","listFragments","getFragment","createFragment","updateFragment","deleteFragments","moveFragment","disableFragment","enableFragment","setFragmentLocked","listGlobalContent","getGlobalContent","createGlobalContent","location","priority","updateGlobalContent","deleteGlobalContent","disableGlobalContent","enableGlobalContent","setGlobalContentLocked","listMenus","saveMenu","deleteMenu","assignMenuLocation","menuId","saveMenuItem","navItem","label","href","icon","newTab","deleteMenuItems","setMenuItemEnabled","enabled","reorderMenuItems","adoptMenuItem","menuName","listExtensions","listAvailableExtensions","listKnownExtensions","search","sort","getKnownExtension","rateExtension","rating","getDashboardWidgets","getNotifications","unreadOnly","getUnreadNotificationCount","markNotificationRead","markNotificationHandled","markAllNotificationsRead","dismissNotification","listRealtimeConnections","installExtension","withDependencies","uninstallExtension","purgeData","cascade","enableExtension","getExtensionRequirements","disableExtension","getExtensionDependents","includeDisabled","undefined","listAdminGlobalContent","getAdminGlobalContent","createAdminGlobalContent","updateAdminGlobalContent","deleteAdminGlobalContent","enableAdminGlobalContent","disableAdminGlobalContent"],"sources":["0"],"sourcesContent":["import { get, post, put, patch, del } from './fetch.js';\nconst API_BASE = '/kempo/api';\n\nexport const register = async ({ email, password, name }) =>\n post(`${API_BASE}/auth/register/email`, { email, password, name });\n\nexport const login = async ({ email, password }) =>\n post(`${API_BASE}/auth/login/email`, { email, password });\n\nexport const logout = async () => post(`${API_BASE}/auth/logout`, {});\n\nexport const getSession = async () => get(`${API_BASE}/auth/session`);\n\nexport const forgotPassword = async ({ email }) =>\n post(`${API_BASE}/forgot-password`, { email });\n\nexport const resetPassword = async ({ token, password, logoutAll = false }) =>\n post(`${API_BASE}/auth/reset-password`, { token, newPassword: password, logoutAll });\n \nexport const changePassword = async ({ currentPassword, newPassword, logoutAll }) => \n post(`${API_BASE}/auth/change-password`, { currentPassword, newPassword, logoutAll });\n\nexport const verifyEmail = async ({ token }) =>\n post(`${API_BASE}/auth/verify-email`, { token });\n\nexport const currentUserHasPermission = async ({ permission }) =>\n get(`${API_BASE}/user/current/permissions`, { permission });\n\nexport const currentUserHasAllPermissions = async ({ permissions }) =>\n get(`${API_BASE}/user/current/permissions/all`, { permissions });\n\nexport const currentUserHasSomePermissions = async ({ permissions }) =>\n get(`${API_BASE}/user/current/permissions/some`, { permissions });\n\nexport const getAllCurrentUserPermissions = async () =>\n get(`${API_BASE}/user/current/permissions/list`);\n\nexport const userHasPermission = async ({ userid, permission }) =>\n get(`${API_BASE}/user/${userid}/permissions`, { permission });\n\nexport const userHasAllPermissions = async ({ userid, permissions }) =>\n get(`${API_BASE}/user/${userid}/permissions/all`, { permissions });\n\nexport const userHasSomePermissions = async ({ userid, permissions }) =>\n get(`${API_BASE}/user/${userid}/permissions/some`, { permissions });\n\nexport const getAllUserPermissions = async ({ userid }) =>\n get(`${API_BASE}/user/${userid}/permissions/list`);\n\nexport const getUsers = async ({ limit, offset } = {}) =>\n get(`${API_BASE}/user`, { limit, offset });\n\nexport const searchUsers = async ({ q, limit, offset, inGroup, notInGroup } = {}) =>\n get(`${API_BASE}/user/search`, { q, limit, offset, inGroup, notInGroup });\n\nexport const createUser = async ({ name, email, password, emailVerified }) =>\n post(`${API_BASE}/user/create`, { name, email, password, emailVerified });\n\nexport const deleteUsers = async (ids) =>\n del(`${API_BASE}/user`, { ids });\n\nexport const updateUser = async ({ id, name, email, emailVerified, createdAt }) =>\n patch(`${API_BASE}/user`, { id, name, email, emailVerified, createdAt });\n\nexport const getUser = async (userid) =>\n get(`${API_BASE}/user/${userid}`);\n\nexport const getUserSessions = async (userid, { limit, offset } = {}) =>\n get(`${API_BASE}/user/${userid}/sessions`, { limit, offset });\n\nexport const deleteUserSession = async (userid, sessionToken) =>\n del(`${API_BASE}/user/${userid}/sessions`, { sessionToken });\n\nexport const deleteExpiredUserSessions = async (userid) =>\n del(`${API_BASE}/user/${userid}/sessions/expired`);\n\nexport const getUserGroups = async (userid, { limit, offset } = {}) =>\n get(`${API_BASE}/user/${userid}/groups`, { limit, offset });\n\nexport const addUserToGroup = async (userid, groupName) =>\n post(`${API_BASE}/user/${userid}/groups`, { groupName });\n\nexport const removeUserFromGroup = async (userid, groupName) =>\n del(`${API_BASE}/user/${userid}/groups`, { groupName });\n\nexport const listGroups = async ({ limit, offset, owner } = {}) =>\n get(`${API_BASE}/groups`, { limit, offset, owner });\n\nexport const createGroup = async ({ name, description }) =>\n post(`${API_BASE}/groups`, { name, description });\n\nexport const updateGroup = async ({ name, newName, description }) =>\n patch(`${API_BASE}/groups`, { name, newName, description });\n\nexport const deleteGroups = async (names) =>\n del(`${API_BASE}/groups`, { names });\n\nexport const getGroup = async (name) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}`);\n\nexport const getGroupMembers = async (name, { limit, offset } = {}) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { limit, offset });\n\nexport const addMemberToGroup = async (name, userId) =>\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\n\nexport const removeMemberFromGroup = async (name, userId) =>\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\n\nexport const getGroupPermissions = async (name) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`);\n\nexport const addPermissionToGroup = async (name, permissionName) =>\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\n\nexport const removePermissionFromGroup = async (name, permissionName) =>\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\n\nexport const listPermissions = async ({ limit, offset, owner } = {}) =>\n get(`${API_BASE}/permissions`, { limit, offset, owner });\n\nexport const createPermissions = async (permissions) =>\n post(`${API_BASE}/permissions`, { permissions });\n\nexport const updatePermission = async ({ name, description }) =>\n patch(`${API_BASE}/permissions`, { name, description });\n\nexport const deletePermissions = async (names) =>\n del(`${API_BASE}/permissions`, { names });\n\nexport const getSetting = async (owner, name) =>\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\n\nexport const listSettings = async ({ owner, limit, offset } = {}) =>\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}`, { limit, offset });\n\nexport const setSetting = async (owner, name, data) =>\n post(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`, data);\n\nexport const deleteSetting = async (owner, name) =>\n del(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\n\n/*\n Pages\n*/\n\nexport const listPages = async ({ owner, showHidden, offset, limit } = {}) =>\n get(`${API_BASE}/pages`, { owner, showHidden, offset, limit });\n\nexport const rescanPages = async () =>\n post(`${API_BASE}/pages/rescan`, {});\n\nexport const listTemplates = async () =>\n get(`${API_BASE}/templates`);\n\nexport const listDirectories = async () =>\n get(`${API_BASE}/pages/directories`);\n\nexport const getPage = async (file) =>\n get(`${API_BASE}/pages/file`, { file });\n\nexport const createPage = async ({ directory, name, template }) =>\n post(`${API_BASE}/pages`, { directory, name, template });\n\nexport const updatePage = async ({ file, name, title, description, author, template, contents, extraMetadata }) =>\n put(`${API_BASE}/pages/file`, { file, name, title, description, author, template, contents, extraMetadata });\n\nexport const deletePages = async (files) =>\n del(`${API_BASE}/pages`, { files });\n\nexport const disablePage = async (file) =>\n put(`${API_BASE}/pages/disable`, { file });\n\nexport const enablePage = async (file) =>\n put(`${API_BASE}/pages/enable`, { file });\n\nexport const movePage = async ({ file, newFile }) =>\n patch(`${API_BASE}/pages/file`, { file, newFile });\n\nexport const searchPagesByMetadata = async (query) =>\n get(`${API_BASE}/pages/search`, { query: JSON.stringify(query) });\n\nexport const setPageLocked = async ({ file, locked }) =>\n put(`${API_BASE}/pages/lock`, { file, locked });\n\n/*\n Templates\n*/\n\nexport const getTemplate = async (file) =>\n get(`${API_BASE}/templates/file`, { file });\n\nexport const createTemplate = async ({ directory, name, copyFrom }) =>\n post(`${API_BASE}/templates`, { directory, name, copyFrom });\n\nexport const updateTemplate = async ({ file, name, author, markup }) =>\n put(`${API_BASE}/templates/file`, { file, name, author, markup });\n\nexport const deleteTemplates = async (files) =>\n del(`${API_BASE}/templates`, { files });\n\nexport const disableTemplate = async (file) =>\n put(`${API_BASE}/templates/disable`, { file });\n\nexport const enableTemplate = async (file) =>\n put(`${API_BASE}/templates/enable`, { file });\n\nexport const moveTemplate = async ({ file, newFile }) =>\n patch(`${API_BASE}/templates/file`, { file, newFile });\n\nexport const setTemplateLocked = async ({ file, locked }) =>\n put(`${API_BASE}/templates/lock`, { file, locked });\n\n/*\n Fragments\n*/\n\nexport const listFragments = async () =>\n get(`${API_BASE}/fragments`);\n\nexport const getFragment = async (file) =>\n get(`${API_BASE}/fragments/file`, { file });\n\nexport const createFragment = async ({ directory, name }) =>\n post(`${API_BASE}/fragments`, { directory, name });\n\nexport const updateFragment = async ({ file, author, markup }) =>\n put(`${API_BASE}/fragments/file`, { file, author, markup });\n\nexport const deleteFragments = async (files) =>\n del(`${API_BASE}/fragments`, { files });\n\nexport const moveFragment = async ({ file, newFile }) =>\n patch(`${API_BASE}/fragments/file`, { file, newFile });\n\nexport const disableFragment = async (file) =>\n put(`${API_BASE}/fragments/disable`, { file });\n\nexport const enableFragment = async (file) =>\n put(`${API_BASE}/fragments/enable`, { file });\n\nexport const setFragmentLocked = async ({ file, locked }) =>\n put(`${API_BASE}/fragments/lock`, { file, locked });\n\n/*\n Global Content\n*/\n\nexport const listGlobalContent = async () =>\n get(`${API_BASE}/globals`);\n\nexport const getGlobalContent = async (id) =>\n get(`${API_BASE}/globals/entry`, { id });\n\nexport const createGlobalContent = async ({ name, location, priority }) =>\n post(`${API_BASE}/globals`, { name, location, priority });\n\nexport const updateGlobalContent = async ({ id, name, location, priority, markup }) =>\n put(`${API_BASE}/globals/entry`, { id, name, location, priority, markup });\n\nexport const deleteGlobalContent = async (ids) =>\n del(`${API_BASE}/globals`, { ids });\n\nexport const disableGlobalContent = async (id) =>\n put(`${API_BASE}/globals/disable`, { id });\n\nexport const enableGlobalContent = async (id) =>\n put(`${API_BASE}/globals/enable`, { id });\n\nexport const setGlobalContentLocked = async ({ id, locked }) =>\n put(`${API_BASE}/globals/lock`, { id, locked });\n\n/*\n Menus\n*/\n\nexport const listMenus = async () =>\n get(`${API_BASE}/menus`);\n\nexport const saveMenu = async ({ id, name }) =>\n post(`${API_BASE}/menus`, { id, name });\n\nexport const deleteMenu = async (id) =>\n del(`${API_BASE}/menus`, { id });\n\nexport const assignMenuLocation = async ({ location, menuId }) =>\n put(`${API_BASE}/menus/location`, { location, menuId });\n\nexport const saveMenuItem = async ({ id, menuId, location, navItem, label, href, icon, newTab }) =>\n post(`${API_BASE}/menus/items`, { id, menuId, location, navItem, label, href, icon, newTab });\n\nexport const deleteMenuItems = async (ids) =>\n del(`${API_BASE}/menus/items`, { ids });\n\nexport const setMenuItemEnabled = async ({ id, enabled }) =>\n put(`${API_BASE}/menus/items/enabled`, { id, enabled });\n\nexport const reorderMenuItems = async ({ menuId, ids }) =>\n put(`${API_BASE}/menus/items/order`, { menuId, ids });\n\nexport const adoptMenuItem = async ({ id, menuId, menuName }) =>\n put(`${API_BASE}/menus/adopt`, { id, menuId, menuName });\n\n/*\n Extensions\n*/\n\nexport const listExtensions = async ({ limit, offset } = {}) =>\n get(`${API_BASE}/extensions`, { limit, offset });\n\nexport const listAvailableExtensions = async () =>\n get(`${API_BASE}/extensions/available`);\n\nexport const listKnownExtensions = async ({ search, sort, limit, offset } = {}) =>\n get(`${API_BASE}/extensions/known`, { search, sort, limit, offset });\n\nexport const getKnownExtension = async (name) =>\n get(`${API_BASE}/extensions/known`, { name });\n\nexport const rateExtension = async (name, rating) =>\n put(`${API_BASE}/extensions/rating`, { name, rating });\n\n/*\n Dashboard\n*/\n\nexport const getDashboardWidgets = async () =>\n get(`${API_BASE}/dashboard/widgets`);\n\n/*\n Notifications\n*/\n\nexport const getNotifications = async ({ unreadOnly = false, limit, offset } = {}) =>\n get(`${API_BASE}/notifications`, { unreadOnly, limit, offset });\n\nexport const getUnreadNotificationCount = async () =>\n get(`${API_BASE}/notifications/count`);\n\nexport const markNotificationRead = async (id) =>\n post(`${API_BASE}/notifications/${encodeURIComponent(id)}/read`, {});\n\nexport const markNotificationHandled = async (id) =>\n post(`${API_BASE}/notifications/${encodeURIComponent(id)}/handled`, {});\n\nexport const markAllNotificationsRead = async () =>\n post(`${API_BASE}/notifications/read-all`, {});\n\nexport const dismissNotification = async (id) =>\n del(`${API_BASE}/notifications/${encodeURIComponent(id)}`);\n\nexport const listRealtimeConnections = async () =>\n get(`${API_BASE}/realtime/connections`);\n\nexport const installExtension = async (name, { withDependencies = false } = {}) =>\n post(`${API_BASE}/extensions`, { name, withDependencies });\n\nexport const uninstallExtension = async (name, purgeData = false, { cascade = false } = {}) =>\n del(`${API_BASE}/extensions`, { name, purgeData, cascade });\n\nexport const enableExtension = async (name, { cascade = false } = {}) =>\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/enable`, { cascade });\n\nexport const getExtensionRequirements = async (name) =>\n get(`${API_BASE}/extensions/${encodeURIComponent(name)}/requirements`);\n\nexport const disableExtension = async (name, { cascade = false } = {}) =>\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/disable`, { cascade });\n\nexport const getExtensionDependents = async (name, { includeDisabled = false } = {}) =>\n get(`${API_BASE}/extensions/${encodeURIComponent(name)}/dependents`, { includeDisabled: includeDisabled ? 'true' : undefined });\n\n/*\n Admin Global Content\n*/\n\nexport const listAdminGlobalContent = async () =>\n get(`${API_BASE}/admin-globals`);\n\nexport const getAdminGlobalContent = async (id) =>\n get(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`);\n\nexport const createAdminGlobalContent = async ({ name, location, priority, owner, markup }) =>\n post(`${API_BASE}/admin-globals`, { name, location, priority, owner, markup });\n\nexport const updateAdminGlobalContent = async (id, { name, location, priority, markup }) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`, { name, location, priority, markup });\n\nexport const deleteAdminGlobalContent = async (ids) =>\n del(`${API_BASE}/admin-globals`, { ids });\n\nexport const enableAdminGlobalContent = async (id) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/enable`, {});\n\nexport const disableAdminGlobalContent = async (id) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/disable`, {});\n\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"src\\kempo\\sdk.js","names":["get","post","put","patch","del","API_BASE","register","async","email","password","name","login","logout","getSession","forgotPassword","resetPassword","token","logoutAll","newPassword","changePassword","currentPassword","verifyEmail","currentUserHasPermission","permission","currentUserHasAllPermissions","permissions","currentUserHasSomePermissions","getAllCurrentUserPermissions","userHasPermission","userid","userHasAllPermissions","userHasSomePermissions","getAllUserPermissions","getUsers","limit","offset","searchUsers","q","inGroup","notInGroup","createUser","emailVerified","deleteUsers","ids","updateUser","id","createdAt","getUser","getUserSessions","deleteUserSession","sessionToken","deleteExpiredUserSessions","getUserGroups","addUserToGroup","groupName","removeUserFromGroup","listGroups","owner","createGroup","description","updateGroup","newName","deleteGroups","names","getGroup","encodeURIComponent","getGroupMembers","addMemberToGroup","userId","removeMemberFromGroup","getGroupPermissions","addPermissionToGroup","permissionName","removePermissionFromGroup","listPermissions","createPermissions","updatePermission","deletePermissions","getSetting","listSettings","setSetting","data","deleteSetting","listEmails","status","type","search","getEmail","retryEmail","sendTestEmail","to","listPages","showHidden","rescanPages","listTemplates","listDirectories","getPage","file","createPage","directory","template","updatePage","title","author","contents","extraMetadata","deletePages","files","disablePage","enablePage","movePage","newFile","searchPagesByMetadata","query","JSON","stringify","setPageLocked","locked","getTemplate","createTemplate","copyFrom","updateTemplate","markup","deleteTemplates","listEmailTemplates","getEmailTemplate","saveEmailTemplate","source","resetEmailTemplate","disableTemplate","enableTemplate","moveTemplate","setTemplateLocked","listFragments","getFragment","createFragment","updateFragment","deleteFragments","moveFragment","disableFragment","enableFragment","setFragmentLocked","listGlobalContent","getGlobalContent","createGlobalContent","location","priority","updateGlobalContent","deleteGlobalContent","disableGlobalContent","enableGlobalContent","setGlobalContentLocked","listMenus","saveMenu","deleteMenu","assignMenuLocation","menuId","saveMenuItem","navItem","label","href","icon","newTab","deleteMenuItems","setMenuItemEnabled","enabled","reorderMenuItems","adoptMenuItem","menuName","listExtensions","listAvailableExtensions","listKnownExtensions","sort","getKnownExtension","rateExtension","rating","getDashboardWidgets","getNotifications","unreadOnly","getUnreadNotificationCount","markNotificationRead","markNotificationHandled","markAllNotificationsRead","dismissNotification","listRealtimeConnections","installExtension","withDependencies","uninstallExtension","purgeData","cascade","enableExtension","getExtensionRequirements","disableExtension","getExtensionDependents","includeDisabled","undefined","listAdminGlobalContent","getAdminGlobalContent","createAdminGlobalContent","updateAdminGlobalContent","deleteAdminGlobalContent","enableAdminGlobalContent","disableAdminGlobalContent"],"sources":["0"],"sourcesContent":["import { get, post, put, patch, del } from './fetch.js';\nconst API_BASE = '/kempo/api';\n\nexport const register = async ({ email, password, name }) =>\n post(`${API_BASE}/auth/register/email`, { email, password, name });\n\nexport const login = async ({ email, password }) =>\n post(`${API_BASE}/auth/login/email`, { email, password });\n\nexport const logout = async () => post(`${API_BASE}/auth/logout`, {});\n\nexport const getSession = async () => get(`${API_BASE}/auth/session`);\n\nexport const forgotPassword = async ({ email }) =>\n post(`${API_BASE}/forgot-password`, { email });\n\nexport const resetPassword = async ({ token, password, logoutAll = false }) =>\n post(`${API_BASE}/auth/reset-password`, { token, newPassword: password, logoutAll });\n \nexport const changePassword = async ({ currentPassword, newPassword, logoutAll }) => \n post(`${API_BASE}/auth/change-password`, { currentPassword, newPassword, logoutAll });\n\nexport const verifyEmail = async ({ token }) =>\n post(`${API_BASE}/auth/verify-email`, { token });\n\nexport const currentUserHasPermission = async ({ permission }) =>\n get(`${API_BASE}/user/current/permissions`, { permission });\n\nexport const currentUserHasAllPermissions = async ({ permissions }) =>\n get(`${API_BASE}/user/current/permissions/all`, { permissions });\n\nexport const currentUserHasSomePermissions = async ({ permissions }) =>\n get(`${API_BASE}/user/current/permissions/some`, { permissions });\n\nexport const getAllCurrentUserPermissions = async () =>\n get(`${API_BASE}/user/current/permissions/list`);\n\nexport const userHasPermission = async ({ userid, permission }) =>\n get(`${API_BASE}/user/${userid}/permissions`, { permission });\n\nexport const userHasAllPermissions = async ({ userid, permissions }) =>\n get(`${API_BASE}/user/${userid}/permissions/all`, { permissions });\n\nexport const userHasSomePermissions = async ({ userid, permissions }) =>\n get(`${API_BASE}/user/${userid}/permissions/some`, { permissions });\n\nexport const getAllUserPermissions = async ({ userid }) =>\n get(`${API_BASE}/user/${userid}/permissions/list`);\n\nexport const getUsers = async ({ limit, offset } = {}) =>\n get(`${API_BASE}/user`, { limit, offset });\n\nexport const searchUsers = async ({ q, limit, offset, inGroup, notInGroup } = {}) =>\n get(`${API_BASE}/user/search`, { q, limit, offset, inGroup, notInGroup });\n\nexport const createUser = async ({ name, email, password, emailVerified }) =>\n post(`${API_BASE}/user/create`, { name, email, password, emailVerified });\n\nexport const deleteUsers = async (ids) =>\n del(`${API_BASE}/user`, { ids });\n\nexport const updateUser = async ({ id, name, email, emailVerified, createdAt }) =>\n patch(`${API_BASE}/user`, { id, name, email, emailVerified, createdAt });\n\nexport const getUser = async (userid) =>\n get(`${API_BASE}/user/${userid}`);\n\nexport const getUserSessions = async (userid, { limit, offset } = {}) =>\n get(`${API_BASE}/user/${userid}/sessions`, { limit, offset });\n\nexport const deleteUserSession = async (userid, sessionToken) =>\n del(`${API_BASE}/user/${userid}/sessions`, { sessionToken });\n\nexport const deleteExpiredUserSessions = async (userid) =>\n del(`${API_BASE}/user/${userid}/sessions/expired`);\n\nexport const getUserGroups = async (userid, { limit, offset } = {}) =>\n get(`${API_BASE}/user/${userid}/groups`, { limit, offset });\n\nexport const addUserToGroup = async (userid, groupName) =>\n post(`${API_BASE}/user/${userid}/groups`, { groupName });\n\nexport const removeUserFromGroup = async (userid, groupName) =>\n del(`${API_BASE}/user/${userid}/groups`, { groupName });\n\nexport const listGroups = async ({ limit, offset, owner } = {}) =>\n get(`${API_BASE}/groups`, { limit, offset, owner });\n\nexport const createGroup = async ({ name, description }) =>\n post(`${API_BASE}/groups`, { name, description });\n\nexport const updateGroup = async ({ name, newName, description }) =>\n patch(`${API_BASE}/groups`, { name, newName, description });\n\nexport const deleteGroups = async (names) =>\n del(`${API_BASE}/groups`, { names });\n\nexport const getGroup = async (name) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}`);\n\nexport const getGroupMembers = async (name, { limit, offset } = {}) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { limit, offset });\n\nexport const addMemberToGroup = async (name, userId) =>\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\n\nexport const removeMemberFromGroup = async (name, userId) =>\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\n\nexport const getGroupPermissions = async (name) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`);\n\nexport const addPermissionToGroup = async (name, permissionName) =>\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\n\nexport const removePermissionFromGroup = async (name, permissionName) =>\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\n\nexport const listPermissions = async ({ limit, offset, owner } = {}) =>\n get(`${API_BASE}/permissions`, { limit, offset, owner });\n\nexport const createPermissions = async (permissions) =>\n post(`${API_BASE}/permissions`, { permissions });\n\nexport const updatePermission = async ({ name, description }) =>\n patch(`${API_BASE}/permissions`, { name, description });\n\nexport const deletePermissions = async (names) =>\n del(`${API_BASE}/permissions`, { names });\n\nexport const getSetting = async (owner, name) =>\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\n\nexport const listSettings = async ({ owner, limit, offset } = {}) =>\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}`, { limit, offset });\n\nexport const setSetting = async (owner, name, data) =>\n post(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`, data);\n\nexport const deleteSetting = async (owner, name) =>\n del(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\n\n/*\n Emails\n*/\nexport const listEmails = async ({ status, type, search, limit, offset } = {}) =>\n get(`${API_BASE}/emails`, { status, type, search, limit, offset });\n\nexport const getEmail = async (id) =>\n get(`${API_BASE}/emails/${encodeURIComponent(id)}`);\n\nexport const retryEmail = async (id) =>\n post(`${API_BASE}/emails/${encodeURIComponent(id)}/retry`, {});\n\nexport const sendTestEmail = async (to) =>\n post(`${API_BASE}/emails/test`, { to });\n\n/*\n Pages\n*/\n\nexport const listPages = async ({ owner, showHidden, offset, limit } = {}) =>\n get(`${API_BASE}/pages`, { owner, showHidden, offset, limit });\n\nexport const rescanPages = async () =>\n post(`${API_BASE}/pages/rescan`, {});\n\nexport const listTemplates = async () =>\n get(`${API_BASE}/templates`);\n\nexport const listDirectories = async () =>\n get(`${API_BASE}/pages/directories`);\n\nexport const getPage = async (file) =>\n get(`${API_BASE}/pages/file`, { file });\n\nexport const createPage = async ({ directory, name, template }) =>\n post(`${API_BASE}/pages`, { directory, name, template });\n\nexport const updatePage = async ({ file, name, title, description, author, template, contents, extraMetadata }) =>\n put(`${API_BASE}/pages/file`, { file, name, title, description, author, template, contents, extraMetadata });\n\nexport const deletePages = async (files) =>\n del(`${API_BASE}/pages`, { files });\n\nexport const disablePage = async (file) =>\n put(`${API_BASE}/pages/disable`, { file });\n\nexport const enablePage = async (file) =>\n put(`${API_BASE}/pages/enable`, { file });\n\nexport const movePage = async ({ file, newFile }) =>\n patch(`${API_BASE}/pages/file`, { file, newFile });\n\nexport const searchPagesByMetadata = async (query) =>\n get(`${API_BASE}/pages/search`, { query: JSON.stringify(query) });\n\nexport const setPageLocked = async ({ file, locked }) =>\n put(`${API_BASE}/pages/lock`, { file, locked });\n\n/*\n Templates\n*/\n\nexport const getTemplate = async (file) =>\n get(`${API_BASE}/templates/file`, { file });\n\nexport const createTemplate = async ({ directory, name, copyFrom }) =>\n post(`${API_BASE}/templates`, { directory, name, copyFrom });\n\nexport const updateTemplate = async ({ file, name, author, markup }) =>\n put(`${API_BASE}/templates/file`, { file, name, author, markup });\n\nexport const deleteTemplates = async (files) =>\n del(`${API_BASE}/templates`, { files });\n\nexport const listEmailTemplates = async (owner) =>\n get(`${API_BASE}/email-templates`, owner ? { owner } : {});\n\nexport const getEmailTemplate = async (template) =>\n get(`${API_BASE}/email-templates/file`, { template });\n\nexport const saveEmailTemplate = async ({ template, source }) =>\n put(`${API_BASE}/email-templates/file`, { template, source });\n\nexport const resetEmailTemplate = async (template) =>\n del(`${API_BASE}/email-templates/file`, { template });\n\nexport const disableTemplate = async (file) =>\n put(`${API_BASE}/templates/disable`, { file });\n\nexport const enableTemplate = async (file) =>\n put(`${API_BASE}/templates/enable`, { file });\n\nexport const moveTemplate = async ({ file, newFile }) =>\n patch(`${API_BASE}/templates/file`, { file, newFile });\n\nexport const setTemplateLocked = async ({ file, locked }) =>\n put(`${API_BASE}/templates/lock`, { file, locked });\n\n/*\n Fragments\n*/\n\nexport const listFragments = async () =>\n get(`${API_BASE}/fragments`);\n\nexport const getFragment = async (file) =>\n get(`${API_BASE}/fragments/file`, { file });\n\nexport const createFragment = async ({ directory, name }) =>\n post(`${API_BASE}/fragments`, { directory, name });\n\nexport const updateFragment = async ({ file, author, markup }) =>\n put(`${API_BASE}/fragments/file`, { file, author, markup });\n\nexport const deleteFragments = async (files) =>\n del(`${API_BASE}/fragments`, { files });\n\nexport const moveFragment = async ({ file, newFile }) =>\n patch(`${API_BASE}/fragments/file`, { file, newFile });\n\nexport const disableFragment = async (file) =>\n put(`${API_BASE}/fragments/disable`, { file });\n\nexport const enableFragment = async (file) =>\n put(`${API_BASE}/fragments/enable`, { file });\n\nexport const setFragmentLocked = async ({ file, locked }) =>\n put(`${API_BASE}/fragments/lock`, { file, locked });\n\n/*\n Global Content\n*/\n\nexport const listGlobalContent = async () =>\n get(`${API_BASE}/globals`);\n\nexport const getGlobalContent = async (id) =>\n get(`${API_BASE}/globals/entry`, { id });\n\nexport const createGlobalContent = async ({ name, location, priority }) =>\n post(`${API_BASE}/globals`, { name, location, priority });\n\nexport const updateGlobalContent = async ({ id, name, location, priority, markup }) =>\n put(`${API_BASE}/globals/entry`, { id, name, location, priority, markup });\n\nexport const deleteGlobalContent = async (ids) =>\n del(`${API_BASE}/globals`, { ids });\n\nexport const disableGlobalContent = async (id) =>\n put(`${API_BASE}/globals/disable`, { id });\n\nexport const enableGlobalContent = async (id) =>\n put(`${API_BASE}/globals/enable`, { id });\n\nexport const setGlobalContentLocked = async ({ id, locked }) =>\n put(`${API_BASE}/globals/lock`, { id, locked });\n\n/*\n Menus\n*/\n\nexport const listMenus = async () =>\n get(`${API_BASE}/menus`);\n\nexport const saveMenu = async ({ id, name }) =>\n post(`${API_BASE}/menus`, { id, name });\n\nexport const deleteMenu = async (id) =>\n del(`${API_BASE}/menus`, { id });\n\nexport const assignMenuLocation = async ({ location, menuId }) =>\n put(`${API_BASE}/menus/location`, { location, menuId });\n\nexport const saveMenuItem = async ({ id, menuId, location, navItem, label, href, icon, newTab }) =>\n post(`${API_BASE}/menus/items`, { id, menuId, location, navItem, label, href, icon, newTab });\n\nexport const deleteMenuItems = async (ids) =>\n del(`${API_BASE}/menus/items`, { ids });\n\nexport const setMenuItemEnabled = async ({ id, enabled }) =>\n put(`${API_BASE}/menus/items/enabled`, { id, enabled });\n\nexport const reorderMenuItems = async ({ menuId, ids }) =>\n put(`${API_BASE}/menus/items/order`, { menuId, ids });\n\nexport const adoptMenuItem = async ({ id, menuId, menuName }) =>\n put(`${API_BASE}/menus/adopt`, { id, menuId, menuName });\n\n/*\n Extensions\n*/\n\nexport const listExtensions = async ({ limit, offset } = {}) =>\n get(`${API_BASE}/extensions`, { limit, offset });\n\nexport const listAvailableExtensions = async () =>\n get(`${API_BASE}/extensions/available`);\n\nexport const listKnownExtensions = async ({ search, sort, limit, offset } = {}) =>\n get(`${API_BASE}/extensions/known`, { search, sort, limit, offset });\n\nexport const getKnownExtension = async (name) =>\n get(`${API_BASE}/extensions/known`, { name });\n\nexport const rateExtension = async (name, rating) =>\n put(`${API_BASE}/extensions/rating`, { name, rating });\n\n/*\n Dashboard\n*/\n\nexport const getDashboardWidgets = async () =>\n get(`${API_BASE}/dashboard/widgets`);\n\n/*\n Notifications\n*/\n\nexport const getNotifications = async ({ unreadOnly = false, limit, offset } = {}) =>\n get(`${API_BASE}/notifications`, { unreadOnly, limit, offset });\n\nexport const getUnreadNotificationCount = async () =>\n get(`${API_BASE}/notifications/count`);\n\nexport const markNotificationRead = async (id) =>\n post(`${API_BASE}/notifications/${encodeURIComponent(id)}/read`, {});\n\nexport const markNotificationHandled = async (id) =>\n post(`${API_BASE}/notifications/${encodeURIComponent(id)}/handled`, {});\n\nexport const markAllNotificationsRead = async () =>\n post(`${API_BASE}/notifications/read-all`, {});\n\nexport const dismissNotification = async (id) =>\n del(`${API_BASE}/notifications/${encodeURIComponent(id)}`);\n\nexport const listRealtimeConnections = async () =>\n get(`${API_BASE}/realtime/connections`);\n\nexport const installExtension = async (name, { withDependencies = false } = {}) =>\n post(`${API_BASE}/extensions`, { name, withDependencies });\n\nexport const uninstallExtension = async (name, purgeData = false, { cascade = false } = {}) =>\n del(`${API_BASE}/extensions`, { name, purgeData, cascade });\n\nexport const enableExtension = async (name, { cascade = false } = {}) =>\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/enable`, { cascade });\n\nexport const getExtensionRequirements = async (name) =>\n get(`${API_BASE}/extensions/${encodeURIComponent(name)}/requirements`);\n\nexport const disableExtension = async (name, { cascade = false } = {}) =>\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/disable`, { cascade });\n\nexport const getExtensionDependents = async (name, { includeDisabled = false } = {}) =>\n get(`${API_BASE}/extensions/${encodeURIComponent(name)}/dependents`, { includeDisabled: includeDisabled ? 'true' : undefined });\n\n/*\n Admin Global Content\n*/\n\nexport const listAdminGlobalContent = async () =>\n get(`${API_BASE}/admin-globals`);\n\nexport const getAdminGlobalContent = async (id) =>\n get(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`);\n\nexport const createAdminGlobalContent = async ({ name, location, priority, owner, markup }) =>\n post(`${API_BASE}/admin-globals`, { name, location, priority, owner, markup });\n\nexport const updateAdminGlobalContent = async (id, { name, location, priority, markup }) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`, { name, location, priority, markup });\n\nexport const deleteAdminGlobalContent = async (ids) =>\n del(`${API_BASE}/admin-globals`, { ids });\n\nexport const enableAdminGlobalContent = async (id) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/enable`, {});\n\nexport const disableAdminGlobalContent = async (id) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/disable`, {});\n\n"],"mappings":"cAASA,UAAKC,SAAMC,WAAKC,SAAOC,MAAW,aAC3C,MAAMC,EAAW,oBAEV,MAAMC,SAAWC,OAASC,QAAOC,WAAUC,UAChDT,EAAK,GAAGI,wBAAgC,CAAEG,QAAOC,WAAUC,gBAEtD,MAAMC,MAAQJ,OAASC,QAAOC,cACnCR,EAAK,GAAGI,qBAA6B,CAAEG,QAAOC,oBAEzC,MAAMG,OAASL,SAAYN,EAAK,GAAGI,gBAAwB,CAAC,UAE5D,MAAMQ,WAAaN,SAAYP,EAAI,GAAGK,yBAEtC,MAAMS,eAAiBP,OAASC,WACrCP,EAAK,GAAGI,oBAA4B,CAAEG,iBAEjC,MAAMO,cAAgBR,OAASS,QAAOP,WAAUQ,aAAY,KACjEhB,EAAK,GAAGI,wBAAgC,CAAEW,QAAOE,YAAaT,EAAUQ,qBAEnE,MAAME,eAAiBZ,OAASa,kBAAiBF,cAAaD,eACnEhB,EAAK,GAAGI,yBAAiC,CAAEe,kBAAiBF,cAAaD,qBAEpE,MAAMI,YAAcd,OAASS,WAClCf,EAAK,GAAGI,sBAA8B,CAAEW,iBAEnC,MAAMM,yBAA2Bf,OAASgB,gBAC/CvB,EAAI,GAAGK,6BAAqC,CAAEkB,sBAEzC,MAAMC,6BAA+BjB,OAASkB,iBACnDzB,EAAI,GAAGK,iCAAyC,CAAEoB,uBAE7C,MAAMC,8BAAgCnB,OAASkB,iBACpDzB,EAAI,GAAGK,kCAA0C,CAAEoB,uBAE9C,MAAME,6BAA+BpB,SAC1CP,EAAI,GAAGK,0CAEF,MAAMuB,kBAAoBrB,OAASsB,SAAQN,gBAChDvB,EAAI,GAAGK,UAAiBwB,gBAAsB,CAAEN,sBAE3C,MAAMO,sBAAwBvB,OAASsB,SAAQJ,iBACpDzB,EAAI,GAAGK,UAAiBwB,oBAA0B,CAAEJ,uBAE/C,MAAMM,uBAAyBxB,OAASsB,SAAQJ,iBACrDzB,EAAI,GAAGK,UAAiBwB,qBAA2B,CAAEJ,uBAEhD,MAAMO,sBAAwBzB,OAASsB,YAC5C7B,EAAI,GAAGK,UAAiBwB,6BAEnB,MAAMI,SAAW1B,OAAS2B,QAAOC,UAAW,CAAC,IAClDnC,EAAI,GAAGK,SAAiB,CAAE6B,QAAOC,kBAE5B,MAAMC,YAAc7B,OAAS8B,IAAGH,QAAOC,SAAQG,UAASC,cAAe,CAAC,IAC7EvC,EAAI,GAAGK,gBAAwB,CAAEgC,IAAGH,QAAOC,SAAQG,UAASC,sBAEvD,MAAMC,WAAajC,OAASG,OAAMF,QAAOC,WAAUgC,mBACxDxC,EAAK,GAAGI,gBAAwB,CAAEK,OAAMF,QAAOC,WAAUgC,yBAEpD,MAAMC,YAAcnC,MAAOoC,GAChCvC,EAAI,GAAGC,SAAiB,CAAEsC,eAErB,MAAMC,WAAarC,OAASsC,KAAInC,OAAMF,QAAOiC,gBAAeK,eACjE3C,EAAM,GAAGE,SAAiB,CAAEwC,KAAInC,OAAMF,QAAOiC,gBAAeK,qBAEvD,MAAMC,QAAUxC,MAAOsB,GAC5B7B,EAAI,GAAGK,UAAiBwB,YAEnB,MAAMmB,gBAAkBzC,MAAOsB,GAAUK,QAAOC,UAAW,CAAC,IACjEnC,EAAI,GAAGK,UAAiBwB,aAAmB,CAAEK,QAAOC,kBAE/C,MAAMc,kBAAoB1C,MAAOsB,EAAQqB,IAC9C9C,EAAI,GAAGC,UAAiBwB,aAAmB,CAAEqB,wBAExC,MAAMC,0BAA4B5C,MAAOsB,GAC9CzB,EAAI,GAAGC,UAAiBwB,6BAEnB,MAAMuB,cAAgB7C,MAAOsB,GAAUK,QAAOC,UAAW,CAAC,IAC/DnC,EAAI,GAAGK,UAAiBwB,WAAiB,CAAEK,QAAOC,kBAE7C,MAAMkB,eAAiB9C,MAAOsB,EAAQyB,IAC3CrD,EAAK,GAAGI,UAAiBwB,WAAiB,CAAEyB,qBAEvC,MAAMC,oBAAsBhD,MAAOsB,EAAQyB,IAChDlD,EAAI,GAAGC,UAAiBwB,WAAiB,CAAEyB,qBAEtC,MAAME,WAAajD,OAAS2B,QAAOC,SAAQsB,SAAU,CAAC,IAC3DzD,EAAI,GAAGK,WAAmB,CAAE6B,QAAOC,SAAQsB,iBAEtC,MAAMC,YAAcnD,OAASG,OAAMiD,iBACxC1D,EAAK,GAAGI,WAAmB,CAAEK,OAAMiD,uBAE9B,MAAMC,YAAcrD,OAASG,OAAMmD,UAASF,iBACjDxD,EAAM,GAAGE,WAAmB,CAAEK,OAAMmD,UAASF,uBAExC,MAAMG,aAAevD,MAAOwD,GACjC3D,EAAI,GAAGC,WAAmB,CAAE0D,iBAEvB,MAAMC,SAAWzD,MAAOG,GAC7BV,EAAI,GAAGK,YAAmB4D,mBAAmBvD,aAExC,MAAMwD,gBAAkB3D,MAAOG,GAAQwB,QAAOC,UAAW,CAAC,IAC/DnC,EAAI,GAAGK,YAAmB4D,mBAAmBvD,aAAiB,CAAEwB,QAAOC,kBAElE,MAAMgC,iBAAmB5D,MAAOG,EAAM0D,IAC3CnE,EAAK,GAAGI,YAAmB4D,mBAAmBvD,aAAiB,CAAE0D,kBAE5D,MAAMC,sBAAwB9D,MAAOG,EAAM0D,IAChDhE,EAAI,GAAGC,YAAmB4D,mBAAmBvD,aAAiB,CAAE0D,kBAE3D,MAAME,oBAAsB/D,MAAOG,GACxCV,EAAI,GAAGK,YAAmB4D,mBAAmBvD,yBAExC,MAAM6D,qBAAuBhE,MAAOG,EAAM8D,IAC/CvE,EAAK,GAAGI,YAAmB4D,mBAAmBvD,iBAAqB,CAAE8D,0BAEhE,MAAMC,0BAA4BlE,MAAOG,EAAM8D,IACpDpE,EAAI,GAAGC,YAAmB4D,mBAAmBvD,iBAAqB,CAAE8D,0BAE/D,MAAME,gBAAkBnE,OAAS2B,QAAOC,SAAQsB,SAAU,CAAC,IAChEzD,EAAI,GAAGK,gBAAwB,CAAE6B,QAAOC,SAAQsB,iBAE3C,MAAMkB,kBAAoBpE,MAAOkB,GACtCxB,EAAK,GAAGI,gBAAwB,CAAEoB,uBAE7B,MAAMmD,iBAAmBrE,OAASG,OAAMiD,iBAC7CxD,EAAM,GAAGE,gBAAwB,CAAEK,OAAMiD,uBAEpC,MAAMkB,kBAAoBtE,MAAOwD,GACtC3D,EAAI,GAAGC,gBAAwB,CAAE0D,iBAE5B,MAAMe,WAAavE,MAAOkD,EAAO/C,IACtCV,EAAI,GAAGK,cAAqB4D,mBAAmBR,MAAUQ,mBAAmBvD,aAEvE,MAAMqE,aAAexE,OAASkD,QAAOvB,QAAOC,UAAW,CAAC,IAC7DnC,EAAI,GAAGK,cAAqB4D,mBAAmBR,KAAU,CAAEvB,QAAOC,kBAE7D,MAAM6C,WAAazE,MAAOkD,EAAO/C,EAAMuE,IAC5ChF,EAAK,GAAGI,cAAqB4D,mBAAmBR,MAAUQ,mBAAmBvD,KAASuE,UAEjF,MAAMC,cAAgB3E,MAAOkD,EAAO/C,IACzCN,EAAI,GAAGC,cAAqB4D,mBAAmBR,MAAUQ,mBAAmBvD,aAKvE,MAAMyE,WAAa5E,OAAS6E,SAAQC,OAAMC,SAAQpD,QAAOC,UAAW,CAAC,IAC1EnC,EAAI,GAAGK,WAAmB,CAAE+E,SAAQC,OAAMC,SAAQpD,QAAOC,kBAEpD,MAAMoD,SAAWhF,MAAOsC,GAC7B7C,EAAI,GAAGK,YAAmB4D,mBAAmBpB,aAExC,MAAM2C,WAAajF,MAAOsC,GAC/B5C,EAAK,GAAGI,YAAmB4D,mBAAmBpB,WAAa,CAAC,UAEvD,MAAM4C,cAAgBlF,MAAOmF,GAClCzF,EAAK,GAAGI,gBAAwB,CAAEqF,cAM7B,MAAMC,UAAYpF,OAASkD,QAAOmC,aAAYzD,SAAQD,SAAU,CAAC,IACtElC,EAAI,GAAGK,UAAkB,CAAEoD,QAAOmC,aAAYzD,SAAQD,iBAEjD,MAAM2D,YAActF,SACzBN,EAAK,GAAGI,iBAAyB,CAAC,UAE7B,MAAMyF,cAAgBvF,SAC3BP,EAAI,GAAGK,sBAEF,MAAM0F,gBAAkBxF,SAC7BP,EAAI,GAAGK,8BAEF,MAAM2F,QAAUzF,MAAO0F,GAC5BjG,EAAI,GAAGK,eAAuB,CAAE4F,gBAE3B,MAAMC,WAAa3F,OAAS4F,YAAWzF,OAAM0F,cAClDnG,EAAK,GAAGI,UAAkB,CAAE8F,YAAWzF,OAAM0F,oBAExC,MAAMC,WAAa9F,OAAS0F,OAAMvF,OAAM4F,QAAO3C,cAAa4C,SAAQH,WAAUI,WAAUC,mBAC7FvG,EAAI,GAAGG,eAAuB,CAAE4F,OAAMvF,OAAM4F,QAAO3C,cAAa4C,SAAQH,WAAUI,WAAUC,yBAEvF,MAAMC,YAAcnG,MAAOoG,GAChCvG,EAAI,GAAGC,UAAkB,CAAEsG,iBAEtB,MAAMC,YAAcrG,MAAO0F,GAChC/F,EAAI,GAAGG,kBAA0B,CAAE4F,gBAE9B,MAAMY,WAAatG,MAAO0F,GAC/B/F,EAAI,GAAGG,iBAAyB,CAAE4F,gBAE7B,MAAMa,SAAWvG,OAAS0F,OAAMc,aACrC5G,EAAM,GAAGE,eAAuB,CAAE4F,OAAMc,mBAEnC,MAAMC,sBAAwBzG,MAAO0G,GAC1CjH,EAAI,GAAGK,iBAAyB,CAAE4G,MAAOC,KAAKC,UAAUF,YAEnD,MAAMG,cAAgB7G,OAAS0F,OAAMoB,YAC1CnH,EAAI,GAAGG,eAAuB,CAAE4F,OAAMoB,kBAMjC,MAAMC,YAAc/G,MAAO0F,GAChCjG,EAAI,GAAGK,mBAA2B,CAAE4F,gBAE/B,MAAMsB,eAAiBhH,OAAS4F,YAAWzF,OAAM8G,cACtDvH,EAAK,GAAGI,cAAsB,CAAE8F,YAAWzF,OAAM8G,oBAE5C,MAAMC,eAAiBlH,OAAS0F,OAAMvF,OAAM6F,SAAQmB,YACzDxH,EAAI,GAAGG,mBAA2B,CAAE4F,OAAMvF,OAAM6F,SAAQmB,kBAEnD,MAAMC,gBAAkBpH,MAAOoG,GACpCvG,EAAI,GAAGC,cAAsB,CAAEsG,iBAE1B,MAAMiB,mBAAqBrH,MAAOkD,GACvCzD,EAAI,GAAGK,oBAA4BoD,EAAQ,CAAEA,SAAU,CAAC,UAEnD,MAAMoE,iBAAmBtH,MAAO6F,GACrCpG,EAAI,GAAGK,yBAAiC,CAAE+F,oBAErC,MAAM0B,kBAAoBvH,OAAS6F,WAAU2B,YAClD7H,EAAI,GAAGG,yBAAiC,CAAE+F,WAAU2B,kBAE/C,MAAMC,mBAAqBzH,MAAO6F,GACvChG,EAAI,GAAGC,yBAAiC,CAAE+F,oBAErC,MAAM6B,gBAAkB1H,MAAO0F,GACpC/F,EAAI,GAAGG,sBAA8B,CAAE4F,gBAElC,MAAMiC,eAAiB3H,MAAO0F,GACnC/F,EAAI,GAAGG,qBAA6B,CAAE4F,gBAEjC,MAAMkC,aAAe5H,OAAS0F,OAAMc,aACzC5G,EAAM,GAAGE,mBAA2B,CAAE4F,OAAMc,mBAEvC,MAAMqB,kBAAoB7H,OAAS0F,OAAMoB,YAC9CnH,EAAI,GAAGG,mBAA2B,CAAE4F,OAAMoB,kBAMrC,MAAMgB,cAAgB9H,SAC3BP,EAAI,GAAGK,sBAEF,MAAMiI,YAAc/H,MAAO0F,GAChCjG,EAAI,GAAGK,mBAA2B,CAAE4F,gBAE/B,MAAMsC,eAAiBhI,OAAS4F,YAAWzF,UAChDT,EAAK,GAAGI,cAAsB,CAAE8F,YAAWzF,gBAEtC,MAAM8H,eAAiBjI,OAAS0F,OAAMM,SAAQmB,YACnDxH,EAAI,GAAGG,mBAA2B,CAAE4F,OAAMM,SAAQmB,kBAE7C,MAAMe,gBAAkBlI,MAAOoG,GACpCvG,EAAI,GAAGC,cAAsB,CAAEsG,iBAE1B,MAAM+B,aAAenI,OAAS0F,OAAMc,aACzC5G,EAAM,GAAGE,mBAA2B,CAAE4F,OAAMc,mBAEvC,MAAM4B,gBAAkBpI,MAAO0F,GACpC/F,EAAI,GAAGG,sBAA8B,CAAE4F,gBAElC,MAAM2C,eAAiBrI,MAAO0F,GACnC/F,EAAI,GAAGG,qBAA6B,CAAE4F,gBAEjC,MAAM4C,kBAAoBtI,OAAS0F,OAAMoB,YAC9CnH,EAAI,GAAGG,mBAA2B,CAAE4F,OAAMoB,kBAMrC,MAAMyB,kBAAoBvI,SAC/BP,EAAI,GAAGK,oBAEF,MAAM0I,iBAAmBxI,MAAOsC,GACrC7C,EAAI,GAAGK,kBAA0B,CAAEwC,cAE9B,MAAMmG,oBAAsBzI,OAASG,OAAMuI,WAAUC,cAC1DjJ,EAAK,GAAGI,YAAoB,CAAEK,OAAMuI,WAAUC,oBAEzC,MAAMC,oBAAsB5I,OAASsC,KAAInC,OAAMuI,WAAUC,WAAUxB,YACxExH,EAAI,GAAGG,kBAA0B,CAAEwC,KAAInC,OAAMuI,WAAUC,WAAUxB,kBAE5D,MAAM0B,oBAAsB7I,MAAOoC,GACxCvC,EAAI,GAAGC,YAAoB,CAAEsC,eAExB,MAAM0G,qBAAuB9I,MAAOsC,GACzC3C,EAAI,GAAGG,oBAA4B,CAAEwC,cAEhC,MAAMyG,oBAAsB/I,MAAOsC,GACxC3C,EAAI,GAAGG,mBAA2B,CAAEwC,cAE/B,MAAM0G,uBAAyBhJ,OAASsC,KAAIwE,YACjDnH,EAAI,GAAGG,iBAAyB,CAAEwC,KAAIwE,kBAMjC,MAAMmC,UAAYjJ,SACvBP,EAAI,GAAGK,kBAEF,MAAMoJ,SAAWlJ,OAASsC,KAAInC,UACnCT,EAAK,GAAGI,UAAkB,CAAEwC,KAAInC,gBAE3B,MAAMgJ,WAAanJ,MAAOsC,GAC/BzC,EAAI,GAAGC,UAAkB,CAAEwC,cAEtB,MAAM8G,mBAAqBpJ,OAAS0I,WAAUW,YACnD1J,EAAI,GAAGG,mBAA2B,CAAE4I,WAAUW,kBAEzC,MAAMC,aAAetJ,OAASsC,KAAI+G,SAAQX,WAAUa,UAASC,QAAOC,OAAMC,OAAMC,YACrFjK,EAAK,GAAGI,gBAAwB,CAAEwC,KAAI+G,SAAQX,WAAUa,UAASC,QAAOC,OAAMC,OAAMC,kBAE/E,MAAMC,gBAAkB5J,MAAOoC,GACpCvC,EAAI,GAAGC,gBAAwB,CAAEsC,eAE5B,MAAMyH,mBAAqB7J,OAASsC,KAAIwH,aAC7CnK,EAAI,GAAGG,wBAAgC,CAAEwC,KAAIwH,mBAExC,MAAMC,iBAAmB/J,OAASqJ,SAAQjH,SAC/CzC,EAAI,GAAGG,sBAA8B,CAAEuJ,SAAQjH,eAE1C,MAAM4H,cAAgBhK,OAASsC,KAAI+G,SAAQY,cAChDtK,EAAI,GAAGG,gBAAwB,CAAEwC,KAAI+G,SAAQY,oBAMxC,MAAMC,eAAiBlK,OAAS2B,QAAOC,UAAW,CAAC,IACxDnC,EAAI,GAAGK,eAAuB,CAAE6B,QAAOC,kBAElC,MAAMuI,wBAA0BnK,SACrCP,EAAI,GAAGK,iCAEF,MAAMsK,oBAAsBpK,OAAS+E,SAAQsF,OAAM1I,QAAOC,UAAW,CAAC,IAC3EnC,EAAI,GAAGK,qBAA6B,CAAEiF,SAAQsF,OAAM1I,QAAOC,kBAEtD,MAAM0I,kBAAoBtK,MAAOG,GACtCV,EAAI,GAAGK,qBAA6B,CAAEK,gBAEjC,MAAMoK,cAAgBvK,MAAOG,EAAMqK,IACxC7K,EAAI,GAAGG,sBAA8B,CAAEK,OAAMqK,kBAMxC,MAAMC,oBAAsBzK,SACjCP,EAAI,GAAGK,8BAMF,MAAM4K,iBAAmB1K,OAAS2K,cAAa,EAAOhJ,QAAOC,UAAW,CAAC,IAC9EnC,EAAI,GAAGK,kBAA0B,CAAE6K,aAAYhJ,QAAOC,kBAEjD,MAAMgJ,2BAA6B5K,SACxCP,EAAI,GAAGK,gCAEF,MAAM+K,qBAAuB7K,MAAOsC,GACzC5C,EAAK,GAAGI,mBAA0B4D,mBAAmBpB,UAAY,CAAC,UAE7D,MAAMwI,wBAA0B9K,MAAOsC,GAC5C5C,EAAK,GAAGI,mBAA0B4D,mBAAmBpB,aAAe,CAAC,UAEhE,MAAMyI,yBAA2B/K,SACtCN,EAAK,GAAGI,2BAAmC,CAAC,UAEvC,MAAMkL,oBAAsBhL,MAAOsC,GACxCzC,EAAI,GAAGC,mBAA0B4D,mBAAmBpB,aAE/C,MAAM2I,wBAA0BjL,SACrCP,EAAI,GAAGK,iCAEF,MAAMoL,iBAAmBlL,MAAOG,GAAQgL,oBAAmB,GAAU,CAAC,IAC3EzL,EAAK,GAAGI,eAAuB,CAAEK,OAAMgL,4BAElC,MAAMC,mBAAqBpL,MAAOG,EAAMkL,GAAY,GAASC,WAAU,GAAU,CAAC,IACvFzL,EAAI,GAAGC,eAAuB,CAAEK,OAAMkL,YAAWC,mBAE5C,MAAMC,gBAAkBvL,MAAOG,GAAQmL,WAAU,GAAU,CAAC,IACjE5L,EAAK,GAAGI,gBAAuB4D,mBAAmBvD,YAAgB,CAAEmL,mBAE/D,MAAME,yBAA2BxL,MAAOG,GAC7CV,EAAI,GAAGK,gBAAuB4D,mBAAmBvD,0BAE5C,MAAMsL,iBAAmBzL,MAAOG,GAAQmL,WAAU,GAAU,CAAC,IAClE5L,EAAK,GAAGI,gBAAuB4D,mBAAmBvD,aAAiB,CAAEmL,mBAEhE,MAAMI,uBAAyB1L,MAAOG,GAAQwL,mBAAkB,GAAU,CAAC,IAChFlM,EAAI,GAAGK,gBAAuB4D,mBAAmBvD,gBAAoB,CAAEwL,gBAAiBA,EAAkB,YAASC,WAM9G,MAAMC,uBAAyB7L,SACpCP,EAAI,GAAGK,0BAEF,MAAMgM,sBAAwB9L,MAAOsC,GAC1C7C,EAAI,GAAGK,mBAA0B4D,mBAAmBpB,aAE/C,MAAMyJ,yBAA2B/L,OAASG,OAAMuI,WAAUC,WAAUzF,QAAOiE,YAChFzH,EAAK,GAAGI,kBAA0B,CAAEK,OAAMuI,WAAUC,WAAUzF,QAAOiE,kBAEhE,MAAM6E,yBAA2BhM,MAAOsC,GAAMnC,OAAMuI,WAAUC,WAAUxB,YAC7ExH,EAAI,GAAGG,mBAA0B4D,mBAAmBpB,KAAO,CAAEnC,OAAMuI,WAAUC,WAAUxB,kBAElF,MAAM8E,yBAA2BjM,MAAOoC,GAC7CvC,EAAI,GAAGC,kBAA0B,CAAEsC,eAE9B,MAAM8J,yBAA2BlM,MAAOsC,GAC7C3C,EAAI,GAAGG,mBAA0B4D,mBAAmBpB,YAAc,CAAC,UAE9D,MAAM6J,0BAA4BnM,MAAOsC,GAC9C3C,EAAI,GAAGG,mBAA0B4D,mBAAmBpB,aAAe,CAAC","ignoreList":[]}
|
package/middleware/kempo.js
CHANGED
|
@@ -13,6 +13,9 @@ import triggerHook from '../server/utils/hooks/triggerHook.js';
|
|
|
13
13
|
import { ADMIN_GLOBALS_DIR } from '../server/utils/admin-global-content/helpers.js';
|
|
14
14
|
import { startScheduler } from '../server/utils/scheduler/scheduler.js';
|
|
15
15
|
import { ensureEncryptionKey } from '../server/utils/settings/ensureEncryptionKey.js';
|
|
16
|
+
import { ensureSystemSettings } from '../server/utils/settings/systemSettings.js';
|
|
17
|
+
import { ensureSystemPermissions } from '../server/utils/setup/systemPermissions.js';
|
|
18
|
+
import registerJobs from '../server/utils/scheduler/registerJobs.js';
|
|
16
19
|
import { resolveAlias } from '../server/utils/extensions/aliases.js';
|
|
17
20
|
import { normalizeScopes } from '../server/utils/extensions/scopes.js';
|
|
18
21
|
|
|
@@ -194,6 +197,23 @@ export default config => {
|
|
|
194
197
|
: ensureEncryptionKey().then(([error]) => {
|
|
195
198
|
if(error) console.error(`[kempo] ${error.msg}`);
|
|
196
199
|
});
|
|
200
|
+
/*
|
|
201
|
+
What kempo added in a later version appears on an older site at start: its settings (so they show on the settings screen and can be
|
|
202
|
+
edited), its permissions, and its own scheduled job (the email retries and cleanup). Only what is missing is added, and never in
|
|
203
|
+
the in-memory test database (nothing is set up there yet).
|
|
204
|
+
*/
|
|
205
|
+
const systemSettingsReady = process.env.KEMPO_DB === 'pglite'
|
|
206
|
+
? Promise.resolve()
|
|
207
|
+
: (async () => {
|
|
208
|
+
for(const [step, run] of [
|
|
209
|
+
['settings', ensureSystemSettings],
|
|
210
|
+
['permissions', ensureSystemPermissions],
|
|
211
|
+
['jobs', () => registerJobs({ owner: 'kempo', jobs: [{ name: 'email-maintenance', every: '1m', callback: './server/utils/email/maintainEmails.js' }] })],
|
|
212
|
+
]){
|
|
213
|
+
const [error] = await run();
|
|
214
|
+
if(error) console.error(`[kempo] System ${step}: ${error.msg}`);
|
|
215
|
+
}
|
|
216
|
+
})();
|
|
197
217
|
const PROJECT_PUBLIC = config.rootPath || join(process.cwd(), 'public');
|
|
198
218
|
const ADMIN_ROOT = resolveAdminRoot(config.adminRoot);
|
|
199
219
|
|
|
@@ -206,6 +226,7 @@ export default config => {
|
|
|
206
226
|
|
|
207
227
|
return async (request, response, next) => {
|
|
208
228
|
await encryptionKeyReady;
|
|
229
|
+
await systemSettingsReady;
|
|
209
230
|
const { path } = request;
|
|
210
231
|
const url = request.url.split('?')[0];
|
|
211
232
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kempo",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A fullstack CMS framework for creating websites with user authentication",
|
|
6
6
|
"scripts": {
|
|
@@ -54,8 +54,7 @@
|
|
|
54
54
|
"inquirer": "^14.0.2",
|
|
55
55
|
"lexical": "0.43.0",
|
|
56
56
|
"monaco-editor": "^0.56.0",
|
|
57
|
-
"postgres": "^3.4.7"
|
|
58
|
-
"resend": "^6.5.2"
|
|
57
|
+
"postgres": "^3.4.7"
|
|
59
58
|
},
|
|
60
59
|
"peerDependencies": {
|
|
61
60
|
"kempo-css": ">=2.2.5",
|
package/server/db/schema.js
CHANGED
|
@@ -138,6 +138,38 @@ export const scheduledJob = pgTable('scheduledJob', {
|
|
|
138
138
|
index('scheduledJob_next_idx').on(table.nextRunAt),
|
|
139
139
|
]);
|
|
140
140
|
|
|
141
|
+
/*
|
|
142
|
+
Email
|
|
143
|
+
|
|
144
|
+
Every email kempo is asked to send is a row here before anything is sent, so there is always a record of what was
|
|
145
|
+
sent, to whom, and what happened. Kempo itself sends nothing: it fires the `email:send` hook and an email extension
|
|
146
|
+
(kempo-resend, kempo-smtp...) sends it.
|
|
147
|
+
|
|
148
|
+
`status` is `queued` (waiting for a sender, or for a retry), `sending` (a sender is working on it now), `sent` or
|
|
149
|
+
`failed` (no attempts left). `type` is the template it was made from (`verify-email`, `kempo-commerce/order-processing`)
|
|
150
|
+
or `other`, and is what the retention settings are keyed by. `html` is the whole email, which can hold links that
|
|
151
|
+
sign someone in or set a password, so reading the log needs its own permission.
|
|
152
|
+
*/
|
|
153
|
+
export const email = pgTable('email', {
|
|
154
|
+
id: text('id').primaryKey(),
|
|
155
|
+
type: text('type').notNull().default('other'),
|
|
156
|
+
recipient: text('recipient').notNull(),
|
|
157
|
+
sender: text('sender').notNull(),
|
|
158
|
+
subject: text('subject').notNull(),
|
|
159
|
+
html: text('html').notNull(),
|
|
160
|
+
status: text('status').notNull().default('queued'),
|
|
161
|
+
attempts: integer('attempts').notNull().default(0),
|
|
162
|
+
lastError: text('lastError'),
|
|
163
|
+
provider: text('provider'),
|
|
164
|
+
providerRef: text('providerRef'),
|
|
165
|
+
createdAt: timestamp('createdAt').notNull(),
|
|
166
|
+
updatedAt: timestamp('updatedAt').notNull(),
|
|
167
|
+
sentAt: timestamp('sentAt'),
|
|
168
|
+
}, table => [
|
|
169
|
+
index('email_status_idx').on(table.status, table.updatedAt),
|
|
170
|
+
index('email_createdAt_idx').on(table.createdAt),
|
|
171
|
+
]);
|
|
172
|
+
|
|
141
173
|
/*
|
|
142
174
|
Realtime System
|
|
143
175
|
*/
|
package/server/sdk.js
CHANGED
|
@@ -186,6 +186,11 @@ export { startScheduler, stopScheduler } from './utils/scheduler/scheduler.js';
|
|
|
186
186
|
export { default as sendEmail } from './utils/email/sendEmail.js';
|
|
187
187
|
export { default as sendEmailFromTemplate } from './utils/email/sendEmailFromTemplate.js';
|
|
188
188
|
export { default as renderEmailTemplate } from './utils/email/renderEmailTemplate.js';
|
|
189
|
+
export { default as getSenderEmail } from './utils/email/getSenderEmail.js';
|
|
190
|
+
export { default as listEmails } from './utils/email/listEmails.js';
|
|
191
|
+
export { default as getEmail } from './utils/email/getEmail.js';
|
|
192
|
+
export { default as retryEmail } from './utils/email/retryEmail.js';
|
|
193
|
+
export { default as pruneEmails } from './utils/email/pruneEmails.js';
|
|
189
194
|
|
|
190
195
|
/*
|
|
191
196
|
Filesystem Utilities
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import db from '../../db/index.js';
|
|
2
|
+
import { email } from '../../db/schema.js';
|
|
3
|
+
import { and, eq, sql } from 'drizzle-orm';
|
|
4
|
+
import triggerHook from '../hooks/triggerHook.js';
|
|
5
|
+
import emailSettings from './emailSettings.js';
|
|
6
|
+
import defaultSender from './defaultSender.js';
|
|
7
|
+
|
|
8
|
+
export const SEND_HOOK = 'email:send';
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
One try at sending a queued email.
|
|
12
|
+
|
|
13
|
+
The email is taken (queued -> sending, one conditional UPDATE, so of several processes only one sends it) and the `email:send`
|
|
14
|
+
hook is fired with `{ email }`. Each email extension answers with what it did, and the first to say it sent it ends the chain, so
|
|
15
|
+
with several installed only one sends and a failed one hands the email to the next:
|
|
16
|
+
|
|
17
|
+
export default async ({ email }) => ({ sent: true, provider: 'my-sender', messageId: 'abc' }); // it went
|
|
18
|
+
export default async ({ email }) => ({ sent: false, error: 'Mailbox full' }); // it tried and could not (throwing says the same)
|
|
19
|
+
export default async ({ email }) => ({ sent: false, skipped: true, error: 'No API key is saved' }); // it is not set up, so it did not try
|
|
20
|
+
|
|
21
|
+
`email` is `{ id, to, from, fromIsDefault, subject, html, type, attempt }`; `fromIsDefault` says the site has not set its own sender, so
|
|
22
|
+
`from` is only a placeholder and a sender that knows a better one (the mailbox it signs in with) can use that. A handler sends the email and returns; it never touches the log, which
|
|
23
|
+
kempo keeps. After the chain the email is `sent`, or goes back to `queued` for a retry, or is `failed` when it has no tries left
|
|
24
|
+
(the first try plus `email_max_retries`). Resolves like sendEmail.
|
|
25
|
+
*/
|
|
26
|
+
const finish = async (id, changes) => {
|
|
27
|
+
const [row] = await db.update(email).set({ ...changes, updatedAt: new Date() }).where(eq(email.id, id)).returning();
|
|
28
|
+
return row;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default async id => {
|
|
32
|
+
const [claimed] = await db.update(email)
|
|
33
|
+
.set({ status: 'sending', attempts: sql`${email.attempts} + 1`, updatedAt: new Date() })
|
|
34
|
+
.where(and(eq(email.id, id), eq(email.status, 'queued')))
|
|
35
|
+
.returning();
|
|
36
|
+
if(!claimed) return [{ code: 409, msg: 'That email is not waiting to be sent', emailId: id }, null];
|
|
37
|
+
|
|
38
|
+
const [hookError, fired] = await triggerHook(SEND_HOOK, { email: { id: claimed.id, to: claimed.recipient, from: claimed.sender, fromIsDefault: claimed.sender === defaultSender, subject: claimed.subject, html: claimed.html, type: claimed.type, attempt: claimed.attempts } }, { stopWhen: result => result?.sent === true });
|
|
39
|
+
const results = fired?.results ?? [];
|
|
40
|
+
const sent = results.find(entry => entry.result?.sent === true);
|
|
41
|
+
|
|
42
|
+
if(sent){
|
|
43
|
+
await finish(id, { status: 'sent', sentAt: new Date(), lastError: null, provider: String(sent.result.provider ?? sent.owner).slice(0, 100), providerRef: sent.result.messageId ? String(sent.result.messageId).slice(0, 200) : null });
|
|
44
|
+
return [null, { id, status: 'sent', provider: sent.result.provider ?? sent.owner }];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* An extension that is not set up says so and is not counted as having tried: its reason is only shown when nothing else did. */
|
|
48
|
+
const said = entry => `${entry.owner}: ${entry.error ?? entry.result?.error ?? 'did not send it'}`;
|
|
49
|
+
const tried = results.filter(entry => !entry.result?.skipped);
|
|
50
|
+
const reasons = hookError ? [hookError.msg] : (tried.length ? tried : results).map(said);
|
|
51
|
+
const message = (tried.length || hookError ? reasons.join('; ') : results.length ? `No email extension is set up yet. ${reasons.join('; ')}` : '') || 'No email extension is installed to send email (install kempo-resend or kempo-smtp)';
|
|
52
|
+
const { maxRetries } = await emailSettings();
|
|
53
|
+
const final = claimed.attempts >= 1 + maxRetries;
|
|
54
|
+
await finish(id, { status: final ? 'failed' : 'queued', lastError: message.slice(0, 1000) });
|
|
55
|
+
return [{ code: tried.length ? 502 : 503, msg: message, emailId: id }, null];
|
|
56
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import getSetting from '../settings/getSetting.js';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_RETRIES = 1;
|
|
4
|
+
const DEFAULT_RETENTION_DAYS = 30;
|
|
5
|
+
|
|
6
|
+
const whole = (value, fallback) => (Number.isInteger(Number(value)) && value !== '' && value !== null && Number(value) >= 0 ? Number(value) : fallback);
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
What the email settings say, with a safe value for anything unreadable: `maxRetries` (tries after the first), `retentionDays` (0 keeps
|
|
10
|
+
forever) and `retentionByType` ({ type: days }), which wins over `retentionDays` for that kind of email.
|
|
11
|
+
*/
|
|
12
|
+
export default async () => {
|
|
13
|
+
const [, retries] = await getSetting('system', 'email_max_retries', DEFAULT_RETRIES);
|
|
14
|
+
const [, days] = await getSetting('system', 'email_retention_days', DEFAULT_RETENTION_DAYS);
|
|
15
|
+
const [, byType] = await getSetting('system', 'email_retention_by_type', {});
|
|
16
|
+
const retentionByType = {};
|
|
17
|
+
if(byType && typeof byType === 'object' && !Array.isArray(byType)){
|
|
18
|
+
for(const [type, value] of Object.entries(byType)) if(whole(value, null) !== null) retentionByType[type] = Number(value);
|
|
19
|
+
}
|
|
20
|
+
return { maxRetries: whole(retries, DEFAULT_RETRIES), retentionDays: whole(days, DEFAULT_RETENTION_DAYS), retentionByType };
|
|
21
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { join, dirname } from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
|
|
4
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const PART = '[a-z0-9][a-z0-9-]*';
|
|
6
|
+
const NAME = new RegExp(`^(?:(${PART})/)?(${PART})$`);
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
Where an email template lives, the same places renderEmailTemplate looks:
|
|
10
|
+
|
|
11
|
+
"owner/name" the extension's own `node_modules/owner/emails/name.page.html`, which the site overrides at
|
|
12
|
+
`templates/emails/owner/name.page.html`
|
|
13
|
+
"name" kempo's own `app-templates/emails/name.page.html`, which the site overrides at
|
|
14
|
+
`templates/emails/name.page.html`
|
|
15
|
+
*/
|
|
16
|
+
export const parseTemplateName = template => {
|
|
17
|
+
const match = typeof template === 'string' ? NAME.exec(template) : null;
|
|
18
|
+
return match ? { owner: match[1] ?? null, name: match[2] } : null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const siteEmailsDir = owner => (owner ? join(process.cwd(), 'templates/emails', owner) : join(process.cwd(), 'templates/emails'));
|
|
22
|
+
|
|
23
|
+
export const defaultEmailsDir = owner => (owner ? join(process.cwd(), 'node_modules', owner, 'emails') : join(here, '../../../app-templates/emails'));
|
|
24
|
+
|
|
25
|
+
export const locateTemplate = template => {
|
|
26
|
+
const parsed = parseTemplateName(template);
|
|
27
|
+
if(!parsed) return null;
|
|
28
|
+
return { ...parsed, template, sitePath: join(siteEmailsDir(parsed.owner), `${parsed.name}.page.html`), defaultPath: join(defaultEmailsDir(parsed.owner), `${parsed.name}.page.html`) };
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const isOwnerName = owner => new RegExp(`^${PART}$`).test(owner);
|
|
32
|
+
|
|
33
|
+
/* The page's `title`, which is what a list shows as the email's name. */
|
|
34
|
+
export const titleOf = source => /<page\b[^>]*\stitle="([^"]*)"/.exec(source)?.[1] ?? '';
|
|
35
|
+
|
|
36
|
+
export const sameSource = (a, b) => String(a).replace(/\r\n/g, '\n').trim() === String(b).replace(/\r\n/g, '\n').trim();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import db from '../../db/index.js';
|
|
2
|
+
import { email } from '../../db/schema.js';
|
|
3
|
+
import { eq } from 'drizzle-orm';
|
|
4
|
+
|
|
5
|
+
/* One email of the log, with its whole body. Resolves to [error, email]. */
|
|
6
|
+
export default async id => {
|
|
7
|
+
if(!id) return [{ code: 400, msg: 'An email id is required' }, null];
|
|
8
|
+
try {
|
|
9
|
+
const [row] = await db.select().from(email).where(eq(email.id, String(id))).limit(1);
|
|
10
|
+
return row ? [null, row] : [{ code: 404, msg: 'Email not found' }, null];
|
|
11
|
+
} catch {
|
|
12
|
+
return [{ code: 500, msg: 'Failed to read the email' }, null];
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import getSenderFrom from './getSenderFrom.js';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
The bare address kempo sends its emails from (`shop@example.com`, out of `Shop <shop@example.com>`), for an extension that needs
|
|
5
|
+
one for the site, such as a shipping label's sender. It is the same address emails go out from (see getSenderFrom), so it is a
|
|
6
|
+
placeholder (noreply@example.com) until the site sets one. Resolves to the address, or null if there is none to find.
|
|
7
|
+
*/
|
|
8
|
+
export default async () => {
|
|
9
|
+
const from = (await getSenderFrom()).trim();
|
|
10
|
+
return (/<([^<>\s]+@[^<>\s]+)>/.exec(from) ?? /^([^<>\s]+@[^<>\s]+)$/.exec(from))?.[1] ?? null;
|
|
11
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import getSetting from '../settings/getSetting.js';
|
|
2
|
+
import isValidSender from './isValidSender.js';
|
|
3
|
+
import PLACEHOLDER from './defaultSender.js';
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
What kempo sends its emails from, as the email provider takes it. In order:
|
|
7
|
+
|
|
8
|
+
the `system:email_from` setting edited on the admin Settings screen (it must be a valid address, or empty)
|
|
9
|
+
`EMAIL_FROM` the server environment; what the README and `kempo init` write
|
|
10
|
+
`SMTP_FROM` what the environment used to be called
|
|
11
|
+
a placeholder address (noreply@example.com), which most providers will refuse until the site sets its own
|
|
12
|
+
|
|
13
|
+
A value that is not an address is skipped rather than sent, so a bad environment value cannot stop every email.
|
|
14
|
+
*/
|
|
15
|
+
export default async () => {
|
|
16
|
+
const [, stored] = await getSetting('system', 'email_from', '');
|
|
17
|
+
return [stored, process.env.EMAIL_FROM, process.env.SMTP_FROM].map(value => String(value ?? '').trim()).find(isValidSender) ?? PLACEHOLDER;
|
|
18
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/* Whether text can be the address emails are sent from: `name@example.com` or `Shop Name <name@example.com>`, on one line. */
|
|
2
|
+
export default value => typeof value === 'string'
|
|
3
|
+
&& value.length <= 200
|
|
4
|
+
&& /^(?:[^<>\r\n@]+<[^<>\s@]+@[^<>\s@]+\.[^<>\s@]+>|[^<>\s@]+@[^<>\s@]+\.[^<>\s@]+)$/.test(value.trim());
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { readdir } from 'fs/promises';
|
|
2
|
+
import { defaultEmailsDir, isOwnerName, siteEmailsDir } from './emailTemplatePaths.js';
|
|
3
|
+
import readEmailTemplate from './readEmailTemplate.js';
|
|
4
|
+
|
|
5
|
+
const SUFFIX = '.page.html';
|
|
6
|
+
|
|
7
|
+
const namesIn = async dir => (await readdir(dir).catch(() => [])).filter(file => file.endsWith(SUFFIX)).map(file => file.slice(0, -SUFFIX.length));
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
The email templates of one extension (`owner`), or kempo's own and the site's (no owner): the packaged ones, and any the site has
|
|
11
|
+
added. Resolves to [error, [{ template, name, title, overridden, packaged }]] sorted by name.
|
|
12
|
+
*/
|
|
13
|
+
export default async ({ owner = null } = {}) => {
|
|
14
|
+
if(owner !== null && !isOwnerName(owner)) return [{ code: 400, msg: 'Invalid extension name' }, null];
|
|
15
|
+
|
|
16
|
+
const names = [...new Set([...await namesIn(defaultEmailsDir(owner)), ...await namesIn(siteEmailsDir(owner))])].sort();
|
|
17
|
+
const templates = [];
|
|
18
|
+
for(const name of names){
|
|
19
|
+
const [error, found] = await readEmailTemplate({ template: owner ? `${owner}/${name}` : name });
|
|
20
|
+
if(!error) templates.push({ template: found.template, name, title: found.title, overridden: found.overridden, packaged: found.defaultSource !== null });
|
|
21
|
+
}
|
|
22
|
+
return [null, templates];
|
|
23
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import db from '../../db/index.js';
|
|
2
|
+
import { email } from '../../db/schema.js';
|
|
3
|
+
import { and, desc, eq, ilike, or, sql } from 'drizzle-orm';
|
|
4
|
+
|
|
5
|
+
const STATUSES = ['queued', 'sending', 'sent', 'failed'];
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
The log, newest first, without the bodies (read one with getEmail). `status`, `type` and `search` (a part of the recipient or the
|
|
9
|
+
subject) narrow it. Resolves to [error, { emails, total, limit, offset }].
|
|
10
|
+
*/
|
|
11
|
+
export default async ({ status, type, search, limit = 50, offset = 0 } = {}) => {
|
|
12
|
+
try {
|
|
13
|
+
const conditions = [];
|
|
14
|
+
if(status){
|
|
15
|
+
if(!STATUSES.includes(status)) return [{ code: 400, msg: `Status must be one of ${STATUSES.join(', ')}` }, null];
|
|
16
|
+
conditions.push(eq(email.status, status));
|
|
17
|
+
}
|
|
18
|
+
if(type) conditions.push(eq(email.type, String(type)));
|
|
19
|
+
if(search){
|
|
20
|
+
const like = `%${String(search).replace(/[\\%_]/g, '\\$&')}%`;
|
|
21
|
+
conditions.push(or(ilike(email.recipient, like), ilike(email.subject, like)));
|
|
22
|
+
}
|
|
23
|
+
const where = conditions.length ? and(...conditions) : undefined;
|
|
24
|
+
const take = Math.min(Math.max(Number(limit) || 50, 1), 200);
|
|
25
|
+
const skip = Math.max(Number(offset) || 0, 0);
|
|
26
|
+
const emails = await db.select({
|
|
27
|
+
id: email.id, type: email.type, recipient: email.recipient, sender: email.sender, subject: email.subject, status: email.status, attempts: email.attempts,
|
|
28
|
+
lastError: email.lastError, provider: email.provider, createdAt: email.createdAt, updatedAt: email.updatedAt, sentAt: email.sentAt,
|
|
29
|
+
}).from(email).where(where).orderBy(desc(email.createdAt)).limit(take).offset(skip);
|
|
30
|
+
const [{ total }] = await db.select({ total: sql`count(*)::int` }).from(email).where(where);
|
|
31
|
+
return [null, { emails, total, limit: take, offset: skip }];
|
|
32
|
+
} catch {
|
|
33
|
+
return [{ code: 500, msg: 'Failed to read the email log' }, null];
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import db from '../../db/index.js';
|
|
2
|
+
import { email } from '../../db/schema.js';
|
|
3
|
+
import { and, asc, eq, lt, or, gt, sql } from 'drizzle-orm';
|
|
4
|
+
import dispatchEmail from './dispatchEmail.js';
|
|
5
|
+
import emailSettings from './emailSettings.js';
|
|
6
|
+
import pruneEmails from './pruneEmails.js';
|
|
7
|
+
|
|
8
|
+
const MINUTE_MS = 60 * 1000;
|
|
9
|
+
const STUCK_MS = 5 * MINUTE_MS;
|
|
10
|
+
const BACKOFF_MS = MINUTE_MS;
|
|
11
|
+
const TOO_OLD_MS = 24 * 60 * MINUTE_MS;
|
|
12
|
+
const BATCH = 50;
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
The email job, run every minute by the scheduler (kempo registers it itself):
|
|
16
|
+
|
|
17
|
+
- a `sending` email that has not finished in five minutes (the process died) goes back to `queued`;
|
|
18
|
+
- a `queued` email with tries left is sent again, a minute after its last try at the earliest, up to 50 at a time;
|
|
19
|
+
- a `queued` email older than a day is marked `failed` instead, so a password-reset link is never sent a day late;
|
|
20
|
+
- emails older than the retention settings are deleted.
|
|
21
|
+
*/
|
|
22
|
+
export default async () => {
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
const { maxRetries } = await emailSettings();
|
|
25
|
+
|
|
26
|
+
await db.update(email)
|
|
27
|
+
.set({ status: 'queued', lastError: 'The send did not finish' })
|
|
28
|
+
.where(and(eq(email.status, 'sending'), lt(email.updatedAt, new Date(now - STUCK_MS))));
|
|
29
|
+
|
|
30
|
+
await db.update(email)
|
|
31
|
+
.set({ status: 'failed', lastError: sql`coalesce(${email.lastError}, 'Never sent') || ' (too old to send now)'`, updatedAt: new Date() })
|
|
32
|
+
.where(and(eq(email.status, 'queued'), lt(email.createdAt, new Date(now - TOO_OLD_MS))));
|
|
33
|
+
|
|
34
|
+
const due = await db.select({ id: email.id }).from(email)
|
|
35
|
+
.where(and(
|
|
36
|
+
eq(email.status, 'queued'),
|
|
37
|
+
gt(email.createdAt, new Date(now - TOO_OLD_MS)),
|
|
38
|
+
lt(email.attempts, 1 + maxRetries),
|
|
39
|
+
or(eq(email.attempts, 0), lt(email.updatedAt, new Date(now - BACKOFF_MS))),
|
|
40
|
+
))
|
|
41
|
+
.orderBy(asc(email.createdAt))
|
|
42
|
+
.limit(BATCH);
|
|
43
|
+
for(const { id } of due) await dispatchEmail(id);
|
|
44
|
+
|
|
45
|
+
await pruneEmails();
|
|
46
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import db from '../../db/index.js';
|
|
2
|
+
import { email } from '../../db/schema.js';
|
|
3
|
+
import { and, lt, eq, notInArray } from 'drizzle-orm';
|
|
4
|
+
import emailSettings from './emailSettings.js';
|
|
5
|
+
|
|
6
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
Deletes the emails that are older than the settings keep them. A kind of email in `email_retention_by_type` is kept that many days,
|
|
10
|
+
every other kind `email_retention_days`; 0 keeps it forever. Resolves to [error, { deleted }].
|
|
11
|
+
*/
|
|
12
|
+
export default async ({ now = new Date() } = {}) => {
|
|
13
|
+
try {
|
|
14
|
+
const { retentionDays, retentionByType } = await emailSettings();
|
|
15
|
+
const specific = Object.keys(retentionByType);
|
|
16
|
+
let deleted = 0;
|
|
17
|
+
for(const [type, days] of Object.entries(retentionByType)){
|
|
18
|
+
if(days === 0) continue;
|
|
19
|
+
deleted += (await db.delete(email).where(and(eq(email.type, type), lt(email.createdAt, new Date(now.getTime() - days * DAY_MS)))).returning({ id: email.id })).length;
|
|
20
|
+
}
|
|
21
|
+
if(retentionDays > 0){
|
|
22
|
+
const older = lt(email.createdAt, new Date(now.getTime() - retentionDays * DAY_MS));
|
|
23
|
+
deleted += (await db.delete(email).where(specific.length ? and(notInArray(email.type, specific), older) : older).returning({ id: email.id })).length;
|
|
24
|
+
}
|
|
25
|
+
return [null, { deleted }];
|
|
26
|
+
} catch {
|
|
27
|
+
return [{ code: 500, msg: 'Failed to delete old emails' }, null];
|
|
28
|
+
}
|
|
29
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { locateTemplate, sameSource, titleOf } from './emailTemplatePaths.js';
|
|
3
|
+
|
|
4
|
+
const read = path => readFile(path, 'utf8').catch(() => null);
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
One email template as the site would send it: `source` is the site's own version when it has one and the packaged one
|
|
8
|
+
otherwise, `defaultSource` is the packaged one (null when there is none), and `overridden` says the site's differs from it.
|
|
9
|
+
Resolves to [error, { template, owner, name, title, source, defaultSource, overridden }].
|
|
10
|
+
*/
|
|
11
|
+
export default async ({ template } = {}) => {
|
|
12
|
+
const located = locateTemplate(template);
|
|
13
|
+
if(!located) return [{ code: 400, msg: 'Invalid email template name' }, null];
|
|
14
|
+
|
|
15
|
+
const [site, packaged] = await Promise.all([read(located.sitePath), read(located.defaultPath)]);
|
|
16
|
+
if(site === null && packaged === null) return [{ code: 404, msg: `Email template not found: ${template}` }, null];
|
|
17
|
+
|
|
18
|
+
const source = site ?? packaged;
|
|
19
|
+
return [null, {
|
|
20
|
+
template,
|
|
21
|
+
owner: located.owner,
|
|
22
|
+
name: located.name,
|
|
23
|
+
title: titleOf(source),
|
|
24
|
+
source,
|
|
25
|
+
defaultSource: packaged,
|
|
26
|
+
overridden: site !== null && packaged !== null && !sameSource(site, packaged),
|
|
27
|
+
}];
|
|
28
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readFile, rm } from 'fs/promises';
|
|
2
|
+
import { locateTemplate } from './emailTemplatePaths.js';
|
|
3
|
+
|
|
4
|
+
/* Removes the site's version of an email template so the packaged one is used again. Resolves to [error, { template }]. */
|
|
5
|
+
export default async ({ template } = {}) => {
|
|
6
|
+
const located = locateTemplate(template);
|
|
7
|
+
if(!located) return [{ code: 400, msg: 'Invalid email template name' }, null];
|
|
8
|
+
if(await readFile(located.defaultPath, 'utf8').catch(() => null) === null) return [{ code: 409, msg: 'There is no packaged version to go back to' }, null];
|
|
9
|
+
try {
|
|
10
|
+
await rm(located.sitePath, { force: true });
|
|
11
|
+
return [null, { template }];
|
|
12
|
+
} catch {
|
|
13
|
+
return [{ code: 500, msg: 'The template could not be reset' }, null];
|
|
14
|
+
}
|
|
15
|
+
};
|