├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
├── config-diff.png
└── workflows
│ ├── deploy-docs.yml
│ ├── publish.yml
│ └── tests.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── admin
└── src
│ ├── components
│ ├── .gitkeep
│ ├── ActionButtons
│ │ └── index.jsx
│ ├── ConfigDiff
│ │ └── index.jsx
│ ├── ConfigList
│ │ ├── ConfigListRow
│ │ │ └── index.jsx
│ │ └── index.jsx
│ ├── ConfirmModal
│ │ └── index.jsx
│ ├── FirstExport
│ │ └── index.jsx
│ ├── Header
│ │ └── index.jsx
│ └── NoChanges
│ │ └── index.jsx
│ ├── config
│ ├── constants.js
│ └── logger.js
│ ├── containers
│ ├── App
│ │ └── index.jsx
│ ├── ConfigPage
│ │ └── index.jsx
│ └── Initializer
│ │ └── index.jsx
│ ├── helpers
│ ├── blob.js
│ ├── configureStore.js
│ ├── getTrad.js
│ ├── pluginId.js
│ └── prefixPluginTranslations.js
│ ├── index.cy.jsx
│ ├── index.js
│ ├── permissions.js
│ ├── state
│ ├── actions
│ │ └── Config.js
│ └── reducers
│ │ ├── Config
│ │ └── index.js
│ │ └── index.js
│ └── translations
│ ├── ar.json
│ ├── cs.json
│ ├── de.json
│ ├── en.json
│ ├── es.json
│ ├── fr.json
│ ├── id.json
│ ├── index.js
│ ├── it.json
│ ├── ko.json
│ ├── ms.json
│ ├── nl.json
│ ├── pl.json
│ ├── pt-BR.json
│ ├── pt.json
│ ├── ru.json
│ ├── sk.json
│ ├── th.json
│ ├── tr.json
│ ├── uk.json
│ ├── vi.json
│ ├── zh-Hans.json
│ └── zh.json
├── bin
└── config-sync
├── codecov.yml
├── cypress.config.js
├── cypress
└── support
│ ├── commands.js
│ └── e2e.js
├── dependabot.yml
├── docs
├── .github
│ └── workflows
│ │ └── deploy.yml
├── .gitignore
├── Dockerfile
├── README.md
├── babel.config.js
├── blog
│ ├── 2019-05-28-first-blog-post.md
│ ├── 2019-05-29-long-blog-post.md
│ ├── 2021-08-01-mdx-blog-post.mdx
│ ├── 2021-08-26-welcome
│ │ ├── docusaurus-plushie-banner.jpeg
│ │ └── index.md
│ ├── authors.yml
│ └── tags.yml
├── docs
│ ├── api
│ │ └── plugin-config-types.md
│ ├── configuration
│ │ ├── custom-types.md
│ │ ├── excluded-config.md
│ │ ├── excluded-types.md
│ │ ├── import-on-bootstrap.md
│ │ ├── introduction.md
│ │ ├── minify.md
│ │ ├── soft.md
│ │ └── sync-dir.md
│ ├── getting-started
│ │ ├── admin-gui.md
│ │ ├── cli.md
│ │ ├── config-types.md
│ │ ├── installation.md
│ │ ├── motivation.md
│ │ ├── naming-convention.md
│ │ └── workflow.md
│ └── upgrading
│ │ └── generic-update.md
├── docusaurus.config.ts
├── package.json
├── sidebars.ts
├── src
│ ├── components
│ │ ├── ApiCall.js
│ │ ├── Badge.js
│ │ ├── Button
│ │ │ ├── Button.jsx
│ │ │ └── button.module.scss
│ │ ├── Card
│ │ │ ├── Card.jsx
│ │ │ └── card.module.scss
│ │ ├── Container
│ │ │ ├── Container.jsx
│ │ │ └── container.module.scss
│ │ ├── CustomDocCard.js
│ │ ├── CustomDocCardsWrapper.js
│ │ ├── FeaturesList
│ │ │ ├── FeaturesList.jsx
│ │ │ └── features-list.module.scss
│ │ ├── Hero
│ │ │ ├── Hero.jsx
│ │ │ └── hero.module.scss
│ │ ├── HomepageFeatures
│ │ │ ├── index.tsx
│ │ │ └── styles.module.css
│ │ ├── LinkWithArrow
│ │ │ ├── LinkWithArrow.jsx
│ │ │ └── link-with-arrow.module.scss
│ │ ├── Request.js
│ │ ├── Response.js
│ │ ├── SubtleCallout.js
│ │ └── index.js
│ ├── scss
│ │ ├── __index.scss
│ │ ├── _base.scss
│ │ ├── _fonts.scss
│ │ ├── _mixins.scss
│ │ ├── _tokens-overrides.scss
│ │ ├── _tokens.scss
│ │ ├── admonition.scss
│ │ ├── api-call.scss
│ │ ├── badge.scss
│ │ ├── breadcrumbs.scss
│ │ ├── card.scss
│ │ ├── columns.scss
│ │ ├── container.scss
│ │ ├── custom-doc-cards.scss
│ │ ├── details.scss
│ │ ├── footer.scss
│ │ ├── grid.scss
│ │ ├── images.scss
│ │ ├── markdown.scss
│ │ ├── medium-zoom.scss
│ │ ├── navbar.scss
│ │ ├── pagination-nav.scss
│ │ ├── scene.scss
│ │ ├── search.scss
│ │ ├── sidebar.scss
│ │ ├── table-of-contents.scss
│ │ ├── table.scss
│ │ ├── tabs.scss
│ │ └── typography.scss
│ └── theme
│ │ ├── Admonition
│ │ └── index.js
│ │ └── MDXComponents.js
├── static
│ ├── .nojekyll
│ └── img
│ │ ├── assets
│ │ ├── admin-diff-viewer.png
│ │ └── icons
│ │ │ ├── ArrowClockwise.svg
│ │ │ ├── Browsers.svg
│ │ │ ├── CheckCircle.svg
│ │ │ ├── Clock.svg
│ │ │ ├── CreditCard.svg
│ │ │ ├── CrossCircle.svg
│ │ │ ├── Duplicate copy.svg
│ │ │ ├── Eye.svg
│ │ │ ├── Faders.svg
│ │ │ ├── Invoice.svg
│ │ │ ├── MapTrifold.svg
│ │ │ ├── ONHOLDCarretDown.svg
│ │ │ ├── ONHOLDCarretUp.svg
│ │ │ ├── Palette.svg
│ │ │ ├── add.svg
│ │ │ ├── add_circle.svg
│ │ │ ├── add_icon.svg
│ │ │ ├── after.svg
│ │ │ ├── api_tokens.svg
│ │ │ ├── application.svg
│ │ │ ├── arrow-right.svg
│ │ │ ├── back.svg
│ │ │ ├── carret.svg
│ │ │ ├── check_icon.svg
│ │ │ ├── chevron-left.svg
│ │ │ ├── chevron-right.svg
│ │ │ ├── clear.svg
│ │ │ ├── close-icon.svg
│ │ │ ├── code.svg
│ │ │ ├── code2.svg
│ │ │ ├── cog.svg
│ │ │ ├── content.svg
│ │ │ ├── content_manager.svg
│ │ │ ├── content_types_builder.svg
│ │ │ ├── crop.svg
│ │ │ ├── cross.svg
│ │ │ ├── ctb_boolean.svg
│ │ │ ├── ctb_component.svg
│ │ │ ├── ctb_date.svg
│ │ │ ├── ctb_dz.svg
│ │ │ ├── ctb_email.svg
│ │ │ ├── ctb_enum.svg
│ │ │ ├── ctb_json.svg
│ │ │ ├── ctb_media.svg
│ │ │ ├── ctb_number.svg
│ │ │ ├── ctb_password.svg
│ │ │ ├── ctb_relation.svg
│ │ │ ├── ctb_relation_1to1.svg
│ │ │ ├── ctb_relation_1tomany.svg
│ │ │ ├── ctb_relation_manyto1.svg
│ │ │ ├── ctb_relation_manytomany.svg
│ │ │ ├── ctb_relation_manyway.svg
│ │ │ ├── ctb_relation_oneway.svg
│ │ │ ├── ctb_richtext.svg
│ │ │ ├── ctb_richtextblocks.svg
│ │ │ ├── ctb_text.svg
│ │ │ ├── ctb_uid.svg
│ │ │ ├── delete.svg
│ │ │ ├── documentation-plugin.svg
│ │ │ ├── documentation.svg
│ │ │ ├── down.svg
│ │ │ ├── down2.svg
│ │ │ ├── download.svg
│ │ │ ├── drag.svg
│ │ │ ├── duplicate.svg
│ │ │ ├── edit.svg
│ │ │ ├── email_template.svg
│ │ │ ├── external_link.svg
│ │ │ ├── feather.svg
│ │ │ ├── globe.svg
│ │ │ ├── grid_view.svg
│ │ │ ├── image.svg
│ │ │ ├── link.svg
│ │ │ ├── list_view.svg
│ │ │ ├── lock.svg
│ │ │ ├── marketplace.svg
│ │ │ ├── media_library.svg
│ │ │ ├── minus.svg
│ │ │ ├── more.svg
│ │ │ ├── move.svg
│ │ │ ├── notifications.svg
│ │ │ ├── official-market.svg
│ │ │ ├── plugins.svg
│ │ │ ├── provider.svg
│ │ │ ├── releases.svg
│ │ │ ├── reorder.svg
│ │ │ ├── reset_icon.svg
│ │ │ ├── roles.svg
│ │ │ ├── roles_permissions.svg
│ │ │ ├── search.svg
│ │ │ ├── settings.svg
│ │ │ ├── up.svg
│ │ │ ├── up2.svg
│ │ │ ├── users.svg
│ │ │ ├── verified-marketplace.svg
│ │ │ ├── webhooks.svg
│ │ │ ├── world.svg
│ │ │ └── world_striked.svg
│ │ ├── docusaurus-social-card.jpg
│ │ ├── favicon.jpg
│ │ ├── logo.png
│ │ ├── undraw_docusaurus_mountain.svg
│ │ ├── undraw_docusaurus_react.svg
│ │ └── undraw_docusaurus_tree.svg
├── tsconfig.json
└── yarn.lock
├── jest.config.js
├── package.json
├── packup.config.ts
├── playground
├── .editorconfig
├── .env
├── .env.example
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .strapi
│ └── client
│ │ ├── app.js
│ │ └── index.html
├── README.md
├── __tests__
│ ├── cli.test.js
│ ├── helpers.js
│ └── import-on-boostrap.test.js
├── config
│ ├── admin.js
│ ├── api.js
│ ├── database.js
│ ├── middlewares.js
│ ├── plugins.js
│ └── server.js
├── database
│ └── migrations
│ │ └── .gitkeep
├── favicon.png
├── jest.config.js
├── jsconfig.json
├── package.json
├── public
│ ├── robots.txt
│ └── uploads
│ │ └── .gitkeep
├── src
│ ├── admin
│ │ ├── app.example.js
│ │ └── webpack.config.example.js
│ ├── api
│ │ ├── .gitkeep
│ │ ├── home
│ │ │ ├── content-types
│ │ │ │ └── home
│ │ │ │ │ └── schema.json
│ │ │ ├── controllers
│ │ │ │ └── home.js
│ │ │ ├── routes
│ │ │ │ └── home.js
│ │ │ └── services
│ │ │ │ └── home.js
│ │ └── page
│ │ │ ├── content-types
│ │ │ └── page
│ │ │ │ └── schema.json
│ │ │ ├── controllers
│ │ │ └── page.js
│ │ │ ├── routes
│ │ │ └── page.js
│ │ │ └── services
│ │ │ └── page.js
│ ├── extensions
│ │ └── .gitkeep
│ └── index.js
└── types
│ └── generated
│ ├── components.d.ts
│ └── contentTypes.d.ts
├── server
├── bootstrap.js
├── cli.js
├── config.js
├── config
│ ├── type.js
│ └── types.js
├── controllers
│ ├── config.js
│ └── index.js
├── index.js
├── register.js
├── routes
│ ├── admin.js
│ └── index.js
├── services
│ ├── index.js
│ └── main.js
├── utils
│ ├── getArrayDiff.js
│ ├── getObjectDiff.js
│ ├── index.js
│ └── queryFallBack.js
└── warnings.js
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = LF
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | **/public
3 | **/build
4 | **/dist
5 | **/config
6 | **/scripts
7 | **/docs
8 | **/playground
9 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes
2 |
3 | # Handle line endings automatically for files detected as text
4 | # and leave all files detected as binary untouched.
5 | * text=auto
6 |
7 | #
8 | # The above will handle all files NOT found below
9 | #
10 |
11 | #
12 | ## These files are text and should be normalized (Convert crlf => lf)
13 | #
14 |
15 | # source code
16 | *.php text
17 | *.css text
18 | *.sass text
19 | *.scss text
20 | *.less text
21 | *.styl text
22 | *.js text eol=lf
23 | *.coffee text
24 | *.json text
25 | *.htm text
26 | *.html text
27 | *.xml text
28 | *.svg text
29 | *.txt text
30 | *.ini text
31 | *.inc text
32 | *.pl text
33 | *.rb text
34 | *.py text
35 | *.scm text
36 | *.sql text
37 | *.sh text
38 | *.bat text
39 |
40 | # templates
41 | *.ejs text
42 | *.hbt text
43 | *.jade text
44 | *.haml text
45 | *.hbs text
46 | *.dot text
47 | *.tmpl text
48 | *.phtml text
49 |
50 | # git config
51 | .gitattributes text
52 | .gitignore text
53 | .gitconfig text
54 |
55 | # code analysis config
56 | .jshintrc text
57 | .jscsrc text
58 | .jshintignore text
59 | .csslintrc text
60 |
61 | # misc config
62 | *.yaml text
63 | *.yml text
64 | .editorconfig text
65 |
66 | # build config
67 | *.npmignore text
68 | *.bowerrc text
69 |
70 | # Heroku
71 | Procfile text
72 | .slugignore text
73 |
74 | # Documentation
75 | *.md text
76 | LICENSE text
77 | AUTHORS text
78 |
79 |
80 | #
81 | ## These files are binary and should be left untouched
82 | #
83 |
84 | # (binary is a macro for -text -diff)
85 | *.png binary
86 | *.jpg binary
87 | *.jpeg binary
88 | *.gif binary
89 | *.ico binary
90 | *.mov binary
91 | *.mp4 binary
92 | *.mp3 binary
93 | *.flv binary
94 | *.fla binary
95 | *.swf binary
96 | *.gz binary
97 | *.zip binary
98 | *.7z binary
99 | *.ttf binary
100 | *.eot binary
101 | *.woff binary
102 | *.pyc binary
103 | *.pdf binary
104 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🐛 Bug Report
3 | about: Create a report to help improve this plugin
4 | ---
5 |
6 |
9 |
10 | ## Bug report
11 |
12 | ### Describe the bug
13 |
14 | A clear and concise description of what the bug is.
15 |
16 | ### Steps to reproduce the behavior
17 |
18 | 1. Go to '...'
19 | 2. Click on '....'
20 | 3. Scroll down to '....'
21 | 4. See error
22 |
23 | ### Expected behavior
24 |
25 | A clear and concise description of what you expected to happen.
26 |
27 | ### Screenshots
28 |
29 | If applicable, add screenshots to help explain your problem.
30 |
31 | ### Code snippets
32 |
33 | If applicable, add code samples to help explain your problem.
34 |
35 | ### System
36 |
37 | - Node.js version:
38 | - NPM version:
39 | - Strapi version:
40 | - Plugin version:
41 | - Database:
42 | - Operating system:
43 |
44 | ### Additional context
45 |
46 | Add any other context about the problem here.
47 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🚀 Feature Request
3 | about: Suggest an idea to help make this plugin even better!
4 | ---
5 |
6 |
9 |
10 | ## Feature request
11 |
12 | ### Summary
13 |
14 | Quick summary what's this feature request about.
15 |
16 | ### Why is it needed?
17 |
18 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
19 |
20 | ### Suggested solution(s)
21 |
22 | A clear and concise description of what you want to happen.
23 |
24 | ### Related issue(s)/PR(s)
25 |
26 | Let us know if this is related to any issue/pull request.
27 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
11 |
12 | ### What does it do?
13 |
14 | Describe the technical changes you did.
15 |
16 | ### Why is it needed?
17 |
18 | Describe the issue you are solving.
19 |
20 | ### How to test it?
21 |
22 | Provide information about the environment and the path to verify the behaviour.
23 |
24 | ### Related issue(s)/PR(s)
25 |
26 | Let us know if this is related to any issue/pull request
27 |
--------------------------------------------------------------------------------
/.github/config-diff.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/.github/config-diff.png
--------------------------------------------------------------------------------
/.github/workflows/deploy-docs.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Docs
2 |
3 | on:
4 | workflow_dispatch:
5 | release:
6 | types: [published]
7 |
8 | jobs:
9 | deploy:
10 | name: Deploy
11 | runs-on: ubuntu-latest
12 | environment:
13 | name: docs.pluginpal.io
14 | url: https://docs.pluginpal.io/config-sync
15 | steps:
16 | - name: Checkout repository
17 | uses: actions/checkout@v4
18 |
19 | - name: Set up Docker
20 | uses: actions/setup-node@v4
21 | with:
22 | node-version: '14'
23 |
24 | - name: Build a Docker image
25 | run: |
26 | cd docs
27 | docker build \
28 | -t docs-config-sync:latest .
29 | docker save -o ../docs-config-sync-latest.tar docs-config-sync:latest
30 |
31 | - name: Transfer the Docker image to the Dokku server
32 | uses: appleboy/scp-action@v0.1.3
33 | with:
34 | host: ${{ secrets.SSH_HOST }}
35 | username: ${{ secrets.SSH_CI_USERNAME }}
36 | password: ${{ secrets.SSH_CI_PASSWORD }}
37 | source: docs-config-sync-latest.tar
38 | target: /var/lib/dokku/data/storage/docs/docker-images
39 |
40 | - name: Deploy the Dokku app based on the Docker image
41 | uses: appleboy/ssh-action@v0.1.10
42 | with:
43 | host: ${{ secrets.SSH_HOST }}
44 | username: ${{ secrets.SSH_CI_USERNAME }}
45 | password: ${{ secrets.SSH_CI_PASSWORD }}
46 | script_stop: true
47 | script: |
48 | sudo docker load -i /var/lib/dokku/data/storage/docs/docker-images/docs-config-sync-latest.tar
49 | DOCS_CONFIG_SYNC_LATEST_IMAGE=$(sudo docker images --format "{{.ID}}" docs-config-sync:latest)
50 | sudo docker tag docs-config-sync:latest docs-config-sync:$DOCS_CONFIG_SYNC_LATEST_IMAGE
51 | dokku git:from-image docs-config-sync docs-config-sync:$DOCS_CONFIG_SYNC_LATEST_IMAGE
52 | sudo docker system prune --all --force
53 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to NPM
2 | on:
3 | release:
4 | types: [published]
5 | jobs:
6 | publish:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout
10 | uses: actions/checkout@v4
11 | - name: Setup Node.js
12 | uses: actions/setup-node@v4
13 | with:
14 | always-auth: true
15 | node-version: 18
16 | cache: 'yarn'
17 | registry-url: 'https://registry.npmjs.org/'
18 | - name: Install dependencies
19 | run: yarn install --frozen-lockfile
20 | - name: Build the plugin
21 | run: yarn build
22 | - name: Get the release tag version
23 | id: get_version
24 | run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
25 | - name: Set package version
26 | run: yarn version --new-version "${{ steps.get_version.outputs.VERSION }}" --no-git-tag-version
27 | - name: Publish package
28 | run: yarn publish --access public
29 | env:
30 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
31 | - name: Push version bump
32 | uses: stefanzweifel/git-auto-commit-action@v4
33 | with:
34 | commit_message: 'chore: Bump version to ${{ steps.get_version.outputs.VERSION }}'
35 | file_pattern: 'package.json'
36 | branch: master
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Don't check auto-generated stuff into git
2 | coverage
3 | node_modules
4 | stats.json
5 | package-lock.json
6 | files
7 |
8 | # Cruft
9 | .DS_Store
10 | npm-debug.log
11 | .idea
12 |
13 | # Production build
14 | build
15 | dist
16 | bundle
17 |
18 | # Cypress
19 | cypress/screenshots/
20 | cypress/videos/
21 | cypress/downloads/
22 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Copyright (c) 2021 Boaz Poolman.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/admin/src/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/admin/src/components/.gitkeep
--------------------------------------------------------------------------------
/admin/src/components/ConfigList/ConfigListRow/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Tr, Td, Checkbox, Typography } from '@strapi/design-system';
3 |
4 | const CustomRow = ({ row, checked, updateValue, ...props }) => {
5 | const { configName, configType, state, onClick } = row;
6 |
7 | const stateStyle = (stateStr) => {
8 | const style = {
9 | display: 'inline-flex',
10 | padding: '0 10px',
11 | borderRadius: '12px',
12 | height: '24px',
13 | alignItems: 'center',
14 | fontWeight: '500',
15 | };
16 |
17 | if (stateStr === 'Only in DB') {
18 | style.backgroundColor = '#cbf2d7';
19 | style.color = '#1b522b';
20 | }
21 |
22 | if (stateStr === 'Only in sync dir') {
23 | style.backgroundColor = '#f0cac7';
24 | style.color = '#3d302f';
25 | }
26 |
27 | if (stateStr === 'Different') {
28 | style.backgroundColor = '#e8e6b7';
29 | style.color = '#4a4934';
30 | }
31 |
32 | return style;
33 | };
34 |
35 | return (
36 |
{
39 | if (e.target.type !== 'checkbox') {
40 | onClick(configType, configName);
41 | }
42 | }}
43 | style={{ cursor: 'pointer' }}
44 | >
45 |
46 |
51 | |
52 | props.onClick(e)}>
53 | {configName}
54 | |
55 | props.onClick(e)}>
56 | {configType}
57 | |
58 | props.onClick(e)}>
59 | {state}
60 | |
61 |
62 | );
63 | };
64 |
65 | export default CustomRow;
66 |
--------------------------------------------------------------------------------
/admin/src/components/FirstExport/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useIntl } from 'react-intl';
3 | import { useDispatch } from 'react-redux';
4 | import { getFetchClient, useNotification } from '@strapi/strapi/admin';
5 | import { Button, EmptyStateLayout } from '@strapi/design-system';
6 | import { EmptyDocuments } from '@strapi/icons/symbols';
7 |
8 |
9 | import { exportAllConfig } from '../../state/actions/Config';
10 | import ConfirmModal from '../ConfirmModal';
11 |
12 | const FirstExport = () => {
13 | const { post, get } = getFetchClient();
14 | const { toggleNotification } = useNotification();
15 | const dispatch = useDispatch();
16 | const { formatMessage } = useIntl();
17 |
18 | return (
19 |
20 | dispatch(exportAllConfig([], toggleNotification, formatMessage, post, get))}
26 | trigger={(
27 |
28 | )}
29 | />
30 | )}
31 | icon={}
32 | />
33 |
34 | );
35 | };
36 |
37 | export default FirstExport;
38 |
--------------------------------------------------------------------------------
/admin/src/components/Header/index.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * HeaderComponent
4 | *
5 | */
6 |
7 | import React, { memo } from 'react';
8 | import { useIntl } from 'react-intl';
9 |
10 | import { Layouts } from '@strapi/admin/strapi-admin';
11 | import { Box } from '@strapi/design-system';
12 |
13 | const HeaderComponent = () => {
14 | const { formatMessage } = useIntl();
15 |
16 | return (
17 |
18 |
22 |
23 | );
24 | };
25 |
26 | export default memo(HeaderComponent);
27 |
--------------------------------------------------------------------------------
/admin/src/components/NoChanges/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { EmptyStateLayout } from '@strapi/design-system';
3 | import { useIntl } from 'react-intl';
4 | import { EmptyDocuments } from '@strapi/icons/symbols';
5 |
6 | const NoChanges = () => {
7 | const { formatMessage } = useIntl();
8 | return (
9 | }
12 | />
13 | );
14 | };
15 |
16 | export default NoChanges;
17 |
--------------------------------------------------------------------------------
/admin/src/config/constants.js:
--------------------------------------------------------------------------------
1 | export const __DEBUG__ = true; // TODO: set actual env.
2 |
--------------------------------------------------------------------------------
/admin/src/config/logger.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | blacklist: [
3 | 'REDUX_STORAGE_SAVE',
4 | 'REDUX_STORAGE_LOAD',
5 | ],
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/admin/src/containers/App/index.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * This component is the skeleton around the actual pages, and should only
4 | * contain code that should be seen on all pages. (e.g. navigation bar)
5 | *
6 | */
7 |
8 | import React from 'react';
9 | import { Provider } from 'react-redux';
10 | import { Page } from '@strapi/strapi/admin';
11 |
12 | import pluginPermissions from '../../permissions';
13 | import Header from '../../components/Header';
14 | import { store } from "../../helpers/configureStore";
15 | import ConfigPage from '../ConfigPage';
16 |
17 | const App = () => {
18 | return (
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 | };
27 |
28 | export default App;
29 |
--------------------------------------------------------------------------------
/admin/src/containers/ConfigPage/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 | import { Map } from 'immutable';
4 | import {
5 | Box,
6 | Alert,
7 | Typography,
8 | } from '@strapi/design-system';
9 | import { useNotification } from '@strapi/strapi/admin';
10 | import { getFetchClient, Layouts } from '@strapi/admin/strapi-admin';
11 | import { useIntl } from 'react-intl';
12 |
13 | import { getAllConfigDiff, getAppEnv } from '../../state/actions/Config';
14 | import ConfigList from '../../components/ConfigList';
15 | import ActionButtons from '../../components/ActionButtons';
16 |
17 | const ConfigPage = () => {
18 | const { toggleNotification } = useNotification();
19 | const { get } = getFetchClient();
20 | const { formatMessage } = useIntl();
21 |
22 | const dispatch = useDispatch();
23 | const isLoading = useSelector((state) => state.getIn(['config', 'isLoading'], Map({})));
24 | const configDiff = useSelector((state) => state.getIn(['config', 'configDiff'], Map({})));
25 | const appEnv = useSelector((state) => state.getIn(['config', 'appEnv', 'env']));
26 |
27 | useEffect(() => {
28 | dispatch(getAllConfigDiff(toggleNotification, formatMessage, get));
29 | dispatch(getAppEnv(toggleNotification, formatMessage, get));
30 | }, []);
31 |
32 | return (
33 |
34 | {appEnv === 'production' && (
35 |
36 |
37 | You're in the production environment
38 | Please be careful when syncing your config in production.
39 | Make sure you are not overriding critical config changes on import.
40 |
41 |
42 | )}
43 |
44 |
45 |
46 | );
47 | };
48 |
49 | export default ConfigPage;
50 |
--------------------------------------------------------------------------------
/admin/src/containers/Initializer/index.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Initializer
4 | *
5 | */
6 |
7 | import { useEffect, useRef } from 'react';
8 | import PropTypes from 'prop-types';
9 | import pluginId from '../../helpers/pluginId';
10 |
11 | const Initializer = ({ updatePlugin }) => {
12 | const ref = useRef();
13 | ref.current = updatePlugin;
14 |
15 | useEffect(() => {
16 | ref.current(pluginId, 'isReady', true);
17 | }, []);
18 |
19 | return null;
20 | };
21 |
22 | Initializer.propTypes = {
23 | updatePlugin: PropTypes.func.isRequired,
24 | };
25 |
26 | export default Initializer;
27 |
--------------------------------------------------------------------------------
/admin/src/helpers/blob.js:
--------------------------------------------------------------------------------
1 | export function b64toBlob(dataURI, type) {
2 | const byteString = atob(dataURI);
3 | const ab = new ArrayBuffer(byteString.length);
4 | const ia = new Uint8Array(ab);
5 | for (let i = 0; i < byteString.length; i++) {
6 | ia[i] = byteString.charCodeAt(i);
7 | }
8 | return new Blob([ab], { type });
9 | }
10 |
--------------------------------------------------------------------------------
/admin/src/helpers/configureStore.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux';
2 | import thunkMiddleware from 'redux-thunk';
3 | import { Map } from 'immutable';
4 |
5 | import rootReducer from '../state/reducers';
6 | import { __DEBUG__ } from '../config/constants';
7 |
8 | const configureStore = () => {
9 | const initialStoreState = Map();
10 |
11 | const enhancers = [];
12 | const middlewares = [
13 | thunkMiddleware,
14 | ];
15 |
16 | let devtools;
17 |
18 | if (__DEBUG__) {
19 | devtools = (
20 | typeof window !== 'undefined'
21 | && typeof window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ === 'function'
22 | && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ actionsBlacklist: [] })
23 | );
24 |
25 | if (devtools) {
26 | console.info('[setup] ✓ Enabling Redux DevTools Extension');
27 | }
28 | }
29 |
30 | const composedEnhancers = devtools || compose;
31 | const storeEnhancers = composedEnhancers(
32 | applyMiddleware(...middlewares),
33 | ...enhancers,
34 | );
35 |
36 | const store = createStore(
37 | rootReducer,
38 | initialStoreState,
39 | storeEnhancers,
40 | );
41 |
42 | return store;
43 | };
44 |
45 | export default configureStore;
46 |
47 | export const store = configureStore();
48 |
--------------------------------------------------------------------------------
/admin/src/helpers/getTrad.js:
--------------------------------------------------------------------------------
1 | import pluginId from './pluginId';
2 |
3 | const getTrad = (id) => `${pluginId}.${id}`;
4 |
5 | export default getTrad;
6 |
--------------------------------------------------------------------------------
/admin/src/helpers/pluginId.js:
--------------------------------------------------------------------------------
1 | import pluginPkg from '../../../package.json';
2 |
3 | const pluginId = pluginPkg.name.replace(
4 | /^strapi-plugin-/i,
5 | '',
6 | );
7 |
8 | export default pluginId;
9 |
--------------------------------------------------------------------------------
/admin/src/helpers/prefixPluginTranslations.js:
--------------------------------------------------------------------------------
1 | const prefixPluginTranslations = (trad, pluginId) => {
2 | if (!pluginId) {
3 | throw new TypeError("pluginId can't be empty");
4 | }
5 | return Object.keys(trad).reduce((acc, current) => {
6 | acc[`${pluginId}.${current}`] = trad[current];
7 | return acc;
8 | }, {});
9 | };
10 |
11 | export { prefixPluginTranslations };
12 |
--------------------------------------------------------------------------------
/admin/src/index.js:
--------------------------------------------------------------------------------
1 | import pluginPkg from '../../package.json';
2 | import pluginId from './helpers/pluginId';
3 | import { prefixPluginTranslations } from './helpers/prefixPluginTranslations';
4 | import pluginPermissions from './permissions';
5 | // import pluginIcon from './components/PluginIcon';
6 | // import getTrad from './helpers/getTrad';
7 |
8 | const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
9 | const { name } = pluginPkg.strapi;
10 |
11 | export default {
12 | register(app) {
13 | app.registerPlugin({
14 | description: pluginDescription,
15 | id: pluginId,
16 | isReady: true,
17 | isRequired: pluginPkg.strapi.required || false,
18 | name,
19 | });
20 |
21 | app.createSettingSection(
22 | {
23 | id: pluginId,
24 | intlLabel: {
25 | id: `${pluginId}.plugin.name`,
26 | defaultMessage: 'Config Sync',
27 | },
28 | },
29 | [
30 | {
31 | intlLabel: {
32 | id: `${pluginId}.Settings.Tool.Title`,
33 | defaultMessage: 'Interface',
34 | },
35 | id: 'config-sync-page',
36 | to: `${pluginId}`,
37 | Component: () => import('./containers/App'),
38 | permissions: pluginPermissions['settings'],
39 | },
40 | ],
41 | );
42 | },
43 | bootstrap(app) {},
44 | async registerTrads({ locales }) {
45 | const importedTrads = await Promise.all(
46 | locales.map((locale) => {
47 | return import(`./translations/${locale}.json`)
48 | .then(({ default: data }) => {
49 | return {
50 | data: prefixPluginTranslations(data, pluginId),
51 | locale,
52 | };
53 | })
54 | .catch(() => {
55 | return {
56 | data: {},
57 | locale,
58 | };
59 | });
60 | }),
61 | );
62 |
63 | return Promise.resolve(importedTrads);
64 | },
65 | };
66 |
--------------------------------------------------------------------------------
/admin/src/permissions.js:
--------------------------------------------------------------------------------
1 | const pluginPermissions = {
2 | // This permission regards the main component (App) and is used to tell
3 | // If the plugin link should be displayed in the menu
4 | // And also if the plugin is accessible. This use case is found when a user types the url of the
5 | // plugin directly in the browser
6 | 'menu-link': [{ action: 'plugin::config-sync.menu-link', subject: null }],
7 | settings: [{ action: 'plugin::config-sync.settings.read', subject: null }],
8 | };
9 |
10 | export default pluginPermissions;
11 |
--------------------------------------------------------------------------------
/admin/src/state/reducers/Config/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Main reducer
4 | *
5 | */
6 |
7 | import { fromJS, Map, List } from 'immutable';
8 | import {
9 | SET_CONFIG_DIFF_IN_STATE,
10 | SET_CONFIG_PARTIAL_DIFF_IN_STATE,
11 | SET_LOADING_STATE,
12 | SET_APP_ENV_IN_STATE,
13 | } from '../../actions/Config';
14 |
15 | const initialState = fromJS({
16 | configDiff: Map({}),
17 | partialDiff: List([]),
18 | isLoading: false,
19 | appEnv: Map({}),
20 | });
21 |
22 | export default function configReducer(state = initialState, action) {
23 | switch (action.type) {
24 | case SET_CONFIG_DIFF_IN_STATE:
25 | return state
26 | .update('configDiff', () => fromJS(action.config));
27 | case SET_CONFIG_PARTIAL_DIFF_IN_STATE:
28 | return state
29 | .update('partialDiff', () => fromJS(action.config));
30 | case SET_LOADING_STATE:
31 | return state
32 | .update('isLoading', () => fromJS(action.value));
33 | case SET_APP_ENV_IN_STATE:
34 | return state
35 | .update('appEnv', () => fromJS(action.value));
36 | default:
37 | return state;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/admin/src/state/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux-immutable';
2 | import configReducer from './Config';
3 |
4 | const rootReducer = combineReducers({
5 | config: configReducer,
6 | });
7 |
8 | export default rootReducer;
9 |
--------------------------------------------------------------------------------
/admin/src/translations/ar.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/cs.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/de.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/admin/src/translations/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "popUpWarning.warning.import_1": "If you continue all your local config files",
3 | "popUpWarning.warning.import_2": "will be imported into the database.",
4 | "popUpWarning.warning.export_1": "If you continue all your database config",
5 | "popUpWarning.warning.export_2": "will be written into config files.",
6 | "popUpWarning.button.import": "Yes, import",
7 | "popUpWarning.button.export": "Yes, export",
8 | "popUpWarning.button.cancel": "Cancel",
9 | "popUpWarning.force": "Force",
10 | "popUpWarning.Confirmation": "Confirmation",
11 |
12 | "Header.Title": "Config Sync",
13 | "Header.Description": "Manage your database config across environments.",
14 |
15 | "ConfigList.Loading": "Loading content...",
16 | "ConfigList.SelectAll": "Select all entries",
17 | "ConfigList.ConfigName": "Config name",
18 | "ConfigList.ConfigType": "Config type",
19 | "ConfigList.State": "State",
20 | "ConfigList.Different": "Different",
21 | "ConfigList.OnlyDir": "Only in sync dir",
22 | "ConfigList.OnlyDB": "Only in DB",
23 |
24 | "NoChanges.Message": "No differences between DB and sync directory. You are up-to-date!",
25 |
26 | "ConfigDiff.Title": "Config changes for",
27 | "ConfigDiff.SyncDirectory": "Sync directory",
28 | "ConfigDiff.Database": "Database",
29 |
30 | "Buttons.Export": "Export",
31 | "Buttons.DownloadConfig": "Download Config",
32 | "Buttons.Import": "Import",
33 |
34 | "FirstExport.Message": "Looks like this is your first time using config-sync for this project.",
35 | "FirstExport.Button": "Make the initial export",
36 |
37 | "Settings.Tool.Title": "Interface",
38 |
39 | "plugin.name": "Config Sync"
40 | }
41 |
--------------------------------------------------------------------------------
/admin/src/translations/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "popUpWarning.warning.import_1": "Si continuas todos tus ficheros de configuración locales",
3 | "popUpWarning.warning.import_2": "se importarán a la base de datos.",
4 | "popUpWarning.warning.export_1": "Si continuas las configuraciones de tu base de datos",
5 | "popUpWarning.warning.export_2": "se escribirán en ficheros de configuración locales.",
6 | "popUpWarning.button.import": "Sí, importar",
7 | "popUpWarning.button.export": "Sí, exportar",
8 | "popUpWarning.button.cancel": "Cancelar",
9 | "popUpWarning.force": "Forzar",
10 | "popUpWarning.Confirmation": "Confirmación",
11 |
12 | "Header.Title": "Config Sync",
13 | "Header.Description": "Gestiona las configuraciones de tu base de datos entre diferentes entornos o instancias.",
14 |
15 | "ConfigList.Loading": "Cargando contenido...",
16 | "ConfigList.SelectAll": "Seleccionar todas las entradas",
17 | "ConfigList.ConfigName": "Nombre",
18 | "ConfigList.ConfigType": "Tipo",
19 | "ConfigList.State": "Estado",
20 | "ConfigList.Different": "Diferentes",
21 | "ConfigList.OnlyDir": "Sólo en directorio de sincronización",
22 | "ConfigList.OnlyDB": "Sólo en la base de datos",
23 |
24 | "NoChanges.Message": "No hay diferencia entre la base de datos y el directorio de sincronización. ¡Estás actualizado!",
25 |
26 | "ConfigDiff.Title": "Cambios en la configuración para",
27 | "ConfigDiff.SyncDirectory": "Directorio de sincronización",
28 | "ConfigDiff.Database": "Base de datos",
29 |
30 | "Buttons.Import": "Importar",
31 | "Buttons.Export": "Exportar",
32 |
33 | "FirstExport.Message": "Parece ser la primera vez que se usa config-sync en este proyecto.",
34 | "FirstExport.Button": "Hacer la exportación inicial",
35 |
36 | "Settings.Tool.Title": "Interfaz",
37 |
38 | "plugin.name": "Config Sync"
39 | }
--------------------------------------------------------------------------------
/admin/src/translations/fr.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/id.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/index.js:
--------------------------------------------------------------------------------
1 | import ar from './ar.json';
2 | import cs from './cs.json';
3 | import de from './de.json';
4 | import en from './en.json';
5 | import es from './es.json';
6 | import fr from './fr.json';
7 | import id from './id.json';
8 | import it from './it.json';
9 | import ko from './ko.json';
10 | import ms from './ms.json';
11 | import nl from './nl.json';
12 | import pl from './pl.json';
13 | import ptBR from './pt-BR.json';
14 | import pt from './pt.json';
15 | import ru from './ru.json';
16 | import th from './th.json';
17 | import tr from './tr.json';
18 | import uk from './uk.json';
19 | import vi from './vi.json';
20 | import zhHans from './zh-Hans.json';
21 | import zh from './zh.json';
22 | import sk from './sk.json';
23 |
24 | const trads = {
25 | ar,
26 | cs,
27 | de,
28 | en,
29 | es,
30 | fr,
31 | id,
32 | it,
33 | ko,
34 | ms,
35 | nl,
36 | pl,
37 | 'pt-BR': ptBR,
38 | pt,
39 | ru,
40 | th,
41 | tr,
42 | uk,
43 | vi,
44 | 'zh-Hans': zhHans,
45 | zh,
46 | sk,
47 | };
48 |
49 | export default trads;
50 |
--------------------------------------------------------------------------------
/admin/src/translations/it.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/ko.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/ms.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/nl.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/pl.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/admin/src/translations/pt-BR.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/pt.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/ru.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/admin/src/translations/sk.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/th.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/tr.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/admin/src/translations/uk.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/admin/src/translations/vi.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/admin/src/translations/zh-Hans.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/admin/src/translations/zh.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/bin/config-sync:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | 'use strict';
4 |
5 | require('../dist/cli');
6 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment:
2 | branches:
3 | - master
4 | - develop
5 |
--------------------------------------------------------------------------------
/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress');
2 | const fs = require('fs-extra');
3 |
4 | module.exports = defineConfig({
5 | e2e: {
6 | baseUrl: 'http://localhost:1337',
7 | specPattern: '**/*.cy.{js,ts,jsx,tsx}',
8 | video: true,
9 | defaultCommandTimeout: 30000,
10 | requestTimeout: 30000,
11 | setupNodeEvents(on, config) {
12 | // implement node event listeners here.
13 | // eslint-disable-next-line global-require
14 | require('cypress-terminal-report/src/installLogsPrinter')(on);
15 |
16 | on('task', {
17 | deleteFolder(folderName) {
18 | console.log(`deleting folder ${folderName}`);
19 |
20 | return fs.remove(folderName)
21 | .then(() => {
22 | console.log(`folder ${folderName} deleted`);
23 | return null;
24 | })
25 | .catch((err) => {
26 | console.error(`error deleting folder ${folderName}`, err);
27 | throw err;
28 | });
29 | },
30 | });
31 | },
32 | },
33 | });
34 |
--------------------------------------------------------------------------------
/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/e2e.ts is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
19 | require('cypress-terminal-report/src/installLogsCollector')();
20 |
21 | // Alternatively you can use CommonJS syntax:
22 | // require('./commands')
23 |
--------------------------------------------------------------------------------
/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: /
5 | schedule:
6 | interval: daily
7 | ignore:
8 | - dependency-name: '\*'
9 | update-types: ["version-update:semver-patch"]
10 | groups:
11 | strapi:
12 | patterns:
13 | - "@strapi/*"
14 |
--------------------------------------------------------------------------------
/docs/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - main
8 |
9 | jobs:
10 | deploy:
11 | name: Deploy
12 | runs-on: ubuntu-latest
13 | environment:
14 | name: docs.pluginpal.io
15 | url: https://docs.pluginpal.io
16 | steps:
17 | - name: Checkout repository
18 | uses: actions/checkout@v3
19 |
20 | - name: Set up Docker
21 | uses: actions/setup-node@v3
22 | with:
23 | node-version: '14'
24 |
25 | - name: Build a Docker image
26 | run: |
27 | docker build \
28 | -t pluginpal-docs:latest .
29 | docker save -o pluginpal-docs-latest.tar pluginpal-docs:latest
30 |
31 | - name: Transfer the Docker image to the Dokku server
32 | uses: appleboy/scp-action@v0.1.3
33 | with:
34 | host: ${{ secrets.SSH_HOST }}
35 | username: ${{ secrets.SSH_CI_USERNAME }}
36 | password: ${{ secrets.SSH_CI_PASSWORD }}
37 | source: pluginpal-docs-latest.tar
38 | target: /var/lib/dokku/data/storage/docs/docker-images
39 |
40 | - name: Deploy the Dokku app based on the Docker image
41 | uses: appleboy/ssh-action@v0.1.10
42 | with:
43 | host: ${{ secrets.SSH_HOST }}
44 | username: ${{ secrets.SSH_CI_USERNAME }}
45 | password: ${{ secrets.SSH_CI_PASSWORD }}
46 | script_stop: true
47 | script: |
48 | sudo docker load -i /var/lib/dokku/data/storage/docs/docker-images/pluginpal-docs-latest.tar
49 | DOCS_LATEST_IMAGE=$(sudo docker images --format "{{.ID}}" pluginpal-docs:latest)
50 | sudo docker tag pluginpal-docs:latest pluginpal-docs:$DOCS_LATEST_IMAGE
51 | dokku git:from-image docs pluginpal-docs:$DOCS_LATEST_IMAGE
52 | sudo docker system prune --all --force
53 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
--------------------------------------------------------------------------------
/docs/Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 |
3 | # Stage 1: Base image.
4 | ## Start with a base image containing NodeJS so we can build Docusaurus.
5 | FROM node:18-alpine3.18 as base
6 | ## Disable colour output from yarn to make logs easier to read.
7 | ENV FORCE_COLOR=0
8 | ## Enable corepack.
9 | RUN corepack enable
10 | ## Set the working directory to `/opt/docusaurus`.
11 | WORKDIR /opt/docusaurus
12 |
13 | # Stage 2b: Production build mode.
14 | FROM base as prod
15 | ## Set the working directory to `/opt/docusaurus`.
16 | WORKDIR /opt/docusaurus
17 | ## Copy over the source code.
18 | COPY . /opt/docusaurus/
19 | ## Install dependencies with `--immutable` to ensure reproducibility.
20 | RUN yarn install
21 | ## Build the static site.
22 | RUN yarn build
23 |
24 | # Stage 3a: Serve with `docusaurus serve`.
25 | FROM prod as serve
26 | ## Expose the port that Docusaurus will run on.
27 | EXPOSE 3000
28 | ## Run the production server.
29 | CMD ["yarn", "serve", "--host", "0.0.0.0", "--no-open"]
30 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 |
3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
4 |
5 | ### Installation
6 |
7 | ```
8 | $ yarn
9 | ```
10 |
11 | ### Local Development
12 |
13 | ```
14 | $ yarn start
15 | ```
16 |
17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18 |
19 | ### Build
20 |
21 | ```
22 | $ yarn build
23 | ```
24 |
25 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
26 |
27 | ### Deployment
28 |
29 | Using SSH:
30 |
31 | ```
32 | $ USE_SSH=true yarn deploy
33 | ```
34 |
35 | Not using SSH:
36 |
37 | ```
38 | $ GIT_USER= yarn deploy
39 | ```
40 |
41 | 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.
42 |
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/docs/blog/2019-05-28-first-blog-post.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: first-blog-post
3 | title: First Blog Post
4 | authors: [slorber, yangshun]
5 | tags: [hola, docusaurus]
6 | ---
7 |
8 | Lorem ipsum dolor sit amet...
9 |
10 |
11 |
12 | ...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
13 |
--------------------------------------------------------------------------------
/docs/blog/2021-08-01-mdx-blog-post.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: mdx-blog-post
3 | title: MDX Blog Post
4 | authors: [slorber]
5 | tags: [docusaurus]
6 | ---
7 |
8 | Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).
9 |
10 | :::tip
11 |
12 | Use the power of React to create interactive blog posts.
13 |
14 | :::
15 |
16 | {/* truncate */}
17 |
18 | For example, use JSX to create an interactive button:
19 |
20 | ```js
21 |
22 | ```
23 |
24 |
25 |
--------------------------------------------------------------------------------
/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg
--------------------------------------------------------------------------------
/docs/blog/2021-08-26-welcome/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | slug: welcome
3 | title: Welcome
4 | authors: [slorber, yangshun]
5 | tags: [facebook, hello, docusaurus]
6 | ---
7 |
8 | [Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).
9 |
10 | Here are a few tips you might find useful.
11 |
12 |
13 |
14 | Simply add Markdown files (or folders) to the `blog` directory.
15 |
16 | Regular blog authors can be added to `authors.yml`.
17 |
18 | The blog post date can be extracted from filenames, such as:
19 |
20 | - `2019-05-30-welcome.md`
21 | - `2019-05-30-welcome/index.md`
22 |
23 | A blog post folder can be convenient to co-locate blog post images:
24 |
25 | 
26 |
27 | The blog supports tags as well!
28 |
29 | **And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config.
30 |
--------------------------------------------------------------------------------
/docs/blog/authors.yml:
--------------------------------------------------------------------------------
1 | yangshun:
2 | name: Yangshun Tay
3 | title: Front End Engineer @ Facebook
4 | url: https://github.com/yangshun
5 | image_url: https://github.com/yangshun.png
6 | page: true
7 | socials:
8 | x: yangshunz
9 | github: yangshun
10 |
11 | slorber:
12 | name: Sébastien Lorber
13 | title: Docusaurus maintainer
14 | url: https://sebastienlorber.com
15 | image_url: https://github.com/slorber.png
16 | page:
17 | # customize the url of the author page at /blog/authors/
18 | permalink: '/all-sebastien-lorber-articles'
19 | socials:
20 | x: sebastienlorber
21 | linkedin: sebastienlorber
22 | github: slorber
23 | newsletter: https://thisweekinreact.com
24 |
--------------------------------------------------------------------------------
/docs/blog/tags.yml:
--------------------------------------------------------------------------------
1 | facebook:
2 | label: Facebook
3 | permalink: /facebook
4 | description: Facebook tag description
5 |
6 | hello:
7 | label: Hello
8 | permalink: /hello
9 | description: Hello tag description
10 |
11 | docusaurus:
12 | label: Docusaurus
13 | permalink: /docusaurus
14 | description: Docusaurus tag description
15 |
16 | hola:
17 | label: Hola
18 | permalink: /hola
19 | description: Hola tag description
20 |
--------------------------------------------------------------------------------
/docs/docs/api/plugin-config-types.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Plugin config types'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /api/plugin-config-types
5 | ---
6 |
7 | # Plugin config types
8 |
9 | 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.
10 |
11 | ## Register a config type programatically
12 |
13 | You can register a config type by adding some code to the register function of your plugin.
14 |
15 | ```md title="register.js"
16 | // Register the config type when using the config-sync plugin.
17 | if (strapi.plugin('config-sync')) {
18 | if (!strapi.plugin('config-sync').pluginTypes) {
19 | strapi.plugin('config-sync').pluginTypes = [];
20 | }
21 |
22 | strapi.plugin('config-sync').pluginTypes.push({
23 | configName: 'url-pattern',
24 | queryString: 'plugin::webtools.url-pattern',
25 | uid: 'code',
26 | });
27 | }
28 | ```
29 |
30 | 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
31 |
--------------------------------------------------------------------------------
/docs/docs/configuration/custom-types.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Custom types'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /configuration/custom-types
5 | ---
6 |
7 | # Custom types
8 |
9 | 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.
10 |
11 | | Name | Details |
12 | | ---- | ------- |
13 | | Key | `customTypes` |
14 | | Required | false |
15 | | Type | array |
16 | | Default | `[]` |
17 |
--------------------------------------------------------------------------------
/docs/docs/configuration/excluded-config.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Excluded config'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /configuration/excluded-config
5 | ---
6 |
7 | # Excluded config
8 |
9 | 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.
10 |
11 | | Name | Details |
12 | | ---- | ------- |
13 | | Key | `excludedConfig` |
14 | | Required | false |
15 | | Type | array |
16 | | Default | `['core-store.plugin_users-permissions_grant', 'core-store.plugin_upload_metrics', 'core-store.strapi_content_types_schema', 'core-store.ee_information',]` |
17 |
--------------------------------------------------------------------------------
/docs/docs/configuration/excluded-types.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Excluded types'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /configuration/excluded-types
5 | ---
6 |
7 | # Excluded types
8 |
9 | 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.
10 |
11 | For example:
12 |
13 | ```
14 | excludedTypes: ['admin-role']
15 | ```
16 |
17 | | Name | Details |
18 | | ---- | ------- |
19 | | Key | `excludedTypes` |
20 | | Required | false |
21 | | Type | array |
22 | | Default | `[]` |
23 |
--------------------------------------------------------------------------------
/docs/docs/configuration/import-on-bootstrap.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Import on bootstrap'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /configuration/import-on-bootstrap
5 | ---
6 |
7 | # Import on bootstrap
8 |
9 | Allows you to let the config be imported automaticly when strapi is bootstrapping (on `strapi start`).
10 |
11 | :::danger
12 | 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**.
13 | :::
14 |
15 | | Name | Details |
16 | | ---- | ------- |
17 | | Key | `importOnBootstrap` |
18 | | Required | false |
19 | | Type | bool |
20 | | Default | `false` |
21 |
--------------------------------------------------------------------------------
/docs/docs/configuration/introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Introduction'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /configuration
5 | ---
6 |
7 | # 🔧 Configuration
8 | The settings of the plugin can be overridden in the `config/plugins.js` file.
9 | In the example below you can see how, and also what the default settings are.
10 |
11 | ```md title="config/plugins.js"
12 | module.exports = ({ env }) => ({
13 | // ...
14 | 'config-sync': {
15 | enabled: true,
16 | config: {
17 | syncDir: "config/sync/",
18 | minify: false,
19 | soft: false,
20 | importOnBootstrap: false,
21 | customTypes: [],
22 | excludedTypes: [],
23 | excludedConfig: [
24 | "core-store.plugin_users-permissions_grant",
25 | "core-store.plugin_upload_metrics",
26 | "core-store.strapi_content_types_schema",
27 | "core-store.ee_information",
28 | ],
29 | },
30 | },
31 | });
32 | ```
33 |
--------------------------------------------------------------------------------
/docs/docs/configuration/minify.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Minify'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /configuration/minify
5 | ---
6 |
7 | # Minify
8 |
9 | When enabled all the exported JSON files will be minified.
10 |
11 | | Name | Details |
12 | | ---- | ------- |
13 | | Key | `minify` |
14 | | Required | false |
15 | | Type | bool |
16 | | Default | `false` |
17 |
--------------------------------------------------------------------------------
/docs/docs/configuration/soft.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Soft'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /configuration/soft
5 | ---
6 |
7 | # Soft
8 |
9 | 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.
10 |
11 | | Name | Details |
12 | | ---- | ------- |
13 | | Key | `soft` |
14 | | Required | false |
15 | | Type | bool |
16 | | Default | `false` |
17 |
--------------------------------------------------------------------------------
/docs/docs/configuration/sync-dir.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Sync dir'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /configuration/sync-dir
5 | ---
6 |
7 | # Sync dir
8 |
9 | The path for reading and writing the sync files.
10 |
11 | | Name | Details |
12 | | ---- | ------- |
13 | | Key | `syncDir` |
14 | | Required | true |
15 | | Type | string |
16 | | Default | `config/sync/` |
17 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/admin-gui.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Admin GUI'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /admin-gui
5 | ---
6 |
7 | # 🖥️ Admin panel (GUI)
8 | 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.
9 |
10 | **Pro tip:**
11 | 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.
12 |
13 | 
14 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/installation.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Installation'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /
5 | ---
6 |
7 | # ⏳ Installation
8 |
9 | :::prerequisites
10 | Complete installation requirements are the exact same as for Strapi itself and can be found in the Strapi documentation.
11 |
12 | **Supported Strapi versions:**
13 |
14 | Strapi v5 use `strapi-plugin-config-sync@^3`
15 |
16 | Strapi v4 use `strapi-plugin-config-sync@^1`
17 |
18 | :::
19 |
20 | Install the plugin in your Strapi project.
21 |
22 |
23 |
24 | ```
25 | yarn add strapi-plugin-config-sync
26 | ```
27 |
28 |
29 | ```
30 | npm install strapi-plugin-config-sync --save
31 | ```
32 |
33 |
34 |
35 | Add the export path to the `watchIgnoreFiles` list in the `config/admin.js` file.
36 | This way your app won't reload when you export the config in development.
37 |
38 | ```md title="config/admin.js"
39 | module.exports = ({ env }) => ({
40 | // ...
41 | watchIgnoreFiles: [
42 | '**/config/sync/**',
43 | ],
44 | });
45 | ```
46 |
47 | After successful installation you have to rebuild the admin UI so it'll include this plugin. To rebuild and restart Strapi run:
48 |
49 |
50 |
51 | ```
52 | yarn build
53 | yarn develop
54 | ```
55 |
56 |
57 | ```
58 | npm run build
59 | npm run develop
60 | ```
61 |
62 |
63 |
64 | The **Config Sync** plugin should now appear in the **Settings** section of your Strapi app.
65 |
66 | 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)
67 |
68 | Enjoy 🎉
69 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/motivation.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Motivation'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /motivation
5 | ---
6 |
7 | # 💡 Motivation
8 |
9 | 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.
10 |
11 | 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.
12 |
13 | Examples of these types are:
14 |
15 | - Admin roles _(admin::role)_
16 | - User roles _(plugin::users-permissions.role)_
17 | - Admin settings _(strapi::core-store)_
18 | - I18n locale _(plugin::i18n.locale)_
19 |
20 | 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).
21 |
22 | _With great power comes great responsibility - Uncle Ben_
23 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/naming-convention.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Naming convention'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /naming-convention
5 | ---
6 |
7 | # 🔍 Naming convention
8 | All the config files written in the sync directory have the same naming convention. It goes as follows:
9 |
10 | [config-type].[identifier].json
11 |
12 | - `config-type` - Corresponds to the `configName` of the config type.
13 | - `identifier` - Corresponds to the value of the `uid` field of the config type.
14 |
--------------------------------------------------------------------------------
/docs/docs/getting-started/workflow.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Workflow'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /workflow
5 | ---
6 |
7 | # ⌨️ Usage / Workflow
8 | This plugin works best when you use `git` for the version control of your Strapi project.
9 |
10 | _The following workflows are assuming you're using `git`._
11 |
12 | ### Intro
13 | 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:
14 |
15 | - Change the file(s), and run an import. You have now imported from filesystem -> database.
16 | - Change the record(s), and run an export. You have now exported from database -> filesystem.
17 |
18 | ### Local development
19 | When building a new feature locally for your Strapi project you'd use the following workflow:
20 |
21 | - Build the feature.
22 | - Export the config.
23 | - Commit and push the files to git.
24 |
25 | ### Deployment
26 | When deploying the newly created feature - to either a server, or a co-worker's machine - you'd use the following workflow:
27 |
28 | - Pull the latest file changes to the environment.
29 | - (Re)start your Strapi instance.
30 | - Import the config.
31 |
32 | ## Production deployment
33 | 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.
34 |
--------------------------------------------------------------------------------
/docs/docs/upgrading/generic-update.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: 'Generic update'
3 | displayed_sidebar: configSyncSidebar
4 | slug: /upgrading/generic-update
5 | ---
6 |
7 | # Updating Config Sync
8 |
9 | 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.
10 |
11 | ## Bump a minor/patch version
12 |
13 | When you're updating Config Sync you'll have to follow these steps:
14 |
15 | 1. Make sure there are no config changes before starting. Either export or import all staged changes.
16 | 2. Update the version of the `strapi-plugin-config-sync` package in your `package.json` using your package manager of choice (yarn/npm/pnpm)
17 | 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.
18 | 4. You're now ready to push these changes an commit them to your source control!
19 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pluginpal-docs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "docusaurus": "docusaurus",
7 | "start": "docusaurus start",
8 | "build": "docusaurus build",
9 | "swizzle": "docusaurus swizzle",
10 | "deploy": "docusaurus deploy",
11 | "clear": "docusaurus clear",
12 | "serve": "docusaurus serve",
13 | "write-translations": "docusaurus write-translations",
14 | "write-heading-ids": "docusaurus write-heading-ids",
15 | "typecheck": "tsc"
16 | },
17 | "dependencies": {
18 | "@docusaurus/core": "3.5.2",
19 | "@docusaurus/plugin-sitemap": "^3.5.2",
20 | "@docusaurus/preset-classic": "3.5.2",
21 | "@docusaurus/theme-live-codeblock": "^3.5.2",
22 | "@docusaurus/theme-mermaid": "^3.5.2",
23 | "@docusaurus/theme-search-algolia": "^3.5.2",
24 | "@mdx-js/react": "^3.0.0",
25 | "classnames": "^2.5.1",
26 | "clsx": "^2.0.0",
27 | "docusaurus-plugin-sass": "^0.2.5",
28 | "prism-react-renderer": "^2.3.0",
29 | "react": "^18.0.0",
30 | "react-dom": "^18.0.0",
31 | "sass": "^1.78.0"
32 | },
33 | "devDependencies": {
34 | "@docusaurus/module-type-aliases": "3.5.2",
35 | "@docusaurus/tsconfig": "3.5.2",
36 | "@docusaurus/types": "3.5.2",
37 | "typescript": "~5.5.2"
38 | },
39 | "browserslist": {
40 | "production": [
41 | ">0.5%",
42 | "not dead",
43 | "not op_mini all"
44 | ],
45 | "development": [
46 | "last 3 chrome version",
47 | "last 3 firefox version",
48 | "last 5 safari version"
49 | ]
50 | },
51 | "engines": {
52 | "node": ">=18.0"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/docs/sidebars.ts:
--------------------------------------------------------------------------------
1 | /**
2 | - create an ordered group of docs
3 | - render a sidebar for each doc of that group
4 | - provide next/previous navigation
5 |
6 | The sidebars can be generated from the filesystem, or explicitly defined here.
7 |
8 | Create as many sidebars as you want.
9 | */
10 |
11 | // @ts-check
12 |
13 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
14 | const sidebars = {
15 | // By default, Docusaurus generates a sidebar from the docs folder structure
16 | // tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
17 |
18 | // But you can create a sidebar manually
19 | configSyncSidebar: [
20 | {
21 | type: "category",
22 | collapsed: false,
23 | label: "🚀 Getting Started",
24 | items: [
25 | "getting-started/installation",
26 | "getting-started/motivation",
27 | "getting-started/cli",
28 | "getting-started/admin-gui",
29 | "getting-started/config-types",
30 | "getting-started/workflow",
31 | "getting-started/naming-convention",
32 | // "dev-docs/usage-information",
33 | ],
34 | },
35 | {
36 | type: "category",
37 | collapsed: false,
38 | label: "⚙️ Configuration",
39 | items: [
40 | "configuration/introduction",
41 | "configuration/sync-dir",
42 | "configuration/minify",
43 | "configuration/import-on-bootstrap",
44 | "configuration/custom-types",
45 | "configuration/soft",
46 | "configuration/excluded-types",
47 | "configuration/excluded-config",
48 | ],
49 | },
50 | {
51 | type: "category",
52 | collapsed: false,
53 | label: "📦 API",
54 | items: [
55 | "api/plugin-config-types",
56 | ],
57 | },
58 | {
59 | type: "category",
60 | collapsed: false,
61 | label: "♻️ Upgrading",
62 | items: [
63 | "upgrading/generic-update",
64 | ],
65 | },
66 | ],
67 | };
68 |
69 | module.exports = sidebars;
70 |
--------------------------------------------------------------------------------
/docs/src/components/ApiCall.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 |
4 | export default function ApiCall({
5 | children,
6 | noSideBySide = false,
7 | }) {
8 | return (
9 |
15 | {children}
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/docs/src/components/Button/Button.jsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import React from 'react';
3 | import Link from '@docusaurus/Link';
4 | import styles from './button.module.scss';
5 |
6 | export function Button({
7 | href,
8 | to,
9 | children,
10 | className,
11 | decorative,
12 | size = '',
13 | variant = 'primary',
14 | ...rest
15 | }) {
16 | const ButtonElement = (to ? Link : (href ? 'a' : 'button'));
17 |
18 | return (
19 |
32 | {children}
33 | {decorative && (
34 |
35 | {decorative}
36 |
37 | )}
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/docs/src/components/Container/Container.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import styles from './container.module.scss';
4 |
5 | export function Container({ className, ...rest }) {
6 | return (
7 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/docs/src/components/Container/container.module.scss:
--------------------------------------------------------------------------------
1 | /** Component: Container */
2 |
3 | :root {
4 | --strapi-container-px: var(--ifm-spacing-horizontal);
5 | --strapi-container-mw: calc(863px + calc(var(--strapi-container-px) * 2));
6 | }
7 |
8 | .container {
9 | display: flex;
10 | flex-direction: column;
11 | align-items: stretch;
12 | margin-right: auto;
13 | margin-left: auto;
14 | padding-right: var(--strapi-container-px);
15 | padding-left: var(--strapi-container-px);
16 | max-width: var(--strapi-container-mw);
17 | width: 100%;
18 | }
19 |
--------------------------------------------------------------------------------
/docs/src/components/CustomDocCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import classNames from 'classnames';
3 |
4 | export default function CustomDocCard(props) {
5 | const { title, description, link, emoji, small = false } = props;
6 | const linkClasses = classNames({
7 | card: true,
8 | cardContainer: true,
9 | 'padding--lg': !small,
10 | 'padding--md': small,
11 | });
12 | const cardClasses = classNames({
13 | 'custom-doc-card': true,
14 | 'margin-bottom--lg': !small,
15 | 'margin-bottom--sm': small,
16 | 'custom-doc-card--small': small,
17 | });
18 | return (
19 |
20 |
23 |
24 | {emoji ? emoji : '📄️'} {title}
25 |
26 |
27 | {description}
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/docs/src/components/CustomDocCardsWrapper.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function CustomDocCardsWrapper({ children }) {
4 | return (
5 |
6 | {children}
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/docs/src/components/FeaturesList/FeaturesList.jsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import React from 'react';
3 | import styles from './features-list.module.scss';
4 | import { LinkWithArrow } from '../LinkWithArrow/LinkWithArrow';
5 |
6 | export function FeatureListItem({
7 | children,
8 | className,
9 | href,
10 | icon: Icon,
11 | iconColor,
12 | label,
13 | to,
14 | ...rest
15 | }) {
16 | const ContentElement = ((href || to) ? LinkWithArrow : 'span');
17 | const IconElement = ((href || to) ? 'a' : 'span');
18 |
19 | return (
20 |
26 | {Icon && (
27 |
36 |
37 |
38 | )}
39 |
45 | {children || label}
46 |
47 |
48 | );
49 | }
50 |
51 | export function FeaturesList({
52 | className,
53 | id,
54 | icon,
55 | iconColor,
56 | items,
57 | ...rest
58 | }) {
59 | const defaultId = `featureListItem${Math.random()}`;
60 |
61 | return (
62 |
69 | {items?.map((featureListItem, featureListItemIndex) => {
70 | return (
71 |
77 | );
78 | })}
79 |
80 | );
81 | }
82 |
--------------------------------------------------------------------------------
/docs/src/components/Hero/Hero.jsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import React from 'react';
3 | import styles from './hero.module.scss';
4 |
5 | export function HeroTitle({
6 | className,
7 | ...rest
8 | }) {
9 | return (
10 |
17 | );
18 | }
19 |
20 | export function HeroDescription({
21 | className,
22 | ...rest
23 | }) {
24 | return (
25 |
32 | );
33 | }
34 |
35 | export function Hero({
36 | className,
37 | ...rest
38 | }) {
39 | return (
40 |
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/docs/src/components/Hero/hero.module.scss:
--------------------------------------------------------------------------------
1 | /** Component: Hero */
2 |
3 | @import '../../scss/_mixins.scss';
4 |
5 | :root {
6 | --strapi-hero-py: var(--strapi-spacing-6);
7 | --strapi-hero-gap: var(--strapi-spacing-4);
8 |
9 | --strapi-hero-title-color: #1D1B84;
10 | --strapi-hero-title-font-size: var(--strapi-font-size-xl);
11 | --strapi-hero-title-line-height: 1.25;
12 |
13 | --strapi-hero-description-color: #4E6294;
14 | --strapi-hero-description-font-size: var(--strapi-font-size-lg);
15 | --strapi-hero-description-line-height: 1.25;
16 | }
17 |
18 | .hero {
19 | padding-top: var(--strapi-hero-py);
20 | padding-bottom: var(--strapi-hero-py);
21 | text-align: center;
22 |
23 | &__title {
24 | color: var(--strapi-hero-title-color);
25 | font-weight: 700;
26 | font-size: var(--strapi-hero-title-font-size);
27 | line-height: var(--strapi-hero-title-line-height);
28 | letter-spacing: 0.4px;
29 | margin: 0 0 var(--strapi-hero-gap);
30 | }
31 |
32 | &__description {
33 | color: var(--strapi-hero-description-color);
34 | font-size: var(--strapi-hero-description-font-size);
35 | line-height: var(--strapi-hero-description-line-height);
36 | margin: 0;
37 | }
38 | }
39 |
40 | /** Responsive */
41 | @include medium-up {
42 | :root {
43 | --strapi-hero-py: 40px;
44 | --strapi-hero-title-font-size: 43px;
45 | --strapi-hero-title-line-height: 56px;
46 | --strapi-hero-description-font-size: 21px;
47 | --strapi-hero-description-line-height: 28px;
48 | }
49 | }
50 |
51 | /** Dark mode */
52 | @include dark {
53 | --strapi-hero-title-color: white;
54 | --strapi-hero-description-color: white;
55 | }
56 |
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures/index.tsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import Heading from '@theme/Heading';
3 | import styles from './styles.module.css';
4 |
5 | type FeatureItem = {
6 | title: string;
7 | Svg: React.ComponentType>;
8 | description: JSX.Element;
9 | };
10 |
11 | const FeatureList: FeatureItem[] = [
12 | {
13 | title: 'Easy to Use',
14 | Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
15 | description: (
16 | <>
17 | Docusaurus was designed from the ground up to be easily installed and
18 | used to get your website up and running quickly.
19 | >
20 | ),
21 | },
22 | {
23 | title: 'Focus on What Matters',
24 | Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
25 | description: (
26 | <>
27 | Docusaurus lets you focus on your docs, and we'll do the chores. Go
28 | ahead and move your docs into the docs
directory.
29 | >
30 | ),
31 | },
32 | {
33 | title: 'Powered by React',
34 | Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
35 | description: (
36 | <>
37 | Extend or customize your website layout by reusing React. Docusaurus can
38 | be extended while reusing the same header and footer.
39 | >
40 | ),
41 | },
42 | ];
43 |
44 | function Feature({title, Svg, description}: FeatureItem) {
45 | return (
46 |
47 |
48 |
49 |
50 |
51 |
{title}
52 |
{description}
53 |
54 |
55 | );
56 | }
57 |
58 | export default function HomepageFeatures(): JSX.Element {
59 | return (
60 |
61 |
62 |
63 | {FeatureList.map((props, idx) => (
64 |
65 | ))}
66 |
67 |
68 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures/styles.module.css:
--------------------------------------------------------------------------------
1 | .features {
2 | display: flex;
3 | align-items: center;
4 | padding: 2rem 0;
5 | width: 100%;
6 | }
7 |
8 | .featureSvg {
9 | height: 200px;
10 | width: 200px;
11 | }
12 |
--------------------------------------------------------------------------------
/docs/src/components/LinkWithArrow/LinkWithArrow.jsx:
--------------------------------------------------------------------------------
1 | import clsx from 'clsx';
2 | import React from 'react';
3 | import Link from '@docusaurus/Link';
4 | import IconArrow from '@site/static/img/assets/icons/arrow-right.svg';
5 | import styles from './link-with-arrow.module.scss';
6 |
7 | export function LinkWithArrow({
8 | apart = false,
9 | children,
10 | className,
11 | href,
12 | to,
13 | ...rest
14 | }) {
15 | const LinkElement = (to ? Link : 'a');
16 |
17 | return (
18 |
28 |
29 | {children}
30 |
31 |
34 |
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/docs/src/components/LinkWithArrow/link-with-arrow.module.scss:
--------------------------------------------------------------------------------
1 | /** Component: Link with Arrow */
2 |
3 | @import '../../scss/_mixins.scss';
4 |
5 | :root {
6 | --strapi-lwa-icon-ml: var(--strapi-spacing-2);
7 | --strapi-lwa-font-size: 14px;
8 | --strapi-lwa-font-weight: 500;
9 | --strapi-lwa-line-height: 20px;
10 | --strapi-lwa-hover-icon-ml: var(--strapi-spacing-3);
11 | }
12 |
13 | .lwa {
14 | --ifm-link-color: #1D1B84;
15 | --ifm-link-decoration: none;
16 | --ifm-link-hover-color: #2825B8;
17 | --ifm-link-hover-decoration: none;
18 |
19 | font-size: var(--strapi-lwa-font-size);
20 | font-weight: var(--strapi-lwa-font-weight);
21 | line-height: var(--strapi-lwa-line-height);
22 | transition: all 0.2s ease;
23 |
24 | &:focus, &:hover {
25 | --strapi-lwa-icon-ml: var(--strapi-lwa-hover-icon-ml);
26 | }
27 |
28 | &__icon {
29 | display: inline-block;
30 | line-height: 0;
31 | margin-left: var(--strapi-lwa-icon-ml);
32 | transition: margin-left 0.1s ease;
33 | }
34 |
35 | &--apart {
36 | display: flex;
37 | align-items: center;
38 |
39 | .lwa {
40 | &__content {
41 | flex-grow: 1;
42 | }
43 | }
44 | }
45 | }
46 |
47 | /** Dark mode */
48 | @include dark {
49 | .lwa {
50 | --ifm-link-color: var(--strapi-neutral-1000);
51 | --ifm-link-hover-color: var(--strapi-neutral-500);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/docs/src/components/Request.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default function Request({
4 | children,
5 | title = 'Example request',
6 | }) {
7 | return (
8 |
9 |
{title}
10 |
{children}
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/docs/src/components/Response.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default function Response({
4 | children,
5 | title = 'Example response',
6 | }) {
7 | return (
8 |
9 |
{title}
10 |
{children}
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/docs/src/components/SubtleCallout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function SubtleCallout({
4 | children,
5 | title,
6 | emoji = '🤓',
7 | }) {
8 | return (
9 |
10 |
11 | { emoji } { title }
12 |
13 | { children }
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/docs/src/components/index.js:
--------------------------------------------------------------------------------
1 | export * from './Button/Button';
2 | export * from './Card/Card';
3 | export * from './Container/Container';
4 | export * from './HomepageFeatures';
5 | export * from './FeaturesList/FeaturesList';
6 | export * from './Hero/Hero';
7 | export * from './LinkWithArrow/LinkWithArrow';
8 |
--------------------------------------------------------------------------------
/docs/src/scss/__index.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Any CSS included here will be global.
3 | * The classic template bundles Infima by default.
4 | * Infima is a CSS framework designed to work well for content-centric websites.
5 | */
6 |
7 | /** Core */
8 | @import '_mixins.scss';
9 | @import '_tokens.scss';
10 | @import '_tokens-overrides.scss';
11 |
12 | /** Base */
13 | @import '_fonts.scss';
14 | @import '_base.scss';
15 |
16 | /** Components */
17 | @import 'admonition.scss';
18 | @import 'api-call.scss';
19 | @import 'badge.scss';
20 | @import 'breadcrumbs.scss';
21 | @import 'card.scss';
22 | @import 'columns.scss';
23 | @import 'container.scss';
24 | @import 'custom-doc-cards.scss';
25 | @import 'details.scss';
26 | @import 'footer.scss';
27 | @import 'grid.scss';
28 | @import 'images.scss';
29 | @import 'markdown.scss';
30 | @import 'medium-zoom.scss';
31 | @import 'navbar.scss';
32 | @import 'pagination-nav.scss';
33 | @import 'search.scss';
34 | @import 'scene.scss';
35 | @import 'sidebar.scss';
36 | @import 'table.scss';
37 | @import 'tabs.scss';
38 | @import 'table-of-contents.scss';
39 | @import 'typography.scss';
40 |
--------------------------------------------------------------------------------
/docs/src/scss/_base.scss:
--------------------------------------------------------------------------------
1 | /** Base: General Styles */
2 |
3 | :root {
4 | --custom-selection-background-color: var(--strapi-primary-600);
5 | }
6 |
7 | ::selection {
8 | background-color: var(--custom-selection-background-color);
9 | color: var(--custom-selection-color, var(--strapi-neutral-0));
10 | }
11 |
12 | main {
13 | article:first-child:not(.col):not(.custom-doc-card),
14 | article:first-child:not(.col) + nav {
15 | --custom-main-px: var(--strapi-spacing-0);
16 | --custom-main-width: 683px;
17 |
18 | max-width: calc(var(--custom-main-width) + calc(var(--strapi-spacing-4) * 2));
19 | padding-left: var(--custom-main-px) !important;
20 | padding-right: var(--custom-main-px) !important;
21 | margin-left: auto;
22 | margin-right: auto;
23 | }
24 | }
25 |
26 | /** Responsive */
27 | @include medium-up {
28 | main {
29 | article:first-child:not(.col),
30 | article:first-child:not(.col) + nav {
31 | --custom-main-px: var(--strapi-spacing-4);
32 | }
33 | }
34 | }
35 |
36 | /** Dark mode */
37 | @include dark {
38 | .container img[width="16"] {
39 | /* 'Temporary' fix while we figure a way to display white icons in dark mode 😅 */
40 | background-color: white;
41 | border-radius: 2px;
42 | padding: 1px;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/docs/src/scss/_fonts.scss:
--------------------------------------------------------------------------------
1 | /** Fonts */
2 |
3 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;700&display=swap');
4 |
5 | .font-poppins,
6 | .font-poppins button,
7 | .font-poppins input {
8 | --custom-font-family: "Poppins", sans-serif;
9 | --ifm-heading-font-family: "Poppins", sans-serif;
10 |
11 | font-family: var(--custom-font-family);
12 | }
13 |
--------------------------------------------------------------------------------
/docs/src/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 | /** Core: Sass Mixins */
2 |
3 | /** Mixin: Responsive */
4 | @mixin small-up {
5 | @media (min-width: 769px) {
6 | @content;
7 | }
8 | }
9 |
10 | /** Mixin: Responsive */
11 | @mixin medium-up {
12 | @media (min-width: 997px) {
13 | @content;
14 | }
15 | }
16 |
17 | /** Mixin: Responsive */
18 | @mixin large-up {
19 | @media (min-width: 1536px) {
20 | @content;
21 | }
22 | }
23 |
24 | /** Mixin: Dark mode */
25 | @mixin dark {
26 | html[data-theme='dark'] {
27 | @content;
28 | }
29 | }
30 |
31 | /** Mixin: Light mode */
32 | @mixin light {
33 | html[data-theme='light'] {
34 | @content;
35 | }
36 | }
37 |
38 | /** Mixin: Transition */
39 | @mixin transition {
40 | transition: all 0.2s ease;
41 | }
42 |
43 | /** Mixin: Flex */
44 | @mixin flex-row(
45 | $gap: var(--strapi-spacing-2),
46 | $align-items: center,
47 | $justify-content: null
48 | ) {
49 | display: flex;
50 | flex-direction: row;
51 | gap: $gap;
52 | align-items: $align-items;
53 | justify-content: $justify-content;
54 | }
55 |
--------------------------------------------------------------------------------
/docs/src/scss/breadcrumbs.scss:
--------------------------------------------------------------------------------
1 | /** Component: Breadcrumbs */
2 |
3 | .breadcrumbs {
4 | --ifm-breadcrumb-item-background-active: transparent;
5 | --ifm-breadcrumb-border-radius: var(--strapi-spacing-1);
6 | --ifm-breadcrumb-color-active: var(--strapi-neutral-800);
7 | --ifm-link-hover-color: var(--strapi-neutral-700);
8 |
9 | --custom-breadcrumbs-font-size: var(--strapi-font-size-xs);
10 | --custom-breadcrumbs-pt: var(--strapi-spacing-4);
11 | --custom-breadcrumbs-item-caret-mx: var(--strapi-spacing-1);
12 |
13 | padding: var(--custom-breadcrumbs-pt) 0 0;
14 |
15 | &__item {
16 | &:after {
17 | --ifm-breadcrumb-spacing: var(--custom-breadcrumbs-item-caret-mx);
18 | }
19 |
20 | &--active {
21 | font-weight: 700;
22 | }
23 | }
24 |
25 | &__link {
26 | background-color: transparent;
27 | font-size: var(--custom-breadcrumbs-font-size);
28 |
29 | @include transition;
30 |
31 | &:any-link:hover {
32 | --ifm-breadcrumb-item-background-active: var(--strapi-neutral-100);
33 | }
34 | }
35 | }
36 |
37 | @include medium-up {
38 | .breadcrumbs {
39 | --custom-breadcrumbs-pt: var(--strapi-spacing-6);
40 | --custom-breadcrumbs-item-caret-mx: var(--strapi-spacing-3);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/docs/src/scss/card.scss:
--------------------------------------------------------------------------------
1 | /** Component: Card */
2 |
3 | :root body {
4 | --custom-card-border-color: var(--strapi-neutral-200);
5 |
6 | --ifm-card-background-color: var(--strapi-neutral-0);
7 | --ifm-card-border-radius: var(--strapi-spacing-1);
8 | }
9 |
10 | .card {
11 | --ifm-color-emphasis-200: var(--custom-card-border-color);
12 |
13 | border: 1px solid var(--custom-card-border-color);
14 | box-shadow: 0px 1px 4px rgba(33, 33, 52, 0.1) !important;
15 | @include transition;
16 |
17 | &[href] {
18 | color: var(--strapi-neutral-600);
19 |
20 | &:hover {
21 | --ifm-color-primary: var(--strapi-neutral-300);
22 |
23 | box-shadow: 0px 2px 15px rgba(33, 33, 52, 0.1) !important;
24 | }
25 | }
26 |
27 | h2 {
28 | --ifm-h2-font-size: var(--strapi-font-size-md);
29 | margin-bottom: var(--strapi-spacing-2);
30 | }
31 |
32 | p {
33 | font-size: var(--strapi-font-size-sm);
34 |
35 | &:last-of-type {
36 | margin-bottom: 0;
37 | }
38 | }
39 | }
40 |
41 | /** Dark mode */
42 | @include dark {
43 | .card {
44 | &[href] {
45 | color: var(--strapi-neutral-700);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/docs/src/scss/columns.scss:
--------------------------------------------------------------------------------
1 | /** Components: Columns */
2 |
3 | .column {
4 | &__title {
5 | font-weight: 700;
6 | margin-bottom: var(--strapi-spacing-1);
7 | }
8 | }
9 |
10 | /** Responsive */
11 | @include medium-up {
12 | .columns {
13 | display: flex;
14 | justify-content: space-between;
15 | gap: var(--strapi-spacing-4);
16 |
17 | > .column {
18 | flex: 1;
19 | max-width: 50%;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docs/src/scss/container.scss:
--------------------------------------------------------------------------------
1 | /** Component: Container */
2 |
3 | .container {
4 | padding-top: 0 !important;
5 | }
6 |
--------------------------------------------------------------------------------
/docs/src/scss/custom-doc-cards.scss:
--------------------------------------------------------------------------------
1 | /** Component: CustomDocCardWrapper */
2 |
3 | .custom-cards-wrapper {
4 | display: flex;
5 | flex-flow: row wrap;
6 | max-width: 715px;
7 | justify-content: flex-start;
8 |
9 | .custom-doc-card {
10 | max-width: 320px;
11 | width: 320px;
12 | margin-bottom: 10px;
13 | margin-right: 20px;
14 |
15 | a {
16 | height: 170px;
17 | }
18 |
19 | }
20 | }
21 |
22 | /** Component: CustomDocCard */
23 | .custom-doc-card {
24 |
25 | a {
26 | text-decoration: none;
27 | }
28 |
29 | a:hover {
30 | .cardTitle {
31 | color: var(--strapi-primary-600);
32 | }
33 | }
34 |
35 | .text--truncate.cardDescription {
36 | // cancel --truncate styles since we can't remove the' class
37 | overflow: auto;
38 | white-space: normal;
39 | }
40 | }
41 |
42 | .custom-doc-card--small {
43 | .cardTitle {
44 | margin-bottom: 0;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/docs/src/scss/details.scss:
--------------------------------------------------------------------------------
1 | /** Component: Details/Accordion */
2 |
3 | details.alert {
4 | --docusaurus-details-decoration-color: var(--strapi-neutral-800);
5 |
6 | --ifm-alert-background-color: var(--strapi-neutral-150);
7 | --ifm-alert-background-color-highlight: var(--strapi-neutral-500);
8 | --ifm-alert-border-radius: var(--strapi-spacing-1);
9 | --ifm-alert-foreground-color: var( --ifm-color-info-contrast-foreground );
10 | --ifm-alert-border-color: transparent;
11 |
12 | --ifm-tabs-color-active: var(--ifm-color-primary);
13 | --ifm-tabs-color-active-border: var(--ifm-color-primary);
14 |
15 | summary {
16 | p {
17 | margin: 0;
18 | }
19 | }
20 |
21 | /** Content element */
22 | > div > div {
23 | --docusaurus-details-decoration-color: transparent;
24 |
25 | margin-top: 0;
26 | }
27 |
28 | a {
29 | color: var(--custom-code-color);
30 | }
31 |
32 | a:hover {
33 | text-decoration: var(--ifm-link-decoration);
34 | }
35 | }
36 |
37 | @include dark {
38 | details a {
39 | color: var(--strapi-primary-500);
40 | font-weight: 700;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/docs/src/scss/footer.scss:
--------------------------------------------------------------------------------
1 | /** Component: Footer */
2 |
3 | .footer {
4 | a {
5 | @include transition;
6 | font-weight: 400;
7 |
8 | &:hover {
9 | font-weight: 700;
10 | }
11 | }
12 |
13 | &.footer--dark {
14 | --ifm-footer-background-color: var(--strapi-neutral-700);
15 | --ifm-footer-link-hover-color: var(--strapi-neutral-0);
16 | }
17 | }
18 |
19 | /** Dark mode */
20 | @include dark {
21 | .footer {
22 | &.footer--dark {
23 | --ifm-footer-background-color: var(--strapi-neutral-150);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/docs/src/scss/grid.scss:
--------------------------------------------------------------------------------
1 | /** Component: Grid */
2 |
3 | .row {
4 | --custom-grid-spacing: var(--strapi-spacing-2);
5 | --ifm-spacing-horizontal: var(--custom-grid-spacing);
6 |
7 | .col {
8 | &.margin-bottom--lg {
9 | margin-bottom: calc(var(--custom-grid-spacing) * 2) !important;
10 | }
11 | }
12 | }
13 |
14 | /** Responsive */
15 | @include medium-up {
16 | .row {
17 | &--huge {
18 | --custom-grid-spacing: var(--strapi-spacing-4);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/docs/src/scss/images.scss:
--------------------------------------------------------------------------------
1 | /** General: Images */
2 |
3 | /** Dark Mode images border */
4 | .theme-doc-markdown.markdown [class*="themedImage--dark"] {
5 | border: 0.125rem solid var(--strapi-neutral-200);
6 | border-radius: 0.25rem;
7 | }
8 |
--------------------------------------------------------------------------------
/docs/src/scss/markdown.scss:
--------------------------------------------------------------------------------
1 | /** Component: Markdown */
2 |
3 | $selector-markdown: ".theme-doc-markdown.markdown";
4 |
5 | #{$selector-markdown} {
6 | --custom-markdown-pt: var(--strapi-spacing-0);
7 | --custom-markdown-pb: var(--strapi-spacing-2);
8 |
9 | --markdown-link-color: var(--strapi-primary-600);
10 |
11 | font-weight: 400;
12 | padding: var(--custom-markdown-pt) 0 var(--custom-markdown-pb);
13 | }
14 |
15 | /** Dark mode */
16 | @include dark {
17 | #{$selector-markdown} {
18 | --markdown-link-color: var(--strapi-primary-600);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/docs/src/scss/medium-zoom.scss:
--------------------------------------------------------------------------------
1 | /** Component: Medium Zoom */
2 |
3 | .medium-zoom {
4 | &-overlay {
5 | background: var(--strapi-neutral-150) !important;
6 | }
7 | }
8 |
9 | /** Responsive */
10 | @include medium-up {
11 | .medium-zoom {
12 | &-image {
13 | &--opened {
14 | margin-top: var(--ifm-navbar-height) !important;
15 | margin-bottom: var(--ifm-navbar-height) !important;
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/docs/src/scss/pagination-nav.scss:
--------------------------------------------------------------------------------
1 | /** Component: Pagination Nav */
2 |
3 | .pagination-nav {
4 | --ifm-spacing-horizontal: var(--strapi-spacing-4);
5 |
6 | &__link {
7 | &:hover {
8 | --ifm-pagination-nav-color-hover: var(--custom-card-border-color);
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/docs/src/scss/scene.scss:
--------------------------------------------------------------------------------
1 | /** Component: Scene */
2 |
3 | #scene {
4 | position: fixed;
5 | top: 0px;
6 | width: 100%;
7 | height: 100%;
8 | z-index: 1000;
9 | }
10 |
--------------------------------------------------------------------------------
/docs/src/scss/search.scss:
--------------------------------------------------------------------------------
1 | /** Component: Search */
2 |
3 | :root body {
4 | --docsearch-hit-height: 56px;
5 | --docsearch-searchbox-height: 40px;
6 | --docsearch-spacing: var(--strapi-spacing-4);
7 | }
8 |
9 | body .DocSearch {
10 | --custom-search-hit-pb: var(--strapi-spacing-2);
11 |
12 | &-SearchBar {
13 | padding-bottom: var(--strapi-spacing-1);
14 | }
15 |
16 | &-Input {
17 | --docsearch-text-color: var(--strapi-neutral-800);
18 | }
19 |
20 | &-Input,
21 | &-Cancel {
22 | font-size: var(--strapi-font-size-md);
23 | }
24 |
25 | &-Hit {
26 | padding-bottom: var(--custom-search-hit-pb);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/docs/src/scss/table-of-contents.scss:
--------------------------------------------------------------------------------
1 | /** Component: Table of Contents */
2 |
3 | .table-of-contents {
4 | --custom-toc-py: var(--strapi-spacing-1);
5 | --custom-toc-items-py: var(--strapi-spacing-0);
6 |
7 | --ifm-toc-padding-vertical: var(--custom-toc-py);
8 |
9 | font-size: var(--strapi-font-size-xs);
10 |
11 | > li {
12 | margin: 0 var(--ifm-toc-padding-horizontal);
13 | padding-top: var(--custom-toc-items-py);
14 | padding-bottom: var(--custom-toc-items-py);
15 | }
16 |
17 | &__link {
18 | --custom-table-of-contents-link-active-before-left: -16px;
19 |
20 | position: relative;
21 | font-weight: 400;
22 |
23 | @include transition;
24 |
25 | &:hover {
26 | font-weight: 500;
27 |
28 | &:not(.table-of-contents__link--active) {
29 | --ifm-color-primary: var(--strapi-neutral-900);
30 | }
31 | }
32 |
33 | &--active {
34 | font-weight: 500;
35 |
36 | &:before {
37 | content: " ";
38 | position: absolute;
39 | top: 0;
40 | bottom: 0;
41 | left: var(--custom-table-of-contents-link-active-before-left);
42 | width: 3px;
43 | border-radius: 0 2px 2px 0;
44 | background-color: var(--strapi-primary-600);
45 | }
46 | }
47 |
48 | + ul li .table-of-contents {
49 | &__link {
50 | --custom-table-of-contents-link-active-before-left: -32px;
51 | }
52 | }
53 |
54 | img {
55 | display: inline-block;
56 | vertical-align: bottom;
57 | max-width: 22px;
58 | margin-right: 2px;
59 | }
60 | }
61 | }
62 |
63 | /** Responsive */
64 | @include medium-up {
65 | .table-of-contents {
66 | --custom-toc-items-py: var(--strapi-spacing-2);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/docs/src/scss/table.scss:
--------------------------------------------------------------------------------
1 | /** Component: Table */
2 | table {
3 | min-width: 100%;
4 | overflow: auto;
5 |
6 | thead {
7 | --ifm-table-background: transparent;
8 | --ifm-table-stripe-background: transparent;
9 |
10 | tr {
11 | border-bottom-width: 1px;
12 |
13 | th {
14 | --ifm-table-head-background: transparent;
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/docs/src/scss/tabs.scss:
--------------------------------------------------------------------------------
1 | /** Component: Tab */
2 |
3 | :root body {
4 | --custom-tabs-px: var(--strapi-spacing-5);
5 | --custom-tabs-py: var(--strapi-spacing-2);
6 |
7 | --ifm-tabs-padding-horizontal: var(--custom-tabs-px);
8 | --ifm-tabs-padding-vertical: var(--custom-tabs-py);
9 | }
10 |
11 | .tabs {
12 | &__item {
13 | border-top: 2px solid transparent;
14 | }
15 |
16 | /** Tabs inside Tabs */
17 | + div {
18 | [role="tabpanel"] {
19 | .tabs {
20 | font-size: var(--strapi-font-size-ssm);
21 |
22 | &__item {
23 | &--active {
24 | --ifm-tabs-color-active-border: transparent;
25 |
26 | background-color: var(--ifm-hover-overlay);
27 | }
28 | }
29 |
30 | + [class*="margin-top"] {
31 | margin-top: 0 !important;
32 | }
33 | }
34 | }
35 | }
36 | }
37 |
38 | /** Tabs inside Details component */
39 | details {
40 | .tabs {
41 | --ifm-tabs-color-active-border: var(--strapi-)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/docs/src/scss/typography.scss:
--------------------------------------------------------------------------------
1 | /** General: Typography */
2 |
3 | :root {
4 | --custom-heading-decorative-line-color: var(--strapi-neutral-150);
5 | }
6 |
7 | h1, h2, h3, h4, h5, h6 {
8 | --ifm-heading-color: var(--strapi-neutral-900);
9 | --ifm-heading-font-weight: 600;
10 | --ifm-code-font-size: 70%;
11 | }
12 |
13 | h1, .markdown h1:first-child {
14 | --ifm-h1-font-size: 35px;
15 | // --ifm-heading-line-height: 24px; // not good
16 | }
17 |
18 | h2, .markdown > h2 {
19 | --ifm-h2-font-size: 26px;
20 | // --ifm-heading-line-height: 24px; // not good
21 | }
22 |
23 | h3, .markdown > h3 {
24 | --ifm-h3-font-size: var(--strapi-font-size-lg);
25 | @include flex-row;
26 | }
27 |
28 | h2 {
29 | position: relative;
30 |
31 | &:after {
32 | content: " ";
33 | position: absolute;
34 | left: 0;
35 | right: 0;
36 | bottom: -10px;
37 | height: 1px;
38 | background-color: var(--custom-heading-decorative-line-color);
39 | }
40 | }
41 |
42 | p, ul {
43 | img {
44 | display: inline-block;
45 | vertical-align: text-bottom;
46 | }
47 | }
48 |
49 | /** Dark mode */
50 | @include dark {
51 | h1, h2, h3, h4, h5, h6 {
52 | --ifm-heading-color: var(--strapi-neutral-900);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/docs/src/theme/Admonition/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import { ThemeClassNames } from '@docusaurus/theme-common';
4 |
5 | const defaultClassName = ThemeClassNames.common.admonition;
6 | const customDefaultProps = {
7 | note: {
8 | icon: '✏️',
9 | title: 'Note',
10 | },
11 | tip: {
12 | icon: '💡',
13 | title: 'Tip',
14 | },
15 | info: {
16 | icon: '👀',
17 | title: 'Info',
18 | },
19 | caution: {
20 | icon: '✋',
21 | title: 'Caution',
22 | },
23 | warning: {
24 | icon: '⚠️',
25 | title: 'Warning',
26 | },
27 | danger: {
28 | icon: '❗️',
29 | title: 'Warning',
30 | },
31 | strapi: {
32 | icon: '🤓',
33 | },
34 | prerequisites: {
35 | icon: '☑️',
36 | title: 'Prerequisites',
37 | },
38 | };
39 |
40 | export default function CustomAdmonition({
41 | children,
42 | className,
43 | icon: propIcon,
44 | title: propTitle,
45 | type,
46 | ...rest
47 | }) {
48 | const { icon: defaultIcon, title: defaultTitle } = (customDefaultProps[type] || {});
49 | const icon = (propIcon || defaultIcon);
50 | const title = (propTitle || defaultTitle);
51 | const shouldRenderHeading = !!(icon || title);
52 |
53 | return (
54 |
62 | {shouldRenderHeading && (
63 |
64 | {icon && (
65 |
66 | {icon}{' '}
67 |
68 | )}
69 | {title}
70 |
71 | )}
72 | {children}
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/docs/src/theme/MDXComponents.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | // Import the original mapper
3 | import MDXComponents from '@theme-original/MDXComponents';
4 | /** Import built-in Docusaurus components at the global level
5 | * so we don't have to re-import them in every file
6 | */
7 |
8 | import Tabs from '@theme/Tabs';
9 | import TabItem from '@theme/TabItem';
10 |
11 | // Import custom components, globally as well
12 | import Request from '../components/Request';
13 | import Response from '../components/Response';
14 | import ApiCall from '../components/ApiCall';
15 | import SubtleCallout from '../components/SubtleCallout';
16 | import CustomDocCard from '../components/CustomDocCard';
17 | import CustomDocCardsWrapper from '../components/CustomDocCardsWrapper';
18 |
19 | export default {
20 | // Re-use the default mapping
21 | ...MDXComponents,
22 |
23 | /**
24 | * Components below are imported within the global scope,
25 | * meaning you don't have to insert the typical 'import SomeStuff from '/path/to/stuff' line
26 | * at the top of a Markdown file before being able to use these components
27 | * — see https://docusaurus.io/docs/next/markdown-features/react#mdx-component-scope
28 | */
29 | Request,
30 | Response,
31 | ApiCall,
32 | Tabs,
33 | TabItem,
34 | SubtleCallout,
35 | CustomDocCard,
36 | CustomDocCardsWrapper,
37 | };
38 |
--------------------------------------------------------------------------------
/docs/static/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/docs/static/.nojekyll
--------------------------------------------------------------------------------
/docs/static/img/assets/admin-diff-viewer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/docs/static/img/assets/admin-diff-viewer.png
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ArrowClockwise.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/Browsers.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/CheckCircle.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/Clock.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/CreditCard.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/CrossCircle.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/Duplicate copy.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/Eye.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/Invoice.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/MapTrifold.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ONHOLDCarretDown.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ONHOLDCarretUp.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/add.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/add_circle.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/add_icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/after.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/api_tokens.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/application.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/arrow-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/back.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/carret.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/check_icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/chevron-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/chevron-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/clear.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/close-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/code.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/cog.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/content.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/content_manager.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/content_types_builder.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/crop.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/cross.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_boolean.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_date.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_dz.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_email.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_enum.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_json.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_media.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_number.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_password.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_relation_1to1.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_relation_1tomany.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_relation_manyto1.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_relation_manyway.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_relation_oneway.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_richtext.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_text.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/ctb_uid.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/delete.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/documentation-plugin.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/documentation.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/down.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/down2.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/download.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/duplicate.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/edit.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/email_template.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/external_link.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/feather.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/globe.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/grid_view.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/image.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/list_view.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/lock.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/marketplace.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/media_library.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/minus.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/more.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/move.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/notifications.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/official-market.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/plugins.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/provider.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/releases.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/reorder.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/reset_icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/roles.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/roles_permissions.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/search.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/up.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/up2.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/users.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/verified-marketplace.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/assets/icons/webhooks.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/static/img/docusaurus-social-card.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/docs/static/img/docusaurus-social-card.jpg
--------------------------------------------------------------------------------
/docs/static/img/favicon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/docs/static/img/favicon.jpg
--------------------------------------------------------------------------------
/docs/static/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/docs/static/img/logo.png
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // This file is not used in compilation. It is here just for a nice editor experience.
3 | "extends": "@docusaurus/tsconfig",
4 | "compilerOptions": {
5 | "baseUrl": "."
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'Unit test',
3 | testMatch: ['**/__tests__/?(*.)+(spec|test).js'],
4 | transform: {},
5 | coverageDirectory: "./coverage/",
6 | collectCoverage: true,
7 | };
8 |
--------------------------------------------------------------------------------
/packup.config.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-extraneous-dependencies
2 | import { defineConfig } from '@strapi/pack-up';
3 |
4 | export default defineConfig({
5 | bundles: [
6 | {
7 | source: './admin/src/index.js',
8 | import: './dist/admin/index.mjs',
9 | require: './dist/admin/index.js',
10 | runtime: 'web',
11 | },
12 | {
13 | source: './server/index.js',
14 | import: './dist/server/index.mjs',
15 | require: './dist/server/index.js',
16 | runtime: 'node',
17 | },
18 | {
19 | source: './server/cli.js',
20 | import: './dist/cli/index.mjs',
21 | require: './dist/cli/index.js',
22 | runtime: 'node',
23 | },
24 | ],
25 | dist: './dist',
26 | exports: {},
27 | });
28 |
--------------------------------------------------------------------------------
/playground/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [{package.json,*.yml}]
12 | indent_style = space
13 | indent_size = 2
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/playground/.env:
--------------------------------------------------------------------------------
1 | HOST=0.0.0.0
2 | PORT=1337
3 | APP_KEYS=ujfpKPEst1tv0WDxJEhjJw==,MOnFjWYKbWYmtrBZ3cQTFQ==,zQpX70tJw/Mw+Y656kXfVA==,xJT1vbsiz3cgabfgpLu72w==
4 | API_TOKEN_SALT=5FoJkYoZV8IA6+NnZJDzng==
5 | ADMIN_JWT_SECRET=tkeg3+HqE+QmTd2ITEivtA==
6 | TRANSFER_TOKEN_SALT=UUMCRQ2cx9qvKw/RkB815Q==
7 | # Database
8 | DATABASE_CLIENT=sqlite
9 | DATABASE_FILENAME=.tmp/data.db
10 | JWT_SECRET=Dn/nUGQsREUw4/lfQYOScw==
11 |
--------------------------------------------------------------------------------
/playground/.env.example:
--------------------------------------------------------------------------------
1 | HOST=0.0.0.0
2 | PORT=1337
3 | APP_KEYS="toBeModified1,toBeModified2"
4 | API_TOKEN_SALT=tobemodified
5 | ADMIN_JWT_SECRET=tobemodified
6 | TRANSFER_TOKEN_SALT=tobemodified
7 | JWT_SECRET=tobemodified
8 |
--------------------------------------------------------------------------------
/playground/.eslintignore:
--------------------------------------------------------------------------------
1 | .cache
2 | build
3 | **/node_modules/**
4 |
--------------------------------------------------------------------------------
/playground/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": "eslint:recommended",
4 | "env": {
5 | "commonjs": true,
6 | "es6": true,
7 | "node": true,
8 | "browser": false
9 | },
10 | "parserOptions": {
11 | "ecmaFeatures": {
12 | "experimentalObjectRestSpread": true,
13 | "jsx": false
14 | },
15 | "sourceType": "module"
16 | },
17 | "globals": {
18 | "strapi": true
19 | },
20 | "rules": {
21 | "indent": ["error", 2, { "SwitchCase": 1 }],
22 | "linebreak-style": ["error", "unix"],
23 | "no-console": 0,
24 | "quotes": ["error", "single"],
25 | "semi": ["error", "always"]
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/playground/.gitignore:
--------------------------------------------------------------------------------
1 | ############################
2 | # OS X
3 | ############################
4 |
5 | .DS_Store
6 | .AppleDouble
7 | .LSOverride
8 | Icon
9 | .Spotlight-V100
10 | .Trashes
11 | ._*
12 |
13 |
14 | ############################
15 | # Linux
16 | ############################
17 |
18 | *~
19 |
20 |
21 | ############################
22 | # Windows
23 | ############################
24 |
25 | Thumbs.db
26 | ehthumbs.db
27 | Desktop.ini
28 | $RECYCLE.BIN/
29 | *.cab
30 | *.msi
31 | *.msm
32 | *.msp
33 |
34 |
35 | ############################
36 | # Packages
37 | ############################
38 |
39 | *.7z
40 | *.csv
41 | *.dat
42 | *.dmg
43 | *.gz
44 | *.iso
45 | *.jar
46 | *.rar
47 | *.tar
48 | *.zip
49 | *.com
50 | *.class
51 | *.dll
52 | *.exe
53 | *.o
54 | *.seed
55 | *.so
56 | *.swo
57 | *.swp
58 | *.swn
59 | *.swm
60 | *.out
61 | *.pid
62 |
63 |
64 | ############################
65 | # Logs and databases
66 | ############################
67 |
68 | .tmp
69 | *.log
70 | *.sql
71 | *.sqlite
72 | *.sqlite3
73 |
74 |
75 | ############################
76 | # Misc.
77 | ############################
78 |
79 | *#
80 | ssl
81 | .idea
82 | nbproject
83 | public/uploads/*
84 | !public/uploads/.gitkeep
85 |
86 | ############################
87 | # Node.js
88 | ############################
89 |
90 | lib-cov
91 | lcov.info
92 | pids
93 | logs
94 | results
95 | node_modules
96 | .node_history
97 | yarn.lock
98 |
99 | ############################
100 | # Tests
101 | ############################
102 |
103 | testApp
104 | coverage
105 | /config/sync
106 |
107 | ############################
108 | # Strapi
109 | ############################
110 |
111 | # .env
112 | license.txt
113 | exports
114 | *.cache
115 | build
116 | .strapi-updater.json
117 |
118 | # yalc
119 | .yalc
120 | yalc.lock
121 |
--------------------------------------------------------------------------------
/playground/.strapi/client/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file was automatically generated by Strapi.
3 | * Any modifications made will be discarded.
4 | */
5 | import strapiCloud from "@strapi/plugin-cloud/strapi-admin";
6 | import usersPermissions from "@strapi/plugin-users-permissions/strapi-admin";
7 | import configSync from "strapi-plugin-config-sync/strapi-admin";
8 | import { renderAdmin } from "@strapi/strapi/admin";
9 |
10 | renderAdmin(document.getElementById("strapi"), {
11 | plugins: {
12 | "strapi-cloud": strapiCloud,
13 | "users-permissions": usersPermissions,
14 | "config-sync": configSync,
15 | },
16 | });
17 |
--------------------------------------------------------------------------------
/playground/.strapi/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
13 |
14 |
15 | Strapi Admin
16 |
27 |
28 |
29 |
30 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/playground/__tests__/helpers.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const { createStrapi, compileStrapi } = require('@strapi/strapi');
3 |
4 | let instance;
5 |
6 | async function setupStrapi() {
7 | if (!instance) {
8 | const appContext = await compileStrapi();
9 | await createStrapi(appContext).load();
10 | instance = strapi;
11 |
12 | await instance.server.mount();
13 | }
14 | return instance;
15 | }
16 |
17 | async function cleanupStrapi() {
18 | const dbSettings = strapi.config.get('database.connection');
19 |
20 | // close server to release the db-file.
21 | await strapi.server.httpServer.close();
22 |
23 | // close the connection to the database before deletion.
24 | await strapi.db.connection.destroy();
25 |
26 | // delete test database after all tests have completed.
27 | if (dbSettings && dbSettings.connection && dbSettings.connection.filename) {
28 | const tmpDbFile = dbSettings.connection.filename;
29 | if (fs.existsSync(tmpDbFile)) {
30 | fs.unlinkSync(tmpDbFile);
31 | }
32 | }
33 | }
34 |
35 | module.exports = { setupStrapi, cleanupStrapi };
36 |
--------------------------------------------------------------------------------
/playground/__tests__/import-on-boostrap.test.js:
--------------------------------------------------------------------------------
1 | const util = require('util');
2 | const exec = util.promisify(require('child_process').exec);
3 |
4 | const { setupStrapi, cleanupStrapi } = require('./helpers');
5 |
6 | jest.setTimeout(20000);
7 |
8 | afterEach(async () => {
9 | // Disable importOnBootstrap
10 | await exec('sed -i "s/importOnBootstrap: true/importOnBootstrap: false/g" config/plugins.js');
11 |
12 | await cleanupStrapi();
13 | await exec('rm -rf config/sync');
14 | });
15 |
16 | describe('Test the importOnBootstrap feature', () => {
17 | test('Without a database', async () => {
18 | // Do the initial export and remove the database.
19 | await exec('yarn cs export -y');
20 | await exec('rm -rf .tmp');
21 |
22 | // Manually change the plugins.js to enable importOnBoostrap.
23 | await exec('sed -i "s/importOnBootstrap: false/importOnBootstrap: true/g" config/plugins.js');
24 |
25 | // Start up Strapi to initiate the importOnBootstrap function.
26 | await setupStrapi();
27 |
28 | expect(strapi).toBeDefined();
29 | });
30 |
31 | test('With a database', async () => {
32 | // Delete any existing database and do an export.
33 | await exec('rm -rf .tmp');
34 | await exec('yarn cs export -y');
35 |
36 | // Manually change the plugins.js to enable importOnBoostrap.
37 | await exec('sed -i "s/importOnBootstrap: false/importOnBootstrap: true/g" config/plugins.js');
38 |
39 | // Remove a config file to make sure the importOnBoostrap
40 | // function actually attempts to import.
41 | await exec('rm -rf config/sync/admin-role.strapi-editor.json');
42 |
43 | // Start up Strapi to initiate the importOnBootstrap function.
44 | await setupStrapi();
45 |
46 | expect(strapi).toBeDefined();
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/playground/config/admin.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ env }) => ({
2 | auth: {
3 | secret: env('ADMIN_JWT_SECRET'),
4 | },
5 | apiToken: {
6 | salt: env('API_TOKEN_SALT'),
7 | },
8 | rateLimit: {
9 | enabled: false,
10 | },
11 | transfer: {
12 | token: {
13 | salt: env('TRANSFER_TOKEN_SALT'),
14 | },
15 | },
16 | flags: {
17 | nps: env.bool('FLAG_NPS', true),
18 | promoteEE: env.bool('FLAG_PROMOTE_EE', true),
19 | },
20 | watchIgnoreFiles: [
21 | '!**/.yalc/**/server/**',
22 | '**/config/sync/**',
23 | ]
24 | });
25 |
--------------------------------------------------------------------------------
/playground/config/api.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rest: {
3 | defaultLimit: 25,
4 | maxLimit: 100,
5 | withCount: true,
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/playground/config/database.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = ({ env }) => ({
4 | connection: {
5 | client: 'sqlite',
6 | connection: {
7 | filename: path.join(__dirname, '..', env('DATABASE_FILENAME', '.tmp/data.db')),
8 | },
9 | useNullAsDefault: true,
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/playground/config/middlewares.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | 'strapi::logger',
3 | 'strapi::errors',
4 | 'strapi::security',
5 | 'strapi::cors',
6 | 'strapi::poweredBy',
7 | 'strapi::query',
8 | 'strapi::body',
9 | 'strapi::session',
10 | 'strapi::favicon',
11 | 'strapi::public',
12 | ];
13 |
--------------------------------------------------------------------------------
/playground/config/plugins.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'config-sync': {
3 | enabled: true,
4 | config: {
5 | importOnBootstrap: false,
6 | minify: true,
7 | },
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/playground/config/server.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ env }) => ({
2 | host: env('HOST', '0.0.0.0'),
3 | port: env.int('PORT', 1337),
4 | app: {
5 | keys: env.array('APP_KEYS'),
6 | },
7 | webhooks: {
8 | populateRelations: env.bool('WEBHOOKS_POPULATE_RELATIONS', false),
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/playground/database/migrations/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/playground/database/migrations/.gitkeep
--------------------------------------------------------------------------------
/playground/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/playground/favicon.png
--------------------------------------------------------------------------------
/playground/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'Integration test',
3 | testMatch: ['**/__tests__/?(*.)+(spec|test).js'],
4 | transform: {},
5 | coverageDirectory: '../coverage/',
6 | collectCoverage: true,
7 | };
8 |
--------------------------------------------------------------------------------
/playground/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "moduleResolution": "nodenext",
4 | "target": "ES2021",
5 | "checkJs": true,
6 | "allowJs": true
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "strapi-5-beta",
3 | "private": true,
4 | "version": "0.1.0",
5 | "description": "A Strapi application",
6 | "scripts": {
7 | "develop": "strapi develop",
8 | "start": "strapi start",
9 | "build": "strapi build",
10 | "strapi": "strapi",
11 | "cs": "config-sync"
12 | },
13 | "devDependencies": {
14 | "jest": "^29.7.0",
15 | "jest-cli": "^29.7.0",
16 | "supertest": "^6.3.3",
17 | "yalc": "^1.0.0-pre.53"
18 | },
19 | "dependencies": {
20 | "@strapi/plugin-cloud": "5.0.4",
21 | "@strapi/plugin-users-permissions": "5.0.4",
22 | "@strapi/strapi": "5.0.4",
23 | "better-sqlite3": "9.4.3",
24 | "react": "^18.0.0",
25 | "react-dom": "^18.0.0",
26 | "react-router-dom": "^6.0.0",
27 | "strapi-plugin-config-sync": "link:.yalc/strapi-plugin-config-sync",
28 | "styled-components": "^6.0.0"
29 | },
30 | "author": {
31 | "name": "A Strapi developer"
32 | },
33 | "strapi": {
34 | "uuid": "edadddbd-0f25-4da7-833b-d4cd7dcae2fc"
35 | },
36 | "engines": {
37 | "node": ">=18.0.0 <=20.x.x",
38 | "npm": ">=6.0.0"
39 | },
40 | "license": "MIT"
41 | }
42 |
--------------------------------------------------------------------------------
/playground/public/robots.txt:
--------------------------------------------------------------------------------
1 | # To prevent search engines from seeing the site altogether, uncomment the next two lines:
2 | # User-Agent: *
3 | # Disallow: /
4 |
--------------------------------------------------------------------------------
/playground/public/uploads/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/playground/public/uploads/.gitkeep
--------------------------------------------------------------------------------
/playground/src/admin/app.example.js:
--------------------------------------------------------------------------------
1 | export default {
2 | config: {
3 | locales: [
4 | // 'ar',
5 | // 'fr',
6 | // 'cs',
7 | // 'de',
8 | // 'dk',
9 | // 'es',
10 | // 'he',
11 | // 'id',
12 | // 'it',
13 | // 'ja',
14 | // 'ko',
15 | // 'ms',
16 | // 'nl',
17 | // 'no',
18 | // 'pl',
19 | // 'pt-BR',
20 | // 'pt',
21 | // 'ru',
22 | // 'sk',
23 | // 'sv',
24 | // 'th',
25 | // 'tr',
26 | // 'uk',
27 | // 'vi',
28 | // 'zh-Hans',
29 | // 'zh',
30 | ],
31 | },
32 | bootstrap(app) {
33 | console.log(app);
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/playground/src/admin/webpack.config.example.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* eslint-disable no-unused-vars */
4 | module.exports = (config, webpack) => {
5 | // Note: we provide webpack above so you should not `require` it
6 | // Perform customizations to webpack config
7 | // Important: return the modified config
8 | return config;
9 | };
10 |
--------------------------------------------------------------------------------
/playground/src/api/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/playground/src/api/.gitkeep
--------------------------------------------------------------------------------
/playground/src/api/home/content-types/home/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "singleType",
3 | "collectionName": "homes",
4 | "info": {
5 | "singularName": "home",
6 | "pluralName": "homes",
7 | "displayName": "Home",
8 | "description": ""
9 | },
10 | "options": {
11 | "draftAndPublish": false
12 | },
13 | "pluginOptions": {},
14 | "attributes": {
15 | "title": {
16 | "type": "string"
17 | },
18 | "slug": {
19 | "type": "uid",
20 | "targetField": "title"
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/playground/src/api/home/controllers/home.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * home controller
5 | */
6 |
7 | const { createCoreController } = require('@strapi/strapi').factories;
8 |
9 | module.exports = createCoreController('api::home.home');
10 |
--------------------------------------------------------------------------------
/playground/src/api/home/routes/home.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * home router
5 | */
6 |
7 | const { createCoreRouter } = require('@strapi/strapi').factories;
8 |
9 | module.exports = createCoreRouter('api::home.home');
10 |
--------------------------------------------------------------------------------
/playground/src/api/home/services/home.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * home service
5 | */
6 |
7 | const { createCoreService } = require('@strapi/strapi').factories;
8 |
9 | module.exports = createCoreService('api::home.home');
10 |
--------------------------------------------------------------------------------
/playground/src/api/page/content-types/page/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "collectionType",
3 | "collectionName": "pages",
4 | "info": {
5 | "singularName": "page",
6 | "pluralName": "pages",
7 | "displayName": "Page"
8 | },
9 | "options": {
10 | "draftAndPublish": false
11 | },
12 | "pluginOptions": {},
13 | "attributes": {
14 | "title": {
15 | "type": "string",
16 | "required": true
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/playground/src/api/page/controllers/page.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * page controller
5 | */
6 |
7 | const { createCoreController } = require('@strapi/strapi').factories;
8 |
9 | module.exports = createCoreController('api::page.page');
10 |
--------------------------------------------------------------------------------
/playground/src/api/page/routes/page.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * page router
5 | */
6 |
7 | const { createCoreRouter } = require('@strapi/strapi').factories;
8 |
9 | module.exports = createCoreRouter('api::page.page');
10 |
--------------------------------------------------------------------------------
/playground/src/api/page/services/page.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * page service
5 | */
6 |
7 | const { createCoreService } = require('@strapi/strapi').factories;
8 |
9 | module.exports = createCoreService('api::page.page');
10 |
--------------------------------------------------------------------------------
/playground/src/extensions/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pluginpal/strapi-plugin-config-sync/8a7d4baca80c6895b7f46acb4fe70d04a3fd6de7/playground/src/extensions/.gitkeep
--------------------------------------------------------------------------------
/playground/src/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | /**
5 | * An asynchronous register function that runs before
6 | * your application is initialized.
7 | *
8 | * This gives you an opportunity to extend code.
9 | */
10 | register(/*{ strapi }*/) {},
11 |
12 | /**
13 | * An asynchronous bootstrap function that runs before
14 | * your application gets started.
15 | *
16 | * This gives you an opportunity to set up your data model,
17 | * run jobs, or perform some special logic.
18 | */
19 | bootstrap(/*{ strapi }*/) {},
20 | };
21 |
--------------------------------------------------------------------------------
/playground/types/generated/components.d.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * The app doesn't have any components yet.
3 | */
4 |
--------------------------------------------------------------------------------
/server/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default {
4 | default: {
5 | syncDir: "config/sync/",
6 | minify: false,
7 | soft: false,
8 | importOnBootstrap: false,
9 | customTypes: [],
10 | excludedTypes: [],
11 | excludedConfig: [
12 | "core-store.plugin_users-permissions_grant",
13 | "core-store.plugin_upload_metrics",
14 | "core-store.plugin_upload_api-folder",
15 | "core-store.strapi_content_types_schema",
16 | "core-store.ee_information",
17 | ],
18 | },
19 | validator() {},
20 | };
21 |
--------------------------------------------------------------------------------
/server/config/types.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const types = (strapi) => {
4 | // Initiate Strapi 'core-store' and 'admin-role' types.
5 | const typesArray = [
6 | {
7 | configName: 'core-store',
8 | queryString: 'strapi::core-store',
9 | uid: 'key',
10 | jsonFields: ['value'],
11 | },
12 | {
13 | configName: 'admin-role',
14 | queryString: 'admin::role',
15 | uid: 'code',
16 | relations: [{
17 | queryString: 'admin::permission',
18 | relationName: 'permissions',
19 | parentName: 'role',
20 | relationSortFields: ['action', 'subject'],
21 | }],
22 | },
23 | ];
24 |
25 | // Register plugin users-permissions 'role' type.
26 | if (strapi.plugin('users-permissions')) {
27 | typesArray.push({
28 | configName: 'user-role',
29 | queryString: 'plugin::users-permissions.role',
30 | uid: 'type',
31 | relations: [{
32 | queryString: 'plugin::users-permissions.permission',
33 | relationName: 'permissions',
34 | parentName: 'role',
35 | relationSortFields: ['action'],
36 | }],
37 | });
38 | }
39 |
40 | // Register plugin i18n 'locale' type.
41 | if (strapi.plugin('i18n')) {
42 | typesArray.push({
43 | configName: 'i18n-locale',
44 | queryString: 'plugin::i18n.locale',
45 | uid: 'code',
46 | });
47 | }
48 |
49 | return typesArray;
50 | };
51 |
52 | export default types;
53 |
--------------------------------------------------------------------------------
/server/controllers/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import config from './config';
4 |
5 | export default {
6 | config: config,
7 | };
8 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import register from './register';
4 | import bootstrap from './bootstrap';
5 | import services from './services';
6 | import routes from './routes';
7 | import config from './config';
8 | import controllers from './controllers';
9 |
10 | export default {
11 | register,
12 | bootstrap,
13 | routes,
14 | config,
15 | controllers,
16 | services,
17 | };
18 |
--------------------------------------------------------------------------------
/server/register.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default async ({ strapi }) => {
4 | // Instantiate the pluginTypes array.
5 | if (!strapi.plugin('config-sync').pluginTypes) {
6 | strapi.plugin('config-sync').pluginTypes = [];
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/server/routes/admin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default {
4 | type: 'admin',
5 | routes: [
6 | {
7 | method: "POST",
8 | path: "/export",
9 | handler: "config.exportAll",
10 | config: {
11 | policies: [],
12 | },
13 | },
14 | {
15 | method: "POST",
16 | path: "/import",
17 | handler: "config.importAll",
18 | config: {
19 | policies: [],
20 | },
21 | },
22 | {
23 | method: "GET",
24 | path: "/diff",
25 | handler: "config.getDiff",
26 | config: {
27 | policies: [],
28 | },
29 | },
30 | {
31 | method: "GET",
32 | path: "/zip",
33 | handler: "config.zipConfig",
34 | config: {
35 | policies: [],
36 | },
37 | },
38 | {
39 | method: "GET",
40 | path: "/app-env",
41 | handler: "config.getAppEnv",
42 | config: {
43 | policies: [],
44 | },
45 | },
46 | ],
47 | };
48 |
--------------------------------------------------------------------------------
/server/routes/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import adminRoutes from './admin';
4 |
5 | export default {
6 | admin: adminRoutes,
7 | };
8 |
--------------------------------------------------------------------------------
/server/services/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import main from './main';
4 |
5 | export default {
6 | main,
7 | };
8 |
--------------------------------------------------------------------------------
/server/utils/getArrayDiff.js:
--------------------------------------------------------------------------------
1 | export const difference = (arrayOne, arrayTwo, compareKeys) => {
2 | return arrayOne.filter(({
3 | [compareKeys[0]]: id1,
4 | [compareKeys[1]]: id2,
5 | }) => {
6 | return !arrayTwo.some(({
7 | [compareKeys[0]]: id3,
8 | [compareKeys[1]]: id4,
9 | }) => id1 === id3 && id2 === id4);
10 | });
11 | };
12 |
13 | export const same = (arrayOne, arrayTwo, compareKeys) => {
14 | return arrayOne.filter(({
15 | [compareKeys[0]]: id1,
16 | [compareKeys[1]]: id2,
17 | ...restOne
18 | }) => {
19 | return !arrayTwo.some(({
20 | [compareKeys[0]]: id3,
21 | [compareKeys[1]]: id4,
22 | ...restTwo
23 | }) => id1 === id3
24 | && id2 === id4
25 | && JSON.stringify(restOne) === JSON.stringify(restTwo));
26 | });
27 | };
28 |
--------------------------------------------------------------------------------
/server/utils/getObjectDiff.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import transform from 'lodash/transform';
4 | import isEqual from 'lodash/isEqual';
5 | import isArray from 'lodash/isArray';
6 | import isObject from 'lodash/isObject';
7 |
8 | /**
9 | * Find difference between two objects
10 | * @param {object} origObj - Source object to compare newObj against
11 | * @param {object} newObj - New object with potential changes
12 | * @return {object} differences
13 | */
14 | const difference = (origObj, newObj) => {
15 | let arrayIndexCounter = 0;
16 |
17 | const newObjChange = transform(newObj, (result, value, key) => {
18 | if (!isEqual(value, origObj[key])) {
19 | const resultKey = isArray(origObj) ? arrayIndexCounter++ : key;
20 | result[resultKey] = (isObject(value) && isObject(origObj[key])) ? difference(value, origObj[key]) : value;
21 | }
22 | });
23 | const origObjChange = transform(origObj, (result, value, key) => {
24 | if (!isEqual(value, newObj[key])) {
25 | const resultKey = isArray(newObj) ? arrayIndexCounter++ : key;
26 | result[resultKey] = (isObject(value) && isObject(newObj[key])) ? difference(value, newObj[key]) : value;
27 | }
28 | });
29 |
30 | return Object.assign(newObjChange, origObjChange);
31 | };
32 |
33 | export default difference;
34 |
--------------------------------------------------------------------------------
/server/utils/queryFallBack.js:
--------------------------------------------------------------------------------
1 | const queryFallBack = {
2 | create: async (queryString, options) => {
3 | try {
4 | const newEntity = await strapi.documents(queryString).create(options);
5 |
6 | return newEntity;
7 | } catch (e) {
8 | return strapi.query(queryString).create(options);
9 | }
10 | },
11 | update: async (queryString, options) => {
12 | try {
13 | const entity = await strapi.query(queryString).findOne(options);
14 | const updatedEntity = await strapi.documents(queryString).update({
15 | documentId: entity.documentId,
16 | ...options,
17 | });
18 |
19 | return updatedEntity;
20 | } catch (e) {
21 | return strapi.query(queryString).update(options);
22 | }
23 | },
24 | delete: async (queryString, options) => {
25 | try {
26 | const entity = await strapi.query(queryString).findOne(options);
27 | await strapi.documents(queryString).delete({
28 | documentId: entity.documentId,
29 | });
30 | } catch (e) {
31 | await strapi.query(queryString).delete(options);
32 | }
33 | },
34 | };
35 |
36 | export default queryFallBack;
37 |
--------------------------------------------------------------------------------
/server/warnings.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default {
4 | delete: {
5 | 'user-role.public': "You've just deleted a default role from the users-permissions plugin.",
6 | 'user-role.authenticated': "You've just deleted a default role from the users-permissions plugin.",
7 | 'admin-role.strapi-super-admin': "You've just deleted one of the default admin roles from Strapi.",
8 | 'admin-role.strapi-author': "You've just deleted one of the default admin roles from Strapi.",
9 | 'admin-role.strapi-editor': "You've just deleted one of the default admin roles from Strapi.",
10 | },
11 | };
12 |
--------------------------------------------------------------------------------