@qwickapps/server 1.3.1 → 1.5.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 (395) hide show
  1. package/README.md +157 -0
  2. package/dist/core/control-panel.d.ts.map +1 -1
  3. package/dist/core/control-panel.js +114 -0
  4. package/dist/core/control-panel.js.map +1 -1
  5. package/dist/core/types.d.ts +19 -0
  6. package/dist/core/types.d.ts.map +1 -1
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +15 -3
  10. package/dist/index.js.map +1 -1
  11. package/dist/plugins/auth/adapter-wrapper.d.ts +47 -0
  12. package/dist/plugins/auth/adapter-wrapper.d.ts.map +1 -0
  13. package/dist/plugins/auth/adapter-wrapper.js +166 -0
  14. package/dist/plugins/auth/adapter-wrapper.js.map +1 -0
  15. package/dist/plugins/auth/adapter-wrapper.test.d.ts +7 -0
  16. package/dist/plugins/auth/adapter-wrapper.test.d.ts.map +1 -0
  17. package/dist/plugins/auth/adapter-wrapper.test.js +303 -0
  18. package/dist/plugins/auth/adapter-wrapper.test.js.map +1 -0
  19. package/dist/plugins/auth/config-store.d.ts +11 -0
  20. package/dist/plugins/auth/config-store.d.ts.map +1 -0
  21. package/dist/plugins/auth/config-store.js +232 -0
  22. package/dist/plugins/auth/config-store.js.map +1 -0
  23. package/dist/plugins/auth/config-store.test.d.ts +7 -0
  24. package/dist/plugins/auth/config-store.test.d.ts.map +1 -0
  25. package/dist/plugins/auth/config-store.test.js +299 -0
  26. package/dist/plugins/auth/config-store.test.js.map +1 -0
  27. package/dist/plugins/auth/env-config.d.ts +51 -1
  28. package/dist/plugins/auth/env-config.d.ts.map +1 -1
  29. package/dist/plugins/auth/env-config.js +640 -7
  30. package/dist/plugins/auth/env-config.js.map +1 -1
  31. package/dist/plugins/auth/index.d.ts +6 -2
  32. package/dist/plugins/auth/index.d.ts.map +1 -1
  33. package/dist/plugins/auth/index.js +5 -1
  34. package/dist/plugins/auth/index.js.map +1 -1
  35. package/dist/plugins/auth/types.d.ts +106 -0
  36. package/dist/plugins/auth/types.d.ts.map +1 -1
  37. package/dist/plugins/bans/bans-plugin.d.ts.map +1 -1
  38. package/dist/plugins/bans/bans-plugin.js +12 -3
  39. package/dist/plugins/bans/bans-plugin.js.map +1 -1
  40. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts +11 -0
  41. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts.map +1 -0
  42. package/dist/plugins/devices/__tests__/devices-plugin.test.js +410 -0
  43. package/dist/plugins/devices/__tests__/devices-plugin.test.js.map +1 -0
  44. package/dist/plugins/devices/__tests__/token-utils.test.d.ts +7 -0
  45. package/dist/plugins/devices/__tests__/token-utils.test.d.ts.map +1 -0
  46. package/dist/plugins/devices/__tests__/token-utils.test.js +197 -0
  47. package/dist/plugins/devices/__tests__/token-utils.test.js.map +1 -0
  48. package/dist/plugins/devices/adapters/compute-adapter.d.ts +36 -0
  49. package/dist/plugins/devices/adapters/compute-adapter.d.ts.map +1 -0
  50. package/dist/plugins/devices/adapters/compute-adapter.js +100 -0
  51. package/dist/plugins/devices/adapters/compute-adapter.js.map +1 -0
  52. package/dist/plugins/devices/adapters/index.d.ts +12 -0
  53. package/dist/plugins/devices/adapters/index.d.ts.map +1 -0
  54. package/dist/plugins/devices/adapters/index.js +10 -0
  55. package/dist/plugins/devices/adapters/index.js.map +1 -0
  56. package/dist/plugins/devices/adapters/mobile-adapter.d.ts +41 -0
  57. package/dist/plugins/devices/adapters/mobile-adapter.d.ts.map +1 -0
  58. package/dist/plugins/devices/adapters/mobile-adapter.js +131 -0
  59. package/dist/plugins/devices/adapters/mobile-adapter.js.map +1 -0
  60. package/dist/plugins/devices/devices-plugin.d.ts +70 -0
  61. package/dist/plugins/devices/devices-plugin.d.ts.map +1 -0
  62. package/dist/plugins/devices/devices-plugin.js +453 -0
  63. package/dist/plugins/devices/devices-plugin.js.map +1 -0
  64. package/dist/plugins/devices/index.d.ts +18 -0
  65. package/dist/plugins/devices/index.d.ts.map +1 -0
  66. package/dist/plugins/devices/index.js +18 -0
  67. package/dist/plugins/devices/index.js.map +1 -0
  68. package/dist/plugins/devices/stores/index.d.ts +9 -0
  69. package/dist/plugins/devices/stores/index.d.ts.map +1 -0
  70. package/dist/plugins/devices/stores/index.js +9 -0
  71. package/dist/plugins/devices/stores/index.js.map +1 -0
  72. package/dist/plugins/devices/stores/postgres-store.d.ts +26 -0
  73. package/dist/plugins/devices/stores/postgres-store.d.ts.map +1 -0
  74. package/dist/plugins/devices/stores/postgres-store.js +199 -0
  75. package/dist/plugins/devices/stores/postgres-store.js.map +1 -0
  76. package/dist/plugins/devices/token-utils.d.ts +100 -0
  77. package/dist/plugins/devices/token-utils.d.ts.map +1 -0
  78. package/dist/plugins/devices/token-utils.js +162 -0
  79. package/dist/plugins/devices/token-utils.js.map +1 -0
  80. package/dist/plugins/devices/types.d.ts +307 -0
  81. package/dist/plugins/devices/types.d.ts.map +1 -0
  82. package/dist/plugins/devices/types.js +10 -0
  83. package/dist/plugins/devices/types.js.map +1 -0
  84. package/dist/plugins/index.d.ts +18 -4
  85. package/dist/plugins/index.d.ts.map +1 -1
  86. package/dist/plugins/index.js +16 -2
  87. package/dist/plugins/index.js.map +1 -1
  88. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts +5 -0
  89. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts.map +1 -0
  90. package/dist/plugins/notifications/__tests__/notifications-manager.test.js +470 -0
  91. package/dist/plugins/notifications/__tests__/notifications-manager.test.js.map +1 -0
  92. package/dist/plugins/notifications/index.d.ts +71 -0
  93. package/dist/plugins/notifications/index.d.ts.map +1 -0
  94. package/dist/plugins/notifications/index.js +72 -0
  95. package/dist/plugins/notifications/index.js.map +1 -0
  96. package/dist/plugins/notifications/notifications-manager.d.ts +182 -0
  97. package/dist/plugins/notifications/notifications-manager.d.ts.map +1 -0
  98. package/dist/plugins/notifications/notifications-manager.js +610 -0
  99. package/dist/plugins/notifications/notifications-manager.js.map +1 -0
  100. package/dist/plugins/notifications/notifications-plugin.d.ts +83 -0
  101. package/dist/plugins/notifications/notifications-plugin.d.ts.map +1 -0
  102. package/dist/plugins/notifications/notifications-plugin.js +337 -0
  103. package/dist/plugins/notifications/notifications-plugin.js.map +1 -0
  104. package/dist/plugins/notifications/types.d.ts +164 -0
  105. package/dist/plugins/notifications/types.d.ts.map +1 -0
  106. package/dist/plugins/notifications/types.js +9 -0
  107. package/dist/plugins/notifications/types.js.map +1 -0
  108. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts +12 -0
  109. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts.map +1 -0
  110. package/dist/plugins/parental/__tests__/parental-plugin.test.js +349 -0
  111. package/dist/plugins/parental/__tests__/parental-plugin.test.js.map +1 -0
  112. package/dist/plugins/parental/adapters/index.d.ts +8 -0
  113. package/dist/plugins/parental/adapters/index.d.ts.map +1 -0
  114. package/dist/plugins/parental/adapters/index.js +7 -0
  115. package/dist/plugins/parental/adapters/index.js.map +1 -0
  116. package/dist/plugins/parental/adapters/kids-adapter.d.ts +24 -0
  117. package/dist/plugins/parental/adapters/kids-adapter.d.ts.map +1 -0
  118. package/dist/plugins/parental/adapters/kids-adapter.js +174 -0
  119. package/dist/plugins/parental/adapters/kids-adapter.js.map +1 -0
  120. package/dist/plugins/parental/index.d.ts +14 -0
  121. package/dist/plugins/parental/index.d.ts.map +1 -0
  122. package/dist/plugins/parental/index.js +15 -0
  123. package/dist/plugins/parental/index.js.map +1 -0
  124. package/dist/plugins/parental/parental-plugin.d.ts +88 -0
  125. package/dist/plugins/parental/parental-plugin.d.ts.map +1 -0
  126. package/dist/plugins/parental/parental-plugin.js +666 -0
  127. package/dist/plugins/parental/parental-plugin.js.map +1 -0
  128. package/dist/plugins/parental/stores/index.d.ts +7 -0
  129. package/dist/plugins/parental/stores/index.d.ts.map +1 -0
  130. package/dist/plugins/parental/stores/index.js +7 -0
  131. package/dist/plugins/parental/stores/index.js.map +1 -0
  132. package/dist/plugins/parental/stores/postgres-store.d.ts +10 -0
  133. package/dist/plugins/parental/stores/postgres-store.d.ts.map +1 -0
  134. package/dist/plugins/parental/stores/postgres-store.js +209 -0
  135. package/dist/plugins/parental/stores/postgres-store.js.map +1 -0
  136. package/dist/plugins/parental/types.d.ts +154 -0
  137. package/dist/plugins/parental/types.d.ts.map +1 -0
  138. package/dist/plugins/parental/types.js +10 -0
  139. package/dist/plugins/parental/types.js.map +1 -0
  140. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts +11 -0
  141. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts.map +1 -0
  142. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js +243 -0
  143. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js.map +1 -0
  144. package/dist/plugins/profiles/index.d.ts +12 -0
  145. package/dist/plugins/profiles/index.d.ts.map +1 -0
  146. package/dist/plugins/profiles/index.js +13 -0
  147. package/dist/plugins/profiles/index.js.map +1 -0
  148. package/dist/plugins/profiles/profiles-plugin.d.ts +71 -0
  149. package/dist/plugins/profiles/profiles-plugin.d.ts.map +1 -0
  150. package/dist/plugins/profiles/profiles-plugin.js +481 -0
  151. package/dist/plugins/profiles/profiles-plugin.js.map +1 -0
  152. package/dist/plugins/profiles/stores/index.d.ts +9 -0
  153. package/dist/plugins/profiles/stores/index.d.ts.map +1 -0
  154. package/dist/plugins/profiles/stores/index.js +9 -0
  155. package/dist/plugins/profiles/stores/index.js.map +1 -0
  156. package/dist/plugins/profiles/stores/postgres-store.d.ts +18 -0
  157. package/dist/plugins/profiles/stores/postgres-store.d.ts.map +1 -0
  158. package/dist/plugins/profiles/stores/postgres-store.js +310 -0
  159. package/dist/plugins/profiles/stores/postgres-store.js.map +1 -0
  160. package/dist/plugins/profiles/types.d.ts +289 -0
  161. package/dist/plugins/profiles/types.d.ts.map +1 -0
  162. package/dist/plugins/profiles/types.js +10 -0
  163. package/dist/plugins/profiles/types.js.map +1 -0
  164. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.d.ts +7 -0
  165. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.d.ts.map +1 -0
  166. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.js +220 -0
  167. package/dist/plugins/rate-limit/__tests__/rate-limit-plugin.test.js.map +1 -0
  168. package/dist/plugins/rate-limit/cleanup.d.ts +40 -0
  169. package/dist/plugins/rate-limit/cleanup.d.ts.map +1 -0
  170. package/dist/plugins/rate-limit/cleanup.js +72 -0
  171. package/dist/plugins/rate-limit/cleanup.js.map +1 -0
  172. package/dist/plugins/rate-limit/env-config.d.ts +91 -0
  173. package/dist/plugins/rate-limit/env-config.d.ts.map +1 -0
  174. package/dist/plugins/rate-limit/env-config.js +318 -0
  175. package/dist/plugins/rate-limit/env-config.js.map +1 -0
  176. package/dist/plugins/rate-limit/index.d.ts +76 -0
  177. package/dist/plugins/rate-limit/index.d.ts.map +1 -0
  178. package/dist/plugins/rate-limit/index.js +79 -0
  179. package/dist/plugins/rate-limit/index.js.map +1 -0
  180. package/dist/plugins/rate-limit/middleware.d.ts +40 -0
  181. package/dist/plugins/rate-limit/middleware.d.ts.map +1 -0
  182. package/dist/plugins/rate-limit/middleware.js +169 -0
  183. package/dist/plugins/rate-limit/middleware.js.map +1 -0
  184. package/dist/plugins/rate-limit/rate-limit-plugin.d.ts +44 -0
  185. package/dist/plugins/rate-limit/rate-limit-plugin.d.ts.map +1 -0
  186. package/dist/plugins/rate-limit/rate-limit-plugin.js +354 -0
  187. package/dist/plugins/rate-limit/rate-limit-plugin.js.map +1 -0
  188. package/dist/plugins/rate-limit/rate-limit-service.d.ts +110 -0
  189. package/dist/plugins/rate-limit/rate-limit-service.d.ts.map +1 -0
  190. package/dist/plugins/rate-limit/rate-limit-service.js +172 -0
  191. package/dist/plugins/rate-limit/rate-limit-service.js.map +1 -0
  192. package/dist/plugins/rate-limit/stores/cache-store.d.ts +33 -0
  193. package/dist/plugins/rate-limit/stores/cache-store.d.ts.map +1 -0
  194. package/dist/plugins/rate-limit/stores/cache-store.js +225 -0
  195. package/dist/plugins/rate-limit/stores/cache-store.js.map +1 -0
  196. package/dist/plugins/rate-limit/stores/index.d.ts +8 -0
  197. package/dist/plugins/rate-limit/stores/index.d.ts.map +1 -0
  198. package/dist/plugins/rate-limit/stores/index.js +8 -0
  199. package/dist/plugins/rate-limit/stores/index.js.map +1 -0
  200. package/dist/plugins/rate-limit/stores/postgres-store.d.ts +34 -0
  201. package/dist/plugins/rate-limit/stores/postgres-store.d.ts.map +1 -0
  202. package/dist/plugins/rate-limit/stores/postgres-store.js +320 -0
  203. package/dist/plugins/rate-limit/stores/postgres-store.js.map +1 -0
  204. package/dist/plugins/rate-limit/strategies/fixed-window.d.ts +21 -0
  205. package/dist/plugins/rate-limit/strategies/fixed-window.d.ts.map +1 -0
  206. package/dist/plugins/rate-limit/strategies/fixed-window.js +97 -0
  207. package/dist/plugins/rate-limit/strategies/fixed-window.js.map +1 -0
  208. package/dist/plugins/rate-limit/strategies/index.d.ts +14 -0
  209. package/dist/plugins/rate-limit/strategies/index.d.ts.map +1 -0
  210. package/dist/plugins/rate-limit/strategies/index.js +27 -0
  211. package/dist/plugins/rate-limit/strategies/index.js.map +1 -0
  212. package/dist/plugins/rate-limit/strategies/sliding-window.d.ts +22 -0
  213. package/dist/plugins/rate-limit/strategies/sliding-window.d.ts.map +1 -0
  214. package/dist/plugins/rate-limit/strategies/sliding-window.js +122 -0
  215. package/dist/plugins/rate-limit/strategies/sliding-window.js.map +1 -0
  216. package/dist/plugins/rate-limit/strategies/token-bucket.d.ts +28 -0
  217. package/dist/plugins/rate-limit/strategies/token-bucket.d.ts.map +1 -0
  218. package/dist/plugins/rate-limit/strategies/token-bucket.js +121 -0
  219. package/dist/plugins/rate-limit/strategies/token-bucket.js.map +1 -0
  220. package/dist/plugins/rate-limit/types.d.ts +265 -0
  221. package/dist/plugins/rate-limit/types.d.ts.map +1 -0
  222. package/dist/plugins/rate-limit/types.js +9 -0
  223. package/dist/plugins/rate-limit/types.js.map +1 -0
  224. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts +11 -0
  225. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts.map +1 -0
  226. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js +305 -0
  227. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js.map +1 -0
  228. package/dist/plugins/subscriptions/index.d.ts +12 -0
  229. package/dist/plugins/subscriptions/index.d.ts.map +1 -0
  230. package/dist/plugins/subscriptions/index.js +13 -0
  231. package/dist/plugins/subscriptions/index.js.map +1 -0
  232. package/dist/plugins/subscriptions/stores/index.d.ts +9 -0
  233. package/dist/plugins/subscriptions/stores/index.d.ts.map +1 -0
  234. package/dist/plugins/subscriptions/stores/index.js +9 -0
  235. package/dist/plugins/subscriptions/stores/index.js.map +1 -0
  236. package/dist/plugins/subscriptions/stores/postgres-store.d.ts +14 -0
  237. package/dist/plugins/subscriptions/stores/postgres-store.d.ts.map +1 -0
  238. package/dist/plugins/subscriptions/stores/postgres-store.js +359 -0
  239. package/dist/plugins/subscriptions/stores/postgres-store.js.map +1 -0
  240. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts +82 -0
  241. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts.map +1 -0
  242. package/dist/plugins/subscriptions/subscriptions-plugin.js +449 -0
  243. package/dist/plugins/subscriptions/subscriptions-plugin.js.map +1 -0
  244. package/dist/plugins/subscriptions/types.d.ts +308 -0
  245. package/dist/plugins/subscriptions/types.d.ts.map +1 -0
  246. package/dist/plugins/subscriptions/types.js +10 -0
  247. package/dist/plugins/subscriptions/types.js.map +1 -0
  248. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts +11 -0
  249. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts.map +1 -0
  250. package/dist/plugins/usage/__tests__/usage-plugin.test.js +218 -0
  251. package/dist/plugins/usage/__tests__/usage-plugin.test.js.map +1 -0
  252. package/dist/plugins/usage/index.d.ts +12 -0
  253. package/dist/plugins/usage/index.d.ts.map +1 -0
  254. package/dist/plugins/usage/index.js +13 -0
  255. package/dist/plugins/usage/index.js.map +1 -0
  256. package/dist/plugins/usage/stores/index.d.ts +9 -0
  257. package/dist/plugins/usage/stores/index.d.ts.map +1 -0
  258. package/dist/plugins/usage/stores/index.js +9 -0
  259. package/dist/plugins/usage/stores/index.js.map +1 -0
  260. package/dist/plugins/usage/stores/postgres-store.d.ts +14 -0
  261. package/dist/plugins/usage/stores/postgres-store.d.ts.map +1 -0
  262. package/dist/plugins/usage/stores/postgres-store.js +146 -0
  263. package/dist/plugins/usage/stores/postgres-store.js.map +1 -0
  264. package/dist/plugins/usage/types.d.ts +195 -0
  265. package/dist/plugins/usage/types.d.ts.map +1 -0
  266. package/dist/plugins/usage/types.js +10 -0
  267. package/dist/plugins/usage/types.js.map +1 -0
  268. package/dist/plugins/usage/usage-plugin.d.ts +51 -0
  269. package/dist/plugins/usage/usage-plugin.d.ts.map +1 -0
  270. package/dist/plugins/usage/usage-plugin.js +412 -0
  271. package/dist/plugins/usage/usage-plugin.js.map +1 -0
  272. package/dist/plugins/users/__tests__/postgres-store.test.d.ts +10 -0
  273. package/dist/plugins/users/__tests__/postgres-store.test.d.ts.map +1 -0
  274. package/dist/plugins/users/__tests__/postgres-store.test.js +229 -0
  275. package/dist/plugins/users/__tests__/postgres-store.test.js.map +1 -0
  276. package/dist/plugins/users/__tests__/users-plugin.test.js +3 -0
  277. package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -1
  278. package/dist/plugins/users/index.d.ts +2 -2
  279. package/dist/plugins/users/index.d.ts.map +1 -1
  280. package/dist/plugins/users/index.js +1 -1
  281. package/dist/plugins/users/index.js.map +1 -1
  282. package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -1
  283. package/dist/plugins/users/stores/postgres-store.js +76 -0
  284. package/dist/plugins/users/stores/postgres-store.js.map +1 -1
  285. package/dist/plugins/users/types.d.ts +74 -6
  286. package/dist/plugins/users/types.d.ts.map +1 -1
  287. package/dist/plugins/users/users-plugin.d.ts +15 -1
  288. package/dist/plugins/users/users-plugin.d.ts.map +1 -1
  289. package/dist/plugins/users/users-plugin.js +29 -0
  290. package/dist/plugins/users/users-plugin.js.map +1 -1
  291. package/dist-ui/assets/index-CynOqPkb.js +469 -0
  292. package/dist-ui/assets/{index-BY8OxNgO.js.map → index-CynOqPkb.js.map} +1 -1
  293. package/dist-ui/index.html +1 -1
  294. package/dist-ui-lib/api/controlPanelApi.d.ts +187 -0
  295. package/dist-ui-lib/components/StatCard.d.ts +16 -0
  296. package/dist-ui-lib/dashboard/widgets/AuthStatusWidget.d.ts +9 -0
  297. package/dist-ui-lib/dashboard/widgets/IntegrationStatusWidget.d.ts +9 -0
  298. package/dist-ui-lib/dashboard/widgets/NotificationsStatsWidget.d.ts +12 -0
  299. package/dist-ui-lib/dashboard/widgets/index.d.ts +3 -0
  300. package/dist-ui-lib/index.js +3579 -2379
  301. package/dist-ui-lib/index.js.map +1 -1
  302. package/dist-ui-lib/pages/IntegrationsPage.d.ts +1 -0
  303. package/dist-ui-lib/pages/NotificationsPage.d.ts +9 -0
  304. package/dist-ui-lib/pages/RateLimitPage.d.ts +1 -0
  305. package/dist-ui-lib/utils/formatters.d.ts +19 -0
  306. package/package.json +1 -1
  307. package/src/core/control-panel.ts +128 -0
  308. package/src/core/types.ts +17 -0
  309. package/src/index.ts +216 -0
  310. package/src/plugins/auth/adapter-wrapper.test.ts +395 -0
  311. package/src/plugins/auth/adapter-wrapper.ts +205 -0
  312. package/src/plugins/auth/config-store.test.ts +417 -0
  313. package/src/plugins/auth/config-store.ts +305 -0
  314. package/src/plugins/auth/env-config.ts +714 -7
  315. package/src/plugins/auth/index.ts +22 -1
  316. package/src/plugins/auth/types.ts +138 -0
  317. package/src/plugins/bans/bans-plugin.ts +15 -3
  318. package/src/plugins/devices/__tests__/devices-plugin.test.ts +551 -0
  319. package/src/plugins/devices/__tests__/token-utils.test.ts +264 -0
  320. package/src/plugins/devices/adapters/compute-adapter.ts +139 -0
  321. package/src/plugins/devices/adapters/index.ts +13 -0
  322. package/src/plugins/devices/adapters/mobile-adapter.ts +179 -0
  323. package/src/plugins/devices/devices-plugin.ts +538 -0
  324. package/src/plugins/devices/index.ts +69 -0
  325. package/src/plugins/devices/stores/index.ts +9 -0
  326. package/src/plugins/devices/stores/postgres-store.ts +304 -0
  327. package/src/plugins/devices/token-utils.ts +213 -0
  328. package/src/plugins/devices/types.ts +351 -0
  329. package/src/plugins/index.ts +267 -0
  330. package/src/plugins/notifications/__tests__/notifications-manager.test.ts +637 -0
  331. package/src/plugins/notifications/index.ts +91 -0
  332. package/src/plugins/notifications/notifications-manager.ts +773 -0
  333. package/src/plugins/notifications/notifications-plugin.ts +398 -0
  334. package/src/plugins/notifications/types.ts +207 -0
  335. package/src/plugins/parental/__tests__/parental-plugin.test.ts +465 -0
  336. package/src/plugins/parental/adapters/index.ts +8 -0
  337. package/src/plugins/parental/adapters/kids-adapter.ts +206 -0
  338. package/src/plugins/parental/index.ts +55 -0
  339. package/src/plugins/parental/parental-plugin.ts +759 -0
  340. package/src/plugins/parental/stores/index.ts +7 -0
  341. package/src/plugins/parental/stores/postgres-store.ts +304 -0
  342. package/src/plugins/parental/types.ts +180 -0
  343. package/src/plugins/profiles/__tests__/profiles-plugin.test.ts +321 -0
  344. package/src/plugins/profiles/index.ts +49 -0
  345. package/src/plugins/profiles/profiles-plugin.ts +546 -0
  346. package/src/plugins/profiles/stores/index.ts +9 -0
  347. package/src/plugins/profiles/stores/postgres-store.ts +439 -0
  348. package/src/plugins/profiles/types.ts +338 -0
  349. package/src/plugins/rate-limit/__tests__/rate-limit-plugin.test.ts +259 -0
  350. package/src/plugins/rate-limit/cleanup.ts +117 -0
  351. package/src/plugins/rate-limit/env-config.ts +400 -0
  352. package/src/plugins/rate-limit/index.ts +128 -0
  353. package/src/plugins/rate-limit/middleware.ts +212 -0
  354. package/src/plugins/rate-limit/rate-limit-plugin.ts +400 -0
  355. package/src/plugins/rate-limit/rate-limit-service.ts +228 -0
  356. package/src/plugins/rate-limit/stores/cache-store.ts +261 -0
  357. package/src/plugins/rate-limit/stores/index.ts +8 -0
  358. package/src/plugins/rate-limit/stores/postgres-store.ts +402 -0
  359. package/src/plugins/rate-limit/strategies/fixed-window.ts +116 -0
  360. package/src/plugins/rate-limit/strategies/index.ts +30 -0
  361. package/src/plugins/rate-limit/strategies/sliding-window.ts +157 -0
  362. package/src/plugins/rate-limit/strategies/token-bucket.ts +154 -0
  363. package/src/plugins/rate-limit/types.ts +338 -0
  364. package/src/plugins/subscriptions/__tests__/subscriptions-plugin.test.ts +404 -0
  365. package/src/plugins/subscriptions/index.ts +51 -0
  366. package/src/plugins/subscriptions/stores/index.ts +9 -0
  367. package/src/plugins/subscriptions/stores/postgres-store.ts +482 -0
  368. package/src/plugins/subscriptions/subscriptions-plugin.ts +530 -0
  369. package/src/plugins/subscriptions/types.ts +355 -0
  370. package/src/plugins/usage/__tests__/usage-plugin.test.ts +288 -0
  371. package/src/plugins/usage/index.ts +39 -0
  372. package/src/plugins/usage/stores/index.ts +9 -0
  373. package/src/plugins/usage/stores/postgres-store.ts +213 -0
  374. package/src/plugins/usage/types.ts +222 -0
  375. package/src/plugins/usage/usage-plugin.ts +484 -0
  376. package/src/plugins/users/__tests__/postgres-store.test.ts +326 -0
  377. package/src/plugins/users/__tests__/users-plugin.test.ts +3 -0
  378. package/src/plugins/users/index.ts +6 -0
  379. package/src/plugins/users/stores/postgres-store.ts +104 -0
  380. package/src/plugins/users/types.ts +82 -6
  381. package/src/plugins/users/users-plugin.ts +37 -0
  382. package/ui/src/App.tsx +36 -14
  383. package/ui/src/api/controlPanelApi.ts +329 -6
  384. package/ui/src/components/StatCard.tsx +58 -0
  385. package/ui/src/dashboard/builtInWidgets.tsx +7 -1
  386. package/ui/src/dashboard/widgets/AuthStatusWidget.tsx +143 -0
  387. package/ui/src/dashboard/widgets/IntegrationStatusWidget.tsx +135 -0
  388. package/ui/src/dashboard/widgets/NotificationsStatsWidget.tsx +167 -0
  389. package/ui/src/dashboard/widgets/index.ts +3 -0
  390. package/ui/src/pages/AuthPage.tsx +986 -142
  391. package/ui/src/pages/IntegrationsPage.tsx +288 -0
  392. package/ui/src/pages/NotificationsPage.tsx +417 -0
  393. package/ui/src/pages/RateLimitPage.tsx +292 -0
  394. package/ui/src/utils/formatters.ts +33 -0
  395. package/dist-ui/assets/index-BY8OxNgO.js +0 -465
