diff --git a/docs/.prettierignore b/docs/.prettierignore new file mode 100644 index 0000000..b5f1a4a --- /dev/null +++ b/docs/.prettierignore @@ -0,0 +1 @@ +*/**/*.mdx diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 91d493f..9738f1e 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -25,6 +25,11 @@ export default defineConfig({ path: '/reference/views', autogenerate: { directory: 'reference/views', collapsed: true }, }, + { + label: 'Frontend', + path: '/reference/frontend', + autogenerate: { directory: 'reference/frontend', collapsed: true }, + }, ], }, ], diff --git a/docs/src/content/docs/guides/frontend-deployment.mdx b/docs/src/content/docs/guides/frontend-deployment.mdx new file mode 100644 index 0000000..3fb7910 --- /dev/null +++ b/docs/src/content/docs/guides/frontend-deployment.mdx @@ -0,0 +1,132 @@ +--- +title: 'Frontend Deployment' +description: How to deploy Adomin frontend +sidebar: + order: 4 +--- + +To deploy the Adomin frontend, you will need to: + +- run `yarn build` with correct VITE_API_URL env variable set +- copy dist folder to your static files service +- serve those files with a rule for SPAs (e.g. serving the index.html on 404) + +## S3 / Cloudfront + +```fish +# copy dist files into your s3 bucket +aws s3 sync ./dist s3://your-s3-bucket-name/ + +# invalidate cloudfront distribution to serve the new files +aws cloudfront create-invalidation --distribution-id YOUR_DISTRIB_ID --paths "/*" +``` + +### Github action for S3 / Cloudfront + +```yml +name: Deploy staging ⚙️ +on: + push: + branches: + - does-not-exist # replace by e.g: staging + +jobs: + back-office: + runs-on: ubuntu-latest + env: + VITE_API_URL: https://api.staging.your-own-domain.fr/ # replace by your API url + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: yarn + - name: Build + run: yarn build + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + # you will have to create AWS_ACCESS_KEY and AWS_SECRET_KEY secrets in your repository settings + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-region: eu-west-3 + - name: Deploy + run: aws s3 sync ./dist s3://your-s3-bucket-name/ # replace by your s3 bucket name + - name: Invalidate dashboard cloudfront + run: aws cloudfront create-invalidation --distribution-id YOUR_DISTRIB_ID --paths "/*" # replace by your cloudfront distribution id +``` + +## Caddy + +Example of Caddy config to serve a SPA + +``` +example.com { + root * /usr/share/caddy/frontend + file_server + try_files {path} {path}/ /index.html +} +``` + +### Github action for Caddy + +:::note +This github action is taken from a project that uses [Galacrypt](https://github.com/galadrimteam/galacrypt) to encrypt/decrypt the env file +::: + +```yml +name: Deploy staging ⚙️ +on: + push: + branches: + - does-not-exist # replace by e.g: staging + +jobs: + frontend: + runs-on: ubuntu-latest + env: + VITE_API_URL: https://api.staging.your-own-domain.fr/ # replace by your API url + SERVER_IP: 1.2.3.4 # replace by your server IP + KEY_PATH: ./ssh_keys/staging/id_ed25519 # do not commit/push this file directly + SSH_USER: YOUR_SSH_USER # replace by your ssh user + SSH_PARAMS: -i ./ssh_keys/staging/id_ed25519 YOUR_SSH_USER@1.2.3.4 # replace dummy user / ip + + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + cache: 'yarn' + + - name: Dependencies installation + run: yarn + + - name: Galacrypt decrypt # used to decrypt ssh key, you will have to create GALACRYPT_KEY in your repository secrets + run: yarn galacrypt use ${{ secrets.GALACRYPT_KEY }} && yarn galacrypt decrypt + + - name: Generate build + run: yarn build + + - name: Generate frontend zip + run: cd dist && zip -r ../frontend.zip . + + - name: Setup SSH + run: | + mkdir -p ~/.ssh + ssh-keyscan $SERVER_IP 2>/dev/null > ~/.ssh/known_hosts + chmod 600 $KEY_PATH + + - name: Copy files + run: scp -i $KEY_PATH ./frontend.zip $SSH_USER@$SERVER_IP:/home/$SSH_USER/frontend.zip + + - name: Unzip + run: ssh $SSH_PARAMS "cd /home/$SSH_USER/ && rm -rf front-build && mkdir front-build && mv frontend.zip front-build/ && cd front-build && unzip frontend.zip && rm frontend.zip" + + - name: Move website files + run: ssh $SSH_PARAMS "cd /home/$SSH_USER/ && (sudo mv /usr/share/caddy/frontend old_frontend || echo 'skipping mv old_frontend') && sudo mv front-build /usr/share/caddy/frontend && sudo rm -rf old_frontend" +``` + +## Nginx + +I don't want to deal with nginx anymore, but if you want to, you can find inspiration [here](https://sdickinson.com/nginx-config-for-single-page-applications/) diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index 15b7c08..0645884 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -40,7 +40,7 @@ import { Card, CardGrid, LinkCard } from "@astrojs/starlight/components"; - - icons from tabler icons + - icons from [tabler icons](https://tabler.io/icons) - folders and subfolders (with no limit) diff --git a/docs/src/content/docs/reference/frontend/routing.mdx b/docs/src/content/docs/reference/frontend/routing.mdx new file mode 100644 index 0000000..4a69764 --- /dev/null +++ b/docs/src/content/docs/reference/frontend/routing.mdx @@ -0,0 +1,134 @@ +--- +title: Frontend Routing +description: Adomin frontend routing reference +sidebar: + order: 0 +--- + +You might have some questions: + +- how does the frontend routing work ? +- how to add custom routes ? + +## Router file + +It all begins with the **react-router-dom** router + +import { FileTree } from '@astrojs/starlight/components' + + + - src + - [router.tsx](https://github.com/galadrimteam/adomin-frontend/blob/main/src/router.tsx) Frontend react-router-dom router + + +Inside this file is all the routing + +If this documentation is up to date 😳, it will look like this: + +```tsx +export const adominRoutes = createBrowserRouter([ + { + path: '/', + element: , + }, + { + path: ADOMIN_HOME_PATH, + element: , + }, + { + path: ADOMIN_FOLDERS_PATH, + children: [ + { + path: ':view', + element: , + }, + ], + element: , + }, + { + path: ADOMIN_STATS_PATH, + children: [ + { + path: ':view', + element: , + }, + ], + element: , + }, + { + path: ADOMIN_MODELS_PATH, + children: [ + { + path: ':view', + children: [ + { index: true, element: }, + { path: 'create', element: }, + { path: ':primaryKeyValue', element: }, + ], + element: , + }, + ], + element: , + }, + { + path: ADOMIN_LOGIN_PATH, + element: , + }, +]) +``` + +You can see that it is creating dynamic paths, those paths will be determined with your backend adomin config. + +To be clear, if you do not change the `ADOMIN_*_PATH` variables, the following paths are created: + +```bash +/ # this path just redirects to /adomin, you can customize/remove this behaviour if you don't like it +/login + +/backoffice/folders/:view # this dynamic path renders a folder view, which will redirect you to the nearest non-folder view +/backoffice/stats/:view # this dynamic path renders a stat view + +/backoffice # this path adds a layout around the children of this route +/backoffice/:model # this is the list page +/backoffice/:model/create # this is the create page +/backoffice/:model/:primaryKeyValue # this is the update page +``` + +and here is an example if you configured adomin with the User model + +```bash +/backoffice/User +/backoffice/User/create +/backoffice/User/1 # page to update user with id = 1 +``` + +## Custom routes and overrides + +If you want to override some page, let's say the create and the update page for your resource User, you will be able to do so by using the `makeOverridePage` helper, then put it somewhere in the router config, here is an example: + +```tsx +export const adominRoutes = createBrowserRouter([ + // default adomin config + // ... + + // your config + makeOverridePage({ model: 'User', type: 'create' }, ), + makeOverridePage({ model: 'User', type: 'update' }, ), + makeOverridePage({ model: 'User', type: 'list' }, ), +]) +``` + +:::note +💡 the `makeOverridePage` helper just creates a `RouteObject` wrapping the override component with a `CustomPage`, you can do it manually if you want more control: + +```tsx +{ + path: "/backoffice/User/create", + element: + + + , +} +``` + +:::