Compare commits

...

243 Commits

Author SHA1 Message Date
Boaz Poolman 047af45cc1
Merge pull request #189 from pluginpal/dependabot/npm_and_yarn/form-data-4.0.4
chore(deps): bump form-data from 4.0.1 to 4.0.4
2025-07-22 14:25:59 +02:00
dependabot[bot] 355b3d2c9c
chore(deps): bump form-data from 4.0.1 to 4.0.4
Bumps [form-data](https://github.com/form-data/form-data) from 4.0.1 to 4.0.4.
- [Release notes](https://github.com/form-data/form-data/releases)
- [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md)
- [Commits](https://github.com/form-data/form-data/compare/v4.0.1...v4.0.4)

---
updated-dependencies:
- dependency-name: form-data
  dependency-version: 4.0.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-22 12:23:55 +00:00
boazpoolman 7140fac0af chore: Bump version to 3.1.2 2025-06-30 07:26:35 +00:00
Boaz Poolman 6a060c9b11
Merge pull request #185 from pluginpal/dependabot/npm_and_yarn/pbkdf2-3.1.3
chore(deps): bump pbkdf2 from 3.1.2 to 3.1.3
2025-06-30 07:49:50 +02:00
Boaz Poolman 10c3a80a7d Merge branch 'master' of github.com:pluginpal/strapi-plugin-config-sync 2025-06-30 07:36:41 +02:00
Boaz Poolman fc82cc962f fix: add /config/sync folder to the watchIgnoreFiles 2025-06-30 07:36:31 +02:00
boazpoolman daec71f7eb chore: Bump version to 3.1.2-beta.2 2025-06-30 05:29:33 +00:00
Boaz Poolman bb944efb16 chore: update lockfile 2025-06-30 07:27:17 +02:00
Boaz Poolman b55dc1864d fix: add @strapi/typescript-utils to peerDependencies to prevent it from being bundled 2025-06-30 07:24:29 +02:00
dependabot[bot] db6659ce4b
chore(deps): bump pbkdf2 from 3.1.2 to 3.1.3
Bumps [pbkdf2](https://github.com/crypto-browserify/pbkdf2) from 3.1.2 to 3.1.3.
- [Changelog](https://github.com/browserify/pbkdf2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crypto-browserify/pbkdf2/compare/v3.1.2...v3.1.3)

---
updated-dependencies:
- dependency-name: pbkdf2
  dependency-version: 3.1.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-30 05:17:05 +00:00
boazpoolman 432b0c1aec chore: Bump version to 3.1.2-beta.1 2025-06-30 05:15:01 +00:00
Boaz Poolman 75e9f4cb04 Merge branch 'master' of github.com:pluginpal/strapi-plugin-config-sync 2025-06-30 07:07:57 +02:00
Boaz Poolman 5fc0aea585 feat: ability to create automated alpha & beta releases 2025-06-30 07:07:42 +02:00
Boaz Poolman 748a31a0b6
Merge pull request #187 from gboutte/master
Avoid CLI re building the project if it's already built
2025-06-29 20:27:53 +02:00
gboutte 0b71235389 test: restore dist folder after the tests are done 2025-06-29 19:03:27 +02:00
Boaz Poolman 8b0f41bcd1 chore: update playground 2025-06-29 11:35:04 +02:00
Boaz Poolman a31a06cd72 chore: playground typescript cleanup 2025-06-29 11:20:27 +02:00
gboutte b45ec9c8d1 test: cli compile strapi only when not already built 2025-06-28 17:37:38 +02:00
gboutte c0253b5498 feat: migrate playground to typescript 2025-06-28 17:21:16 +02:00
gboutte 1e78cca1d3 fix: app context dist directory 2025-06-26 21:45:17 +02:00
gboutte 251d8df336 feat: avoid compilation is project already compiled 2025-06-26 13:49:39 +02:00
Boaz Poolman 8a7d4baca8 chore: update github action workflows 2025-05-29 17:22:35 +02:00
Boaz Poolman 8becc53ddf
Merge pull request #184 from mccrackend/patch-1
Documentation typo, fixing broken link
2025-05-29 17:19:56 +02:00
Dan McCracken a2a136a336
fixing broken image ref
See docs state <img..> tags in .md https://docusaurus.io/docs/static-assets#in-markdown
2025-05-27 13:05:30 -04:00
Dan McCracken 3835908542
fixing typo 2025-05-27 12:21:57 -04:00
Boaz Poolman c6e865bec1
Merge pull request #183 from pluginpal/dependabot/npm_and_yarn/docs/prismjs-1.30.0
chore(deps): bump prismjs from 1.29.0 to 1.30.0 in /docs
2025-04-29 10:38:56 +02:00
dependabot[bot] 5fa1fc7a1f
chore(deps): bump prismjs from 1.29.0 to 1.30.0 in /docs
Bumps [prismjs](https://github.com/PrismJS/prism) from 1.29.0 to 1.30.0.
- [Release notes](https://github.com/PrismJS/prism/releases)
- [Changelog](https://github.com/PrismJS/prism/blob/v2/CHANGELOG.md)
- [Commits](https://github.com/PrismJS/prism/compare/v1.29.0...v1.30.0)

---
updated-dependencies:
- dependency-name: prismjs
  dependency-version: 1.30.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-29 08:24:24 +00:00
Boaz Poolman 07ee4ebd90
Merge pull request #182 from pluginpal/dependabot/npm_and_yarn/docs/http-proxy-middleware-2.0.9
chore(deps): bump http-proxy-middleware from 2.0.7 to 2.0.9 in /docs
2025-04-29 10:22:44 +02:00
dependabot[bot] 60b7cb9614
chore(deps): bump http-proxy-middleware from 2.0.7 to 2.0.9 in /docs
Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.7 to 2.0.9.
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.9/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.7...v2.0.9)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-version: 2.0.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-29 02:13:11 +00:00
Boaz Poolman 1ae52f0ebd
Merge pull request #181 from pluginpal/dependabot/npm_and_yarn/docs/estree-util-value-to-estree-3.3.3
chore(deps): bump estree-util-value-to-estree from 3.1.2 to 3.3.3 in /docs
2025-04-09 17:54:49 +02:00
dependabot[bot] 18856cc719
chore(deps): bump estree-util-value-to-estree in /docs
Bumps [estree-util-value-to-estree](https://github.com/remcohaszing/estree-util-value-to-estree) from 3.1.2 to 3.3.3.
- [Release notes](https://github.com/remcohaszing/estree-util-value-to-estree/releases)
- [Commits](https://github.com/remcohaszing/estree-util-value-to-estree/compare/v3.1.2...v3.3.3)

---
updated-dependencies:
- dependency-name: estree-util-value-to-estree
  dependency-version: 3.3.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-08 02:05:46 +00:00
Boaz Poolman 7317a3e14d
Merge pull request #176 from pluginpal/dependabot/npm_and_yarn/docs/babel/runtime-7.27.0
chore(deps): bump @babel/runtime from 7.25.6 to 7.27.0 in /docs
2025-04-07 10:11:10 +02:00
Boaz Poolman fefe1bd93a
Merge pull request #177 from pluginpal/dependabot/npm_and_yarn/docs/cross-spawn-7.0.6
chore(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /docs
2025-04-07 10:11:01 +02:00
Boaz Poolman 954c3fb1d1
Merge pull request #178 from pluginpal/dependabot/npm_and_yarn/docs/http-proxy-middleware-2.0.7
chore(deps): bump http-proxy-middleware from 2.0.6 to 2.0.7 in /docs
2025-04-07 10:10:52 +02:00
Boaz Poolman 69b1633af8
Merge pull request #179 from pluginpal/dependabot/npm_and_yarn/docs/mermaid-10.9.3
chore(deps): bump mermaid from 10.9.1 to 10.9.3 in /docs
2025-04-07 10:10:44 +02:00
Boaz Poolman 790228f9e4
Merge pull request #180 from pluginpal/dependabot/npm_and_yarn/docs/image-size-1.2.1
chore(deps): bump image-size from 1.1.1 to 1.2.1 in /docs
2025-04-07 10:10:31 +02:00
dependabot[bot] d330a80e60
chore(deps): bump image-size from 1.1.1 to 1.2.1 in /docs
Bumps [image-size](https://github.com/image-size/image-size) from 1.1.1 to 1.2.1.
- [Release notes](https://github.com/image-size/image-size/releases)
- [Commits](https://github.com/image-size/image-size/compare/v1.1.1...v1.2.1)

---
updated-dependencies:
- dependency-name: image-size
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-02 15:10:22 +00:00
dependabot[bot] 68fd241829
chore(deps): bump mermaid from 10.9.1 to 10.9.3 in /docs
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 10.9.1 to 10.9.3.
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Changelog](https://github.com/mermaid-js/mermaid/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/mermaid-js/mermaid/compare/v10.9.1...v10.9.3)

---
updated-dependencies:
- dependency-name: mermaid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-25 19:22:18 +00:00
dependabot[bot] 2e54e4461f
chore(deps): bump http-proxy-middleware from 2.0.6 to 2.0.7 in /docs
Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.6 to 2.0.7.
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.7/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.6...v2.0.7)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-25 19:21:50 +00:00
dependabot[bot] c70db6153b
chore(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /docs
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-25 19:21:39 +00:00
dependabot[bot] a8808f2e1c
chore(deps): bump @babel/runtime from 7.25.6 to 7.27.0 in /docs
Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) from 7.25.6 to 7.27.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-runtime)

---
updated-dependencies:
- dependency-name: "@babel/runtime"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-25 19:21:32 +00:00
Boaz Poolman d3382d172a
Merge pull request #169 from pluginpal/dependabot/npm_and_yarn/prismjs-1.30.0 2025-03-25 20:21:04 +01:00
Boaz Poolman 67b6b0e511
Merge pull request #170 from pluginpal/dependabot/npm_and_yarn/docs/nanoid-3.3.10 2025-03-25 20:20:54 +01:00
Boaz Poolman a6df4bf396
Merge pull request #171 from pluginpal/dependabot/npm_and_yarn/docs/babel/runtime-corejs3-7.26.10 2025-03-25 20:20:45 +01:00
Boaz Poolman 7bf7e41d7f
Merge pull request #172 from pluginpal/dependabot/npm_and_yarn/babel/runtime-7.26.10 2025-03-25 20:20:28 +01:00
Boaz Poolman a41f4ddc5e
Merge pull request #173 from pluginpal/dependabot/npm_and_yarn/docs/dompurify-3.2.4 2025-03-25 20:20:08 +01:00
Boaz Poolman ba5552830e
Merge pull request #174 from pluginpal/dependabot/npm_and_yarn/docs/babel/helpers-7.26.10 2025-03-25 20:19:58 +01:00
Boaz Poolman 3fe2f7aff0
Merge pull request #175 from pluginpal/dependabot/npm_and_yarn/docs/katex-0.16.21 2025-03-25 20:19:43 +01:00
Boaz Poolman a96ed745cc chore: deploy docs only on release 2025-03-17 21:06:50 +01:00
Boaz Poolman 53436c2c03 feat: add algolia search to docs 2025-03-17 21:06:21 +01:00
Boaz Poolman 44471af96e feat: configs-ync branding for docs 2025-03-17 14:33:01 +01:00
Boaz Poolman 7ee80fd923 fix: update docs logo 2025-03-17 11:45:43 +01:00
Boaz Poolman c51568fdde fix: correctly set the baseUrl for config-sync docs 2025-03-17 11:31:21 +01:00
Boaz Poolman 307d5c179b fix: set the baseUrl for docusaurus 2025-03-17 10:55:12 +01:00
Boaz Poolman eb0024c8cc fix: docs build 2025-03-17 09:31:24 +01:00
dependabot[bot] 516c1068a3
chore(deps): bump katex from 0.16.11 to 0.16.21 in /docs
Bumps [katex](https://github.com/KaTeX/KaTeX) from 0.16.11 to 0.16.21.
- [Release notes](https://github.com/KaTeX/KaTeX/releases)
- [Changelog](https://github.com/KaTeX/KaTeX/blob/main/CHANGELOG.md)
- [Commits](https://github.com/KaTeX/KaTeX/compare/v0.16.11...v0.16.21)

---
updated-dependencies:
- dependency-name: katex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 08:24:40 +00:00
dependabot[bot] 2039105c88
chore(deps): bump @babel/helpers from 7.25.6 to 7.26.10 in /docs
Bumps [@babel/helpers](https://github.com/babel/babel/tree/HEAD/packages/babel-helpers) from 7.25.6 to 7.26.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-helpers)

---
updated-dependencies:
- dependency-name: "@babel/helpers"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 08:24:22 +00:00
dependabot[bot] cf35548edf
chore(deps): bump dompurify from 3.1.6 to 3.2.4 in /docs
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.1.6 to 3.2.4.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.1.6...3.2.4)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 08:24:21 +00:00
dependabot[bot] fb4a28c068
chore(deps): bump @babel/runtime from 7.26.0 to 7.26.10
Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) from 7.26.0 to 7.26.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-runtime)

---
updated-dependencies:
- dependency-name: "@babel/runtime"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 08:24:08 +00:00
Boaz Poolman c17ea4234c fix: docs deploy 2025-03-17 09:23:58 +01:00
Boaz Poolman b54537cd34 style: don't lint the docs source code 2025-03-17 09:23:49 +01:00
dependabot[bot] 3da9ad26c5
chore(deps): bump @babel/runtime-corejs3 from 7.25.6 to 7.26.10 in /docs
Bumps [@babel/runtime-corejs3](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime-corejs3) from 7.25.6 to 7.26.10.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-runtime-corejs3)

---
updated-dependencies:
- dependency-name: "@babel/runtime-corejs3"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 08:23:44 +00:00
dependabot[bot] 0168a343d3
chore(deps): bump nanoid from 3.3.7 to 3.3.10 in /docs
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.10.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.10)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 08:23:38 +00:00
Boaz Poolman c2dafa17c4 fix: use explicit node version for docusaurus build 2025-03-17 09:19:51 +01:00
Boaz Poolman 8c839138f6 chore: add auto deploy for config-sync docs 2025-03-17 09:08:39 +01:00
Boaz Poolman 6c65d64b81 chore: move docs into the config-sync repo 2025-03-17 09:08:26 +01:00
dependabot[bot] bd2805b23b
chore(deps): bump prismjs from 1.29.0 to 1.30.0
Bumps [prismjs](https://github.com/PrismJS/prism) from 1.29.0 to 1.30.0.
- [Release notes](https://github.com/PrismJS/prism/releases)
- [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PrismJS/prism/compare/v1.29.0...v1.30.0)

---
updated-dependencies:
- dependency-name: prismjs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 22:38:25 +00:00
Boaz Poolman 10ef97a4d4 fix: ci 2025-01-25 11:26:59 +00:00
Boaz Poolman cbe5ed9386 fix: ci 2025-01-25 11:05:41 +00:00
Boaz Poolman f738ca5cf5 Merge branch 'master' of github.com:pluginpal/strapi-plugin-config-sync 2025-01-03 10:54:17 +00:00
Boaz Poolman 5947a1ba42 chore: have dependabot do scheduled updates of Strapi 2025-01-03 10:53:55 +00:00
Boaz Poolman 05941739bf
Merge pull request #166 from pluginpal/feature/e2e-tests
Feature/e2e tests
2024-12-30 22:05:23 +00:00
Boaz Poolman b25fe0cefb feat: add a e2e test for partial import/export from the gui 2024-12-30 21:47:02 +00:00
Boaz Poolman 54bec393bf fix: recursive loggin in upon failed login attempt 2024-12-30 20:54:54 +00:00
Boaz Poolman 7a0d228f77 fix: only reload after login, not after register 2024-12-30 20:26:14 +00:00
Boaz Poolman 4e81ebe622 fix: cypress flakyness by reloading after logging in 2024-12-30 20:18:44 +00:00
Boaz Poolman 83cf8ed165 fix: up the cypress timeout time 2024-12-30 19:59:39 +00:00
Boaz Poolman f0d9ab32bb fix: upload video artifacts on runner failure 2024-12-30 18:32:06 +00:00
Boaz Poolman c46fcc9db8 chore: upload cypress video's as artifacts in debug mode of github actions 2024-12-30 17:55:08 +00:00
Boaz Poolman e22906afa4 fix: up the timeout time of cypress 2024-12-30 15:10:14 +00:00
Boaz Poolman b7d31ddb0c fix: diff viewer 2024-12-30 15:09:52 +00:00
Boaz Poolman f731691cd1 chore: try to fail the diff viewer test 2024-12-30 13:05:04 +00:00
Boaz Poolman 11cde0b638 fix: disable unfinished test 2024-12-30 13:04:25 +00:00
Boaz Poolman fc5df403af fix: add missing playground:start command 2024-12-30 12:37:38 +00:00
Boaz Poolman 4e4613d8ab fix: updated eslint rules for cypress files 2024-12-30 10:43:38 +00:00
Boaz Poolman 24bbf0788b fix: add the /dist folder to eslintignore 2024-12-30 10:43:18 +00:00
Boaz Poolman 35463b9631 chore: run the e2e tests in the pipeline 2024-12-29 22:41:44 +00:00
Boaz Poolman f194c394d2 feat: add cypress and e2e tests 2024-12-29 22:41:05 +00:00
Boaz Poolman 592e934fe8 chore: add config/sync to the watch ignore files 2024-12-29 22:38:38 +00:00
Boaz Poolman c30f034cf2 chore: disable rate limiting in the playground 2024-12-29 22:38:20 +00:00
Boaz Poolman 4193e13c7c chore: update the generated content types in the playground 2024-12-29 22:36:48 +00:00
boazpoolman 14ed79b7b9 chore: Bump version to 3.1.1 2024-12-24 14:00:06 +00:00
Boaz Poolman acbfdbb89d
Merge pull request #165 from pluginpal/feature/fix-diff-viewer
fix: workaround for ssr issue of react diff-viewer lib
2024-12-24 14:57:11 +01:00
Boaz Poolman 2261b9915a fix: workaround for ssr issue of react diff-viewer lib 2024-12-24 13:51:06 +00:00
boazpoolman dbed0cb27d chore: Bump version to 3.1.0 2024-12-20 10:00:34 +00:00
Boaz Poolman 8990c9971a
Merge pull request #163 from pluginpal/feature/strapi-range
refactor: move @strapi packages from dependencies to peerDependencies
2024-12-19 22:17:20 +01:00
Boaz Poolman 750f2c3fe0 fix: add rc versions of @strapi/icons and @strapi/design-system 2024-12-19 21:07:10 +00:00
Boaz Poolman e0211d2164 refactor: move @strapi packages from dependencies to peerDependencies 2024-12-19 21:00:25 +00:00
Boaz Poolman 78acb059c2
Merge pull request #162 from pluginpal/dependabot/npm_and_yarn/nanoid-3.3.8 2024-12-18 14:13:36 +01:00
dependabot[bot] fc32b5b54c
chore(deps): bump nanoid from 3.3.7 to 3.3.8
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 17:31:33 +00:00
Boaz Poolman f01398e80a
Merge pull request #159 from pluginpal/dependabot/npm_and_yarn/cross-spawn-7.0.6
chore(deps): bump cross-spawn from 7.0.3 to 7.0.6
2024-11-21 09:15:01 +01:00
dependabot[bot] 9b83d227db
chore(deps): bump cross-spawn from 7.0.3 to 7.0.6
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-21 00:11:49 +00:00
boazpoolman 89ff246e64 chore: Bump version to 3.0.4 2024-11-11 19:05:38 +00:00
Boaz Poolman d9198099a7 chore: fix deprecated strapi config annotation 2024-11-11 19:58:29 +01:00
Boaz Poolman 5e0ccf8e3d
Merge pull request #157 from pluginpal/feature/node-zip
feat: use adm-zip instead of 'zip' command
2024-11-11 19:56:51 +01:00
Boaz Poolman c1ad39d7ad feat: use adm-zip instead of 'zip' command 2024-11-11 19:31:31 +01:00
boazpoolman 39c4a66b75 chore: Bump version to 3.0.3 2024-11-08 19:48:22 +00:00
Boaz Poolman 79ca64ab34 Merge branch 'master' of github.com:pluginpal/strapi-plugin-config-sync 2024-11-08 20:45:59 +01:00
Boaz Poolman 0d630b21ec fix: update peer dependencies 2024-11-08 20:45:44 +01:00
boazpoolman 14a0ae877b chore: Bump version to 3.0.2 2024-11-06 19:01:31 +00:00
Boaz Poolman 02d5c20616 Merge branch 'master' of github.com:pluginpal/strapi-plugin-config-sync 2024-11-06 19:58:32 +01:00
Boaz Poolman d9fd9585ad fix: console warnings 2024-11-06 19:58:20 +01:00
boazpoolman 7ccf4cd274 chore: Bump version to 3.0.1 2024-11-06 14:00:06 +00:00
Boaz Poolman 16529e049c fix: actually build the plugin during the publish workflow 2024-11-06 14:58:06 +01:00
boazpoolman 1072afb31a chore: Bump version to 3.0.0 2024-11-06 13:52:10 +00:00
Boaz Poolman 775733eff8 fix: add @strapi/strapi as a devDependency 2024-11-06 14:30:59 +01:00
Boaz Poolman 0f6ac10c91 fix: remove @strapi/strapi as a dependency 2024-11-06 14:26:08 +01:00
Boaz Poolman a34246352e fix: add react-query as a dependency 2024-11-06 13:24:53 +01:00
Boaz Poolman c989e3f371 fix: shrink bundle size 2024-11-06 12:38:28 +01:00
Boaz Poolman 5de1accc77 refactor: updated dependency management 2024-11-06 12:29:16 +01:00
Boaz Poolman abfaaa8e5a chore: dependency updates 2024-11-06 12:12:36 +01:00
Boaz Poolman 20794eaa21 style: fix eslint issue 2024-11-06 12:01:37 +01:00
Boaz Poolman cc0e71988a refactor: use pack-up for bundling 2024-11-06 11:59:15 +01:00
boazpoolman 203fb4e7d1 chore: Bump version to 2.1.0 2024-10-18 20:09:31 +00:00
Boaz Poolman c02262184f
Merge pull request #150 from michaelrazmgah/feature/fix-issue-where-locale-is-not-found
fix: Fixing bug where Strapi UI is crashing when no locale is found
2024-10-18 22:00:59 +02:00
Boaz Poolman 829cbb0ae2 fix: eslint issue 2024-10-18 21:56:10 +02:00
Michael Razmgah 2a72cb1bb5
fix: Adding require into try catch to simplify by catching error inside single try-catch 2024-10-18 20:31:07 +02:00
Boaz Poolman 8ba4dbb728
Merge pull request #94 from The-Real-Established/master
feat: added option to download zip
2024-10-16 22:48:38 +02:00
Alexander Engel a794d30892
Merge pull request #1 from TMSchipper/fix/response-data
feat: add response.data and fix styling for layout with download zip …
2024-10-16 09:34:16 -07:00
Tim Schipper 9911e78b50 feat: add response.data and fix styling for layout with download zip button 2024-10-16 09:55:19 +02:00
Alexander Engel 3a71083a05 chore: fixed linting, moved helper function 2024-10-14 12:27:22 -07:00
Alexander Engel 6e6b6cfba7 fix: moved button to new file 2024-10-14 12:07:17 -07:00
Alexander Engel d735151d9b Merge branch 'master' of github.com:boazpoolman/strapi-plugin-config-sync 2024-10-14 12:07:03 -07:00
Alexander Engel 38563dbe6d fix: do not store config file 2024-10-14 11:58:53 -07:00
Michael Razmgah e74594a970
fix: Fixing bug where Strapi UI is crashing when no locale is found 2024-10-14 09:49:14 +02:00
Boaz Poolman 79a4a0b41c
Merge pull request #148 from pluginpal/feature/document-service
refactor: replace the entity service with the document service
2024-10-12 23:07:42 +02:00
Boaz Poolman fff5a765fe refactor: replace the entity service with the document service 2024-10-12 22:46:32 +02:00
Boaz Poolman 4f0ceccb85
Merge pull request #147 from pluginpal/feature/beta-fixes
Feature/beta fixes
2024-10-12 20:13:45 +02:00
Boaz Poolman d41f292e2d fix: push to the yalc registry in CI before starting the integration tests 2024-10-12 20:09:29 +02:00
Boaz Poolman 2b6bf4de94 fix: use yalc 'file' instead of yalc 'link' in CI for a more real-life package installation scenario 2024-10-12 20:01:43 +02:00
Boaz Poolman 75f53344d2 style: fix eslint issues 2024-10-12 19:52:13 +02:00
Boaz Poolman b14ee74c36 fix: config checkbox selection in the admin panel 2024-10-12 19:44:39 +02:00
Boaz Poolman 00da5012d8 fix: modal, dialog & typography styling 2024-10-12 18:46:42 +02:00
Boaz Poolman 11662c8421 chore: don't install just --production depdencies in ci, this case is handled by using the yalc registry 2024-10-12 17:18:40 +02:00
Boaz Poolman a6444d411d fix: add yalc as dependency to the playground also 2024-10-12 17:14:26 +02:00
Boaz Poolman e3e2d3e0af chore: update auto generated files 2024-10-12 16:47:00 +02:00
Boaz Poolman e69e39e436 docs: update contributing docs 2024-10-12 16:46:48 +02:00
Boaz Poolman e1aefbf5b6 chore: use yalc for playground development 2024-10-12 16:46:36 +02:00
Boaz Poolman 750c1a2895 chore: update strapi dependencies to 5.0.4 2024-10-12 16:22:33 +02:00
Boaz Poolman da2f064383
Merge pull request #146 from pluginpal/feature/update-readme
docs: update the README to refer to the docs website
2024-10-05 14:01:46 +02:00
Boaz Poolman 5c6ac137f2 docs: update the README to refer to the docs website 2024-10-05 14:01:01 +02:00
Boaz Poolman 3a635e2d44
Merge pull request #145 from pluginpal/dependabot/npm_and_yarn/rollup-4.22.4
chore(deps): bump rollup from 4.18.0 to 4.22.4
2024-09-24 07:39:46 +02:00
dependabot[bot] 767b409886
chore(deps): bump rollup from 4.18.0 to 4.22.4
Bumps [rollup](https://github.com/rollup/rollup) from 4.18.0 to 4.22.4.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.18.0...v4.22.4)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 23:59:01 +00:00
Boaz Poolman c29660bf5d
Merge pull request #143 from pluginpal/dependabot/npm_and_yarn/path-to-regexp-6.3.0
chore(deps): bump path-to-regexp from 6.2.1 to 6.3.0
2024-09-18 19:08:03 +02:00
Boaz Poolman 5d8ce7a028
Merge pull request #142 from pluginpal/dependabot/npm_and_yarn/webpack-5.94.0
chore(deps): bump webpack from 5.91.0 to 5.94.0
2024-09-18 19:07:54 +02:00
dependabot[bot] af9427823d
chore(deps): bump path-to-regexp from 6.2.1 to 6.3.0
Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) from 6.2.1 to 6.3.0.
- [Release notes](https://github.com/pillarjs/path-to-regexp/releases)
- [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md)
- [Commits](https://github.com/pillarjs/path-to-regexp/compare/v6.2.1...v6.3.0)

---
updated-dependencies:
- dependency-name: path-to-regexp
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-18 16:25:58 +00:00
dependabot[bot] 46c9f83508
chore(deps): bump webpack from 5.91.0 to 5.94.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.91.0 to 5.94.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.91.0...v5.94.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-18 16:25:58 +00:00
Boaz Poolman 8cca77377a
Merge pull request #138 from pluginpal/beta
Beta
2024-09-18 18:23:53 +02:00
Boaz Poolman 0a239323f1 fix: conflicts 2024-09-18 17:59:49 +02:00
Boaz Poolman e445967d82
Merge pull request #137 from pluginpal/dependabot/npm_and_yarn/elliptic-6.5.7
chore(deps): bump elliptic from 6.5.4 to 6.5.7
2024-09-01 10:47:15 +02:00
dependabot[bot] 1dea4bda2f
chore(deps): bump elliptic from 6.5.4 to 6.5.7
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.4 to 6.5.7.
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.4...v6.5.7)

---
updated-dependencies:
- dependency-name: elliptic
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-01 08:41:15 +00:00
Boaz Poolman e987b8d710
Merge pull request #136 from pluginpal/dependabot/npm_and_yarn/micromatch-4.0.8
chore(deps): bump micromatch from 4.0.5 to 4.0.8
2024-09-01 10:40:16 +02:00
dependabot[bot] 3c2d27310d
chore(deps): bump micromatch from 4.0.5 to 4.0.8
Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.5 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.5...4.0.8)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-01 01:25:07 +00:00
Boaz Poolman 7374c2758e
Merge pull request #135 from pluginpal/feature/fix-tests-ci
fix: run tests twice in CI, allowing the first try to fail
2024-08-28 22:51:26 +02:00
Boaz Poolman 645580b5ca fix: integration tests 2024-08-28 22:42:05 +02:00
Boaz Poolman 3b91e4b035 v2.0.0-beta.4 2024-08-13 20:30:25 +02:00
Boaz Poolman 8ef3217f5f fix: recursive config sanitization 2024-08-13 20:29:46 +02:00
Boaz Poolman c913705752
Merge pull request #132 from pluginpal/feature/fix-component-syncing
chore: setup components in the playground
2024-08-12 11:14:17 +02:00
Boaz Poolman 75634a30d2 fix: recursively santize all config except for content_manager_configuration items 2024-08-08 12:35:57 +02:00
Boaz Poolman e4e3825def chore: setup components in the playground 2024-08-05 18:37:56 +02:00
Boaz Poolman e9628ec2ba
Merge pull request #128 from pluginpal/dependabot/npm_and_yarn/braces-3.0.3
chore(deps): bump braces from 3.0.2 to 3.0.3
2024-07-18 22:12:36 +02:00
dependabot[bot] ceb504bb5f
chore(deps): bump braces from 3.0.2 to 3.0.3
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-18 19:57:10 +00:00
Boaz Poolman 5e1e2a6386 v2.0.0-beta.3 2024-07-11 10:10:20 +02:00
Boaz Poolman dc3ab4b1b6
Merge pull request #130 from pluginpal/feature/update-to-rc
Update strapi to 5.0.0-rc.2
2024-07-10 10:33:46 +02:00
Boaz Poolman e8db8c6107 fix: rename design-system components for migtration to v2 2024-07-10 10:23:57 +02:00
Boaz Poolman 1ccd3d28f2 chore: update the .strapi/client/app.js to include config-sync 2024-07-10 10:23:10 +02:00
Boaz Poolman 9387c810ba chore: update design-system to 2.0.0-rc.7 2024-07-10 10:03:23 +02:00
Boaz Poolman dfb6852eb3 chore: update strapi to 5.0.0-rc.2 2024-07-10 09:57:14 +02:00
Boaz Poolman b2299346f8 v2.0.0-beta.2 2024-05-31 15:39:30 +02:00
Boaz Poolman 00bc2dde8a
Merge pull request #127 from pluginpal/feature/beta-testing
Beta testing
2024-05-31 13:49:42 +02:00
Boaz Poolman fb897c79b8 chore: update styled-components in the playground 2024-05-31 08:34:23 +02:00
Boaz Poolman 7ab1ad86e9 refactor: migrate design-system components for Strapi 5 2024-05-31 08:26:15 +02:00
Boaz Poolman e3f551e965 chore: update playground dependencies 2024-05-31 08:19:56 +02:00
Boaz Poolman 0965a94fe6 chore: update dependencies 2024-05-31 08:04:35 +02:00
Boaz Poolman 980593dc54
Merge pull request #126 from pluginpal/feature/migrate-v5
feat: initial v5 migration
2024-05-08 23:04:40 +02:00
Boaz Poolman 9c729759a2 fix: sanitize publishedAt fields 2024-05-08 23:00:03 +02:00
Boaz Poolman 943b83be90 fix: integration tests 2024-05-08 22:50:04 +02:00
Boaz Poolman 5ad9e8f0f6 fix: add testing libraries to the playground 2024-05-08 21:45:04 +02:00
Boaz Poolman fd63444630 chore: migrate the playground to v5 beta 2024-05-08 21:38:32 +02:00
Boaz Poolman 1c6f990d4d fix: use createStrapi in the tests helpers 2024-05-08 21:21:45 +02:00
Boaz Poolman b76187562d chore: test with beta.5 2024-05-08 21:21:29 +02:00
Boaz Poolman ab68450bea chore: run tests workflow for the beta branch 2024-05-08 21:16:14 +02:00
Boaz Poolman 60e2a1a7ea fix: use createStrapi instead of strapi default export 2024-05-08 21:12:18 +02:00
Boaz Poolman 4ee4e74bd7 style: fix eslint issue 2024-05-08 20:52:31 +02:00
Boaz Poolman 2a59690aae fix: merge conflicts 2024-05-08 20:50:28 +02:00
Boaz Poolman ab82a0dbf6 fix: deprecation warning regarding plugin config 2024-05-08 20:48:33 +02:00
Boaz Poolman 9178f1e902 fix: admin requests 2024-05-08 20:46:09 +02:00
Boaz Poolman 0b4974a338 fix: missing import 2024-05-08 19:51:35 +02:00
Boaz Poolman 6212b949ab fix: eslint no-unresolved 2024-05-08 19:49:40 +02:00
Boaz Poolman 60fca4899f chore: update lock file 2024-05-08 16:04:17 +02:00
Boaz Poolman ae6402803a refactor: update EmptyStateLayout to conform to it's new props 2024-05-08 15:58:30 +02:00
Boaz Poolman dfb5522024 chore: update to strapi beta.6 2024-05-08 15:57:35 +02:00
boazpoolman f8631f9685 chore: Bump version to 1.2.5 2024-04-18 21:12:12 +00:00
Boaz Poolman 15b265a37c Merge branch 'master' of github.com:boazpoolman/strapi-plugin-config-sync 2024-04-18 23:09:25 +02:00
Boaz Poolman 769e2c3be3 fix: check for process.env.CONFIG_SYNC_CLI with 'true' instead of true 2024-04-18 23:09:12 +02:00
boazpoolman e06ed0b968 chore: Bump version to 1.2.4 2024-04-18 20:36:03 +00:00
Boaz Poolman 6821110d7e feat: don't importOnBootstrap when using the config-sync CLI to start Strapi 2024-04-18 21:21:32 +02:00
Boaz Poolman 2063f88507 chore: move react-intl to the dependencies, rather then the devDependencies 2024-04-03 20:59:30 +02:00
Boaz Poolman c2ab9288c1 feat: initial v5 migration 2024-04-03 20:55:53 +02:00
Boaz Poolman 0f527048cc fix: update github URL in package.json 2024-03-27 16:21:39 +01:00
Boaz Poolman 9ad372ff59 chore: update the author & homepage info in the package.json 2024-03-27 16:15:05 +01:00
Boaz Poolman 338ed7ce39
Merge pull request #123 from pluginpal/dependabot/npm_and_yarn/follow-redirects-1.15.6
chore(deps): bump follow-redirects from 1.15.4 to 1.15.6
2024-03-18 19:50:12 +01:00
dependabot[bot] 65cc1caba1
chore(deps): bump follow-redirects from 1.15.4 to 1.15.6
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-16 22:59:50 +00:00
Mathijs Schouten 8f6e9da135
Merge pull request #121 from boazpoolman/feature/update-strapi
chore: update strapi to 4.19.0
2024-02-04 15:28:37 +01:00
Boaz Poolman af666ee2e5 chore: update strapi to 4.19.0 2024-01-27 10:24:45 +01:00
Boaz Poolman f828a9c198
Merge pull request #119 from boazpoolman/dependabot/npm_and_yarn/follow-redirects-1.15.4
chore(deps): bump follow-redirects from 1.15.3 to 1.15.4
2024-01-11 08:48:16 +01:00
dependabot[bot] 7ac23fb2be
chore(deps): bump follow-redirects from 1.15.3 to 1.15.4
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-11 03:09:51 +00:00
Boaz Poolman 8930a537b9
Merge pull request #118 from boazpoolman/feature/update-strapi-dependencies
chore: update @strapi devDependencies
2023-12-02 12:03:44 +01:00
Boaz Poolman 200f4f276c chore: update @strapi devDependencies 2023-12-02 11:59:59 +01:00
Boaz Poolman 3d5680e2c9
Merge pull request #116 from boazpoolman/dependabot/npm_and_yarn/adobe/css-tools-4.3.2
chore(deps): bump @adobe/css-tools from 4.3.1 to 4.3.2
2023-12-02 11:54:37 +01:00
dependabot[bot] a5c6bf15dd
chore(deps): bump @adobe/css-tools from 4.3.1 to 4.3.2
Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.3.1 to 4.3.2.
- [Changelog](https://github.com/adobe/css-tools/blob/main/History.md)
- [Commits](https://github.com/adobe/css-tools/commits)

---
updated-dependencies:
- dependency-name: "@adobe/css-tools"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-02 10:51:39 +00:00
Boaz Poolman 3872a19c58
Merge pull request #117 from boazpoolman/feature/upgrade-playground
chore: add missing dependencies in playground
2023-12-02 11:48:27 +01:00
Boaz Poolman c77d2988b9 chore: add missing dependencies in playground 2023-12-02 11:42:48 +01:00
boazpoolman 1585fbfa45 chore: Bump version to 1.2.3 2023-11-04 13:39:43 +00:00
Boaz Poolman b51c2e91ec
Merge pull request #115 from boazpoolman/dependabot/npm_and_yarn/browserify-sign-4.2.2
chore(deps): bump browserify-sign from 4.2.1 to 4.2.2
2023-10-30 20:32:56 +01:00
dependabot[bot] 3318c4589d
chore(deps): bump browserify-sign from 4.2.1 to 4.2.2
Bumps [browserify-sign](https://github.com/crypto-browserify/browserify-sign) from 4.2.1 to 4.2.2.
- [Changelog](https://github.com/browserify/browserify-sign/blob/main/CHANGELOG.md)
- [Commits](https://github.com/crypto-browserify/browserify-sign/compare/v4.2.1...v4.2.2)

---
updated-dependencies:
- dependency-name: browserify-sign
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-28 04:56:23 +00:00
Boaz Poolman 7e78f04c24
Merge pull request #114 from boazpoolman/feature/update-query-fallback
fix: queryFallback utility
2023-10-23 21:13:58 +02:00
Boaz Poolman e2147ea5b6 fix: queryFallback utility 2023-10-23 21:06:04 +02:00
Boaz Poolman dd6e2f6c81 chore: Add more integration tests for create/update/delete in CLI 2023-10-22 12:36:28 +02:00
Mathijs Schouten a4b767f412
Merge pull request #111 from boazpoolman/feature/fix-import-on-bootstrap
Feature/fix import on bootstrap
2023-10-21 12:17:50 +02:00
Mathijs Schouten 88a40e7dfb
Merge pull request #113 from boazpoolman/feature/fix-config-sanitization-utility
fix: Remove recursive config sanitisation
2023-10-21 12:14:44 +02:00
Boaz Poolman a8e2180713 fix: Remove recursive config sanitisation 2023-10-20 16:59:38 +02:00
Boaz Poolman 19f5ba15f8 chore: More frequent database cleanups in between integration tests 2023-10-19 19:23:50 +02:00
Boaz Poolman 991da15d52 fix: Don't return promises in the queryFallback util 2023-10-19 19:08:30 +02:00
Boaz Poolman 5b9eaee29f fix: Use sed in ci 2023-10-19 09:15:39 +02:00
Boaz Poolman 81f22c6c93 fix: Add fallback for entitySerivce 2023-10-19 08:51:29 +02:00
Boaz Poolman 1acf0eee19 fix: Use sed in pipeline 2023-10-19 08:51:19 +02:00
Boaz Poolman 9333e8518b fix: Try using sed in pipeline 2023-10-19 08:33:57 +02:00
Boaz Poolman 3b7084fa87 chore: Add integration test for the importOnBootstrap setting 2023-10-19 08:19:06 +02:00
Boaz Poolman 7573cfd75c chore: Update test command 2023-10-19 08:18:42 +02:00
Boaz Poolman 27b349a82a chore: Update testing libraries 2023-10-19 07:49:52 +02:00
Boaz Poolman d101e3cc43 fix: Update tests workflow 2023-10-17 21:24:56 +02:00
Alexander Engel 761024cc74 feat: added option to download zip 2023-06-15 16:33:56 -07:00
288 changed files with 27091 additions and 3039 deletions

View File

@ -1,6 +1,8 @@
**/node_modules
**/public
**/build
**/dist
**/config
**/scripts
**/docs
**/playground

View File

@ -25,7 +25,29 @@
"globals": {
"strapi": true
},
"overrides": [
{
"files": [
"**/*.cy.*",
"./cypress/**/*.*"
],
"extends": [
"plugin:cypress/recommended"
],
"parserOptions": {
"project": "./tsconfig.cypress.json"
}
}
],
"rules": {
"import/no-unresolved": [2, {
"ignore": [
"@strapi/strapi/admin",
"@strapi/icons/symbols",
"@strapi/admin/strapi-admin"
]
}],
"template-curly-spacing" : "off",
"indent" : "off",

52
.github/workflows/deploy-docs.yml vendored Normal file
View File

@ -0,0 +1,52 @@
name: Deploy Docs
on:
workflow_dispatch:
release:
types: [published]
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
environment:
name: docs.pluginpal.io
url: https://docs.pluginpal.io/config-sync
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker
uses: actions/setup-node@v4
with:
node-version: '14'
- name: Build a Docker image
run: |
cd docs
docker build \
-t docs-config-sync:latest .
docker save -o ../docs-config-sync-latest.tar docs-config-sync:latest
- name: Transfer the Docker image to the Dokku server
uses: appleboy/scp-action@v0.1.3
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_CI_USERNAME }}
password: ${{ secrets.SSH_CI_PASSWORD }}
source: docs-config-sync-latest.tar
target: /var/lib/dokku/data/storage/docs/docker-images
- name: Deploy the Dokku app based on the Docker image
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_CI_USERNAME }}
password: ${{ secrets.SSH_CI_PASSWORD }}
script_stop: true
script: |
sudo docker load -i /var/lib/dokku/data/storage/docs/docker-images/docs-config-sync-latest.tar
DOCS_CONFIG_SYNC_LATEST_IMAGE=$(sudo docker images --format "{{.ID}}" docs-config-sync:latest)
sudo docker tag docs-config-sync:latest docs-config-sync:$DOCS_CONFIG_SYNC_LATEST_IMAGE
dokku git:from-image docs-config-sync docs-config-sync:$DOCS_CONFIG_SYNC_LATEST_IMAGE
sudo docker system prune --all --force

View File

@ -7,9 +7,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
always-auth: true
node-version: 18
@ -17,13 +17,34 @@ jobs:
registry-url: 'https://registry.npmjs.org/'
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build the plugin
run: yarn build
- name: Get the release tag version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
- name: Extract pre-release tag if any
id: extract_tag
run: |
VERSION="${{ steps.get_version.outputs.VERSION }}"
if [[ $VERSION == *-* ]]; then
# Extract everything between hyphen and last period (or end of string)
PRETAG=$(echo $VERSION | sed -E 's/.*-([^.]+).*/\1/')
echo "IS_PRERELEASE=true" >> $GITHUB_OUTPUT
echo "NPM_TAG=$PRETAG" >> $GITHUB_OUTPUT
else
echo "IS_PRERELEASE=false" >> $GITHUB_OUTPUT
echo "NPM_TAG=latest" >> $GITHUB_OUTPUT
fi
- name: Get source branch
id: get_branch
run: |
RELEASE_COMMIT=$(git rev-list -n 1 ${{ steps.get_version.outputs.VERSION }})
SOURCE_BRANCH=$(git branch -r --contains $RELEASE_COMMIT | grep -v HEAD | head -n 1 | sed 's/.*origin\///')
echo "SOURCE_BRANCH=$SOURCE_BRANCH" >> $GITHUB_OUTPUT
- name: Set package version
run: yarn version --new-version "${{ steps.get_version.outputs.VERSION }}" --no-git-tag-version
- name: Publish package
run: yarn publish --access public
run: yarn publish --access public --tag ${{ steps.extract_tag.outputs.NPM_TAG }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Push version bump

View File

@ -8,6 +8,7 @@ on:
branches:
- master
- develop
- beta
jobs:
lint:
@ -17,34 +18,58 @@ jobs:
matrix:
node: [18, 20]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'yarn'
- name: Install dependencies
run: yarn
run: yarn --frozen-lockfile
- name: Run eslint
run: yarn run eslint
integration:
name: 'integration'
test:
name: 'test'
needs: [lint]
runs-on: ubuntu-latest
strategy:
matrix:
node: [18, 20]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'yarn'
- name: Install dependencies plugin
run: yarn --no-lockfile --unsafe-perm --production
run: yarn --no-lockfile --unsafe-perm
- name: Push the package to yalc
run: yarn build
- name: Add yalc package to the playground
run: yarn playground:yalc-add
- name: Install dependencies playground
run: yarn playground:install --unsafe-perm
run: cd playground && yarn install --unsafe-perm
- name: Build playground
run: yarn playground:build
- name: Run test
# - name: Run unit tests
# run: yarn test:unit
- name: Run integration tests
run: yarn run -s test:integration
- name: Run end-to-end tests
uses: cypress-io/github-action@v6
with:
start: yarn playground:start
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots
if-no-files-found: ignore # 'warn' or 'error' are also available, defaults to `warn`
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-videos
path: cypress/videos
if-no-files-found: ignore # 'warn' or 'error' are also available, defaults to `warn`
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
with:
@ -52,20 +77,3 @@ jobs:
flags: unit
verbose: true
fail_ci_if_error: true
# unit:
# name: 'unit'
# needs: [lint]
# runs-on: ubuntu-latest
# strategy:
# matrix:
# node: [16, 18, 20]
# steps:
# - uses: actions/checkout@v2
# - uses: actions/setup-node@v2
# with:
# node-version: ${{ matrix.node }}
# cache: 'yarn'
# - name: Install dependencies
# run: yarn --ignore-scripts --frozen-lockfile
# - name: Run test
# run: yarn run -s test:unit

10
.gitignore vendored
View File

@ -9,3 +9,13 @@ files
.DS_Store
npm-debug.log
.idea
# Production build
build
dist
bundle
# Cypress
cypress/screenshots/
cypress/videos/
cypress/downloads/

View File

@ -4,60 +4,55 @@ We want this community to be friendly and respectful to each other. Please follo
## Development Workflow
To get started with the project, make sure you have a local instance of Strapi running.
See the [Strapi docs](https://github.com/strapi/strapi#getting-started) on how to setup a Strapi project.
This plugin provides a local development instance of Strapi to develop it's features. We call this instance `playground` and it can be found in the playground folder in the root of the project. For that reason it is not needed to have your own Strapi instance running to work on this plugin. Just clone the repo and you're ready to go!
#### 1. Fork the [repository](https://github.com/boazpoolman/strapi-plugin-config-sync)
#### 1. Fork the [repository](https://github.com/pluginpal/strapi-plugin-config-sync)
[Go to the repository](https://github.com/boazpoolman/strapi-plugin-config-sync) and fork it to your own GitHub account.
[Go to the repository](https://github.com/pluginpal/strapi-plugin-config-sync) and fork it to your own GitHub account.
#### 2. Clone from your repository into the plugins folder
#### 2. Clone the forked repository
```bash
cd YOUR_STRAPI_PROJECT/src/plugins
git clone git@github.com:YOUR_USERNAME/strapi-plugin-config-sync.git config-sync
git clone git@github.com:YOUR_USERNAME/strapi-plugin-config-sync.git
```
#### 3. Install the dependencies
Go to the plugin and install it's dependencies.
Go to the folder and install the dependencies
```bash
cd YOUR_STRAPI_PROJECT/src/plugins/config-sync/ && yarn plugin:install
cd strapi-plugin-config-sync && yarn install
```
#### 4. Enable the plugin
#### 4. Install the playground dependencies
Add the following lines to the `config/plugins.js` file in your Strapi project.
```
const path = require('path');
// ...
{
'config-sync': {
enabled: true,
resolve: path.resolve(__dirname, '../src/plugins/config-sync'),
},
}
```
#### 5. Rebuild your Strapi project
Rebuild your strapi project to build the admin part of the plugin.
Run this in the root of the repository
```bash
cd YOUR_STRAPI_PROJECT && yarn build
yarn playground:install
```
#### 6. Running the administration panel in development mode
#### 5. Run the compiler of the plugin
**Start the administration panel server for development**
We use `yalc` to publish the package to a local registry. Run the following command o watch for changes and push to `yalc` every time a change is made:
```bash
cd YOUR_STRAPI_PROJECT && yarn develop --watch-admin
yarn develop
```
The administration panel will be available at http://localhost:8080/admin
#### 6. Start the playground instance
Leave the watcher running, open up a new terminal window and browse back to the root of the plugin repo. Run the following command:
```bash
yarn playground:develop
```
This will start the playground instance that will have the plugin installed from the `yalc` registry. Browse to http://localhost:1337 and create a test admin user to log in to the playground.
#### 7. Start your contribution!
You can now start working on your contribution. If you had trouble setting up this testing environment please feel free to report an issue on Github.
### Commit message convention
@ -82,12 +77,10 @@ The `package.json` file contains various scripts for common tasks:
- `yarn eslint`: lint files with ESLint.
- `yarn eslint:fix`: auto-fix ESLint issues.
- `yarn test:unit`: run unit tests with Jest.
- `yarn test:integration`: run integration tests with Jest.
### Sending a pull request
> **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
When you're sending a pull request:
- Prefer small pull requests focused on one change.

410
README.md
View File

@ -1,7 +1,9 @@
<div align="center">
<h1>Strapi config-sync plugin</h1>
<p style="margin-top: 0;">This plugin is a multi-purpose tool to manage your Strapi database records through JSON files. Mostly used to version control <a href="#-config-types">config data</a> for automated deployment, automated tests and data sharing for collaboration purposes.</p>
<p style="margin-top: 0;">This plugin is a multi-purpose tool to manage your Strapi database records through JSON files. Mostly used to version controlconfig data for automated deployment, automated tests and data sharing for collaboration purposes.</p>
<a href="https://docs.pluginpal.io/config-sync">Read the documentation</a>
<p>
<a href="https://www.npmjs.org/package/strapi-plugin-config-sync">
@ -19,19 +21,6 @@
</p>
</div>
## Table of Contents
- [Features](#-features)
- [Installation](#-installation)
- [Requirements](#-requirements)
- [Motivation](#-motivation)
- [CLI](#-command-line-interface-cli)
- [Admin panel](#%EF%B8%8F-admin-panel-gui)
- [Usage / Workflow](#%EF%B8%8F-usage--workflow)
- [Config types](#-config-types)
- [Naming convention](#-naming-convention)
- [Settings](#-settings)
## ✨ Features
- **CLI** - `config-sync` CLI for syncing the config from the command line
@ -42,9 +31,9 @@
- **Exclusion** - Exclude single config entries or all entries of a given type
- **Diff viewer** - A git-style diff viewer to inspect the config changes
## ⏳ Installation
## ⏳ Getting started
Install the plugin in your Strapi project.
[Read the Getting Started tutorial](https://docs.pluginpal.io/config-sync) or follow the steps below:
```bash
# using yarn
@ -81,392 +70,15 @@ npm run develop
The **Config Sync** plugin should now appear in the **Settings** section of your Strapi app.
To start tracking your config changes you have to make the first export. This will dump all your configuration data to the `/config/sync` directory. You can export either through [the CLI](#-command-line-interface-cli) or [Strapi admin panel](#%EF%B8%8F-admin-panel-gui)
To start tracking your config changes you have to make the first export. This will dump all your configuration data to the `/config/sync` directory. You can export either through [the CLI](https://docs.pluginpal.io/config-sync/cli) or [Strapi admin panel](https://docs.pluginpal.io/config-sync/admin-gui)
Enjoy 🎉
## 🖐 Requirements
## 📓 Documentation
Complete installation requirements are the exact same as for Strapi itself and can be found in the [Strapi documentation](https://strapi.io/documentation).
**Supported Strapi versions**:
- Strapi 4.14.4 (recently tested)
- Strapi ^4.x (use `strapi-plugin-config-sync@^1.0.0`)
- Strapi ^3.4.x (use `strapi-plugin-config-sync@0.1.6`)
## 💡 Motivation
In Strapi we come across what I would call config types. These are models of which the records are stored in our database, just like content types. Though the big difference here is that your code ofter relies on the database records of these types.
Having said that, it makes sense that these records can be exported, added to git, and be migrated across environments. This way we can make sure we have all the data our code relies on, on each environment.
Examples of these types are:
- Admin roles _(admin::role)_
- User roles _(plugin::users-permissions.role)_
- Admin settings _(strapi::core-store)_
- I18n locale _(plugin::i18n.locale)_
This plugin gives you the tools to sync this data. You can export the data as JSON files on one env, and import them on every other env. By writing this data as JSON files you can easily track them in your version control system (git).
_With great power comes great responsibility - Spider-Man_
## 🔌 Command line interface (CLI)
Add the `config-sync` command as a script to the `package.json` of your Strapi project:
```
"scripts": {
// ...
"cs": "config-sync"
},
```
You can now run all the `config-sync` commands like this:
```bash
# using yarn
yarn cs --help
# using npm
npm run cs --help
```
### ⬆️ Import ⬇️ Export
> _Command:_ `import` _Alias:_ `i`
>
> _Command:_ `export` _Alias:_ `e`
These commands are used to sync the config in your Strapi project.
_Example:_
```bash
# using yarn
yarn cs import
yarn cs export
# using npm
npm run cs import
npm run cs export
```
##### Flag: `-y`, `--yes`
Use this flag to skip the confirm prompt and go straight to syncing the config.
```bash
[command] --yes
```
##### Flag: `-t`, `--type`
Use this flag to specify the type of config you want to sync.
```bash
[command] --type user-role
```
##### Flag: `-p`, `--partial`
Use this flag to sync a specific set of configs by giving the CLI a comma-separated string of config names.
```bash
[command] --partial user-role.public,i18n-locale.en
```
##### Flag: `-f`, `--force`
If you're using the soft setting to gracefully import config, you can use this flag to ignore the setting for the current command and forcefully import all changes anyway.
```bash
[command] --force
```
### ↔️ Diff
> _Command:_ `diff` | _Alias:_ `d`
This command is used to see the difference between the config as found in the sync directory, and the config as found in the database.
_Example:_
```bash
# using yarn
yarn cs diff
# using npm
npm run cs diff
```
##### Argument: `<single>`
Add a single config name as the argument of the `diff` command to see the difference of that single file in a git-style diff viewer.
_Example:_
```bash
# using yarn
yarn cs diff user-role.public
# using npm
npm run cs diff user-role.public
```
## 🖥️ Admin panel (GUI)
This plugin ships with a React app which can be accessed from the settings page in Strapi admin panel. On this page you can pretty much do the same as you can from the CLI. You can import, export and see the difference between the config as found in the sync directory, and the config as found in the database.
**Pro tip:**
By clicking on one of the items in the diff table you can see the exact difference between sync dir and database in a git-style diff viewer.
<img src="https://raw.githubusercontent.com/boazpoolman/strapi-plugin-config-sync/master/.github/config-diff.png" alt="Config diff in admin" />
## ⌨️ Usage / Workflow
This plugin works best when you use `git` for the version control of your Strapi project.
_The following workflows are assuming you're using `git`._
### Intro
All database records tracked with this plugin will be exported to JSON files. Once exported each change to the file or the record will be tracked. Meaning you can now do one of two things:
- Change the file(s), and run an import. You have now imported from filesystem -> database.
- Change the record(s), and run an export. You have now exported from database -> filesystem.
### Local development
When building a new feature locally for your Strapi project you'd use the following workflow:
- Build the feature.
- Export the config.
- Commit and push the files to git.
### Deployment
When deploying the newly created feature - to either a server, or a co-worker's machine - you'd use the following workflow:
- Pull the latest file changes to the environment.
- (Re)start your Strapi instance.
- Import the config.
### Production deployment
The production deployment will be the same as a regular deployment. You just have to be careful before running the import. Ideally making sure the are no open changes before you pull the new code to the environment.
## 🚀 Config types
By default the plugin will track 4 (official) types.
To track your own custom types you can register them by setting some plugin config.
### Default types
These 4 types are by default registered in the sync process.
#### Admin role
> Config name: `admin-role` | UID: `code` | Query string: `admin::role`
#### User role
> Config name: `user-role` | UID: `type` | Query string: `plugin::users-permissions.role`
#### Core store
> Config name: `core-store` | UID: `key` | Query string: `strapi::core-store`
#### I18n locale
> Config name: `i18n-locale` | UID: `code` | Query string: `plugin::i18n.locale`
### Custom types
Your custom types can be registered through the `customTypes` plugin config. This is a setting that can be set in the `config/plugins.js` file in your project.
_Read more about the `config/plugins.js` file [here](#-settings)._
You can register a type by giving the `customTypes` array an object which contains at least the following 3 properties:
```
customTypes: [{
configName: 'webhook',
queryString: 'webhook',
uid: 'name',
}],
```
_The example above will register the Strapi webhook type._
#### Config name
The name of the config type. This value will be used as the first part of the filename for all config of this type. It should be unique from the other types and is preferably written in kebab-case.
###### Key: `configName`
> `required:` YES | `type:` string
#### Query string
This is the query string of the type. Each type in Strapi has its own query string you can use to programatically preform CRUD actions on the entries of the type. Often for custom types in Strapi the format is something like `api::custom-api.custom-type`.
###### Key: `queryString`
> `required:` YES | `type:` string
#### UID
The UID represents a field on the registered type. The value of this field will act as a unique identifier to identify the entries across environments. Therefore it should be unique and preferably un-editable after initial creation.
Mind that you can not use an auto-incremental value like the `id` as auto-increment does not play nice when you try to match entries across different databases.
If you do not have a single unique value, you can also pass in an array of keys for a combined uid key. This is for example the case for all content types which use i18n features (An example config would be `uid: ['productId', 'locale']`).
###### Key: `uid`
> `required:` YES | `type:` string | string[]
#### Relations
The relations array specifies the relations you want to include in the sync process.
This feature is used to sync the relations between `roles` and `permissions`. See https://github.com/boazpoolman/strapi-plugin-config-sync/blob/master/server/config/types.js#L16.
Example:
```
{
configName: 'admin-role',
queryString: 'admin::role',
uid: 'code',
relations: [{
queryString: 'admin::permission',
relationName: 'permissions',
parentName: 'role',
relationSortFields: ['action', 'subject'],
}],
},
```
###### Key: `relations`
> `required:` NO | `type:` array
#### Components
This property can accept an array of component names from the type. Strapi Components can be included in the export/import process. With "." nested components can also be included in the process.
```
customTypes: [{
configName: 'webhook',
queryString: 'webhook',
uid: 'name',
components: ['ParentComponentA', 'ParentComponentA.ChildComponent', 'ParentComponentB']
}],
```
###### Key: `components`
> `required:` NO | `type:` array
#### JSON fields
This property can accept an array of field names from the type. It is meant to specify the JSON fields on the type so the plugin can better format the field values when calculating the config difference.
###### Key: `jsonFields`
> `required:` NO | `type:` array
## 🔍 Naming convention
All the config files written in the sync directory have the same naming convention. It goes as follows:
[config-type].[identifier].json
- `config-type` - Corresponds to the `configName` of the config type.
- `identifier` - Corresponds to the value of the `uid` field of the config type.
## 🔧 Settings
The settings of the plugin can be overridden in the `config/plugins.js` file.
In the example below you can see how, and also what the default settings are.
##### `config/plugins.js`:
module.exports = ({ env }) => ({
// ...
'config-sync': {
enabled: true,
config: {
syncDir: "config/sync/",
minify: false,
soft: false,
importOnBootstrap: false,
customTypes: [],
excludedTypes: [],
excludedConfig: [
"core-store.plugin_users-permissions_grant",
"core-store.plugin_upload_metrics",
"core-store.strapi_content_types_schema",
"core-store.ee_information",
],
},
},
});
### Sync dir
The path for reading and writing the sync files.
###### Key: `syncDir`
> `required:` YES | `type:` string | `default:` `config/sync/`
### Minify
When enabled all the exported JSON files will be minified.
###### Key: `minify`
> `required:` NO | `type:` bool | `default:` `false`
### Soft
When enabled the import action will be limited to only create new entries. Entries to be deleted, or updated will be skipped from the import process and will remain in it's original state.
###### Key: `soft`
> `required:` NO | `type:` bool | `default:` `false`
### Import on bootstrap
Allows you to let the config be imported automaticly when strapi is bootstrapping (on `strapi start`). This setting can't be used locally and should be handled very carefully as it can unintendedly overwrite the changes in your database. **PLEASE USE WITH CARE**.
###### Key: `importOnBootstrap`
> `required:` NO | `type:` bool | `default:` `false`
### Custom types
With this setting you can register your own custom config types. This is an array which expects objects with at least the `configName`, `queryString` and `uid` properties. Read more about registering custom types in the [Custom config types](#custom-types) documentation.
###### Key: `customTypes`
> `required:` NO | `type:` array | `default:` `[]`
### Excluded types
This setting will exclude all the config from a given type from the syncing process. The config types are specified by the `configName` of the type.
For example:
```
excludedTypes: ['admin-role']
```
###### Key: `excludedTypes`
> `required:` NO | `type:` array | `default:` `[]`
### Excluded config
Specify the names of configs you want to exclude from the syncing process. By default the API tokens for users-permissions, which are stored in core_store, are excluded. This setting expects the config names to comply with the naming convention.
###### Key: `excludedConfig`
> `required:` NO | `type:` array | `default:` `['core-store.plugin_users-permissions_grant', 'core-store.plugin_upload_metrics', 'core-store.strapi_content_types_schema', 'core-store.ee_information',]`
See our dedicated [repository](https://github.com/pluginpal/docs) for all of PluginPal's documentation, or view the Config Sync documentation live:
- [Config Sync documentation](https://docs.pluginpal.io/config-sync)
## 🤝 Contributing
@ -478,8 +90,10 @@ Give a star if this project helped you.
## 🔗 Links
- [PluginPal marketplace](https://www.pluginpal.io/plugin/config-sync)
- [NPM package](https://www.npmjs.com/package/strapi-plugin-config-sync)
- [GitHub repository](https://github.com/boazpoolman/strapi-plugin-config-sync)
- [Strapi marketplace](https://market.strapi.io/plugins/strapi-plugin-config-sync)
## 🌎 Community support
@ -488,4 +102,4 @@ Give a star if this project helped you.
## 📝 Resources
- [MIT License](LICENSE.md)
- [MIT License](https://github.com/pluginpal/strapi-plugin-config-sync/blob/master/LICENSE.md)

View File

@ -1,62 +0,0 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import { useDispatch, useSelector } from 'react-redux';
import { isEmpty } from 'lodash';
import { Button } from '@strapi/design-system';
import { Map } from 'immutable';
import { useNotification } from '@strapi/helper-plugin';
import { useIntl } from 'react-intl';
import ConfirmModal from '../ConfirmModal';
import { exportAllConfig, importAllConfig } from '../../state/actions/Config';
const ActionButtons = () => {
const dispatch = useDispatch();
const toggleNotification = useNotification();
const [modalIsOpen, setModalIsOpen] = useState(false);
const [actionType, setActionType] = useState('');
const partialDiff = useSelector((state) => state.getIn(['config', 'partialDiff'], Map({}))).toJS();
const { formatMessage } = useIntl();
const closeModal = () => {
setActionType('');
setModalIsOpen(false);
};
const openModal = (type) => {
setActionType(type);
setModalIsOpen(true);
};
return (
<ActionButtonsStyling>
<Button disabled={isEmpty(partialDiff)} onClick={() => openModal('import')}>
{formatMessage({ id: 'config-sync.Buttons.Import' })}
</Button>
<Button disabled={isEmpty(partialDiff)} onClick={() => openModal('export')}>
{formatMessage({ id: 'config-sync.Buttons.Export' })}
</Button>
{!isEmpty(partialDiff) && (
<h4 style={{ display: 'inline' }}>{Object.keys(partialDiff).length} {Object.keys(partialDiff).length === 1 ? "config change" : "config changes"}</h4>
)}
<ConfirmModal
isOpen={modalIsOpen}
onClose={closeModal}
type={actionType}
onSubmit={(force) => actionType === 'import' ? dispatch(importAllConfig(partialDiff, force, toggleNotification)) : dispatch(exportAllConfig(partialDiff, toggleNotification))}
/>
</ActionButtonsStyling>
);
};
const ActionButtonsStyling = styled.div`
padding: 10px 0 20px 0;
display: flex;
align-items: center;
> button {
margin-right: 10px;
}
`;
export default ActionButtons;

View File

@ -0,0 +1,61 @@
import React from 'react';
import styled from 'styled-components';
import { useDispatch, useSelector } from 'react-redux';
import isEmpty from 'lodash/isEmpty';
import { Button, Typography } from '@strapi/design-system';
import { Map } from 'immutable';
import { getFetchClient, useNotification } from '@strapi/strapi/admin';
import { useIntl } from 'react-intl';
import ConfirmModal from '../ConfirmModal';
import { exportAllConfig, importAllConfig, downloadZip } from '../../state/actions/Config';
const ActionButtons = () => {
const { post, get } = getFetchClient();
const dispatch = useDispatch();
const { toggleNotification } = useNotification();
const partialDiff = useSelector((state) => state.getIn(['config', 'partialDiff'], Map({}))).toJS();
const { formatMessage } = useIntl();
return (
<ActionButtonsStyling>
<ConfirmModal
type="import"
trigger={(
<Button disabled={isEmpty(partialDiff)}>
{formatMessage({ id: 'config-sync.Buttons.Import' })}
</Button>
)}
onSubmit={(force) => dispatch(importAllConfig(partialDiff, force, toggleNotification, formatMessage, post, get))}
/>
<ConfirmModal
type="export"
trigger={(
<Button disabled={isEmpty(partialDiff)}>
{formatMessage({ id: 'config-sync.Buttons.Export' })}
</Button>
)}
onSubmit={(force) => dispatch(exportAllConfig(partialDiff, toggleNotification, formatMessage, post, get))}
/>
{!isEmpty(partialDiff) && (
<Typography variant="epsilon">{Object.keys(partialDiff).length} {Object.keys(partialDiff).length === 1 ? "config change" : "config changes"}</Typography>
)}
<Button onClick={() => dispatch(downloadZip(toggleNotification, formatMessage, post, get))}>{formatMessage({ id: 'config-sync.Buttons.DownloadConfig' })}</Button>
</ActionButtonsStyling>
);
};
const ActionButtonsStyling = styled.div`
padding: 10px 0 20px 0;
display: flex;
align-items: center;
> button {
margin-right: 10px;
}
> button:last-of-type {
margin-left: auto;
}
`;
export default ActionButtons;

View File

@ -1,48 +0,0 @@
import React from 'react';
import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer-continued';
import { useIntl } from 'react-intl';
import {
ModalLayout,
ModalBody,
ModalHeader,
Grid,
GridItem,
Typography,
} from '@strapi/design-system';
const ConfigDiff = ({ isOpen, onClose, oldValue, newValue, configName }) => {
const { formatMessage } = useIntl();
if (!isOpen) return null;
return (
<ModalLayout
onClose={onClose}
labelledBy="title"
>
<ModalHeader>
<Typography variant="omega" fontWeight="bold" textColor="neutral800">
{formatMessage({ id: 'config-sync.ConfigDiff.Title' })} {configName}
</Typography>
</ModalHeader>
<ModalBody>
<Grid paddingBottom={4} style={{ textAlign: 'center' }}>
<GridItem col={6}>
<Typography variant="delta">{formatMessage({ id: 'config-sync.ConfigDiff.SyncDirectory' })}</Typography>
</GridItem>
<GridItem col={6}>
<Typography variant="delta">{formatMessage({ id: 'config-sync.ConfigDiff.Database' })}</Typography>
</GridItem>
</Grid>
<ReactDiffViewer
oldValue={JSON.stringify(oldValue, null, 2)}
newValue={JSON.stringify(newValue, null, 2)}
splitView
compareMethod={DiffMethod.WORDS}
/>
</ModalBody>
</ModalLayout>
);
};
export default ConfigDiff;

View File

@ -0,0 +1,64 @@
import React from 'react';
import RDV, { DiffMethod } from 'react-diff-viewer-continued';
import { useIntl } from 'react-intl';
/**
* An issue with the diff-viewer library causes a difference in the way the library is exported.
* Depending on whether the library is loaded through the browser or through the server, the default export may or may not be present.
* This causes issues with SSR and the way the library is imported.
*
* Below a workaround to fix this issue.
*
* @see https://github.com/Aeolun/react-diff-viewer-continued/issues/43
*/
let ReactDiffViewer;
if (typeof RDV.default !== 'undefined') {
ReactDiffViewer = RDV.default;
} else {
ReactDiffViewer = RDV;
}
import {
Modal,
Grid,
Typography,
} from '@strapi/design-system';
const ConfigDiff = ({ oldValue, newValue, configName, trigger }) => {
const { formatMessage } = useIntl();
return (
<Modal.Root>
<Modal.Trigger>
{trigger}
</Modal.Trigger>
<Modal.Content>
<Modal.Header>
<Typography variant="omega" fontWeight="bold" textColor="neutral800">
{formatMessage({ id: 'config-sync.ConfigDiff.Title' })} {configName}
</Typography>
</Modal.Header>
<Modal.Body>
<Grid.Root paddingBottom={4} style={{ textAlign: 'center' }}>
<Grid.Item col={6}>
<Typography variant="delta" style={{ width: '100%' }}>{formatMessage({ id: 'config-sync.ConfigDiff.SyncDirectory' })}</Typography>
</Grid.Item>
<Grid.Item col={6}>
<Typography variant="delta" style={{ width: '100%' }}>{formatMessage({ id: 'config-sync.ConfigDiff.Database' })}</Typography>
</Grid.Item>
</Grid.Root>
<Typography variant="pi">
<ReactDiffViewer
oldValue={JSON.stringify(oldValue, null, 2)}
newValue={JSON.stringify(newValue, null, 2)}
splitView
compareMethod={DiffMethod.WORDS}
/>
</Typography>
</Modal.Body>
</Modal.Content>
</Modal.Root>
);
};
export default ConfigDiff;

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Tr, Td, BaseCheckbox } from '@strapi/design-system';
import { Tr, Td, Checkbox, Typography } from '@strapi/design-system';
const CustomRow = ({ row, checked, updateValue }) => {
const CustomRow = ({ row, checked, updateValue, ...props }) => {
const { configName, configType, state, onClick } = row;
const stateStyle = (stateStr) => {
@ -34,6 +34,7 @@ const CustomRow = ({ row, checked, updateValue }) => {
return (
<Tr
{...props}
onClick={(e) => {
if (e.target.type !== 'checkbox') {
onClick(configType, configName);
@ -42,20 +43,20 @@ const CustomRow = ({ row, checked, updateValue }) => {
style={{ cursor: 'pointer' }}
>
<Td>
<BaseCheckbox
<Checkbox
aria-label={`Select ${configName}`}
value={checked}
onValueChange={updateValue}
checked={checked}
onCheckedChange={updateValue}
/>
</Td>
<Td>
<p>{configName}</p>
<Td onClick={(e) => props.onClick(e)}>
<Typography variant="omega">{configName}</Typography>
</Td>
<Td>
<p>{configType}</p>
<Td onClick={(e) => props.onClick(e)}>
<Typography variant="omega">{configType}</Typography>
</Td>
<Td>
<p style={stateStyle(state)}>{state}</p>
<Td onClick={(e) => props.onClick(e)}>
<Typography variant="omega" style={stateStyle(state)}>{state}</Typography>
</Td>
</Tr>
);

View File

@ -10,7 +10,7 @@ import {
Tr,
Th,
Typography,
BaseCheckbox,
Checkbox,
Loader,
} from '@strapi/design-system';
@ -22,7 +22,6 @@ import { setConfigPartialDiffInState } from '../../state/actions/Config';
const ConfigList = ({ diff, isLoading }) => {
const [openModal, setOpenModal] = useState(false);
const [originalConfig, setOriginalConfig] = useState({});
const [newConfig, setNewConfig] = useState({});
const [cName, setCname] = useState('');
@ -72,7 +71,6 @@ const ConfigList = ({ diff, isLoading }) => {
setOriginalConfig(diff.fileConfig[`${configType}.${configName}`]);
setNewConfig(diff.databaseConfig[`${configType}.${configName}`]);
setCname(`${configType}.${configName}`);
setOpenModal(true);
},
});
});
@ -89,13 +87,6 @@ const ConfigList = ({ diff, isLoading }) => {
dispatch(setConfigPartialDiffInState(newPartialDiff));
}, [checkedItems]);
const closeModal = () => {
setOriginalConfig({});
setNewConfig({});
setCname('');
setOpenModal(false);
};
if (isLoading) {
return (
<div style={{ textAlign: 'center', marginTop: 40 }}>
@ -117,22 +108,14 @@ const ConfigList = ({ diff, isLoading }) => {
return (
<div>
<ConfigDiff
isOpen={openModal}
oldValue={originalConfig}
newValue={newConfig}
onClose={closeModal}
configName={cName}
/>
<Table colCount={4} rowCount={rows.length + 1}>
<Thead>
<Tr>
<Th>
<BaseCheckbox
<Checkbox
aria-label={formatMessage({ id: 'config-sync.ConfigList.SelectAll' })}
indeterminate={isIndeterminate}
onValueChange={(value) => setCheckedItems(checkedItems.map(() => value))}
value={allChecked}
checked={isIndeterminate ? "indeterminate" : allChecked}
onCheckedChange={(value) => setCheckedItems(checkedItems.map(() => value))}
/>
</Th>
<Th>
@ -148,14 +131,21 @@ const ConfigList = ({ diff, isLoading }) => {
</Thead>
<Tbody>
{rows.map((row, index) => (
<ConfigListRow
<ConfigDiff
key={row.configName}
row={row}
checked={checkedItems[index]}
updateValue={() => {
checkedItems[index] = !checkedItems[index];
setCheckedItems([...checkedItems]);
}}
oldValue={originalConfig}
newValue={newConfig}
configName={cName}
trigger={(
<ConfigListRow
row={row}
checked={checkedItems[index]}
updateValue={() => {
checkedItems[index] = !checkedItems[index];
setCheckedItems([...checkedItems]);
}}
/>
)}
/>
))}
</Tbody>

View File

@ -1,83 +0,0 @@
import React, { useState } from 'react';
import { useIntl } from 'react-intl';
import { useSelector } from 'react-redux';
import {
Dialog,
DialogBody,
DialogFooter,
Flex,
Typography,
Stack,
Button,
Checkbox,
Divider,
Box,
} from '@strapi/design-system';
import { ExclamationMarkCircle } from '@strapi/icons';
const ConfirmModal = ({ isOpen, onClose, onSubmit, type }) => {
const soft = useSelector((state) => state.getIn(['config', 'appEnv', 'config', 'soft'], false));
const [force, setForce] = useState(false);
const { formatMessage } = useIntl();
if (!isOpen) return null;
return (
<Dialog
onClose={onClose}
title={formatMessage({ id: "config-sync.popUpWarning.Confirmation" })}
isOpen={isOpen}
>
<DialogBody icon={<ExclamationMarkCircle />}>
<Stack size={2}>
<Flex justifyContent="center">
<Typography variant="omega" id="confirm-description" style={{ textAlign: 'center' }}>
{formatMessage({ id: `config-sync.popUpWarning.warning.${type}_1` })}<br />
{formatMessage({ id: `config-sync.popUpWarning.warning.${type}_2` })}
</Typography>
</Flex>
</Stack>
</DialogBody>
{(soft && type === 'import') && (
<React.Fragment>
<Divider />
<Box padding={4}>
<Checkbox
onValueChange={(value) => setForce(value)}
value={force}
name="force"
hint="Check this to ignore the soft setting."
>
{formatMessage({ id: 'config-sync.popUpWarning.force' })}
</Checkbox>
</Box>
</React.Fragment>
)}
<DialogFooter
startAction={(
<Button
onClick={() => {
onClose();
}}
variant="tertiary"
>
{formatMessage({ id: 'config-sync.popUpWarning.button.cancel' })}
</Button>
)}
endAction={(
<Button
variant="secondary"
onClick={() => {
onClose();
onSubmit(force);
}}
>
{formatMessage({ id: `config-sync.popUpWarning.button.${type}` })}
</Button>
)} />
</Dialog>
);
};
export default ConfirmModal;

View File

@ -0,0 +1,80 @@
import React, { useState } from 'react';
import { useIntl } from 'react-intl';
import { useSelector } from 'react-redux';
import {
Dialog,
Flex,
Typography,
Button,
Checkbox,
Divider,
Box,
Field,
} from '@strapi/design-system';
import { WarningCircle } from '@strapi/icons';
const ConfirmModal = ({ onClose, onSubmit, type, trigger }) => {
const soft = useSelector((state) => state.getIn(['config', 'appEnv', 'config', 'soft'], false));
const [force, setForce] = useState(false);
const { formatMessage } = useIntl();
return (
<Dialog.Root>
<Dialog.Trigger>
{trigger}
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>{formatMessage({ id: "config-sync.popUpWarning.Confirmation" })}</Dialog.Header>
<Dialog.Body>
<WarningCircle fill="danger600" width="32px" height="32px" />
<Flex size={2}>
<Flex justifyContent="center">
<Typography variant="omega" id="confirm-description" style={{ textAlign: 'center' }}>
{formatMessage({ id: `config-sync.popUpWarning.warning.${type}_1` })}<br />
{formatMessage({ id: `config-sync.popUpWarning.warning.${type}_2` })}
</Typography>
</Flex>
</Flex>
{(soft && type === 'import') && (
<Box width="100%">
<Divider marginTop={4} />
<Box paddingTop={6}>
<Field.Root hint="Check this to ignore the soft setting.">
<Checkbox
onValueChange={(value) => setForce(value)}
value={force}
name="force"
>
{formatMessage({ id: 'config-sync.popUpWarning.force' })}
</Checkbox>
<Field.Hint />
</Field.Root>
</Box>
</Box>
)}
</Dialog.Body>
<Dialog.Footer>
<Dialog.Cancel>
<Button fullWidth variant="tertiary">
{formatMessage({ id: 'config-sync.popUpWarning.button.cancel' })}
</Button>
</Dialog.Cancel>
<Dialog.Action>
<Button
fullWidth
variant="secondary"
onClick={() => {
onSubmit(force);
}}
>
{formatMessage({ id: `config-sync.popUpWarning.button.${type}` })}
</Button>
</Dialog.Action>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>
);
};
export default ConfirmModal;

View File

@ -1,36 +0,0 @@
import React, { useState } from 'react';
import { useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { NoContent, useNotification } from '@strapi/helper-plugin';
import { Button } from '@strapi/design-system';
import { exportAllConfig } from '../../state/actions/Config';
import ConfirmModal from '../ConfirmModal';
const FirstExport = () => {
const toggleNotification = useNotification();
const dispatch = useDispatch();
const [modalIsOpen, setModalIsOpen] = useState(false);
const { formatMessage } = useIntl();
return (
<div>
<ConfirmModal
isOpen={modalIsOpen}
onClose={() => setModalIsOpen(false)}
type="export"
onSubmit={() => dispatch(exportAllConfig([], toggleNotification))}
/>
<NoContent
content={{
id: 'emptyState',
defaultMessage:
formatMessage({ id: 'config-sync.FirstExport.Message' }),
}}
action={<Button onClick={() => setModalIsOpen(true)}>{formatMessage({ id: 'config-sync.FirstExport.Button' })}</Button>}
/>
</div>
);
};
export default FirstExport;

View File

@ -0,0 +1,37 @@
import React from 'react';
import { useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { getFetchClient, useNotification } from '@strapi/strapi/admin';
import { Button, EmptyStateLayout } from '@strapi/design-system';
import { EmptyDocuments } from '@strapi/icons/symbols';
import { exportAllConfig } from '../../state/actions/Config';
import ConfirmModal from '../ConfirmModal';
const FirstExport = () => {
const { post, get } = getFetchClient();
const { toggleNotification } = useNotification();
const dispatch = useDispatch();
const { formatMessage } = useIntl();
return (
<div>
<EmptyStateLayout
content={formatMessage({ id: 'config-sync.FirstExport.Message' })}
action={(
<ConfirmModal
type="export"
onSubmit={() => dispatch(exportAllConfig([], toggleNotification, formatMessage, post, get))}
trigger={(
<Button>{formatMessage({ id: 'config-sync.FirstExport.Button' })}</Button>
)}
/>
)}
icon={<EmptyDocuments width={160} />}
/>
</div>
);
};
export default FirstExport;

View File

@ -7,17 +7,17 @@
import React, { memo } from 'react';
import { useIntl } from 'react-intl';
import { HeaderLayout, Box } from '@strapi/design-system';
import { Layouts } from '@strapi/admin/strapi-admin';
import { Box } from '@strapi/design-system';
const HeaderComponent = () => {
const { formatMessage } = useIntl();
return (
<Box background="neutral100">
<HeaderLayout
<Layouts.Header
title={formatMessage({ id: 'config-sync.Header.Title' })}
subtitle={formatMessage({ id: 'config-sync.Header.Description' })}
as="h2"
/>
</Box>
);

View File

@ -1,18 +0,0 @@
import React from 'react';
import { NoContent } from '@strapi/helper-plugin';
import { useIntl } from 'react-intl';
const NoChanges = () => {
const { formatMessage } = useIntl();
return (
<NoContent
content={{
id: 'emptyState',
defaultMessage:
formatMessage({ id: 'config-sync.NoChanges.Message' }),
}}
/>
);
};
export default NoChanges;

View File

@ -0,0 +1,16 @@
import React from 'react';
import { EmptyStateLayout } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import { EmptyDocuments } from '@strapi/icons/symbols';
const NoChanges = () => {
const { formatMessage } = useIntl();
return (
<EmptyStateLayout
content={formatMessage({ id: 'config-sync.NoChanges.Message', defaultMessage: 'No differences between DB and sync directory. You are up-to-date!' })}
icon={<EmptyDocuments width={160} />}
/>
);
};
export default NoChanges;

View File

@ -7,7 +7,7 @@
import React from 'react';
import { Provider } from 'react-redux';
import { CheckPagePermissions } from '@strapi/helper-plugin';
import { Page } from '@strapi/strapi/admin';
import pluginPermissions from '../../permissions';
import Header from '../../components/Header';
@ -16,12 +16,12 @@ import ConfigPage from '../ConfigPage';
const App = () => {
return (
<CheckPagePermissions permissions={pluginPermissions.settings}>
<Page.Protect permissions={pluginPermissions.settings}>
<Provider store={store}>
<Header />
<ConfigPage />
</Provider>
</CheckPagePermissions>
</Page.Protect>
);
};

View File

@ -3,30 +3,34 @@ import { useDispatch, useSelector } from 'react-redux';
import { Map } from 'immutable';
import {
Box,
ContentLayout,
Alert,
Typography,
} from '@strapi/design-system';
import { useNotification } from '@strapi/helper-plugin';
import { useNotification } from '@strapi/strapi/admin';
import { getFetchClient, Layouts } from '@strapi/admin/strapi-admin';
import { useIntl } from 'react-intl';
import { getAllConfigDiff, getAppEnv } from '../../state/actions/Config';
import ConfigList from '../../components/ConfigList';
import ActionButtons from '../../components/ActionButtons';
const ConfigPage = () => {
const toggleNotification = useNotification();
const { toggleNotification } = useNotification();
const { get } = getFetchClient();
const { formatMessage } = useIntl();
const dispatch = useDispatch();
const isLoading = useSelector((state) => state.getIn(['config', 'isLoading'], Map({})));
const configDiff = useSelector((state) => state.getIn(['config', 'configDiff'], Map({})));
const appEnv = useSelector((state) => state.getIn(['config', 'appEnv', 'env']));
useEffect(() => {
dispatch(getAllConfigDiff(toggleNotification));
dispatch(getAppEnv(toggleNotification));
dispatch(getAllConfigDiff(toggleNotification, formatMessage, get));
dispatch(getAppEnv(toggleNotification, formatMessage, get));
}, []);
return (
<ContentLayout paddingBottom={8}>
<Layouts.Content paddingBottom={8}>
{appEnv === 'production' && (
<Box paddingBottom={4}>
<Alert variant="danger">
@ -38,7 +42,7 @@ const ConfigPage = () => {
)}
<ActionButtons />
<ConfigList isLoading={isLoading} diff={configDiff.toJS()} />
</ContentLayout>
</Layouts.Content>
);
};

View File

@ -0,0 +1,9 @@
export function b64toBlob(dataURI, type) {
const byteString = atob(dataURI);
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type });
}

View File

@ -1,8 +1,8 @@
const pluginPkg = require('../../../package.json');
import pluginPkg from '../../../package.json';
const pluginId = pluginPkg.name.replace(
/^strapi-plugin-/i,
'',
);
module.exports = pluginId;
export default pluginId;

View File

@ -0,0 +1,11 @@
const prefixPluginTranslations = (trad, pluginId) => {
if (!pluginId) {
throw new TypeError("pluginId can't be empty");
}
return Object.keys(trad).reduce((acc, current) => {
acc[`${pluginId}.${current}`] = trad[current];
return acc;
}, {});
};
export { prefixPluginTranslations };

74
admin/src/index.cy.jsx Normal file
View File

@ -0,0 +1,74 @@
// <reference types="cypress" />
describe('Config Sync', () => {
beforeEach(() => {
cy.task('deleteFolder', 'playground/config/sync');
});
it('Check the config diff', () => {
cy.login();
cy.navigateToInterface();
cy.initialExport();
cy.makeConfigChanges();
cy.navigateToInterface();
cy.get('tbody tr').contains('plugin_users-permissions_advanced').click();
cy.contains('"unique_email": true,');
cy.contains('"unique_email": false,');
});
it('Download the config as zip', () => {
cy.login();
cy.navigateToInterface();
cy.initialExport();
cy.intercept({
method: 'GET',
url: '/config-sync/zip',
}).as('getConfigZip');
cy.get('button').contains('Download Config').click();
cy.wait('@getConfigZip').then((interception) => {
const configZipResponse = interception.response.body;
const downloadsFolder = Cypress.config('downloadsFolder');
cy.readFile(`${downloadsFolder}/${configZipResponse.name.replaceAll(':', '_')}`).should('exist');
});
});
it('Partial import & export', () => {
cy.login();
cy.navigateToInterface();
cy.initialExport();
cy.makeConfigChanges();
cy.navigateToInterface();
cy.get('button[aria-label="Select all entries"]').click();
cy.intercept({
method: 'POST',
url: '/config-sync/import',
}).as('importConfig');
cy.get('button[aria-label="Select plugin_upload_settings"]').click();
cy.get('button').contains('Import').click();
cy.get('button').contains('Yes, import').click();
cy.wait('@importConfig').its('response.statusCode').should('equal', 200);
cy.contains('plugin_users-permissions_advanced');
cy.contains('plugin_users-permissions_email');
cy.intercept({
method: 'POST',
url: '/config-sync/export',
}).as('exportConfig');
cy.get('button[aria-label="Select plugin_users-permissions_advanced"]').click();
cy.get('button').contains('Export').click();
cy.get('button').contains('Yes, export').click();
cy.wait('@exportConfig').its('response.statusCode').should('equal', 200);
cy.contains('plugin_users-permissions_email');
});
});

View File

@ -1,6 +1,6 @@
import { prefixPluginTranslations } from '@strapi/helper-plugin';
import pluginPkg from '../../package.json';
import pluginId from './helpers/pluginId';
import { prefixPluginTranslations } from './helpers/prefixPluginTranslations';
import pluginPermissions from './permissions';
// import pluginIcon from './components/PluginIcon';
// import getTrad from './helpers/getTrad';
@ -30,17 +30,11 @@ export default {
{
intlLabel: {
id: `${pluginId}.Settings.Tool.Title`,
defaultMessage: 'Tools',
defaultMessage: 'Interface',
},
id: 'config-sync-page',
to: `/settings/${pluginId}`,
Component: async () => {
const component = await import(
/* webpackChunkName: "config-sync-settings-page" */ './containers/App'
);
return component;
},
to: `${pluginId}`,
Component: () => import('./containers/App'),
permissions: pluginPermissions['settings'],
},
],
@ -50,9 +44,7 @@ export default {
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map((locale) => {
return import(
/* webpackChunkName: "config-sync-translation-[request]" */ `./translations/${locale}.json`
)
return import(`./translations/${locale}.json`)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),

View File

@ -3,19 +3,19 @@
* Main actions
*
*/
import { saveAs } from 'file-saver';
import { b64toBlob } from '../../helpers/blob';
import { request } from '@strapi/helper-plugin';
export function getAllConfigDiff(toggleNotification) {
export function getAllConfigDiff(toggleNotification, formatMessage, get) {
return async function(dispatch) {
dispatch(setLoadingState(true));
try {
const configDiff = await request('/config-sync/diff', { method: 'GET' });
const configDiff = await get('/config-sync/diff');
dispatch(setConfigPartialDiffInState([]));
dispatch(setConfigDiffInState(configDiff));
dispatch(setConfigDiffInState(configDiff.data));
dispatch(setLoadingState(false));
} catch (err) {
toggleNotification({ type: 'warning', message: { id: 'notification.error' } });
toggleNotification({ type: 'warning', message: formatMessage({ id: 'notification.error' }) });
dispatch(setLoadingState(false));
}
};
@ -37,40 +37,51 @@ export function setConfigPartialDiffInState(config) {
};
}
export function exportAllConfig(partialDiff, toggleNotification) {
export function exportAllConfig(partialDiff, toggleNotification, formatMessage, post, get) {
return async function(dispatch) {
dispatch(setLoadingState(true));
try {
const { message } = await request('/config-sync/export', {
method: 'POST',
body: partialDiff,
});
toggleNotification({ type: 'success', message });
dispatch(getAllConfigDiff(toggleNotification));
const response = await post('/config-sync/export', partialDiff);
toggleNotification({ type: 'success', message: response.data.message });
dispatch(getAllConfigDiff(toggleNotification, formatMessage, get));
dispatch(setLoadingState(false));
} catch (err) {
toggleNotification({ type: 'warning', message: { id: 'notification.error' } });
toggleNotification({ type: 'warning', message: formatMessage({ id: 'notification.error' }) });
dispatch(setLoadingState(false));
}
};
}
export function importAllConfig(partialDiff, force, toggleNotification) {
export function downloadZip(toggleNotification, formatMessage, post, get) {
return async function(dispatch) {
dispatch(setLoadingState(true));
try {
const { message } = await request('/config-sync/import', {
method: 'POST',
body: {
force,
config: partialDiff,
},
});
const { message, base64Data, name } = (await get('/config-sync/zip')).data;
toggleNotification({ type: 'success', message });
dispatch(getAllConfigDiff(toggleNotification));
if (base64Data) {
saveAs(b64toBlob(base64Data, 'application/zip'), name, { type: 'application/zip' });
}
dispatch(setLoadingState(false));
} catch (err) {
toggleNotification({ type: 'warning', message: { id: 'notification.error' } });
toggleNotification({ type: 'warning', message: formatMessage({ id: 'notification.error' }) });
dispatch(setLoadingState(false));
}
};
}
export function importAllConfig(partialDiff, force, toggleNotification, formatMessage, post, get) {
return async function(dispatch) {
dispatch(setLoadingState(true));
try {
const response = await post('/config-sync/import', {
force,
config: partialDiff,
});
toggleNotification({ type: 'success', message: response.data.message });
dispatch(getAllConfigDiff(toggleNotification, formatMessage, get));
dispatch(setLoadingState(false));
} catch (err) {
toggleNotification({ type: 'warning', message: formatMessage({ id: 'notification.error' }) });
dispatch(setLoadingState(false));
}
};
@ -84,15 +95,13 @@ export function setLoadingState(value) {
};
}
export function getAppEnv(toggleNotification) {
export function getAppEnv(toggleNotification, formatMessage, get) {
return async function(dispatch) {
try {
const envVars = await request('/config-sync/app-env', {
method: 'GET',
});
dispatch(setAppEnvInState(envVars));
const envVars = await get('/config-sync/app-env');
dispatch(setAppEnvInState(envVars.data));
} catch (err) {
toggleNotification({ type: 'warning', message: { id: 'notification.error' } });
toggleNotification({ type: 'warning', message: formatMessage({ id: 'notification.error' }) });
}
};
}

View File

@ -28,6 +28,7 @@
"ConfigDiff.Database": "Database",
"Buttons.Export": "Export",
"Buttons.DownloadConfig": "Download Config",
"Buttons.Import": "Import",
"FirstExport.Message": "Looks like this is your first time using config-sync for this project.",

View File

@ -2,4 +2,4 @@
'use strict';
require('../server/cli');
require('../dist/cli');

33
cypress.config.js Normal file
View File

@ -0,0 +1,33 @@
const { defineConfig } = require('cypress');
const fs = require('fs-extra');
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:1337',
specPattern: '**/*.cy.{js,ts,jsx,tsx}',
video: true,
defaultCommandTimeout: 30000,
requestTimeout: 30000,
setupNodeEvents(on, config) {
// implement node event listeners here.
// eslint-disable-next-line global-require
require('cypress-terminal-report/src/installLogsPrinter')(on);
on('task', {
deleteFolder(folderName) {
console.log(`deleting folder ${folderName}`);
return fs.remove(folderName)
.then(() => {
console.log(`folder ${folderName} deleted`);
return null;
})
.catch((err) => {
console.error(`error deleting folder ${folderName}`, err);
throw err;
});
},
});
},
},
});

129
cypress/support/commands.js Normal file
View File

@ -0,0 +1,129 @@
// <reference types="cypress" />
// ***********************************************
// This example commands.ts shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
Cypress.Commands.add('login', (path) => {
cy.visit('/');
cy.intercept({
method: 'GET',
url: '/admin/users/me',
}).as('sessionCheck');
cy.intercept({
method: 'GET',
url: '/admin/init',
}).as('adminInit');
// Wait for the initial request to complete.
cy.wait('@adminInit').its('response.statusCode').should('equal', 200);
// Wait for the form to render.
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
cy.get('body').then(($body) => {
// Login
if ($body.text().includes('Log in to your Strapi account')) {
cy.get('input[name="email"]').type('johndoe@example.com');
cy.get('input[name="password"]').type('Abc12345678');
cy.get('button[type="submit"]').click();
cy.wait('@sessionCheck').its('response.statusCode').should('equal', 200);
}
// Register
if ($body.text().includes('Credentials are only used to authenticate in Strapi')) {
cy.get('input[name="firstname"]').type('John');
cy.get('input[name="email"]').type('johndoe@example.com');
cy.get('input[name="password"]').type('Abc12345678');
cy.get('input[name="confirmPassword"]').type('Abc12345678');
cy.get('button[type="submit"]').click();
cy.wait('@sessionCheck').its('response.statusCode').should('equal', 200);
}
});
});
Cypress.Commands.add('navigateToInterface', (path) => {
cy.intercept({
method: 'GET',
url: '/config-sync/diff',
}).as('getConfigDiff');
cy.get('a[href="/admin/settings"]').click();
cy.get('a[href="/admin/settings/config-sync"]').click();
cy.wait('@getConfigDiff').its('response.statusCode').should('equal', 200);
});
Cypress.Commands.add('initialExport', (path) => {
cy.intercept({
method: 'POST',
url: '/config-sync/export',
}).as('exportConfig');
cy.get('button').contains('Make the initial export').click();
cy.get('button').contains('Yes, export').click();
cy.wait('@exportConfig').its('response.statusCode').should('equal', 200);
cy.contains('Config was successfully exported to config/sync/.');
});
Cypress.Commands.add('makeConfigChanges', (path) => {
// Change a setting in the UP advanced settings
cy.intercept({
method: 'PUT',
url: '/users-permissions/advanced',
}).as('saveUpAdvanced');
cy.get('a[href="/admin/settings/users-permissions/advanced-settings"]').click();
cy.get('input[name="unique_email"').click();
cy.get('button[type="submit"]').click();
cy.wait('@saveUpAdvanced').its('response.statusCode').should('equal', 200);
// Change a setting in the media library settings
cy.intercept({
method: 'PUT',
url: '/upload/settings',
}).as('saveMediaLibrarySettings');
cy.get('a[href="/admin/settings/media-library"]').click();
cy.get('input[name="responsiveDimensions"').click();
cy.get('button[type="submit"]').click();
cy.wait('@saveMediaLibrarySettings').its('response.statusCode').should('equal', 200);
// Change a setting in the email templates
cy.intercept({
method: 'PUT',
url: '/users-permissions/email-templates',
}).as('saveUpEmailTemplates');
cy.get('a[href="/admin/settings/users-permissions/email-templates"]').click();
cy.get('tbody tr').contains('Reset password').click();
cy.get('input[name="options.response_email"]').clear();
cy.get('input[name="options.response_email"]').type(`${Math.random().toString(36).substring(2, 15)}@example.com`);
cy.get('button[type="submit"]').click();
cy.wait('@saveUpEmailTemplates').its('response.statusCode').should('equal', 200);
});

22
cypress/support/e2e.js Normal file
View File

@ -0,0 +1,22 @@
// ***********************************************************
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
require('cypress-terminal-report/src/installLogsCollector')();
// Alternatively you can use CommonJS syntax:
// require('./commands')

13
dependabot.yml Normal file
View File

@ -0,0 +1,13 @@
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: daily
ignore:
- dependency-name: '\*'
update-types: ["version-update:semver-patch"]
groups:
strapi:
patterns:
- "@strapi/*"

52
docs/.github/workflows/deploy.yml vendored Normal file
View File

@ -0,0 +1,52 @@
name: Deploy
on:
workflow_dispatch:
push:
branches:
- main
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
environment:
name: docs.pluginpal.io
url: https://docs.pluginpal.io
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Docker
uses: actions/setup-node@v3
with:
node-version: '14'
- name: Build a Docker image
run: |
docker build \
-t pluginpal-docs:latest .
docker save -o pluginpal-docs-latest.tar pluginpal-docs:latest
- name: Transfer the Docker image to the Dokku server
uses: appleboy/scp-action@v0.1.3
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_CI_USERNAME }}
password: ${{ secrets.SSH_CI_PASSWORD }}
source: pluginpal-docs-latest.tar
target: /var/lib/dokku/data/storage/docs/docker-images
- name: Deploy the Dokku app based on the Docker image
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_CI_USERNAME }}
password: ${{ secrets.SSH_CI_PASSWORD }}
script_stop: true
script: |
sudo docker load -i /var/lib/dokku/data/storage/docs/docker-images/pluginpal-docs-latest.tar
DOCS_LATEST_IMAGE=$(sudo docker images --format "{{.ID}}" pluginpal-docs:latest)
sudo docker tag pluginpal-docs:latest pluginpal-docs:$DOCS_LATEST_IMAGE
dokku git:from-image docs pluginpal-docs:$DOCS_LATEST_IMAGE
sudo docker system prune --all --force

20
docs/.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

29
docs/Dockerfile Normal file
View File

@ -0,0 +1,29 @@
# syntax=docker/dockerfile:1
# Stage 1: Base image.
## Start with a base image containing NodeJS so we can build Docusaurus.
FROM node:18-alpine3.18 as base
## Disable colour output from yarn to make logs easier to read.
ENV FORCE_COLOR=0
## Enable corepack.
RUN corepack enable
## Set the working directory to `/opt/docusaurus`.
WORKDIR /opt/docusaurus
# Stage 2b: Production build mode.
FROM base as prod
## Set the working directory to `/opt/docusaurus`.
WORKDIR /opt/docusaurus
## Copy over the source code.
COPY . /opt/docusaurus/
## Install dependencies with `--immutable` to ensure reproducibility.
RUN yarn install
## Build the static site.
RUN yarn build
# Stage 3a: Serve with `docusaurus serve`.
FROM prod as serve
## Expose the port that Docusaurus will run on.
EXPOSE 3000
## Run the production server.
CMD ["yarn", "serve", "--host", "0.0.0.0", "--no-open"]

41
docs/README.md Normal file
View File

@ -0,0 +1,41 @@
# Website
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
### Installation
```
$ yarn
```
### Local Development
```
$ yarn start
```
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
### Build
```
$ yarn build
```
This command generates static content into the `build` directory and can be served using any static contents hosting service.
### Deployment
Using SSH:
```
$ USE_SSH=true yarn deploy
```
Not using SSH:
```
$ GIT_USER=<Your GitHub username> yarn deploy
```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

3
docs/babel.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};

View File

@ -0,0 +1,12 @@
---
slug: first-blog-post
title: First Blog Post
authors: [slorber, yangshun]
tags: [hola, docusaurus]
---
Lorem ipsum dolor sit amet...
<!-- truncate -->
...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

View File

@ -0,0 +1,44 @@
---
slug: long-blog-post
title: Long Blog Post
authors: yangshun
tags: [hello, docusaurus]
---
This is the summary of a very long blog post,
Use a `<!--` `truncate` `-->` comment to limit blog post size in the list view.
<!-- truncate -->
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

View File

@ -0,0 +1,24 @@
---
slug: mdx-blog-post
title: MDX Blog Post
authors: [slorber]
tags: [docusaurus]
---
Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).
:::tip
Use the power of React to create interactive blog posts.
:::
{/* truncate */}
For example, use JSX to create an interactive button:
```js
<button onClick={() => alert('button clicked!')}>Click me!</button>
```
<button onClick={() => alert('button clicked!')}>Click me!</button>

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View File

@ -0,0 +1,29 @@
---
slug: welcome
title: Welcome
authors: [slorber, yangshun]
tags: [facebook, hello, docusaurus]
---
[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).
Here are a few tips you might find useful.
<!-- truncate -->
Simply add Markdown files (or folders) to the `blog` directory.
Regular blog authors can be added to `authors.yml`.
The blog post date can be extracted from filenames, such as:
- `2019-05-30-welcome.md`
- `2019-05-30-welcome/index.md`
A blog post folder can be convenient to co-locate blog post images:
![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg)
The blog supports tags as well!
**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config.

23
docs/blog/authors.yml Normal file
View File

@ -0,0 +1,23 @@
yangshun:
name: Yangshun Tay
title: Front End Engineer @ Facebook
url: https://github.com/yangshun
image_url: https://github.com/yangshun.png
page: true
socials:
x: yangshunz
github: yangshun
slorber:
name: Sébastien Lorber
title: Docusaurus maintainer
url: https://sebastienlorber.com
image_url: https://github.com/slorber.png
page:
# customize the url of the author page at /blog/authors/<permalink>
permalink: '/all-sebastien-lorber-articles'
socials:
x: sebastienlorber
linkedin: sebastienlorber
github: slorber
newsletter: https://thisweekinreact.com

19
docs/blog/tags.yml Normal file
View File

@ -0,0 +1,19 @@
facebook:
label: Facebook
permalink: /facebook
description: Facebook tag description
hello:
label: Hello
permalink: /hello
description: Hello tag description
docusaurus:
label: Docusaurus
permalink: /docusaurus
description: Docusaurus tag description
hola:
label: Hola
permalink: /hola
description: Hola tag description

View File

@ -0,0 +1,30 @@
---
sidebar_label: 'Plugin config types'
displayed_sidebar: configSyncSidebar
slug: /api/plugin-config-types
---
# Plugin config types
When you're writing a plugin, which registers a content type, you might want to consider that content type as a config type as defined in the Config Sync specification.
## Register a config type programatically
You can register a config type by adding some code to the register function of your plugin.
```md title="register.js"
// Register the config type when using the config-sync plugin.
if (strapi.plugin('config-sync')) {
if (!strapi.plugin('config-sync').pluginTypes) {
strapi.plugin('config-sync').pluginTypes = [];
}
strapi.plugin('config-sync').pluginTypes.push({
configName: 'url-pattern',
queryString: 'plugin::webtools.url-pattern',
uid: 'code',
});
}
```
If you want to read more about what the different values of a config type actually mean please read the in depth [custom types](/config-types#custom-types) docs

View File

@ -0,0 +1,16 @@
---
sidebar_label: 'Custom types'
displayed_sidebar: configSyncSidebar
slug: /configuration/custom-types
---
# Custom types
With this setting you can register your own custom config types. This is an array which expects objects with at least the `configName`, `queryString` and `uid` properties. Read more about registering custom types in the [Custom config types](/config-types#custom-types) documentation.
| Name | Details |
| ---- | ------- |
| Key | `customTypes` |
| Required | false |
| Type | array |
| Default | `[]` |

View File

@ -0,0 +1,16 @@
---
sidebar_label: 'Excluded config'
displayed_sidebar: configSyncSidebar
slug: /configuration/excluded-config
---
# Excluded config
Specify the names of configs you want to exclude from the syncing process. By default the API tokens for users-permissions, which are stored in core_store, are excluded. This setting expects the config names to comply with the naming convention.
| Name | Details |
| ---- | ------- |
| Key | `excludedConfig` |
| Required | false |
| Type | array |
| Default | `['core-store.plugin_users-permissions_grant', 'core-store.plugin_upload_metrics', 'core-store.strapi_content_types_schema', 'core-store.ee_information',]` |

View File

@ -0,0 +1,22 @@
---
sidebar_label: 'Excluded types'
displayed_sidebar: configSyncSidebar
slug: /configuration/excluded-types
---
# Excluded types
This setting will exclude all the config from a given type from the syncing process. The config types are specified by the `configName` of the type.
For example:
```
excludedTypes: ['admin-role']
```
| Name | Details |
| ---- | ------- |
| Key | `excludedTypes` |
| Required | false |
| Type | array |
| Default | `[]` |

View File

@ -0,0 +1,20 @@
---
sidebar_label: 'Import on bootstrap'
displayed_sidebar: configSyncSidebar
slug: /configuration/import-on-bootstrap
---
# Import on bootstrap
Allows you to let the config be imported automaticly when strapi is bootstrapping (on `strapi start`).
:::danger
This setting can't be used locally and should be handled very carefully as it can unintendedly overwrite the changes in your database. **PLEASE USE WITH CARE**.
:::
| Name | Details |
| ---- | ------- |
| Key | `importOnBootstrap` |
| Required | false |
| Type | bool |
| Default | `false` |

View File

@ -0,0 +1,32 @@
---
sidebar_label: 'Introduction'
displayed_sidebar: configSyncSidebar
slug: /configuration
---
# 🔧 Configuration
The settings of the plugin can be overridden in the `config/plugins.js` file.
In the example below you can see how, and also what the default settings are.
```md title="config/plugins.js"
module.exports = ({ env }) => ({
// ...
'config-sync': {
enabled: true,
config: {
syncDir: "config/sync/",
minify: false,
soft: false,
importOnBootstrap: false,
customTypes: [],
excludedTypes: [],
excludedConfig: [
"core-store.plugin_users-permissions_grant",
"core-store.plugin_upload_metrics",
"core-store.strapi_content_types_schema",
"core-store.ee_information",
],
},
},
});
```

View File

@ -0,0 +1,16 @@
---
sidebar_label: 'Minify'
displayed_sidebar: configSyncSidebar
slug: /configuration/minify
---
# Minify
When enabled all the exported JSON files will be minified.
| Name | Details |
| ---- | ------- |
| Key | `minify` |
| Required | false |
| Type | bool |
| Default | `false` |

View File

@ -0,0 +1,16 @@
---
sidebar_label: 'Soft'
displayed_sidebar: configSyncSidebar
slug: /configuration/soft
---
# Soft
When enabled the import action will be limited to only create new entries. Entries to be deleted, or updated will be skipped from the import process and will remain in it's original state.
| Name | Details |
| ---- | ------- |
| Key | `soft` |
| Required | false |
| Type | bool |
| Default | `false` |

View File

@ -0,0 +1,16 @@
---
sidebar_label: 'Sync dir'
displayed_sidebar: configSyncSidebar
slug: /configuration/sync-dir
---
# Sync dir
The path for reading and writing the sync files.
| Name | Details |
| ---- | ------- |
| Key | `syncDir` |
| Required | true |
| Type | string |
| Default | `config/sync/` |

View File

@ -0,0 +1,13 @@
---
sidebar_label: 'Admin GUI'
displayed_sidebar: configSyncSidebar
slug: /admin-gui
---
# 🖥️ Admin panel (GUI)
This plugin ships with a React app which can be accessed from the settings page in Strapi admin panel. On this page you can pretty much do the same as you can from the CLI. You can import, export and see the difference between the config as found in the sync directory, and the config as found in the database.
**Pro tip:**
By clicking on one of the items in the diff table you can see the exact difference between sync dir and database in a git-style diff viewer.
![Config diff in admin](/img/assets/admin-diff-viewer.png)

View File

@ -0,0 +1,138 @@
---
sidebar_label: 'CLI'
displayed_sidebar: configSyncSidebar
slug: /cli
---
# 🔌 Command line interface (CLI)
Add the `config-sync` command as a script to the `package.json` of your Strapi project:
```
"scripts": {
// ...
"cs": "config-sync"
},
```
You can now run all the `config-sync` commands like this:
<Tabs groupId="yarn-npm">
<TabItem value="yarn" label="Yarn">
```
yarn cs --help
```
</TabItem>
<TabItem value="npm" label="NPM">
```
npm run cs -- --help
```
</TabItem>
</Tabs>
## ⬆️ Import ⬇️ Export
> _Command:_ `import` _Alias:_ `i`
>
> _Command:_ `export` _Alias:_ `e`
These commands are used to sync the config in your Strapi project.
_Example:_
<Tabs groupId="yarn-npm">
<TabItem value="yarn" label="Yarn">
```
yarn cs import
yarn cs export
```
</TabItem>
<TabItem value="npm" label="NPM">
```
npm run cs import
npm run cs export
```
</TabItem>
</Tabs>
:::info
When you're using `npm` to run these commands, please note that you need an extra `--` to forward the flags to the script.
More information about this topic can be found on the <a href="https://docs.npmjs.com/cli/commands/npm-run-script">NPM documentation</a>.
Example:
```
npm run cs import -- --yes
```
:::
### Flag: `-y`, `--yes`
Use this flag to skip the confirm prompt and go straight to syncing the config.
```bash
[command] --yes
```
### Flag: `-t`, `--type`
Use this flag to specify the type of config you want to sync.
```bash
[command] --type user-role
```
### Flag: `-p`, `--partial`
Use this flag to sync a specific set of configs by giving the CLI a comma-separated string of config names.
```bash
[command] --partial user-role.public,i18n-locale.en
```
### Flag: `-f`, `--force`
If you're using the soft setting to gracefully import config, you can use this flag to ignore the setting for the current command and forcefully import all changes anyway.
```bash
[command] --force
```
## ↔️ Diff
> _Command:_ `diff` | _Alias:_ `d`
This command is used to see the difference between the config as found in the sync directory, and the config as found in the database.
_Example:_
<Tabs groupId="yarn-npm">
<TabItem value="yarn" label="Yarn">
```
yarn cs diff
```
</TabItem>
<TabItem value="npm" label="NPM">
```
npm run cs diff
```
</TabItem>
</Tabs>
### Argument: `<single>`
Add a single config name as the argument of the `diff` command to see the difference of that single file in a git-style diff viewer.
_Example:_
<Tabs groupId="yarn-npm">
<TabItem value="yarn" label="Yarn">
```
yarn cs diff user-role.public
```
</TabItem>
<TabItem value="npm" label="NPM">
```
npm run cs diff user-role.public
```
</TabItem>
</Tabs>

View File

@ -0,0 +1,125 @@
---
sidebar_label: 'Config Types'
displayed_sidebar: configSyncSidebar
slug: /config-types
---
# 🚀 Config types
By default the plugin will track 4 (official) types.
To track your own custom types you can register them by setting some plugin config.
## Default types
These 4 types are by default registered in the sync process.
### Admin role
> Config name: `admin-role` | UID: `code` | Query string: `admin::role`
### User role
> Config name: `user-role` | UID: `type` | Query string: `plugin::users-permissions.role`
### Core store
> Config name: `core-store` | UID: `key` | Query string: `strapi::core-store`
### I18n locale
> Config name: `i18n-locale` | UID: `code` | Query string: `plugin::i18n.locale`
## Custom types
Your custom types can be registered through the `customTypes` plugin config. This is a setting that can be set in the `config/plugins.js` file in your project.
_Read more about the `config/plugins.js` file [here](/configuration)._
You can register a type by giving the `customTypes` array an object which contains at least the following 3 properties:
```
customTypes: [{
configName: 'webhook',
queryString: 'webhook',
uid: 'name',
}],
```
_The example above will register the Strapi webhook type._
### Config name
The name of the config type. This value will be used as the first part of the filename for all config of this type. It should be unique from the other types and is preferably written in kebab-case.
##### Key: `configName`
> `required:` YES | `type:` string
### Query string
This is the query string of the type. Each type in Strapi has its own query string you can use to programatically preform CRUD actions on the entries of the type. Often for custom types in Strapi the format is something like `api::custom-api.custom-type`.
##### Key: `queryString`
> `required:` YES | `type:` string
### UID
The UID represents a field on the registered type. The value of this field will act as a unique identifier to identify the entries across environments. Therefore it should be unique and preferably un-editable after initial creation.
Mind that you can not use an auto-incremental value like the `id` as auto-increment does not play nice when you try to match entries across different databases.
If you do not have a single unique value, you can also pass in an array of keys for a combined uid key. This is for example the case for all content types which use i18n features (An example config would be `uid: ['productId', 'locale']`).
##### Key: `uid`
> `required:` YES | `type:` string | string[]
### Relations
The relations array specifies the relations you want to include in the sync process.
This feature is used to sync the relations between `roles` and `permissions`. See https://github.com/boazpoolman/strapi-plugin-config-sync/blob/master/server/config/types.js#L16.
Example:
```
{
configName: 'admin-role',
queryString: 'admin::role',
uid: 'code',
relations: [{
queryString: 'admin::permission',
relationName: 'permissions',
parentName: 'role',
relationSortFields: ['action', 'subject'],
}],
},
```
##### Key: `relations`
> `required:` NO | `type:` array
### Components
This property can accept an array of component names from the type. Strapi Components can be included in the export/import process. With "." nested components can also be included in the process.
```
customTypes: [{
configName: 'webhook',
queryString: 'webhook',
uid: 'name',
components: ['ParentComponentA', 'ParentComponentA.ChildComponent', 'ParentComponentB']
}],
```
##### Key: `components`
> `required:` NO | `type:` array
### JSON fields
This property can accept an array of field names from the type. It is meant to specify the JSON fields on the type so the plugin can better format the field values when calculating the config difference.
##### Key: `jsonFields`
> `required:` NO | `type:` array

View File

@ -0,0 +1,68 @@
---
sidebar_label: 'Installation'
displayed_sidebar: configSyncSidebar
slug: /
---
# ⏳ Installation
:::prerequisites
Complete installation requirements are the exact same as for Strapi itself and can be found in the Strapi documentation.
**Supported Strapi versions:**
Strapi v5 use `strapi-plugin-config-sync@^3`
Strapi v4 use `strapi-plugin-config-sync@^1`
:::
Install the plugin in your Strapi project.
<Tabs groupId="yarn-npm">
<TabItem value="yarn" label="Yarn">
```
yarn add strapi-plugin-config-sync
```
</TabItem>
<TabItem value="npm" label="NPM">
```
npm install strapi-plugin-config-sync --save
```
</TabItem>
</Tabs>
Add the export path to the `watchIgnoreFiles` list in the `config/admin.js` file.
This way your app won't reload when you export the config in development.
```md title="config/admin.js"
module.exports = ({ env }) => ({
// ...
watchIgnoreFiles: [
'**/config/sync/**',
],
});
```
After successful installation you have to rebuild the admin UI so it'll include this plugin. To rebuild and restart Strapi run:
<Tabs groupId="yarn-npm">
<TabItem value="yarn" label="Yarn">
```
yarn build
yarn develop
```
</TabItem>
<TabItem value="npm" label="NPM">
```
npm run build
npm run develop
```
</TabItem>
</Tabs>
The **Config Sync** plugin should now appear in the **Settings** section of your Strapi app.
To start tracking your config changes you have to make the first export. This will dump all your configuration data to the `/config/sync` directory. You can export either through [the CLI](/cli) or [Strapi admin panel](/admin-gui)
Enjoy 🎉

View File

@ -0,0 +1,22 @@
---
sidebar_label: 'Motivation'
displayed_sidebar: configSyncSidebar
slug: /motivation
---
# 💡 Motivation
In Strapi we come across what I would call config types. These are models of which the records are stored in our database, just like content types. Though the big difference here is that your code often relies on the database records of these types.
Having said that, it makes sense that these records can be exported, added to git, and be migrated across environments. This way we can make sure we have all the data our code relies on, on each environment.
Examples of these types are:
- Admin roles _(admin::role)_
- User roles _(plugin::users-permissions.role)_
- Admin settings _(strapi::core-store)_
- I18n locale _(plugin::i18n.locale)_
This plugin gives you the tools to sync this data. You can export the data as JSON files on one env, and import them on every other env. By writing this data as JSON files you can easily track them in your version control system (git).
_With great power comes great responsibility - Uncle Ben_

View File

@ -0,0 +1,13 @@
---
sidebar_label: 'Naming convention'
displayed_sidebar: configSyncSidebar
slug: /naming-convention
---
# 🔍 Naming convention
All the config files written in the sync directory have the same naming convention. It goes as follows:
[config-type].[identifier].json
- `config-type` - Corresponds to the `configName` of the config type.
- `identifier` - Corresponds to the value of the `uid` field of the config type.

View File

@ -0,0 +1,33 @@
---
sidebar_label: 'Workflow'
displayed_sidebar: configSyncSidebar
slug: /workflow
---
# ⌨️ Usage / Workflow
This plugin works best when you use `git` for the version control of your Strapi project.
_The following workflows are assuming you're using `git`._
### Intro
All database records tracked with this plugin will be exported to JSON files. Once exported each change to the file or the record will be tracked. Meaning you can now do one of two things:
- Change the file(s), and run an import. You have now imported from filesystem -> database.
- Change the record(s), and run an export. You have now exported from database -> filesystem.
### Local development
When building a new feature locally for your Strapi project you'd use the following workflow:
- Build the feature.
- Export the config.
- Commit and push the files to git.
### Deployment
When deploying the newly created feature - to either a server, or a co-worker's machine - you'd use the following workflow:
- Pull the latest file changes to the environment.
- (Re)start your Strapi instance.
- Import the config.
## Production deployment
The production deployment will be the same as a regular deployment. You just have to be careful before running the import. Ideally making sure the are no open changes before you pull the new code to the environment.

View File

@ -0,0 +1,18 @@
---
sidebar_label: 'Generic update'
displayed_sidebar: configSyncSidebar
slug: /upgrading/generic-update
---
# Updating Config Sync
We are always working to make Config Sync better by fixing bugs and introducing new features. These changes will be released as minor or patch versions as defined in the Semantic Versioning specification.
## Bump a minor/patch version
When you're updating Config Sync you'll have to follow these steps:
1. Make sure there are no config changes before starting. Either export or import all staged changes.
2. Update the version of the `strapi-plugin-config-sync` package in your `package.json` using your package manager of choice (yarn/npm/pnpm)
3. After you've bumped the version make sure to export any new changes that are now shown. It is possible that new configs are introduced, or old ones are updated/removed.
4. You're now ready to push these changes an commit them to your source control!

147
docs/docusaurus.config.ts Normal file
View File

@ -0,0 +1,147 @@
import {themes as prismThemes} from 'prism-react-renderer';
import type {Config} from '@docusaurus/types';
import type * as Preset from '@docusaurus/preset-classic';
const config: Config = {
title: 'Strapi Config Sync',
tagline: "Documentation for the config-sync plugin for Strapi",
favicon: 'img/favicon.jpg',
plugins: [
'docusaurus-plugin-sass',
],
// Set the production url of your site here
url: 'https://docs.pluginpal.io',
// Set the /<baseUrl>/ pathname under which your site is served
// For GitHub pages deployment, it is often '/<projectName>/'
baseUrl: '/config-sync/',
// GitHub pages deployment config.
// If you aren't using GitHub pages, you don't need these.
organizationName: 'pluginpal', // Usually your GitHub org/user name.
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
// Even if you don't use internationalization, you can use this field to set
// useful metadata like html lang. For example, if your site is Chinese, you
// may want to replace "en" with "zh-Hans".
i18n: {
defaultLocale: 'en',
locales: ['en'],
},
// themes: ['@docusaurus/theme-live-codeblock', '@docusaurus/theme-mermaid'],
presets: [
[
'classic',
{
docs: {
routeBasePath: '/',
sidebarPath: './sidebars.ts',
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl:
'https://github.com/pluginpal/strapi-plugin-config-sync/tree/master/docs',
admonitions: {
keywords: [
// Admonitions defaults
'note',
'tip',
'info',
'caution',
'danger',
// Admonitions custom
'callout',
'prerequisites',
'strapi',
'warning',
],
},
},
blog: false,
sitemap: {
lastmod: 'date',
changefreq: 'weekly',
priority: 0.6,
// ignorePatterns: ['/tags/**'],
filename: 'sitemap.xml',
createSitemapItems: async (params) => {
const {defaultCreateSitemapItems, ...rest} = params;
const items = await defaultCreateSitemapItems(rest);
return items;
},
},
theme: {
customCss: './src/scss/__index.scss',
},
} satisfies Preset.Options,
],
],
themeConfig: {
// Replace with your project's social card
// image: 'img/docusaurus-social-card.jpg',
navbar: {
title: 'Strapi Config Sync',
logo: {
alt: 'Config Sync logo',
src: 'img/logo.png',
},
items: [
{
href: 'https://github.com/pluginpal/strapi-plugin-config-sync',
label: 'GitHub',
position: 'right',
},
],
},
footer: {
style: 'dark',
links: [
{
title: 'Community',
items: [
{
label: 'Discord',
href: 'https://discord.com/invite/strapi',
},
{
label: 'Forum',
href: 'https://forum.strapi.io/',
},
],
},
{
title: 'More',
items: [
{
label: 'Website',
href: 'https://www.pluginpal.io',
},
{
label: 'GitHub',
href: 'https://github.com/pluginpal',
},
],
},
],
},
algolia: {
appId: 'ADLP623G89',
apiKey: '8f91ceaf54e8e8db14479fd79a420a8c',
indexName: 'pluginpal',
},
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
},
} satisfies Preset.ThemeConfig,
};
export default config;

54
docs/package.json Normal file
View File

@ -0,0 +1,54 @@
{
"name": "pluginpal-docs",
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "3.5.2",
"@docusaurus/plugin-sitemap": "^3.5.2",
"@docusaurus/preset-classic": "3.5.2",
"@docusaurus/theme-live-codeblock": "^3.5.2",
"@docusaurus/theme-mermaid": "^3.5.2",
"@docusaurus/theme-search-algolia": "^3.5.2",
"@mdx-js/react": "^3.0.0",
"classnames": "^2.5.1",
"clsx": "^2.0.0",
"docusaurus-plugin-sass": "^0.2.5",
"prism-react-renderer": "^2.3.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"sass": "^1.78.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.5.2",
"@docusaurus/tsconfig": "3.5.2",
"@docusaurus/types": "3.5.2",
"typescript": "~5.5.2"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 3 chrome version",
"last 3 firefox version",
"last 5 safari version"
]
},
"engines": {
"node": ">=18.0"
}
}

69
docs/sidebars.ts Normal file
View File

@ -0,0 +1,69 @@
/**
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
// @ts-check
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
const sidebars = {
// By default, Docusaurus generates a sidebar from the docs folder structure
// tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
// But you can create a sidebar manually
configSyncSidebar: [
{
type: "category",
collapsed: false,
label: "🚀 Getting Started",
items: [
"getting-started/installation",
"getting-started/motivation",
"getting-started/cli",
"getting-started/admin-gui",
"getting-started/config-types",
"getting-started/workflow",
"getting-started/naming-convention",
// "dev-docs/usage-information",
],
},
{
type: "category",
collapsed: false,
label: "⚙️ Configuration",
items: [
"configuration/introduction",
"configuration/sync-dir",
"configuration/minify",
"configuration/import-on-bootstrap",
"configuration/custom-types",
"configuration/soft",
"configuration/excluded-types",
"configuration/excluded-config",
],
},
{
type: "category",
collapsed: false,
label: "📦 API",
items: [
"api/plugin-config-types",
],
},
{
type: "category",
collapsed: false,
label: "♻️ Upgrading",
items: [
"upgrading/generic-update",
],
},
],
};
module.exports = sidebars;

View File

@ -0,0 +1,18 @@
import React from 'react'
import clsx from 'clsx'
export default function ApiCall({
children,
noSideBySide = false,
}) {
return (
<div
className={clsx(
'api-call',
(noSideBySide && 'api-call--no-side-by-side'),
)}
>
{children}
</div>
);
}

View File

@ -0,0 +1,121 @@
import React from 'react';
import clsx from 'clsx';
export default function Badge({
children,
className,
link = '',
noLink = false,
variant = '',
...rest
}) {
const variantNormalized = variant.toLowerCase().replace(/\W/g, '');
return (
<span
className={clsx(
'badge',
'badge--feature',
(variantNormalized && `badge--${variantNormalized.toLowerCase()}`),
)}
{...rest}
>
{(noLink || !link) ? (
<>
{variant}
</>
) : (
<a className="badge__link" href={link}>
{variant}
</a>
)}
{children}
</span>
);
}
export function AlphaBadge(props) {
return (
<Badge
variant="Alpha"
{...props}
/>
);
}
export function BetaBadge(props) {
return (
<Badge
variant="Beta"
{...props}
/>
);
}
export function FutureBadge(props) {
return (
<Badge
variant="Future"
link="/dev-docs/configurations/features"
{...props}
/>
);
}
export function EnterpriseBadge(props) {
return (
<Badge
variant="Enterprise"
link="https://strapi.io/pricing-self-hosted"
{...props}
/>
);
}
export function CloudProBadge(props) {
return (
<Badge
variant="Strapi Cloud Pro"
link="https://strapi.io/pricing-cloud"
{...props}
/>
);
}
export function CloudTeamBadge(props) {
return (
<Badge
variant="Strapi Cloud Team"
link="https://strapi.io/pricing-cloud"
{...props}
/>
);
}
export function CloudDevBadge(props) {
return (
<Badge
variant="Strapi Cloud Dev"
link="https://strapi.io/pricing-cloud"
{...props}
/>
);
}
export function NewBadge(props) {
return (
<Badge
variant="New ✨"
{...props}
/>
);
}
export function UpdatedBadge(props) {
return (
<Badge
variant="Updated ️🖌"
{...props}
/>
);
}

View File

@ -0,0 +1,40 @@
import clsx from 'clsx';
import React from 'react';
import Link from '@docusaurus/Link';
import styles from './button.module.scss';
export function Button({
href,
to,
children,
className,
decorative,
size = '',
variant = 'primary',
...rest
}) {
const ButtonElement = (to ? Link : (href ? 'a' : 'button'));
return (
<ButtonElement
{...rest}
{...(!href ? {} : { href, target: '_blank' })}
{...(!to ? {} : { to })}
className={clsx(
'button',
(variant && styles[`button--${variant}`]),
(size && styles[`button--${size}`]),
styles.button,
styles[variant],
className,
)}
>
{children}
{decorative && (
<span className={styles.button__decorative}>
{decorative}
</span>
)}
</ButtonElement>
);
}

View File

@ -0,0 +1,96 @@
/** Component: Button */
@import '../../scss/_mixins.scss';
.button {
--strapi-button-background-color: var(--strapi-primary-600);
--strapi-button-border-color: var(--strapi-primary-600);
--strapi-button-border-radius: 4px;
--strapi-button-box-shadow: 0 0 0 transparent;
--strapi-button-color: #fff;
--strapi-button-font-size: 12px;
--strapi-button-font-weight: 600;
--strapi-button-line-height: 16px;
--strapi-button-position: relative;
--strapi-button-py: 7px;
--strapi-button-px: 15px;
--strapi-button-transition-property: color, background, border-color, box-shadow;
--strapi-button-hover-background-color: var(--strapi-primary-700);
--strapi-button-hover-border-color: var(--strapi-primary-700);
--strapi-button-hover-box-shadow: 0px 9px 10px rgba(44, 56, 148, 0.2475);
--strapi-button-hover-color: #fff;
--ifm-button-color: var(--strapi-button-color);
--ifm-button-background-color: var(--strapi-button-background-color);
--ifm-button-border-color: var(--strapi-button-border-color);
--ifm-button-border-radius: var(--strapi-button-border-radius);
--ifm-button-font-weight: var(--strapi-button-font-weight);
--ifm-button-padding-horizontal: var(--strapi-button-px);
--ifm-button-padding-vertical: var(--strapi-button-py);
--ifm-button-size-multiplier: 1;
--ifm-color-primary-darker: var(--strapi-primary-200);
--ifm-link-hover-color: var(--strapi-button-color);
--ifm-link-hover-decoration: none;
position: var(--strapi-button-position);
font-size: var(--strapi-button-font-size);
line-height: var(--strapi-button-line-height);
box-shadow: var(--strapi-button-box-shadow);
transition-property: var(--strapi-button-transition-property);
&__decorative {
position: absolute;
font-size: 32px;
line-height: 32px;
bottom: -16px;
right: -8px;
}
&:not(:disabled),
&:not([aria-disabled="true"]) {
&:focus, &:hover {
--strapi-button-box-shadow: var(--strapi-button-hover-box-shadow);
--strapi-button-background-color: var(--strapi-button-hover-background-color);
--strapi-button-border-color: var(--strapi-button-hover-border-color);
--strapi-button-color: var(--strapi-button-hover-color);
}
}
/** Sizes */
&--huge {
--strapi-button-border-radius: 6px;
--strapi-button-font-size: 15px;
--strapi-button-line-height: 23px;
--strapi-button-py: 11px;
--strapi-button-px: 71px;
}
/** Variants */
&--secondary {
--strapi-button-background-color: #f0f0ff;
--strapi-button-border-color: #d9d8ff;
--strapi-button-color: var(--strapi-primary-600);
--strapi-button-hover-background-color: var(--strapi-neutral-0);
--strapi-button-hover-border-color: #d9d8ff;
--strapi-button-hover-box-shadow: none;
--strapi-button-hover-color: var(--strapi-primary-600);
}
}
/** Dark mode */
@include dark {
.button {
/** Dark mode Variants */
&--secondary {
--strapi-button-background-color: var(--strapi-neutral-100);
--strapi-button-border-color: var(--strapi-neutral-200);
--strapi-button-hover-background-color: var(--strapi-neutral-0);
--strapi-button-hover-border-color: var(--strapi-neutral-200);
}
}
}

View File

@ -0,0 +1,107 @@
import React from 'react';
import clsx from 'clsx';
import Link from '@docusaurus/Link';
import styles from './card.module.scss';
import IconArrow from '@site/static/img/assets/icons/arrow-right.svg';
export function CardTitle({
as,
children,
className,
withArrow,
...rest
}) {
const TitleElement = (as || 'h3');
return (
<TitleElement
className={clsx(
styles.card__title,
className,
)}
{...rest}
>
{children}
{withArrow && (
<span className={styles.card__title__arrow}>
<IconArrow />
</span>
)}
</TitleElement>
);
}
export function CardDescription({
as,
className,
...rest
}) {
const DescriptionElement = (as || 'div');
return (
<DescriptionElement
className={clsx(
styles.card__description,
className,
)}
{...rest}
/>
);
}
export function CardImgBg({
className,
...rest
}) {
return (
<img
className={clsx(
styles['card__img-bg'],
className,
)}
{...rest}
/>
);
}
export function CardImg({
className,
...rest
}) {
return (
<img
className={clsx(
styles['card__img'],
className,
)}
{...rest}
/>
);
}
export function Card({
className,
href,
isContentDelimited,
to,
variant,
...rest
}) {
const asCallToAction = !!(href || to);
const CardElement = (to ? Link : (href ? 'a' : 'div'));
return (
<CardElement
{...(!href ? {} : { href, target: '_blank' })}
{...(!to ? {} : { to })}
className={clsx(
styles.card,
(asCallToAction && styles['card--cta']),
(isContentDelimited && styles['card--content-delimited']),
(variant && styles[`card--${variant}`]),
className,
)}
{...rest}
/>
);
}

View File

@ -0,0 +1,182 @@
/** Component: Card */
@import '../../scss/_mixins.scss';
:root {
--strapi-card-background: var(--strapi-neutral-0);
--strapi-card-border-color: #EDEDFF;
--strapi-card-border-radius: 10px;
--strapi-card-box-shadow: 0 0 0 transparent;
--strapi-card-content-delimited: 395px;
--strapi-card-img-border-width: 5px;
--strapi-card-img-border-radius: 5px 5px 0 0;
--strapi-card-img-bg-scale: 1;
--strapi-card-justify-content: center;
--strapi-card-position: relative;
--strapi-card-overflow: hidden;
--strapi-card-text-align: center;
--strapi-card-gap: var(--strapi-spacing-2);
--strapi-card-px: var(--strapi-spacing-6);
--strapi-card-py: var(--strapi-spacing-6);
--strapi-card-title-arrow-left: var(--strapi-card-gap);
--strapi-card-title-color: #1D1B84;
--strapi-card-title-font-size: 17px;
--strapi-card-title-font-weight: 700;
--strapi-card-title-line-height: 26px;
--strapi-card-description-color: #4E6294;
--strapi-card-description-font-size: 15px;
--strapi-card-description-line-height: 24px;
--strapi-card-hover-border-color: #D6D6FF;
--strapi-card-hover-img-bg-scale: 1.15;
}
.card {
position: var(--strapi-card-position);
overflow: var(--strapi-card-overflow);
background: var(--strapi-card-background);
border-radius: var(--strapi-card-border-radius);
border: 1px solid var(--strapi-card-border-color);
box-shadow: var(--strapi-card-box-shadow);
display: flex;
flex-direction: column;
gap: var(--strapi-card-gap);
align-items: stretch;
justify-content: var(--strapi-card-justify-content);
text-align: var(--strapi-card-text-align);
padding: var(--strapi-card-py) var(--strapi-card-px);
transition: all 0.2s ease;
&:focus, &:hover {
--strapi-card-border-color: var(--strapi-card-hover-border-color);
--strapi-card-title-arrow-left: var(--strapi-spacing-3);
--strapi-card-img-bg-scale: var(--strapi-card-hover-img-bg-scale);
}
&__title {
display: block;
color: var(--strapi-card-title-color);
font-size: var(--strapi-card-title-font-size);
font-weight: var(--strapi-card-title-font-weight);
line-height: var(--strapi-card-title-line-height);
margin: 0;
&:after {
content: none;
}
&__arrow {
display: inline-block;
line-height: 0;
margin-left: var(--strapi-card-title-arrow-left);
transition: margin-left 0.1s ease;
}
}
&__description {
--ifm-link-color: var(--strapi-card-description-color);
--ifm-link-decoration: underline;
color: var(--strapi-card-description-color);
opacity: 0.8;
font-size: var(--strapi-card-description-font-size);
line-height: var(--strapi-card-description-line-height);
}
&__img {
border-bottom: none;
box-shadow: 0 1px 10px 0 #7A78B61A;
}
&--cta {
--ifm-link-color: currentColor;
--strapi-card-background:
linear-gradient(
310deg,
rgba(168, 166, 255, 0.15) 1.16%,
rgba(226, 225, 255, 0.15) 69.23%
),
#FFFFFF
;
--strapi-card-text-align: left;
--strapi-card-gap: var(--strapi-spacing-2);
--strapi-card-title-font-size: 21px;
--strapi-card-title-font-weight: 600;
--strapi-card-title-line-height: 28px;
--ifm-link-decoration: none;
--ifm-link-hover-decoration: none;
}
&--content-delimited {
.card {
&__title,
&__description {
width: 100%;
max-width: var(--strapi-card-content-delimited);
margin-right: auto;
margin-left: auto;
}
}
}
}
/** Responsive */
@include medium-up {
:root {
--strapi-card-px: var(--strapi-spacing-8);
--strapi-card-py: var(--strapi-spacing-9);
}
.card {
&__title {
&__arrow {
transition: margin-left 0.2s ease;
will-change: margin-left;
}
}
&:focus, &:hover {
&.card--cta {
--strapi-card-border-color: #D6D6FF;
--strapi-card-box-shadow: 0px 1px 4px rgba(33, 33, 52, 0.1);
}
}
&--cta {
transition: all 0.2s ease;
will-change: border-color, box-shadow, color;
.card {
&__img {
transition: all 0.2s ease;
will-change: border-radius, transform;
transform:
scale(var(--strapi-card-img-scale, 1))
translate(var(--strapi-card-img-translate, '0, 0'))
;
}
}
}
}
}
/** Dark mode */
@include dark {
--strapi-card-border-color: var(--strapi-neutral-150);
--strapi-card-title-color: var(--strapi-netral-1000);
--strapi-card-description-color: var(--strapi-netral-1000);
--strapi-card-img-border-color: rgba(255, 255, 255, 0.5);
--strapi-card-hover-border-color: #49494D;
.card {
&--cta {
--strapi-card-background: var(--strapi-neutral-0);
&:focus, &:hover {
--strapi-card-border-color: #49494D;
--strapi-card-color: var(--strapi-neutral-1000);
--ifm-link-hover-color: var(--strapi-neutral-1000);
}
}
}
}

View File

@ -0,0 +1,15 @@
import React from 'react';
import clsx from 'clsx';
import styles from './container.module.scss';
export function Container({ className, ...rest }) {
return (
<div
className={clsx(
styles.container,
className,
)}
{...rest}
/>
);
}

View File

@ -0,0 +1,18 @@
/** Component: Container */
:root {
--strapi-container-px: var(--ifm-spacing-horizontal);
--strapi-container-mw: calc(863px + calc(var(--strapi-container-px) * 2));
}
.container {
display: flex;
flex-direction: column;
align-items: stretch;
margin-right: auto;
margin-left: auto;
padding-right: var(--strapi-container-px);
padding-left: var(--strapi-container-px);
max-width: var(--strapi-container-mw);
width: 100%;
}

View File

@ -0,0 +1,32 @@
import React from 'react'
import classNames from 'classnames';
export default function CustomDocCard(props) {
const { title, description, link, emoji, small = false } = props;
const linkClasses = classNames({
card: true,
cardContainer: true,
'padding--lg': !small,
'padding--md': small,
});
const cardClasses = classNames({
'custom-doc-card': true,
'margin-bottom--lg': !small,
'margin-bottom--sm': small,
'custom-doc-card--small': small,
});
return (
<article className={ cardClasses }>
<a className={ linkClasses }
href={ link }
>
<h2 className="text--truncate cardTitle" title={title}>
{emoji ? emoji : '📄️'} {title}
</h2>
<p className="text--truncate cardDescription" title={ description }>
{description}
</p>
</a>
</article>
);
}

View File

@ -0,0 +1,9 @@
import React from 'react';
export default function CustomDocCardsWrapper({ children }) {
return (
<div className="custom-cards-wrapper">
{children}
</div>
);
}

View File

@ -0,0 +1,81 @@
import clsx from 'clsx';
import React from 'react';
import styles from './features-list.module.scss';
import { LinkWithArrow } from '../LinkWithArrow/LinkWithArrow';
export function FeatureListItem({
children,
className,
href,
icon: Icon,
iconColor,
label,
to,
...rest
}) {
const ContentElement = ((href || to) ? LinkWithArrow : 'span');
const IconElement = ((href || to) ? 'a' : 'span');
return (
<li
className={clsx(
styles['features-list__item'],
className,
)}
>
{Icon && (
<IconElement
className={clsx(
styles['features-list__item__icon'],
(iconColor && styles[`features-list__item__icon--${iconColor}`]),
)}
href={href}
to={to}
{...(IconElement === 'a' ? { href: to || href } : {})}
>
<Icon />
</IconElement>
)}
<ContentElement
className={styles['features-list__item__content']}
href={href}
to={to}
{...rest}
>
{children || label}
</ContentElement>
</li>
);
}
export function FeaturesList({
className,
id,
icon,
iconColor,
items,
...rest
}) {
const defaultId = `featureListItem${Math.random()}`;
return (
<ul
className={clsx(
styles['features-list'],
className,
)}
{...rest}
>
{items?.map((featureListItem, featureListItemIndex) => {
return (
<FeatureListItem
key={`${id || defaultId}${featureListItemIndex}`}
icon={icon}
iconColor={iconColor}
{...featureListItem}
/>
);
})}
</ul>
);
}

View File

@ -0,0 +1,89 @@
/** Component: Features List */
@import '../../scss/_mixins.scss';
:root {
--strapi-features-list-gap: var(--strapi-spacing-2);
--strapi-features-list-margin: 0;
--strapi-features-list-py: var(--ifm-spacing-horizontal);
--strapi-features-list-px: 0;
--strapi-features-list-item-inner-gap: 8px;
--strapi-features-list-item-icon-background-color: var(--strapi-secondary-100);
--strapi-features-list-item-icon-border-color: #D4EDFF;
--strapi-features-list-item-icon-color: var(--strapi-secondary-500);
--strapi-features-list-item-icon-border-radius: 7px;
--strapi-features-list-item-icon-area: 32px;
--strapi-features-list-item-icon-size: 16px;
}
.features-list {
margin: var(--strapi-features-list-margin);
padding: var(--strapi-features-list-py) var(--strapi-features-list-px);
list-style: none;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: flex-start;
gap: var(--strapi-features-list-gap);
&__item {
display: flex;
align-items: center;
gap: var(--strapi-features-list-item-inner-gap);
&__icon {
background-color: var(--strapi-features-list-item-icon-background-color);
border: 1px solid var(--strapi-features-list-item-icon-border-color);
border-radius: var(--strapi-features-list-item-icon-border-radius);
color: var(--strapi-features-list-item-icon-color);
display: inline-flex;
align-items: center;
justify-content: center;
height: var(--strapi-features-list-item-icon-area);
width: var(--strapi-features-list-item-icon-area);
transition: all 0.2s ease;
&--green {
--strapi-features-list-item-icon-background-color: var(--strapi-success-100);
--strapi-features-list-item-icon-border-color: #DAF0D8;
--strapi-features-list-item-icon-color: var(--strapi-success-500);
}
svg {
height: var(--strapi-features-list-item-icon-size);
width: var(--strapi-features-list-item-icon-size);
}
}
}
}
/** Responsive */
@include medium-up {
:root {
--strapi-features-list-py: calc(var(--ifm-spacing-horizontal) * 2);
--strapi-features-list-px: var(--ifm-spacing-horizontal);
--strapi-features-list-item-inner-gap: 20px;
--strapi-features-list-item-icon-area: 40px;
}
}
/** Dark mode */
@include dark {
--strapi-features-list-item-icon-background-color: var(--strapi-secondary-500);
--strapi-features-list-item-icon-border-color: var(--strapi-secondary-600);
.features-list {
&__item {
&__icon {
--strapi-features-list-item-icon-color: #fff;
&--green {
--strapi-features-list-item-icon-background-color: var(--strapi-success-500);
--strapi-features-list-item-icon-border-color: var(--strapi-success-600);
}
}
}
}
}

View File

@ -0,0 +1,48 @@
import clsx from 'clsx';
import React from 'react';
import styles from './hero.module.scss';
export function HeroTitle({
className,
...rest
}) {
return (
<h1
className={clsx(
styles.hero__title,
className,
)}
{...rest}
/>
);
}
export function HeroDescription({
className,
...rest
}) {
return (
<div
className={clsx(
styles.hero__description,
className,
)}
{...rest}
/>
);
}
export function Hero({
className,
...rest
}) {
return (
<header
className={clsx(
styles.hero,
className,
)}
{...rest}
/>
);
}

View File

@ -0,0 +1,55 @@
/** Component: Hero */
@import '../../scss/_mixins.scss';
:root {
--strapi-hero-py: var(--strapi-spacing-6);
--strapi-hero-gap: var(--strapi-spacing-4);
--strapi-hero-title-color: #1D1B84;
--strapi-hero-title-font-size: var(--strapi-font-size-xl);
--strapi-hero-title-line-height: 1.25;
--strapi-hero-description-color: #4E6294;
--strapi-hero-description-font-size: var(--strapi-font-size-lg);
--strapi-hero-description-line-height: 1.25;
}
.hero {
padding-top: var(--strapi-hero-py);
padding-bottom: var(--strapi-hero-py);
text-align: center;
&__title {
color: var(--strapi-hero-title-color);
font-weight: 700;
font-size: var(--strapi-hero-title-font-size);
line-height: var(--strapi-hero-title-line-height);
letter-spacing: 0.4px;
margin: 0 0 var(--strapi-hero-gap);
}
&__description {
color: var(--strapi-hero-description-color);
font-size: var(--strapi-hero-description-font-size);
line-height: var(--strapi-hero-description-line-height);
margin: 0;
}
}
/** Responsive */
@include medium-up {
:root {
--strapi-hero-py: 40px;
--strapi-hero-title-font-size: 43px;
--strapi-hero-title-line-height: 56px;
--strapi-hero-description-font-size: 21px;
--strapi-hero-description-line-height: 28px;
}
}
/** Dark mode */
@include dark {
--strapi-hero-title-color: white;
--strapi-hero-description-color: white;
}

View File

@ -0,0 +1,70 @@
import clsx from 'clsx';
import Heading from '@theme/Heading';
import styles from './styles.module.css';
type FeatureItem = {
title: string;
Svg: React.ComponentType<React.ComponentProps<'svg'>>;
description: JSX.Element;
};
const FeatureList: FeatureItem[] = [
{
title: 'Easy to Use',
Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
description: (
<>
Docusaurus was designed from the ground up to be easily installed and
used to get your website up and running quickly.
</>
),
},
{
title: 'Focus on What Matters',
Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
description: (
<>
Docusaurus lets you focus on your docs, and we&apos;ll do the chores. Go
ahead and move your docs into the <code>docs</code> directory.
</>
),
},
{
title: 'Powered by React',
Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
description: (
<>
Extend or customize your website layout by reusing React. Docusaurus can
be extended while reusing the same header and footer.
</>
),
},
];
function Feature({title, Svg, description}: FeatureItem) {
return (
<div className={clsx('col col--4')}>
<div className="text--center">
<Svg className={styles.featureSvg} role="img" />
</div>
<div className="text--center padding-horiz--md">
<Heading as="h3">{title}</Heading>
<p>{description}</p>
</div>
</div>
);
}
export default function HomepageFeatures(): JSX.Element {
return (
<section className={styles.features}>
<div className="container">
<div className="row">
{FeatureList.map((props, idx) => (
<Feature key={idx} {...props} />
))}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,11 @@
.features {
display: flex;
align-items: center;
padding: 2rem 0;
width: 100%;
}
.featureSvg {
height: 200px;
width: 200px;
}

View File

@ -0,0 +1,36 @@
import clsx from 'clsx';
import React from 'react';
import Link from '@docusaurus/Link';
import IconArrow from '@site/static/img/assets/icons/arrow-right.svg';
import styles from './link-with-arrow.module.scss';
export function LinkWithArrow({
apart = false,
children,
className,
href,
to,
...rest
}) {
const LinkElement = (to ? Link : 'a');
return (
<LinkElement
className={clsx(
styles.lwa,
(apart && styles['lwa--apart']),
className,
)}
{...(href && { href, target: '_blank' })}
{...(to && { to })}
{...rest}
>
<span className={styles.lwa__content}>
{children}
</span>
<IconArrow
className={styles.lwa__icon}
/>
</LinkElement>
)
}

View File

@ -0,0 +1,53 @@
/** Component: Link with Arrow */
@import '../../scss/_mixins.scss';
:root {
--strapi-lwa-icon-ml: var(--strapi-spacing-2);
--strapi-lwa-font-size: 14px;
--strapi-lwa-font-weight: 500;
--strapi-lwa-line-height: 20px;
--strapi-lwa-hover-icon-ml: var(--strapi-spacing-3);
}
.lwa {
--ifm-link-color: #1D1B84;
--ifm-link-decoration: none;
--ifm-link-hover-color: #2825B8;
--ifm-link-hover-decoration: none;
font-size: var(--strapi-lwa-font-size);
font-weight: var(--strapi-lwa-font-weight);
line-height: var(--strapi-lwa-line-height);
transition: all 0.2s ease;
&:focus, &:hover {
--strapi-lwa-icon-ml: var(--strapi-lwa-hover-icon-ml);
}
&__icon {
display: inline-block;
line-height: 0;
margin-left: var(--strapi-lwa-icon-ml);
transition: margin-left 0.1s ease;
}
&--apart {
display: flex;
align-items: center;
.lwa {
&__content {
flex-grow: 1;
}
}
}
}
/** Dark mode */
@include dark {
.lwa {
--ifm-link-color: var(--strapi-neutral-1000);
--ifm-link-hover-color: var(--strapi-neutral-500);
}
}

View File

@ -0,0 +1,13 @@
import React from 'react'
export default function Request({
children,
title = 'Example request',
}) {
return (
<div className="api-call__request">
<div className="api-call__request__heading">{title}</div>
<div className="api-call__request__content">{children}</div>
</div>
);
}

View File

@ -0,0 +1,13 @@
import React from 'react'
export default function Response({
children,
title = 'Example response',
}) {
return (
<div className="api-call__response">
<div className="api-call__response__heading">{title}</div>
<div className="api-call__response__content">{children}</div>
</div>
);
}

View File

@ -0,0 +1,16 @@
import React from 'react';
export default function SubtleCallout({
children,
title,
emoji = '🤓',
}) {
return (
<div className="theme-admonition theme-admonition--callout theme-admonition--subtle">
<div className="theme-admonition__heading">
<span style={{fontWeight: 300}} className="theme-admonition__heading__icon">{ emoji } </span>{ title }
</div>
{ children }
</div>
);
}

View File

@ -0,0 +1,7 @@
export * from './Button/Button';
export * from './Card/Card';
export * from './Container/Container';
export * from './HomepageFeatures';
export * from './FeaturesList/FeaturesList';
export * from './Hero/Hero';
export * from './LinkWithArrow/LinkWithArrow';

View File

@ -0,0 +1,39 @@
/**
* Any CSS included here will be global.
* The classic template bundles Infima by default.
* Infima is a CSS framework designed to work well for content-centric websites.
*/
/** Core */
@import '_mixins.scss';
@import '_tokens.scss';
@import '_tokens-overrides.scss';
/** Base */
@import '_fonts.scss';
@import '_base.scss';
/** Components */
@import 'admonition.scss';
@import 'api-call.scss';
@import 'badge.scss';
@import 'breadcrumbs.scss';
@import 'card.scss';
@import 'columns.scss';
@import 'container.scss';
@import 'custom-doc-cards.scss';
@import 'details.scss';
@import 'footer.scss';
@import 'grid.scss';
@import 'images.scss';
@import 'markdown.scss';
@import 'medium-zoom.scss';
@import 'navbar.scss';
@import 'pagination-nav.scss';
@import 'search.scss';
@import 'scene.scss';
@import 'sidebar.scss';
@import 'table.scss';
@import 'tabs.scss';
@import 'table-of-contents.scss';
@import 'typography.scss';

44
docs/src/scss/_base.scss Normal file
View File

@ -0,0 +1,44 @@
/** Base: General Styles */
:root {
--custom-selection-background-color: var(--strapi-primary-600);
}
::selection {
background-color: var(--custom-selection-background-color);
color: var(--custom-selection-color, var(--strapi-neutral-0));
}
main {
article:first-child:not(.col):not(.custom-doc-card),
article:first-child:not(.col) + nav {
--custom-main-px: var(--strapi-spacing-0);
--custom-main-width: 683px;
max-width: calc(var(--custom-main-width) + calc(var(--strapi-spacing-4) * 2));
padding-left: var(--custom-main-px) !important;
padding-right: var(--custom-main-px) !important;
margin-left: auto;
margin-right: auto;
}
}
/** Responsive */
@include medium-up {
main {
article:first-child:not(.col),
article:first-child:not(.col) + nav {
--custom-main-px: var(--strapi-spacing-4);
}
}
}
/** Dark mode */
@include dark {
.container img[width="16"] {
/* 'Temporary' fix while we figure a way to display white icons in dark mode 😅 */
background-color: white;
border-radius: 2px;
padding: 1px;
}
}

12
docs/src/scss/_fonts.scss Normal file
View File

@ -0,0 +1,12 @@
/** Fonts */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;700&display=swap');
.font-poppins,
.font-poppins button,
.font-poppins input {
--custom-font-family: "Poppins", sans-serif;
--ifm-heading-font-family: "Poppins", sans-serif;
font-family: var(--custom-font-family);
}

View File

@ -0,0 +1,54 @@
/** Core: Sass Mixins */
/** Mixin: Responsive */
@mixin small-up {
@media (min-width: 769px) {
@content;
}
}
/** Mixin: Responsive */
@mixin medium-up {
@media (min-width: 997px) {
@content;
}
}
/** Mixin: Responsive */
@mixin large-up {
@media (min-width: 1536px) {
@content;
}
}
/** Mixin: Dark mode */
@mixin dark {
html[data-theme='dark'] {
@content;
}
}
/** Mixin: Light mode */
@mixin light {
html[data-theme='light'] {
@content;
}
}
/** Mixin: Transition */
@mixin transition {
transition: all 0.2s ease;
}
/** Mixin: Flex */
@mixin flex-row(
$gap: var(--strapi-spacing-2),
$align-items: center,
$justify-content: null
) {
display: flex;
flex-direction: row;
gap: $gap;
align-items: $align-items;
justify-content: $justify-content;
}

View File

@ -0,0 +1,80 @@
/** Core: Docusaurus/Infima Style Tokens Overrides */
:root {
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
--ifm-background-color: var(--strapi-neutral-0);
--ifm-color-content: var(--strapi-neutral-800);
--ifm-font-color-base: var(--strapi-neutral-800);
--ifm-color-content-secondary: var(--strapi-neutral-600);
--ifm-color-emphasis-100: var(--strapi-neutral-100);
--ifm-color-emphasis-200: var(--strapi-neutral-150);
--ifm-color-emphasis-300: var(--strapi-neutral-200);
--ifm-color-emphasis-400: var(--strapi-neutral-300);
--ifm-color-emphasis-500: var(--strapi-neutral-400);
--ifm-color-emphasis-600: var(--strapi-neutral-500);
--ifm-color-emphasis-700: var(--strapi-neutral-600);
--ifm-color-emphasis-800: var(--strapi-neutral-700);
--ifm-color-emphasis-900: var(--strapi-neutral-800);
--ifm-color-emphasis-1000: var(--strapi-neutral-1000);
--ifm-color-primary: var(--strapi-primary-600);
--ifm-color-primary-dark: var(--strapi-primary-700);
--ifm-color-primary-darker: var(--strapi-primary-800);
--ifm-color-primary-darkest: var(--strapi-primary-900);
--ifm-color-primary-light: var(--strapi-primary-500);
--ifm-color-primary-lighter: var(--strapi-primary-400);
--ifm-color-primary-lightest: var(--strapi-primary-300);
--ifm-link-color: var(--strapi-primary-600);
--ifm-link-decoration: none;
// --ifm-link-hover-decoration: none;
--ifm-paragraph-margin-bottom: var(--strapi-spacing-4);
--ifm-scrollbar-track-background-color: var(--strapi-neutral-100);
--ifm-scrollbar-thumb-background-color: var(--strapi-neutral-200);
--ifm-scrollbar-thumb-hover-background-color: var(--strapi-neutral-300);
--ifm-table-stripe-background: #DCDCE43E;
--ifm-toc-border-color: var(--strapi-neutral-150);
}
/* Dark mode */
@include dark {
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
--ifm-background-color: var(--strapi-neutral-0);
--ifm-color-content: var(--strapi-neutral-800);
--ifm-font-color-base: var(--strapi-neutral-800);
--ifm-color-emphasis-100: var(--strapi-neutral-100);
--ifm-color-emphasis-200: var(--strapi-neutral-150);
--ifm-color-emphasis-300: var(--strapi-neutral-200);
--ifm-color-emphasis-400: var(--strapi-neutral-300);
--ifm-color-emphasis-500: var(--strapi-neutral-400);
--ifm-color-emphasis-600: var(--strapi-neutral-500);
--ifm-color-emphasis-700: var(--strapi-neutral-600);
--ifm-color-emphasis-800: var(--strapi-neutral-700);
--ifm-color-emphasis-900: var(--strapi-neutral-800);
--ifm-color-emphasis-1000: var(--strapi-neutral-1000);
--ifm-color-primary: var(--strapi-primary-600);
--ifm-color-primary-dark: var(--strapi-primary-700);
--ifm-color-primary-darker: var(--strapi-primary-800);
--ifm-color-primary-darkest: var(--strapi-primary-900);
--ifm-color-primary-light: var(--strapi-primary-500);
--ifm-color-primary-lighter: var(--strapi-primary-400);
--ifm-color-primary-lightest: var(--strapi-primary-300);
--ifm-link-color: var(--strapi-primary-500);
--ifm-navbar-background-color: #212134;
--ifm-scrollbar-track-background-color: var(--strapi-neutral-100);
--ifm-scrollbar-thumb-background-color: var(--strapi-neutral-150);
--ifm-scrollbar-thumb-hover-background-color: var(--strapi-neutral-200);
--ifm-toc-border-color: var(--strapi-neutral-150);
}

121
docs/src/scss/_tokens.scss Normal file
View File

@ -0,0 +1,121 @@
/** Core: Strapi Design System Tokens */
:root {
/** Spacing */
--strapi-spacing-0: 0; // 0px
--strapi-spacing-1: 0.25rem; // 4px
--strapi-spacing-2: 0.5rem; // 8px
--strapi-spacing-3: 0.75rem; // 12px
--strapi-spacing-4: 1rem; // 16px
--strapi-spacing-5: 1.25rem; // 20px
--strapi-spacing-6: 1.5rem; // 24px
--strapi-spacing-7: 2rem; // 32px
--strapi-spacing-8: 2.5rem; // 40px
--strapi-spacing-9: 3rem; // 48px
--strapi-spacing-10: 3.5rem; // 56px
--strapi-spacing-11: 3.75rem; // 64px
/** Fonts */
--strapi-font-size-tiny: 0.625rem; // 10px
--strapi-font-size-xxs: 0.6875rem; // 11px
--strapi-font-size-xs: 0.75rem; // 12px
--strapi-font-size-ssm: 0.8125rem; // 13px
--strapi-font-size-sm: 0.875rem; // 14px
--strapi-font-size-md: 1rem; // 16px
--strapi-font-size-lg: 1.125rem; // 18px
--strapi-font-size-xl: 2rem; // 32px
/** Color: Neutral */
--strapi-neutral-1000: #181826;
--strapi-neutral-900: #212134;
--strapi-neutral-800: #32324D;
--strapi-neutral-700: #4A4A6A;
--strapi-neutral-600: #666687;
--strapi-neutral-500: #8E8EA9;
--strapi-neutral-400: #A5A5BA;
--strapi-neutral-300: #C0C0CF;
--strapi-neutral-200: #DCDCE4;
--strapi-neutral-150: #EAEAEF;
--strapi-neutral-100: #F6F6F9;
--strapi-neutral-0: #FFFFFF;
/** Color: Primary */
--strapi-primary-700: #271FE0;
--strapi-primary-600: #4945FF;
--strapi-primary-500: #7B79FF;
--strapi-primary-200: #D9D8FF;
--strapi-primary-100: #F0F0FF;
/** Color: Secondary */
--strapi-secondary-700: #006096;
--strapi-secondary-600: #0C75AF;
--strapi-secondary-500: #66B7F1;
--strapi-secondary-200: #B8E1FF;
--strapi-secondary-100: #EAF5FF;
/** Color: Success */
--strapi-success-700: #2F6846;
--strapi-success-600: #328048;
--strapi-success-500: #5CB176;
--strapi-success-300: #BBEFB5; /* not in Figma, used for code blocks */
--strapi-success-200: #C6F0C2;
--strapi-success-100: #EAFBE7;
/** Color: Danger */
--strapi-danger-800: #4B1113; /* not actually existing in Figma */
--strapi-danger-700: #B72B1A;
--strapi-danger-600: #D02B20;
--strapi-danger-500: #EE5E52;
--strapi-danger-200: #F5C0B8;
--strapi-danger-100: #FCECEA;
/** Color: Warning */
--strapi-warning-700: #BE5D01;
--strapi-warning-600: #D9822F;
--strapi-warning-500: #F29D41;
--strapi-warning-200: #FAE7B9;
--strapi-warning-100: #FDF4DC;
/** Color: Alternative */
--strapi-alternative-600: #9736E8;
--strapi-alternative-500: #AC73E6;
--strapi-alternative-200: #E0C1F4;
--strapi-alternative-100: #F6ECFC;
/** Color: Code */
--strapi-code-fluo-green: #50FA7B;
--strapi-code-green: #50FA7B;
--strapi-code-rose: #FF79C6;
--strapi-code-purple: #BD93F9;
--strapi-code-dark-blue: #6272A4;
/** Components */
--strapi-input-border-color: var(--strapi-neutral-200);
--strapi-input-border-width: 1px;
--strapi-input-border-style: solid;
--strapi-input-border:
var(--strapi-input-border-width)
var(--strapi-input-border-style)
var(--strapi-input-border-color)
;
}
/** Dark mode */
@include dark {
/** Dark Color: Neutral */
--strapi-neutral-1000: #FFFFFF; /* both 800 and 900 identical in Figma */
--strapi-neutral-900: #FFFFFF; /* both 800 and 900 identical in Figma */
--strapi-neutral-800: #FFFFFF;
--strapi-neutral-700: #EAEAEF;
--strapi-neutral-600: #666687;
--strapi-neutral-500: #c0c0cf;
--strapi-neutral-400: #A5A5BA; /* incorrecly named in Figma */
--strapi-neutral-300: #666687;
--strapi-neutral-200: #4A4A6A;
--strapi-neutral-150: #32324D;
--strapi-neutral-100: #181826;
--strapi-neutral-0: #212134;
/** Dark Color: Primary */
--strapi-primary-600: #7B79FF;
}

View File

@ -0,0 +1,189 @@
/** Component: Alert */
.theme-admonition {
--custom-admonition-background-color: var(--strapi-neutral-100);
--custom-admonition-border-color: var(--strapi-neutral-500);
--custom-admonition-border-radius: var(--strapi-spacing-0);
--custom-admonition-color: var(--strapi-neutral-800);
--custom-admonition-heading-color: var(--strapi-neutral-700);
--custom-admonition-heading-mb: var(--strapi-spacing-2);
--custom-admonition-mb: var(--strapi-spacing-4);
--custom-admonition-px: var(--strapi-spacing-4);
--custom-admonition-py: var(--strapi-spacing-4);
--ifm-link-decoration: none;
--ifm-link-color: var(--custom-admonition-heading-color);
--ifm-link-hover-color: var(--custom-admonition-heading-color);
background-color: var(--custom-admonition-background-color);
border-left: 4px solid var(--custom-admonition-border-color);
border-radius: var(--custom-admonition-border-radius);
color: var(--custom-admonition-color);
margin: 0 0 var(--custom-admonition-mb);
padding: var(--custom-admonition-py) var(--custom-admonition-px);
/** Alert Title element */
&__heading {
color: var(--custom-admonition-heading-color);
font-weight: var(--custom-admonition-heading-font-weight, 600);
margin-bottom: var(--custom-admonition-heading-mb);
}
h1, h2, h3, li, p, table {
code,
a code {
--custom-code-border-color: transparent;
--custom-code-background-color: var(--custom-admonition-code-background-color);
--custom-code-color: var(--custom-admonition-heading-color);
}
}
a {
font-weight: 700;
}
*:last-child {
margin-bottom: 0;
}
&--info,
&--callout,
&--prerequisites {
--custom-admonition-background-color: var(--strapi-neutral-100);
--custom-admonition-border-color: var(--strapi-neutral-500);
--custom-admonition-code-background-color: var(--strapi-neutral-200);
--custom-admonition-code-color: var(--strapi-primary-600);
--custom-admonition-heading-color: var(--strapi-neutral-700);
--custom-selection-background-color: var(--strapi-neutral-700);
--custom-selection-color: var(--strapi-neutral-100);
--ifm-link-color: var(--strapi-primary-600);
--ifm-link-hover-color: var(--strapi-primary-600);
}
&--note {
--custom-admonition-background-color: var(--strapi-secondary-100);
--custom-admonition-border-color: var(--strapi-secondary-500);
--custom-admonition-code-background-color: var(--strapi-secondary-200);
--custom-admonition-code-color: var(--strapi-secondary-600);
--custom-admonition-heading-color: var(--strapi-secondary-700);
--custom-selection-background-color: var(--strapi-secondary-700);
--custom-selection-color: var(--strapi-secondary-100);
}
&--tip,
&--success {
--custom-admonition-background-color: var(--strapi-success-100);
--custom-admonition-border-color: var(--strapi-success-500);
--custom-admonition-code-background-color: var(--strapi-success-200);
--custom-admonition-code-color: var(--strapi-success-600);
--custom-admonition-heading-color: var(--strapi-success-700);
--custom-selection-background-color: var(--strapi-success-700);
--custom-selection-color: var(--strapi-success-100);
}
&--caution {
--custom-admonition-background-color: var(--strapi-warning-100);
--custom-admonition-border-color: var(--strapi-warning-500);
--custom-admonition-code-background-color: var(--strapi-warning-200);
--custom-admonition-code-color: var(--strapi-warning-600);
--custom-admonition-heading-color: var(--strapi-warning-700);
--custom-selection-background-color: var(--strapi-warning-700);
--custom-selection-color: var(--strapi-warning-100);
}
&--danger,
&--warning {
--custom-admonition-background-color: var(--strapi-danger-100);
--custom-admonition-border-color: var(--strapi-danger-500);
--custom-admonition-code-background-color: var(--strapi-danger-200);
--custom-admonition-code-color: var(--strapi-danger-600);
--custom-admonition-heading-color: var(--strapi-danger-700);
--custom-selection-background-color: var(--strapi-danger-700);
--custom-selection-color: var(--strapi-danger-100);
}
&--strapi {
--custom-admonition-background-color: var(--strapi-primary-100);
--custom-admonition-border-color: var(--strapi-primary-500);
--custom-admonition-code-background-color: var(--strapi-primary-200);
--custom-admonition-code-color: var(--strapi-primary-600);
--custom-admonition-heading-color: var(--strapi-primary-700);
--custom-selection-background-color: var(--strapi-primary-700);
--custom-selection-color: var(--strapi-primary-100);
}
&--subtle {
border-radius: 8px;
border: none;
background-color: transparent;
border: solid 1px var(--strapi-neutral-500);
font-size: 90%;
}
}
@media screen and (min-width: 997px) {
[class*="sbs-column"]:nth-of-type(2) .theme-admonition--subtle {
margin-left: 80px;
}
}
/** Dark mode */
@include dark {
.theme-admonition {
// --custom-admonition-color: var(--strapi-neutral-150);
--custom-admonition-background-color: var(--strapi-neutral-100);
--custom-admonition-code-background-color: var(--strapi-neutral-200);
&--info,
&--callout,
&--prerequisites {
--custom-admonition-code-color: var(--strapi-primary-500);
--custom-admonition-heading-color: var(--strapi-neutral-500);
}
&--note {
--custom-admonition-code-color: var(--strapi-secondary-500);
--custom-admonition-heading-color: var(--strapi-secondary-500);
}
&--tip,
&--success {
--custom-admonition-code-color: var(--strapi-success-500);
--custom-admonition-heading-color: var(--strapi-success-500);
}
&--caution,
&--warning {
--custom-admonition-code-color: var(--strapi-warning-500);
--custom-admonition-heading-color: var(--strapi-warning-500);
}
&--danger {
--custom-admonition-code-color: var(--strapi-danger-500);
--custom-admonition-heading-color: var(--strapi-danger-500);
}
&--strapi {
--custom-admonition-code-color: var(--strapi-primary-500);
--custom-admonition-heading-color: var(--strapi-primary-500);
}
code,
a code {
--custom-code-border-color: transparent;
--custom-code-background-color: var(--custom-admonition-code-background-color);
--custom-code-color: var(--custom-admonition-code-color);
}
.strapi-iqb {
--strapi-iqb-background-color: var(--strapi-neutral-0);
}
}
}

119
docs/src/scss/api-call.scss Normal file
View File

@ -0,0 +1,119 @@
/** Component: API Call / Request / Response */
:root {
--custom-api-call-gap: var(--strapi-spacing-4);
--custom-api-call-heading-font-size: var(--strapi-font-size-sm);
--custom-api-call-heading-font-weight: 700;
--custom-api-call-heading-py: var(--strapi-spacing-2);
--custom-api-call-heading-px: var(--strapi-spacing-4);
--custom-api-call-radius: var(--strapi-spacing-2);
--custom-api-call-response-heading-background-color: var(--strapi-neutral-200);
--custom-api-call-response-content-background-color: var(--strapi-neutral-300);
--custom-api-call-request-heading-background-color: var(--strapi-neutral-100);
--custom-api-call-request-content-background-color: var(--strapi-neutral-150);
}
.api-call {
display: flex;
flex-direction: column;
align-items: stretch;
gap: var(--custom-api-call-gap);
margin-bottom: var(--custom-api-call-gap);
&__request__content,
&__request__heading,
&__response__content,
&__response__heading {
background-color: var(--custom-api-call-background-color);
color: var(--custom-api-call-color);
}
&__request,
&__response {
border-radius: var(--custom-api-call-radius);
&__heading,
&__content {
*:last-child {
margin-bottom: 0;
}
}
&__heading {
border-top-left-radius: var(--custom-api-call-radius);
border-top-right-radius: var(--custom-api-call-radius);
font-size: var(--custom-api-call-heading-font-size);
font-weight: var(--custom-api-call-heading-font-weight);
padding: var(--custom-api-call-heading-py) var(--custom-api-call-heading-px);
}
&__content {
border-bottom-left-radius: var(--custom-api-call-radius);
border-bottom-right-radius: var(--custom-api-call-radius);
padding: var(--custom-api-call-content-py) var(--custom-api-call-content-px);
}
}
&__request {
&__heading {
--custom-api-call-background-color: var(--custom-api-call-request-heading-background-color);
}
&__content {
--custom-api-call-background-color: var(--custom-api-call-request-content-background-color);
--custom-api-call-content-py: var(--custom-api-call-heading-px);
--custom-api-call-content-px: var(--custom-api-call-heading-px);
--custom-code-block-background-color: var(--custom-api-call-response-heading-background-color);
--custom-code-background-color: var(--custom-api-call-response-heading-background-color);
--custom-code-border-color: transparent;
--custom-code-color: currentColor;
}
}
&__response {
&__heading {
--custom-api-call-background-color: var(--custom-api-call-request-content-background-color);
}
&__content {
--custom-api-call-background-color: var(--custom-api-call-request-content-background-color);
--custom-code-block-background-color: var(--custom-api-call-response-content-background-color);
}
}
.theme-code-block {
border-radius: var(--custom-api-call-radius);
}
}
/** Dark mode */
@include dark {
--custom-api-call-color: var(--strapi-neutral-1000);
--custom-api-call-request-heading-background-color: var(--strapi-neutral-300);
--custom-api-call-request-content-background-color: var(--strapi-neutral-200);
--custom-api-call-response-heading-background-color: var(--strapi-neutral-150);
--custom-api-call-response-content-background-color: var(--strapi-neutral-100);
.api-call {
&__request {
&__content {
pre {
--custom-code-block-background-color: var(--custom-api-call-response-heading-background-color);
}
}
}
&__response {
&__content {
pre {
--custom-code-block-background-color: var(--custom-api-call-response-content-background-color);
}
}
}
}
}

124
docs/src/scss/badge.scss Normal file
View File

@ -0,0 +1,124 @@
/** Component: Feature Badges (alpha, beta) */
.badge {
--custom-badge-background-color: var(--strapi-neutral-200);
--custom-badge-color: var(--strapi-neutral-800);
--custom-badge-font-size: var(--strapi-font-size-sm);
--custom-selection-background-color: var(--custom-badge-color);
--ifm-badge-background-color: var(--custom-badge-background-color);
--ifm-badge-color: var(--custom-badge-color);
--ifm-badge-border-radius: var(--strapi-spacing-1);
--ifm-badge-padding-horizontal: var(--strapi-spacing-2);
--ifm-badge-padding-vertical: var(--strapi-spacing-2);
--ifm-font-weight-bold: var(--strapi-font-size-sm);
--ifm-link-color: var(--ifm-badge-color);
--ifm-link-hover-color: var(--ifm-badge-color);
position: relative;
top: var(--custom-badge-inside-titles-top);
font-size: var(--custom-badge-font-size);
font-weight: 400;
letter-spacing: 0.3px;
text-align: center;
&-link {
font-weight: 400;
}
&--alpha {
--custom-badge-background-color: rgb(255,230,228);
--custom-badge-color: rgb(219, 0, 22);
}
&--beta {
--custom-badge-background-color: rgb(246, 228, 253);
--custom-badge-color: rgb(160,25,240);
}
&--future {
--custom-badge-background-color: var(--strapi-danger-200);
--custom-badge-color: rgb(219, 0, 22);
}
&--enterprise {
--custom-badge-background-color: rgb(255, 241, 209);
--custom-badge-color: rgb(229, 136, 41);
}
&--strapicloudpro {
--custom-badge-background-color: rgb(55, 34, 254);
--custom-badge-color: #fff;
}
&--strapicloudteam {
--custom-badge-background-color: rgb(154, 53, 242);
--custom-badge-color: #fff;
}
&--strapiclouddev {
--custom-badge-background-color: rgb(44,170,73);
--custom-badge-color: #fff;
}
&--new {
--custom-badge-background-color: var(--strapi-warning-100);
--custom-badge-border-color: var(--strapi-warning-200);
--custom-badge-color: var(--strapi-warning-700);
--ifm-badge-padding-horizontal: 2px;
--ifm-badge-padding-vertical: 2px;
font-weight: 500;
font-size: 12px;
line-height: 20px;
letter-spacing: -0.5px;
text-transform: uppercase;
min-width: 52px;
border: 1px solid var(--custom-badge-border-color);
}
&--updated {
--custom-badge-background-color: var(--strapi-secondary-100);
--custom-badge-border-color: var(--strapi-secondary-200);
--custom-badge-color: var(--strapi-secondary-600);
--ifm-badge-padding-horizontal: 2px;
--ifm-badge-padding-vertical: 2px;
font-weight: 500;
font-size: 12px;
line-height: 20px;
letter-spacing: -0.5px;
text-transform: uppercase;
min-width: 52px;
border: 1px solid var(--custom-badge-border-color);
flex-shrink: 0;
}
}
h1 .badge {
--custom-badge-inside-titles-top: -.4em;
}
h2 .badge {
--custom-badge-inside-titles-top: -.3em;
}
/** Dark mode */
@include dark {
.badge {
&--new {
--custom-badge-background-color: var(--strapi-neutral-100);
--custom-badge-border-color: var(--strapi-warning-500);
--custom-badge-color: var(--strapi-warning-500);
}
&--updated {
--custom-badge-background-color: var(--strapi-neutral-100);
--custom-badge-border-color: var(--strapi-secondary-500);
--custom-badge-color: var(--strapi-secondary-500);
}
}
}

View File

@ -0,0 +1,42 @@
/** Component: Breadcrumbs */
.breadcrumbs {
--ifm-breadcrumb-item-background-active: transparent;
--ifm-breadcrumb-border-radius: var(--strapi-spacing-1);
--ifm-breadcrumb-color-active: var(--strapi-neutral-800);
--ifm-link-hover-color: var(--strapi-neutral-700);
--custom-breadcrumbs-font-size: var(--strapi-font-size-xs);
--custom-breadcrumbs-pt: var(--strapi-spacing-4);
--custom-breadcrumbs-item-caret-mx: var(--strapi-spacing-1);
padding: var(--custom-breadcrumbs-pt) 0 0;
&__item {
&:after {
--ifm-breadcrumb-spacing: var(--custom-breadcrumbs-item-caret-mx);
}
&--active {
font-weight: 700;
}
}
&__link {
background-color: transparent;
font-size: var(--custom-breadcrumbs-font-size);
@include transition;
&:any-link:hover {
--ifm-breadcrumb-item-background-active: var(--strapi-neutral-100);
}
}
}
@include medium-up {
.breadcrumbs {
--custom-breadcrumbs-pt: var(--strapi-spacing-6);
--custom-breadcrumbs-item-caret-mx: var(--strapi-spacing-3);
}
}

Some files were not shown because too many files have changed in this diff Show More