@@ -1,4 +1,4 @@
1
- import { useState, useEffect } from 'react';
1
+ import { useState, useEffect, useCallback } from 'react';
2
2
  import {
3
3
  Box,
4
4
  Card,
@@ -15,13 +15,44 @@ import {
15
15
  Alert,
16
16
  IconButton,
17
17
  Tooltip,
18
+ Button,
19
+ TextField,
20
+ Select,
21
+ MenuItem,
22
+ FormControl,
23
+ InputLabel,
24
+ FormControlLabel,
25
+ Switch,
26
+ Divider,
27
+ Collapse,
28
+ Dialog,
29
+ DialogTitle,
30
+ DialogContent,
31
+ DialogActions,
18
32
  } from '@mui/material';
19
33
  import CheckCircleIcon from '@mui/icons-material/CheckCircle';
20
34
  import ErrorIcon from '@mui/icons-material/Error';
21
35
  import BlockIcon from '@mui/icons-material/Block';
22
36
  import ContentCopyIcon from '@mui/icons-material/ContentCopy';
23
37
  import RefreshIcon from '@mui/icons-material/Refresh';
24
- import { api, AuthConfigStatus } from '../api/controlPanelApi';
38
+ import EditIcon from '@mui/icons-material/Edit';
39
+ import SaveIcon from '@mui/icons-material/Save';
40
+ import CancelIcon from '@mui/icons-material/Cancel';
41
+ import DeleteIcon from '@mui/icons-material/Delete';
42
+ import PlayArrowIcon from '@mui/icons-material/PlayArrow';
43
+ import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
44
+ import ExpandLessIcon from '@mui/icons-material/ExpandLess';
45
+ import {
46
+ api,
47
+ AuthConfigStatus,
48
+ AuthAdapterType,
49
+ Auth0AdapterConfig,
50
+ SupabaseAdapterConfig,
51
+ SupertokensAdapterConfig,
52
+ BasicAdapterConfig,
53
+ UpdateAuthConfigRequest,
54
+ TestProviderResponse,
55
+ } from '../api/controlPanelApi';
25
56
 
26
57
  /**
27
58
  * Get the status color for the auth state
@@ -53,28 +84,152 @@ function getStateIcon(state: string) {
53
84
  }
54
85
  }
55
86
 
87
+ // Default empty configs for each adapter type
88
+ const defaultAuth0Config: Auth0AdapterConfig = {
89
+ domain: '',
90
+ clientId: '',
91
+ clientSecret: '',
92
+ baseUrl: '',
93
+ secret: '',
94
+ audience: '',
95
+ scopes: ['openid', 'profile', 'email'],
96
+ allowedRoles: [],
97
+ allowedDomains: [],
98
+ };
99
+
100
+ const defaultSupabaseConfig: SupabaseAdapterConfig = {
101
+ url: '',
102
+ anonKey: '',
103
+ };
104
+
105
+ const defaultBasicConfig: BasicAdapterConfig = {
106
+ username: '',
107
+ password: '',
108
+ realm: 'Protected Area',
109
+ };
110
+
111
+ const defaultSupertokensConfig: SupertokensAdapterConfig = {
112
+ connectionUri: '',
113
+ apiKey: '',
114
+ appName: '',
115
+ apiDomain: '',
116
+ websiteDomain: '',
117
+ apiBasePath: '/auth',
118
+ websiteBasePath: '/auth',
119
+ enableEmailPassword: true,
120
+ socialProviders: {},
121
+ };
122
+
123
+ interface SocialProvider {
124
+ enabled: boolean;
125
+ clientId: string;
126
+ clientSecret: string;
127
+ keyId?: string;
128
+ teamId?: string;
129
+ }
130
+
56
131
  export function AuthPage() {
57
132
  const [status, setStatus] = useState<AuthConfigStatus | null>(null);
58
133
  const [loading, setLoading] = useState(true);
59
134
  const [error, setError] = useState<string | null>(null);
60
135
  const [copied, setCopied] = useState<string | null>(null);
61
136
 
62
- const fetchStatus = async () => {
137
+ // Edit mode state
138
+ const [editMode, setEditMode] = useState(false);
139
+ const [saving, setSaving] = useState(false);
140
+ const [testing, setTesting] = useState(false);
141
+ const [testResult, setTestResult] = useState<TestProviderResponse | null>(null);
142
+
143
+ // Form state
144
+ const [selectedAdapter, setSelectedAdapter] = useState<AuthAdapterType | ''>('');
145
+ const [auth0Config, setAuth0Config] = useState<Auth0AdapterConfig>(defaultAuth0Config);
146
+ const [supabaseConfig, setSupabaseConfig] = useState<SupabaseAdapterConfig>(defaultSupabaseConfig);
147
+ const [basicConfig, setBasicConfig] = useState<BasicAdapterConfig>(defaultBasicConfig);
148
+ const [supertokensConfig, setSupertokensConfig] = useState<SupertokensAdapterConfig>(defaultSupertokensConfig);
149
+ const [authRequired, setAuthRequired] = useState(true);
150
+ const [excludePaths, setExcludePaths] = useState('');
151
+
152
+ // Social providers state (for SuperTokens)
153
+ const [googleProvider, setGoogleProvider] = useState<SocialProvider>({
154
+ enabled: false,
155
+ clientId: '',
156
+ clientSecret: '',
157
+ });
158
+ const [githubProvider, setGithubProvider] = useState<SocialProvider>({
159
+ enabled: false,
160
+ clientId: '',
161
+ clientSecret: '',
162
+ });
163
+ const [appleProvider, setAppleProvider] = useState<SocialProvider>({
164
+ enabled: false,
165
+ clientId: '',
166
+ clientSecret: '',
167
+ keyId: '',
168
+ teamId: '',
169
+ });
170
+
171
+ // UI state
172
+ const [showSocialProviders, setShowSocialProviders] = useState(false);
173
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
174
+
175
+ const fetchStatus = useCallback(async () => {
63
176
  setLoading(true);
64
177
  setError(null);
65
178
  try {
66
179
  const data = await api.getAuthConfig();
67
180
  setStatus(data);
181
+
182
+ // Initialize form from runtime config if available
183
+ if (data.runtimeConfig) {
184
+ const rc = data.runtimeConfig;
185
+ setSelectedAdapter(rc.adapter || '');
186
+ setAuthRequired(rc.settings.authRequired ?? true);
187
+ setExcludePaths(rc.settings.excludePaths?.join(', ') || '');
188
+
189
+ if (rc.config.auth0) setAuth0Config({ ...defaultAuth0Config, ...rc.config.auth0 });
190
+ if (rc.config.supabase) setSupabaseConfig({ ...defaultSupabaseConfig, ...rc.config.supabase });
191
+ if (rc.config.basic) setBasicConfig({ ...defaultBasicConfig, ...rc.config.basic });
192
+ if (rc.config.supertokens) {
193
+ const st = rc.config.supertokens;
194
+ setSupertokensConfig({ ...defaultSupertokensConfig, ...st });
195
+ if (st.socialProviders?.google) {
196
+ setGoogleProvider({
197
+ enabled: true,
198
+ clientId: st.socialProviders.google.clientId,
199
+ clientSecret: st.socialProviders.google.clientSecret,
200
+ });
201
+ }
202
+ if (st.socialProviders?.github) {
203
+ setGithubProvider({
204
+ enabled: true,
205
+ clientId: st.socialProviders.github.clientId,
206
+ clientSecret: st.socialProviders.github.clientSecret,
207
+ });
208
+ }
209
+ if (st.socialProviders?.apple) {
210
+ setAppleProvider({
211
+ enabled: true,
212
+ clientId: st.socialProviders.apple.clientId,
213
+ clientSecret: st.socialProviders.apple.clientSecret,
214
+ keyId: st.socialProviders.apple.keyId,
215
+ teamId: st.socialProviders.apple.teamId,
216
+ });
217
+ }
218
+ }
219
+ } else if (data.adapter) {
220
+ // Initialize from current adapter if no runtime config
221
+ setSelectedAdapter(data.adapter as AuthAdapterType);
222
+ }
68
223
  } catch (err) {
69
224
  setError(err instanceof Error ? err.message : 'Failed to fetch auth status');
70
225
  } finally {
71
226
  setLoading(false);
72
227
  }
73
- };
228
+ }, []);
74
229
 
75
230
  useEffect(() => {
76
231
  fetchStatus();
77
- }, []);
232
+ }, [fetchStatus]);
78
233
 
79
234
  const handleCopy = (key: string, value: string) => {
80
235
  navigator.clipboard.writeText(value);
@@ -82,6 +237,148 @@ export function AuthPage() {
82
237
  setTimeout(() => setCopied(null), 2000);
83
238
  };
84
239
 
240
+ const handleEnterEditMode = () => {
241
+ setEditMode(true);
242
+ setTestResult(null);
243
+ };
244
+
245
+ const handleCancelEdit = () => {
246
+ setEditMode(false);
247
+ setTestResult(null);
248
+ // Reset form to current status
249
+ fetchStatus();
250
+ };
251
+
252
+ // Helper to convert typed config to plain object for API calls
253
+ // Uses JSON round-trip to ensure clean serialization
254
+ const toPlainObject = <T extends object>(obj: T): Record<string, unknown> =>
255
+ JSON.parse(JSON.stringify(obj));
256
+
257
+ const getCurrentConfig = (): Record<string, unknown> => {
258
+ switch (selectedAdapter) {
259
+ case 'auth0':
260
+ return toPlainObject(auth0Config);
261
+ case 'supabase':
262
+ return toPlainObject(supabaseConfig);
263
+ case 'basic':
264
+ return toPlainObject(basicConfig);
265
+ case 'supertokens': {
266
+ const config: SupertokensAdapterConfig = { ...supertokensConfig };
267
+ const socialProviders: SupertokensAdapterConfig['socialProviders'] = {};
268
+ if (googleProvider.enabled) {
269
+ socialProviders.google = {
270
+ clientId: googleProvider.clientId,
271
+ clientSecret: googleProvider.clientSecret,
272
+ };
273
+ }
274
+ if (githubProvider.enabled) {
275
+ socialProviders.github = {
276
+ clientId: githubProvider.clientId,
277
+ clientSecret: githubProvider.clientSecret,
278
+ };
279
+ }
280
+ if (appleProvider.enabled) {
281
+ socialProviders.apple = {
282
+ clientId: appleProvider.clientId,
283
+ clientSecret: appleProvider.clientSecret,
284
+ keyId: appleProvider.keyId || '',
285
+ teamId: appleProvider.teamId || '',
286
+ };
287
+ }
288
+ if (Object.keys(socialProviders).length > 0) {
289
+ config.socialProviders = socialProviders;
290
+ }
291
+ return toPlainObject(config);
292
+ }
293
+ default:
294
+ return {};
295
+ }
296
+ };
297
+
298
+ const handleTestConnection = async () => {
299
+ if (!selectedAdapter) return;
300
+
301
+ setTesting(true);
302
+ setTestResult(null);
303
+ try {
304
+ const result = await api.testAuthProvider({
305
+ adapter: selectedAdapter,
306
+ config: getCurrentConfig(),
307
+ });
308
+ setTestResult(result);
309
+ } catch (err) {
310
+ setTestResult({
311
+ success: false,
312
+ message: err instanceof Error ? err.message : 'Test failed',
313
+ });
314
+ } finally {
315
+ setTesting(false);
316
+ }
317
+ };
318
+
319
+ // Test the current connection (env-based or runtime config)
320
+ const handleTestCurrentConnection = async () => {
321
+ if (!status?.adapter) return;
322
+
323
+ setTesting(true);
324
+ setTestResult(null);
325
+ try {
326
+ // Call the test endpoint with "current" flag to test existing config
327
+ const result = await api.testCurrentAuthProvider();
328
+ setTestResult(result);
329
+ } catch (err) {
330
+ setTestResult({
331
+ success: false,
332
+ message: err instanceof Error ? err.message : 'Test failed',
333
+ });
334
+ } finally {
335
+ setTesting(false);
336
+ }
337
+ };
338
+
339
+ const handleSave = async () => {
340
+ if (!selectedAdapter) return;
341
+
342
+ setSaving(true);
343
+ setError(null);
344
+ try {
345
+ const request: UpdateAuthConfigRequest = {
346
+ adapter: selectedAdapter,
347
+ config: getCurrentConfig(),
348
+ settings: {
349
+ authRequired,
350
+ excludePaths: excludePaths
351
+ .split(',')
352
+ .map((p) => p.trim())
353
+ .filter(Boolean),
354
+ },
355
+ };
356
+
357
+ await api.updateAuthConfig(request);
358
+ setEditMode(false);
359
+ await fetchStatus();
360
+ } catch (err) {
361
+ setError(err instanceof Error ? err.message : 'Failed to save configuration');
362
+ } finally {
363
+ setSaving(false);
364
+ }
365
+ };
366
+
367
+ const handleDelete = async () => {
368
+ setSaving(true);
369
+ setError(null);
370
+ try {
371
+ await api.deleteAuthConfig();
372
+ setDeleteDialogOpen(false);
373
+ setEditMode(false);
374
+ await fetchStatus();
375
+ } catch (err) {
376
+ setError(err instanceof Error ? err.message : 'Failed to delete configuration');
377
+ } finally {
378
+ setSaving(false);
379
+ }
380
+ };
381
+
85
382
  if (loading) {
86
383
  return (
87
384
  <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '50vh' }}>
@@ -90,16 +387,6 @@ export function AuthPage() {
90
387
  );
91
388
  }
92
389
 
93
- if (error) {
94
- return (
95
- <Card sx={{ bgcolor: 'var(--theme-surface)', border: '1px solid var(--theme-error)' }}>
96
- <CardContent>
97
- <Typography color="error">{error}</Typography>
98
- </CardContent>
99
- </Card>
100
- );
101
- }
102
-
103
390
  const configEntries = status?.config ? Object.entries(status.config) : [];
104
391
 
105
392
  return (
@@ -108,152 +395,709 @@ export function AuthPage() {
108
395
  <Typography variant="h4" sx={{ color: 'var(--theme-text-primary)' }}>
109
396
  Authentication
110
397
  </Typography>
111
- <Tooltip title="Refresh">
112
- <IconButton onClick={fetchStatus} sx={{ color: 'var(--theme-text-secondary)' }}>
113
- <RefreshIcon />
114
- </IconButton>
115
- </Tooltip>
398
+ <Box sx={{ display: 'flex', gap: 1 }}>
399
+ {!editMode && (
400
+ <>
401
+ <Tooltip title="Edit Configuration">
402
+ <IconButton onClick={handleEnterEditMode} sx={{ color: 'var(--theme-primary)' }}>
403
+ <EditIcon />
404
+ </IconButton>
405
+ </Tooltip>
406
+ <Tooltip title="Refresh">
407
+ <IconButton onClick={fetchStatus} sx={{ color: 'var(--theme-text-secondary)' }}>
408
+ <RefreshIcon />
409
+ </IconButton>
410
+ </Tooltip>
411
+ </>
412
+ )}
413
+ </Box>
116
414
  </Box>
117
415
  <Typography variant="body2" sx={{ mb: 4, color: 'var(--theme-text-secondary)' }}>
118
- Auth plugin configuration status
416
+ {editMode ? 'Configure authentication provider' : 'Auth plugin configuration status'}
119
417
  </Typography>
120
418
 
121
- {/* Status Card */}
122
- <Card sx={{ bgcolor: 'var(--theme-surface)', mb: 3 }}>
123
- <CardContent>
124
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 2 }}>
125
- {getStateIcon(status?.state || 'disabled')}
126
- <Box>
127
- <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)' }}>
128
- Status:{' '}
129
- <Chip
130
- label={status?.state?.toUpperCase() || 'UNKNOWN'}
419
+ {error && (
420
+ <Alert severity="error" sx={{ mb: 2 }} onClose={() => setError(null)}>
421
+ {error}
422
+ </Alert>
423
+ )}
424
+
425
+ {/* Edit Mode */}
426
+ {editMode ? (
427
+ <Box>
428
+ {/* Adapter Selection */}
429
+ <Card sx={{ bgcolor: 'var(--theme-surface)', mb: 3 }}>
430
+ <CardContent>
431
+ <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)', mb: 2 }}>
432
+ Provider Selection
433
+ </Typography>
434
+ <FormControl fullWidth sx={{ mb: 2 }}>
435
+ <InputLabel sx={{ color: 'var(--theme-text-secondary)' }}>Auth Provider</InputLabel>
436
+ <Select
437
+ value={selectedAdapter}
438
+ onChange={(e) => setSelectedAdapter(e.target.value as AuthAdapterType | '')}
439
+ label="Auth Provider"
440
+ sx={{ color: 'var(--theme-text-primary)' }}
441
+ >
442
+ <MenuItem value="">
443
+ <em>None (Disabled)</em>
444
+ </MenuItem>
445
+ <MenuItem value="supertokens">SuperTokens</MenuItem>
446
+ <MenuItem value="auth0">Auth0</MenuItem>
447
+ <MenuItem value="supabase">Supabase</MenuItem>
448
+ <MenuItem value="basic">Basic Auth</MenuItem>
449
+ </Select>
450
+ </FormControl>
451
+
452
+ <Box sx={{ display: 'flex', gap: 2, alignItems: 'center' }}>
453
+ <FormControlLabel
454
+ control={
455
+ <Switch
456
+ checked={authRequired}
457
+ onChange={(e) => setAuthRequired(e.target.checked)}
458
+ sx={{ '& .MuiSwitch-switchBase.Mui-checked': { color: 'var(--theme-primary)' } }}
459
+ />
460
+ }
461
+ label="Auth Required"
462
+ sx={{ color: 'var(--theme-text-primary)' }}
463
+ />
464
+ <TextField
465
+ label="Exclude Paths (comma-separated)"
466
+ value={excludePaths}
467
+ onChange={(e) => setExcludePaths(e.target.value)}
131
468
  size="small"
132
- sx={{
133
- bgcolor: `${getStateColor(status?.state || 'disabled')}20`,
134
- color: getStateColor(status?.state || 'disabled'),
135
- fontWeight: 600,
136
- }}
469
+ sx={{ flex: 1, '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
470
+ placeholder="/api/health, /api/public/*"
137
471
  />
138
- </Typography>
139
- {status?.adapter && (
140
- <Typography variant="body2" sx={{ color: 'var(--theme-text-secondary)', mt: 0.5 }}>
141
- Adapter: <strong>{status.adapter}</strong>
472
+ </Box>
473
+ </CardContent>
474
+ </Card>
475
+
476
+ {/* Auth0 Config */}
477
+ {selectedAdapter === 'auth0' && (
478
+ <Card sx={{ bgcolor: 'var(--theme-surface)', mb: 3 }}>
479
+ <CardContent>
480
+ <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)', mb: 2 }}>
481
+ Auth0 Configuration
142
482
  </Typography>
143
- )}
144
- </Box>
145
- </Box>
483
+ <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 2 }}>
484
+ <TextField
485
+ label="Domain"
486
+ value={auth0Config.domain}
487
+ onChange={(e) => setAuth0Config({ ...auth0Config, domain: e.target.value })}
488
+ required
489
+ placeholder="your-tenant.auth0.com"
490
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
491
+ />
492
+ <TextField
493
+ label="Client ID"
494
+ value={auth0Config.clientId}
495
+ onChange={(e) => setAuth0Config({ ...auth0Config, clientId: e.target.value })}
496
+ required
497
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
498
+ />
499
+ <TextField
500
+ label="Client Secret"
501
+ type="password"
502
+ value={auth0Config.clientSecret}
503
+ onChange={(e) => setAuth0Config({ ...auth0Config, clientSecret: e.target.value })}
504
+ required
505
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
506
+ />
507
+ <TextField
508
+ label="Base URL"
509
+ value={auth0Config.baseUrl}
510
+ onChange={(e) => setAuth0Config({ ...auth0Config, baseUrl: e.target.value })}
511
+ required
512
+ placeholder="https://your-app.com"
513
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
514
+ />
515
+ <TextField
516
+ label="Session Secret"
517
+ type="password"
518
+ value={auth0Config.secret}
519
+ onChange={(e) => setAuth0Config({ ...auth0Config, secret: e.target.value })}
520
+ required
521
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
522
+ />
523
+ <TextField
524
+ label="API Audience (optional)"
525
+ value={auth0Config.audience || ''}
526
+ onChange={(e) => setAuth0Config({ ...auth0Config, audience: e.target.value })}
527
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
528
+ />
529
+ </Box>
530
+ </CardContent>
531
+ </Card>
532
+ )}
146
533
 
