sitero 0.3.0 → 0.4.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 (131) hide show
  1. package/dist/export/cms/client/components/index.js +1 -0
  2. package/dist/export/core/server/index.js +1 -0
  3. package/dist/prisma/generated/internal/class.js +4 -4
  4. package/dist/src/cms/client/infrastructure/i18n/messages/en-US/resources/file.js +17 -0
  5. package/dist/src/cms/client/infrastructure/i18n/messages/zh-TW/resources/file.js +17 -0
  6. package/dist/src/cms/client/interfaces/components/create-components.js +1 -0
  7. package/dist/src/cms/client/interfaces/components/create-post-contents.js +7 -4
  8. package/dist/src/cms/client/interfaces/components/resources/admin/author-fields.js +5 -0
  9. package/dist/src/cms/client/interfaces/components/resources/file/file-card.js +1 -1
  10. package/dist/src/cms/client/interfaces/components/resources/post/content/post-create-content.js +24 -6
  11. package/dist/src/cms/client/interfaces/components/resources/post/content/post-edit-content.js +27 -7
  12. package/dist/src/cms/client/interfaces/components/resources/post/content/post-show-content.js +13 -5
  13. package/dist/src/cms/client/interfaces/components/resources/post/content/resolve-control-field-label.js +9 -0
  14. package/dist/src/cms/client/interfaces/components/resources/post/post-list.js +5 -7
  15. package/dist/src/cms/client/interfaces/components/resources/post/search/control-field-filter-state.js +20 -0
  16. package/dist/src/cms/client/interfaces/components/resources/post/search/control-fields-filters.js +28 -14
  17. package/dist/src/cms/client/interfaces/components/ui/form/containers/side-fields.js +1 -1
  18. package/dist/src/cms/client/interfaces/components/ui/form/control-fields/control-fields-display.js +19 -9
  19. package/dist/src/cms/client/interfaces/components/ui/form/control-fields/control-fields-input.js +22 -11
  20. package/dist/src/cms/client/interfaces/components/ui/form/control-fields/control-fields.js +6 -1
  21. package/dist/src/cms/client/interfaces/pages/dashboard/content/content-management/_topicSlug_/page.js +2 -0
  22. package/dist/src/cms/client/interfaces/pages/dashboard/content/content-management/tag/page.js +4 -0
  23. package/dist/src/cms/client/interfaces/pages/dashboard/content/content-management/topic/page.js +4 -0
  24. package/dist/src/cms/client/interfaces/pages/dashboard/system/access/admin/_id_/edit/page.js +12 -11
  25. package/dist/src/cms/client/interfaces/pages/dashboard/system/access/admin/_id_/page.js +12 -11
  26. package/dist/src/cms/client/interfaces/pages/dashboard/system/access/admin/create/page.js +12 -11
  27. package/dist/src/cms/client/interfaces/pages/dashboard/system/storage/file/delete-many/page.js +36 -5
  28. package/dist/src/cms/client/interfaces/pages/dashboard/system/storage/trash/page.js +54 -5
  29. package/dist/src/cms/server/resources/file/actions.js +15 -31
  30. package/dist/src/config/author.js +13 -0
  31. package/dist/src/config/post.js +14 -0
  32. package/dist/src/core/infrastructure/prisma/admin/admin-prisma-ports.js +44 -28
  33. package/dist/src/core/infrastructure/prisma/config/config-prisma-ports.js +48 -31
  34. package/dist/src/core/infrastructure/prisma/file/file-prisma-ports.js +79 -34
  35. package/dist/src/core/infrastructure/prisma/file/include.js +32 -33
  36. package/dist/src/core/infrastructure/prisma/helpers/active-file-references.js +20 -0
  37. package/dist/src/core/infrastructure/prisma/post/post-prisma-ports.js +126 -95
  38. package/dist/src/core/sitero-server/create-sitero-server.js +1 -0
  39. package/dist/types/export/cms/client/components/index.d.ts +2 -1
  40. package/dist/types/export/cms/client/components/index.d.ts.map +1 -1
  41. package/dist/types/export/index.d.ts +1 -1
  42. package/dist/types/export/index.d.ts.map +1 -1
  43. package/dist/types/src/cms/client/infrastructure/i18n/messages/en-US/index.d.ts +45 -0
  44. package/dist/types/src/cms/client/infrastructure/i18n/messages/en-US/index.d.ts.map +1 -1
  45. package/dist/types/src/cms/client/infrastructure/i18n/messages/en-US/resources/file.d.ts +45 -0
  46. package/dist/types/src/cms/client/infrastructure/i18n/messages/en-US/resources/file.d.ts.map +1 -1
  47. package/dist/types/src/cms/client/infrastructure/i18n/messages/en-US/resources/index.d.ts +45 -0
  48. package/dist/types/src/cms/client/infrastructure/i18n/messages/en-US/resources/index.d.ts.map +1 -1
  49. package/dist/types/src/cms/client/infrastructure/i18n/messages/index.d.ts +90 -0
  50. package/dist/types/src/cms/client/infrastructure/i18n/messages/index.d.ts.map +1 -1
  51. package/dist/types/src/cms/client/infrastructure/i18n/messages/zh-TW/index.d.ts +45 -0
  52. package/dist/types/src/cms/client/infrastructure/i18n/messages/zh-TW/index.d.ts.map +1 -1
  53. package/dist/types/src/cms/client/infrastructure/i18n/messages/zh-TW/resources/file.d.ts +45 -0
  54. package/dist/types/src/cms/client/infrastructure/i18n/messages/zh-TW/resources/file.d.ts.map +1 -1
  55. package/dist/types/src/cms/client/infrastructure/i18n/messages/zh-TW/resources/index.d.ts +45 -0
  56. package/dist/types/src/cms/client/infrastructure/i18n/messages/zh-TW/resources/index.d.ts.map +1 -1
  57. package/dist/types/src/cms/client/interfaces/components/create-components.d.ts +2 -3
  58. package/dist/types/src/cms/client/interfaces/components/create-components.d.ts.map +1 -1
  59. package/dist/types/src/cms/client/interfaces/components/create-post-contents.d.ts +3 -1
  60. package/dist/types/src/cms/client/interfaces/components/create-post-contents.d.ts.map +1 -1
  61. package/dist/types/src/cms/client/interfaces/components/resources/admin/author-fields.d.ts +7 -0
  62. package/dist/types/src/cms/client/interfaces/components/resources/admin/author-fields.d.ts.map +1 -0
  63. package/dist/types/src/cms/client/interfaces/components/resources/admin/index.d.ts +1 -0
  64. package/dist/types/src/cms/client/interfaces/components/resources/admin/index.d.ts.map +1 -1
  65. package/dist/types/src/cms/client/interfaces/components/resources/file/file-card.d.ts.map +1 -1
  66. package/dist/types/src/cms/client/interfaces/components/resources/index.d.ts +1 -1
  67. package/dist/types/src/cms/client/interfaces/components/resources/index.d.ts.map +1 -1
  68. package/dist/types/src/cms/client/interfaces/components/resources/post/content/index.d.ts +1 -1
  69. package/dist/types/src/cms/client/interfaces/components/resources/post/content/index.d.ts.map +1 -1
  70. package/dist/types/src/cms/client/interfaces/components/resources/post/content/post-create-content.d.ts +5 -3
  71. package/dist/types/src/cms/client/interfaces/components/resources/post/content/post-create-content.d.ts.map +1 -1
  72. package/dist/types/src/cms/client/interfaces/components/resources/post/content/post-edit-content.d.ts +5 -3
  73. package/dist/types/src/cms/client/interfaces/components/resources/post/content/post-edit-content.d.ts.map +1 -1
  74. package/dist/types/src/cms/client/interfaces/components/resources/post/content/post-show-content.d.ts +5 -3
  75. package/dist/types/src/cms/client/interfaces/components/resources/post/content/post-show-content.d.ts.map +1 -1
  76. package/dist/types/src/cms/client/interfaces/components/resources/post/content/resolve-control-field-label.d.ts +3 -0
  77. package/dist/types/src/cms/client/interfaces/components/resources/post/content/resolve-control-field-label.d.ts.map +1 -0
  78. package/dist/types/src/cms/client/interfaces/components/resources/post/content/types.d.ts +9 -0
  79. package/dist/types/src/cms/client/interfaces/components/resources/post/content/types.d.ts.map +1 -1
  80. package/dist/types/src/cms/client/interfaces/components/resources/post/index.d.ts +1 -1
  81. package/dist/types/src/cms/client/interfaces/components/resources/post/index.d.ts.map +1 -1
  82. package/dist/types/src/cms/client/interfaces/components/resources/post/post-list.d.ts +5 -2
  83. package/dist/types/src/cms/client/interfaces/components/resources/post/post-list.d.ts.map +1 -1
  84. package/dist/types/src/cms/client/interfaces/components/resources/post/search/control-field-filter-state.d.ts +7 -0
  85. package/dist/types/src/cms/client/interfaces/components/resources/post/search/control-field-filter-state.d.ts.map +1 -0
  86. package/dist/types/src/cms/client/interfaces/components/resources/post/search/control-fields-filters.d.ts +4 -23
  87. package/dist/types/src/cms/client/interfaces/components/resources/post/search/control-fields-filters.d.ts.map +1 -1
  88. package/dist/types/src/cms/client/interfaces/components/ui/form/control-fields/control-fields-display.d.ts +2 -0
  89. package/dist/types/src/cms/client/interfaces/components/ui/form/control-fields/control-fields-display.d.ts.map +1 -1
  90. package/dist/types/src/cms/client/interfaces/components/ui/form/control-fields/control-fields-input.d.ts +2 -0
  91. package/dist/types/src/cms/client/interfaces/components/ui/form/control-fields/control-fields-input.d.ts.map +1 -1
  92. package/dist/types/src/cms/client/interfaces/components/ui/form/control-fields/control-fields.d.ts +2 -0
  93. package/dist/types/src/cms/client/interfaces/components/ui/form/control-fields/control-fields.d.ts.map +1 -1
  94. package/dist/types/src/cms/client/interfaces/pages/dashboard/content/content-management/[topicSlug]/page.d.ts.map +1 -1
  95. package/dist/types/src/cms/client/interfaces/pages/dashboard/content/content-management/tag/page.d.ts.map +1 -1
  96. package/dist/types/src/cms/client/interfaces/pages/dashboard/content/content-management/topic/page.d.ts.map +1 -1
  97. package/dist/types/src/cms/client/interfaces/pages/dashboard/system/access/admin/[id]/edit/page.d.ts +3 -2
  98. package/dist/types/src/cms/client/interfaces/pages/dashboard/system/access/admin/[id]/edit/page.d.ts.map +1 -1
  99. package/dist/types/src/cms/client/interfaces/pages/dashboard/system/access/admin/[id]/page.d.ts +3 -2
  100. package/dist/types/src/cms/client/interfaces/pages/dashboard/system/access/admin/[id]/page.d.ts.map +1 -1
  101. package/dist/types/src/cms/client/interfaces/pages/dashboard/system/access/admin/create/page.d.ts +3 -2
  102. package/dist/types/src/cms/client/interfaces/pages/dashboard/system/access/admin/create/page.d.ts.map +1 -1
  103. package/dist/types/src/cms/client/interfaces/pages/dashboard/system/storage/file/delete-many/page.d.ts.map +1 -1
  104. package/dist/types/src/cms/client/interfaces/pages/dashboard/system/storage/trash/page.d.ts.map +1 -1
  105. package/dist/types/src/cms/contracts/resources/file/actions.d.ts +16 -4
  106. package/dist/types/src/cms/contracts/resources/file/actions.d.ts.map +1 -1
  107. package/dist/types/src/cms/server/resources/file/actions.d.ts.map +1 -1
  108. package/dist/types/src/config/author.d.ts +4 -0
  109. package/dist/types/src/config/author.d.ts.map +1 -0
  110. package/dist/types/src/config/index.d.ts +2 -1
  111. package/dist/types/src/config/index.d.ts.map +1 -1
  112. package/dist/types/src/config/post.d.ts +3 -1
  113. package/dist/types/src/config/post.d.ts.map +1 -1
  114. package/dist/types/src/constants/cms-locale.d.ts +1 -0
  115. package/dist/types/src/constants/cms-locale.d.ts.map +1 -1
  116. package/dist/types/src/constants/index.d.ts +1 -1
  117. package/dist/types/src/constants/index.d.ts.map +1 -1
  118. package/dist/types/src/core/infrastructure/prisma/admin/admin-prisma-ports.d.ts.map +1 -1
  119. package/dist/types/src/core/infrastructure/prisma/config/config-prisma-ports.d.ts.map +1 -1
  120. package/dist/types/src/core/infrastructure/prisma/config/types.d.ts +1 -1
  121. package/dist/types/src/core/infrastructure/prisma/config/types.d.ts.map +1 -1
  122. package/dist/types/src/core/infrastructure/prisma/file/file-prisma-ports.d.ts +2 -1
  123. package/dist/types/src/core/infrastructure/prisma/file/file-prisma-ports.d.ts.map +1 -1
  124. package/dist/types/src/core/infrastructure/prisma/file/include.d.ts +32 -168
  125. package/dist/types/src/core/infrastructure/prisma/file/include.d.ts.map +1 -1
  126. package/dist/types/src/core/infrastructure/prisma/helpers/active-file-references.d.ts +9 -0
  127. package/dist/types/src/core/infrastructure/prisma/helpers/active-file-references.d.ts.map +1 -0
  128. package/dist/types/src/core/infrastructure/prisma/post/post-prisma-ports.d.ts.map +1 -1
  129. package/dist/types/src/core/sitero-server/create-sitero-server.d.ts.map +1 -1
  130. package/dist/types/src/web/server/resources/post/actions.d.ts +1 -1
  131. package/package.json +4 -3
