nextjs-cms 0.0.1 → 0.5.1

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 (302) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +289 -0
  3. package/dist/api/axios/axiosInstance.d.ts +2 -0
  4. package/dist/api/axios/axiosInstance.d.ts.map +1 -0
  5. package/dist/api/axios/axiosInstance.js +8 -0
  6. package/dist/api/index.d.ts +856 -0
  7. package/dist/api/index.d.ts.map +1 -0
  8. package/dist/api/index.js +12 -0
  9. package/dist/api/lib/serverActions.d.ts +240 -0
  10. package/dist/api/lib/serverActions.d.ts.map +1 -0
  11. package/dist/api/lib/serverActions.js +834 -0
  12. package/dist/api/root.d.ts +829 -0
  13. package/dist/api/root.d.ts.map +1 -0
  14. package/dist/api/root.js +30 -0
  15. package/dist/api/routers/accountSettings.d.ts +61 -0
  16. package/dist/api/routers/accountSettings.d.ts.map +1 -0
  17. package/dist/api/routers/accountSettings.js +108 -0
  18. package/dist/api/routers/admins.d.ts +106 -0
  19. package/dist/api/routers/admins.d.ts.map +1 -0
  20. package/dist/api/routers/admins.js +219 -0
  21. package/dist/api/routers/auth.d.ts +48 -0
  22. package/dist/api/routers/auth.d.ts.map +1 -0
  23. package/dist/api/routers/auth.js +25 -0
  24. package/dist/api/routers/categorySection.d.ts +104 -0
  25. package/dist/api/routers/categorySection.d.ts.map +1 -0
  26. package/dist/api/routers/categorySection.js +38 -0
  27. package/dist/api/routers/cmsSettings.d.ts +49 -0
  28. package/dist/api/routers/cmsSettings.d.ts.map +1 -0
  29. package/dist/api/routers/cmsSettings.js +51 -0
  30. package/dist/api/routers/cpanel.d.ts +84 -0
  31. package/dist/api/routers/cpanel.d.ts.map +1 -0
  32. package/dist/api/routers/cpanel.js +216 -0
  33. package/dist/api/routers/files.d.ts +48 -0
  34. package/dist/api/routers/files.d.ts.map +1 -0
  35. package/dist/api/routers/files.js +23 -0
  36. package/dist/api/routers/gallery.d.ts +36 -0
  37. package/dist/api/routers/gallery.d.ts.map +1 -0
  38. package/dist/api/routers/gallery.js +62 -0
  39. package/dist/api/routers/googleAnalytics.d.ts +31 -0
  40. package/dist/api/routers/googleAnalytics.d.ts.map +1 -0
  41. package/dist/api/routers/googleAnalytics.js +7 -0
  42. package/dist/api/routers/hasItemsSection.d.ts +140 -0
  43. package/dist/api/routers/hasItemsSection.d.ts.map +1 -0
  44. package/dist/api/routers/hasItemsSection.js +34 -0
  45. package/dist/api/routers/navigation.d.ts +52 -0
  46. package/dist/api/routers/navigation.d.ts.map +1 -0
  47. package/dist/api/routers/navigation.js +11 -0
  48. package/dist/api/routers/simpleSection.d.ts +58 -0
  49. package/dist/api/routers/simpleSection.d.ts.map +1 -0
  50. package/dist/api/routers/simpleSection.js +12 -0
  51. package/dist/api/trpc.d.ts +107 -0
  52. package/dist/api/trpc.d.ts.map +1 -0
  53. package/dist/api/trpc.js +72 -0
  54. package/dist/auth/axios/axiosInstance.d.ts +2 -0
  55. package/dist/auth/axios/axiosInstance.d.ts.map +1 -0
  56. package/dist/auth/axios/axiosInstance.js +8 -0
  57. package/dist/auth/csrf.d.ts +30 -0
  58. package/dist/auth/csrf.d.ts.map +1 -0
  59. package/dist/auth/csrf.js +76 -0
  60. package/dist/auth/hooks/index.d.ts +4 -0
  61. package/dist/auth/hooks/index.d.ts.map +1 -0
  62. package/dist/auth/hooks/index.js +3 -0
  63. package/dist/auth/hooks/useAxiosPrivate.d.ts +5 -0
  64. package/dist/auth/hooks/useAxiosPrivate.d.ts.map +1 -0
  65. package/dist/auth/hooks/useAxiosPrivate.js +74 -0
  66. package/dist/auth/hooks/useRefreshToken.d.ts +7 -0
  67. package/dist/auth/hooks/useRefreshToken.d.ts.map +1 -0
  68. package/dist/auth/hooks/useRefreshToken.js +79 -0
  69. package/dist/auth/index.d.ts +23 -0
  70. package/dist/auth/index.d.ts.map +1 -0
  71. package/dist/auth/index.js +44 -0
  72. package/dist/auth/jwt.d.ts +6 -0
  73. package/dist/auth/jwt.d.ts.map +1 -0
  74. package/dist/auth/jwt.js +25 -0
  75. package/dist/auth/lib/actions.d.ts +33 -0
  76. package/dist/auth/lib/actions.d.ts.map +1 -0
  77. package/dist/auth/lib/actions.js +209 -0
  78. package/dist/auth/lib/client.d.ts +4 -0
  79. package/dist/auth/lib/client.d.ts.map +1 -0
  80. package/dist/auth/lib/client.js +46 -0
  81. package/dist/auth/lib/index.d.ts +3 -0
  82. package/dist/auth/lib/index.d.ts.map +1 -0
  83. package/dist/auth/lib/index.js +2 -0
  84. package/dist/auth/react.d.ts +106 -0
  85. package/dist/auth/react.d.ts.map +1 -0
  86. package/dist/auth/react.js +347 -0
  87. package/dist/auth/trpc.d.ts +6 -0
  88. package/dist/auth/trpc.d.ts.map +1 -0
  89. package/dist/auth/trpc.js +81 -0
  90. package/dist/core/config/config-loader.d.ts +92 -0
  91. package/dist/core/config/config-loader.d.ts.map +1 -0
  92. package/dist/core/config/config-loader.js +230 -0
  93. package/dist/core/config/index.d.ts +3 -0
  94. package/dist/core/config/index.d.ts.map +1 -0
  95. package/dist/core/config/index.js +1 -0
  96. package/dist/core/config/loader.d.ts +2 -0
  97. package/dist/core/config/loader.d.ts.map +1 -0
  98. package/dist/core/config/loader.js +42 -0
  99. package/dist/core/db/index.d.ts +2 -0
  100. package/dist/core/db/index.d.ts.map +1 -0
  101. package/dist/core/db/index.js +1 -0
  102. package/dist/core/db/table-checker/DbTable.d.ts +6 -0
  103. package/dist/core/db/table-checker/DbTable.d.ts.map +1 -0
  104. package/dist/core/db/table-checker/DbTable.js +5 -0
  105. package/dist/core/db/table-checker/MysqlTable.d.ts +34 -0
  106. package/dist/core/db/table-checker/MysqlTable.d.ts.map +1 -0
  107. package/dist/core/db/table-checker/MysqlTable.js +102 -0
  108. package/dist/core/db/table-checker/index.d.ts +2 -0
  109. package/dist/core/db/table-checker/index.d.ts.map +1 -0
  110. package/dist/core/db/table-checker/index.js +1 -0
  111. package/dist/core/factories/FieldFactory.d.ts +124 -0
  112. package/dist/core/factories/FieldFactory.d.ts.map +1 -0
  113. package/dist/core/factories/FieldFactory.js +411 -0
  114. package/dist/core/factories/SectionFactory.d.ts +110 -0
  115. package/dist/core/factories/SectionFactory.d.ts.map +1 -0
  116. package/dist/core/factories/SectionFactory.js +415 -0
  117. package/dist/core/factories/index.d.ts +3 -0
  118. package/dist/core/factories/index.d.ts.map +1 -0
  119. package/dist/core/factories/index.js +2 -0
  120. package/dist/core/fields/checkbox.d.ts +63 -0
  121. package/dist/core/fields/checkbox.d.ts.map +1 -0
  122. package/dist/core/fields/checkbox.js +62 -0
  123. package/dist/core/fields/color.d.ts +84 -0
  124. package/dist/core/fields/color.d.ts.map +1 -0
  125. package/dist/core/fields/color.js +91 -0
  126. package/dist/core/fields/date.d.ts +100 -0
  127. package/dist/core/fields/date.d.ts.map +1 -0
  128. package/dist/core/fields/date.js +108 -0
  129. package/dist/core/fields/document.d.ts +180 -0
  130. package/dist/core/fields/document.d.ts.map +1 -0
  131. package/dist/core/fields/document.js +277 -0
  132. package/dist/core/fields/field-group.d.ts +18 -0
  133. package/dist/core/fields/field-group.d.ts.map +1 -0
  134. package/dist/core/fields/field-group.js +6 -0
  135. package/dist/core/fields/field.d.ts +126 -0
  136. package/dist/core/fields/field.d.ts.map +1 -0
  137. package/dist/core/fields/field.js +148 -0
  138. package/dist/core/fields/fileField.d.ts +15 -0
  139. package/dist/core/fields/fileField.d.ts.map +1 -0
  140. package/dist/core/fields/fileField.js +5 -0
  141. package/dist/core/fields/index.d.ts +65 -0
  142. package/dist/core/fields/index.d.ts.map +1 -0
  143. package/dist/core/fields/index.js +18 -0
  144. package/dist/core/fields/map.d.ts +167 -0
  145. package/dist/core/fields/map.d.ts.map +1 -0
  146. package/dist/core/fields/map.js +152 -0
  147. package/dist/core/fields/number.d.ts +186 -0
  148. package/dist/core/fields/number.d.ts.map +1 -0
  149. package/dist/core/fields/number.js +241 -0
  150. package/dist/core/fields/password.d.ts +109 -0
  151. package/dist/core/fields/password.d.ts.map +1 -0
  152. package/dist/core/fields/password.js +133 -0
  153. package/dist/core/fields/photo.d.ts +289 -0
  154. package/dist/core/fields/photo.d.ts.map +1 -0
  155. package/dist/core/fields/photo.js +410 -0
  156. package/dist/core/fields/richText.d.ts +295 -0
  157. package/dist/core/fields/richText.d.ts.map +1 -0
  158. package/dist/core/fields/richText.js +338 -0
  159. package/dist/core/fields/select.d.ts +366 -0
  160. package/dist/core/fields/select.d.ts.map +1 -0
  161. package/dist/core/fields/select.js +499 -0
  162. package/dist/core/fields/selectMultiple.d.ts +236 -0
  163. package/dist/core/fields/selectMultiple.d.ts.map +1 -0
  164. package/dist/core/fields/selectMultiple.js +417 -0
  165. package/dist/core/fields/tags.d.ts +131 -0
  166. package/dist/core/fields/tags.d.ts.map +1 -0
  167. package/dist/core/fields/tags.js +105 -0
  168. package/dist/core/fields/text.d.ts +136 -0
  169. package/dist/core/fields/text.d.ts.map +1 -0
  170. package/dist/core/fields/text.js +157 -0
  171. package/dist/core/fields/textArea.d.ts +107 -0
  172. package/dist/core/fields/textArea.d.ts.map +1 -0
  173. package/dist/core/fields/textArea.js +126 -0
  174. package/dist/core/fields/video.d.ts +148 -0
  175. package/dist/core/fields/video.d.ts.map +1 -0
  176. package/dist/core/fields/video.js +248 -0
  177. package/dist/core/helpers/entity.d.ts +8 -0
  178. package/dist/core/helpers/entity.d.ts.map +1 -0
  179. package/dist/core/helpers/entity.js +27 -0
  180. package/dist/core/helpers/index.d.ts +5 -0
  181. package/dist/core/helpers/index.d.ts.map +1 -0
  182. package/dist/core/helpers/index.js +3 -0
  183. package/dist/core/index.d.ts +8 -0
  184. package/dist/core/index.d.ts.map +1 -0
  185. package/dist/core/index.js +7 -0
  186. package/dist/core/sections/category.d.ts +283 -0
  187. package/dist/core/sections/category.d.ts.map +1 -0
  188. package/dist/core/sections/category.js +147 -0
  189. package/dist/core/sections/hasItems.d.ts +632 -0
  190. package/dist/core/sections/hasItems.d.ts.map +1 -0
  191. package/dist/core/sections/hasItems.js +144 -0
  192. package/dist/core/sections/index.d.ts +5 -0
  193. package/dist/core/sections/index.d.ts.map +1 -0
  194. package/dist/core/sections/index.js +4 -0
  195. package/dist/core/sections/section.d.ts +226 -0
  196. package/dist/core/sections/section.d.ts.map +1 -0
  197. package/dist/core/sections/section.js +341 -0
  198. package/dist/core/sections/simple.d.ts +99 -0
  199. package/dist/core/sections/simple.d.ts.map +1 -0
  200. package/dist/core/sections/simple.js +95 -0
  201. package/dist/core/security/dom.d.ts +11 -0
  202. package/dist/core/security/dom.d.ts.map +1 -0
  203. package/dist/core/security/dom.js +92 -0
  204. package/dist/core/submit/ItemEditSubmit.d.ts +76 -0
  205. package/dist/core/submit/ItemEditSubmit.d.ts.map +1 -0
  206. package/dist/core/submit/ItemEditSubmit.js +186 -0
  207. package/dist/core/submit/NewItemSubmit.d.ts +14 -0
  208. package/dist/core/submit/NewItemSubmit.d.ts.map +1 -0
  209. package/dist/core/submit/NewItemSubmit.js +93 -0
  210. package/dist/core/submit/SimpleSectionSubmit.d.ts +13 -0
  211. package/dist/core/submit/SimpleSectionSubmit.d.ts.map +1 -0
  212. package/dist/core/submit/SimpleSectionSubmit.js +93 -0
  213. package/dist/core/submit/index.d.ts +5 -0
  214. package/dist/core/submit/index.d.ts.map +1 -0
  215. package/dist/core/submit/index.js +4 -0
  216. package/dist/core/submit/submit.d.ts +116 -0
  217. package/dist/core/submit/submit.d.ts.map +1 -0
  218. package/dist/core/submit/submit.js +479 -0
  219. package/dist/core/types/index.d.ts +280 -0
  220. package/dist/core/types/index.d.ts.map +1 -0
  221. package/dist/core/types/index.js +1 -0
  222. package/dist/db/client.d.ts +9 -0
  223. package/dist/db/client.d.ts.map +1 -0
  224. package/dist/db/client.js +19 -0
  225. package/dist/db/config.d.ts +6 -0
  226. package/dist/db/config.d.ts.map +1 -0
  227. package/dist/db/config.js +22 -0
  228. package/dist/db/drizzle.config.d.ts +6 -0
  229. package/dist/db/drizzle.config.d.ts.map +1 -0
  230. package/dist/db/drizzle.config.js +18 -0
  231. package/dist/db/index.d.ts +3 -0
  232. package/dist/db/index.d.ts.map +1 -0
  233. package/dist/db/index.js +3 -0
  234. package/dist/db/schema.d.ts +639 -0
  235. package/dist/db/schema.d.ts.map +1 -0
  236. package/dist/db/schema.js +73 -0
  237. package/dist/index.d.ts +7 -1
  238. package/dist/index.d.ts.map +1 -1
  239. package/dist/index.js +7 -1
  240. package/dist/translations/dictionaries/ar.json +279 -0
  241. package/dist/translations/dictionaries/en.json +279 -0
  242. package/dist/translations/index.d.ts +3 -0
  243. package/dist/translations/index.d.ts.map +1 -0
  244. package/dist/translations/index.js +15 -0
  245. package/dist/utils/CpanelApi.d.ts +25 -0
  246. package/dist/utils/CpanelApi.d.ts.map +1 -0
  247. package/dist/utils/CpanelApi.js +64 -0
  248. package/dist/utils/constants.d.ts +14 -0
  249. package/dist/utils/constants.d.ts.map +1 -0
  250. package/dist/utils/constants.js +61 -0
  251. package/dist/utils/index.d.ts +5 -0
  252. package/dist/utils/index.d.ts.map +1 -0
  253. package/dist/utils/index.js +4 -0
  254. package/dist/utils/utils.d.ts +60 -0
  255. package/dist/utils/utils.d.ts.map +1 -0
  256. package/dist/utils/utils.js +132 -0
  257. package/dist/validators/checkbox.d.ts +4 -0
  258. package/dist/validators/checkbox.d.ts.map +1 -0
  259. package/dist/validators/checkbox.js +12 -0
  260. package/dist/validators/color.d.ts +4 -0
  261. package/dist/validators/color.d.ts.map +1 -0
  262. package/dist/validators/color.js +7 -0
  263. package/dist/validators/date.d.ts +4 -0
  264. package/dist/validators/date.d.ts.map +1 -0
  265. package/dist/validators/date.js +5 -0
  266. package/dist/validators/document.d.ts +4 -0
  267. package/dist/validators/document.d.ts.map +1 -0
  268. package/dist/validators/document.js +57 -0
  269. package/dist/validators/index.d.ts +15 -0
  270. package/dist/validators/index.d.ts.map +1 -0
  271. package/dist/validators/index.js +14 -0
  272. package/dist/validators/map.d.ts +4 -0
  273. package/dist/validators/map.d.ts.map +1 -0
  274. package/dist/validators/map.js +5 -0
  275. package/dist/validators/number.d.ts +4 -0
  276. package/dist/validators/number.d.ts.map +1 -0
  277. package/dist/validators/number.js +20 -0
  278. package/dist/validators/password.d.ts +4 -0
  279. package/dist/validators/password.d.ts.map +1 -0
  280. package/dist/validators/password.js +11 -0
  281. package/dist/validators/photo.d.ts +4 -0
  282. package/dist/validators/photo.d.ts.map +1 -0
  283. package/dist/validators/photo.js +100 -0
  284. package/dist/validators/richText.d.ts +4 -0
  285. package/dist/validators/richText.d.ts.map +1 -0
  286. package/dist/validators/richText.js +8 -0
  287. package/dist/validators/select-multiple.d.ts +10 -0
  288. package/dist/validators/select-multiple.d.ts.map +1 -0
  289. package/dist/validators/select-multiple.js +20 -0
  290. package/dist/validators/select.d.ts +4 -0
  291. package/dist/validators/select.d.ts.map +1 -0
  292. package/dist/validators/select.js +5 -0
  293. package/dist/validators/text.d.ts +4 -0
  294. package/dist/validators/text.d.ts.map +1 -0
  295. package/dist/validators/text.js +7 -0
  296. package/dist/validators/textarea.d.ts +4 -0
  297. package/dist/validators/textarea.d.ts.map +1 -0
  298. package/dist/validators/textarea.js +7 -0
  299. package/dist/validators/video.d.ts +4 -0
  300. package/dist/validators/video.d.ts.map +1 -0
  301. package/dist/validators/video.js +57 -0
  302. package/package.json +150 -6
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Creates a cookie with the value 'token|hash',
3
+ * where 'token' is the CSRF token and 'hash' is a hash made of the token and
4
+ * the secret, and the two values are joined by a pipe '|'. By storing the
5
+ * value and the hash of the value (with the secret used as a salt) we can
6
+ * verify the cookie was set by the server and not by a malicious attacker.
7
+ *
8
+ * For more details, see the following OWASP links:
9
+ * https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie
10
+ * https://owasp.org/www-chapter-london/assets/slides/David_Johansson-Double_Defeat_of_Double-Submit_Cookie.pdf
11
+ */
12
+ export async function createCSRFToken(cookieValue) {
13
+ /**
14
+ * If there is a CSRF token cookie, we verify it
15
+ */
16
+ if (cookieValue) {
17
+ /**
18
+ * Split the cookie value into the token and the hash
19
+ */
20
+ const [csrfToken, csrfTokenHash] = cookieValue.split('|');
21
+ if (csrfToken && csrfTokenHash) {
22
+ /**
23
+ * Create a hash of the CSRF token and the secret
24
+ */
25
+ const expectedCsrfTokenHash = await createHash(`${csrfToken}${process.env.CSRF_TOKEN_SECRET}`);
26
+ /**
27
+ * If hash matches then we trust the CSRF token value
28
+ */
29
+ if (csrfTokenHash === expectedCsrfTokenHash) {
30
+ return { csrfToken };
31
+ }
32
+ }
33
+ }
34
+ /**
35
+ * If this line is reached, then the CSRF token is not verified and we need to create a new one
36
+ */
37
+ const csrfToken = randomString(32);
38
+ const csrfTokenHash = await createHash(`${csrfToken}${process.env.CSRF_TOKEN_SECRET}`);
39
+ const cookie = `${csrfToken}|${csrfTokenHash}`;
40
+ /**
41
+ * Return the cookie and the CSRF token value
42
+ */
43
+ return { cookie, csrfToken };
44
+ }
45
+ /**
46
+ * This function is used to validate the CSRF token in POST, PUT, DELETE requests (or any request that changes data)
47
+ * @param cookieValue
48
+ * @param bodyValue
49
+ */
50
+ export async function validateCSRFToken({ cookieValue, bodyValue }) {
51
+ if (cookieValue) {
52
+ const [csrfToken, csrfTokenHash] = cookieValue.split('|');
53
+ const expectedCsrfTokenHash = await createHash(`${csrfToken}${process.env.CSRF_TOKEN_SECRET}`);
54
+ if (csrfTokenHash === expectedCsrfTokenHash) {
55
+ // If hash matches then we trust the CSRF token value
56
+ // If this is a POST request and the CSRF Token in the POST request matches
57
+ // the cookie we have already verified is the one we have set, then the token is verified!
58
+ return csrfToken === bodyValue;
59
+ }
60
+ }
61
+ return false;
62
+ }
63
+ export function randomString(size) {
64
+ const i2hex = (i) => ('0' + i.toString(16)).slice(-2);
65
+ const r = (a, i) => a + i2hex(i);
66
+ const bytes = crypto.getRandomValues(new Uint8Array(size));
67
+ return Array.from(bytes).reduce(r, '');
68
+ }
69
+ export async function createHash(message) {
70
+ const data = new TextEncoder().encode(message);
71
+ const hash = await crypto.subtle.digest('SHA-256', data);
72
+ return Array.from(new Uint8Array(hash))
73
+ .map((b) => b.toString(16).padStart(2, '0'))
74
+ .join('')
75
+ .toString();
76
+ }
@@ -0,0 +1,4 @@
1
+ import useAxiosPrivate from "./useAxiosPrivate";
2
+ import useRefreshToken from "./useRefreshToken";
3
+ export { useAxiosPrivate, useRefreshToken };
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,mBAAmB,CAAA;AAC/C,OAAO,eAAe,MAAM,mBAAmB,CAAA;AAE/C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,CAAA"}
@@ -0,0 +1,3 @@
1
+ import useAxiosPrivate from "./useAxiosPrivate";
2
+ import useRefreshToken from "./useRefreshToken";
3
+ export { useAxiosPrivate, useRefreshToken };
@@ -0,0 +1,5 @@
1
+ declare const useAxiosPrivate: (options?: {
2
+ refreshTokenOn?: 401 | 404;
3
+ }) => import("axios").AxiosInstance;
4
+ export default useAxiosPrivate;
5
+ //# sourceMappingURL=useAxiosPrivate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAxiosPrivate.d.ts","sourceRoot":"","sources":["../../../src/auth/hooks/useAxiosPrivate.tsx"],"names":[],"mappings":"AAMA,QAAA,MAAM,eAAe,GAAI,UAAU;IAAE,cAAc,CAAC,EAAE,GAAG,GAAG,GAAG,CAAA;CAAE,kCAgFhE,CAAA;AAED,eAAe,eAAe,CAAA"}
@@ -0,0 +1,74 @@
1
+ import { axiosPrivate } from '../axios/axiosInstance';
2
+ import { useEffect } from 'react';
3
+ import useRefreshToken from './useRefreshToken';
4
+ import axios from 'axios';
5
+ import { getCsrfToken } from '../react';
6
+ const useAxiosPrivate = (options) => {
7
+ const refresh = useRefreshToken();
8
+ const { refreshTokenOn = 401 } = options || {};
9
+ useEffect(() => {
10
+ /**
11
+ * Add a request interceptor
12
+ */
13
+ const requestIntercept = axiosPrivate.interceptors.request.use(
14
+ /**
15
+ * Do something before request is sent
16
+ * @param config The request config object
17
+ */
18
+ async (config) => {
19
+ if (config.method && ['post', 'put', 'delete'].includes(config.method.toLowerCase())) {
20
+ /**
21
+ * If the request is a POST, PUT, or DELETE request, the XSRF-TOKEN header is added to the request.
22
+ */
23
+ config.headers['x-csrf-token'] = await getCsrfToken();
24
+ }
25
+ /**
26
+ * Return the config object
27
+ */
28
+ return config;
29
+ }, (error) => Promise.reject(error));
30
+ /**
31
+ * This is the response interceptor
32
+ */
33
+ const responseIntercept = axiosPrivate.interceptors.response.use((response) => response, // Do nothing if the request is successful,
34
+ async (error) => {
35
+ // If the access token has expired, refresh it and retry the request
36
+ const prevRequest = error?.config; // The request that caused the error (the one that returned 401) is saved in the error object
37
+ if (error?.response?.status === refreshTokenOn && !prevRequest?.sent) {
38
+ // If the error is 401 and the request hasn't been sent before
39
+ prevRequest.sent = true; // Prevent infinite loops
40
+ const refreshStatus = await refresh(); // Refresh the access token cookie
41
+ if (refreshStatus === false)
42
+ return Promise.reject(error); // If the refresh failed, reject the promise
43
+ // NOTICE: This is needed to send the request as multipart form data,
44
+ // because resending the request with axios resets the Content-Type to application/json for some reason
45
+ // Use transformRequest to set Content-Type and include boundary for multipart form data
46
+ prevRequest.headers['Content-Type'] = 'multipart/form-data';
47
+ prevRequest.transformRequest = [
48
+ (data, headers) => {
49
+ // If the request data is FormData, set the boundary
50
+ if (data instanceof FormData) {
51
+ // @ts-ignore
52
+ headers['Content-Type'] += `; boundary=${data._boundary}`;
53
+ }
54
+ return data;
55
+ },
56
+ ...axios.defaults.transformRequest, // Keep the default transformRequest functions
57
+ ];
58
+ return axiosPrivate(prevRequest); // The request that returned 401 is retried with the new access token
59
+ }
60
+ return Promise.reject(error);
61
+ });
62
+ // Remove the interceptors when the component unmounts
63
+ // This is needed to prevent memory leaks
64
+ return () => {
65
+ // Eject the interceptors
66
+ axiosPrivate.interceptors.request.eject(requestIntercept);
67
+ axiosPrivate.interceptors.response.eject(responseIntercept);
68
+ };
69
+ }, [
70
+ /*auth, refresh*/
71
+ ]);
72
+ return axiosPrivate;
73
+ };
74
+ export default useAxiosPrivate;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * This hook is used to refresh the access token when it expires.
3
+ * It is used in the useAxiosPrivate hook to refresh the access token when a request returns a 401 error.
4
+ */
5
+ declare const useRefreshToken: () => () => Promise<unknown>;
6
+ export default useRefreshToken;
7
+ //# sourceMappingURL=useRefreshToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRefreshToken.d.ts","sourceRoot":"","sources":["../../../src/auth/hooks/useRefreshToken.tsx"],"names":[],"mappings":"AAEA;;;GAGG;AACH,QAAA,MAAM,eAAe,8BA6EpB,CAAA;AAED,eAAe,eAAe,CAAA"}
@@ -0,0 +1,79 @@
1
+ import { logout, refreshSession } from '../react';
2
+ /**
3
+ * This hook is used to refresh the access token when it expires.
4
+ * It is used in the useAxiosPrivate hook to refresh the access token when a request returns a 401 error.
5
+ */
6
+ const useRefreshToken = () => {
7
+ let isRefreshing = false; // Is a refresh request being sent?
8
+ let failedQueue = []; // An array of requests that failed because of 401
9
+ const processQueue = (error, token = null) => {
10
+ failedQueue.forEach((prom) => {
11
+ if (error) {
12
+ prom.reject(error);
13
+ }
14
+ else {
15
+ prom.resolve(token);
16
+ }
17
+ });
18
+ failedQueue = [];
19
+ };
20
+ const refresh = async () => {
21
+ try {
22
+ const response = await fetch('/api/auth/refresh');
23
+ const data = await response.json();
24
+ /**
25
+ * The refresh request is done
26
+ */
27
+ isRefreshing = false;
28
+ if (response.status !== 200) {
29
+ /**
30
+ * If the refresh token is invalid, we log out the user
31
+ */
32
+ await logout({
33
+ /**
34
+ * No need to delete the cookies, because they are both invalid.
35
+ */
36
+ deleteCookies: false,
37
+ });
38
+ return false;
39
+ }
40
+ else {
41
+ /**
42
+ * update the session
43
+ */
44
+ await refreshSession();
45
+ }
46
+ /**
47
+ * Process the failed requests
48
+ */
49
+ processQueue(null, data?.accessToken);
50
+ return true;
51
+ }
52
+ catch (error) {
53
+ /**
54
+ * If the refresh token is invalid, we log out the user
55
+ */
56
+ await logout({
57
+ /**
58
+ * No need to delete the cookies, because they are both invalid.
59
+ */
60
+ deleteCookies: false,
61
+ });
62
+ return false;
63
+ }
64
+ };
65
+ // TODO: Apply this inside useAxiosPrivate.tsx to prevent even the 401 errors from happening
66
+ // Let's use semaphores to prevent multiple refreshes at the same time
67
+ return async () => {
68
+ if (isRefreshing) {
69
+ // If a refresh request is being sent, we return a promise
70
+ // that will be resolved when the refresh request is done
71
+ return new Promise((resolve, reject) => {
72
+ failedQueue.push({ resolve, reject });
73
+ });
74
+ }
75
+ isRefreshing = true; // A refresh request is being sent
76
+ return await refresh(); // Send the refresh request and return the new access token
77
+ };
78
+ };
79
+ export default useRefreshToken;
@@ -0,0 +1,23 @@
1
+ export interface Session {
2
+ user: User;
3
+ }
4
+ export interface User {
5
+ id: string;
6
+ name: string;
7
+ locale?: string | null;
8
+ email?: string | null;
9
+ image?: string | null;
10
+ }
11
+ /**
12
+ * Internal function to get the auth session
13
+ */
14
+ declare function __auth__internal(): Promise<Session | null>;
15
+ /**
16
+ * Cache the auth session to avoid unnecessary requests per a single server request.
17
+ */
18
+ declare const auth: typeof __auth__internal;
19
+ /**
20
+ * Export the auth function
21
+ */
22
+ export default auth;
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,OAAO;IACpB,IAAI,EAAE,IAAI,CAAA;CACb;AAED,MAAM,WAAW,IAAI;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAeD;;GAEG;AACH,iBAAe,gBAAgB,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAczD;AAED;;GAEG;AACH,QAAA,MAAM,IAAI,yBAA0B,CAAA;AAEpC;;GAEG;AACH,eAAe,IAAI,CAAA"}
@@ -0,0 +1,44 @@
1
+ import { decodeJWT } from './jwt';
2
+ import { cookies } from 'next/headers';
3
+ import { cache } from 'react';
4
+ /**
5
+ * Get the authenticated user from the access token
6
+ * @param accessToken
7
+ */
8
+ const getAuthedUser = (accessToken) => {
9
+ if (!accessToken)
10
+ return null;
11
+ try {
12
+ return decodeJWT(accessToken);
13
+ }
14
+ catch (err) {
15
+ return null;
16
+ }
17
+ };
18
+ /**
19
+ * Internal function to get the auth session
20
+ */
21
+ async function __auth__internal() {
22
+ const cookieJar = await cookies();
23
+ const jwt = getAuthedUser(cookieJar.get('access_token')?.value);
24
+ if (jwt) {
25
+ return {
26
+ user: {
27
+ id: jwt.id,
28
+ name: jwt.sub,
29
+ locale: jwt.locale,
30
+ },
31
+ };
32
+ }
33
+ else {
34
+ return null;
35
+ }
36
+ }
37
+ /**
38
+ * Cache the auth session to avoid unnecessary requests per a single server request.
39
+ */
40
+ const auth = cache(__auth__internal);
41
+ /**
42
+ * Export the auth function
43
+ */
44
+ export default auth;
@@ -0,0 +1,6 @@
1
+ import type { AccessTokenPayload, RefreshTokenPayload } from 'jsonwebtoken';
2
+ export declare const encodeJWT: (payload: AccessTokenPayload) => string;
3
+ export declare const encodeRefreshToken: (payload: RefreshTokenPayload) => string;
4
+ export declare const decodeJWT: (token: string) => AccessTokenPayload;
5
+ export declare const decodeRefreshToken: (token: string) => RefreshTokenPayload;
6
+ //# sourceMappingURL=jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/auth/jwt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAG3E,eAAO,MAAM,SAAS,GAAI,SAAS,kBAAkB,KAAG,MAKvD,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,SAAS,mBAAmB,KAAG,MAKjE,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,KAAG,kBAEzC,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,KAAG,mBAMlD,CAAA"}
@@ -0,0 +1,25 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import jwt from 'jsonwebtoken';
3
+ export const encodeJWT = (payload) => {
4
+ return jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, {
5
+ expiresIn: process.env.ACCESS_TOKEN_EXPIRATION,
6
+ jwtid: randomUUID(),
7
+ });
8
+ };
9
+ export const encodeRefreshToken = (payload) => {
10
+ return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET, {
11
+ expiresIn: process.env.REFRESH_TOKEN_EXPIRATION,
12
+ jwtid: randomUUID(),
13
+ });
14
+ };
15
+ export const decodeJWT = (token) => {
16
+ return jwt.verify(token, process.env.ACCESS_TOKEN_SECRET || '');
17
+ };
18
+ export const decodeRefreshToken = (token) => {
19
+ try {
20
+ return jwt.verify(token, process.env.REFRESH_TOKEN_SECRET || '');
21
+ }
22
+ catch (err) {
23
+ throw err;
24
+ }
25
+ };
@@ -0,0 +1,33 @@
1
+ import type { Session } from '../index';
2
+ export declare const authRefresh: (refreshToken?: {
3
+ value: string;
4
+ }) => Promise<{
5
+ status: number;
6
+ code: string;
7
+ state: string;
8
+ user?: undefined;
9
+ } | {
10
+ status: number;
11
+ state: string;
12
+ user: {
13
+ id: string;
14
+ username: string;
15
+ lang: string | null;
16
+ avatar: string | null;
17
+ };
18
+ code?: undefined;
19
+ }>;
20
+ export declare const login: ({ username, password }: {
21
+ username: string;
22
+ password: string;
23
+ }) => Promise<{
24
+ user: {
25
+ id: string;
26
+ username: string;
27
+ lang: string | null;
28
+ avatar: string | null;
29
+ };
30
+ accessToken: string;
31
+ }>;
32
+ export declare const deleteSession: (session?: Session | null) => Promise<boolean>;
33
+ //# sourceMappingURL=actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/auth/lib/actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAEvC,eAAO,MAAM,WAAW,GAAU,eAAe;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;EA4GjE,CAAA;AAED,eAAO,MAAM,KAAK,GAAU,wBAAwB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;;;;;;EA+FzF,CAAA;AAED,eAAO,MAAM,aAAa,GAAU,UAAU,OAAO,GAAG,IAAI,qBA0B3D,CAAA"}
@@ -0,0 +1,209 @@
1
+ import { db } from '../../db/client';
2
+ import { AccessTokensTable, AdminsTable } from '../../db/schema';
3
+ import { and, eq } from 'drizzle-orm';
4
+ import bcrypt from 'bcrypt';
5
+ import { decodeRefreshToken, encodeJWT, encodeRefreshToken } from '../jwt';
6
+ import { cookies } from 'next/headers';
7
+ export const authRefresh = async (refreshToken) => {
8
+ if (!refreshToken) {
9
+ return {
10
+ status: 401,
11
+ code: 'no_refresh_token',
12
+ state: 'logged_out',
13
+ };
14
+ }
15
+ try {
16
+ const decoded = decodeRefreshToken(refreshToken.value);
17
+ const result = await db
18
+ .select({
19
+ adminId: AccessTokensTable.adminId,
20
+ accessToken: AccessTokensTable.accessToken,
21
+ refreshToken: AccessTokensTable.refreshToken,
22
+ expiration: AccessTokensTable.expiration,
23
+ username: AdminsTable.user,
24
+ locale: AdminsTable.language,
25
+ coverphoto: AdminsTable.coverphoto,
26
+ })
27
+ .from(AccessTokensTable)
28
+ .innerJoin(AdminsTable, eq(AccessTokensTable.adminId, AdminsTable.id))
29
+ .where(and(eq(AccessTokensTable.adminId, decoded.id), eq(AccessTokensTable.refreshToken, refreshToken.value)))
30
+ .limit(1);
31
+ const adminTokenResult = result[0];
32
+ if (!adminTokenResult) {
33
+ return {
34
+ status: 401,
35
+ code: 'invalid_refresh_token',
36
+ state: 'logged_out',
37
+ };
38
+ }
39
+ // Let's create a JWT access token and refresh token
40
+ const accessToken = encodeJWT({
41
+ id: decoded.id,
42
+ iss: 'Lazemni CMS',
43
+ aud: 'admin',
44
+ sub: adminTokenResult.username,
45
+ locale: adminTokenResult.locale || 'en',
46
+ });
47
+ const newRefreshToken = encodeRefreshToken({
48
+ id: adminTokenResult.adminId,
49
+ iss: 'Lazemni CMS',
50
+ aud: 'admin',
51
+ sub: adminTokenResult.username,
52
+ });
53
+ // Let's update the refresh token in the database
54
+ await db
55
+ .update(AccessTokensTable)
56
+ .set({
57
+ accessToken,
58
+ refreshToken: newRefreshToken,
59
+ expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
60
+ })
61
+ .where(and(eq(AccessTokensTable.adminId, adminTokenResult.adminId), eq(AccessTokensTable.refreshToken, refreshToken.value)));
62
+ // Let's send the refresh token as a cookie
63
+ const cookieStore = await cookies();
64
+ cookieStore.set({
65
+ name: 'refresh_token',
66
+ value: newRefreshToken,
67
+ httpOnly: true,
68
+ path: '/api/auth/refresh',
69
+ secure: true,
70
+ sameSite: true,
71
+ maxAge: 60 * 60 * 24 * 365,
72
+ });
73
+ cookieStore.set({
74
+ name: 'access_token',
75
+ value: accessToken,
76
+ httpOnly: true,
77
+ secure: true,
78
+ sameSite: true,
79
+ maxAge: 60 * 60 * 2, // 2 hours
80
+ });
81
+ return {
82
+ status: 200,
83
+ // accessToken: accessToken,
84
+ state: 'logged_in',
85
+ user: {
86
+ id: adminTokenResult.adminId,
87
+ username: adminTokenResult.username,
88
+ lang: adminTokenResult.locale,
89
+ avatar: adminTokenResult.coverphoto,
90
+ },
91
+ };
92
+ }
93
+ catch (err) {
94
+ return {
95
+ status: 401,
96
+ code: 'invalid_refresh_token',
97
+ state: 'logged_out',
98
+ };
99
+ }
100
+ };
101
+ export const login = async ({ username, password }) => {
102
+ if (!username || !password) {
103
+ throw new Error('Please provide username and password');
104
+ }
105
+ const result = await db
106
+ .select({
107
+ password: AdminsTable.pass,
108
+ id: AdminsTable.id,
109
+ username: AdminsTable.user,
110
+ locale: AdminsTable.language,
111
+ coverphoto: AdminsTable.coverphoto,
112
+ })
113
+ .from(AdminsTable)
114
+ .where(eq(AdminsTable.user, username))
115
+ .limit(1);
116
+ const admin = result[0];
117
+ if (!admin) {
118
+ throw new Error('Invalid credentials');
119
+ }
120
+ // Verify password with bcrypt
121
+ const valid = await bcrypt.compare(password, admin.password);
122
+ if (!valid) {
123
+ throw new Error('Invalid credentials');
124
+ }
125
+ // Let's create a JWT access token and refresh token
126
+ const accessToken = encodeJWT({
127
+ id: admin.id,
128
+ iss: 'Lazemni CMS',
129
+ aud: 'admin',
130
+ sub: admin.username,
131
+ locale: admin.locale || 'en',
132
+ });
133
+ const refreshToken = encodeRefreshToken({
134
+ id: admin.id,
135
+ iss: 'Lazemni CMS',
136
+ aud: 'admin',
137
+ sub: admin.username,
138
+ });
139
+ // Let's save the refresh token in the database
140
+ await db
141
+ .insert(AccessTokensTable)
142
+ .values({
143
+ accessToken,
144
+ refreshToken,
145
+ adminId: admin.id,
146
+ expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
147
+ })
148
+ .onDuplicateKeyUpdate({
149
+ set: {
150
+ accessToken,
151
+ refreshToken,
152
+ expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
153
+ },
154
+ });
155
+ const cookieStore = await cookies();
156
+ // Let's send the refresh token as a cookie
157
+ cookieStore.set({
158
+ name: 'refresh_token',
159
+ value: refreshToken,
160
+ httpOnly: true,
161
+ path: '/api/auth/refresh',
162
+ secure: true,
163
+ sameSite: true,
164
+ maxAge: 60 * 60 * 24 * 365,
165
+ });
166
+ cookieStore.set({
167
+ name: 'access_token',
168
+ value: accessToken,
169
+ httpOnly: true,
170
+ secure: true,
171
+ sameSite: true,
172
+ maxAge: 60 * 60 * 2, // 2 hours
173
+ });
174
+ // Now, return the access token in body
175
+ return {
176
+ user: {
177
+ id: admin.id,
178
+ username: admin.username,
179
+ lang: admin.locale,
180
+ avatar: admin.coverphoto,
181
+ },
182
+ accessToken: accessToken,
183
+ };
184
+ };
185
+ export const deleteSession = async (session) => {
186
+ // Let's delete csrf, access and refresh token cookies
187
+ const cookieStore = await cookies();
188
+ cookieStore.set({
189
+ name: 'csrf_token',
190
+ value: '',
191
+ maxAge: -1,
192
+ });
193
+ cookieStore.set({
194
+ name: 'access_token',
195
+ value: '',
196
+ maxAge: -1,
197
+ });
198
+ cookieStore.set({
199
+ name: 'refresh_token',
200
+ value: '',
201
+ path: '/api/auth/refresh',
202
+ maxAge: -1,
203
+ });
204
+ if (session?.user.id) {
205
+ // Let's delete the refresh token from the database
206
+ await db.delete(AccessTokensTable).where(eq(AccessTokensTable.adminId, session.user.id));
207
+ }
208
+ return true;
209
+ };
@@ -0,0 +1,4 @@
1
+ export declare function fetchData<T = any>(path: string, req?: any): Promise<T | null>;
2
+ export declare function now(): number;
3
+ export declare function useOnline(): boolean;
4
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/auth/lib/client.ts"],"names":[],"mappings":"AAKA,wBAAsB,SAAS,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,GAAQ,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAyBvF;AAED,wBAAgB,GAAG,WAElB;AAED,wBAAgB,SAAS,YAiBxB"}