147
- {/* Error Message */}
148
- {status?.state === 'error' && status.error && (
149
- <Alert severity="error" sx={{ mb: 2 }}>
150
- {status.error}
151
- </Alert>
534
+ {/* Supabase Config */}
535
+ {selectedAdapter === 'supabase' && (
536
+ <Card sx={{ bgcolor: 'var(--theme-surface)', mb: 3 }}>
537
+ <CardContent>
538
+ <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)', mb: 2 }}>
539
+ Supabase Configuration
540
+ </Typography>
541
+ <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 2 }}>
542
+ <TextField
543
+ label="Project URL"
544
+ value={supabaseConfig.url}
545
+ onChange={(e) => setSupabaseConfig({ ...supabaseConfig, url: e.target.value })}
546
+ required
547
+ placeholder="https://your-project.supabase.co"
548
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
549
+ />
550
+ <TextField
551
+ label="Anon Key"
552
+ type="password"
553
+ value={supabaseConfig.anonKey}
554
+ onChange={(e) => setSupabaseConfig({ ...supabaseConfig, anonKey: e.target.value })}
555
+ required
556
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
557
+ />
558
+ </Box>
559
+ </CardContent>
560
+ </Card>
152
561
  )}
153
562
 
154
- {/* Missing Variables */}
155
- {status?.missingVars && status.missingVars.length > 0 && (
156
- <Alert severity="warning" sx={{ mb: 2 }}>
157
- <Typography variant="body2" sx={{ fontWeight: 600, mb: 1 }}>
158
- Missing environment variables:
159
- </Typography>
160
- <Box component="ul" sx={{ m: 0, pl: 2 }}>
161
- {status.missingVars.map((v) => (
162
- <li key={v}>
163
- <code>{v}</code>
164
- </li>
165
- ))}
166
- </Box>
167
- </Alert>
563
+ {/* Basic Auth Config */}
564
+ {selectedAdapter === 'basic' && (
565
+ <Card sx={{ bgcolor: 'var(--theme-surface)', mb: 3 }}>
566
+ <CardContent>
567
+ <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)', mb: 2 }}>
568
+ Basic Auth Configuration
569
+ </Typography>
570
+ <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr 1fr' }, gap: 2 }}>
571
+ <TextField
572
+ label="Username"
573
+ value={basicConfig.username}
574
+ onChange={(e) => setBasicConfig({ ...basicConfig, username: e.target.value })}
575
+ required
576
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
577
+ />
578
+ <TextField
579
+ label="Password"
580
+ type="password"
581
+ value={basicConfig.password}
582
+ onChange={(e) => setBasicConfig({ ...basicConfig, password: e.target.value })}
583
+ required
584
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
585
+ />
586
+ <TextField
587
+ label="Realm (optional)"
588
+ value={basicConfig.realm || ''}
589
+ onChange={(e) => setBasicConfig({ ...basicConfig, realm: e.target.value })}
590
+ placeholder="Protected Area"
591
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
592
+ />
593
+ </Box>
594
+ </CardContent>
595
+ </Card>
168
596
  )}
169
597
 
170
- {/* Disabled State Info */}
171
- {status?.state === 'disabled' && (
172
- <Alert severity="info">
173
- <Typography variant="body2">
174
- Authentication is disabled. Set the <code>AUTH_ADAPTER</code> environment variable to enable.
175
- </Typography>
176
- <Typography variant="body2" sx={{ mt: 1 }}>
177
- Valid options: <code>supertokens</code>, <code>auth0</code>, <code>supabase</code>, <code>basic</code>
598
+ {/* SuperTokens Config */}
599
+ {selectedAdapter === 'supertokens' && (
600
+ <>
601
+ <Card sx={{ bgcolor: 'var(--theme-surface)', mb: 3 }}>
602
+ <CardContent>
603
+ <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)', mb: 2 }}>
604
+ SuperTokens Configuration
605
+ </Typography>
606
+ <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 2 }}>
607
+ <TextField
608
+ label="Connection URI"
609
+ value={supertokensConfig.connectionUri}
610
+ onChange={(e) => setSupertokensConfig({ ...supertokensConfig, connectionUri: e.target.value })}
611
+ required
612
+ placeholder="http://localhost:3567"
613
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
614
+ />
615
+ <TextField
616
+ label="API Key (optional)"
617
+ type="password"
618
+ value={supertokensConfig.apiKey || ''}
619
+ onChange={(e) => setSupertokensConfig({ ...supertokensConfig, apiKey: e.target.value })}
620
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
621
+ />
622
+ <TextField
623
+ label="App Name"
624
+ value={supertokensConfig.appName}
625
+ onChange={(e) => setSupertokensConfig({ ...supertokensConfig, appName: e.target.value })}
626
+ required
627
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
628
+ />
629
+ <TextField
630
+ label="API Domain"
631
+ value={supertokensConfig.apiDomain}
632
+ onChange={(e) => setSupertokensConfig({ ...supertokensConfig, apiDomain: e.target.value })}
633
+ required
634
+ placeholder="http://localhost:3000"
635
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
636
+ />
637
+ <TextField
638
+ label="Website Domain"
639
+ value={supertokensConfig.websiteDomain}
640
+ onChange={(e) => setSupertokensConfig({ ...supertokensConfig, websiteDomain: e.target.value })}
641
+ required
642
+ placeholder="http://localhost:3000"
643
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
644
+ />
645
+ <TextField
646
+ label="API Base Path"
647
+ value={supertokensConfig.apiBasePath || '/auth'}
648
+ onChange={(e) => setSupertokensConfig({ ...supertokensConfig, apiBasePath: e.target.value })}
649
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
650
+ />
651
+ </Box>
652
+ <Box sx={{ mt: 2 }}>
653
+ <FormControlLabel
654
+ control={
655
+ <Switch
656
+ checked={supertokensConfig.enableEmailPassword ?? true}
657
+ onChange={(e) =>
658
+ setSupertokensConfig({ ...supertokensConfig, enableEmailPassword: e.target.checked })
659
+ }
660
+ sx={{ '& .MuiSwitch-switchBase.Mui-checked': { color: 'var(--theme-primary)' } }}
661
+ />
662
+ }
663
+ label="Enable Email/Password Auth"
664
+ sx={{ color: 'var(--theme-text-primary)' }}
665
+ />
666
+ </Box>
667
+ </CardContent>
668
+ </Card>
669
+
670
+ {/* Social Providers */}
671
+ <Card sx={{ bgcolor: 'var(--theme-surface)', mb: 3 }}>
672
+ <CardContent sx={{ pb: showSocialProviders ? 2 : 0 }}>
673
+ <Box
674
+ sx={{
675
+ display: 'flex',
676
+ justifyContent: 'space-between',
677
+ alignItems: 'center',
678
+ cursor: 'pointer',
679
+ }}
680
+ onClick={() => setShowSocialProviders(!showSocialProviders)}
681
+ >
682
+ <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)' }}>
683
+ Social Login Providers
684
+ </Typography>
685
+ {showSocialProviders ? <ExpandLessIcon /> : <ExpandMoreIcon />}
686
+ </Box>
687
+ </CardContent>
688
+ <Collapse in={showSocialProviders}>
689
+ <CardContent sx={{ pt: 0 }}>
690
+ <Divider sx={{ mb: 2 }} />
691
+
692
+ {/* Google */}
693
+ <Box sx={{ mb: 3 }}>
694
+ <FormControlLabel
695
+ control={
696
+ <Switch
697
+ checked={googleProvider.enabled}
698
+ onChange={(e) => setGoogleProvider({ ...googleProvider, enabled: e.target.checked })}
699
+ sx={{ '& .MuiSwitch-switchBase.Mui-checked': { color: 'var(--theme-primary)' } }}
700
+ />
701
+ }
702
+ label="Google"
703
+ sx={{ color: 'var(--theme-text-primary)', mb: 1 }}
704
+ />
705
+ {googleProvider.enabled && (
706
+ <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 2, ml: 4 }}>
707
+ <TextField
708
+ label="Client ID"
709
+ size="small"
710
+ value={googleProvider.clientId}
711
+ onChange={(e) => setGoogleProvider({ ...googleProvider, clientId: e.target.value })}
712
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
713
+ />
714
+ <TextField
715
+ label="Client Secret"
716
+ size="small"
717
+ type="password"
718
+ value={googleProvider.clientSecret}
719
+ onChange={(e) => setGoogleProvider({ ...googleProvider, clientSecret: e.target.value })}
720
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
721
+ />
722
+ </Box>
723
+ )}
724
+ </Box>
725
+
726
+ {/* GitHub */}
727
+ <Box sx={{ mb: 3 }}>
728
+ <FormControlLabel
729
+ control={
730
+ <Switch
731
+ checked={githubProvider.enabled}
732
+ onChange={(e) => setGithubProvider({ ...githubProvider, enabled: e.target.checked })}
733
+ sx={{ '& .MuiSwitch-switchBase.Mui-checked': { color: 'var(--theme-primary)' } }}
734
+ />
735
+ }
736
+ label="GitHub"
737
+ sx={{ color: 'var(--theme-text-primary)', mb: 1 }}
738
+ />
739
+ {githubProvider.enabled && (
740
+ <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 2, ml: 4 }}>
741
+ <TextField
742
+ label="Client ID"
743
+ size="small"
744
+ value={githubProvider.clientId}
745
+ onChange={(e) => setGithubProvider({ ...githubProvider, clientId: e.target.value })}
746
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
747
+ />
748
+ <TextField
749
+ label="Client Secret"
750
+ size="small"
751
+ type="password"
752
+ value={githubProvider.clientSecret}
753
+ onChange={(e) => setGithubProvider({ ...githubProvider, clientSecret: e.target.value })}
754
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
755
+ />
756
+ </Box>
757
+ )}
758
+ </Box>
759
+
760
+ {/* Apple */}
761
+ <Box>
762
+ <FormControlLabel
763
+ control={
764
+ <Switch
765
+ checked={appleProvider.enabled}
766
+ onChange={(e) => setAppleProvider({ ...appleProvider, enabled: e.target.checked })}
767
+ sx={{ '& .MuiSwitch-switchBase.Mui-checked': { color: 'var(--theme-primary)' } }}
768
+ />
769
+ }
770
+ label="Apple"
771
+ sx={{ color: 'var(--theme-text-primary)', mb: 1 }}
772
+ />
773
+ {appleProvider.enabled && (
774
+ <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' }, gap: 2, ml: 4 }}>
775
+ <TextField
776
+ label="Client ID"
777
+ size="small"
778
+ value={appleProvider.clientId}
779
+ onChange={(e) => setAppleProvider({ ...appleProvider, clientId: e.target.value })}
780
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
781
+ />
782
+ <TextField
783
+ label="Client Secret"
784
+ size="small"
785
+ type="password"
786
+ value={appleProvider.clientSecret}
787
+ onChange={(e) => setAppleProvider({ ...appleProvider, clientSecret: e.target.value })}
788
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
789
+ />
790
+ <TextField
791
+ label="Key ID"
792
+ size="small"
793
+ value={appleProvider.keyId || ''}
794
+ onChange={(e) => setAppleProvider({ ...appleProvider, keyId: e.target.value })}
795
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
796
+ />
797
+ <TextField
798
+ label="Team ID"
799
+ size="small"
800
+ value={appleProvider.teamId || ''}
801
+ onChange={(e) => setAppleProvider({ ...appleProvider, teamId: e.target.value })}
802
+ sx={{ '& .MuiInputBase-input': { color: 'var(--theme-text-primary)' } }}
803
+ />
804
+ </Box>
805
+ )}
806
+ </Box>
807
+ </CardContent>
808
+ </Collapse>
809
+ </Card>
810
+ </>
811
+ )}
812
+
813
+ {/* Test Result */}
814
+ {testResult && (
815
+ <Alert severity={testResult.success ? 'success' : 'error'} sx={{ mb: 3 }}>
816
+ <Typography variant="body2" sx={{ fontWeight: 600 }}>
817
+ {testResult.success ? 'Connection Successful' : 'Connection Failed'}
178
818
  </Typography>
819
+ <Typography variant="body2">{testResult.message}</Typography>
820
+ {testResult.details?.latency && (
821
+ <Typography variant="caption" sx={{ display: 'block', mt: 0.5 }}>
822
+ Latency: {testResult.details.latency}ms
823
+ </Typography>
824
+ )}
179
825
  </Alert>
180
826
  )}