@@ -1,3 +1,4 @@
1
+ import { withActiveFileReferences } from '../helpers/active-file-references.js';
1
2
  import { connectOrDisconnectOne, setMany } from '../helpers/relation-writes.js';
2
3
  import { CONFIG_DETAIL_INCLUDE } from './include.js';
3
4
  import { configDetailToDomain, configToDomain } from './mappers.js';
@@ -40,38 +41,54 @@ function createConfigPrismaPorts(prisma) {
40
41
  // ------------------------------------------------------------
41
42
  configCommandPort: {
42
43
  async update({ id, config }) {
43
- const {
44
- files1,
45
- files2,
46
- file1,
47
- file2,
48
- file3,
49
- file4,
50
- translations,
51
- scalarUpdates
52
- } = splitConfigUpdateData(config);
53
- const updated = await prisma.config.update({
54
- where: { id },
55
- data: {
56
- ...scalarUpdates,
57
- files1: setMany(files1),
58
- files2: setMany(files2),
59
- file1: connectOrDisconnectOne(file1),
60
- file2: connectOrDisconnectOne(file2),
61
- file3: connectOrDisconnectOne(file3),
62
- file4: connectOrDisconnectOne(file4),
63
- translations: {
64
- upsert: translations.map((translation) => ({
65
- where: {
66
- configId_locale: { configId: id, locale: translation.locale }
67
- },
68
- update: translation,
69
- create: translation
70
- }))
71
- }
44
+ return withActiveFileReferences(
45
+ prisma,
46
+ [
47
+ ...config.files1 ?? [],
48
+ ...config.files2 ?? [],
49
+ config.file1,
50
+ config.file2,
51
+ config.file3,
52
+ config.file4
53
+ ],
54
+ async (transaction) => {
55
+ const {
56
+ files1,
57
+ files2,
58
+ file1,
59
+ file2,
60
+ file3,
61
+ file4,
62
+ translations,
63
+ scalarUpdates
64
+ } = splitConfigUpdateData(config);
65
+ const updated = await transaction.config.update({
66
+ where: { id },
67
+ data: {
68
+ ...scalarUpdates,
69
+ files1: setMany(files1),
70
+ files2: setMany(files2),
71
+ file1: connectOrDisconnectOne(file1),
72
+ file2: connectOrDisconnectOne(file2),
73
+ file3: connectOrDisconnectOne(file3),
74
+ file4: connectOrDisconnectOne(file4),
75
+ translations: {
76
+ upsert: translations.map((translation) => ({
77
+ where: {
78
+ configId_locale: {
79
+ configId: id,
80
+ locale: translation.locale
81
+ }
82
+ },
83
+ update: translation,
84
+ create: translation
85
+ }))
86
+ }
87
+ }
88
+ });
89
+ return configToDomain(updated);
72
90
  }
73
- });
74
- return configToDomain(updated);
91
+ );
75
92
  }
76
93
  },
77
94
  // ------------------------------------------------------------
@@ -52,52 +52,97 @@ function createFilePrismaPorts(prisma) {
52
52
  return fileToDomain(file);
53
53
  },
54
54
  async softDelete({ id }) {
55
- const file = await prisma.file.update({
56
- where: {
57
- id,
58
- deletedAt: null,
59
- isLocked: false,
60
- ...FILE_NOT_IN_USE_WHERE
61
- },
62
- data: { deletedAt: /* @__PURE__ */ new Date() }
55
+ return prisma.$transaction(async (transaction) => {
56
+ await transaction.$queryRaw`SELECT id FROM files WHERE id = ${id} FOR UPDATE`;
57
+ const file = await transaction.file.update({
58
+ where: {
59
+ id,
60
+ deletedAt: null,
61
+ isLocked: false,
62
+ ...FILE_NOT_IN_USE_WHERE
63
+ },
64
+ data: { deletedAt: /* @__PURE__ */ new Date() }
65
+ });
66
+ return fileToDomain(file);
63
67
  });
64
- return fileToDomain(file);
65
68
  },
66
69
  async restore({ id }) {
67
- const file = await prisma.file.update({
68
- where: { id, deletedAt: { not: null } },
69
- data: { deletedAt: null }
70
- });
71
- return fileToDomain(file);
72
- },
73
- async purge({ id }) {
74
- const file = await prisma.file.delete({
75
- where: {
76
- id,
77
- deletedAt: { not: null },
78
- isLocked: false,
79
- ...FILE_NOT_IN_USE_WHERE
80
- }
70
+ return prisma.$transaction(async (transaction) => {
71
+ await transaction.$queryRaw`SELECT id FROM files WHERE id = ${id} FOR UPDATE`;
72
+ const file = await transaction.file.findUnique({ where: { id } });
73
+ if (!file) return { status: "not-found" };
74
+ if (file.purgeStartedAt) return { status: "purge-started" };
75
+ if (!file.deletedAt) return { status: "not-deleted" };
76
+ const restored = await transaction.file.update({
77
+ where: { id },
78
+ data: { deletedAt: null }
79
+ });
80
+ return { status: "restored", file: fileToDomain(restored) };
81
81
  });
82
- return fileToDomain(file);
83
82
  }
84
83
  },
85
84
  // ------------------------------------------------------------
86
85
  // Extra
87
86
  // ------------------------------------------------------------
87
+ filePurgePort: {
88
+ async prepare({ id }) {
89
+ return prisma.$transaction(async (transaction) => {
90
+ await transaction.$queryRaw`SELECT id FROM files WHERE id = ${id} FOR UPDATE NOWAIT`;
91
+ const file = await transaction.file.findFirstOrThrow({
92
+ where: {
93
+ id,
94
+ deletedAt: { not: null },
95
+ isLocked: false,
96
+ ...FILE_NOT_IN_USE_WHERE
97
+ }
98
+ });
99
+ if (file.purgeStartedAt) return fileToDomain(file);
100
+ return fileToDomain(
101
+ await transaction.file.update({
102
+ where: { id },
103
+ data: { purgeStartedAt: /* @__PURE__ */ new Date() }
104
+ })
105
+ );
106
+ });
107
+ },
108
+ async complete({ id }, removeObject) {
109
+ return prisma.$transaction(
110
+ async (transaction) => {
111
+ await transaction.$queryRaw`SELECT id FROM files WHERE id = ${id} FOR UPDATE NOWAIT`;
112
+ const file = await transaction.file.findFirstOrThrow({
113
+ where: {
114
+ id,
115
+ deletedAt: { not: null },
116
+ purgeStartedAt: { not: null },
117
+ isLocked: false,
118
+ ...FILE_NOT_IN_USE_WHERE
119
+ }
120
+ });
121
+ await removeObject({ key: file.key });
122
+ return fileToDomain(
123
+ await transaction.file.delete({ where: { id } })
124
+ );
125
+ },
126
+ { timeout: 3e4 }
127
+ );
128
+ }
129
+ },
88
130
  fileReplaceContentPort: {
89
131
  async replaceContent(input) {
90
- const file = await prisma.file.update({
91
- where: {
92
- id: input.id,
93
- key: input.expectedKey,
94
- deletedAt: null,
95
- isLocked: false,
96
- ...FILE_NOT_IN_USE_WHERE
97
- },
98
- data: { key: input.key, ...toFileContentData(input) }
132
+ return prisma.$transaction(async (transaction) => {
133
+ await transaction.$queryRaw`SELECT id FROM files WHERE id = ${input.id} FOR UPDATE`;
134
+ const file = await transaction.file.update({
135
+ where: {
136
+ id: input.id,
137
+ key: input.expectedKey,
138
+ deletedAt: null,
139
+ isLocked: false,
140
+ ...FILE_NOT_IN_USE_WHERE
141
+ },
142
+ data: { key: input.key, ...toFileContentData(input) }
143
+ });
144
+ return fileToDomain(file);
99
145
  });
100
- return fileToDomain(file);
101
146
  }
102
147
  },
103
148
  // ------------------------------------------------------------
@@ -1,14 +1,13 @@
1
- const livePostReference = { deletedAt: null };
2
1
  const FILE_USAGE_INCLUDE = {
3
2
  adminAsAvatarImage: true,
4
- postsAsCoverImage: { where: livePostReference },
5
- postsAsContentImage: { where: livePostReference },
6
- postsAsFiles1: { where: livePostReference },
7
- postsAsFiles2: { where: livePostReference },
8
- postsAsFile1: { where: livePostReference },
9
- postsAsFile2: { where: livePostReference },
10
- postsAsFile3: { where: livePostReference },
11
- postsAsFile4: { where: livePostReference },
3
+ postsAsCoverImage: true,
4
+ postsAsContentImage: true,
5
+ postsAsFiles1: true,
6
+ postsAsFiles2: true,
7
+ postsAsFile1: true,
8
+ postsAsFile2: true,
9
+ postsAsFile3: true,
10
+ postsAsFile4: true,
12
11
  configsAsFiles1: true,
13
12
  configsAsFiles2: true,
14
13
  configsAsFile1: true,
@@ -18,14 +17,14 @@ const FILE_USAGE_INCLUDE = {
18
17
  };
19
18
  const FILE_USAGE_COUNT_SELECT = {
20
19
  adminAsAvatarImage: true,
21
- postsAsCoverImage: { where: livePostReference },
22
- postsAsContentImage: { where: livePostReference },
23
- postsAsFiles1: { where: livePostReference },
24
- postsAsFiles2: { where: livePostReference },
25
- postsAsFile1: { where: livePostReference },
26
- postsAsFile2: { where: livePostReference },
27
- postsAsFile3: { where: livePostReference },
28
- postsAsFile4: { where: livePostReference },
20
+ postsAsCoverImage: true,
21
+ postsAsContentImage: true,
22
+ postsAsFiles1: true,
23
+ postsAsFiles2: true,
24
+ postsAsFile1: true,
25
+ postsAsFile2: true,
26
+ postsAsFile3: true,
27
+ postsAsFile4: true,
29
28
  configsAsFiles1: true,
30
29
  configsAsFiles2: true,
31
30
  configsAsFile1: true,
@@ -35,14 +34,14 @@ const FILE_USAGE_COUNT_SELECT = {
35
34
  };
36
35
  const FILE_NOT_IN_USE_WHERE = {
37
36
  adminAsAvatarImage: { none: {} },
38
- postsAsCoverImage: { none: livePostReference },
39
- postsAsContentImage: { none: livePostReference },
40
- postsAsFiles1: { none: livePostReference },
41
- postsAsFiles2: { none: livePostReference },
42
- postsAsFile1: { none: livePostReference },
43
- postsAsFile2: { none: livePostReference },
44
- postsAsFile3: { none: livePostReference },
45
- postsAsFile4: { none: livePostReference },
37
+ postsAsCoverImage: { none: {} },
38
+ postsAsContentImage: { none: {} },
39
+ postsAsFiles1: { none: {} },
40
+ postsAsFiles2: { none: {} },
41
+ postsAsFile1: { none: {} },
42
+ postsAsFile2: { none: {} },
43
+ postsAsFile3: { none: {} },
44
+ postsAsFile4: { none: {} },
46
45
  configsAsFiles1: { none: {} },
47
46
  configsAsFiles2: { none: {} },
48
47
  configsAsFile1: { none: {} },
@@ -56,14 +55,14 @@ const FILE_DETAIL_INCLUDE = {
56
55
  };
57
56
  const FILE_RELATIONS_INCLUDE = {
58
57
  adminAsAvatarImage: { select: { id: true } },
59
- postsAsCoverImage: { where: livePostReference, select: { id: true } },
60
- postsAsContentImage: { where: livePostReference, select: { id: true } },
61
- postsAsFiles1: { where: livePostReference, select: { id: true } },
62
- postsAsFiles2: { where: livePostReference, select: { id: true } },
63
- postsAsFile1: { where: livePostReference, select: { id: true } },
64
- postsAsFile2: { where: livePostReference, select: { id: true } },
65
- postsAsFile3: { where: livePostReference, select: { id: true } },
66
- postsAsFile4: { where: livePostReference, select: { id: true } },
58
+ postsAsCoverImage: { select: { id: true } },
59
+ postsAsContentImage: { select: { id: true } },
60
+ postsAsFiles1: { select: { id: true } },
61
+ postsAsFiles2: { select: { id: true } },
62
+ postsAsFile1: { select: { id: true } },
63
+ postsAsFile2: { select: { id: true } },
64
+ postsAsFile3: { select: { id: true } },
65
+ postsAsFile4: { select: { id: true } },
67
66
  configsAsFiles1: { select: { slug: true } },
68
67
  configsAsFiles2: { select: { slug: true } },
69
68
  configsAsFile1: { select: { slug: true } },
@@ -0,0 +1,20 @@
1
+ import { FileNotFoundError } from 'sitero-server';
2
+
3
+ async function withActiveFileReferences(prisma, items, write) {
4
+ return prisma.$transaction(async (transaction) => {
5
+ const ids = [
6
+ ...new Set(items.flatMap((item) => item ? [item.id] : []))
7
+ ].sort();
8
+ for (const id of ids) {
9
+ const rows = await transaction.$queryRaw`
10
+ SELECT deleted_at, purge_started_at FROM files WHERE id = ${id} FOR UPDATE
11
+ `;
12
+ const file = rows[0];
13
+ if (!file || file.deleted_at || file.purge_started_at)
14
+ throw new FileNotFoundError();
15
+ }
16
+ return write(transaction);
17
+ });
18
+ }
19
+
20
+ export { withActiveFileReferences };
@@ -1,4 +1,5 @@
1
1
  import { ulid } from 'ulid';
2
+ import { withActiveFileReferences } from '../helpers/active-file-references.js';
2
3
  import { connectOrDisconnectOne, setMany, connectOne, connectMany } from '../helpers/relation-writes.js';
3
4
  import { createSearchWhere } from '../helpers/search-where.js';
4
5
  import { postToDomain } from './base-mappers.js';
@@ -54,105 +55,135 @@ function createPostPrismaPorts(prisma) {
54
55
  // ------------------------------------------------------------
55
56
  postCommandPort: {
56
57
  async create(input) {
57
- const id = ulid();
58
- const {
59
- author,
60
- topic,
61
- parents,
62
- tags,
63
- relatedPosts,
64
- coverImage,
65
- contentImages,
66
- files1,
67
- files2,
68
- file1,
69
- file2,
70
- file3,
71
- file4,
72
- translations,
73
- slug,
74
- ...scalars
75
- } = input;
76
- const authorWrite = connectOne(author);
77
- const topicWrite = connectOne(topic);
78
- const coverImageWrite = connectOne(coverImage);
79
- const file1Write = connectOne(file1);
80
- const file2Write = connectOne(file2);
81
- const file3Write = connectOne(file3);
82
- const file4Write = connectOne(file4);
83
- const post = await prisma.post.create({
84
- data: {
85
- ...scalars,
86
- id,
87
- slug: slug?.trim() || id,
88
- ...authorWrite ? { author: authorWrite } : {},
89
- ...topicWrite ? { topic: topicWrite } : {},
90
- parents: connectMany(parents),
91
- tags: connectMany(tags),
92
- relatedPosts: connectMany(relatedPosts),
93
- ...coverImageWrite ? { coverImage: coverImageWrite } : {},
94
- contentImages: connectMany(contentImages),
95
- files1: connectMany(files1),
96
- files2: connectMany(files2),
97
- ...file1Write ? { file1: file1Write } : {},
98
- ...file2Write ? { file2: file2Write } : {},
99
- ...file3Write ? { file3: file3Write } : {},
100
- ...file4Write ? { file4: file4Write } : {},
101
- translations: { create: translations.map(translationToWrite) }
58
+ return withActiveFileReferences(
59
+ prisma,
60
+ [
61
+ input.coverImage,
62
+ ...input.contentImages,
63
+ ...input.files1,
64
+ ...input.files2,
65
+ input.file1,
66
+ input.file2,
67
+ input.file3,
68
+ input.file4
69
+ ],
70
+ async (transaction) => {
71
+ const id = ulid();
72
+ const {
73
+ author,
74
+ topic,
75
+ parents,
76
+ tags,
77
+ relatedPosts,
78
+ coverImage,
79
+ contentImages,
80
+ files1,
81
+ files2,
82
+ file1,
83
+ file2,
84
+ file3,
85
+ file4,
86
+ translations,
87
+ slug,
88
+ ...scalars
89
+ } = input;
90
+ const authorWrite = connectOne(author);
91
+ const topicWrite = connectOne(topic);
92
+ const coverImageWrite = connectOne(coverImage);
93
+ const file1Write = connectOne(file1);
94
+ const file2Write = connectOne(file2);
95
+ const file3Write = connectOne(file3);
96
+ const file4Write = connectOne(file4);
97
+ const post = await transaction.post.create({
98
+ data: {
99
+ ...scalars,
100
+ id,
101
+ slug: slug?.trim() || id,
102
+ ...authorWrite ? { author: authorWrite } : {},
103
+ ...topicWrite ? { topic: topicWrite } : {},
104
+ parents: connectMany(parents),
105
+ tags: connectMany(tags),
106
+ relatedPosts: connectMany(relatedPosts),
107
+ ...coverImageWrite ? { coverImage: coverImageWrite } : {},
108
+ contentImages: connectMany(contentImages),
109
+ files1: connectMany(files1),
110
+ files2: connectMany(files2),
111
+ ...file1Write ? { file1: file1Write } : {},
112
+ ...file2Write ? { file2: file2Write } : {},
113
+ ...file3Write ? { file3: file3Write } : {},
114
+ ...file4Write ? { file4: file4Write } : {},
115
+ translations: { create: translations.map(translationToWrite) }
116
+ }
117
+ });
118
+ return postToDomain(post);
102
119
  }
103
- });
104
- return postToDomain(post);
120
+ );
105
121
  },
106
122
  async update(input) {
107
- const {
108
- id,
109
- author,
110
- topic,
111
- parents,
112
- tags,
113
- relatedPosts,
114
- coverImage,
115
- contentImages,
116
- files1,
117
- files2,
118
- file1,
119
- file2,
120
- file3,
121
- file4,
122
- translations,
123
- slug,
124
- ...scalars
125
- } = input;
126
- const post = await prisma.post.update({
127
- where: { id },
128
- data: {
129
- ...scalars,
130
- slug: slug?.trim() || id,
131
- author: connectOrDisconnectOne(author),
132
- topic: connectOrDisconnectOne(topic),
133
- parents: setMany(parents),
134
- tags: setMany(tags),
135
- relatedPosts: setMany(relatedPosts),
136
- coverImage: connectOrDisconnectOne(coverImage),
137
- contentImages: setMany(contentImages),
138
- files1: setMany(files1),
139
- files2: setMany(files2),
140
- file1: connectOrDisconnectOne(file1),
141
- file2: connectOrDisconnectOne(file2),
142
- file3: connectOrDisconnectOne(file3),
143
- file4: connectOrDisconnectOne(file4),
144
- translations: {
145
- upsert: translations.map((translation) => ({
146
- where: {
147
- postId_locale: { postId: id, locale: translation.locale }
148
- },
149
- update: translationToWrite(translation),
150
- create: translationToWrite(translation)
151
- }))
152
- }
123
+ return withActiveFileReferences(
124
+ prisma,
125
+ [
126
+ input.coverImage,
127
+ ...input.contentImages,
128
+ ...input.files1,
129
+ ...input.files2,
130
+ input.file1,
131
+ input.file2,
132
+ input.file3,
133
+ input.file4
134
+ ],
135
+ async (transaction) => {
136
+ const {
137
+ id,
138
+ author,
139
+ topic,
140
+ parents,
141
+ tags,
142
+ relatedPosts,
143
+ coverImage,
144
+ contentImages,
145
+ files1,
146
+ files2,
147
+ file1,
148
+ file2,
149
+ file3,
150
+ file4,
151
+ translations,
152
+ slug,
153
+ ...scalars
154
+ } = input;
155
+ const post = await transaction.post.update({
156
+ where: { id },
157
+ data: {
158
+ ...scalars,
159
+ slug: slug?.trim() || id,
160
+ author: connectOrDisconnectOne(author),
161
+ topic: connectOrDisconnectOne(topic),
162
+ parents: setMany(parents),
163
+ tags: setMany(tags),
164
+ relatedPosts: setMany(relatedPosts),
165
+ coverImage: connectOrDisconnectOne(coverImage),
166
+ contentImages: setMany(contentImages),
167
+ files1: setMany(files1),
168
+ files2: setMany(files2),
169
+ file1: connectOrDisconnectOne(file1),
170
+ file2: connectOrDisconnectOne(file2),
171
+ file3: connectOrDisconnectOne(file3),
172
+ file4: connectOrDisconnectOne(file4),
173
+ translations: {
174
+ upsert: translations.map((translation) => ({
175
+ where: {
176
+ postId_locale: { postId: id, locale: translation.locale }
177
+ },
178
+ update: translationToWrite(translation),
179
+ create: translationToWrite(translation)
180
+ }))
181
+ }
182
+ }
183
+ });
184
+ return postToDomain(post);
153
185
  }
154
- });
155
- return postToDomain(post);
186
+ );
156
187
  },
157
188
  async delete({ id }) {
158
189
  return postToDomain(
@@ -94,6 +94,7 @@ function createSiteroServer(input) {
94
94
  },
95
95
  file: {
96
96
  fileCommandPort: filePrismaPorts.fileCommandPort,
97
+ filePurgePort: filePrismaPorts.filePurgePort,
97
98
  fileQueryPort: filePrismaPorts.fileQueryPort,
98
99
  fileRelationsQueryPort: filePrismaPorts.fileRelationsQueryPort,
99
100
  fileReplaceContentPort: filePrismaPorts.fileReplaceContentPort,
@@ -1,6 +1,6 @@
1
1
  export { createComponents, type Components, } from "../../../../src/cms/client/interfaces/components/create-components";
2
2
  export { createPostContents, type PostContents, } from "../../../../src/cms/client/interfaces/components/create-post-contents";
3
- export { type AllowPostField, type AllowPostFieldParams, type PostFieldRules, type TopicPostFieldRules, createAllowPostField, PostStateIcons, } from "../../../../src/cms/client/interfaces/components/resources/post";
3
+ export { type PostCustomControlField, type AllowPostField, type AllowPostFieldParams, type PostFieldRules, type TopicPostFieldRules, createAllowPostField, PostStateIcons, } from "../../../../src/cms/client/interfaces/components/resources/post";
4
4
  export { ActionButtonsCard, FeatureCard, } from "../../../../src/cms/client/interfaces/components/ui/cards";
5
5
  export { Input, PasswordInput, Textarea, Select, Option, Checkbox, } from "../../../../src/cms/client/interfaces/components/ui/inputs";
6
6
  export { ActionButton, LinkButton, ExternalLinkButton, CopyButton, } from "../../../../src/cms/client/interfaces/components/ui/buttons";
@@ -10,4 +10,5 @@ export { PickDialog, PickerInput, type PickDialogProps, type PickerInputProps, t
10
10
  export { Form, FormField, type FormFieldControlProps, Field, FieldBody, FieldsContainer, MainFields, SideFields, ControlFields, type ControlMeta, } from "../../../../src/cms/client/interfaces/components/ui/form";
11
11
  export { Separator } from "../../../../src/cms/client/interfaces/components/shadcn";
12
12
  export { createNavbar, createCmsLayout, type NavItem, NavMain, Sidebar, SidebarContent, ContentContainer, } from "../../../../src/cms/client/interfaces/components/ui/layouts";
13
+ export { createAllowAuthorField, type AllowAuthorField, } from "../../../../src/cms/client/interfaces/components/resources/admin";
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../export/cms/client/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,KAAK,UAAU,GAChB,MAAM,oEAAoE,CAAC;AAC5E,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,GAClB,MAAM,uEAAuE,CAAC;AAG/E,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,oBAAoB,EACpB,cAAc,GACf,MAAM,iEAAiE,CAAC;AAKzE,OAAO,EACL,iBAAiB,EACjB,WAAW,GACZ,MAAM,2DAA2D,CAAC;AAKnE,OAAO,EACL,KAAK,EACL,aAAa,EACb,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,GACT,MAAM,4DAA4D,CAAC;AAKpE,OAAO,EACL,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,UAAU,GACX,MAAM,6DAA6D,CAAC;AAKrE,OAAO,EACL,WAAW,EACX,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,GACN,MAAM,6DAA6D,CAAC;AAKrE,OAAO,EACL,gBAAgB,EAChB,aAAa,GACd,MAAM,4DAA4D,CAAC;AAKpE,OAAO,EACL,UAAU,EACV,WAAW,EACX,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,gBAAgB,EAChB,IAAI,EACJ,SAAS,GACV,MAAM,8DAA8D,CAAC;AAKtE,OAAO,EACL,IAAI,EACJ,SAAS,EACT,KAAK,qBAAqB,EAC1B,KAAK,EACL,SAAS,EACT,eAAe,EACf,UAAU,EACV,UAAU,EACV,aAAa,EACb,KAAK,WAAW,GACjB,MAAM,0DAA0D,CAAC;AAKlE,OAAO,EAAE,SAAS,EAAE,MAAM,yDAAyD,CAAC;AAKpF,OAAO,EAEL,YAAY,EAEZ,eAAe,EAEf,KAAK,OAAO,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EAEd,gBAAgB,GACjB,MAAM,6DAA6D,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../export/cms/client/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,KAAK,UAAU,GAChB,MAAM,oEAAoE,CAAC;AAC5E,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,GAClB,MAAM,uEAAuE,CAAC;AAG/E,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,oBAAoB,EACpB,cAAc,GACf,MAAM,iEAAiE,CAAC;AAKzE,OAAO,EACL,iBAAiB,EACjB,WAAW,GACZ,MAAM,2DAA2D,CAAC;AAKnE,OAAO,EACL,KAAK,EACL,aAAa,EACb,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,GACT,MAAM,4DAA4D,CAAC;AAKpE,OAAO,EACL,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,UAAU,GACX,MAAM,6DAA6D,CAAC;AAKrE,OAAO,EACL,WAAW,EACX,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,GACN,MAAM,6DAA6D,CAAC;AAKrE,OAAO,EACL,gBAAgB,EAChB,aAAa,GACd,MAAM,4DAA4D,CAAC;AAKpE,OAAO,EACL,UAAU,EACV,WAAW,EACX,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,gBAAgB,EAChB,IAAI,EACJ,SAAS,GACV,MAAM,8DAA8D,CAAC;AAKtE,OAAO,EACL,IAAI,EACJ,SAAS,EACT,KAAK,qBAAqB,EAC1B,KAAK,EACL,SAAS,EACT,eAAe,EACf,UAAU,EACV,UAAU,EACV,aAAa,EACb,KAAK,WAAW,GACjB,MAAM,0DAA0D,CAAC;AAKlE,OAAO,EAAE,SAAS,EAAE,MAAM,yDAAyD,CAAC;AAKpF,OAAO,EAEL,YAAY,EAEZ,eAAe,EAEf,KAAK,OAAO,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EAEd,gBAAgB,GACjB,MAAM,6DAA6D,CAAC;AAGrE,OAAO,EACL,sBAAsB,EACtB,KAAK,gBAAgB,GACtB,MAAM,kEAAkE,CAAC"}
@@ -1,6 +1,6 @@
1
1
  export * from "sitero-domain";
2
2
  export { type RuntimePublicEnv, getServerPublicEnv, getRuntimePublicEnv, publicEnv, RuntimePublicEnvScript, } from "../src/env";
3
- export { type StaticAdmins, type StaticTopics, getTopicCategories, getTopicPosts, getTopicProducts, getTopicSubcategories, hasTopicRole, isContentTopic, isProductTopic, type StaticPages, type StaticInquiry, type StaticConfigs, type SeoMetadataField, type PostField, type SiteConfig, EMAIL_DEFINITION_KEYS, type EmailRenderContext, type EmailCatalog, type EmailDefinition, type EmailDefinitionKey, type EmailLocaleDefinition, } from "../src/config";
3
+ export { type StaticAdmins, type AuthorField, type StaticTopics, getTopicCategories, getTopicPosts, getTopicProducts, getTopicSubcategories, hasTopicRole, isContentTopic, isProductTopic, type StaticPages, type StaticInquiry, type StaticConfigs, type SeoMetadataField, type PostField, type PostCustomStateField, type SiteConfig, EMAIL_DEFINITION_KEYS, type EmailRenderContext, type EmailCatalog, type EmailDefinition, type EmailDefinitionKey, type EmailLocaleDefinition, } from "../src/config";
4
4
  export type { AdminDetail, AdminListCard, ConfigDetail, FileCard, FileDetail, PostListCard, PostDetail, PostReference, } from "sitero-server";
5
5
  export { CMS_PATHS, API_PATHS, NEW_TAB_REL, NEW_TAB_TARGET, DATE_FORMAT_OPTIONS, CMS_LOCALES_MAP, CMS_LOCALES_ARRAY, CMS_DEFAULT_LOCALE, } from "../src/constants";
6
6
  export { findTranslation, createBuildTranslations, cn, debounce, ensureArray, joinUrl, normalizeSlug, parsePositiveInt, toIsoTime, localeToFlagEmoji, createVersion, normalizeUnorderedCacheKey, dateToUi, deepMerge, TIME, type ListItemStates, DO_NOT_FETCH_KEY, } from "../src/shared";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../export/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAK9B,OAAO,EACL,KAAK,gBAAgB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,SAAS,EACT,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAKpB,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,WAAW,EACX,aAAa,EACb,YAAY,EAEZ,QAAQ,EACR,UAAU,EAEV,YAAY,EACZ,UAAU,EACV,aAAa,GACd,MAAM,eAAe,CAAC;AAKvB,OAAO,EACL,SAAS,EACT,SAAS,EACT,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EAEL,eAAe,EACf,uBAAuB,EAGvB,EAAE,EACF,QAAQ,EACR,WAAW,EACX,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,aAAa,EACb,0BAA0B,EAC1B,QAAQ,EACR,SAAS,EAGT,IAAI,EAGJ,KAAK,cAAc,EACnB,gBAAgB,GACjB,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../export/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAK9B,OAAO,EACL,KAAK,gBAAgB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,SAAS,EACT,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAKpB,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,WAAW,EACX,aAAa,EACb,YAAY,EAEZ,QAAQ,EACR,UAAU,EAEV,YAAY,EACZ,UAAU,EACV,aAAa,GACd,MAAM,eAAe,CAAC;AAKvB,OAAO,EACL,SAAS,EACT,SAAS,EACT,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EAEL,eAAe,EACf,uBAAuB,EAGvB,EAAE,EACF,QAAQ,EACR,WAAW,EACX,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,aAAa,EACb,0BAA0B,EAC1B,QAAQ,EACR,SAAS,EAGT,IAAI,EAGJ,KAAK,cAAc,EACnB,gBAAgB,GACjB,MAAM,eAAe,CAAC"}