kempo 4.2.39 → 4.3.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.
Files changed (41) hide show
  1. package/README.md +7 -0
  2. package/app-public/default.template.html +1 -1
  3. package/dist/admin/nav.fragment.html +5 -0
  4. package/dist/admin/realtime/index.page.html +81 -0
  5. package/dist/admin/settings/[owner]/[name]/index.page.html +52 -1
  6. package/dist/admin/settings/index.page.html +12 -1
  7. package/dist/kempo/api/realtime/WS.js +2 -0
  8. package/dist/kempo/api/realtime/WS.js.map +1 -0
  9. package/dist/kempo/api/realtime/connections/GET.js +2 -0
  10. package/dist/kempo/api/realtime/connections/GET.js.map +1 -0
  11. package/dist/kempo/icons/credit_card.svg +1 -0
  12. package/dist/kempo/icons/sensors.svg +1 -0
  13. package/dist/kempo/realtime.js +2 -0
  14. package/dist/kempo/realtime.js.map +1 -0
  15. package/dist/kempo/sdk.js +1 -1
  16. package/dist/kempo/sdk.js.map +1 -1
  17. package/middleware/kempo.js +30 -15
  18. package/package.json +2 -2
  19. package/scripts/init-db.js +4 -1
  20. package/server/db/schema.js +30 -1
  21. package/server/sdk.js +5 -0
  22. package/server/utils/extensions/contentDirs.js +27 -0
  23. package/server/utils/realtime/Hub.js +676 -0
  24. package/server/utils/realtime/channels.js +247 -0
  25. package/server/utils/realtime/closeConnection.js +4 -0
  26. package/server/utils/realtime/constants.js +20 -0
  27. package/server/utils/realtime/duration.js +18 -0
  28. package/server/utils/realtime/getHub.js +15 -0
  29. package/server/utils/realtime/index.js +14 -0
  30. package/server/utils/realtime/listConnections.js +4 -0
  31. package/server/utils/realtime/listSubscribers.js +4 -0
  32. package/server/utils/realtime/pruneMessages.js +45 -0
  33. package/server/utils/realtime/publish.js +86 -0
  34. package/server/utils/realtime/sendToConnection.js +4 -0
  35. package/server/utils/routing/serveUnmatched.js +8 -1
  36. package/server/utils/settings/getPublicSettings.js +4 -2
  37. package/server/utils/settings/getSettingWithMetadata.js +11 -4
  38. package/server/utils/settings/helpers.js +8 -2
  39. package/server/utils/settings/listSettings.js +9 -1
  40. package/server/utils/settings/secretCrypto.js +41 -0
  41. package/server/utils/settings/setSetting.js +31 -10
@@ -2,32 +2,53 @@ import db from '../../db/index.js';
2
2
  import { setting } from '../../db/schema.js';
3
3
  import { eq } from 'drizzle-orm';
4
4
  import { serializeValue } from './helpers.js';
5
+ import { SECRET_MASK } from './secretCrypto.js';
5
6
 
6
7
  export default async (owner, name, value, type = null, isPublic = false, description = null) => {
7
8
  if(!owner || !name){
8
9
  return [{ code: 400, msg: 'Both owner and name are required' }, null];
9
10
  }
10
-
11
+
11
12
  try {
12
13
  const fullName = `${owner}:${name}`;
13
14
  const detectedType = type || (typeof value === 'object' ? 'json' : typeof value);
14
- const serializedValue = serializeValue(value, detectedType);
15
-
16
- const existing = await db
15
+
16
+ const [existing] = await db
17
17
  .select()
18
18
  .from(setting)
19
19
  .where(eq(setting.name, fullName))
20
20
  .limit(1);
21
-
22
- if(existing.length > 0){
21
+
22
+ /*
23
+ Secrets are never public, and the admin UI only ever shows SECRET_MASK back for an existing
24
+ one — if that's what comes back unchanged, keep the stored ciphertext instead of encrypting
25
+ the mask itself. A masked value with nothing to preserve (no prior secret) means the caller
26
+ never actually supplied a new one.
27
+ */
28
+ let serializedValue;
29
+ if(detectedType === 'secret'){
30
+ isPublic = false;
31
+ if(value === SECRET_MASK){
32
+ if(!existing || existing.type !== 'secret'){
33
+ return [{ code: 400, msg: 'A secret value is required' }, null];
34
+ }
35
+ serializedValue = existing.value;
36
+ } else {
37
+ serializedValue = serializeValue(value, detectedType);
38
+ }
39
+ } else {
40
+ serializedValue = serializeValue(value, detectedType);
41
+ }
42
+
43
+ if(existing){
23
44
  await db
24
45
  .update(setting)
25
- .set({
46
+ .set({
26
47
  value: serializedValue,
27
48
  type: detectedType,
28
49
  isPublic,
29
50
  description,
30
- updatedAt: new Date()
51
+ updatedAt: new Date()
31
52
  })
32
53
  .where(eq(setting.name, fullName));
33
54
  } else {
@@ -41,8 +62,8 @@ export default async (owner, name, value, type = null, isPublic = false, descrip
41
62
  updatedAt: new Date()
42
63
  });
43
64
  }
44
-
45
- return [null, { name: fullName, value, type: detectedType, isPublic, description }];
65
+
66
+ return [null, { name: fullName, value: detectedType === 'secret' ? SECRET_MASK : value, type: detectedType, isPublic, description }];
46
67
  } catch(error){
47
68
  return [{ code: 500, msg: 'Failed to save setting' }, null];
48
69
  }