181
- </CardContent>
182
- </Card>
183
-
184
- {/* Configuration Table */}
185
- {configEntries.length > 0 && (
186
- <Card sx={{ bgcolor: 'var(--theme-surface)' }}>
187
- <CardContent sx={{ pb: 0 }}>
188
- <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)', mb: 2 }}>
189
- Current Configuration
190
- </Typography>
191
- </CardContent>
192
- <TableContainer>
193
- <Table size="small">
194
- <TableHead>
195
- <TableRow>
196
- <TableCell sx={{ color: 'var(--theme-text-secondary)', borderColor: 'var(--theme-border)' }}>
197
- Variable
198
- </TableCell>
199
- <TableCell sx={{ color: 'var(--theme-text-secondary)', borderColor: 'var(--theme-border)' }}>
200
- Value
201
- </TableCell>
202
- <TableCell
203
- sx={{ color: 'var(--theme-text-secondary)', borderColor: 'var(--theme-border)', width: 60 }}
827
+
828
+ {/* Action Buttons */}
829
+ <Box sx={{ display: 'flex', gap: 2, justifyContent: 'space-between' }}>
830
+ <Box sx={{ display: 'flex', gap: 2 }}>
831
+ <Button
832
+ variant="outlined"
833
+ startIcon={<CancelIcon />}
834
+ onClick={handleCancelEdit}
835
+ disabled={saving}
836
+ sx={{
837
+ color: 'var(--theme-text-secondary)',
838
+ borderColor: 'var(--theme-border)',
839
+ }}
840
+ >
841
+ Cancel
842
+ </Button>
843
+ {status?.runtimeConfig && (
844
+ <Button
845
+ variant="outlined"
846
+ color="error"
847
+ startIcon={<DeleteIcon />}
848
+ onClick={() => setDeleteDialogOpen(true)}
849
+ disabled={saving}
850
+ >
851
+ Reset to Env Vars
852
+ </Button>
853
+ )}
854
+ </Box>
855
+ <Box sx={{ display: 'flex', gap: 2 }}>
856
+ <Button
857
+ variant="outlined"
858
+ startIcon={testing ? <CircularProgress size={16} /> : <PlayArrowIcon />}
859
+ onClick={handleTestConnection}
860
+ disabled={!selectedAdapter || testing || saving}
861
+ sx={{
862
+ color: 'var(--theme-text-primary)',
863
+ borderColor: 'var(--theme-border)',
864
+ }}
865
+ >
866
+ Test Connection
867
+ </Button>
868
+ <Button
869
+ variant="contained"
870
+ startIcon={saving ? <CircularProgress size={16} sx={{ color: 'white' }} /> : <SaveIcon />}
871
+ onClick={handleSave}
872
+ disabled={saving}
873
+ sx={{
874
+ bgcolor: 'var(--theme-primary)',
875
+ '&:hover': { bgcolor: 'var(--theme-primary-dark)' },
876
+ }}
877
+ >
878
+ Save Configuration
879
+ </Button>
880
+ </Box>
881
+ </Box>
882
+ </Box>
883
+ ) : (
884
+ <>
885
+ {/* Status Card (Read-only view) */}
886
+ <Card sx={{ bgcolor: 'var(--theme-surface)', mb: 3 }}>
887
+ <CardContent>
888
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 2 }}>
889
+ {getStateIcon(status?.state || 'disabled')}
890
+ <Box sx={{ flex: 1 }}>
891
+ <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)' }}>
892
+ Status:{' '}
893
+ <Chip
894
+ label={status?.state?.toUpperCase() || 'UNKNOWN'}
895
+ size="small"
896
+ sx={{
897
+ bgcolor: `${getStateColor(status?.state || 'disabled')}20`,
898
+ color: getStateColor(status?.state || 'disabled'),
899
+ fontWeight: 600,
900
+ }}
901
+ />
902
+ </Typography>
903
+ {status?.adapter && (
904
+ <Typography variant="body2" sx={{ color: 'var(--theme-text-secondary)', mt: 0.5 }}>
905
+ Adapter: <strong>{status.adapter}</strong>
906
+ </Typography>
907
+ )}
908
+ </Box>
909
+ {/* Test Current Connection Button */}
910
+ {status?.state === 'enabled' && status?.adapter && (
911
+ <Button
912
+ variant="outlined"
913
+ size="small"
914
+ startIcon={testing ? <CircularProgress size={14} /> : <PlayArrowIcon />}
915
+ onClick={handleTestCurrentConnection}
916
+ disabled={testing}
917
+ sx={{
918
+ color: 'var(--theme-text-primary)',
919
+ borderColor: 'var(--theme-border)',
920
+ }}
204
921
  >
205
- Actions
206
- </TableCell>
207
- </TableRow>
208
- </TableHead>
209
- <TableBody>
210
- {configEntries.map(([key, value]) => (
211
- <TableRow key={key}>
212
- <TableCell sx={{ borderColor: 'var(--theme-border)' }}>
213
- <Typography sx={{ color: 'var(--theme-text-primary)', fontFamily: 'monospace', fontSize: 13 }}>
214
- {key}
215
- </Typography>
216
- </TableCell>
217
- <TableCell sx={{ borderColor: 'var(--theme-border)' }}>
218
- <Typography
219
- sx={{
220
- color: value.includes('*') ? 'var(--theme-text-secondary)' : 'var(--theme-text-primary)',
221
- fontFamily: 'monospace',
222
- fontSize: 13,
223
- }}
922
+ Test Connection
923
+ </Button>
924
+ )}
925
+ </Box>
926
+
927
+ {/* Test Result for current connection */}
928
+ {testResult && !editMode && (
929
+ <Alert severity={testResult.success ? 'success' : 'error'} sx={{ mb: 2 }}>
930
+ <Typography variant="body2" sx={{ fontWeight: 600 }}>
931
+ {testResult.success ? 'Connection Successful' : 'Connection Failed'}
932
+ </Typography>
933
+ <Typography variant="body2">{testResult.message}</Typography>
934
+ {testResult.details?.latency && (
935
+ <Typography variant="caption" sx={{ display: 'block', mt: 0.5 }}>
936
+ Latency: {testResult.details.latency}ms
937
+ </Typography>
938
+ )}
939
+ </Alert>
940
+ )}
941
+
942
+ {/* Environment Variables Config Badge */}
943
+ {status?.state === 'enabled' && !status?.runtimeConfig && (
944
+ <Alert severity="success" sx={{ mb: 2 }}>
945
+ <Typography variant="body2" sx={{ fontWeight: 600 }}>
946
+ Configured via Environment Variables
947
+ </Typography>
948
+ <Typography variant="body2">
949
+ Authentication is configured using environment variables. Click "Edit" to override with runtime
950
+ configuration (requires PostgreSQL).
951
+ </Typography>
952
+ </Alert>
953
+ )}
954
+
955
+ {/* Runtime Config Badge */}
956
+ {status?.runtimeConfig && (
957
+ <Chip
958
+ label="Runtime Configuration Active"
959
+ size="small"
960
+ sx={{
961
+ bgcolor: 'var(--theme-primary)',
962
+ color: 'white',
963
+ mb: 2,
964
+ }}
965
+ />
966
+ )}
967
+
968
+ {/* Error Message */}
969
+ {status?.state === 'error' && status.error && (
970
+ <Alert severity="error" sx={{ mb: 2 }}>
971
+ {status.error}
972
+ </Alert>
973
+ )}
974
+
975
+ {/* Missing Variables */}
976
+ {status?.missingVars && status.missingVars.length > 0 && (
977
+ <Alert severity="warning" sx={{ mb: 2 }}>
978
+ <Typography variant="body2" sx={{ fontWeight: 600, mb: 1 }}>
979
+ Missing environment variables:
980
+ </Typography>
981
+ <Box component="ul" sx={{ m: 0, pl: 2 }}>
982
+ {status.missingVars.map((v) => (
983
+ <li key={v}>
984
+ <code>{v}</code>
985
+ </li>
986
+ ))}
987
+ </Box>
988
+ </Alert>
989
+ )}
990
+
991
+ {/* Disabled State Info */}
992
+ {status?.state === 'disabled' && (
993
+ <Alert severity="info">
994
+ <Typography variant="body2">
995
+ Authentication is disabled. Click the edit button to configure a provider, or set the{' '}
996
+ <code>AUTH_ADAPTER</code> environment variable.
997
+ </Typography>
998
+ <Typography variant="body2" sx={{ mt: 1 }}>
999
+ Valid options: <code>supertokens</code>, <code>auth0</code>, <code>supabase</code>,{' '}
1000
+ <code>basic</code>
1001
+ </Typography>
1002
+ </Alert>
1003
+ )}
1004
+ </CardContent>
1005
+ </Card>
1006
+
1007
+ {/* Configuration Table */}
1008
+ {configEntries.length > 0 && (
1009
+ <Card sx={{ bgcolor: 'var(--theme-surface)' }}>
1010
+ <CardContent sx={{ pb: 0 }}>
1011
+ <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)', mb: 2 }}>
1012
+ Current Configuration
1013
+ </Typography>
1014
+ </CardContent>
1015
+ <TableContainer>
1016
+ <Table size="small">
1017
+ <TableHead>
1018
+ <TableRow>
1019
+ <TableCell sx={{ color: 'var(--theme-text-secondary)', borderColor: 'var(--theme-border)' }}>
1020
+ Variable
1021
+ </TableCell>
1022
+ <TableCell sx={{ color: 'var(--theme-text-secondary)', borderColor: 'var(--theme-border)' }}>
1023
+ Value
1024
+ </TableCell>
1025
+ <TableCell
1026
+ sx={{ color: 'var(--theme-text-secondary)', borderColor: 'var(--theme-border)', width: 60 }}
224
1027
  >
225
- {value}
226
- </Typography>
227
- </TableCell>
228
- <TableCell sx={{ borderColor: 'var(--theme-border)' }}>
229
- <Tooltip title={copied === key ? 'Copied!' : 'Copy value'}>
230
- <IconButton
231
- size="small"
232
- onClick={() => handleCopy(key, value)}
233
- sx={{ color: copied === key ? 'var(--theme-success)' : 'var(--theme-text-secondary)' }}
234
- >
235
- <ContentCopyIcon fontSize="small" />
236
- </IconButton>
237
- </Tooltip>
238
- </TableCell>
239
- </TableRow>
240
- ))}
241
- </TableBody>
242
- </Table>
243
- </TableContainer>
244
- </Card>
245
- )}
1028
+ Actions
1029
+ </TableCell>
1030
+ </TableRow>
1031
+ </TableHead>
1032
+ <TableBody>
1033
+ {configEntries.map(([key, value]) => (
1034
+ <TableRow key={key}>
1035
+ <TableCell sx={{ borderColor: 'var(--theme-border)' }}>
1036
+ <Typography
1037
+ sx={{ color: 'var(--theme-text-primary)', fontFamily: 'monospace', fontSize: 13 }}
1038
+ >
1039
+ {key}
1040
+ </Typography>
1041
+ </TableCell>
1042
+ <TableCell sx={{ borderColor: 'var(--theme-border)' }}>
1043
+ <Typography
1044
+ sx={{
1045
+ color: value.includes('*') ? 'var(--theme-text-secondary)' : 'var(--theme-text-primary)',
1046
+ fontFamily: 'monospace',
1047
+ fontSize: 13,
1048
+ }}
1049
+ >
1050
+ {value}
1051
+ </Typography>
1052
+ </TableCell>
1053
+ <TableCell sx={{ borderColor: 'var(--theme-border)' }}>
1054
+ <Tooltip title={copied === key ? 'Copied!' : 'Copy value'}>
1055
+ <IconButton
1056
+ size="small"
1057
+ onClick={() => handleCopy(key, value)}
1058
+ sx={{ color: copied === key ? 'var(--theme-success)' : 'var(--theme-text-secondary)' }}
1059
+ >
1060
+ <ContentCopyIcon fontSize="small" />
1061
+ </IconButton>
1062
+ </Tooltip>
1063
+ </TableCell>
1064
+ </TableRow>
1065
+ ))}
1066
+ </TableBody>
1067
+ </Table>
1068
+ </TableContainer>
1069
+ </Card>
1070
+ )}
246
1071
 
