create-snapify-app 0.0.0-stage → 1.1.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 (614) hide show
  1. package/LICENSE.md +24 -0
  2. package/README.md +55 -2
  3. package/bin/create-snapify-app.mjs +174 -0
  4. package/lib/args.mjs +59 -0
  5. package/lib/copy.mjs +44 -0
  6. package/lib/env.mjs +84 -0
  7. package/lib/prompt.mjs +63 -0
  8. package/package.json +33 -4
  9. package/template/.env.example +79 -0
  10. package/template/.github/workflows/ci.yml +23 -0
  11. package/template/.github/workflows/deploy.yml +26 -0
  12. package/template/.github/workflows/ping.yml +8 -0
  13. package/template/.github/workflows/release.yml +67 -0
  14. package/template/.snapify-install.json +5 -0
  15. package/template/CHANGELOG.md +28 -0
  16. package/template/LICENSE +14 -0
  17. package/template/README.md +287 -0
  18. package/template/administrator.html +23 -0
  19. package/template/apps/client/src/App.tsx +765 -0
  20. package/template/apps/client/src/auth.css +400 -0
  21. package/template/apps/client/src/lib/api.ts +22 -0
  22. package/template/apps/client/src/main.tsx +20 -0
  23. package/template/apps/community/src/App.tsx +592 -0
  24. package/template/apps/community/src/main.tsx +20 -0
  25. package/template/apps/community/src/styles.css +24 -0
  26. package/template/apps/developers/src/App.tsx +643 -0
  27. package/template/apps/developers/src/main.tsx +17 -0
  28. package/template/apps/developers/src/pages.tsx +1587 -0
  29. package/template/apps/developers/src/styles.css +98 -0
  30. package/template/apps/developers/src/ui.tsx +179 -0
  31. package/template/apps/landing/src/App.tsx +598 -0
  32. package/template/apps/landing/src/LegalPages.tsx +158 -0
  33. package/template/apps/landing/src/OrderPage.tsx +381 -0
  34. package/template/apps/landing/src/SeoPages.tsx +923 -0
  35. package/template/apps/landing/src/copy.ts +195 -0
  36. package/template/apps/landing/src/main.tsx +20 -0
  37. package/template/apps/landing/src/styles.css +803 -0
  38. package/template/apps/landing/src/types.ts +19 -0
  39. package/template/apps/marketplace/src/App.tsx +1229 -0
  40. package/template/apps/marketplace/src/developer/DeveloperApp.tsx +1266 -0
  41. package/template/apps/marketplace/src/main.tsx +20 -0
  42. package/template/apps/marketplace/src/styles.css +1001 -0
  43. package/template/apps/shared/AuthContext.tsx +65 -0
  44. package/template/apps/shared/Logo.tsx +33 -0
  45. package/template/apps/shared/api.ts +47 -0
  46. package/template/apps/shared/ui.tsx +128 -0
  47. package/template/apps/superadmin/src/App.tsx +1264 -0
  48. package/template/apps/superadmin/src/lib/api.ts +20 -0
  49. package/template/apps/superadmin/src/lib/hooks.ts +20 -0
  50. package/template/apps/superadmin/src/main.tsx +16 -0
  51. package/template/apps/superadmin/src/pages/ClientsPage.tsx +185 -0
  52. package/template/apps/superadmin/src/pages/InvoicesPage.tsx +157 -0
  53. package/template/apps/superadmin/src/pages/NewsletterPage.tsx +212 -0
  54. package/template/apps/superadmin/src/pages/OrdersPage.tsx +195 -0
  55. package/template/apps/superadmin/src/pages/PaymentsPage.tsx +150 -0
  56. package/template/apps/superadmin/src/pages/PayoutsPage.tsx +133 -0
  57. package/template/apps/superadmin/src/pages/PublishersPage.tsx +151 -0
  58. package/template/apps/superadmin/src/pages/SeoControlPage.tsx +692 -0
  59. package/template/apps/superadmin/src/pages/SettingsPage.tsx +199 -0
  60. package/template/apps/superadmin/src/pages/SystemPage.tsx +109 -0
  61. package/template/apps/superadmin/src/pages/TicketsPage.tsx +201 -0
  62. package/template/apps/superadmin/src/pages/UsersPage.tsx +167 -0
  63. package/template/client.html +21 -0
  64. package/template/community/hamdiayari/akhbar/README.md +23 -0
  65. package/template/community/hamdiayari/akhbar/screenshot.jpg +0 -0
  66. package/template/community/hamdiayari/akhbar/snapify.theme.json +76 -0
  67. package/template/community/hamdiayari/akhbar/src/Brand.tsx +26 -0
  68. package/template/community/hamdiayari/akhbar/src/Footer.tsx +182 -0
  69. package/template/community/hamdiayari/akhbar/src/Header.tsx +191 -0
  70. package/template/community/hamdiayari/akhbar/src/Layout.tsx +49 -0
  71. package/template/community/hamdiayari/akhbar/src/Social.tsx +33 -0
  72. package/template/community/hamdiayari/akhbar/src/Ticker.tsx +86 -0
  73. package/template/community/hamdiayari/akhbar/src/Views.tsx +285 -0
  74. package/template/community/hamdiayari/akhbar/src/blocks/CollectionBlocks.tsx +672 -0
  75. package/template/community/hamdiayari/akhbar/src/blocks/ContentBlocks.tsx +305 -0
  76. package/template/community/hamdiayari/akhbar/src/blocks/ConversionBlocks.tsx +569 -0
  77. package/template/community/hamdiayari/akhbar/src/blocks/HeaderBlocks.tsx +287 -0
  78. package/template/community/hamdiayari/akhbar/src/index.tsx +51 -0
  79. package/template/community/hamdiayari/akhbar/src/kit.tsx +357 -0
  80. package/template/community/hamdiayari/akhbar/src/site.ts +96 -0
  81. package/template/community/hamdiayari/akhbar/src/styles.css +566 -0
  82. package/template/community/hamdiayari/akhbar-actualites/README.md +28 -0
  83. package/template/community/hamdiayari/akhbar-actualites/screenshot.jpg +0 -0
  84. package/template/community/hamdiayari/akhbar-actualites/snapify.module.json +107 -0
  85. package/template/community/hamdiayari/akhbar-actualites/src/admin/AudiencePage.tsx +185 -0
  86. package/template/community/hamdiayari/akhbar-actualites/src/admin/index.tsx +11 -0
  87. package/template/community/hamdiayari/akhbar-actualites/src/public/Archive.tsx +142 -0
  88. package/template/community/hamdiayari/akhbar-actualites/src/public/cards.tsx +152 -0
  89. package/template/community/hamdiayari/akhbar-actualites/src/public/index.tsx +71 -0
  90. package/template/community/hamdiayari/akhbar-actualites/src/public/posts.ts +103 -0
  91. package/template/community/hamdiayari/akhbar-actualites/src/public/styles.css +568 -0
  92. package/template/community/hamdiayari/akhbar-actualites/src/public/widgets.tsx +226 -0
  93. package/template/community/hamdiayari/akhbar-actualites/src/server.ts +119 -0
  94. package/template/community/hamdiayari/akhbar-actualites/src/types.ts +25 -0
  95. package/template/community/hamdiayari/akhbar-urgent/README.md +36 -0
  96. package/template/community/hamdiayari/akhbar-urgent/screenshot.jpg +0 -0
  97. package/template/community/hamdiayari/akhbar-urgent/snapify.module.json +64 -0
  98. package/template/community/hamdiayari/akhbar-urgent/src/admin/FlashesPage.tsx +229 -0
  99. package/template/community/hamdiayari/akhbar-urgent/src/admin/index.tsx +11 -0
  100. package/template/community/hamdiayari/akhbar-urgent/src/public/Ticker.tsx +81 -0
  101. package/template/community/hamdiayari/akhbar-urgent/src/public/index.tsx +63 -0
  102. package/template/community/hamdiayari/akhbar-urgent/src/public/styles.css +142 -0
  103. package/template/community/hamdiayari/akhbar-urgent/src/server.ts +101 -0
  104. package/template/community/hamdiayari/akhbar-urgent/src/types.ts +35 -0
  105. package/template/community.html +21 -0
  106. package/template/developers.html +24 -0
  107. package/template/docker-compose.db.yml +24 -0
  108. package/template/docs/BACKOFFICE_SNAPIFY.md +130 -0
  109. package/template/docs/CI_CD.md +66 -0
  110. package/template/docs/MODULES.md +346 -0
  111. package/template/docs/PLATFORM_DEPLOY.md +54 -0
  112. package/template/docs/logo.svg +10 -0
  113. package/template/drizzle/0000_init.sql +147 -0
  114. package/template/drizzle/0001_page_project.sql +5 -0
  115. package/template/drizzle/0002_modules.sql +23 -0
  116. package/template/drizzle/0003_themes.sql +4 -0
  117. package/template/drizzle/0004_security.sql +82 -0
  118. package/template/drizzle/0005_module_records.sql +16 -0
  119. package/template/drizzle/0006_avatars.sql +4 -0
  120. package/template/drizzle/0007_platform.sql +264 -0
  121. package/template/drizzle/0008_mp_validation.sql +3 -0
  122. package/template/drizzle/0009_saas_ops.sql +77 -0
  123. package/template/drizzle/0010_cms_chrome.sql +13 -0
  124. package/template/drizzle/0011_client_portal.sql +26 -0
  125. package/template/drizzle/meta/0000_snapshot.json +998 -0
  126. package/template/drizzle/meta/0001_snapshot.json +1004 -0
  127. package/template/drizzle/meta/_journal.json +90 -0
  128. package/template/drizzle.config.ts +16 -0
  129. package/template/ecosystem.snapify.cjs +24 -0
  130. package/template/for.html +1 -0
  131. package/template/index.html +29 -0
  132. package/template/landing.html +37 -0
  133. package/template/marketplace/catalog.json +373 -0
  134. package/template/marketplace/gallery/hamdiayari.preveris/01-home.jpg +0 -0
  135. package/template/marketplace/gallery/hamdiayari.recaptcha/01-reglages.jpg +0 -0
  136. package/template/marketplace/gallery/hamdiayari.recaptcha/02-logo.jpg +0 -0
  137. package/template/marketplace/gallery/novalabs.ai-articles/01-generer.jpg +0 -0
  138. package/template/marketplace/gallery/novalabs.ai-articles/02-fournisseur-ia.jpg +0 -0
  139. package/template/marketplace/gallery/novalabs.calendar/01-agenda-site.jpg +0 -0
  140. package/template/marketplace/gallery/novalabs.calendar/02-evenements-admin.jpg +0 -0
  141. package/template/marketplace/gallery/novalabs.calendar/03-reglages.jpg +0 -0
  142. package/template/marketplace/screenshots/hamdiayari.preveris-1.0.6.jpg +0 -0
  143. package/template/marketplace/screenshots/hamdiayari.recaptcha-1.0.0.jpg +0 -0
  144. package/template/marketplace/screenshots/hamdiayari.wp-seo-migrator-1.1.1.jpg +0 -0
  145. package/template/marketplace/screenshots/novalabs.ai-articles-1.0.1.jpg +0 -0
  146. package/template/marketplace/screenshots/novalabs.ai-chat-1.0.1.jpg +0 -0
  147. package/template/marketplace/screenshots/novalabs.calendar-1.0.1.jpg +0 -0
  148. package/template/marketplace/screenshots/novalabs.cart-1.0.0.jpg +0 -0
  149. package/template/marketplace/screenshots/novalabs.checkout-1.0.0.jpg +0 -0
  150. package/template/marketplace/screenshots/novalabs.customers-1.1.0.jpg +0 -0
  151. package/template/marketplace/screenshots/novalabs.dogga-1.0.0.jpg +0 -0
  152. package/template/marketplace/screenshots/novalabs.mahdia-1.0.0.jpg +0 -0
  153. package/template/marketplace/screenshots/novalabs.shop-1.0.0.jpg +0 -0
  154. package/template/marketplace/screenshots/novalabs.sidibou-1.0.0.jpg +0 -0
  155. package/template/marketplace/screenshots/novalabs.whatsapp-1.0.1.jpg +0 -0
  156. package/template/marketplace.html +21 -0
  157. package/template/metadata.json +7 -0
  158. package/template/modules/ai-articles/screenshot.jpg +0 -0
  159. package/template/modules/ai-articles/snapify.module.json +74 -0
  160. package/template/modules/ai-articles/src/admin/ArticlesPage.tsx +162 -0
  161. package/template/modules/ai-articles/src/admin/GeneratePage.tsx +118 -0
  162. package/template/modules/ai-articles/src/admin/index.tsx +12 -0
  163. package/template/modules/ai-articles/src/public/index.tsx +119 -0
  164. package/template/modules/ai-articles/src/server.ts +287 -0
  165. package/template/modules/ai-articles/src/types.ts +30 -0
  166. package/template/modules/ai-chat/screenshot.jpg +0 -0
  167. package/template/modules/ai-chat/snapify.module.json +217 -0
  168. package/template/modules/ai-chat/src/public.tsx +198 -0
  169. package/template/modules/ai-chat/src/server.ts +142 -0
  170. package/template/modules/ai-theme-studio/README.md +15 -0
  171. package/template/modules/ai-theme-studio/screenshot.jpg +0 -0
  172. package/template/modules/ai-theme-studio/snapify.module.json +175 -0
  173. package/template/modules/ai-theme-studio/src/admin/HistoryPage.tsx +91 -0
  174. package/template/modules/ai-theme-studio/src/admin/StudioPage.tsx +414 -0
  175. package/template/modules/ai-theme-studio/src/admin/index.tsx +11 -0
  176. package/template/modules/ai-theme-studio/src/lib/images.ts +240 -0
  177. package/template/modules/ai-theme-studio/src/lib/llm.ts +123 -0
  178. package/template/modules/ai-theme-studio/src/lib/normalize.ts +714 -0
  179. package/template/modules/ai-theme-studio/src/lib/prompts.ts +198 -0
  180. package/template/modules/ai-theme-studio/src/server.ts +137 -0
  181. package/template/modules/ai-theme-studio/src/types.ts +112 -0
  182. package/template/modules/calendar/screenshot.jpg +0 -0
  183. package/template/modules/calendar/snapify.module.json +45 -0
  184. package/template/modules/calendar/src/admin/EventsPage.tsx +217 -0
  185. package/template/modules/calendar/src/admin/index.tsx +11 -0
  186. package/template/modules/calendar/src/public/index.tsx +231 -0
  187. package/template/modules/calendar/src/server.ts +143 -0
  188. package/template/modules/calendar/src/types.ts +25 -0
  189. package/template/modules/cart/screenshot.jpg +0 -0
  190. package/template/modules/cart/snapify.module.json +90 -0
  191. package/template/modules/cart/src/admin/index.tsx +265 -0
  192. package/template/modules/cart/src/public/CartPage.tsx +198 -0
  193. package/template/modules/cart/src/public/index.tsx +261 -0
  194. package/template/modules/cart/src/public/useQuote.ts +67 -0
  195. package/template/modules/cart/src/server.ts +148 -0
  196. package/template/modules/cart/src/types.ts +31 -0
  197. package/template/modules/checkout/screenshot.jpg +0 -0
  198. package/template/modules/checkout/snapify.module.json +264 -0
  199. package/template/modules/checkout/src/admin/OrderDetailPage.tsx +304 -0
  200. package/template/modules/checkout/src/admin/badges.tsx +28 -0
  201. package/template/modules/checkout/src/admin/index.tsx +236 -0
  202. package/template/modules/checkout/src/public/CheckoutPage.tsx +461 -0
  203. package/template/modules/checkout/src/public/OrderPages.tsx +153 -0
  204. package/template/modules/checkout/src/public/index.tsx +23 -0
  205. package/template/modules/checkout/src/server.ts +383 -0
  206. package/template/modules/checkout/src/types.ts +70 -0
  207. package/template/modules/customers/screenshot.jpg +0 -0
  208. package/template/modules/customers/snapify.module.json +115 -0
  209. package/template/modules/customers/src/admin/CustomerDetailPage.tsx +274 -0
  210. package/template/modules/customers/src/admin/index.tsx +194 -0
  211. package/template/modules/customers/src/public/AccountPages.tsx +470 -0
  212. package/template/modules/customers/src/public/AddressForm.tsx +115 -0
  213. package/template/modules/customers/src/public/AuthPages.tsx +349 -0
  214. package/template/modules/customers/src/public/Avatar.tsx +29 -0
  215. package/template/modules/customers/src/public/ProfileSection.tsx +291 -0
  216. package/template/modules/customers/src/public/index.tsx +137 -0
  217. package/template/modules/customers/src/public/session.ts +51 -0
  218. package/template/modules/customers/src/server.ts +421 -0
  219. package/template/modules/customers/src/types.ts +53 -0
  220. package/template/modules/recaptcha/logo.jpg +0 -0
  221. package/template/modules/recaptcha/screenshot.jpg +0 -0
  222. package/template/modules/recaptcha/snapify.module.json +99 -0
  223. package/template/modules/recaptcha/src/public/index.tsx +94 -0
  224. package/template/modules/recaptcha/src/server.ts +65 -0
  225. package/template/modules/recaptcha/src/types.ts +18 -0
  226. package/template/modules/shop/screenshot.jpg +0 -0
  227. package/template/modules/shop/snapify.module.json +340 -0
  228. package/template/modules/shop/src/admin/CategoriesPage.tsx +224 -0
  229. package/template/modules/shop/src/admin/ProductEditor.tsx +313 -0
  230. package/template/modules/shop/src/admin/ProductsPage.tsx +307 -0
  231. package/template/modules/shop/src/admin/index.tsx +14 -0
  232. package/template/modules/shop/src/public/Catalog.tsx +244 -0
  233. package/template/modules/shop/src/public/NotFound.tsx +22 -0
  234. package/template/modules/shop/src/public/ProductPage.tsx +195 -0
  235. package/template/modules/shop/src/public/index.tsx +134 -0
  236. package/template/modules/shop/src/public/useFetch.ts +26 -0
  237. package/template/modules/shop/src/server.ts +493 -0
  238. package/template/modules/shop/src/shared.ts +41 -0
  239. package/template/modules/shop/src/types.ts +60 -0
  240. package/template/modules/whatsapp/screenshot.jpg +0 -0
  241. package/template/modules/whatsapp/snapify.module.json +121 -0
  242. package/template/modules/whatsapp/src/public.tsx +119 -0
  243. package/template/modules/wp-seo-migrator/ARCHITECTURE.md +113 -0
  244. package/template/modules/wp-seo-migrator/README.md +21 -0
  245. package/template/modules/wp-seo-migrator/fixtures/aioseo-sample.csv +3 -0
  246. package/template/modules/wp-seo-migrator/fixtures/sample-exporter.json +39 -0
  247. package/template/modules/wp-seo-migrator/fixtures/sample-page.html +12 -0
  248. package/template/modules/wp-seo-migrator/fixtures/sample.htaccess +3 -0
  249. package/template/modules/wp-seo-migrator/screenshot.jpg +0 -0
  250. package/template/modules/wp-seo-migrator/snapify.module.json +87 -0
  251. package/template/modules/wp-seo-migrator/src/admin/HistoryPage.tsx +64 -0
  252. package/template/modules/wp-seo-migrator/src/admin/WizardPage.tsx +788 -0
  253. package/template/modules/wp-seo-migrator/src/admin/i18n.ts +156 -0
  254. package/template/modules/wp-seo-migrator/src/admin/index.tsx +11 -0
  255. package/template/modules/wp-seo-migrator/src/connectors/CUSTOM_CONNECTOR.md +31 -0
  256. package/template/modules/wp-seo-migrator/src/connectors/csvSeoFactory.ts +106 -0
  257. package/template/modules/wp-seo-migrator/src/connectors/exporter.ts +96 -0
  258. package/template/modules/wp-seo-migrator/src/connectors/gscCsv.ts +53 -0
  259. package/template/modules/wp-seo-migrator/src/connectors/htaccess.ts +65 -0
  260. package/template/modules/wp-seo-migrator/src/connectors/htmlCrawler.ts +90 -0
  261. package/template/modules/wp-seo-migrator/src/connectors/index.ts +113 -0
  262. package/template/modules/wp-seo-migrator/src/connectors/pluginCsv.ts +77 -0
  263. package/template/modules/wp-seo-migrator/src/connectors/rankMathCsv.ts +77 -0
  264. package/template/modules/wp-seo-migrator/src/connectors/redirection.ts +62 -0
  265. package/template/modules/wp-seo-migrator/src/connectors/sitemap.ts +44 -0
  266. package/template/modules/wp-seo-migrator/src/connectors/utils.ts +111 -0
  267. package/template/modules/wp-seo-migrator/src/connectors/wxr.ts +103 -0
  268. package/template/modules/wp-seo-migrator/src/connectors/yoastCsv.ts +56 -0
  269. package/template/modules/wp-seo-migrator/src/pipeline/analyze.ts +158 -0
  270. package/template/modules/wp-seo-migrator/src/pipeline/htmlParse.ts +58 -0
  271. package/template/modules/wp-seo-migrator/src/pipeline/remoteAuto.ts +328 -0
  272. package/template/modules/wp-seo-migrator/src/server.ts +492 -0
  273. package/template/modules/wp-seo-migrator/src/types.ts +151 -0
  274. package/template/modules/wp-seo-migrator/tests/connectors-extra.test.ts +39 -0
  275. package/template/modules/wp-seo-migrator/tests/merge.test.ts +25 -0
  276. package/template/package-lock.json +6016 -0
  277. package/template/package.json +74 -0
  278. package/template/public/images/photos/caravane-chameaux-desert.webp +0 -0
  279. package/template/public/images/photos/hammamet-medina-plage.jpg +0 -0
  280. package/template/public/images/photos/maison-campagne-collines.webp +0 -0
  281. package/template/public/images/photos/maisons-troglodytes-desert.jpg +0 -0
  282. package/template/public/images/photos/panorama-ville.jpg +0 -0
  283. package/template/public/images/photos/preveris-accessibilite-erp.jpg +0 -0
  284. package/template/public/images/photos/preveris-collectivites.jpg +0 -0
  285. package/template/public/images/photos/preveris-etudes-plans.jpg +0 -0
  286. package/template/public/images/photos/preveris-hero-erp.jpg +0 -0
  287. package/template/public/images/photos/preveris-ssi-incendie.jpg +0 -0
  288. package/template/public/images/photos/preveris-suivi-hotel.jpg +0 -0
  289. package/template/public/images/photos/ruelle-bleue-medina.jpg +0 -0
  290. package/template/public/images/photos/sidi-bou-said-port.webp +0 -0
  291. package/template/public/images/photos/terrasse-blanche-mer-voilier.jpg +0 -0
  292. package/template/public/landing/hero-ai-cms.jpg +0 -0
  293. package/template/public/landing/shot-dogga.jpg +0 -0
  294. package/template/public/landing/shot-mahdia.jpg +0 -0
  295. package/template/public/landing/shot-sidibou.jpg +0 -0
  296. package/template/public/llms.txt +35 -0
  297. package/template/public/logo.svg +10 -0
  298. package/template/scripts/app-pack.ts +155 -0
  299. package/template/scripts/app-publish.ts +100 -0
  300. package/template/scripts/apply-ecommerce-home.ts +196 -0
  301. package/template/scripts/auto-deploy-poll.sh +13 -0
  302. package/template/scripts/db-backup.sh +16 -0
  303. package/template/scripts/db-restore.sh +11 -0
  304. package/template/scripts/deploy.sh +34 -0
  305. package/template/scripts/github-webhook-deploy.sh +7 -0
  306. package/template/scripts/install-new-modules.ts +96 -0
  307. package/template/scripts/refresh-demo-baseline.ts +9 -0
  308. package/template/scripts/reinstall-marketplace-packs.ts +51 -0
  309. package/template/scripts/restore-classic-home.ts +13 -0
  310. package/template/scripts/sdk-pack.ts +289 -0
  311. package/template/scripts/sdk-publish.ts +75 -0
  312. package/template/scripts/seed-demo-products.ts +120 -0
  313. package/template/scripts/seed-theme-content-packs.ts +42 -0
  314. package/template/scripts/snapify-module.ts +207 -0
  315. package/template/scripts/test-theme-switch.ts +24 -0
  316. package/template/sdk/admin.css +18 -0
  317. package/template/sdk/admin.ts +95 -0
  318. package/template/sdk/build.ts +249 -0
  319. package/template/sdk/commerce.ts +512 -0
  320. package/template/sdk/content.ts +76 -0
  321. package/template/sdk/icons.ts +101 -0
  322. package/template/sdk/image.ts +60 -0
  323. package/template/sdk/index.ts +500 -0
  324. package/template/sdk/package.ts +298 -0
  325. package/template/sdk/recaptcha.tsx +122 -0
  326. package/template/sdk/settings.ts +84 -0
  327. package/template/sdk/theme.css +29 -0
  328. package/template/sdk/theme.ts +100 -0
  329. package/template/sdk/ui/commerce.tsx +385 -0
  330. package/template/sdk/ui/orders.tsx +136 -0
  331. package/template/sdk-kit/CHANGELOG.md +17 -0
  332. package/template/sdk-kit/LICENSE.md +47 -0
  333. package/template/sdk-kit/README.md +81 -0
  334. package/template/sdk-kit/cli.ts +339 -0
  335. package/template/sdk-kit/create-snapify/README.md +28 -0
  336. package/template/sdk-kit/create-snapify/create-snapify.mjs +11 -0
  337. package/template/sdk-kit/create-snapify/package.template.json +16 -0
  338. package/template/sdk-kit/package.template.json +95 -0
  339. package/template/sdk-kit/templates/common/README.md.tpl +27 -0
  340. package/template/sdk-kit/templates/common/_gitignore +3 -0
  341. package/template/sdk-kit/templates/common/package.json.tpl +19 -0
  342. package/template/sdk-kit/templates/common/src/snapify-env.d.ts +2 -0
  343. package/template/sdk-kit/templates/common/tsconfig.json +15 -0
  344. package/template/sdk-kit/templates/module/snapify.module.json.tpl +33 -0
  345. package/template/sdk-kit/templates/module/src/admin.tsx +53 -0
  346. package/template/sdk-kit/templates/module/src/public.tsx +49 -0
  347. package/template/sdk-kit/templates/module/src/server.ts +30 -0
  348. package/template/sdk-kit/templates/module/src/types.ts +19 -0
  349. package/template/sdk-kit/templates/theme/snapify.theme.json.tpl +22 -0
  350. package/template/sdk-kit/templates/theme/src/Footer.tsx +40 -0
  351. package/template/sdk-kit/templates/theme/src/Header.tsx +47 -0
  352. package/template/sdk-kit/templates/theme/src/Views.tsx +36 -0
  353. package/template/sdk-kit/templates/theme/src/blocks.tsx +93 -0
  354. package/template/sdk-kit/templates/theme/src/index.tsx +30 -0
  355. package/template/sdk-kit/templates/theme/src/site.tsx +59 -0
  356. package/template/sdk-kit/templates/theme/src/styles.css +71 -0
  357. package/template/server/client/routes.ts +252 -0
  358. package/template/server/cms/analyticsStore.ts +180 -0
  359. package/template/server/cms/auth.ts +155 -0
  360. package/template/server/cms/blockCatalog.ts +342 -0
  361. package/template/server/cms/blocks.ts +521 -0
  362. package/template/server/cms/contentMigration.ts +1119 -0
  363. package/template/server/cms/defaults.ts +303 -0
  364. package/template/server/cms/permissions.ts +154 -0
  365. package/template/server/cms/restoreClassicHome.ts +183 -0
  366. package/template/server/cms/revisions.ts +99 -0
  367. package/template/server/cms/scheduler.ts +78 -0
  368. package/template/server/cms/seed.ts +72 -0
  369. package/template/server/cms/seoAnalyzer.ts +213 -0
  370. package/template/server/cms/store.ts +122 -0
  371. package/template/server/cms/themeContent.ts +1386 -0
  372. package/template/server/cms/themePacks/sidibou-branding.json +1 -0
  373. package/template/server/cms/themePacks/sidibou-home.json +1 -0
  374. package/template/server/cms/themePacks/sidibou-menus.json +1 -0
  375. package/template/server/cms/types.ts +432 -0
  376. package/template/server/community/routes.ts +410 -0
  377. package/template/server/db/client.ts +43 -0
  378. package/template/server/db/repository.ts +389 -0
  379. package/template/server/db/schema.ts +726 -0
  380. package/template/server/demo/cmsReset.ts +196 -0
  381. package/template/server/developers/sdk.ts +85 -0
  382. package/template/server/geoip.ts +15 -0
  383. package/template/server/http.ts +53 -0
  384. package/template/server/mail.ts +216 -0
  385. package/template/server/marketplace/catalog.ts +74 -0
  386. package/template/server/marketplace/packageValidate.ts +475 -0
  387. package/template/server/marketplace/payments.ts +193 -0
  388. package/template/server/marketplace/routes.ts +850 -0
  389. package/template/server/marketplace/signing.ts +116 -0
  390. package/template/server/marketplace/syncOfficial.ts +282 -0
  391. package/template/server/media/images.ts +63 -0
  392. package/template/server/middleware/adminAuth.ts +134 -0
  393. package/template/server/modules/legacy.ts +44 -0
  394. package/template/server/modules/manager.ts +1122 -0
  395. package/template/server/modules/marketplace.ts +178 -0
  396. package/template/server/modules/outbound.ts +178 -0
  397. package/template/server/modules/purchases.ts +149 -0
  398. package/template/server/modules/records.ts +168 -0
  399. package/template/server/modules/themeHtml.ts +32 -0
  400. package/template/server/modules/trust.ts +16 -0
  401. package/template/server/modules/types.ts +147 -0
  402. package/template/server/platform/auth.ts +223 -0
  403. package/template/server/platform/clientWelcome.ts +168 -0
  404. package/template/server/platform/clients.ts +139 -0
  405. package/template/server/platform/dailyImprove.ts +356 -0
  406. package/template/server/platform/flouci.ts +126 -0
  407. package/template/server/platform/host.ts +69 -0
  408. package/template/server/platform/hostingProviders.ts +143 -0
  409. package/template/server/platform/licenseOrders.ts +291 -0
  410. package/template/server/platform/money.ts +42 -0
  411. package/template/server/platform/passwords.ts +41 -0
  412. package/template/server/platform/region.ts +86 -0
  413. package/template/server/platform/routes.ts +225 -0
  414. package/template/server/platform/seed.ts +82 -0
  415. package/template/server/platform/settings.ts +72 -0
  416. package/template/server/routes/adminAccount.ts +403 -0
  417. package/template/server/routes/adminCms.ts +808 -0
  418. package/template/server/routes/adminSystem.ts +243 -0
  419. package/template/server/routes/adminUsers.ts +449 -0
  420. package/template/server/routes/googleOAuth.ts +327 -0
  421. package/template/server/routes/modules.ts +323 -0
  422. package/template/server/routes/publicCms.ts +313 -0
  423. package/template/server/routes/seoPublic.ts +158 -0
  424. package/template/server/security/accounts.ts +121 -0
  425. package/template/server/security/audit.ts +143 -0
  426. package/template/server/security/authTokens.ts +74 -0
  427. package/template/server/security/ip.ts +68 -0
  428. package/template/server/security/rateLimit.ts +85 -0
  429. package/template/server/security/retention.ts +46 -0
  430. package/template/server/security/secrets.ts +44 -0
  431. package/template/server/security/sessions.ts +140 -0
  432. package/template/server/security/totp.ts +101 -0
  433. package/template/server/superadmin/analytics.ts +109 -0
  434. package/template/server/superadmin/auth.ts +92 -0
  435. package/template/server/superadmin/backlinksCatalog.ts +210 -0
  436. package/template/server/superadmin/frSeoContent.ts +165 -0
  437. package/template/server/superadmin/landingSeo.ts +675 -0
  438. package/template/server/superadmin/ops.ts +238 -0
  439. package/template/server/superadmin/routes.ts +1095 -0
  440. package/template/server/superadmin/tnSeoContent.ts +130 -0
  441. package/template/server.ts +522 -0
  442. package/template/src/App.tsx +263 -0
  443. package/template/src/admin/AdminApp.tsx +162 -0
  444. package/template/src/admin/account/AuthLayout.tsx +57 -0
  445. package/template/src/admin/account/SecurityForms.tsx +276 -0
  446. package/template/src/admin/admin.css +127 -0
  447. package/template/src/admin/context/AuthContext.tsx +147 -0
  448. package/template/src/admin/context/SiteContext.tsx +50 -0
  449. package/template/src/admin/editor/BlockLibrary.tsx +173 -0
  450. package/template/src/admin/editor/BlocksPanel.tsx +396 -0
  451. package/template/src/admin/editor/EditorBits.tsx +106 -0
  452. package/template/src/admin/editor/PreviewFrame.tsx +145 -0
  453. package/template/src/admin/editor/RichTextEditor.tsx +276 -0
  454. package/template/src/admin/editor/SeoPanel.tsx +349 -0
  455. package/template/src/admin/editor/StyleEditor.tsx +114 -0
  456. package/template/src/admin/editor/blockSchemas.ts +699 -0
  457. package/template/src/admin/editor/fields.tsx +486 -0
  458. package/template/src/admin/layout/AdminShell.tsx +418 -0
  459. package/template/src/admin/layout/nav.ts +95 -0
  460. package/template/src/admin/lib/api.ts +408 -0
  461. package/template/src/admin/lib/audit.ts +141 -0
  462. package/template/src/admin/lib/format.ts +84 -0
  463. package/template/src/admin/lib/hooks.ts +125 -0
  464. package/template/src/admin/main.tsx +16 -0
  465. package/template/src/admin/modules/InstallDialog.tsx +160 -0
  466. package/template/src/admin/modules/SettingsForm.tsx +145 -0
  467. package/template/src/admin/modules/kit.tsx +127 -0
  468. package/template/src/admin/modules/labels.tsx +226 -0
  469. package/template/src/admin/pages/AccountSetupPage.tsx +62 -0
  470. package/template/src/admin/pages/AnalyticsPage.tsx +206 -0
  471. package/template/src/admin/pages/BrandingPage.tsx +154 -0
  472. package/template/src/admin/pages/DashboardPage.tsx +239 -0
  473. package/template/src/admin/pages/EngagementPage.tsx +168 -0
  474. package/template/src/admin/pages/FormsPage.tsx +435 -0
  475. package/template/src/admin/pages/GeneralPage.tsx +120 -0
  476. package/template/src/admin/pages/LoginPage.tsx +249 -0
  477. package/template/src/admin/pages/MaintenancePage.tsx +125 -0
  478. package/template/src/admin/pages/MarketplacePage.tsx +426 -0
  479. package/template/src/admin/pages/MediaPage.tsx +251 -0
  480. package/template/src/admin/pages/MenusPage.tsx +446 -0
  481. package/template/src/admin/pages/ModuleAdminPage.tsx +113 -0
  482. package/template/src/admin/pages/ModuleDetailPage.tsx +293 -0
  483. package/template/src/admin/pages/ModulesPage.tsx +135 -0
  484. package/template/src/admin/pages/NewsletterPage.tsx +193 -0
  485. package/template/src/admin/pages/PageEditorPage.tsx +322 -0
  486. package/template/src/admin/pages/PagesListPage.tsx +277 -0
  487. package/template/src/admin/pages/PostEditorPage.tsx +292 -0
  488. package/template/src/admin/pages/PostsListPage.tsx +187 -0
  489. package/template/src/admin/pages/ProfilePage.tsx +489 -0
  490. package/template/src/admin/pages/ResetPasswordPage.tsx +110 -0
  491. package/template/src/admin/pages/SeoPage.tsx +670 -0
  492. package/template/src/admin/pages/SettingsPage.tsx +189 -0
  493. package/template/src/admin/pages/ThemeCustomizePage.tsx +170 -0
  494. package/template/src/admin/pages/ThemesPage.tsx +292 -0
  495. package/template/src/admin/pages/UsersPage.tsx +57 -0
  496. package/template/src/admin/pages/WidgetsPage.tsx +200 -0
  497. package/template/src/admin/seo/GoogleSetup.tsx +529 -0
  498. package/template/src/admin/settings/EmailTab.tsx +293 -0
  499. package/template/src/admin/settings/OtherTabs.tsx +270 -0
  500. package/template/src/admin/settings/SecurityTab.tsx +172 -0
  501. package/template/src/admin/settings/controls.tsx +133 -0
  502. package/template/src/admin/ui/Pagination.tsx +170 -0
  503. package/template/src/admin/ui/SaveBar.tsx +77 -0
  504. package/template/src/admin/ui/TagInput.tsx +54 -0
  505. package/template/src/admin/ui/charts.tsx +108 -0
  506. package/template/src/admin/ui/media.tsx +265 -0
  507. package/template/src/admin/ui/overlays.tsx +239 -0
  508. package/template/src/admin/ui/primitives.tsx +544 -0
  509. package/template/src/admin/users/AuditTab.tsx +104 -0
  510. package/template/src/admin/users/RolesTab.tsx +368 -0
  511. package/template/src/admin/users/UsersTab.tsx +723 -0
  512. package/template/src/admin/users/shared.tsx +278 -0
  513. package/template/src/cms/icons.ts +7 -0
  514. package/template/src/components/CookieBanner.tsx +166 -0
  515. package/template/src/components/MaintenanceGate.tsx +66 -0
  516. package/template/src/components/SiteAnalytics.tsx +64 -0
  517. package/template/src/components/SnapifyLogo.tsx +44 -0
  518. package/template/src/components/cms/BlockRenderer.tsx +154 -0
  519. package/template/src/components/cms/WidgetArea.tsx +193 -0
  520. package/template/src/context/SiteConfigContext.tsx +130 -0
  521. package/template/src/demoBase.ts +23 -0
  522. package/template/src/index.css +90 -0
  523. package/template/src/main.tsx +16 -0
  524. package/template/src/modules/ModuleHost.tsx +197 -0
  525. package/template/src/preview.ts +63 -0
  526. package/template/src/runtime.ts +38 -0
  527. package/template/src/themes/ThemeHost.tsx +168 -0
  528. package/template/src/themes/fallback.tsx +71 -0
  529. package/template/src/types.ts +94 -0
  530. package/template/src/views/BlogPostView.tsx +68 -0
  531. package/template/src/views/CmsPageView.tsx +34 -0
  532. package/template/start-pm2.sh +3 -0
  533. package/template/superadmin.html +20 -0
  534. package/template/themes/ai-canvas/screenshot.jpg +0 -0
  535. package/template/themes/ai-canvas/snapify.theme.json +96 -0
  536. package/template/themes/ai-canvas/src/CeeSimulator.tsx +247 -0
  537. package/template/themes/ai-canvas/src/Footer.tsx +190 -0
  538. package/template/themes/ai-canvas/src/Header.tsx +324 -0
  539. package/template/themes/ai-canvas/src/Layout.tsx +56 -0
  540. package/template/themes/ai-canvas/src/NotFound.tsx +58 -0
  541. package/template/themes/ai-canvas/src/Post.tsx +26 -0
  542. package/template/themes/ai-canvas/src/SnapifyLogo.tsx +44 -0
  543. package/template/themes/ai-canvas/src/blocks/CollectionBlocks.tsx +711 -0
  544. package/template/themes/ai-canvas/src/blocks/ContentBlocks.tsx +318 -0
  545. package/template/themes/ai-canvas/src/blocks/ConversionBlocks.tsx +559 -0
  546. package/template/themes/ai-canvas/src/blocks/HeaderBlocks.tsx +341 -0
  547. package/template/themes/ai-canvas/src/index.tsx +54 -0
  548. package/template/themes/ai-canvas/src/kit.tsx +398 -0
  549. package/template/themes/ai-canvas/src/site.ts +29 -0
  550. package/template/themes/ai-canvas/src/styles.css +317 -0
  551. package/template/themes/dogga/screenshot.jpg +0 -0
  552. package/template/themes/dogga/snapify.theme.json +55 -0
  553. package/template/themes/dogga/src/Brand.tsx +41 -0
  554. package/template/themes/dogga/src/Footer.tsx +202 -0
  555. package/template/themes/dogga/src/Header.tsx +239 -0
  556. package/template/themes/dogga/src/Layout.tsx +43 -0
  557. package/template/themes/dogga/src/Views.tsx +71 -0
  558. package/template/themes/dogga/src/blocks/CollectionBlocks.tsx +674 -0
  559. package/template/themes/dogga/src/blocks/ContentBlocks.tsx +307 -0
  560. package/template/themes/dogga/src/blocks/ConversionBlocks.tsx +571 -0
  561. package/template/themes/dogga/src/blocks/HeaderBlocks.tsx +289 -0
  562. package/template/themes/dogga/src/index.tsx +53 -0
  563. package/template/themes/dogga/src/kit.tsx +362 -0
  564. package/template/themes/dogga/src/site.ts +56 -0
  565. package/template/themes/dogga/src/styles.css +396 -0
  566. package/template/themes/mahdia/screenshot.jpg +0 -0
  567. package/template/themes/mahdia/snapify.theme.json +65 -0
  568. package/template/themes/mahdia/src/Brand.tsx +39 -0
  569. package/template/themes/mahdia/src/Footer.tsx +210 -0
  570. package/template/themes/mahdia/src/Header.tsx +291 -0
  571. package/template/themes/mahdia/src/Layout.tsx +44 -0
  572. package/template/themes/mahdia/src/Views.tsx +79 -0
  573. package/template/themes/mahdia/src/blocks/CollectionBlocks.tsx +674 -0
  574. package/template/themes/mahdia/src/blocks/ContentBlocks.tsx +307 -0
  575. package/template/themes/mahdia/src/blocks/ConversionBlocks.tsx +571 -0
  576. package/template/themes/mahdia/src/blocks/HeaderBlocks.tsx +289 -0
  577. package/template/themes/mahdia/src/index.tsx +53 -0
  578. package/template/themes/mahdia/src/kit.tsx +362 -0
  579. package/template/themes/mahdia/src/site.ts +75 -0
  580. package/template/themes/mahdia/src/styles.css +446 -0
  581. package/template/themes/preveris/screenshot.jpg +0 -0
  582. package/template/themes/preveris/snapify.theme.json +126 -0
  583. package/template/themes/preveris/src/Brand.tsx +44 -0
  584. package/template/themes/preveris/src/Footer.tsx +202 -0
  585. package/template/themes/preveris/src/Header.tsx +239 -0
  586. package/template/themes/preveris/src/Layout.tsx +42 -0
  587. package/template/themes/preveris/src/Views.tsx +71 -0
  588. package/template/themes/preveris/src/blocks/CollectionBlocks.tsx +678 -0
  589. package/template/themes/preveris/src/blocks/ContentBlocks.tsx +307 -0
  590. package/template/themes/preveris/src/blocks/ConversionBlocks.tsx +571 -0
  591. package/template/themes/preveris/src/blocks/HeaderBlocks.tsx +299 -0
  592. package/template/themes/preveris/src/index.tsx +53 -0
  593. package/template/themes/preveris/src/kit.tsx +365 -0
  594. package/template/themes/preveris/src/site.ts +79 -0
  595. package/template/themes/preveris/src/styles.css +446 -0
  596. package/template/themes/sidibou/screenshot.jpg +0 -0
  597. package/template/themes/sidibou/snapify.theme.json +21 -0
  598. package/template/themes/sidibou/src/CeeSimulator.tsx +247 -0
  599. package/template/themes/sidibou/src/Footer.tsx +203 -0
  600. package/template/themes/sidibou/src/Header.tsx +324 -0
  601. package/template/themes/sidibou/src/Layout.tsx +53 -0
  602. package/template/themes/sidibou/src/NotFound.tsx +58 -0
  603. package/template/themes/sidibou/src/Post.tsx +26 -0
  604. package/template/themes/sidibou/src/SnapifyLogo.tsx +44 -0
  605. package/template/themes/sidibou/src/blocks/CollectionBlocks.tsx +711 -0
  606. package/template/themes/sidibou/src/blocks/ContentBlocks.tsx +318 -0
  607. package/template/themes/sidibou/src/blocks/ConversionBlocks.tsx +559 -0
  608. package/template/themes/sidibou/src/blocks/HeaderBlocks.tsx +341 -0
  609. package/template/themes/sidibou/src/index.tsx +54 -0
  610. package/template/themes/sidibou/src/kit.tsx +398 -0
  611. package/template/themes/sidibou/src/site.ts +28 -0
  612. package/template/themes/sidibou/src/styles.css +317 -0
  613. package/template/tsconfig.json +33 -0
  614. package/template/vite.config.ts +42 -0
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+ # Snapify — déploiement sur le serveur (GitHub Actions → SSH Host hamdi)
3
+ # Usage : DEPLOY_PATH=/home/snapify ./scripts/deploy.sh [git-ref]
4
+ set -euo pipefail
5
+
6
+ ROOT="${DEPLOY_PATH:-/home/snapify}"
7
+ REF="${1:-origin/main}"
8
+ APP_NAME="${PM2_APP_NAME:-snapify}"
9
+
10
+ cd "$ROOT"
11
+
12
+ if [[ ! -f .env ]]; then
13
+ echo "ERREUR : $ROOT/.env manquant — configuration locale requise, non versionnée."
14
+ exit 1
15
+ fi
16
+
17
+ echo "==> git fetch / reset $REF"
18
+ git fetch --prune origin
19
+ git checkout -B main
20
+ git reset --hard "$REF"
21
+
22
+ echo "==> npm ci"
23
+ # NODE_ENV=production sur le serveur omet les devDependencies — tsx/esbuild sont en dependencies
24
+ NODE_ENV=development npm ci --no-audit --no-fund --legacy-peer-deps
25
+
26
+ echo "==> build"
27
+ NODE_ENV=development npm run build
28
+
29
+ echo "==> pm2 restart $APP_NAME"
30
+ command -v pm2 >/dev/null 2>&1 || { echo "pm2 introuvable"; exit 1; }
31
+ pm2 restart "$APP_NAME" --update-env
32
+ pm2 save || true
33
+
34
+ echo "==> OK $(git rev-parse --short HEAD) · $(date -Is)"
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+ # Appelé par un endpoint léger ou manuellement après push GitHub
3
+ set -euo pipefail
4
+ cd /home/snapify
5
+ git fetch origin main
6
+ git reset --hard origin/main
7
+ bash scripts/deploy.sh origin/main
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Installe Calendrier + Articles IA, licence Articles IA, sync marketplace, seed événements démo.
3
+ */
4
+ import 'dotenv/config';
5
+ import fs from 'fs/promises';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ import { runMigrations } from '../server/db/client';
9
+ import { loadSnapshot } from '../server/cms/store';
10
+ import { initModules, installStaged, setModuleEnabled, setModuleLicense, stagePackage } from '../server/modules/manager';
11
+ import { createCollection } from '../server/modules/records';
12
+ import { syncOfficialMarketplace } from '../server/marketplace/syncOfficial';
13
+ import type { CalEvent } from '../modules/calendar/src/types';
14
+
15
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
+ const ROOT = path.resolve(__dirname, '..');
17
+ const PKG = path.join(ROOT, 'marketplace/packages');
18
+
19
+ await runMigrations();
20
+ await loadSnapshot();
21
+ await initModules();
22
+
23
+ async function install(file: string, enabled: boolean, license?: string) {
24
+ const buf = await fs.readFile(path.join(PKG, file));
25
+ const staged = stagePackage(buf, 'marketplace', { enabled });
26
+ const info = await installStaged(staged.token);
27
+ console.log(`[install] ${info.id} ${info.version} enabled=${info.enabled}`);
28
+ if (license) {
29
+ const lic = await setModuleLicense(info.id, license.trim());
30
+ console.log(`[install] licence ${info.id} : ${lic.license.state}`);
31
+ if (!enabled) await setModuleEnabled(info.id, true);
32
+ } else if (!enabled) {
33
+ await setModuleEnabled(info.id, true);
34
+ }
35
+ }
36
+
37
+ const license = (await fs.readFile('/tmp/ai-articles.license', 'utf8')).trim();
38
+
39
+ await install('novalabs.calendar-1.0.0.snapmod', true);
40
+ await install('novalabs.ai-articles-1.0.0.snapmod', false, license);
41
+
42
+ const events = createCollection<CalEvent>('novalabs.calendar', 'events');
43
+ if ((await events.count()) === 0) {
44
+ const today = new Date();
45
+ const d = (offset: number) => {
46
+ const x = new Date(today);
47
+ x.setDate(x.getDate() + offset);
48
+ return x.toISOString().slice(0, 10);
49
+ };
50
+ const demo: CalEvent[] = [
51
+ {
52
+ title: 'Atelier découverte Snapify',
53
+ slug: 'atelier-snapify',
54
+ description: 'Démonstration du CMS et de la marketplace.',
55
+ location: 'Tunis',
56
+ date: d(3),
57
+ startTime: '10:00',
58
+ endTime: '12:00',
59
+ allDay: false,
60
+ color: '#00C6BA',
61
+ status: 'published',
62
+ },
63
+ {
64
+ title: 'Webinaire e-commerce Mahdia',
65
+ slug: 'webinaire-mahdia',
66
+ description: 'Configurer boutique, panier et paiement à la livraison.',
67
+ location: 'En ligne',
68
+ date: d(7),
69
+ startTime: '18:00',
70
+ endTime: '19:00',
71
+ allDay: false,
72
+ color: '#0066EE',
73
+ status: 'published',
74
+ },
75
+ {
76
+ title: 'Journée portes ouvertes',
77
+ slug: 'portes-ouvertes',
78
+ description: 'Rencontrez l’équipe NOVALABS.',
79
+ location: 'Sfax',
80
+ date: d(14),
81
+ startTime: '',
82
+ endTime: '',
83
+ allDay: true,
84
+ color: '#FF7300',
85
+ status: 'published',
86
+ },
87
+ ];
88
+ for (const e of demo) {
89
+ await events.insert(e, { id: `evt-${e.slug}` });
90
+ console.log(`[seed] événement ${e.title}`);
91
+ }
92
+ }
93
+
94
+ const synced = await syncOfficialMarketplace();
95
+ console.log('[marketplace] sync', synced);
96
+ process.exit(0);
@@ -0,0 +1,9 @@
1
+ import 'dotenv/config';
2
+ import { reloadSnapshotFromDb } from '../server/cms/store';
3
+ import { createCmsBaseline, ensureDemoAdminPassword } from '../server/demo/cmsReset';
4
+
5
+ await reloadSnapshotFromDb();
6
+ await ensureDemoAdminPassword();
7
+ await createCmsBaseline();
8
+ console.log('[baseline] démarrage OK');
9
+ process.exit(0);
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Réinstalle thèmes / modules marketplace manquants sans activer (SidiBou reste actif).
3
+ */
4
+ import 'dotenv/config';
5
+ import fs from 'fs/promises';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ import { db, runMigrations } from '../server/db/client';
9
+ import { modules } from '../server/db/schema';
10
+ import { initModules, installStaged, stagePackage } from '../server/modules/manager';
11
+ import { loadSnapshot } from '../server/cms/store';
12
+
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+ const PKG_DIR = path.resolve(__dirname, '../marketplace/packages');
15
+
16
+ /** Thèmes / modules à réinstaller (désactivés) après restore d’une ancienne sauvegarde. */
17
+ const PACKS: { file: string; enabled: boolean }[] = [
18
+ { file: 'novalabs.dogga-1.0.0.snaptheme', enabled: false },
19
+ { file: 'novalabs.mahdia-1.0.0.snaptheme', enabled: false },
20
+ { file: 'novalabs.shop-1.0.0.snapmod', enabled: false },
21
+ { file: 'novalabs.cart-1.0.0.snapmod', enabled: false },
22
+ { file: 'novalabs.checkout-1.0.0.snapmod', enabled: false },
23
+ { file: 'novalabs.customers-1.1.0.snapmod', enabled: false },
24
+ ];
25
+
26
+ await runMigrations();
27
+ await loadSnapshot();
28
+ await initModules();
29
+
30
+ const installed = new Set((await db.select({ id: modules.id }).from(modules)).map((r) => r.id));
31
+ console.log('[reinstall] déjà présents :', [...installed].sort().join(', ') || '(aucun)');
32
+
33
+ for (const p of PACKS) {
34
+ const full = path.join(PKG_DIR, p.file);
35
+ const buf = await fs.readFile(full);
36
+ const staged = stagePackage(buf, 'marketplace', { enabled: p.enabled });
37
+ const id = staged.manifest.id;
38
+ if (installed.has(id)) {
39
+ console.log(`[reinstall] skip ${id} (déjà installé)`);
40
+ continue;
41
+ }
42
+ const info = await installStaged(staged.token);
43
+ console.log(`[reinstall] ${info.kind} ${info.id} ${info.version} enabled=${info.enabled}`);
44
+ }
45
+
46
+ const rows = await db.select({ id: modules.id, kind: modules.kind, enabled: modules.enabled, version: modules.version }).from(modules);
47
+ console.log('[reinstall] final :');
48
+ for (const r of rows.sort((a, b) => a.kind.localeCompare(b.kind) || a.id.localeCompare(b.id))) {
49
+ console.log(` ${r.kind.padEnd(6)} ${r.enabled ? 'ON ' : 'off'} ${r.id} ${r.version}`);
50
+ }
51
+ process.exit(0);
@@ -0,0 +1,13 @@
1
+ /**
2
+ * One-shot : restaure accueil + menus classiques, puis régénère la baseline démo CMS.
3
+ */
4
+ import 'dotenv/config';
5
+ import { restoreClassicHomeAndMenus } from '../server/cms/restoreClassicHome';
6
+ import { createCmsBaseline, ensureDemoAdminPassword } from '../server/demo/cmsReset';
7
+
8
+ const result = await restoreClassicHomeAndMenus();
9
+ console.log('[restore]', result);
10
+ await ensureDemoAdminPassword();
11
+ await createCmsBaseline();
12
+ console.log('[restore] OK');
13
+ process.exit(0);
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Snapify
3
+ * © 2026 NOVALABS WEB DESIGN — Tunisie
4
+ * Contact : contact@novalabs.tn · +216 24 734 647
5
+ * Tous droits réservés. Toute reproduction, modification ou diffusion sans autorisation écrite est interdite.
6
+ */
7
+ /**
8
+ * Construit le kit développeur public (paquet npm `snapify-sdk`, importé sous le nom `@snapify/sdk`) à partir des sources du dépôt :
9
+ *
10
+ * npm run sdk:pack [-- --force] [-- --base-url https://developers.snapify.tn/sdk]
11
+ *
12
+ * Produit dans releases/sdk/ : snapify-sdk-<v>.tgz (installable avec npm), snapify-starter-<v>.zip
13
+ * (projets de départ module et thème), SHA256SUMS-<v>.txt et index.json (lu par le portail développeurs) ;
14
+ * dans releases/npm/ : snapify-sdk-<v>.tgz (identique au kit téléchargeable) et create-snapify-<v>.tgz, publiés par
15
+ * `npm run sdk:publish`.
16
+ * Seuls le SDK, les déclarations de types qu'il référence et l'outil `snapify` sont inclus : aucune source
17
+ * de l'administration ni du serveur, aucune clé privée.
18
+ */
19
+ import crypto from 'crypto';
20
+ import { execFileSync } from 'child_process';
21
+ import fs from 'fs/promises';
22
+ import os from 'os';
23
+ import path from 'path';
24
+ import { builtinModules } from 'module';
25
+ import { fileURLToPath } from 'url';
26
+ import * as esbuild from 'esbuild';
27
+ import { Scanner } from '@tailwindcss/oxide';
28
+ import { __unstable__loadDesignSystem } from '@tailwindcss/node';
29
+ import { REPO_LAYOUT } from '../sdk/build';
30
+ import { SNAPIFY_MODULE_API_VERSION } from '../sdk/index';
31
+
32
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
33
+ const KIT = path.join(ROOT, 'sdk-kit');
34
+ const OUT = path.join(ROOT, 'releases', 'sdk');
35
+ const NPM_OUT = path.join(ROOT, 'releases', 'npm');
36
+
37
+ const argv = process.argv.slice(2);
38
+ const force = argv.includes('--force');
39
+ const baseUrlArg = argv.indexOf('--base-url');
40
+ const BASE_URL = (baseUrlArg >= 0 ? argv[baseUrlArg + 1] : process.env.SNAPIFY_SDK_BASE_URL || 'https://developers.snapify.tn/sdk').replace(/\/+$/, '');
41
+
42
+ /** Points d'entrée publics (voir `exports` de sdk-kit/package.template.json). */
43
+ const ENTRIES = ['index', 'admin', 'theme', 'content', 'commerce', 'image', 'icons', 'settings', 'ui/commerce', 'ui/orders'];
44
+ /** Fichiers du SDK utilisés seulement par l'outil `snapify` (Node), inclus dans son bundle. */
45
+ const TOOLING_ONLY = new Set(['sdk/build.ts', 'sdk/package.ts']);
46
+ /** Code de l'hôte exécuté par le SDK dans le navigateur. */
47
+ const HOST_RUNTIME = ['server/cms/blocks.ts'];
48
+
49
+ const log = (s: string) => console.log(s);
50
+ const fail = (s: string): never => {
51
+ throw new Error(s);
52
+ };
53
+ const sha256 = (buf: Buffer) => crypto.createHash('sha256').update(buf).digest('hex');
54
+ const rel = (p: string) => path.relative(ROOT, p).split(path.sep).join('/');
55
+
56
+ async function walk(dir: string): Promise<string[]> {
57
+ const out: string[] = [];
58
+ for (const e of await fs.readdir(dir, { withFileTypes: true })) {
59
+ const full = path.join(dir, e.name);
60
+ if (e.isDirectory()) out.push(...(await walk(full)));
61
+ else out.push(full);
62
+ }
63
+ return out;
64
+ }
65
+
66
+ async function write(file: string, data: string | Buffer) {
67
+ await fs.mkdir(path.dirname(file), { recursive: true });
68
+ await fs.writeFile(file, data);
69
+ }
70
+
71
+ /** Remplace l'en-tête « Tous droits réservés » des sources par celui du kit (licence LICENSE.md). */
72
+ function withKitHeader(text: string, banner: string) {
73
+ const stripped = text.replace(/^\/\*\*?\n(?: \*.*\n)*? \*.*Tous droits réservés.*\n \*\/\n/, '');
74
+ return `${banner}\n${stripped}`;
75
+ }
76
+
77
+ async function main() {
78
+ const pkgTemplate = JSON.parse(await fs.readFile(path.join(KIT, 'package.template.json'), 'utf8')) as {
79
+ name: string;
80
+ version: string;
81
+ dependencies: Record<string, string>;
82
+ peerDependencies: Record<string, string>;
83
+ };
84
+ const version = pkgTemplate.version;
85
+ const tarballName = `snapify-sdk-${version}.tgz`;
86
+ const starterName = `snapify-starter-${version}.zip`;
87
+ const sumsName = `SHA256SUMS-${version}.txt`;
88
+ const tarballUrl = `${BASE_URL}/${tarballName}`;
89
+ /** Dépendance écrite dans les projets créés : le paquet npm sous l'alias des imports. */
90
+ const sdkDependency = `npm:${pkgTemplate.name}@^${version}`;
91
+ const banner = `/*! Snapify SDK ${version} — © 2026 NOVALABS WEB DESIGN — licence : LICENSE.md */`;
92
+
93
+ await fs.mkdir(OUT, { recursive: true });
94
+ if (!force && (await fs.access(path.join(OUT, tarballName)).then(() => true, () => false))) {
95
+ fail(`${tarballName} existe déjà : une version publiée ne change plus. Augmentez la version ou utilisez --force.`);
96
+ }
97
+
98
+ const staging = await fs.mkdtemp(path.join(os.tmpdir(), 'snapify-sdk-'));
99
+ const pkgDir = path.join(staging, 'package');
100
+ log(`• Kit ${version} (API ${SNAPIFY_MODULE_API_VERSION}) dans ${staging}`);
101
+
102
+ try {
103
+ /* 1. Code exécuté dans le navigateur : transpilé fichier par fichier, imports relatifs conservés. */
104
+ const sources = [...(await walk(path.join(ROOT, 'sdk'))).filter((f) => /\.tsx?$/.test(f)).map(rel).filter((f) => !TOOLING_ONLY.has(f)), ...HOST_RUNTIME];
105
+ const outputs = new Set(sources.map((f) => f.replace(/\.tsx?$/, '.js')));
106
+ for (const src of sources) {
107
+ const code = await fs.readFile(path.join(ROOT, src), 'utf8');
108
+ const { code: js } = await esbuild.transform(code, { loader: src.endsWith('.tsx') ? 'tsx' : 'ts', format: 'esm', target: 'es2022', jsx: 'automatic', legalComments: 'none', charset: 'utf8' });
109
+ const outRel = src.replace(/\.tsx?$/, '.js');
110
+ const fixed = js.replace(/(\bfrom\s*|\bimport\s*\(\s*|\bimport\s+)"(\.{1,2}\/[^"]+)"/g, (_m, lead: string, spec: string) => {
111
+ const target = path.posix.normalize(path.posix.join(path.posix.dirname(outRel), spec));
112
+ const resolved = [`${target}.js`, `${target}/index.js`].find((c) => outputs.has(c)) ?? fail(`${src} : import introuvable dans le kit « ${spec} »`);
113
+ return `${lead}"${path.posix.relative(path.posix.dirname(outRel), resolved).replace(/^(?!\.)/, './')}"`;
114
+ });
115
+ await write(path.join(pkgDir, 'dist', outRel), `${banner}\n${fixed}`);
116
+ }
117
+ log(` ✔ ${sources.length} fichiers JavaScript`);
118
+
119
+ /* 2. Déclarations TypeScript : émises par tsc, puis seules celles atteignables depuis les entrées. */
120
+ const typesDir = path.join(staging, 'types');
121
+ const tsconfig = path.join(ROOT, '.sdk-pack.tsconfig.json');
122
+ await fs.writeFile(
123
+ tsconfig,
124
+ JSON.stringify({
125
+ extends: './tsconfig.json',
126
+ compilerOptions: { noEmit: false, declaration: true, emitDeclarationOnly: true, outDir: typesDir, rootDir: '.', skipLibCheck: true },
127
+ files: ENTRIES.map((e) => `sdk/${e}.ts${e.startsWith('ui/') ? 'x' : ''}`),
128
+ })
129
+ );
130
+ try {
131
+ execFileSync(path.join(ROOT, 'node_modules/.bin/tsc'), ['-p', tsconfig], { cwd: ROOT, stdio: 'inherit' });
132
+ } finally {
133
+ await fs.rm(tsconfig, { force: true });
134
+ }
135
+ const queue = ENTRIES.map((e) => `sdk/${e}.d.ts`);
136
+ const reachable = new Set<string>();
137
+ const externals = new Set<string>();
138
+ while (queue.length) {
139
+ const file = queue.shift()!;
140
+ if (reachable.has(file)) continue;
141
+ reachable.add(file);
142
+ const text = await fs.readFile(path.join(typesDir, file), 'utf8').catch(() => fail(`Déclaration absente : ${file}`));
143
+ for (const [, spec] of text.matchAll(/(?:\bfrom\s*|\bimport\s*\(\s*)['"]([^'"]+)['"]/g)) {
144
+ if (!spec.startsWith('.')) {
145
+ if (spec.startsWith('@/')) fail(`${file} : alias interne « ${spec} » dans une déclaration publique`);
146
+ externals.add(spec);
147
+ continue;
148
+ }
149
+ const base = path.posix.normalize(path.posix.join(path.posix.dirname(file), spec.replace(/\.(tsx?|js)$/, '')));
150
+ const candidates = [`${base}.d.ts`, `${base}/index.d.ts`];
151
+ const found = await (async () => {
152
+ for (const c of candidates) if (await fs.access(path.join(typesDir, c)).then(() => true, () => false)) return c;
153
+ return null;
154
+ })();
155
+ queue.push(found ?? fail(`${file} : déclaration introuvable pour « ${spec} »`));
156
+ }
157
+ }
158
+ for (const file of reachable) await write(path.join(pkgDir, 'dist', file), withKitHeader(await fs.readFile(path.join(typesDir, file), 'utf8'), banner));
159
+ const typeDeps = [...externals].map((s) => (s.startsWith('@') ? s.split('/').slice(0, 2).join('/') : s.split('/')[0]));
160
+ const missingTypes = typeDeps.filter((d) => !(d in pkgTemplate.dependencies) && !(d in pkgTemplate.peerDependencies) && d !== '@types/react');
161
+ log(` ✔ ${reachable.size} déclarations (.d.ts) — dépendances de types : ${[...new Set(typeDeps)].join(', ') || 'aucune'}`);
162
+ if (missingTypes.length) log(` ! types sans dépendance déclarée (résolus en any chez le développeur) : ${[...new Set(missingTypes)].join(', ')}`);
163
+
164
+ /* 3. Feuilles de style et classes des composants d'administration prêtés aux modules. */
165
+ for (const css of ['theme.css', 'admin.css']) await write(path.join(pkgDir, 'css', css), withKitHeader(await fs.readFile(path.join(ROOT, 'sdk', css), 'utf8'), banner.replace(/^\/\*!/, '/*')));
166
+ const scanner = new Scanner({
167
+ sources: await Promise.all(
168
+ REPO_LAYOUT.adminSources.map(async (p) =>
169
+ (await fs.stat(p)).isDirectory() ? { base: p, pattern: '**/*', negated: false } : { base: path.dirname(p), pattern: path.basename(p), negated: false }
170
+ )
171
+ ),
172
+ });
173
+ const candidates = scanner.scan();
174
+ const design = await __unstable__loadDesignSystem(`@import "tailwindcss";\n@import ${JSON.stringify(REPO_LAYOUT.adminCss)};`, { base: ROOT });
175
+ const css = design.candidatesToCss(candidates);
176
+ const classes = candidates.filter((_, i) => css[i]).sort();
177
+ await write(path.join(pkgDir, 'css', 'admin-kit.classes.txt'), `${classes.join('\n')}\n`);
178
+ log(` ✔ ${classes.length} classes des composants d’administration`);
179
+
180
+ /* 4. Outil `snapify` : un seul fichier, dépendances npm externes. */
181
+ const cli = path.join(pkgDir, 'bin', 'snapify.mjs');
182
+ await esbuild.build({
183
+ entryPoints: [path.join(KIT, 'cli.ts')],
184
+ outfile: cli,
185
+ bundle: true,
186
+ platform: 'node',
187
+ format: 'esm',
188
+ target: 'node20',
189
+ packages: 'external',
190
+ legalComments: 'none',
191
+ charset: 'utf8',
192
+ banner: { js: `#!/usr/bin/env node\n${banner}` },
193
+ define: { __SDK_VERSION__: JSON.stringify(version), __SDK_DEPENDENCY__: JSON.stringify(sdkDependency) },
194
+ logLevel: 'warning',
195
+ });
196
+ await fs.chmod(cli, 0o755);
197
+ const cliCode = await fs.readFile(cli, 'utf8');
198
+ const builtins = new Set([...builtinModules, ...builtinModules.map((m) => `node:${m}`)]);
199
+ const imported = new Set(
200
+ [...cliCode.matchAll(/^(?:import|export)\b[^\n]*?\bfrom\s*"([^"]+)";?$|^import\s*"([^"]+)";?$|\bimport\(\s*"([^"]+)"\s*\)/gm)]
201
+ .map((m) => m[1] ?? m[2] ?? m[3])
202
+ .filter((s) => !s.startsWith('.'))
203
+ );
204
+ const undeclared = [...imported]
205
+ .filter((s) => !builtins.has(s))
206
+ .map((s) => (s.startsWith('@') ? s.split('/').slice(0, 2).join('/') : s.split('/')[0]))
207
+ .filter((d) => !(d in pkgTemplate.dependencies) && !(d in pkgTemplate.peerDependencies));
208
+ if (undeclared.length) fail(`snapify.mjs importe des paquets absents des dépendances : ${undeclared.join(', ')}`);
209
+ log(` ✔ bin/snapify.mjs (${(cliCode.length / 1024).toFixed(0)} Ko)`);
210
+
211
+ /* 5. Modèles, documentation, manifeste npm. */
212
+ for (const file of await walk(path.join(KIT, 'templates'))) {
213
+ await write(path.join(pkgDir, 'templates', path.relative(path.join(KIT, 'templates'), file)), await fs.readFile(file));
214
+ }
215
+ await write(path.join(pkgDir, 'README.md'), (await fs.readFile(path.join(KIT, 'README.md'), 'utf8')).replaceAll('__SDK_TARBALL_URL__', tarballUrl).replaceAll('__SDK_VERSION__', version));
216
+ for (const doc of ['LICENSE.md', 'CHANGELOG.md']) await fs.copyFile(path.join(KIT, doc), path.join(pkgDir, doc));
217
+ await write(path.join(pkgDir, 'package.json'), `${JSON.stringify(pkgTemplate, null, 2)}\n`);
218
+
219
+ /* 6. Contrôle : ni clé privée ni secret dans le kit. */
220
+ const SECRET = /-----BEGIN [A-Z ]*PRIVATE KEY-----|SNAPIFY_SIGNING_KEY\s*=|ADMIN_PASSWORD\s*=|DATABASE_URL\s*=|postgres(?:ql)?:\/\/[^\s"'`]*@/;
221
+ for (const file of await walk(pkgDir)) {
222
+ if (/\.(jpe?g|png|webp)$/i.test(file)) continue;
223
+ if (SECRET.test(await fs.readFile(file, 'utf8'))) fail(`Donnée sensible détectée dans ${path.relative(pkgDir, file)}`);
224
+ }
225
+ log(' ✔ aucun secret dans le kit');
226
+
227
+ /* 7. Paquet npm. */
228
+ const packed = JSON.parse(execFileSync('npm', ['pack', '--json', '--pack-destination', staging], { cwd: pkgDir, encoding: 'utf8' })) as { filename: string; files: unknown[] }[];
229
+ await fs.copyFile(path.join(staging, packed[0].filename), path.join(OUT, tarballName));
230
+ await fs.mkdir(NPM_OUT, { recursive: true });
231
+ await fs.copyFile(path.join(staging, packed[0].filename), path.join(NPM_OUT, `${pkgTemplate.name}-${version}.tgz`));
232
+ log(` ✔ ${tarballName} (${packed[0].files.length} fichiers)`);
233
+
234
+ /* 7 bis. `create-snapify` : `npm create snapify@latest` lance `snapify create` de la même version. */
235
+ const createSrc = path.join(KIT, 'create-snapify');
236
+ const createDir = path.join(staging, 'create-snapify');
237
+ const createPkg = JSON.parse(await fs.readFile(path.join(createSrc, 'package.template.json'), 'utf8'));
238
+ createPkg.version = version;
239
+ createPkg.dependencies = { [pkgTemplate.name]: version };
240
+ await write(path.join(createDir, 'package.json'), `${JSON.stringify(createPkg, null, 2)}\n`);
241
+ await write(path.join(createDir, 'bin', 'create-snapify.mjs'), await fs.readFile(path.join(createSrc, 'create-snapify.mjs')));
242
+ await fs.chmod(path.join(createDir, 'bin', 'create-snapify.mjs'), 0o755);
243
+ await fs.copyFile(path.join(createSrc, 'README.md'), path.join(createDir, 'README.md'));
244
+ await fs.copyFile(path.join(KIT, 'LICENSE.md'), path.join(createDir, 'LICENSE.md'));
245
+ const createPacked = JSON.parse(execFileSync('npm', ['pack', '--json', '--pack-destination', staging], { cwd: createDir, encoding: 'utf8' })) as { filename: string }[];
246
+ await fs.copyFile(path.join(staging, createPacked[0].filename), path.join(NPM_OUT, `create-snapify-${version}.tgz`));
247
+ log(` ✔ create-snapify-${version}.tgz`);
248
+
249
+ /* 8. Projets de départ, générés par l'outil lui-même. */
250
+ const starterRoot = path.join(staging, 'starter');
251
+ await fs.mkdir(starterRoot, { recursive: true });
252
+ const env = { ...process.env, SNAPIFY_SDK_VERSION: version, SNAPIFY_SDK_DEPENDENCY: sdkDependency, NO_COLOR: '1' };
253
+ const tsx = path.join(ROOT, 'node_modules/.bin/tsx');
254
+ for (const [dir, extra] of [['snapify-module-starter', ['--id', 'monagence.mon-module', '--name', 'Mon module']], ['snapify-theme-starter', ['--theme', '--id', 'monagence.mon-theme', '--name', 'Mon thème']]] as const) {
255
+ execFileSync(tsx, [path.join(KIT, 'cli.ts'), 'create', dir, ...extra, '--author', 'Mon Agence', '--email', 'dev@monagence.tn'], { cwd: starterRoot, env, stdio: 'ignore' });
256
+ }
257
+ const zip = path.join(OUT, starterName);
258
+ await fs.rm(zip, { force: true });
259
+ execFileSync('python3', ['-m', 'zipfile', '-c', zip, 'snapify-module-starter', 'snapify-theme-starter'], { cwd: starterRoot });
260
+ log(` ✔ ${starterName}`);
261
+
262
+ /* 9. Sommes de contrôle et index du portail. */
263
+ const files = await Promise.all(
264
+ [
265
+ { name: tarballName, kind: 'sdk' },
266
+ { name: starterName, kind: 'starter' },
267
+ ].map(async (f) => {
268
+ const buf = await fs.readFile(path.join(OUT, f.name));
269
+ return { ...f, size: buf.length, sha256: sha256(buf) };
270
+ })
271
+ );
272
+ await fs.writeFile(path.join(OUT, sumsName), files.map((f) => `${f.sha256} ${f.name}\n`).join(''));
273
+ const indexFile = path.join(OUT, 'index.json');
274
+ const index = JSON.parse(await fs.readFile(indexFile, 'utf8').catch(() => '{"releases":[]}')) as { latest?: string; releases: { version: string }[] };
275
+ const release = { version, api: SNAPIFY_MODULE_API_VERSION, date: new Date().toISOString(), checksums: sumsName, npm: { sdk: pkgTemplate.name, create: 'create-snapify' }, files };
276
+ index.releases = [release, ...index.releases.filter((r) => r.version !== version)];
277
+ index.latest = version;
278
+ await fs.writeFile(indexFile, `${JSON.stringify(index, null, 2)}\n`);
279
+ for (const f of files) log(` ${f.sha256} ${f.name} (${(f.size / 1024).toFixed(0)} Ko)`);
280
+ log(`✔ Kit ${version} prêt dans ${rel(OUT)}`);
281
+ } finally {
282
+ await fs.rm(staging, { recursive: true, force: true });
283
+ }
284
+ }
285
+
286
+ main().catch((e) => {
287
+ console.error(`✖ ${e instanceof Error ? e.message : String(e)}`);
288
+ process.exit(1);
289
+ });
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Snapify
3
+ * © 2026 NOVALABS WEB DESIGN — Tunisie
4
+ * Contact : contact@novalabs.tn · +216 24 734 647
5
+ * Tous droits réservés. Toute reproduction, modification ou diffusion sans autorisation écrite est interdite.
6
+ */
7
+ /**
8
+ * Publie sur le registre npm la version du kit préparée par `npm run sdk:pack` :
9
+ *
10
+ * npm run sdk:publish -- --dry-run contrôle sans rien publier
11
+ * npm run sdk:publish [-- --otp 123456] publication (compte npm connecté avec `npm login`)
12
+ *
13
+ * Publie `snapify-sdk` puis `create-snapify` (qui en dépend), depuis les archives de releases/npm/. L'archive
14
+ * `snapify-sdk` doit être identique au kit téléchargeable de developers.snapify.tn (même empreinte SHA-256).
15
+ */
16
+ import crypto from 'crypto';
17
+ import { execFileSync } from 'child_process';
18
+ import fs from 'fs/promises';
19
+ import path from 'path';
20
+ import { fileURLToPath } from 'url';
21
+
22
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
23
+ const argv = process.argv.slice(2);
24
+ const dryRun = argv.includes('--dry-run');
25
+ const otpIndex = argv.indexOf('--otp');
26
+ const otp = otpIndex >= 0 ? argv[otpIndex + 1] : undefined;
27
+
28
+ const sha256 = async (file: string) => crypto.createHash('sha256').update(await fs.readFile(file)).digest('hex');
29
+ const npm = (args: string[]) => execFileSync('npm', args, { cwd: ROOT, encoding: 'utf8', stdio: ['inherit', 'pipe', 'inherit'] }).trim();
30
+
31
+ async function main() {
32
+ const { name, version } = JSON.parse(await fs.readFile(path.join(ROOT, 'sdk-kit/package.template.json'), 'utf8')) as { name: string; version: string };
33
+ const sdk = path.join(ROOT, 'releases/npm', `${name}-${version}.tgz`);
34
+ const create = path.join(ROOT, 'releases/npm', `create-snapify-${version}.tgz`);
35
+ const mirror = path.join(ROOT, 'releases/sdk', `snapify-sdk-${version}.tgz`);
36
+ for (const file of [sdk, create, mirror]) {
37
+ await fs.access(file).catch(() => {
38
+ throw new Error(`${path.relative(ROOT, file)} absent : lancez d'abord npm run sdk:pack`);
39
+ });
40
+ }
41
+ if ((await sha256(sdk)) !== (await sha256(mirror))) throw new Error(`${path.basename(sdk)} diffère du kit téléchargeable ${path.basename(mirror)}`);
42
+
43
+ if (!dryRun) {
44
+ const user = (() => {
45
+ try {
46
+ return npm(['whoami']);
47
+ } catch {
48
+ throw new Error('Aucun compte npm connecté sur cette machine : lancez npm login');
49
+ }
50
+ })();
51
+ console.log(`• Compte npm : ${user}`);
52
+ }
53
+
54
+ for (const [pkg, file] of [[name, sdk], ['create-snapify', create]] as const) {
55
+ const published = (() => {
56
+ try {
57
+ return execFileSync('npm', ['view', `${pkg}@${version}`, 'version'], { cwd: ROOT, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
58
+ } catch {
59
+ return '';
60
+ }
61
+ })();
62
+ if (published === version) {
63
+ console.log(`• ${pkg}@${version} déjà publié : ignoré`);
64
+ continue;
65
+ }
66
+ console.log(`• ${dryRun ? 'Contrôle' : 'Publication'} de ${pkg}@${version}`);
67
+ execFileSync('npm', ['publish', file, '--access', 'public', ...(dryRun ? ['--dry-run'] : []), ...(otp ? ['--otp', otp] : [])], { cwd: ROOT, stdio: 'inherit' });
68
+ }
69
+ console.log(dryRun ? '✔ Contrôle terminé : rien n’a été publié' : `✔ Publié : https://www.npmjs.com/package/${name}`);
70
+ }
71
+
72
+ main().catch((e) => {
73
+ console.error(`✖ ${e instanceof Error ? e.message : String(e)}`);
74
+ process.exit(1);
75
+ });