247
- {/* No Configuration */}
248
- {status?.state === 'enabled' && configEntries.length === 0 && (
249
- <Card sx={{ bgcolor: 'var(--theme-surface)' }}>
250
- <CardContent>
251
- <Typography sx={{ color: 'var(--theme-text-secondary)', textAlign: 'center' }}>
252
- No configuration details available
253
- </Typography>
254
- </CardContent>
255
- </Card>
1072
+ {/* No Configuration */}
1073
+ {status?.state === 'enabled' && configEntries.length === 0 && (
1074
+ <Card sx={{ bgcolor: 'var(--theme-surface)' }}>
1075
+ <CardContent>
1076
+ <Typography sx={{ color: 'var(--theme-text-secondary)', textAlign: 'center' }}>
1077
+ No configuration details available
1078
+ </Typography>
1079
+ </CardContent>
1080
+ </Card>
1081
+ )}
1082
+ </>
256
1083
  )}
1084
+
1085
+ {/* Delete Confirmation Dialog */}
1086
+ <Dialog open={deleteDialogOpen} onClose={() => setDeleteDialogOpen(false)}>
1087
+ <DialogTitle>Reset to Environment Variables?</DialogTitle>
1088
+ <DialogContent>
1089
+ <Typography>
1090
+ This will delete the runtime configuration from the database. The auth plugin will fall back to environment
1091
+ variables on the next request.
1092
+ </Typography>
1093
+ </DialogContent>
1094
+ <DialogActions>
1095
+ <Button onClick={() => setDeleteDialogOpen(false)}>Cancel</Button>
1096
+ <Button onClick={handleDelete} color="error" disabled={saving}>
1097
+ {saving ? <CircularProgress size={20} /> : 'Reset'}
1098
+ </Button>
1099
+ </DialogActions>
1100
+ </Dialog>
257
1101
  </Box>
258
1102
  );
259
1103
  }