mirror of
https://github.com/penpot/penpot.git
synced 2026-01-28 00:03:02 -05:00
Compare commits
210 Commits
alotor-plu
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fda31624c1 | ||
|
|
7f640569bd | ||
|
|
91f1323802 | ||
|
|
dbd4a2366f | ||
|
|
cbb6d098a7 | ||
|
|
b6f5000d1c | ||
|
|
0527124f2f | ||
|
|
faf91ac70d | ||
|
|
9ca76c745f | ||
|
|
e8fd4698c9 | ||
|
|
0ab126748f | ||
|
|
71a5ab9913 | ||
|
|
61969f3eb5 | ||
|
|
bd2ef8057e | ||
|
|
2523096fdd | ||
|
|
8e63c4e3e8 | ||
|
|
d5abc52dac | ||
|
|
3112b240a0 | ||
|
|
56fd66b91a | ||
|
|
3b96eb5476 | ||
|
|
7a842ce36a | ||
|
|
ea25c5db99 | ||
|
|
ce1796eb02 | ||
|
|
6f0685ba8e | ||
|
|
1ce0b60e3d | ||
|
|
d433fd25c1 | ||
|
|
bb0e9b47cb | ||
|
|
ef80901400 | ||
|
|
c5f03d711a | ||
|
|
5306bed548 | ||
|
|
92a319ddd1 | ||
|
|
68a6d4c9a8 | ||
|
|
72cc5ee349 | ||
|
|
804695b48b | ||
|
|
20c8fbf314 | ||
|
|
e02536f8d4 | ||
|
|
d9c56da705 | ||
|
|
75248aec4e | ||
|
|
f0d9429775 | ||
|
|
62ecf48bdb | ||
|
|
18de7f1db6 | ||
|
|
2b2941bd25 | ||
|
|
f2d561eff7 | ||
|
|
418b65a287 | ||
|
|
d4e7810eba | ||
|
|
1d1d32ad39 | ||
|
|
fb08dc65c8 | ||
|
|
927ac93fa7 | ||
|
|
e546a7c614 | ||
|
|
058c20c2e2 | ||
|
|
5016b2a7bf | ||
|
|
43ae213659 | ||
|
|
dc973dac36 | ||
|
|
4467827218 | ||
|
|
6470db8d5f | ||
|
|
dc44156b53 | ||
|
|
f0e53d70ae | ||
|
|
ef73a263b2 | ||
|
|
9b1e007a49 | ||
|
|
ea8632e56a | ||
|
|
2d00e64ede | ||
|
|
1246250198 | ||
|
|
34f2943dcd | ||
|
|
3fb78116b8 | ||
|
|
072e415b9e | ||
|
|
67a904824c | ||
|
|
68184209be | ||
|
|
d2295862b4 | ||
|
|
23cbf33d1b | ||
|
|
b4ff0ccf3a | ||
|
|
6c6666a39a | ||
|
|
c1335961b4 | ||
|
|
eaf64b6e16 | ||
|
|
560a0d09d5 | ||
|
|
b70eb768e0 | ||
|
|
4397ede5c1 | ||
|
|
e0910db99e | ||
|
|
079b3fbfad | ||
|
|
299f628951 | ||
|
|
32d0fe6463 | ||
|
|
cecd3d4a90 | ||
|
|
1c2c0987f5 | ||
|
|
0418147e74 | ||
|
|
9e0ba4429a | ||
|
|
7499a5bca6 | ||
|
|
6cd5bc76d7 | ||
|
|
bbe6ee2e19 | ||
|
|
fb6d8309b6 | ||
|
|
689467bcf9 | ||
|
|
7724450037 | ||
|
|
368fa954ce | ||
|
|
0ecb2bc838 | ||
|
|
e92f3fb3cb | ||
|
|
5193cfd56e | ||
|
|
813d5d8e69 | ||
|
|
84f1ff092d | ||
|
|
f2b082b93e | ||
|
|
1f41bef4a9 | ||
|
|
fdf5bb250b | ||
|
|
786736fadd | ||
|
|
1ff6e00398 | ||
|
|
25455523ad | ||
|
|
8dfeb21978 | ||
|
|
ad2833bb7a | ||
|
|
21911e898f | ||
|
|
538073debf | ||
|
|
214b0efa02 | ||
|
|
661436ecae | ||
|
|
0d5fe6e527 | ||
|
|
e7230d9da4 | ||
|
|
5054f6bc38 | ||
|
|
38396ba299 | ||
|
|
b1997a83b3 | ||
|
|
68f5671eab | ||
|
|
92976143bb | ||
|
|
dd2d03e6a0 | ||
|
|
fd675e0194 | ||
|
|
c98373658e | ||
|
|
2e400768b7 | ||
|
|
01e42b0458 | ||
|
|
7529673812 | ||
|
|
d2dad35d7a | ||
|
|
f7b5266304 | ||
|
|
09c23256b7 | ||
|
|
1ae1c0460e | ||
|
|
291c7349db | ||
|
|
b605a3b53d | ||
|
|
cc2dab2756 | ||
|
|
d0c0664338 | ||
|
|
2240f25143 | ||
|
|
93a5ec2f5d | ||
|
|
d6784771a8 | ||
|
|
930c814ded | ||
|
|
1a5a69bca2 | ||
|
|
9ad323a220 | ||
|
|
bcaf76d055 | ||
|
|
5fa4368d70 | ||
|
|
b8efd2518d | ||
|
|
7b2271ec38 | ||
|
|
2240d93069 | ||
|
|
3f4506284b | ||
|
|
af1dfd91aa | ||
|
|
24feebd73b | ||
|
|
33e5a9a538 | ||
|
|
9c69b07a62 | ||
|
|
56f5be4f37 | ||
|
|
8a70204d41 | ||
|
|
57a27f7e7f | ||
|
|
3b0b2a78d6 | ||
|
|
10bf4610df | ||
|
|
77e8414aea | ||
|
|
20ecf3b066 | ||
|
|
49b1032973 | ||
|
|
5ba7dd8c56 | ||
|
|
38b5125186 | ||
|
|
6677ae83d4 | ||
|
|
0737c055f0 | ||
|
|
4b88748fe3 | ||
|
|
92107e5b1e | ||
|
|
ebc0e3a23c | ||
|
|
ebe4f2da50 | ||
|
|
a07c1d6eaa | ||
|
|
613bfda955 | ||
|
|
f7ef6618e5 | ||
|
|
fe334d9cbe | ||
|
|
268b883c73 | ||
|
|
f6a4effa29 | ||
|
|
ced848077e | ||
|
|
7d9d318539 | ||
|
|
9781fceadb | ||
|
|
3178bd9a27 | ||
|
|
e5d677f449 | ||
|
|
6bf928893c | ||
|
|
53dd90aa24 | ||
|
|
9fd0f6a8f3 | ||
|
|
638c3356d3 | ||
|
|
6879f54e5d | ||
|
|
a71baa5a78 | ||
|
|
8e4a89bd1c | ||
|
|
7aad9da285 | ||
|
|
ab57a4ae52 | ||
|
|
266ee29bb9 | ||
|
|
69ca86bb6c | ||
|
|
ee14a845fc | ||
|
|
73639f5d16 | ||
|
|
9bd106b2bc | ||
|
|
59c75afc7b | ||
|
|
bbc81586e3 | ||
|
|
c9c30eab75 | ||
|
|
86ba9280db | ||
|
|
5800cc4bb2 | ||
|
|
aa29a34c4c | ||
|
|
3276129cc7 | ||
|
|
67a96de475 | ||
|
|
48785b4846 | ||
|
|
3f0573f95d | ||
|
|
d94a2a8881 | ||
|
|
1c237a0968 | ||
|
|
b7eaeffa88 | ||
|
|
722fcc1f82 | ||
|
|
2ad42cfd9b | ||
|
|
795f65632a | ||
|
|
7819e6c440 | ||
|
|
952f622ce9 | ||
|
|
a6c6f97f47 | ||
|
|
88424eb54a | ||
|
|
de9a21121a | ||
|
|
cea10308b7 | ||
|
|
5223c9c881 | ||
|
|
be62fa10c4 |
@@ -45,6 +45,15 @@
|
|||||||
:potok/reify-type
|
:potok/reify-type
|
||||||
{:level :error}
|
{:level :error}
|
||||||
|
|
||||||
|
:redundant-primitive-coercion
|
||||||
|
{:level :off}
|
||||||
|
|
||||||
|
:unused-excluded-var
|
||||||
|
{:level :off}
|
||||||
|
|
||||||
|
:unresolved-excluded-var
|
||||||
|
{:level :off}
|
||||||
|
|
||||||
:missing-protocol-method
|
:missing-protocol-method
|
||||||
{:level :off}
|
{:level :off}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
:remove-multiple-non-indenting-spaces? false
|
:remove-multiple-non-indenting-spaces? false
|
||||||
:remove-surrounding-whitespace? true
|
:remove-surrounding-whitespace? true
|
||||||
:remove-consecutive-blank-lines? false
|
:remove-consecutive-blank-lines? false
|
||||||
|
:indent-line-comments? true
|
||||||
|
:parallel? true
|
||||||
|
:align-form-columns? false
|
||||||
|
;; :align-map-columns? false
|
||||||
|
;; :align-single-column-lines? false
|
||||||
:extra-indents {rumext.v2/fnc [[:inner 0]]
|
:extra-indents {rumext.v2/fnc [[:inner 0]]
|
||||||
cljs.test/async [[:inner 0]]
|
cljs.test/async [[:inner 0]]
|
||||||
promesa.exec/thread [[:inner 0]]
|
promesa.exec/thread [[:inner 0]]
|
||||||
|
|||||||
38
.github/ISSUE_TEMPLATE/new-render-bug-report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/new-render-bug-report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: New Render Bug Report
|
||||||
|
about: Create a report about the bugs you have found in the new render
|
||||||
|
title: ''
|
||||||
|
labels: new render
|
||||||
|
assignees: claragvinola
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**Steps to Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots or screen recordings**
|
||||||
|
If applicable, add screenshots or screen recording to help illustrate your problem.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
- OS: [e.g. iOS]
|
||||||
|
- Browser [e.g. chrome, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Smartphone (please complete the following information):**
|
||||||
|
- Device: [e.g. iPhone6]
|
||||||
|
- OS: [e.g. iOS8.1]
|
||||||
|
- Browser [e.g. stock browser, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
2
.github/workflows/build-bundle.yml
vendored
2
.github/workflows/build-bundle.yml
vendored
@@ -40,7 +40,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-bundle:
|
build-bundle:
|
||||||
name: Build and Upload Penpot Bundle
|
name: Build and Upload Penpot Bundle
|
||||||
runs-on: ubuntu-24.04
|
runs-on: penpot-runner-01
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
|||||||
7
.github/workflows/build-docker-devenv.yml
vendored
7
.github/workflows/build-docker-devenv.yml
vendored
@@ -7,9 +7,14 @@ jobs:
|
|||||||
build-and-push:
|
build-and-push:
|
||||||
name: Build and push DevEnv Docker image
|
name: Build and push DevEnv Docker image
|
||||||
environment: release-admins
|
environment: release-admins
|
||||||
runs-on: ubuntu-24.04
|
runs-on: penpot-runner-02
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Set common environment variables
|
||||||
|
run: |
|
||||||
|
# Each job execution will use its own docker configuration.
|
||||||
|
echo "DOCKER_CONFIG=${{ runner.temp }}/.docker-${{ github.run_id }}-${{ github.job }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|||||||
16
.github/workflows/build-docker.yml
vendored
16
.github/workflows/build-docker.yml
vendored
@@ -19,9 +19,14 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-and-push:
|
build-and-push:
|
||||||
name: Build and Push Penpot Docker Images
|
name: Build and Push Penpot Docker Images
|
||||||
runs-on: ubuntu-24.04-arm
|
runs-on: penpot-runner-02
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Set common environment variables
|
||||||
|
run: |
|
||||||
|
# Each job execution will use its own docker configuration.
|
||||||
|
echo "DOCKER_CONFIG=${{ runner.temp }}/.docker-${{ github.run_id }}-${{ github.job }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -66,6 +71,15 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
# To avoid the “429 Too Many Requests” error when downloading
|
||||||
|
# images from DockerHub for unregistered users.
|
||||||
|
# https://docs.docker.com/docker-hub/usage/
|
||||||
|
- name: Login to DockerHub Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.PUB_DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.PUB_DOCKER_PASSWORD }}
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels)
|
- name: Extract metadata (tags, labels)
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
|
|||||||
21
.github/workflows/build-nitrate-module.yml
vendored
21
.github/workflows/build-nitrate-module.yml
vendored
@@ -1,21 +0,0 @@
|
|||||||
name: _NITRATE MODULE
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '36 5-20 * * 1-5'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-bundle:
|
|
||||||
uses: ./.github/workflows/build-bundle.yml
|
|
||||||
secrets: inherit
|
|
||||||
with:
|
|
||||||
gh_ref: "nitrate-module"
|
|
||||||
build_wasm: "yes"
|
|
||||||
build_storybook: "yes"
|
|
||||||
|
|
||||||
build-docker:
|
|
||||||
needs: build-bundle
|
|
||||||
uses: ./.github/workflows/build-docker.yml
|
|
||||||
secrets: inherit
|
|
||||||
with:
|
|
||||||
gh_ref: "nitrate-module"
|
|
||||||
2
.github/workflows/commit-checker.yml
vendored
2
.github/workflows/commit-checker.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- name: Check Commit Type
|
- name: Check Commit Type
|
||||||
uses: gsactions/commit-message-checker@v2
|
uses: gsactions/commit-message-checker@v2
|
||||||
with:
|
with:
|
||||||
pattern: '^(((:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle|rewind|construction_worker):)\s[A-Z].*[^.])|(Merge|Revert).+[^.])$'
|
pattern: '^(((:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle|rewind|construction_worker):)\s[A-Z].*[^.])|(Merge|Revert|Reapply).+[^.])$'
|
||||||
flags: 'gm'
|
flags: 'gm'
|
||||||
error: 'Commit should match CONTRIBUTING.md guideline'
|
error: 'Commit should match CONTRIBUTING.md guideline'
|
||||||
checkAllCommitMessages: 'true' # optional: this checks all commits associated with a pull request
|
checkAllCommitMessages: 'true' # optional: this checks all commits associated with a pull request
|
||||||
|
|||||||
42
.github/workflows/plugins-deploy-api-doc.yml
vendored
42
.github/workflows/plugins-deploy-api-doc.yml
vendored
@@ -7,11 +7,11 @@ on:
|
|||||||
- staging
|
- staging
|
||||||
- main
|
- main
|
||||||
paths:
|
paths:
|
||||||
- "plugins/libs/plugin-types/index.d.ts"
|
- 'plugins/libs/plugin-types/index.d.ts'
|
||||||
- "plugins/libs/plugin-types/REAME.md"
|
- 'plugins/libs/plugin-types/REAME.md'
|
||||||
- "plugins/tools/typedoc.css"
|
- 'plugins/tools/typedoc.css'
|
||||||
- "plugins/CHANGELOG.md"
|
- 'plugins/CHANGELOG.md'
|
||||||
- "plugins/wrangle-penpot-plugins-api-doc.toml"
|
- 'plugins/wrangler-penpot-plugins-api-doc.toml'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
gh_ref:
|
gh_ref:
|
||||||
@@ -86,16 +86,40 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
REF="${{ steps.vars.outputs.gh_ref }}"
|
REF="${{ steps.vars.outputs.gh_ref }}"
|
||||||
case "$REF" in
|
case "$REF" in
|
||||||
main) echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV ;;
|
main)
|
||||||
staging) echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV ;;
|
echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV
|
||||||
develop) echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV ;;
|
echo "WORKER_URI=doc.plugins.penpot.app" >> $GITHUB_ENV ;;
|
||||||
|
staging)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
|
develop)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
- name: Set the custom url
|
||||||
|
working-directory: plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-api-doc.toml
|
||||||
|
|
||||||
- name: Deploy to Cloudflare Workers
|
- name: Deploy to Cloudflare Workers
|
||||||
uses: cloudflare/wrangler-action@v3
|
uses: cloudflare/wrangler-action@v3
|
||||||
with:
|
with:
|
||||||
workingDirectory: plugins
|
workingDirectory: plugins
|
||||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
command: deploy --config wrangle-penpot-plugins-api-doc.toml --name ${{ env.WORKER_NAME }}
|
command: deploy --config wrangler-penpot-plugins-api-doc.toml --name ${{ env.WORKER_NAME }}
|
||||||
|
|
||||||
|
- name: Notify Mattermost
|
||||||
|
if: failure()
|
||||||
|
uses: mattermost/action-mattermost-notify@master
|
||||||
|
with:
|
||||||
|
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||||
|
MATTERMOST_CHANNEL: bot-alerts-cicd
|
||||||
|
TEXT: |
|
||||||
|
❌ 🧩📚 *[PENPOT PLUGINS] Error deploying API documentation.*
|
||||||
|
📄 Triggered from ref: `${{ inputs.gh_ref }}`
|
||||||
|
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
@infra
|
||||||
|
|||||||
127
.github/workflows/plugins-deploy-package.yml
vendored
Normal file
127
.github/workflows/plugins-deploy-package.yml
vendored
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
name: Plugins/package deployer
|
||||||
|
|
||||||
|
on:
|
||||||
|
# Deploy package from manual action
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
gh_ref:
|
||||||
|
description: 'Name of the branch'
|
||||||
|
type: choice
|
||||||
|
required: true
|
||||||
|
default: 'develop'
|
||||||
|
options:
|
||||||
|
- develop
|
||||||
|
- staging
|
||||||
|
- main
|
||||||
|
plugin_name:
|
||||||
|
description: 'Pluging name (like plugins/apps/<plugin_name>-plugin)'
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
gh_ref:
|
||||||
|
description: 'Name of the branch'
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
default: 'develop'
|
||||||
|
plugin_name:
|
||||||
|
description: 'Publig name (from plugins/apps/<plugin_name>-plugin)'
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: penpot-runner-01
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ inputs.gh_ref }}
|
||||||
|
|
||||||
|
# START: Setup Node and PNPM enabling cache
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version-file: .nvmrc
|
||||||
|
|
||||||
|
- name: Enable PNPM
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
corepack enable;
|
||||||
|
corepack install;
|
||||||
|
|
||||||
|
- name: Get pnpm store path
|
||||||
|
id: pnpm-store
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Cache pnpm store
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-
|
||||||
|
# END: Setup Node and PNPM enabling cache
|
||||||
|
|
||||||
|
- name: Install deps
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
pnpm install --no-frozen-lockfile;
|
||||||
|
pnpm add -D -w wrangler@latest;
|
||||||
|
|
||||||
|
- name: "Build package for ${{ inputs.plugin_name }}-plugin"
|
||||||
|
working-directory: plugins
|
||||||
|
shell: bash
|
||||||
|
run: npx nx build ${{ inputs.plugin_name }}-plugin
|
||||||
|
|
||||||
|
- name: Select Worker name
|
||||||
|
run: |
|
||||||
|
REF="${{ inputs.gh_ref }}"
|
||||||
|
case "$REF" in
|
||||||
|
main)
|
||||||
|
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pro" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.app" >> $GITHUB_ENV ;;
|
||||||
|
staging)
|
||||||
|
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pre" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
|
develop)
|
||||||
|
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-hourly" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
|
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
- name: Set the custom url
|
||||||
|
working-directory: plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" apps/${{ inputs.plugin_name }}-plugin/wrangler.toml
|
||||||
|
|
||||||
|
- name: Deploy to Cloudflare Workers
|
||||||
|
uses: cloudflare/wrangler-action@v3
|
||||||
|
with:
|
||||||
|
workingDirectory: plugins
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
command: deploy --config apps/${{ inputs.plugin_name }}-plugin/wrangler.toml --name ${{ env.WORKER_NAME }}
|
||||||
|
|
||||||
|
- name: Notify Mattermost
|
||||||
|
if: failure()
|
||||||
|
uses: mattermost/action-mattermost-notify@master
|
||||||
|
with:
|
||||||
|
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||||
|
MATTERMOST_CHANNEL: bot-alerts-cicd
|
||||||
|
TEXT: |
|
||||||
|
❌ 🧩📦 *[PENPOT PLUGINS] Error deploying ${{ env.WORKER_NAME }}.*
|
||||||
|
📄 Triggered from ref: `${{ inputs.gh_ref }}`
|
||||||
|
Plugin name: `${{ inputs.plugin_name }}-plugin`
|
||||||
|
Cloudflare worker name: `${{ env.WORKER_NAME }}`
|
||||||
|
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
@infra
|
||||||
143
.github/workflows/plugins-deploy-packages.yml
vendored
Normal file
143
.github/workflows/plugins-deploy-packages.yml
vendored
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
name: Plugins/packages deployer
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
- staging
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'plugins/apps/*-plugin/**'
|
||||||
|
- 'libs/plugins-styles/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
gh_ref:
|
||||||
|
description: 'Name of the branch'
|
||||||
|
type: choice
|
||||||
|
required: true
|
||||||
|
default: 'develop'
|
||||||
|
options:
|
||||||
|
- develop
|
||||||
|
- staging
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
detect-changes:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
colors_to_tokens: ${{ steps.filter.outputs.colors_to_tokens }}
|
||||||
|
create_palette: ${{ steps.filter.outputs.create_palette }}
|
||||||
|
lorem_ipsum: ${{ steps.filter.outputs.lorem_ipsum }}
|
||||||
|
rename_layers: ${{ steps.filter.outputs.rename_layers }}
|
||||||
|
contrast: ${{ steps.filter.outputs.contrast }}
|
||||||
|
icons: ${{ steps.filter.outputs.icons }}
|
||||||
|
poc_state: ${{ steps.filter.outputs.poc_state }}
|
||||||
|
table: ${{ steps.filter.outputs.table }}
|
||||||
|
# [For new plugins]
|
||||||
|
# Add more outputs here
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- id: filter
|
||||||
|
uses: dorny/paths-filter@v3
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
colors_to_tokens:
|
||||||
|
- 'plugins/apps/colors-to-tokens-plugin/**'
|
||||||
|
- 'libs/plugins-styles/**'
|
||||||
|
contrast:
|
||||||
|
- 'plugins/apps/contrast-plugin/**'
|
||||||
|
- 'libs/plugins-styles/**'
|
||||||
|
create_palette:
|
||||||
|
- 'plugins/apps/create-palette-plugin/**'
|
||||||
|
- 'libs/plugins-styles/**'
|
||||||
|
icons:
|
||||||
|
- 'plugins/apps/icons-plugin/**'
|
||||||
|
- 'libs/plugins-styles/**'
|
||||||
|
lorem_ipsum:
|
||||||
|
- 'plugins/apps/lorem-ipsum-plugin/**'
|
||||||
|
- 'libs/plugins-styles/**'
|
||||||
|
rename_layers:
|
||||||
|
- 'plugins/apps/rename-layers-plugin/**'
|
||||||
|
- 'libs/plugins-styles/**'
|
||||||
|
table:
|
||||||
|
- 'plugins/apps/table-plugin/**'
|
||||||
|
- 'libs/plugins-styles/**'
|
||||||
|
# [For new plugins]
|
||||||
|
# Add more plugin filters here
|
||||||
|
# another_plugin:
|
||||||
|
# - 'plugins/apps/another-plugin/**'
|
||||||
|
# - 'libs/plugins-styles/**'
|
||||||
|
|
||||||
|
colors-to-tokens-plugin:
|
||||||
|
needs: detect-changes
|
||||||
|
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.colors_to_tokens == 'true'
|
||||||
|
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||||
|
plugin_name: colors-to-tokens
|
||||||
|
|
||||||
|
contrast-plugin:
|
||||||
|
needs: detect-changes
|
||||||
|
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.contrast == 'true'
|
||||||
|
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||||
|
plugin_name: contrast
|
||||||
|
|
||||||
|
create-palette-plugin:
|
||||||
|
needs: detect-changes
|
||||||
|
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.create_palette == 'true'
|
||||||
|
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||||
|
plugin_name: create-palette
|
||||||
|
|
||||||
|
icons-plugin:
|
||||||
|
needs: detect-changes
|
||||||
|
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.icons == 'true'
|
||||||
|
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||||
|
plugin_name: icons
|
||||||
|
|
||||||
|
lorem-ipsum-plugin:
|
||||||
|
needs: detect-changes
|
||||||
|
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.lorem_ipsum == 'true'
|
||||||
|
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||||
|
plugin_name: lorem-ipsum
|
||||||
|
|
||||||
|
rename-layers-plugin:
|
||||||
|
needs: detect-changes
|
||||||
|
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.rename_layers == 'true'
|
||||||
|
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||||
|
plugin_name: rename-layers
|
||||||
|
|
||||||
|
table-plugin:
|
||||||
|
needs: detect-changes
|
||||||
|
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.table == 'true'
|
||||||
|
uses: ./.github/workflows/plugins-deploy-package.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||||
|
plugin_name: table
|
||||||
|
|
||||||
|
# [For new plugins]
|
||||||
|
# Add more jobs for other plugins below, following the same pattern
|
||||||
|
# another-plugin:
|
||||||
|
# needs: detect-changes
|
||||||
|
# if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.another_plugin == 'true'
|
||||||
|
# uses: ./.github/workflows/plugins-deploy-package.yml
|
||||||
|
# secrets: inherit
|
||||||
|
# with:
|
||||||
|
# gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
|
||||||
|
# plugin_name: another
|
||||||
123
.github/workflows/plugins-deploy-styles-doc.yml
vendored
Normal file
123
.github/workflows/plugins-deploy-styles-doc.yml
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
name: Plugins/styles-doc deployer
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
- staging
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'plugins/apps/example-styles/**'
|
||||||
|
- 'plugins/libs/plugins-styles/**'
|
||||||
|
- 'plugins/wrangler-penpot-plugins-styles-doc.toml'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
gh_ref:
|
||||||
|
description: 'Name of the branch'
|
||||||
|
type: choice
|
||||||
|
required: true
|
||||||
|
default: 'develop'
|
||||||
|
options:
|
||||||
|
- develop
|
||||||
|
- staging
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Extract some useful variables
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ steps.vars.outputs.gh_ref }}
|
||||||
|
|
||||||
|
# START: Setup Node and PNPM enabling cache
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version-file: .nvmrc
|
||||||
|
|
||||||
|
- name: Enable PNPM
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
corepack enable;
|
||||||
|
corepack install;
|
||||||
|
|
||||||
|
- name: Get pnpm store path
|
||||||
|
id: pnpm-store
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Cache pnpm store
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-
|
||||||
|
# END: Setup Node and PNPM enabling cache
|
||||||
|
|
||||||
|
- name: Install deps
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
pnpm install --no-frozen-lockfile;
|
||||||
|
pnpm add -D -w wrangler@latest;
|
||||||
|
|
||||||
|
- name: Build styles
|
||||||
|
working-directory: plugins
|
||||||
|
shell: bash
|
||||||
|
run: npx nx run example-styles:build
|
||||||
|
|
||||||
|
- name: Select Worker name
|
||||||
|
run: |
|
||||||
|
REF="${{ steps.vars.outputs.gh_ref }}"
|
||||||
|
case "$REF" in
|
||||||
|
main)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-styles-doc-pro" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=styles-doc.plugins.penpot.app" >> $GITHUB_ENV ;;
|
||||||
|
staging)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-styles-doc-pre" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=styles-doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
|
develop)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-styles-doc-hourly" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=styles-doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
|
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
- name: Set the custom url
|
||||||
|
working-directory: plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-styles-doc.toml
|
||||||
|
|
||||||
|
- name: Deploy to Cloudflare Workers
|
||||||
|
uses: cloudflare/wrangler-action@v3
|
||||||
|
with:
|
||||||
|
workingDirectory: plugins
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
command: deploy --config wrangler-penpot-plugins-styles-doc.toml --name ${{ env.WORKER_NAME }}
|
||||||
|
|
||||||
|
- name: Notify Mattermost
|
||||||
|
if: failure()
|
||||||
|
uses: mattermost/action-mattermost-notify@master
|
||||||
|
with:
|
||||||
|
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||||
|
MATTERMOST_CHANNEL: bot-alerts-cicd
|
||||||
|
TEXT: |
|
||||||
|
❌ 🧩💅 *[PENPOT PLUGINS] Error deploying Styles documentation.*
|
||||||
|
📄 Triggered from ref: `${{ inputs.gh_ref }}`
|
||||||
|
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
@infra
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -21,6 +21,7 @@
|
|||||||
.rebel_readline_history
|
.rebel_readline_history
|
||||||
.repl
|
.repl
|
||||||
.shadow-cljs
|
.shadow-cljs
|
||||||
|
.pnpm-store/
|
||||||
/*.jpg
|
/*.jpg
|
||||||
/*.md
|
/*.md
|
||||||
/*.png
|
/*.png
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
/backend/resources/public/media
|
/backend/resources/public/media
|
||||||
/backend/target/
|
/backend/target/
|
||||||
/backend/experiments
|
/backend/experiments
|
||||||
|
/backend/scripts/_env.local
|
||||||
/bundle*
|
/bundle*
|
||||||
/cd.md
|
/cd.md
|
||||||
/clj-profiler/
|
/clj-profiler/
|
||||||
@@ -54,6 +56,8 @@
|
|||||||
/exporter/target
|
/exporter/target
|
||||||
/frontend/.storybook/preview-body.html
|
/frontend/.storybook/preview-body.html
|
||||||
/frontend/.storybook/preview-head.html
|
/frontend/.storybook/preview-head.html
|
||||||
|
/frontend/playwright-report/
|
||||||
|
/frontend/text-editor/src/wasm/
|
||||||
/frontend/dist/
|
/frontend/dist/
|
||||||
/frontend/npm-debug.log
|
/frontend/npm-debug.log
|
||||||
/frontend/out/
|
/frontend/out/
|
||||||
@@ -72,6 +76,7 @@
|
|||||||
/library/target/
|
/library/target/
|
||||||
/library/*.zip
|
/library/*.zip
|
||||||
/external
|
/external
|
||||||
|
/penpot-nitrate
|
||||||
|
|
||||||
clj-profiler/
|
clj-profiler/
|
||||||
node_modules
|
node_modules
|
||||||
|
|||||||
105
.gitpod.yml
105
.gitpod.yml
@@ -1,105 +0,0 @@
|
|||||||
image:
|
|
||||||
file: docker/gitpod/Dockerfile
|
|
||||||
|
|
||||||
ports:
|
|
||||||
# nginx
|
|
||||||
- port: 3449
|
|
||||||
onOpen: open-preview
|
|
||||||
|
|
||||||
# frontend nREPL
|
|
||||||
- port: 3447
|
|
||||||
onOpen: ignore
|
|
||||||
visibility: private
|
|
||||||
|
|
||||||
# frontend shadow server
|
|
||||||
- port: 3448
|
|
||||||
onOpen: ignore
|
|
||||||
visibility: private
|
|
||||||
|
|
||||||
# backend
|
|
||||||
- port: 6060
|
|
||||||
onOpen: ignore
|
|
||||||
|
|
||||||
# exporter shadow server
|
|
||||||
- port: 9630
|
|
||||||
onOpen: ignore
|
|
||||||
visibility: private
|
|
||||||
|
|
||||||
# exporter http server
|
|
||||||
- port: 6061
|
|
||||||
onOpen: ignore
|
|
||||||
|
|
||||||
# mailhog web interface
|
|
||||||
- port: 8025
|
|
||||||
onOpen: ignore
|
|
||||||
|
|
||||||
# mailhog postfix
|
|
||||||
- port: 1025
|
|
||||||
onOpen: ignore
|
|
||||||
|
|
||||||
# postgres
|
|
||||||
- port: 5432
|
|
||||||
onOpen: ignore
|
|
||||||
|
|
||||||
# redis
|
|
||||||
- port: 6379
|
|
||||||
onOpen: ignore
|
|
||||||
|
|
||||||
# openldap
|
|
||||||
- port: 389
|
|
||||||
onOpen: ignore
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
# https://github.com/gitpod-io/gitpod/issues/666#issuecomment-534347856
|
|
||||||
- name: gulp
|
|
||||||
command: >
|
|
||||||
cd $GITPOD_REPO_ROOT/frontend/;
|
|
||||||
yarn && gp sync-done 'frontend-yarn';
|
|
||||||
npx gulp --theme=${PENPOT_THEME} watch
|
|
||||||
|
|
||||||
- name: frontend shadow watch
|
|
||||||
command: >
|
|
||||||
cd $GITPOD_REPO_ROOT/frontend/;
|
|
||||||
gp sync-await 'frontend-yarn';
|
|
||||||
npx shadow-cljs watch main
|
|
||||||
|
|
||||||
- init: gp await-port 5432 && psql -f $GITPOD_REPO_ROOT/docker/gitpod/files/postgresql_init.sql
|
|
||||||
name: backend
|
|
||||||
command: >
|
|
||||||
cd $GITPOD_REPO_ROOT/backend/;
|
|
||||||
./scripts/start-dev
|
|
||||||
|
|
||||||
- name: exporter shadow watch
|
|
||||||
command:
|
|
||||||
cd $GITPOD_REPO_ROOT/exporter/;
|
|
||||||
gp sync-await 'frontend-yarn';
|
|
||||||
yarn && npx shadow-cljs watch main
|
|
||||||
|
|
||||||
- name: exporter web server
|
|
||||||
command: >
|
|
||||||
cd $GITPOD_REPO_ROOT/exporter/;
|
|
||||||
./scripts/wait-and-start.sh
|
|
||||||
|
|
||||||
- name: signed terminal
|
|
||||||
before: >
|
|
||||||
[[ ! -z ${GNUGPG} ]] &&
|
|
||||||
cd ~ &&
|
|
||||||
rm -rf .gnupg &&
|
|
||||||
echo ${GNUGPG} | base64 -d | tar --no-same-owner -xzvf -
|
|
||||||
init: >
|
|
||||||
[[ ! -z ${GNUGPG_KEY} ]] &&
|
|
||||||
git config --global commit.gpgsign true &&
|
|
||||||
git config --global user.signingkey ${GNUGPG_KEY}
|
|
||||||
command: cd $GITPOD_REPO_ROOT
|
|
||||||
|
|
||||||
- name: redis
|
|
||||||
command: redis-server
|
|
||||||
|
|
||||||
- before: go get github.com/mailhog/MailHog
|
|
||||||
name: mailhog
|
|
||||||
command: MailHog
|
|
||||||
|
|
||||||
- name: Nginx
|
|
||||||
command: >
|
|
||||||
nginx &&
|
|
||||||
multitail /var/log/nginx/access.log -I /var/log/nginx/error.log
|
|
||||||
11
.yarnrc.yml
11
.yarnrc.yml
@@ -1,11 +0,0 @@
|
|||||||
enableGlobalCache: true
|
|
||||||
|
|
||||||
enableImmutableCache: false
|
|
||||||
|
|
||||||
enableImmutableInstalls: false
|
|
||||||
|
|
||||||
enableTelemetry: false
|
|
||||||
|
|
||||||
httpTimeout: 600000
|
|
||||||
|
|
||||||
nodeLinker: node-modules
|
|
||||||
40
CHANGES.md
40
CHANGES.md
@@ -1,5 +1,37 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## 2.14.0 (Unreleased)
|
||||||
|
|
||||||
|
### :boom: Breaking changes & Deprecations
|
||||||
|
|
||||||
|
### :rocket: Epics and highlights
|
||||||
|
|
||||||
|
### :heart: Community contributions (Thank you!)
|
||||||
|
|
||||||
|
### :sparkles: New features & Enhancements
|
||||||
|
|
||||||
|
- Remap references when renaming tokens [Taiga #10202](https://tree.taiga.io/project/penpot/us/10202)
|
||||||
|
- Tokens panel nested path view [Taiga #9966](https://tree.taiga.io/project/penpot/us/9966)
|
||||||
|
- Improve usability of lock and hide buttons in the layer panel. [Taiga #12916](https://tree.taiga.io/project/penpot/issue/12916)
|
||||||
|
- Optimize sidebar performance for deeply nested shapes [Taiga #13017](https://tree.taiga.io/project/penpot/task/13017)
|
||||||
|
- Remove tokens path node and bulk remove tokens [Taiga #13007](https://tree.taiga.io/project/penpot/us/13007)
|
||||||
|
- Replace themes management modal radio buttons for switches [Taiga #9215](https://tree.taiga.io/project/penpot/us/9215)
|
||||||
|
|
||||||
|
### :bug: Bugs fixed
|
||||||
|
|
||||||
|
- Remove whitespaces from asset export filename [Github #8133](https://github.com/penpot/penpot/pull/8133)
|
||||||
|
- Fix prototype connections lost when switching between variants [Taiga #12812](https://tree.taiga.io/project/penpot/issue/12812)
|
||||||
|
- Fix wrong image in the onboarding invitation block [Taiga #13040](https://tree.taiga.io/project/penpot/issue/13040)
|
||||||
|
- Fix wrong register image [Taiga #12955](https://tree.taiga.io/project/penpot/task/12955)
|
||||||
|
- Fix error message on components doesn't close automatically [Taiga #12012](https://tree.taiga.io/project/penpot/issue/12012)
|
||||||
|
- Fix incorrect handling of input values on layout gap and padding inputs [Github #8113](https://github.com/penpot/penpot/issues/8113)
|
||||||
|
- Fix incorrect default option on tokens import dialog [Github #8051](https://github.com/penpot/penpot/pull/8051)
|
||||||
|
- Fix unhandled exception tokens creation dialog [Github #8110](https://github.com/penpot/penpot/issues/8110)
|
||||||
|
- Fix displaying a hidden user avatar when there is only one more [Taiga #13058](https://tree.taiga.io/project/penpot/issue/13058)
|
||||||
|
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
||||||
|
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
||||||
|
- Fix boolean operators in menu for boards [Taiga #13174](https://tree.taiga.io/project/penpot/issue/13174)
|
||||||
|
|
||||||
## 2.13.0 (Unreleased)
|
## 2.13.0 (Unreleased)
|
||||||
|
|
||||||
### :boom: Breaking changes & Deprecations
|
### :boom: Breaking changes & Deprecations
|
||||||
@@ -23,6 +55,7 @@
|
|||||||
- Fix wrong board size presets in Android [Taiga #12339](https://tree.taiga.io/project/penpot/issue/12339)
|
- Fix wrong board size presets in Android [Taiga #12339](https://tree.taiga.io/project/penpot/issue/12339)
|
||||||
- Fix problem with grid layout components and auto sizing [Github #7797](https://github.com/penpot/penpot/issues/7797)
|
- Fix problem with grid layout components and auto sizing [Github #7797](https://github.com/penpot/penpot/issues/7797)
|
||||||
- Fix some alignments on inspect tab [Taiga #12915](https://tree.taiga.io/project/penpot/issue/12915)
|
- Fix some alignments on inspect tab [Taiga #12915](https://tree.taiga.io/project/penpot/issue/12915)
|
||||||
|
- Fix problem with text editor maintaining previous styles [Taiga #12835](https://tree.taiga.io/project/penpot/issue/12835)
|
||||||
- Fix color assets from shared libraries not appearing as assets in Selected colors panel [Taiga #12957](https://tree.taiga.io/project/penpot/issue/12957)
|
- Fix color assets from shared libraries not appearing as assets in Selected colors panel [Taiga #12957](https://tree.taiga.io/project/penpot/issue/12957)
|
||||||
- Fix CSS generated box-shadow property [Taiga #12997](https://tree.taiga.io/project/penpot/issue/12997)
|
- Fix CSS generated box-shadow property [Taiga #12997](https://tree.taiga.io/project/penpot/issue/12997)
|
||||||
- Fix inner shadow selector on shadow token [Taiga #12951](https://tree.taiga.io/project/penpot/issue/12951)
|
- Fix inner shadow selector on shadow token [Taiga #12951](https://tree.taiga.io/project/penpot/issue/12951)
|
||||||
@@ -37,7 +70,8 @@
|
|||||||
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
||||||
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
||||||
- Fix incorrect handling of input values on layout gap and padding inputs [Github #8113](https://github.com/penpot/penpot/issues/8113)
|
- Fix incorrect handling of input values on layout gap and padding inputs [Github #8113](https://github.com/penpot/penpot/issues/8113)
|
||||||
|
- Fix several race conditions on path editor [Github #8187](https://github.com/penpot/penpot/pull/8187)
|
||||||
|
- Fix app freeze when introducing an error on a very long token name [Taiga #13214](https://tree.taiga.io/project/penpot/issue/13214)
|
||||||
|
|
||||||
## 2.12.1
|
## 2.12.1
|
||||||
|
|
||||||
@@ -47,7 +81,6 @@
|
|||||||
- Fix problem with style in fonts input [Taiga #12935](https://tree.taiga.io/project/penpot/issue/12935)
|
- Fix problem with style in fonts input [Taiga #12935](https://tree.taiga.io/project/penpot/issue/12935)
|
||||||
- Fix problem with path editor and right click [Github #7917](https://github.com/penpot/penpot/issues/7917)
|
- Fix problem with path editor and right click [Github #7917](https://github.com/penpot/penpot/issues/7917)
|
||||||
|
|
||||||
|
|
||||||
## 2.12.0
|
## 2.12.0
|
||||||
|
|
||||||
### :boom: Breaking changes & Deprecations
|
### :boom: Breaking changes & Deprecations
|
||||||
@@ -59,7 +92,6 @@ The backend RPC API URLS are changed from `/api/rpc/command/<name>` to
|
|||||||
compatibility; however, if you are a user of this API, it is strongly
|
compatibility; however, if you are a user of this API, it is strongly
|
||||||
recommended that you adapt your code to use the new PATH.
|
recommended that you adapt your code to use the new PATH.
|
||||||
|
|
||||||
|
|
||||||
#### Updated SSO Callback URL
|
#### Updated SSO Callback URL
|
||||||
|
|
||||||
The OAuth / Single Sign-On (SSO) callback endpoint has changed to
|
The OAuth / Single Sign-On (SSO) callback endpoint has changed to
|
||||||
@@ -92,7 +124,6 @@ This update standardizes all authentication flows under the single URL
|
|||||||
and makis it more modular, enabling the ability to configure SSO auth
|
and makis it more modular, enabling the ability to configure SSO auth
|
||||||
provider dinamically.
|
provider dinamically.
|
||||||
|
|
||||||
|
|
||||||
#### Changes on default docker compose
|
#### Changes on default docker compose
|
||||||
|
|
||||||
We have updated the `docker/images/docker-compose.yaml` with a small
|
We have updated the `docker/images/docker-compose.yaml` with a small
|
||||||
@@ -156,7 +187,6 @@ example. It's still usable as before, we just removed the example.
|
|||||||
|
|
||||||
- Deprecated configuration variables with the prefix `PENPOT_ASSETS_*`, and will be
|
- Deprecated configuration variables with the prefix `PENPOT_ASSETS_*`, and will be
|
||||||
removed in future versions:
|
removed in future versions:
|
||||||
|
|
||||||
- The `PENPOT_ASSETS_STORAGE_BACKEND` becomes `PENPOT_OBJECTS_STORAGE_BACKEND` and its
|
- The `PENPOT_ASSETS_STORAGE_BACKEND` becomes `PENPOT_OBJECTS_STORAGE_BACKEND` and its
|
||||||
values passes from (`assets-fs` or `assets-s3`) to (`fs` or `s3`)
|
values passes from (`assets-fs` or `assets-s3`) to (`fs` or `s3`)
|
||||||
- The `PENPOT_STORAGE_ASSETS_FS_DIRECTORY` becomes `PENPOT_OBJECTS_STORAGE_FS_DIRECTORY`
|
- The `PENPOT_STORAGE_ASSETS_FS_DIRECTORY` becomes `PENPOT_OBJECTS_STORAGE_FS_DIRECTORY`
|
||||||
|
|||||||
@@ -120,17 +120,12 @@ them on your system, you can run them with:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check formatting
|
# Check formatting
|
||||||
yarn fmt:clj:check
|
./scripts/fmt
|
||||||
|
|
||||||
# Check and fix formatting
|
# Lint
|
||||||
yarn fmt:clj
|
./scripts/lint
|
||||||
|
|
||||||
# Run the linter
|
|
||||||
yarn lint:clj
|
|
||||||
```
|
```
|
||||||
|
|
||||||
There are more choices in `package.json`.
|
|
||||||
|
|
||||||
Ideally, you should run these commands as git pre-commit hooks. A convenient way
|
Ideally, you should run these commands as git pre-commit hooks. A convenient way
|
||||||
of defining them is to use [Husky](https://typicode.github.io/husky/#/).
|
of defining them is to use [Husky](https://typicode.github.io/husky/#/).
|
||||||
|
|
||||||
|
|||||||
7
backend/.gitignore
vendored
7
backend/.gitignore
vendored
@@ -1,7 +0,0 @@
|
|||||||
.pnp.*
|
|
||||||
.yarn/*
|
|
||||||
!.yarn/patches
|
|
||||||
!.yarn/plugins
|
|
||||||
!.yarn/releases
|
|
||||||
!.yarn/sdks
|
|
||||||
!.yarn/versions
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"author": "Kaleidos INC",
|
"author": "Kaleidos INC",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c",
|
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/penpot/penpot"
|
"url": "https://github.com/penpot/penpot"
|
||||||
|
|||||||
306
backend/pnpm-lock.yaml
generated
Normal file
306
backend/pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.:
|
||||||
|
dependencies:
|
||||||
|
luxon:
|
||||||
|
specifier: ^3.4.4
|
||||||
|
version: 3.7.2
|
||||||
|
sax:
|
||||||
|
specifier: ^1.4.1
|
||||||
|
version: 1.4.3
|
||||||
|
devDependencies:
|
||||||
|
nodemon:
|
||||||
|
specifier: ^3.1.2
|
||||||
|
version: 3.1.11
|
||||||
|
source-map-support:
|
||||||
|
specifier: ^0.5.21
|
||||||
|
version: 0.5.21
|
||||||
|
ws:
|
||||||
|
specifier: ^8.17.0
|
||||||
|
version: 8.18.3
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
anymatch@3.1.3:
|
||||||
|
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
balanced-match@1.0.2:
|
||||||
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
|
binary-extensions@2.3.0:
|
||||||
|
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
brace-expansion@1.1.12:
|
||||||
|
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
||||||
|
|
||||||
|
braces@3.0.3:
|
||||||
|
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
buffer-from@1.1.2:
|
||||||
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
|
||||||
|
chokidar@3.6.0:
|
||||||
|
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||||
|
engines: {node: '>= 8.10.0'}
|
||||||
|
|
||||||
|
concat-map@0.0.1:
|
||||||
|
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||||
|
|
||||||
|
debug@4.4.3:
|
||||||
|
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||||
|
engines: {node: '>=6.0'}
|
||||||
|
peerDependencies:
|
||||||
|
supports-color: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
supports-color:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
fill-range@7.1.1:
|
||||||
|
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
glob-parent@5.1.2:
|
||||||
|
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
has-flag@3.0.0:
|
||||||
|
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
ignore-by-default@1.0.1:
|
||||||
|
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
|
||||||
|
|
||||||
|
is-binary-path@2.1.0:
|
||||||
|
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
is-extglob@2.1.1:
|
||||||
|
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
is-glob@4.0.3:
|
||||||
|
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
is-number@7.0.0:
|
||||||
|
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||||
|
engines: {node: '>=0.12.0'}
|
||||||
|
|
||||||
|
luxon@3.7.2:
|
||||||
|
resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
minimatch@3.1.2:
|
||||||
|
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||||
|
|
||||||
|
ms@2.1.3:
|
||||||
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
|
nodemon@3.1.11:
|
||||||
|
resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
normalize-path@3.0.0:
|
||||||
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
picomatch@2.3.1:
|
||||||
|
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||||
|
engines: {node: '>=8.6'}
|
||||||
|
|
||||||
|
pstree.remy@1.1.8:
|
||||||
|
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
|
||||||
|
|
||||||
|
readdirp@3.6.0:
|
||||||
|
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||||
|
engines: {node: '>=8.10.0'}
|
||||||
|
|
||||||
|
sax@1.4.3:
|
||||||
|
resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==}
|
||||||
|
|
||||||
|
semver@7.7.3:
|
||||||
|
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
simple-update-notifier@2.0.0:
|
||||||
|
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
source-map-support@0.5.21:
|
||||||
|
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||||
|
|
||||||
|
source-map@0.6.1:
|
||||||
|
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
supports-color@5.5.0:
|
||||||
|
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
to-regex-range@5.0.1:
|
||||||
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
|
engines: {node: '>=8.0'}
|
||||||
|
|
||||||
|
touch@3.1.1:
|
||||||
|
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
undefsafe@2.0.5:
|
||||||
|
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
|
||||||
|
|
||||||
|
ws@8.18.3:
|
||||||
|
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
|
||||||
|
engines: {node: '>=10.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
bufferutil: ^4.0.1
|
||||||
|
utf-8-validate: '>=5.0.2'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
bufferutil:
|
||||||
|
optional: true
|
||||||
|
utf-8-validate:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
|
||||||
|
anymatch@3.1.3:
|
||||||
|
dependencies:
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
picomatch: 2.3.1
|
||||||
|
|
||||||
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
|
binary-extensions@2.3.0: {}
|
||||||
|
|
||||||
|
brace-expansion@1.1.12:
|
||||||
|
dependencies:
|
||||||
|
balanced-match: 1.0.2
|
||||||
|
concat-map: 0.0.1
|
||||||
|
|
||||||
|
braces@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
fill-range: 7.1.1
|
||||||
|
|
||||||
|
buffer-from@1.1.2: {}
|
||||||
|
|
||||||
|
chokidar@3.6.0:
|
||||||
|
dependencies:
|
||||||
|
anymatch: 3.1.3
|
||||||
|
braces: 3.0.3
|
||||||
|
glob-parent: 5.1.2
|
||||||
|
is-binary-path: 2.1.0
|
||||||
|
is-glob: 4.0.3
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
readdirp: 3.6.0
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
concat-map@0.0.1: {}
|
||||||
|
|
||||||
|
debug@4.4.3(supports-color@5.5.0):
|
||||||
|
dependencies:
|
||||||
|
ms: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
supports-color: 5.5.0
|
||||||
|
|
||||||
|
fill-range@7.1.1:
|
||||||
|
dependencies:
|
||||||
|
to-regex-range: 5.0.1
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
glob-parent@5.1.2:
|
||||||
|
dependencies:
|
||||||
|
is-glob: 4.0.3
|
||||||
|
|
||||||
|
has-flag@3.0.0: {}
|
||||||
|
|
||||||
|
ignore-by-default@1.0.1: {}
|
||||||
|
|
||||||
|
is-binary-path@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
binary-extensions: 2.3.0
|
||||||
|
|
||||||
|
is-extglob@2.1.1: {}
|
||||||
|
|
||||||
|
is-glob@4.0.3:
|
||||||
|
dependencies:
|
||||||
|
is-extglob: 2.1.1
|
||||||
|
|
||||||
|
is-number@7.0.0: {}
|
||||||
|
|
||||||
|
luxon@3.7.2: {}
|
||||||
|
|
||||||
|
minimatch@3.1.2:
|
||||||
|
dependencies:
|
||||||
|
brace-expansion: 1.1.12
|
||||||
|
|
||||||
|
ms@2.1.3: {}
|
||||||
|
|
||||||
|
nodemon@3.1.11:
|
||||||
|
dependencies:
|
||||||
|
chokidar: 3.6.0
|
||||||
|
debug: 4.4.3(supports-color@5.5.0)
|
||||||
|
ignore-by-default: 1.0.1
|
||||||
|
minimatch: 3.1.2
|
||||||
|
pstree.remy: 1.1.8
|
||||||
|
semver: 7.7.3
|
||||||
|
simple-update-notifier: 2.0.0
|
||||||
|
supports-color: 5.5.0
|
||||||
|
touch: 3.1.1
|
||||||
|
undefsafe: 2.0.5
|
||||||
|
|
||||||
|
normalize-path@3.0.0: {}
|
||||||
|
|
||||||
|
picomatch@2.3.1: {}
|
||||||
|
|
||||||
|
pstree.remy@1.1.8: {}
|
||||||
|
|
||||||
|
readdirp@3.6.0:
|
||||||
|
dependencies:
|
||||||
|
picomatch: 2.3.1
|
||||||
|
|
||||||
|
sax@1.4.3: {}
|
||||||
|
|
||||||
|
semver@7.7.3: {}
|
||||||
|
|
||||||
|
simple-update-notifier@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
semver: 7.7.3
|
||||||
|
|
||||||
|
source-map-support@0.5.21:
|
||||||
|
dependencies:
|
||||||
|
buffer-from: 1.1.2
|
||||||
|
source-map: 0.6.1
|
||||||
|
|
||||||
|
source-map@0.6.1: {}
|
||||||
|
|
||||||
|
supports-color@5.5.0:
|
||||||
|
dependencies:
|
||||||
|
has-flag: 3.0.0
|
||||||
|
|
||||||
|
to-regex-range@5.0.1:
|
||||||
|
dependencies:
|
||||||
|
is-number: 7.0.0
|
||||||
|
|
||||||
|
touch@3.1.1: {}
|
||||||
|
|
||||||
|
undefsafe@2.0.5: {}
|
||||||
|
|
||||||
|
ws@8.18.3: {}
|
||||||
0
backend/pnpm-workspace.yaml
Normal file
0
backend/pnpm-workspace.yaml
Normal file
@@ -12,43 +12,22 @@ Debug Main Page
|
|||||||
</nav>
|
</nav>
|
||||||
<main class="dashboard">
|
<main class="dashboard">
|
||||||
<section class="widget">
|
<section class="widget">
|
||||||
<fieldset>
|
|
||||||
<legend>Error reports</legend>
|
|
||||||
<desc><a href="/dbg/error">CLICK HERE TO SEE THE ERROR REPORTS</a> </desc>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Profile Management</legend>
|
<legend>CURRENT PROFILE</legend>
|
||||||
<form method="post" action="/dbg/actions/resend-email-verification">
|
<desc>
|
||||||
<div class="row">
|
<p>
|
||||||
<input type="email" name="email" placeholder="example@example.com" value="" />
|
Name: <b>{{profile.fullname}}</b> <br />
|
||||||
</div>
|
Email: <b>{{profile.email}}</b>
|
||||||
|
</p>
|
||||||
<div class="row">
|
</desc>
|
||||||
<label for="force-verify">Are you sure?</label>
|
|
||||||
<input id="force-verify" type="checkbox" name="force" />
|
|
||||||
<br />
|
|
||||||
<small>
|
|
||||||
This is a just a security double check for prevent non intentional submits.
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<input type="submit" name="resend" value="Resend Verification" />
|
|
||||||
<input type="submit" name="verify" value="Verify" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<input type="submit" class="danger" name="block" value="Block" />
|
|
||||||
<input type="submit" class="danger" name="unblock" value="Unblock" />
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>VIRTUAL CLOCK</legend>
|
<legend>VIRTUAL CLOCK</legend>
|
||||||
|
|
||||||
<desc>
|
<desc>
|
||||||
|
<p><b>IMPORTANT:</b> The virtual clock is profile based and only affects the currently logged-in profile.</p>
|
||||||
<p>
|
<p>
|
||||||
CURRENT CLOCK: <b>{{current-clock}}</b>
|
CURRENT CLOCK: <b>{{current-clock}}</b>
|
||||||
<br />
|
<br />
|
||||||
@@ -81,8 +60,93 @@ Debug Main Page
|
|||||||
</form>
|
</form>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>ERROR REPORTS</legend>
|
||||||
|
<desc><a href="/dbg/error">CLICK HERE TO SEE THE ERROR REPORTS</a> </desc>
|
||||||
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<section class="widget">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Profile Management</legend>
|
||||||
|
<form method="post" action="/dbg/actions/resend-email-verification">
|
||||||
|
<div class="row">
|
||||||
|
<input type="email" name="email" placeholder="example@example.com" value="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<label for="force-verify">Are you sure?</label>
|
||||||
|
<input id="force-verify" type="checkbox" name="force" />
|
||||||
|
<br />
|
||||||
|
<small>
|
||||||
|
This is a just a security double check for prevent non intentional submits.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<input type="submit" name="resend" value="Resend Verification" />
|
||||||
|
<input type="submit" name="verify" value="Verify" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<input type="submit" class="danger" name="block" value="Block" />
|
||||||
|
<input type="submit" class="danger" name="unblock" value="Unblock" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Feature Flags for Team</legend>
|
||||||
|
<desc>Add a feature flag to a team</desc>
|
||||||
|
<form method="post" action="/dbg/actions/handle-team-features">
|
||||||
|
<div class="row">
|
||||||
|
<input type="text" style="width:300px" name="team-id" placeholder="team-id" />
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<select type="text" style="width:100px" name="feature">
|
||||||
|
{% for feature in supported-features %}
|
||||||
|
<option value="{{feature}}">{{feature}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<select style="width:100px" name="action">
|
||||||
|
<option value="">Action...</option>
|
||||||
|
<option value="show">Show</option>
|
||||||
|
<option value="enable">Enable</option>
|
||||||
|
<option value="disable">Disable</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<label for="check-feature">Skip feature check</label>
|
||||||
|
<input id="check-feature" type="checkbox" name="skip-check" />
|
||||||
|
<br />
|
||||||
|
<small>
|
||||||
|
Do not check if the feature is supported
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<label for="force-version">Are you sure?</label>
|
||||||
|
<input id="force-version" type="checkbox" name="force" />
|
||||||
|
<br />
|
||||||
|
<small>
|
||||||
|
This is a just a security double check for prevent non intentional submits.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<input type="submit" value="Submit" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<section class="widget">
|
<section class="widget">
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@@ -173,55 +237,5 @@ Debug Main Page
|
|||||||
</form>
|
</form>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="widget">
|
|
||||||
<fieldset>
|
|
||||||
<legend>Feature Flags for Team</legend>
|
|
||||||
<desc>Add a feature flag to a team</desc>
|
|
||||||
<form method="post" action="/dbg/actions/handle-team-features">
|
|
||||||
<div class="row">
|
|
||||||
<input type="text" style="width:300px" name="team-id" placeholder="team-id" />
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<select type="text" style="width:100px" name="feature">
|
|
||||||
{% for feature in supported-features %}
|
|
||||||
<option value="{{feature}}">{{feature}}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<select style="width:100px" name="action">
|
|
||||||
<option value="">Action...</option>
|
|
||||||
<option value="show">Show</option>
|
|
||||||
<option value="enable">Enable</option>
|
|
||||||
<option value="disable">Disable</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<label for="check-feature">Skip feature check</label>
|
|
||||||
<input id="check-feature" type="checkbox" name="skip-check" />
|
|
||||||
<br />
|
|
||||||
<small>
|
|
||||||
Do not check if the feature is supported
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<label for="force-version">Are you sure?</label>
|
|
||||||
<input id="force-version" type="checkbox" name="force" />
|
|
||||||
<br />
|
|
||||||
<small>
|
|
||||||
This is a just a security double check for prevent non intentional submits.
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<input type="submit" value="Submit" />
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</fieldset>
|
|
||||||
</section>
|
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export PENPOT_FLAGS="\
|
|||||||
disable-login-with-google \
|
disable-login-with-google \
|
||||||
disable-login-with-github \
|
disable-login-with-github \
|
||||||
disable-login-with-gitlab \
|
disable-login-with-gitlab \
|
||||||
|
disable-telemetry \
|
||||||
enable-backend-worker \
|
enable-backend-worker \
|
||||||
enable-backend-asserts \
|
enable-backend-asserts \
|
||||||
disable-feature-fdata-pointer-map \
|
disable-feature-fdata-pointer-map \
|
||||||
@@ -55,6 +56,8 @@ export PENPOT_OBJECTS_STORAGE_BACKEND=s3
|
|||||||
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
|
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
|
||||||
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
|
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
|
||||||
|
|
||||||
|
export PENPOT_NITRATE_BACKEND_URI=http://localhost:3000/control-center
|
||||||
|
|
||||||
export JAVA_OPTS="\
|
export JAVA_OPTS="\
|
||||||
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
|
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
|
||||||
-Djdk.attach.allowAttachSelf \
|
-Djdk.attach.allowAttachSelf \
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
SCRIPT_DIR=$(dirname $0);
|
SCRIPT_DIR=$(dirname $0);
|
||||||
source $SCRIPT_DIR/_env;
|
source $SCRIPT_DIR/_env;
|
||||||
|
|
||||||
|
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||||
|
source $SCRIPT_DIR/_env.local;
|
||||||
|
fi
|
||||||
|
|
||||||
# Initialize MINIO config
|
# Initialize MINIO config
|
||||||
setup_minio;
|
setup_minio;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
SCRIPT_DIR=$(dirname $0);
|
SCRIPT_DIR=$(dirname $0);
|
||||||
|
|
||||||
source $SCRIPT_DIR/_env;
|
source $SCRIPT_DIR/_env;
|
||||||
|
|
||||||
|
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||||
|
source $SCRIPT_DIR/_env.local;
|
||||||
|
fi
|
||||||
|
|
||||||
export OPTIONS="-A:dev"
|
export OPTIONS="-A:dev"
|
||||||
|
|
||||||
entrypoint=${1:-app.main};
|
entrypoint=${1:-app.main};
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
SCRIPT_DIR=$(dirname $0);
|
SCRIPT_DIR=$(dirname $0);
|
||||||
source $SCRIPT_DIR/_env;
|
source $SCRIPT_DIR/_env;
|
||||||
|
|
||||||
|
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||||
|
source $SCRIPT_DIR/_env.local;
|
||||||
|
fi
|
||||||
|
|
||||||
# Initialize MINIO config
|
# Initialize MINIO config
|
||||||
setup_minio;
|
setup_minio;
|
||||||
|
|
||||||
|
|||||||
@@ -873,11 +873,8 @@
|
|||||||
(import-storage-objects cfg)
|
(import-storage-objects cfg)
|
||||||
|
|
||||||
(let [files (get manifest :files)
|
(let [files (get manifest :files)
|
||||||
result (reduce (fn [result {:keys [id] :as file}]
|
result (reduce (fn [result file]
|
||||||
(let [name' (get file :name)
|
(let [name' (get file :name)
|
||||||
name' (if (map? name)
|
|
||||||
(get name id)
|
|
||||||
name')
|
|
||||||
file (assoc file :name name')]
|
file (assoc file :name name')]
|
||||||
(conj result (import-file cfg file))))
|
(conj result (import-file cfg file))))
|
||||||
[]
|
[]
|
||||||
|
|||||||
@@ -225,6 +225,8 @@
|
|||||||
[:netty-io-threads {:optional true} ::sm/int]
|
[:netty-io-threads {:optional true} ::sm/int]
|
||||||
[:executor-threads {:optional true} ::sm/int]
|
[:executor-threads {:optional true} ::sm/int]
|
||||||
|
|
||||||
|
[:nitrate-backend-uri {:optional true} ::sm/uri]
|
||||||
|
|
||||||
;; DEPRECATED
|
;; DEPRECATED
|
||||||
[:assets-storage-backend {:optional true} :keyword]
|
[:assets-storage-backend {:optional true} :keyword]
|
||||||
[:storage-assets-fs-directory {:optional true} :string]
|
[:storage-assets-fs-directory {:optional true} :string]
|
||||||
|
|||||||
@@ -49,13 +49,16 @@
|
|||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn index-handler
|
(defn index-handler
|
||||||
[_cfg _request]
|
[cfg request]
|
||||||
(let [{:keys [clock offset]} @clock/current]
|
(let [profile-id (::session/profile-id request)
|
||||||
|
offset (clock/get-offset profile-id)
|
||||||
|
profile (profile/get-profile cfg profile-id)]
|
||||||
{::yres/status 200
|
{::yres/status 200
|
||||||
::yres/headers {"content-type" "text/html"}
|
::yres/headers {"content-type" "text/html"}
|
||||||
::yres/body (-> (io/resource "app/templates/debug.tmpl")
|
::yres/body (-> (io/resource "app/templates/debug.tmpl")
|
||||||
(tmpl/render {:version (:full cf/version)
|
(tmpl/render {:version (:full cf/version)
|
||||||
:current-clock (str clock)
|
:profile profile
|
||||||
|
:current-clock ct/*clock*
|
||||||
:current-offset (if offset
|
:current-offset (if offset
|
||||||
(ct/format-duration offset)
|
(ct/format-duration offset)
|
||||||
"NO OFFSET")
|
"NO OFFSET")
|
||||||
@@ -447,15 +450,16 @@
|
|||||||
|
|
||||||
(defn- set-virtual-clock
|
(defn- set-virtual-clock
|
||||||
[_ {:keys [params] :as request}]
|
[_ {:keys [params] :as request}]
|
||||||
(let [offset (some-> params :offset str/trim not-empty ct/duration)
|
(let [offset (some-> params :offset str/trim not-empty ct/duration)
|
||||||
reset? (contains? params :reset)]
|
profile-id (::session/profile-id request)
|
||||||
|
reset? (contains? params :reset)]
|
||||||
(if (= "production" (cf/get :tenant))
|
(if (= "production" (cf/get :tenant))
|
||||||
{::yres/status 501
|
{::yres/status 501
|
||||||
::yres/body "OPERATION NOT ALLOWED"}
|
::yres/body "OPERATION NOT ALLOWED"}
|
||||||
(do
|
(do
|
||||||
(if (or reset? (zero? (inst-ms offset)))
|
(if (or reset? (zero? (inst-ms offset)))
|
||||||
(clock/set-offset! nil)
|
(clock/assign-offset profile-id nil)
|
||||||
(clock/set-offset! offset))
|
(clock/assign-offset profile-id offset))
|
||||||
{::yres/status 302
|
{::yres/status 302
|
||||||
::yres/headers {"location" "/dbg"}}))))
|
::yres/headers {"location" "/dbg"}}))))
|
||||||
|
|
||||||
@@ -495,7 +499,7 @@
|
|||||||
|
|
||||||
(defn authorized?
|
(defn authorized?
|
||||||
[pool {:keys [::session/profile-id]}]
|
[pool {:keys [::session/profile-id]}]
|
||||||
(or (= "devenv" (cf/get :host))
|
(or (and (= "devenv" (cf/get :host)) profile-id)
|
||||||
(let [profile (ex/ignoring (profile/get-profile pool profile-id))
|
(let [profile (ex/ignoring (profile/get-profile pool profile-id))
|
||||||
admins (or (cf/get :admins) #{})]
|
admins (or (cf/get :admins) #{})]
|
||||||
(contains? admins (:email profile)))))
|
(contains? admins (:email profile)))))
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
[app.http.session.tasks :as-alias tasks]
|
[app.http.session.tasks :as-alias tasks]
|
||||||
[app.main :as-alias main]
|
[app.main :as-alias main]
|
||||||
[app.setup :as-alias setup]
|
[app.setup :as-alias setup]
|
||||||
|
[app.setup.clock :as clock]
|
||||||
[app.tokens :as tokens]
|
[app.tokens :as tokens]
|
||||||
[integrant.core :as ig]
|
[integrant.core :as ig]
|
||||||
[yetti.request :as yreq]
|
[yetti.request :as yreq]
|
||||||
@@ -229,18 +230,22 @@
|
|||||||
(let [{:keys [type token claims metadata]} (get request ::http/auth-data)]
|
(let [{:keys [type token claims metadata]} (get request ::http/auth-data)]
|
||||||
(cond
|
(cond
|
||||||
(= type :cookie)
|
(= type :cookie)
|
||||||
(let [session (case (:ver metadata)
|
(let [session
|
||||||
;; BACKWARD COMPATIBILITY WITH OLD TOKENS
|
(case (:ver metadata)
|
||||||
0 (read-session manager token)
|
;; BACKWARD COMPATIBILITY WITH OLD TOKENS
|
||||||
1 (some->> (:sid claims) (read-session manager))
|
0 (read-session manager token)
|
||||||
nil)
|
1 (some->> (:sid claims) (read-session manager))
|
||||||
|
nil)
|
||||||
|
|
||||||
request (cond-> request
|
request
|
||||||
(some? session)
|
(cond-> request
|
||||||
(-> (assoc ::profile-id (:profile-id session))
|
(some? session)
|
||||||
(assoc ::session session)))
|
(-> (assoc ::profile-id (:profile-id session))
|
||||||
|
(assoc ::session session)))
|
||||||
|
|
||||||
response (handler request)]
|
response
|
||||||
|
(binding [ct/*clock* (clock/get-clock (:profile-id session))]
|
||||||
|
(handler request))]
|
||||||
|
|
||||||
(if (and session (renew-session? session))
|
(if (and session (renew-session? session))
|
||||||
(let [session (->> session
|
(let [session (->> session
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
(ns app.http.sse
|
(ns app.http.sse
|
||||||
"SSE (server sent events) helpers"
|
"SSE (server sent events) helpers"
|
||||||
(:refer-clojure :exclude [tap])
|
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.json :as json]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.time :as ct]
|
[app.common.time :as ct]
|
||||||
@@ -223,7 +224,7 @@
|
|||||||
(some? tnow)
|
(some? tnow)
|
||||||
(assoc :tracked-at tnow))))
|
(assoc :tracked-at tnow))))
|
||||||
|
|
||||||
(defn- append-audit-entry!
|
(defn- append-audit-entry
|
||||||
[cfg params]
|
[cfg params]
|
||||||
(let [params (-> params
|
(let [params (-> params
|
||||||
(update :props db/tjson)
|
(update :props db/tjson)
|
||||||
@@ -236,6 +237,16 @@
|
|||||||
(let [params (event->params event)
|
(let [params (event->params event)
|
||||||
tnow (ct/now)]
|
tnow (ct/now)]
|
||||||
|
|
||||||
|
(when (contains? cf/flags :audit-log-logger)
|
||||||
|
(l/log! ::l/logger "app.audit"
|
||||||
|
::l/level :info
|
||||||
|
:profile-id (str (::profile-id event))
|
||||||
|
:ip-addr (str (::ip-addr event))
|
||||||
|
:type (::type event)
|
||||||
|
:name (::name event)
|
||||||
|
:props (json/encode (::props event) :key-fn json/write-camel-key)
|
||||||
|
:context (json/encode (::context event) :key-fn json/write-camel-key)))
|
||||||
|
|
||||||
(when (contains? cf/flags :audit-log)
|
(when (contains? cf/flags :audit-log)
|
||||||
;; NOTE: this operation may cause primary key conflicts on inserts
|
;; NOTE: this operation may cause primary key conflicts on inserts
|
||||||
;; because of the timestamp precission (two concurrent requests), in
|
;; because of the timestamp precission (two concurrent requests), in
|
||||||
@@ -243,7 +254,7 @@
|
|||||||
(let [params (-> params
|
(let [params (-> params
|
||||||
(assoc :created-at tnow)
|
(assoc :created-at tnow)
|
||||||
(update :tracked-at #(or % tnow)))]
|
(update :tracked-at #(or % tnow)))]
|
||||||
(append-audit-entry! cfg params)))
|
(append-audit-entry cfg params)))
|
||||||
|
|
||||||
(when (and (or (contains? cf/flags :telemetry)
|
(when (and (or (contains? cf/flags :telemetry)
|
||||||
(cf/get :telemetry-enabled))
|
(cf/get :telemetry-enabled))
|
||||||
@@ -258,7 +269,7 @@
|
|||||||
(update :tracked-at #(or % tnow))
|
(update :tracked-at #(or % tnow))
|
||||||
(assoc :props {})
|
(assoc :props {})
|
||||||
(assoc :context {}))]
|
(assoc :context {}))]
|
||||||
(append-audit-entry! cfg params)))
|
(append-audit-entry cfg params)))
|
||||||
|
|
||||||
(when (and (contains? cf/flags :webhooks)
|
(when (and (contains? cf/flags :webhooks)
|
||||||
(::webhooks/event? event))
|
(::webhooks/event? event))
|
||||||
@@ -312,4 +323,4 @@
|
|||||||
params (-> (event->params event)
|
params (-> (event->params event)
|
||||||
(assoc :created-at tnow)
|
(assoc :created-at tnow)
|
||||||
(update :tracked-at #(or % tnow)))]
|
(update :tracked-at #(or % tnow)))]
|
||||||
(append-audit-entry! cfg params)))))))
|
(append-audit-entry cfg params)))))))
|
||||||
|
|||||||
@@ -323,6 +323,7 @@
|
|||||||
{::http.client/client (ig/ref ::http.client/client)
|
{::http.client/client (ig/ref ::http.client/client)
|
||||||
::db/pool (ig/ref ::db/pool)
|
::db/pool (ig/ref ::db/pool)
|
||||||
::rds/pool (ig/ref ::rds/pool)
|
::rds/pool (ig/ref ::rds/pool)
|
||||||
|
:app.nitrate/client (ig/ref :app.nitrate/client)
|
||||||
::wrk/executor (ig/ref ::wrk/netty-executor)
|
::wrk/executor (ig/ref ::wrk/netty-executor)
|
||||||
::session/manager (ig/ref ::session/manager)
|
::session/manager (ig/ref ::session/manager)
|
||||||
::ldap/provider (ig/ref ::ldap/provider)
|
::ldap/provider (ig/ref ::ldap/provider)
|
||||||
@@ -339,6 +340,9 @@
|
|||||||
::email/blacklist (ig/ref ::email/blacklist)
|
::email/blacklist (ig/ref ::email/blacklist)
|
||||||
::email/whitelist (ig/ref ::email/whitelist)}
|
::email/whitelist (ig/ref ::email/whitelist)}
|
||||||
|
|
||||||
|
:app.nitrate/client
|
||||||
|
{::http.client/client (ig/ref ::http.client/client)}
|
||||||
|
|
||||||
:app.rpc/management-methods
|
:app.rpc/management-methods
|
||||||
{::http.client/client (ig/ref ::http.client/client)
|
{::http.client/client (ig/ref ::http.client/client)
|
||||||
::db/pool (ig/ref ::db/pool)
|
::db/pool (ig/ref ::db/pool)
|
||||||
@@ -348,6 +352,7 @@
|
|||||||
::sto/storage (ig/ref ::sto/storage)
|
::sto/storage (ig/ref ::sto/storage)
|
||||||
::mtx/metrics (ig/ref ::mtx/metrics)
|
::mtx/metrics (ig/ref ::mtx/metrics)
|
||||||
::mbus/msgbus (ig/ref ::mbus/msgbus)
|
::mbus/msgbus (ig/ref ::mbus/msgbus)
|
||||||
|
:app.nitrate/client (ig/ref :app.nitrate/client)
|
||||||
::rds/client (ig/ref ::rds/client)
|
::rds/client (ig/ref ::rds/client)
|
||||||
::setup/props (ig/ref ::setup/props)}
|
::setup/props (ig/ref ::setup/props)}
|
||||||
|
|
||||||
|
|||||||
147
backend/src/app/nitrate.clj
Normal file
147
backend/src/app/nitrate.clj
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.nitrate
|
||||||
|
"Module that make calls to the external nitrate aplication"
|
||||||
|
(:require
|
||||||
|
[app.common.logging :as l]
|
||||||
|
[app.common.schema :as sm]
|
||||||
|
[app.config :as cf]
|
||||||
|
[app.http.client :as http]
|
||||||
|
[app.rpc :as-alias rpc]
|
||||||
|
[app.setup :as-alias setup]
|
||||||
|
[app.util.json :as json]
|
||||||
|
[clojure.core :as c]
|
||||||
|
[integrant.core :as ig]))
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; HELPERS
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn- request-builder
|
||||||
|
[cfg method uri management-key profile-id]
|
||||||
|
(fn []
|
||||||
|
(http/req! cfg {:method method
|
||||||
|
:headers {"content-type" "application/json"
|
||||||
|
"accept" "application/json"
|
||||||
|
"x-shared-key" management-key
|
||||||
|
"x-profile-id" (str profile-id)}
|
||||||
|
:uri uri
|
||||||
|
:version :http1.1})))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- with-retries
|
||||||
|
[handler max-retries]
|
||||||
|
(fn []
|
||||||
|
(loop [attempt 1]
|
||||||
|
(let [result (try
|
||||||
|
(handler)
|
||||||
|
(catch Exception e
|
||||||
|
(if (< attempt max-retries)
|
||||||
|
::retry
|
||||||
|
(do
|
||||||
|
;; TODO Error handling
|
||||||
|
(l/error :hint "request fail after multiple retries" :cause e)
|
||||||
|
nil))))]
|
||||||
|
(if (= result ::retry)
|
||||||
|
(recur (inc attempt))
|
||||||
|
result)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- with-validate [handler uri schema]
|
||||||
|
(fn []
|
||||||
|
(let [coercer-http (sm/coercer schema
|
||||||
|
:type :validation
|
||||||
|
:hint (str "invalid data received calling " uri))]
|
||||||
|
(try
|
||||||
|
(coercer-http (-> (handler) :body json/decode))
|
||||||
|
(catch Exception e
|
||||||
|
;; TODO Error handling
|
||||||
|
(l/error :hint "error validating json response" :cause e)
|
||||||
|
nil)))))
|
||||||
|
|
||||||
|
(defn- request-to-nitrate
|
||||||
|
[{:keys [::management-key] :as cfg} method uri schema {:keys [::rpc/profile-id] :as params}]
|
||||||
|
(let [full-http-call (-> (request-builder cfg method uri management-key profile-id)
|
||||||
|
(with-retries 3)
|
||||||
|
(with-validate uri schema))]
|
||||||
|
(full-http-call)))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; API
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn call
|
||||||
|
[cfg method params]
|
||||||
|
(when (contains? cf/flags :nitrate)
|
||||||
|
(let [client (get cfg ::client)
|
||||||
|
method (get client method)]
|
||||||
|
(method params))))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(def ^:private schema:organization
|
||||||
|
[:map
|
||||||
|
[:id ::sm/text]
|
||||||
|
[:name ::sm/text]])
|
||||||
|
|
||||||
|
(def ^:private schema:user
|
||||||
|
[:map
|
||||||
|
[:valid ::sm/boolean]])
|
||||||
|
|
||||||
|
(defn- get-team-org
|
||||||
|
[cfg {:keys [team-id] :as params}]
|
||||||
|
(let [baseuri (cf/get :nitrate-backend-uri)]
|
||||||
|
(request-to-nitrate cfg :get (str baseuri "/api/teams/" (str team-id)) schema:organization params)))
|
||||||
|
|
||||||
|
(defn- is-valid-user
|
||||||
|
[cfg {:keys [profile-id] :as params}]
|
||||||
|
(let [baseuri (cf/get :nitrate-backend-uri)]
|
||||||
|
(request-to-nitrate cfg :get (str baseuri "/api/users/" (str profile-id)) schema:user params)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; INITIALIZATION
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defmethod ig/init-key ::client
|
||||||
|
[_ {:keys [::setup/props] :as cfg}]
|
||||||
|
(if (contains? cf/flags :nitrate)
|
||||||
|
(let [management-key (or (cf/get :management-api-key)
|
||||||
|
(get props :management-key))
|
||||||
|
cfg (assoc cfg ::management-key management-key)]
|
||||||
|
{:get-team-org (partial get-team-org cfg)
|
||||||
|
:is-valid-user (partial is-valid-user cfg)})
|
||||||
|
{}))
|
||||||
|
|
||||||
|
(defmethod ig/halt-key! ::client
|
||||||
|
[_ {:keys []}]
|
||||||
|
(do :stuff))
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; UTILS
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
|
||||||
|
(defn add-nitrate-licence-to-profile
|
||||||
|
[cfg profile]
|
||||||
|
(try
|
||||||
|
(let [nitrate-licence (call cfg :is-valid-user {:profile-id (:id profile)})]
|
||||||
|
(assoc profile :nitrate-licence (:valid nitrate-licence)))
|
||||||
|
(catch Throwable cause
|
||||||
|
(l/error :hint "failed to get nitrate licence"
|
||||||
|
:profile-id (:id profile)
|
||||||
|
:cause cause)
|
||||||
|
profile)))
|
||||||
|
|
||||||
|
(defn add-org-to-team
|
||||||
|
[cfg team params]
|
||||||
|
(let [params (assoc (or params {}) :team-id (:id team))
|
||||||
|
org (call cfg :get-team-org params)]
|
||||||
|
(assoc team :organization-id (:id org) :organization-name (:name org))))
|
||||||
@@ -301,6 +301,7 @@
|
|||||||
(let [cfg (assoc cfg ::type "management" ::metrics-id :rpc-management-timing)]
|
(let [cfg (assoc cfg ::type "management" ::metrics-id :rpc-management-timing)]
|
||||||
(->> (sv/scan-ns
|
(->> (sv/scan-ns
|
||||||
'app.rpc.management.subscription
|
'app.rpc.management.subscription
|
||||||
|
'app.rpc.management.nitrate
|
||||||
'app.rpc.management.exporter)
|
'app.rpc.management.exporter)
|
||||||
(map (partial process-method cfg "management" wrap-management))
|
(map (partial process-method cfg "management" wrap-management))
|
||||||
(into {}))))
|
(into {}))))
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
[app.loggers.audit :as audit]
|
[app.loggers.audit :as audit]
|
||||||
[app.main :as-alias main]
|
[app.main :as-alias main]
|
||||||
[app.media :as media]
|
[app.media :as media]
|
||||||
|
[app.nitrate :as nitrate]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.rpc.climit :as climit]
|
[app.rpc.climit :as climit]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
@@ -88,6 +89,8 @@
|
|||||||
|
|
||||||
;; --- QUERY: Get profile (own)
|
;; --- QUERY: Get profile (own)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(sv/defmethod ::get-profile
|
(sv/defmethod ::get-profile
|
||||||
{::rpc/auth false
|
{::rpc/auth false
|
||||||
::doc/added "1.18"
|
::doc/added "1.18"
|
||||||
@@ -98,9 +101,13 @@
|
|||||||
;; no profile-id is in session, and when db call raises not found. In all other
|
;; no profile-id is in session, and when db call raises not found. In all other
|
||||||
;; cases we need to reraise the exception.
|
;; cases we need to reraise the exception.
|
||||||
(try
|
(try
|
||||||
(-> (get-profile pool profile-id)
|
(let [profile (-> (get-profile pool profile-id)
|
||||||
(strip-private-attrs)
|
(strip-private-attrs)
|
||||||
(update :props filter-props))
|
(update :props filter-props))]
|
||||||
|
(if (contains? cf/flags :nitrate)
|
||||||
|
(nitrate/add-nitrate-licence-to-profile cfg profile)
|
||||||
|
profile))
|
||||||
|
|
||||||
(catch Throwable _
|
(catch Throwable _
|
||||||
{:id uuid/zero :fullname "Anonymous User"})))
|
{:id uuid/zero :fullname "Anonymous User"})))
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
[app.main :as-alias main]
|
[app.main :as-alias main]
|
||||||
[app.media :as media]
|
[app.media :as media]
|
||||||
[app.msgbus :as mbus]
|
[app.msgbus :as mbus]
|
||||||
|
[app.nitrate :as nitrate]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.rpc.commands.profile :as profile]
|
[app.rpc.commands.profile :as profile]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
@@ -190,7 +191,9 @@
|
|||||||
::sm/params schema:get-teams}
|
::sm/params schema:get-teams}
|
||||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
|
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
|
||||||
(dm/with-open [conn (db/open pool)]
|
(dm/with-open [conn (db/open pool)]
|
||||||
(get-teams conn profile-id)))
|
(cond->> (get-teams conn profile-id)
|
||||||
|
(contains? cf/flags :nitrate)
|
||||||
|
(map #(nitrate/add-org-to-team cfg % params)))))
|
||||||
|
|
||||||
(def ^:private sql:get-owned-teams
|
(def ^:private sql:get-owned-teams
|
||||||
"SELECT t.id, t.name,
|
"SELECT t.id, t.name,
|
||||||
|
|||||||
@@ -248,11 +248,11 @@
|
|||||||
|
|
||||||
invitations (into #{}
|
invitations (into #{}
|
||||||
(comp
|
(comp
|
||||||
;; We don't re-send invitations to
|
;; We don't re-send invitations to
|
||||||
;; already existing members
|
;; already existing members
|
||||||
(remove #(contains? team-members (:email %)))
|
(remove #(contains? team-members (:email %)))
|
||||||
;; We don't send invitations to
|
;; We don't send invitations to
|
||||||
;; join-requested members
|
;; join-requested members
|
||||||
(remove #(contains? join-requests (:email %)))
|
(remove #(contains? join-requests (:email %)))
|
||||||
(map (fn [{:keys [email role]}]
|
(map (fn [{:keys [email role]}]
|
||||||
(create-invitation cfg
|
(create-invitation cfg
|
||||||
|
|||||||
112
backend/src/app/rpc/management/nitrate.clj
Normal file
112
backend/src/app/rpc/management/nitrate.clj
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.rpc.management.nitrate
|
||||||
|
"Internal Nitrate HTTP API.
|
||||||
|
Provides authenticated access to organization management and token validation endpoints.
|
||||||
|
All requests must include a valid shared key token in the `x-shared-key` header, and
|
||||||
|
a cookie `auth-token` with the user token.
|
||||||
|
They will return `401 Unauthorized` if the shared key or user token are invalid."
|
||||||
|
(:require
|
||||||
|
[app.common.schema :as sm]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.config :as cf]
|
||||||
|
[app.db :as db]
|
||||||
|
[app.msgbus :as mbus]
|
||||||
|
[app.rpc :as-alias rpc]
|
||||||
|
[app.rpc.commands.files :as files]
|
||||||
|
[app.rpc.commands.profile :as profile]
|
||||||
|
[app.rpc.doc :as doc]
|
||||||
|
[app.util.services :as sv]))
|
||||||
|
|
||||||
|
;; ---- API: authenticate
|
||||||
|
(def ^:private schema:profile
|
||||||
|
[:map
|
||||||
|
[:id ::sm/uuid]
|
||||||
|
[:name :string]
|
||||||
|
[:email :string]
|
||||||
|
[:photo-url :string]])
|
||||||
|
|
||||||
|
(sv/defmethod ::authenticate
|
||||||
|
"Authenticate an user
|
||||||
|
@api GET /authenticate
|
||||||
|
@returns
|
||||||
|
200 OK: Returns the authenticated user."
|
||||||
|
{::doc/added "2.12"
|
||||||
|
::sm/result schema:profile}
|
||||||
|
[cfg {:keys [::rpc/profile-id] :as params}]
|
||||||
|
(let [profile (profile/get-profile cfg profile-id)]
|
||||||
|
{:id (get profile :id)
|
||||||
|
:name (get profile :fullname)
|
||||||
|
:email (get profile :email)
|
||||||
|
:photo-url (files/resolve-public-uri (get profile :photo-id))}))
|
||||||
|
|
||||||
|
;; ---- API: get-teams
|
||||||
|
|
||||||
|
(def ^:private sql:get-teams
|
||||||
|
"SELECT t.*
|
||||||
|
FROM team AS t
|
||||||
|
JOIN team_profile_rel AS tpr ON t.id = tpr.team_id
|
||||||
|
WHERE tpr.profile_id = ?
|
||||||
|
AND tpr.is_owner = 't'
|
||||||
|
AND t.is_default = 'f'
|
||||||
|
AND t.deleted_at is null;")
|
||||||
|
|
||||||
|
(def ^:private schema:team
|
||||||
|
[:map
|
||||||
|
[:id ::sm/uuid]
|
||||||
|
[:name :string]])
|
||||||
|
|
||||||
|
(def ^:private schema:get-teams-result
|
||||||
|
[:vector schema:team])
|
||||||
|
|
||||||
|
(sv/defmethod ::get-teams
|
||||||
|
"List teams for which current user is owner.
|
||||||
|
@api GET /get-teams
|
||||||
|
@returns
|
||||||
|
200 OK: Returns the list of teams for the user."
|
||||||
|
{::doc/added "2.12"
|
||||||
|
::sm/result schema:get-teams-result}
|
||||||
|
[cfg {:keys [::rpc/profile-id]}]
|
||||||
|
(when (contains? cf/flags :nitrate)
|
||||||
|
(let [current-user-id (-> (profile/get-profile cfg profile-id) :id)]
|
||||||
|
(->> (db/exec! cfg [sql:get-teams current-user-id])
|
||||||
|
(map #(select-keys % [:id :name]))))))
|
||||||
|
|
||||||
|
;; ---- API: notify-team-change
|
||||||
|
|
||||||
|
(def ^:private schema:notify-team-change
|
||||||
|
[:map
|
||||||
|
[:id ::sm/uuid]
|
||||||
|
[:organization-id ::sm/text]])
|
||||||
|
|
||||||
|
|
||||||
|
(sv/defmethod ::notify-team-change
|
||||||
|
"Notify to Penpot a team change from nitrate
|
||||||
|
@api POST /notify-team-change
|
||||||
|
@returns
|
||||||
|
200 OK"
|
||||||
|
{::doc/added "2.12"
|
||||||
|
::sm/params schema:notify-team-change
|
||||||
|
::rpc/auth false}
|
||||||
|
[cfg {:keys [id organization-id organization-name]}]
|
||||||
|
(when (contains? cf/flags :nitrate)
|
||||||
|
(let [msgbus (::mbus/msgbus cfg)]
|
||||||
|
(mbus/pub! msgbus
|
||||||
|
;;TODO There is a bug on dashboard with teams notifications.
|
||||||
|
;;For now we send it to uuid/zero instead of team-id
|
||||||
|
:topic uuid/zero
|
||||||
|
:message {:type :team-org-change
|
||||||
|
:team-id id
|
||||||
|
:organization-id organization-id
|
||||||
|
:organization-name organization-name}))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -9,48 +9,35 @@
|
|||||||
modification of time offset (useful for testing and time adjustments)."
|
modification of time offset (useful for testing and time adjustments)."
|
||||||
(:require
|
(:require
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
[app.common.time :as ct]
|
[app.common.time :as ct]))
|
||||||
[app.setup :as-alias setup]
|
|
||||||
[integrant.core :as ig])
|
|
||||||
(:import
|
|
||||||
java.time.Clock
|
|
||||||
java.time.Duration
|
|
||||||
java.time.Instant
|
|
||||||
java.time.ZoneId))
|
|
||||||
|
|
||||||
(defonce current
|
(defonce state
|
||||||
(atom {:clock (Clock/systemDefaultZone)
|
(atom {}))
|
||||||
:offset nil}))
|
|
||||||
|
|
||||||
(defmethod ig/init-key ::setup/clock
|
(defn assign-offset
|
||||||
[_ _]
|
"Assign virtual clock offset to a specific user. Is the responsability
|
||||||
(add-watch current ::common
|
of RPC module to properly bind the correct clock to the user
|
||||||
(fn [_ _ _ {:keys [clock offset]}]
|
request."
|
||||||
(let [clock (if (ct/duration? offset)
|
[profile-id duration]
|
||||||
(Clock/offset ^Clock clock
|
(swap! state (fn [state]
|
||||||
^Duration offset)
|
(if (nil? duration)
|
||||||
clock)]
|
(dissoc state profile-id)
|
||||||
(l/wrn :hint "altering clock" :clock (str clock))
|
(assoc state profile-id duration)))))
|
||||||
(alter-var-root #'ct/*clock* (constantly clock))))))
|
|
||||||
|
|
||||||
|
(defn get-offset
|
||||||
|
[profile-id]
|
||||||
|
(get @state profile-id))
|
||||||
|
|
||||||
(defmethod ig/halt-key! ::setup/clock
|
(defn get-clock
|
||||||
[_ _]
|
[profile-id]
|
||||||
(remove-watch current ::common))
|
(if-let [offset (get-offset profile-id)]
|
||||||
|
(ct/offset-clock offset)
|
||||||
|
(ct/get-system-clock)))
|
||||||
|
|
||||||
(defn fixed
|
(defn set-global-clock
|
||||||
"Get fixed clock, mainly used in tests"
|
|
||||||
[instant]
|
|
||||||
(Clock/fixed ^Instant (ct/inst instant)
|
|
||||||
^ZoneId (ZoneId/of "Z")))
|
|
||||||
|
|
||||||
(defn set-offset!
|
|
||||||
[duration]
|
|
||||||
(swap! current assoc :offset (some-> duration ct/duration)))
|
|
||||||
|
|
||||||
(defn set-clock!
|
|
||||||
([]
|
([]
|
||||||
(swap! current assoc :clock (Clock/systemDefaultZone)))
|
(set-global-clock (ct/get-system-clock)))
|
||||||
([clock]
|
([clock]
|
||||||
(when (instance? Clock clock)
|
(assert (ct/clock? clock) "expected valid clock instance")
|
||||||
(swap! current assoc :clock clock))))
|
(l/wrn :hint "altering clock" :clock (str clock))
|
||||||
|
(alter-var-root #'ct/*clock* (constantly clock))))
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"A generic asynchronous events notifications subsystem; used mainly
|
"A generic asynchronous events notifications subsystem; used mainly
|
||||||
for mark event points in functions and be able to attach listeners
|
for mark event points in functions and be able to attach listeners
|
||||||
to them. Mainly used in http.sse for progress reporting."
|
to them. Mainly used in http.sse for progress reporting."
|
||||||
(:refer-clojure :exclude [tap run!])
|
(:refer-clojure :exclude [run!])
|
||||||
(:require
|
(:require
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
[app.db.sql :as sql]
|
[app.db.sql :as sql]
|
||||||
[app.http :as http]
|
[app.http :as http]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.setup.clock :as clock]
|
|
||||||
[app.storage :as sto]
|
[app.storage :as sto]
|
||||||
[backend-tests.helpers :as th]
|
[backend-tests.helpers :as th]
|
||||||
[clojure.test :as t]
|
[clojure.test :as t]
|
||||||
@@ -134,7 +133,7 @@
|
|||||||
;; this will run pending task triggered by deleting user snapshot
|
;; this will run pending task triggered by deleting user snapshot
|
||||||
(th/run-pending-tasks!)
|
(th/run-pending-tasks!)
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||||
(let [res (th/run-task! :objects-gc {})]
|
(let [res (th/run-task! :objects-gc {})]
|
||||||
;; delete 2 snapshots and 2 file data entries
|
;; delete 2 snapshots and 2 file data entries
|
||||||
(t/is (= 4 (:processed res)))))))))
|
(t/is (= 4 (:processed res)))))))))
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
[app.http :as http]
|
[app.http :as http]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.rpc.commands.files :as files]
|
[app.rpc.commands.files :as files]
|
||||||
[app.setup.clock :as clock]
|
|
||||||
[app.storage :as sto]
|
[app.storage :as sto]
|
||||||
[backend-tests.helpers :as th]
|
[backend-tests.helpers :as th]
|
||||||
[clojure.test :as t]
|
[clojure.test :as t]
|
||||||
@@ -842,7 +841,7 @@
|
|||||||
out (th/command! data)
|
out (th/command! data)
|
||||||
error (:error out)]
|
error (:error out)]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (th/ex-info? error))
|
(t/is (th/ex-info? error))
|
||||||
(t/is (th/ex-of-type? error :not-found))))
|
(t/is (th/ex-of-type? error :not-found))))
|
||||||
|
|
||||||
@@ -864,7 +863,7 @@
|
|||||||
out (th/command! data)
|
out (th/command! data)
|
||||||
error (:error out)]
|
error (:error out)]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (th/ex-info? error))
|
(t/is (th/ex-info? error))
|
||||||
(t/is (th/ex-of-type? error :not-found))))
|
(t/is (th/ex-of-type? error :not-found))))
|
||||||
|
|
||||||
@@ -922,7 +921,7 @@
|
|||||||
(t/is (= 0 (:processed result))))
|
(t/is (= 0 (:processed result))))
|
||||||
|
|
||||||
;; run permanent deletion
|
;; run permanent deletion
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||||
(let [result (th/run-task! :objects-gc {})]
|
(let [result (th/run-task! :objects-gc {})]
|
||||||
(t/is (= 3 (:processed result)))))
|
(t/is (= 3 (:processed result)))))
|
||||||
|
|
||||||
@@ -1262,7 +1261,7 @@
|
|||||||
(t/is (= 1 (count rows)))
|
(t/is (= 1 (count rows)))
|
||||||
(t/is (every? #(some? (:data %)) rows)))
|
(t/is (every? #(some? (:data %)) rows)))
|
||||||
|
|
||||||
;; Mark the file ellegible again for GC
|
;; Mark the file ellegible again for GC
|
||||||
(th/db-update! :file
|
(th/db-update! :file
|
||||||
{:has-media-trimmed false}
|
{:has-media-trimmed false}
|
||||||
{:id (:id file)})
|
{:id (:id file)})
|
||||||
@@ -1319,7 +1318,7 @@
|
|||||||
{:file-id (:id file)
|
{:file-id (:id file)
|
||||||
:type "fragment"}
|
:type "fragment"}
|
||||||
{:order-by [:created-at]})]
|
{:order-by [:created-at]})]
|
||||||
;; (pp/pprint rows)
|
;; (pp/pprint rows)
|
||||||
(t/is (= 2 (count rows)))
|
(t/is (= 2 (count rows)))
|
||||||
(t/is (nil? (:data row1)))
|
(t/is (nil? (:data row1)))
|
||||||
(t/is (= "storage" (:backend row1)))
|
(t/is (= "storage" (:backend row1)))
|
||||||
@@ -1875,7 +1874,7 @@
|
|||||||
file-id (uuid/next)
|
file-id (uuid/next)
|
||||||
now (ct/inst "2025-10-31T00:00:00Z")]
|
now (ct/inst "2025-10-31T00:00:00Z")]
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed now)]
|
(binding [ct/*clock* (ct/fixed-clock now)]
|
||||||
(let [data {::th/type :create-file
|
(let [data {::th/type :create-file
|
||||||
::rpc/profile-id (:id prof)
|
::rpc/profile-id (:id prof)
|
||||||
:project-id proj-id
|
:project-id proj-id
|
||||||
@@ -1937,7 +1936,7 @@
|
|||||||
file-id (uuid/next)
|
file-id (uuid/next)
|
||||||
now (ct/inst "2025-10-31T00:00:00Z")]
|
now (ct/inst "2025-10-31T00:00:00Z")]
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed now)]
|
(binding [ct/*clock* (ct/fixed-clock now)]
|
||||||
(let [data {::th/type :create-file
|
(let [data {::th/type :create-file
|
||||||
::rpc/profile-id (:id prof)
|
::rpc/profile-id (:id prof)
|
||||||
:project-id proj-id
|
:project-id proj-id
|
||||||
@@ -2000,7 +1999,7 @@
|
|||||||
team-id (:default-team-id profile)
|
team-id (:default-team-id profile)
|
||||||
now (ct/inst "2025-10-31T00:00:00Z")]
|
now (ct/inst "2025-10-31T00:00:00Z")]
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed now)]
|
(binding [ct/*clock* (ct/fixed-clock now)]
|
||||||
(let [project (th/create-project* 1 {:profile-id (:id profile)
|
(let [project (th/create-project* 1 {:profile-id (:id profile)
|
||||||
:team-id team-id})
|
:team-id team-id})
|
||||||
file (th/create-file* 1 {:profile-id (:id profile)
|
file (th/create-file* 1 {:profile-id (:id profile)
|
||||||
|
|||||||
@@ -85,7 +85,7 @@
|
|||||||
(t/is (map? (:result out))))
|
(t/is (map? (:result out))))
|
||||||
|
|
||||||
;; run the task again
|
;; run the task again
|
||||||
(let [res (binding [ct/*clock* (clock/fixed (ct/in-future {:minutes 31}))]
|
(let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:minutes 31}))]
|
||||||
(th/run-task! "storage-gc-touched" {}))]
|
(th/run-task! "storage-gc-touched" {}))]
|
||||||
(t/is (= 2 (:freeze res))))
|
(t/is (= 2 (:freeze res))))
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@
|
|||||||
(t/is (some? (sto/get-object storage (:media-id row2))))
|
(t/is (some? (sto/get-object storage (:media-id row2))))
|
||||||
|
|
||||||
;; run the task again
|
;; run the task again
|
||||||
(let [res (binding [ct/*clock* (clock/fixed (ct/in-future {:minutes 31}))]
|
(let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:minutes 31}))]
|
||||||
(th/run-task! :storage-gc-touched {}))]
|
(th/run-task! :storage-gc-touched {}))]
|
||||||
(t/is (= 1 (:delete res)))
|
(t/is (= 1 (:delete res)))
|
||||||
(t/is (= 0 (:freeze res))))
|
(t/is (= 0 (:freeze res))))
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
|
|
||||||
;; Run the storage gc deleted task, it should permanently delete
|
;; Run the storage gc deleted task, it should permanently delete
|
||||||
;; all storage objects related to the deleted thumbnails
|
;; all storage objects related to the deleted thumbnails
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||||
(t/is (= 1 (:deleted res)))))
|
(t/is (= 1 (:deleted res)))))
|
||||||
|
|
||||||
@@ -247,7 +247,7 @@
|
|||||||
|
|
||||||
;; Run the storage gc deleted task, it should permanently delete
|
;; Run the storage gc deleted task, it should permanently delete
|
||||||
;; all storage objects related to the deleted thumbnails
|
;; all storage objects related to the deleted thumbnails
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||||
(let [result (th/run-task! :storage-gc-deleted {})]
|
(let [result (th/run-task! :storage-gc-deleted {})]
|
||||||
(t/is (= 1 (:deleted result)))))
|
(t/is (= 1 (:deleted result)))))
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.http :as http]
|
[app.http :as http]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.setup.clock :as clock]
|
|
||||||
[app.storage :as sto]
|
[app.storage :as sto]
|
||||||
[backend-tests.helpers :as th]
|
[backend-tests.helpers :as th]
|
||||||
[clojure.test :as t]
|
[clojure.test :as t]
|
||||||
@@ -147,7 +146,7 @@
|
|||||||
(t/is (= 0 (:freeze res)))
|
(t/is (= 0 (:freeze res)))
|
||||||
(t/is (= 0 (:delete res))))
|
(t/is (= 0 (:delete res))))
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||||
(let [res (th/run-task! :objects-gc {})]
|
(let [res (th/run-task! :objects-gc {})]
|
||||||
(t/is (= 2 (:processed res))))
|
(t/is (= 2 (:processed res))))
|
||||||
|
|
||||||
@@ -208,7 +207,7 @@
|
|||||||
(t/is (= 0 (:freeze res)))
|
(t/is (= 0 (:freeze res)))
|
||||||
(t/is (= 0 (:delete res))))
|
(t/is (= 0 (:delete res))))
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||||
(let [res (th/run-task! :objects-gc {})]
|
(let [res (th/run-task! :objects-gc {})]
|
||||||
(t/is (= 1 (:processed res))))
|
(t/is (= 1 (:processed res))))
|
||||||
|
|
||||||
@@ -268,7 +267,7 @@
|
|||||||
(t/is (= 0 (:freeze res)))
|
(t/is (= 0 (:freeze res)))
|
||||||
(t/is (= 0 (:delete res))))
|
(t/is (= 0 (:delete res))))
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||||
(let [res (th/run-task! :objects-gc {})]
|
(let [res (th/run-task! :objects-gc {})]
|
||||||
(t/is (= 1 (:processed res))))
|
(t/is (= 1 (:processed res))))
|
||||||
|
|
||||||
|
|||||||
@@ -536,7 +536,7 @@
|
|||||||
:token rtoken}
|
:token rtoken}
|
||||||
|
|
||||||
{:keys [result error] :as out} (th/command! data)]
|
{:keys [result error] :as out} (th/command! data)]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? error))
|
(t/is (nil? error))
|
||||||
(t/is (map? result))
|
(t/is (map? result))
|
||||||
(t/is (string? (:invitation-token result))))))
|
(t/is (string? (:invitation-token result))))))
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.http :as http]
|
[app.http :as http]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.setup.clock :as clock]
|
|
||||||
[backend-tests.helpers :as th]
|
[backend-tests.helpers :as th]
|
||||||
[clojure.test :as t]))
|
[clojure.test :as t]))
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@
|
|||||||
:team-id (:id team)
|
:team-id (:id team)
|
||||||
:name "test project"}
|
:name "test project"}
|
||||||
out (th/command! data)]
|
out (th/command! data)]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
|
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
(let [result (:result out)]
|
(let [result (:result out)]
|
||||||
@@ -94,7 +93,7 @@
|
|||||||
:id project-id}
|
:id project-id}
|
||||||
out (th/command! data)]
|
out (th/command! data)]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
(t/is (nil? (:result out))))
|
(t/is (nil? (:result out))))
|
||||||
|
|
||||||
@@ -228,7 +227,7 @@
|
|||||||
(t/is (= 0 (count result)))))
|
(t/is (= 0 (count result)))))
|
||||||
|
|
||||||
;; run permanent deletion
|
;; run permanent deletion
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||||
(let [result (th/run-task! :objects-gc {})]
|
(let [result (th/run-task! :objects-gc {})]
|
||||||
(t/is (= 1 (:processed result)))))
|
(t/is (= 1 (:processed result)))))
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.http :as http]
|
[app.http :as http]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.setup.clock :as clock]
|
|
||||||
[app.storage :as sto]
|
[app.storage :as sto]
|
||||||
[app.tokens :as tokens]
|
[app.tokens :as tokens]
|
||||||
[backend-tests.helpers :as th]
|
[backend-tests.helpers :as th]
|
||||||
@@ -526,7 +525,7 @@
|
|||||||
(t/is (= :not-found (:type edata)))))
|
(t/is (= :not-found (:type edata)))))
|
||||||
|
|
||||||
;; run permanent deletion
|
;; run permanent deletion
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||||
(let [result (th/run-task! :objects-gc {})]
|
(let [result (th/run-task! :objects-gc {})]
|
||||||
(t/is (= 2 (:processed result)))))
|
(t/is (= 2 (:processed result)))))
|
||||||
|
|
||||||
@@ -583,7 +582,7 @@
|
|||||||
(t/is (= 1 (count rows)))
|
(t/is (= 1 (count rows)))
|
||||||
(t/is (ct/inst? (:deleted-at (first rows)))))
|
(t/is (ct/inst? (:deleted-at (first rows)))))
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:days 8}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))]
|
||||||
(let [result (th/run-task! :objects-gc {})]
|
(let [result (th/run-task! :objects-gc {})]
|
||||||
(t/is (= 7 (:processed result)))))))
|
(t/is (= 7 (:processed result)))))))
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.setup.clock :as clock]
|
|
||||||
[app.storage :as sto]
|
[app.storage :as sto]
|
||||||
[backend-tests.helpers :as th]
|
[backend-tests.helpers :as th]
|
||||||
[clojure.test :as t]
|
[clojure.test :as t]
|
||||||
@@ -99,14 +98,14 @@
|
|||||||
::sto/expired-at (ct/in-future {:hours 1})
|
::sto/expired-at (ct/in-future {:hours 1})
|
||||||
:content-type "text/plain"})]
|
:content-type "text/plain"})]
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:minutes 0}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:minutes 0}))]
|
||||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||||
(t/is (= 1 (:deleted res)))))
|
(t/is (= 1 (:deleted res)))))
|
||||||
|
|
||||||
(let [res (th/db-exec-one! ["select count(*) from storage_object;"])]
|
(let [res (th/db-exec-one! ["select count(*) from storage_object;"])]
|
||||||
(t/is (= 2 (:count res))))
|
(t/is (= 2 (:count res))))
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed (ct/in-future {:minutes 61}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/in-future {:minutes 61}))]
|
||||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||||
(t/is (= 1 (:deleted res)))))
|
(t/is (= 1 (:deleted res)))))
|
||||||
|
|
||||||
@@ -331,22 +330,22 @@
|
|||||||
:content-type "text/plain"})]
|
:content-type "text/plain"})]
|
||||||
|
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed now)]
|
(binding [ct/*clock* (ct/fixed-clock now)]
|
||||||
(let [res (th/run-task! :storage-gc-touched {})]
|
(let [res (th/run-task! :storage-gc-touched {})]
|
||||||
(t/is (= 0 (:freeze res)))
|
(t/is (= 0 (:freeze res)))
|
||||||
(t/is (= 0 (:delete res)))))
|
(t/is (= 0 (:delete res)))))
|
||||||
|
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed (ct/plus now {:minutes 1}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/plus now {:minutes 1}))]
|
||||||
(let [res (th/run-task! :storage-gc-touched {})]
|
(let [res (th/run-task! :storage-gc-touched {})]
|
||||||
(t/is (= 0 (:freeze res)))
|
(t/is (= 0 (:freeze res)))
|
||||||
(t/is (= 1 (:delete res)))))
|
(t/is (= 1 (:delete res)))))
|
||||||
|
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed (ct/plus now {:hours 1}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/plus now {:hours 1}))]
|
||||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||||
(t/is (= 0 (:deleted res)))))
|
(t/is (= 0 (:deleted res)))))
|
||||||
|
|
||||||
(binding [ct/*clock* (clock/fixed (ct/plus now {:hours 2}))]
|
(binding [ct/*clock* (ct/fixed-clock (ct/plus now {:hours 2}))]
|
||||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||||
(t/is (= 0 (:deleted res)))))))
|
(t/is (= 0 (:deleted res)))))))
|
||||||
|
|||||||
1145
backend/yarn.lock
1145
backend/yarn.lock
File diff suppressed because it is too large
Load Diff
7
common/.gitignore
vendored
7
common/.gitignore
vendored
@@ -1,7 +0,0 @@
|
|||||||
.pnp.*
|
|
||||||
.yarn/*
|
|
||||||
!.yarn/patches
|
|
||||||
!.yarn/plugins
|
|
||||||
!.yarn/releases
|
|
||||||
!.yarn/sdks
|
|
||||||
!.yarn/versions
|
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
thheller/shadow-cljs {:mvn/version "3.2.0"}
|
thheller/shadow-cljs {:mvn/version "3.2.0"}
|
||||||
com.clojure-goes-fast/clj-async-profiler {:mvn/version "RELEASE"}
|
com.clojure-goes-fast/clj-async-profiler {:mvn/version "RELEASE"}
|
||||||
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
|
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
|
||||||
criterium/criterium {:mvn/version "RELEASE"}
|
criterium/criterium {:mvn/version "0.4.6"}
|
||||||
mockery/mockery {:mvn/version "RELEASE"}}
|
mockery/mockery {:mvn/version "RELEASE"}}
|
||||||
:extra-paths ["test" "dev"]}
|
:extra-paths ["test" "dev"]}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"author": "Kaleidos INC",
|
"author": "Kaleidos INC",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c",
|
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -23,9 +23,9 @@
|
|||||||
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",
|
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",
|
||||||
"fmt:clj": "cljfmt fix --parallel=true src/ test/",
|
"fmt:clj": "cljfmt fix --parallel=true src/ test/",
|
||||||
"lint:clj": "clj-kondo --parallel=true --lint src/",
|
"lint:clj": "clj-kondo --parallel=true --lint src/",
|
||||||
"lint": "yarn run lint:clj",
|
"lint": "pnpm run lint:clj",
|
||||||
"watch:test": "concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests/ --exec 'node target/tests/test.js'\"",
|
"watch:test": "concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests/ --exec 'node target/tests/test.js'\"",
|
||||||
"build:test": "clojure -M:dev:shadow-cljs compile test",
|
"build:test": "clojure -M:dev:shadow-cljs compile test",
|
||||||
"test": "yarn run build:test && node target/tests/test.js"
|
"test": "pnpm run build:test && node target/tests/test.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
489
common/pnpm-lock.yaml
generated
Normal file
489
common/pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,489 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.:
|
||||||
|
dependencies:
|
||||||
|
date-fns:
|
||||||
|
specifier: ^4.1.0
|
||||||
|
version: 4.1.0
|
||||||
|
devDependencies:
|
||||||
|
concurrently:
|
||||||
|
specifier: ^9.1.2
|
||||||
|
version: 9.2.1
|
||||||
|
nodemon:
|
||||||
|
specifier: ^3.1.10
|
||||||
|
version: 3.1.11
|
||||||
|
source-map-support:
|
||||||
|
specifier: ^0.5.21
|
||||||
|
version: 0.5.21
|
||||||
|
ws:
|
||||||
|
specifier: ^8.18.2
|
||||||
|
version: 8.18.3
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
ansi-regex@5.0.1:
|
||||||
|
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
ansi-styles@4.3.0:
|
||||||
|
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
anymatch@3.1.3:
|
||||||
|
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
balanced-match@1.0.2:
|
||||||
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
|
binary-extensions@2.3.0:
|
||||||
|
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
brace-expansion@1.1.12:
|
||||||
|
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
||||||
|
|
||||||
|
braces@3.0.3:
|
||||||
|
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
buffer-from@1.1.2:
|
||||||
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
|
||||||
|
chalk@4.1.2:
|
||||||
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
chokidar@3.6.0:
|
||||||
|
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||||
|
engines: {node: '>= 8.10.0'}
|
||||||
|
|
||||||
|
cliui@8.0.1:
|
||||||
|
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
color-convert@2.0.1:
|
||||||
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
|
engines: {node: '>=7.0.0'}
|
||||||
|
|
||||||
|
color-name@1.1.4:
|
||||||
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
|
||||||
|
concat-map@0.0.1:
|
||||||
|
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||||
|
|
||||||
|
concurrently@9.2.1:
|
||||||
|
resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
date-fns@4.1.0:
|
||||||
|
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
|
||||||
|
|
||||||
|
debug@4.4.3:
|
||||||
|
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||||
|
engines: {node: '>=6.0'}
|
||||||
|
peerDependencies:
|
||||||
|
supports-color: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
supports-color:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
emoji-regex@8.0.0:
|
||||||
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
|
||||||
|
escalade@3.2.0:
|
||||||
|
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
fill-range@7.1.1:
|
||||||
|
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
get-caller-file@2.0.5:
|
||||||
|
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||||
|
engines: {node: 6.* || 8.* || >= 10.*}
|
||||||
|
|
||||||
|
glob-parent@5.1.2:
|
||||||
|
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
has-flag@3.0.0:
|
||||||
|
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
has-flag@4.0.0:
|
||||||
|
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
ignore-by-default@1.0.1:
|
||||||
|
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
|
||||||
|
|
||||||
|
is-binary-path@2.1.0:
|
||||||
|
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
is-extglob@2.1.1:
|
||||||
|
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
is-fullwidth-code-point@3.0.0:
|
||||||
|
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
is-glob@4.0.3:
|
||||||
|
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
is-number@7.0.0:
|
||||||
|
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||||
|
engines: {node: '>=0.12.0'}
|
||||||
|
|
||||||
|
minimatch@3.1.2:
|
||||||
|
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||||
|
|
||||||
|
ms@2.1.3:
|
||||||
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
|
nodemon@3.1.11:
|
||||||
|
resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
normalize-path@3.0.0:
|
||||||
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
picomatch@2.3.1:
|
||||||
|
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||||
|
engines: {node: '>=8.6'}
|
||||||
|
|
||||||
|
pstree.remy@1.1.8:
|
||||||
|
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
|
||||||
|
|
||||||
|
readdirp@3.6.0:
|
||||||
|
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||||
|
engines: {node: '>=8.10.0'}
|
||||||
|
|
||||||
|
require-directory@2.1.1:
|
||||||
|
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
rxjs@7.8.2:
|
||||||
|
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
|
||||||
|
|
||||||
|
semver@7.7.3:
|
||||||
|
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
shell-quote@1.8.3:
|
||||||
|
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
simple-update-notifier@2.0.0:
|
||||||
|
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
source-map-support@0.5.21:
|
||||||
|
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||||
|
|
||||||
|
source-map@0.6.1:
|
||||||
|
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
string-width@4.2.3:
|
||||||
|
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
strip-ansi@6.0.1:
|
||||||
|
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
supports-color@5.5.0:
|
||||||
|
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
supports-color@7.2.0:
|
||||||
|
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
supports-color@8.1.1:
|
||||||
|
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
to-regex-range@5.0.1:
|
||||||
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
|
engines: {node: '>=8.0'}
|
||||||
|
|
||||||
|
touch@3.1.1:
|
||||||
|
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
tree-kill@1.2.2:
|
||||||
|
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
tslib@2.8.1:
|
||||||
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
|
undefsafe@2.0.5:
|
||||||
|
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
|
||||||
|
|
||||||
|
wrap-ansi@7.0.0:
|
||||||
|
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
ws@8.18.3:
|
||||||
|
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
|
||||||
|
engines: {node: '>=10.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
bufferutil: ^4.0.1
|
||||||
|
utf-8-validate: '>=5.0.2'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
bufferutil:
|
||||||
|
optional: true
|
||||||
|
utf-8-validate:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
y18n@5.0.8:
|
||||||
|
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
yargs-parser@21.1.1:
|
||||||
|
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
yargs@17.7.2:
|
||||||
|
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
|
||||||
|
ansi-regex@5.0.1: {}
|
||||||
|
|
||||||
|
ansi-styles@4.3.0:
|
||||||
|
dependencies:
|
||||||
|
color-convert: 2.0.1
|
||||||
|
|
||||||
|
anymatch@3.1.3:
|
||||||
|
dependencies:
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
picomatch: 2.3.1
|
||||||
|
|
||||||
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
|
binary-extensions@2.3.0: {}
|
||||||
|
|
||||||
|
brace-expansion@1.1.12:
|
||||||
|
dependencies:
|
||||||
|
balanced-match: 1.0.2
|
||||||
|
concat-map: 0.0.1
|
||||||
|
|
||||||
|
braces@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
fill-range: 7.1.1
|
||||||
|
|
||||||
|
buffer-from@1.1.2: {}
|
||||||
|
|
||||||
|
chalk@4.1.2:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
supports-color: 7.2.0
|
||||||
|
|
||||||
|
chokidar@3.6.0:
|
||||||
|
dependencies:
|
||||||
|
anymatch: 3.1.3
|
||||||
|
braces: 3.0.3
|
||||||
|
glob-parent: 5.1.2
|
||||||
|
is-binary-path: 2.1.0
|
||||||
|
is-glob: 4.0.3
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
readdirp: 3.6.0
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
cliui@8.0.1:
|
||||||
|
dependencies:
|
||||||
|
string-width: 4.2.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
wrap-ansi: 7.0.0
|
||||||
|
|
||||||
|
color-convert@2.0.1:
|
||||||
|
dependencies:
|
||||||
|
color-name: 1.1.4
|
||||||
|
|
||||||
|
color-name@1.1.4: {}
|
||||||
|
|
||||||
|
concat-map@0.0.1: {}
|
||||||
|
|
||||||
|
concurrently@9.2.1:
|
||||||
|
dependencies:
|
||||||
|
chalk: 4.1.2
|
||||||
|
rxjs: 7.8.2
|
||||||
|
shell-quote: 1.8.3
|
||||||
|
supports-color: 8.1.1
|
||||||
|
tree-kill: 1.2.2
|
||||||
|
yargs: 17.7.2
|
||||||
|
|
||||||
|
date-fns@4.1.0: {}
|
||||||
|
|
||||||
|
debug@4.4.3(supports-color@5.5.0):
|
||||||
|
dependencies:
|
||||||
|
ms: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
supports-color: 5.5.0
|
||||||
|
|
||||||
|
emoji-regex@8.0.0: {}
|
||||||
|
|
||||||
|
escalade@3.2.0: {}
|
||||||
|
|
||||||
|
fill-range@7.1.1:
|
||||||
|
dependencies:
|
||||||
|
to-regex-range: 5.0.1
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
get-caller-file@2.0.5: {}
|
||||||
|
|
||||||
|
glob-parent@5.1.2:
|
||||||
|
dependencies:
|
||||||
|
is-glob: 4.0.3
|
||||||
|
|
||||||
|
has-flag@3.0.0: {}
|
||||||
|
|
||||||
|
has-flag@4.0.0: {}
|
||||||
|
|
||||||
|
ignore-by-default@1.0.1: {}
|
||||||
|
|
||||||
|
is-binary-path@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
binary-extensions: 2.3.0
|
||||||
|
|
||||||
|
is-extglob@2.1.1: {}
|
||||||
|
|
||||||
|
is-fullwidth-code-point@3.0.0: {}
|
||||||
|
|
||||||
|
is-glob@4.0.3:
|
||||||
|
dependencies:
|
||||||
|
is-extglob: 2.1.1
|
||||||
|
|
||||||
|
is-number@7.0.0: {}
|
||||||
|
|
||||||
|
minimatch@3.1.2:
|
||||||
|
dependencies:
|
||||||
|
brace-expansion: 1.1.12
|
||||||
|
|
||||||
|
ms@2.1.3: {}
|
||||||
|
|
||||||
|
nodemon@3.1.11:
|
||||||
|
dependencies:
|
||||||
|
chokidar: 3.6.0
|
||||||
|
debug: 4.4.3(supports-color@5.5.0)
|
||||||
|
ignore-by-default: 1.0.1
|
||||||
|
minimatch: 3.1.2
|
||||||
|
pstree.remy: 1.1.8
|
||||||
|
semver: 7.7.3
|
||||||
|
simple-update-notifier: 2.0.0
|
||||||
|
supports-color: 5.5.0
|
||||||
|
touch: 3.1.1
|
||||||
|
undefsafe: 2.0.5
|
||||||
|
|
||||||
|
normalize-path@3.0.0: {}
|
||||||
|
|
||||||
|
picomatch@2.3.1: {}
|
||||||
|
|
||||||
|
pstree.remy@1.1.8: {}
|
||||||
|
|
||||||
|
readdirp@3.6.0:
|
||||||
|
dependencies:
|
||||||
|
picomatch: 2.3.1
|
||||||
|
|
||||||
|
require-directory@2.1.1: {}
|
||||||
|
|
||||||
|
rxjs@7.8.2:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
semver@7.7.3: {}
|
||||||
|
|
||||||
|
shell-quote@1.8.3: {}
|
||||||
|
|
||||||
|
simple-update-notifier@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
semver: 7.7.3
|
||||||
|
|
||||||
|
source-map-support@0.5.21:
|
||||||
|
dependencies:
|
||||||
|
buffer-from: 1.1.2
|
||||||
|
source-map: 0.6.1
|
||||||
|
|
||||||
|
source-map@0.6.1: {}
|
||||||
|
|
||||||
|
string-width@4.2.3:
|
||||||
|
dependencies:
|
||||||
|
emoji-regex: 8.0.0
|
||||||
|
is-fullwidth-code-point: 3.0.0
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
|
strip-ansi@6.0.1:
|
||||||
|
dependencies:
|
||||||
|
ansi-regex: 5.0.1
|
||||||
|
|
||||||
|
supports-color@5.5.0:
|
||||||
|
dependencies:
|
||||||
|
has-flag: 3.0.0
|
||||||
|
|
||||||
|
supports-color@7.2.0:
|
||||||
|
dependencies:
|
||||||
|
has-flag: 4.0.0
|
||||||
|
|
||||||
|
supports-color@8.1.1:
|
||||||
|
dependencies:
|
||||||
|
has-flag: 4.0.0
|
||||||
|
|
||||||
|
to-regex-range@5.0.1:
|
||||||
|
dependencies:
|
||||||
|
is-number: 7.0.0
|
||||||
|
|
||||||
|
touch@3.1.1: {}
|
||||||
|
|
||||||
|
tree-kill@1.2.2: {}
|
||||||
|
|
||||||
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
|
undefsafe@2.0.5: {}
|
||||||
|
|
||||||
|
wrap-ansi@7.0.0:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
string-width: 4.2.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
|
ws@8.18.3: {}
|
||||||
|
|
||||||
|
y18n@5.0.8: {}
|
||||||
|
|
||||||
|
yargs-parser@21.1.1: {}
|
||||||
|
|
||||||
|
yargs@17.7.2:
|
||||||
|
dependencies:
|
||||||
|
cliui: 8.0.1
|
||||||
|
escalade: 3.2.0
|
||||||
|
get-caller-file: 2.0.5
|
||||||
|
require-directory: 2.1.1
|
||||||
|
string-width: 4.2.3
|
||||||
|
y18n: 5.0.8
|
||||||
|
yargs-parser: 21.1.1
|
||||||
0
common/pnpm-workspace.yaml
Normal file
0
common/pnpm-workspace.yaml
Normal file
@@ -3,5 +3,5 @@
|
|||||||
set -ex
|
set -ex
|
||||||
corepack enable;
|
corepack enable;
|
||||||
corepack install;
|
corepack install;
|
||||||
yarn install;
|
pnpm install;
|
||||||
yarn run test;
|
pnpm run test;
|
||||||
|
|||||||
@@ -1092,9 +1092,9 @@
|
|||||||
(if (number? num)
|
(if (number? num)
|
||||||
(try
|
(try
|
||||||
(let [num-str (mth/to-fixed num precision)
|
(let [num-str (mth/to-fixed num precision)
|
||||||
;; Remove all trailing zeros after the comma 100.00000
|
;; Remove all trailing zeros after the comma 100.00000
|
||||||
num-str (str/replace num-str trail-zeros-regex-1 "")]
|
num-str (str/replace num-str trail-zeros-regex-1 "")]
|
||||||
;; Remove trailing zeros after a decimal number: 0.001|00|
|
;; Remove trailing zeros after a decimal number: 0.001|00|
|
||||||
(if-let [m (re-find trail-zeros-regex-2 num-str)]
|
(if-let [m (re-find trail-zeros-regex-2 num-str)]
|
||||||
(str/replace num-str (first m) (second m))
|
(str/replace num-str (first m) (second m))
|
||||||
num-str))
|
num-str))
|
||||||
|
|||||||
@@ -241,7 +241,20 @@
|
|||||||
(with-out-str
|
(with-out-str
|
||||||
(print-all cause)))))
|
(print-all cause)))))
|
||||||
|
|
||||||
#?(:clj
|
(defn print-throwable
|
||||||
(defn print-throwable
|
[cause & {:as opts}]
|
||||||
[cause & {:as opts}]
|
#?(:clj
|
||||||
(println (format-throwable cause opts))))
|
(println (format-throwable cause opts))
|
||||||
|
:cljs
|
||||||
|
(let [prefix (get opts :prefix "exception")
|
||||||
|
title (str prefix ": " (ex-message cause))
|
||||||
|
exdata (ex-data cause)]
|
||||||
|
(js/console.group title)
|
||||||
|
(when-let [explain (get exdata ::sm/explain)]
|
||||||
|
(println (sm/humanize-explain explain)))
|
||||||
|
|
||||||
|
(js/console.log "\nData:")
|
||||||
|
(pp/pprint (dissoc exdata ::sm/explain))
|
||||||
|
|
||||||
|
(js/console.log "\nTrace:")
|
||||||
|
(js/console.error (.-stack cause)))))
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Set parent to root frame.
|
;; Set parent to root frame.
|
||||||
(log/debug :hint " -> set to " :parent-id uuid/zero)
|
(log/debug :hint " -> set to " :parent-id uuid/zero)
|
||||||
(assoc shape :parent-id uuid/zero))]
|
(assoc shape :parent-id uuid/zero))]
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [parent-shape]
|
(fn [parent-shape]
|
||||||
; Add shape to parent's children list
|
;; Add shape to parent's children list
|
||||||
(log/debug :hint " -> add children to" :parent-id (:id parent-shape))
|
(log/debug :hint " -> add children to" :parent-id (:id parent-shape))
|
||||||
(update parent-shape :shapes conj (:id shape)))]
|
(update parent-shape :shapes conj (:id shape)))]
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Remove duplicated
|
;; Remove duplicated
|
||||||
(log/debug :hint " -> remove duplicated children")
|
(log/debug :hint " -> remove duplicated children")
|
||||||
(update shape :shapes distinct))]
|
(update shape :shapes distinct))]
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Locate the first frame in parents and set frame-id to it.
|
;; Locate the first frame in parents and set frame-id to it.
|
||||||
(let [page (ctpl/get-page file-data page-id)
|
(let [page (ctpl/get-page file-data page-id)
|
||||||
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
||||||
frame-id (or (:id frame) uuid/zero)]
|
frame-id (or (:id frame) uuid/zero)]
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Locate the first frame in parents and set frame-id to it.
|
;; Locate the first frame in parents and set frame-id to it.
|
||||||
(let [page (ctpl/get-page file-data page-id)
|
(let [page (ctpl/get-page file-data page-id)
|
||||||
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
||||||
frame-id (or (:id frame) uuid/zero)]
|
frame-id (or (:id frame) uuid/zero)]
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Set the :shape as main instance root
|
;; Set the :shape as main instance root
|
||||||
(log/debug :hint " -> set :main-instance")
|
(log/debug :hint " -> set :main-instance")
|
||||||
(assoc shape :main-instance true))]
|
(assoc shape :main-instance true))]
|
||||||
|
|
||||||
@@ -147,12 +147,13 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Set :component-file to local file
|
;; Set :component-file to local file
|
||||||
(log/debug :hint " -> set :component-file to local file")
|
(log/debug :hint " -> set :component-file to local file")
|
||||||
(assoc shape :component-file (:id file-data)))]
|
(assoc shape :component-file (:id file-data)))]
|
||||||
; There is no solution that may recover it with confidence
|
|
||||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
;; There is no solution that may recover it with confidence
|
||||||
;; shape)]
|
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
|
;; shape)]
|
||||||
|
|
||||||
(log/dbg :hint "repairing shape :component-main-external" :id (:id shape) :name (:name shape) :page-id page-id)
|
(log/dbg :hint "repairing shape :component-main-external" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||||
(-> (pcb/empty-changes nil page-id)
|
(-> (pcb/empty-changes nil page-id)
|
||||||
@@ -166,12 +167,12 @@
|
|||||||
|
|
||||||
repair-shape
|
repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Detach the shape and convert it to non instance.
|
;; Detach the shape and convert it to non instance.
|
||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
; There is no solution that may recover it with confidence
|
;; There is no solution that may recover it with confidence
|
||||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
;; shape)]
|
;; shape)]
|
||||||
|
|
||||||
(log/dbg :hint "repairing shape :component-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
(log/dbg :hint "repairing shape :component-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||||
(-> (pcb/empty-changes nil page-id)
|
(-> (pcb/empty-changes nil page-id)
|
||||||
@@ -184,7 +185,7 @@
|
|||||||
|
|
||||||
repair-component
|
repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
; Assign main instance in the component to current shape
|
;; Assign main instance in the component to current shape
|
||||||
(log/debug :hint " -> assign main-instance-id" :component-id (:id component))
|
(log/debug :hint " -> assign main-instance-id" :component-id (:id component))
|
||||||
(assoc component :main-instance-id (:id shape)))
|
(assoc component :main-instance-id (:id shape)))
|
||||||
|
|
||||||
@@ -207,7 +208,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-component
|
(let [repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
; Assign main instance in the component to current shape
|
;; Assign main instance in the component to current shape
|
||||||
(log/debug :hint " -> assign main-instance-page" :component-id (:id component))
|
(log/debug :hint " -> assign main-instance-page" :component-id (:id component))
|
||||||
(assoc component :main-instance-page page-id))]
|
(assoc component :main-instance-page page-id))]
|
||||||
(log/dbg :hint "repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
|
(log/dbg :hint "repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||||
@@ -219,7 +220,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; There is no solution that may recover it with confidence
|
;; There is no solution that may recover it with confidence
|
||||||
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
shape)]
|
shape)]
|
||||||
|
|
||||||
@@ -232,7 +233,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Unset the :shape as main instance root
|
;; Unset the :shape as main instance root
|
||||||
(log/debug :hint " -> unset :main-instance")
|
(log/debug :hint " -> unset :main-instance")
|
||||||
(dissoc shape :main-instance))]
|
(dissoc shape :main-instance))]
|
||||||
|
|
||||||
@@ -245,7 +246,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a top copy root.
|
;; Convert the shape in a top copy root.
|
||||||
(log/debug :hint " -> set :component-root")
|
(log/debug :hint " -> set :component-root")
|
||||||
(assoc shape :component-root true))]
|
(assoc shape :component-root true))]
|
||||||
|
|
||||||
@@ -258,7 +259,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a nested copy root.
|
;; Convert the shape in a nested copy root.
|
||||||
(log/debug :hint " -> unset :component-root")
|
(log/debug :hint " -> unset :component-root")
|
||||||
(dissoc shape :component-root))]
|
(dissoc shape :component-root))]
|
||||||
|
|
||||||
@@ -307,8 +308,8 @@
|
|||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
|
|
||||||
; If the shape still refers to the remote component, try to find the corresponding near one
|
;; If the shape still refers to the remote component, try to find the corresponding near one
|
||||||
; and link to it. If not, detach the shape.
|
;; and link to it. If not, detach the shape.
|
||||||
(log/dbg :hint "repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
(log/dbg :hint "repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||||
(if (some? matching-shape)
|
(if (some? matching-shape)
|
||||||
(-> (pcb/empty-changes nil page-id)
|
(-> (pcb/empty-changes nil page-id)
|
||||||
@@ -324,7 +325,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert shape in a normal copy, removing nested copy status
|
;; Convert shape in a normal copy, removing nested copy status
|
||||||
(log/debug :hint " -> unhead shape")
|
(log/debug :hint " -> unhead shape")
|
||||||
(ctk/unhead-shape shape))]
|
(ctk/unhead-shape shape))]
|
||||||
|
|
||||||
@@ -337,7 +338,7 @@
|
|||||||
[_ {:keys [shape page-id args] :as error} file-data _]
|
[_ {:keys [shape page-id args] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert shape in a nested head, adding component info
|
;; Convert shape in a nested head, adding component info
|
||||||
(log/debug :hint " -> reroot shape")
|
(log/debug :hint " -> reroot shape")
|
||||||
(ctk/rehead-shape shape (:component-file args) (:component-id args)))]
|
(ctk/rehead-shape shape (:component-file args) (:component-id args)))]
|
||||||
|
|
||||||
@@ -350,8 +351,9 @@
|
|||||||
[_ {:keys [shape args] :as error} file-data _]
|
[_ {:keys [shape args] :as error} file-data _]
|
||||||
(let [repair-component
|
(let [repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
(let [objects (:objects component) ;; we only have encounter this on deleted components,
|
(let [objects (:objects component)
|
||||||
;; so the relevant objects are inside the component
|
;; we only have encounter this on deleted components,
|
||||||
|
;; so the relevant objects are inside the component
|
||||||
to-detach (->> (:cycles-ids args)
|
to-detach (->> (:cycles-ids args)
|
||||||
(map #(get objects %))
|
(map #(get objects %))
|
||||||
(map #(ctn/get-head-shape objects %))
|
(map #(ctn/get-head-shape objects %))
|
||||||
@@ -378,7 +380,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Remove shape-ref
|
;; Remove shape-ref
|
||||||
(log/debug :hint " -> unset :shape-ref")
|
(log/debug :hint " -> unset :shape-ref")
|
||||||
(dissoc shape :shape-ref))]
|
(dissoc shape :shape-ref))]
|
||||||
|
|
||||||
@@ -391,7 +393,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a nested main head.
|
;; Convert the shape in a nested main head.
|
||||||
(log/debug :hint " -> unset :component-root")
|
(log/debug :hint " -> unset :component-root")
|
||||||
(dissoc shape :component-root))]
|
(dissoc shape :component-root))]
|
||||||
|
|
||||||
@@ -404,7 +406,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a top main head.
|
;; Convert the shape in a top main head.
|
||||||
(log/debug :hint " -> set :component-root")
|
(log/debug :hint " -> set :component-root")
|
||||||
(assoc shape :component-root true))]
|
(assoc shape :component-root true))]
|
||||||
|
|
||||||
@@ -418,7 +420,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a nested copy head.
|
;; Convert the shape in a nested copy head.
|
||||||
(log/debug :hint " -> unset :component-root")
|
(log/debug :hint " -> unset :component-root")
|
||||||
(dissoc shape :component-root))]
|
(dissoc shape :component-root))]
|
||||||
|
|
||||||
@@ -431,7 +433,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a top copy root.
|
;; Convert the shape in a top copy root.
|
||||||
(log/debug :hint " -> set :component-root")
|
(log/debug :hint " -> set :component-root")
|
||||||
(assoc shape :component-root true))]
|
(assoc shape :component-root true))]
|
||||||
|
|
||||||
@@ -444,7 +446,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Detach the shape and convert it to non instance.
|
;; Detach the shape and convert it to non instance.
|
||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
|
|
||||||
@@ -457,7 +459,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Detach the shape and convert it to non instance.
|
;; Detach the shape and convert it to non instance.
|
||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
|
|
||||||
@@ -470,7 +472,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; There is no solution that may recover it with confidence
|
;; There is no solution that may recover it with confidence
|
||||||
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
shape)]
|
shape)]
|
||||||
|
|
||||||
@@ -483,7 +485,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a frame.
|
;; Convert the shape in a frame.
|
||||||
(log/debug :hint " -> set :type :frame")
|
(log/debug :hint " -> set :type :frame")
|
||||||
(assoc shape :type :frame
|
(assoc shape :type :frame
|
||||||
:fills []
|
:fills []
|
||||||
@@ -502,7 +504,7 @@
|
|||||||
[_ {:keys [shape] :as error} file-data _]
|
[_ {:keys [shape] :as error} file-data _]
|
||||||
(let [repair-component
|
(let [repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
; Remove the objects key, or set it to {} if the component is deleted
|
;; Remove the objects key, or set it to {} if the component is deleted
|
||||||
(if (:deleted component)
|
(if (:deleted component)
|
||||||
(do
|
(do
|
||||||
(log/debug :hint " -> set :objects {}")
|
(log/debug :hint " -> set :objects {}")
|
||||||
|
|||||||
@@ -430,8 +430,8 @@
|
|||||||
(assoc :frame-id frame-id)
|
(assoc :frame-id frame-id)
|
||||||
(assoc :svg-viewbox vbox)
|
(assoc :svg-viewbox vbox)
|
||||||
(assoc :svg-attrs props)
|
(assoc :svg-attrs props)
|
||||||
;; We need to ensure fills are empty on import process
|
;; We need to ensure fills are empty on import process
|
||||||
;; because setup-shape assings one by default.
|
;; because setup-shape assings one by default.
|
||||||
(assoc :fills [])
|
(assoc :fills [])
|
||||||
(merge radius-attrs)))))
|
(merge radius-attrs)))))
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
#{:audit-log
|
#{:audit-log
|
||||||
:audit-log-archive
|
:audit-log-archive
|
||||||
:audit-log-gc
|
:audit-log-gc
|
||||||
|
:audit-log-logger
|
||||||
:auto-file-snapshot
|
:auto-file-snapshot
|
||||||
;; enables the `/api/doc` endpoint that lists all the rpc methods available.
|
;; enables the `/api/doc` endpoint that lists all the rpc methods available.
|
||||||
:backend-api-doc
|
:backend-api-doc
|
||||||
@@ -147,7 +148,10 @@
|
|||||||
|
|
||||||
;; A temporal flag, enables backend code use more extensivelly
|
;; A temporal flag, enables backend code use more extensivelly
|
||||||
;; redis for caching data
|
;; redis for caching data
|
||||||
:redis-cache})
|
:redis-cache
|
||||||
|
|
||||||
|
;; Activates the nitrate module
|
||||||
|
:nitrate})
|
||||||
|
|
||||||
(def all-flags
|
(def all-flags
|
||||||
(set/union email login varia))
|
(set/union email login varia))
|
||||||
|
|||||||
@@ -75,20 +75,25 @@
|
|||||||
|
|
||||||
#?(:cljs
|
#?(:cljs
|
||||||
(defn ->clj
|
(defn ->clj
|
||||||
[o & {:keys [key-fn val-fn] :or {key-fn read-kebab-key val-fn identity}}]
|
[o & {:keys [key-fn val-fn recursive] :or {key-fn read-kebab-key val-fn identity recursive true}}]
|
||||||
(let [f (fn this-fn [x]
|
(let [f (fn this-fn [x]
|
||||||
(let [x (val-fn x)]
|
(let [x (val-fn x)]
|
||||||
(cond
|
(cond
|
||||||
(array? x)
|
(array? x)
|
||||||
(persistent!
|
(persistent!
|
||||||
(.reduce ^js/Array x
|
(.reduce ^js/Array x
|
||||||
#(conj! %1 (this-fn %2))
|
#(conj! %1 (if recursive
|
||||||
|
(this-fn %2)
|
||||||
|
%2))
|
||||||
(transient [])))
|
(transient [])))
|
||||||
|
|
||||||
(identical? (type x) js/Object)
|
(identical? (type x) js/Object)
|
||||||
(persistent!
|
(persistent!
|
||||||
(.reduce ^js/Array (js-keys x)
|
(.reduce ^js/Array (js-keys x)
|
||||||
#(assoc! %1 (key-fn %2) (this-fn (unchecked-get x %2)))
|
#(assoc! %1 (key-fn %2)
|
||||||
|
(if recursive
|
||||||
|
(this-fn (unchecked-get x %2))
|
||||||
|
(unchecked-get x %2)))
|
||||||
(transient {})))
|
(transient {})))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
|
|||||||
@@ -49,9 +49,9 @@
|
|||||||
(def log-container-ids #{})
|
(def log-container-ids #{})
|
||||||
|
|
||||||
(def updatable-attrs (->> (seq (keys ctk/sync-attrs))
|
(def updatable-attrs (->> (seq (keys ctk/sync-attrs))
|
||||||
;; We don't update the flex-child attrs
|
;; We don't update the flex-child attrs
|
||||||
(remove ctk/swap-keep-attrs)
|
(remove ctk/swap-keep-attrs)
|
||||||
;; We don't do automatic update of the `layout-grid-cells` property.
|
;; We don't do automatic update of the `layout-grid-cells` property.
|
||||||
(remove #(= :layout-grid-cells %))))
|
(remove #(= :layout-grid-cells %))))
|
||||||
|
|
||||||
(defn enabled-shape?
|
(defn enabled-shape?
|
||||||
@@ -1898,10 +1898,10 @@
|
|||||||
(gsh/absolute-move shape new-pos)))
|
(gsh/absolute-move shape new-pos)))
|
||||||
|
|
||||||
(defn- switch-path-change-value
|
(defn- switch-path-change-value
|
||||||
[prev-shape ;; The shape before the switch
|
[prev-shape ; The shape before the switch
|
||||||
current-shape ;; The shape after the switch (a clean copy)
|
current-shape ; The shape after the switch (a clean copy)
|
||||||
ref-shape ;; The referenced shape on the main component
|
ref-shape ; The referenced shape on the main component
|
||||||
;; before the switch
|
; before the switch
|
||||||
attr]
|
attr]
|
||||||
(let [old-width (-> ref-shape :selrect :width)
|
(let [old-width (-> ref-shape :selrect :width)
|
||||||
new-width (-> prev-shape :selrect :width)
|
new-width (-> prev-shape :selrect :width)
|
||||||
@@ -1918,10 +1918,9 @@
|
|||||||
|
|
||||||
|
|
||||||
(defn- switch-text-change-value
|
(defn- switch-text-change-value
|
||||||
[prev-content ;; The :content of the text before the switch
|
[prev-content ; The :content of the text before the switch
|
||||||
current-content ;; The :content of the text after the switch (a clean copy)
|
current-content ; The :content of the text after the switch (a clean copy)
|
||||||
ref-content touched] ;; The :content of the referenced text on the main component
|
ref-content touched] ; The :content of the referenced text on the main component before the switch
|
||||||
;; before the switch
|
|
||||||
(let [;; We need the differences between the contents on the main
|
(let [;; We need the differences between the contents on the main
|
||||||
;; components. current-content is the content of a clean copy,
|
;; components. current-content is the content of a clean copy,
|
||||||
;; so for all effects its the same as the content on its main
|
;; so for all effects its the same as the content on its main
|
||||||
@@ -2497,7 +2496,7 @@
|
|||||||
(-> changes
|
(-> changes
|
||||||
(cls/generate-delete-shapes
|
(cls/generate-delete-shapes
|
||||||
file page objects (d/ordered-set (:id shape))
|
file page objects (d/ordered-set (:id shape))
|
||||||
{:allow-altering-copies true :ignore-children-fn ignore-swapped-fn :ignore-mask true}))
|
{:allow-altering-copies true :ignore-children-fn ignore-swapped-fn :ignore-mask true :ignore-flows-for #{(:id shape)}}))
|
||||||
[new-shape changes]
|
[new-shape changes]
|
||||||
(-> changes
|
(-> changes
|
||||||
(generate-new-shape-for-swap shape file page libraries id-new-component index target-cell keep-props-values))]
|
(generate-new-shape-for-swap shape file page libraries id-new-component index target-cell keep-props-values))]
|
||||||
@@ -2845,8 +2844,8 @@
|
|||||||
duplicating-component?
|
duplicating-component?
|
||||||
true
|
true
|
||||||
(and remove-swap-slot?
|
(and remove-swap-slot?
|
||||||
;; only remove swap slot of children when the current shape
|
;; only remove swap slot of children when the current shape
|
||||||
;; is not a subinstance head nor a instance root
|
;; is not a subinstance head nor a instance root
|
||||||
(not subinstance-head?)
|
(not subinstance-head?)
|
||||||
(not instance-root?))
|
(not instance-root?))
|
||||||
variant-props))
|
variant-props))
|
||||||
@@ -2902,7 +2901,7 @@
|
|||||||
variant-props)
|
variant-props)
|
||||||
changes))
|
changes))
|
||||||
|
|
||||||
;; We need to check the changes to get the ids-map
|
;; We need to check the changes to get the ids-map
|
||||||
ids-map
|
ids-map
|
||||||
(into {}
|
(into {}
|
||||||
(comp
|
(comp
|
||||||
|
|||||||
@@ -124,9 +124,11 @@
|
|||||||
;; on the deletion process. It should receive a shape and
|
;; on the deletion process. It should receive a shape and
|
||||||
;; return a boolean
|
;; return a boolean
|
||||||
ignore-children-fn
|
ignore-children-fn
|
||||||
ignore-mask]
|
ignore-mask
|
||||||
|
ignore-flows-for]
|
||||||
:or {ignore-children-fn (constantly false)
|
:or {ignore-children-fn (constantly false)
|
||||||
ignore-mask false}}]
|
ignore-mask false
|
||||||
|
ignore-flows-for #{}}}]
|
||||||
(let [objects (pcb/get-objects changes)
|
(let [objects (pcb/get-objects changes)
|
||||||
data (pcb/get-library-data changes)
|
data (pcb/get-library-data changes)
|
||||||
page-id (pcb/get-page-id changes)
|
page-id (pcb/get-page-id changes)
|
||||||
@@ -136,12 +138,12 @@
|
|||||||
ids (cfh/clean-loops objects ids)
|
ids (cfh/clean-loops objects ids)
|
||||||
in-component-copy?
|
in-component-copy?
|
||||||
(fn [shape-id]
|
(fn [shape-id]
|
||||||
;; Look for shapes that are inside a component copy, but are
|
;; Look for shapes that are inside a component copy, but are
|
||||||
;; not the root. In this case, they must not be deleted,
|
;; not the root. In this case, they must not be deleted,
|
||||||
;; but hidden (to be able to recover them more easily).
|
;; but hidden (to be able to recover them more easily).
|
||||||
;; If we want to specifically allow altering the copies, this is
|
;; If we want to specifically allow altering the copies, this is
|
||||||
;; a special case, like a component swap, in which case we want
|
;; a special case, like a component swap, in which case we want
|
||||||
;; to delete the old shape
|
;; to delete the old shape
|
||||||
(let [shape (get objects shape-id)]
|
(let [shape (get objects shape-id)]
|
||||||
(and (ctn/has-any-copy-parent? objects shape)
|
(and (ctn/has-any-copy-parent? objects shape)
|
||||||
(not allow-altering-copies))))
|
(not allow-altering-copies))))
|
||||||
@@ -166,9 +168,9 @@
|
|||||||
groups-to-unmask
|
groups-to-unmask
|
||||||
(when-not ignore-mask
|
(when-not ignore-mask
|
||||||
(reduce (fn [group-ids id]
|
(reduce (fn [group-ids id]
|
||||||
;; When the shape to delete is the mask of a masked group,
|
;; When the shape to delete is the mask of a masked group,
|
||||||
;; the mask condition must be removed, and it must be
|
;; the mask condition must be removed, and it must be
|
||||||
;; converted to a normal group.
|
;; converted to a normal group.
|
||||||
(let [obj (lookup id)
|
(let [obj (lookup id)
|
||||||
parent (lookup (:parent-id obj))]
|
parent (lookup (:parent-id obj))]
|
||||||
(if (and (:masked-group parent)
|
(if (and (:masked-group parent)
|
||||||
@@ -181,8 +183,8 @@
|
|||||||
|
|
||||||
interacting-shapes
|
interacting-shapes
|
||||||
(filter (fn [shape]
|
(filter (fn [shape]
|
||||||
;; If any of the deleted shapes is the destination of
|
;; If any of the deleted shapes is the destination of
|
||||||
;; some interaction, this must be deleted, too.
|
;; some interaction, this must be deleted, too.
|
||||||
(let [interactions (:interactions shape)]
|
(let [interactions (:interactions shape)]
|
||||||
(some #(and (ctsi/has-destination %)
|
(some #(and (ctsi/has-destination %)
|
||||||
(contains? ids-to-delete (:destination %)))
|
(contains? ids-to-delete (:destination %)))
|
||||||
@@ -194,7 +196,8 @@
|
|||||||
(->> (:flows page)
|
(->> (:flows page)
|
||||||
(reduce
|
(reduce
|
||||||
(fn [changes [id flow]]
|
(fn [changes [id flow]]
|
||||||
(if (id-to-delete? (:starting-frame flow))
|
(if (and (id-to-delete? (:starting-frame flow))
|
||||||
|
(not (contains? ignore-flows-for (:starting-frame flow))))
|
||||||
(-> changes
|
(-> changes
|
||||||
(pcb/with-page page)
|
(pcb/with-page page)
|
||||||
(pcb/set-flow id nil))
|
(pcb/set-flow id nil))
|
||||||
@@ -204,7 +207,7 @@
|
|||||||
|
|
||||||
all-parents
|
all-parents
|
||||||
(reduce (fn [res id]
|
(reduce (fn [res id]
|
||||||
;; All parents of any deleted shape must be resized.
|
;; All parents of any deleted shape must be resized.
|
||||||
(into res (cfh/get-parent-ids objects id)))
|
(into res (cfh/get-parent-ids objects id)))
|
||||||
(d/ordered-set)
|
(d/ordered-set)
|
||||||
(concat ids-to-delete ids-to-hide))
|
(concat ids-to-delete ids-to-hide))
|
||||||
@@ -236,10 +239,10 @@
|
|||||||
(recursive-find-empty-parents parents))))
|
(recursive-find-empty-parents parents))))
|
||||||
|
|
||||||
empty-parents
|
empty-parents
|
||||||
;; Any parent whose children are all deleted, must be deleted too.
|
;; Any parent whose children are all deleted, must be deleted too.
|
||||||
;; If we want to specifically allow altering the copies, this is a special case,
|
;; If we want to specifically allow altering the copies, this is a special case,
|
||||||
;; for example during a component swap. in this case we are replacing a shape by
|
;; for example during a component swap. in this case we are replacing a shape by
|
||||||
;; other one, so must not delete empty parents.
|
;; other one, so must not delete empty parents.
|
||||||
(if-not allow-altering-copies
|
(if-not allow-altering-copies
|
||||||
(into (d/ordered-set) (find-all-empty-parents #{}))
|
(into (d/ordered-set) (find-all-empty-parents #{}))
|
||||||
#{})
|
#{})
|
||||||
@@ -271,8 +274,8 @@
|
|||||||
guides-to-delete)
|
guides-to-delete)
|
||||||
|
|
||||||
changes (reduce (fn [changes component-id]
|
changes (reduce (fn [changes component-id]
|
||||||
;; It's important to delete the component before the main instance, because we
|
;; It's important to delete the component before the main instance, because we
|
||||||
;; need to store the instance position if we want to restore it later.
|
;; need to store the instance position if we want to restore it later.
|
||||||
(pcb/delete-component changes component-id (:id page)))
|
(pcb/delete-component changes component-id (:id page)))
|
||||||
changes
|
changes
|
||||||
components-to-delete)
|
components-to-delete)
|
||||||
@@ -324,7 +327,7 @@
|
|||||||
result #{}]
|
result #{}]
|
||||||
|
|
||||||
(if-not current-id
|
(if-not current-id
|
||||||
;; Base case, no next element
|
;; Base case, no next element
|
||||||
result
|
result
|
||||||
|
|
||||||
(let [group (get objects current-id)]
|
(let [group (get objects current-id)]
|
||||||
@@ -332,14 +335,14 @@
|
|||||||
(not= current-id parent-id)
|
(not= current-id parent-id)
|
||||||
(empty? (remove removed-id? (:shapes group))))
|
(empty? (remove removed-id? (:shapes group))))
|
||||||
|
|
||||||
;; Adds group to the remove and check its parent
|
;; Adds group to the remove and check its parent
|
||||||
(let [to-check (concat to-check [(cfh/get-parent-id objects current-id)])]
|
(let [to-check (concat to-check [(cfh/get-parent-id objects current-id)])]
|
||||||
(recur (first to-check)
|
(recur (first to-check)
|
||||||
(rest to-check)
|
(rest to-check)
|
||||||
(conj removed-id? current-id)
|
(conj removed-id? current-id)
|
||||||
(conj result current-id)))
|
(conj result current-id)))
|
||||||
|
|
||||||
;; otherwise recur
|
;; otherwise recur
|
||||||
(recur (first to-check)
|
(recur (first to-check)
|
||||||
(rest to-check)
|
(rest to-check)
|
||||||
removed-id?
|
removed-id?
|
||||||
|
|||||||
@@ -111,7 +111,7 @@
|
|||||||
"Check if any ancestor of a shape (between base-parent-id and shape) was swapped"
|
"Check if any ancestor of a shape (between base-parent-id and shape) was swapped"
|
||||||
[shape objects base-parent-id]
|
[shape objects base-parent-id]
|
||||||
(let [ancestors (->> (ctn/get-parent-heads objects shape)
|
(let [ancestors (->> (ctn/get-parent-heads objects shape)
|
||||||
;; Ignore ancestors ahead of base-parent
|
;; Ignore ancestors ahead of base-parent
|
||||||
(drop-while #(not= base-parent-id (:id %)))
|
(drop-while #(not= base-parent-id (:id %)))
|
||||||
seq)
|
seq)
|
||||||
num-ancestors (count ancestors)
|
num-ancestors (count ancestors)
|
||||||
|
|||||||
@@ -132,3 +132,94 @@ Some naming conventions:
|
|||||||
(if-let [last-period (str/last-index-of s ".")]
|
(if-let [last-period (str/last-index-of s ".")]
|
||||||
[(subs s 0 (inc last-period)) (subs s (inc last-period))]
|
[(subs s 0 (inc last-period)) (subs s (inc last-period))]
|
||||||
[s ""]))
|
[s ""]))
|
||||||
|
|
||||||
|
;; Tree building functions --------------------------------------------------
|
||||||
|
|
||||||
|
"Build tree structure from flat list of paths"
|
||||||
|
|
||||||
|
"`build-tree-root` is the main function to build the tree."
|
||||||
|
|
||||||
|
"Receives a list of segments with 'name' properties representing paths,
|
||||||
|
and a separator string."
|
||||||
|
"E.g segments = [{... :name 'one/two/three'} {... :name 'one/two/four'} {... :name 'one/five'}]"
|
||||||
|
|
||||||
|
"Transforms into a tree structure like:
|
||||||
|
[{:name 'one'
|
||||||
|
:path 'one'
|
||||||
|
:depth 0
|
||||||
|
:leaf nil
|
||||||
|
:children-fn (fn [] [{:name 'two'
|
||||||
|
:path 'one.two'
|
||||||
|
:depth 1
|
||||||
|
:leaf nil
|
||||||
|
:children-fn (fn [] [{... :name 'three'} {... :name 'four'}])}
|
||||||
|
{:name 'five'
|
||||||
|
:path 'one.five'
|
||||||
|
:depth 1
|
||||||
|
:leaf {... :name 'five'}
|
||||||
|
...}])}]"
|
||||||
|
|
||||||
|
(defn- sort-by-children
|
||||||
|
"Sorts segments so that those with children come first."
|
||||||
|
[segments separator]
|
||||||
|
(sort-by (fn [segment]
|
||||||
|
(let [path (split-path (:name segment) :separator separator)
|
||||||
|
path-length (count path)]
|
||||||
|
(if (= path-length 1)
|
||||||
|
1
|
||||||
|
0)))
|
||||||
|
segments))
|
||||||
|
|
||||||
|
(defn- group-by-first-segment
|
||||||
|
"Groups segments by their first path segment and update segment name."
|
||||||
|
[segments separator]
|
||||||
|
(reduce (fn [acc segment]
|
||||||
|
(let [[first-segment & remaining-segments] (split-path (:name segment) :separator separator)
|
||||||
|
rest-path (when (seq remaining-segments) (join-path remaining-segments :separator separator :with-spaces? false))]
|
||||||
|
(update acc first-segment (fnil conj [])
|
||||||
|
(if rest-path
|
||||||
|
(assoc segment :name rest-path)
|
||||||
|
segment))))
|
||||||
|
{}
|
||||||
|
segments))
|
||||||
|
|
||||||
|
(defn- sort-and-group-segments
|
||||||
|
"Sorts elements and groups them by their first path segment."
|
||||||
|
[segments separator]
|
||||||
|
(let [sorted (sort-by-children segments separator)
|
||||||
|
grouped (group-by-first-segment sorted separator)]
|
||||||
|
grouped))
|
||||||
|
|
||||||
|
(defn- build-tree-node
|
||||||
|
"Builds a single tree node with lazy children."
|
||||||
|
[segment-name remaining-segments separator parent-path depth]
|
||||||
|
(let [current-path (if parent-path
|
||||||
|
(str parent-path "." segment-name)
|
||||||
|
segment-name)
|
||||||
|
|
||||||
|
is-leaf? (and (seq remaining-segments)
|
||||||
|
(every? (fn [segment]
|
||||||
|
(let [remaining-segment-name (first (split-path (:name segment) :separator separator))]
|
||||||
|
(= segment-name remaining-segment-name)))
|
||||||
|
remaining-segments))
|
||||||
|
|
||||||
|
leaf-segment (when is-leaf? (first remaining-segments))
|
||||||
|
node {:name segment-name
|
||||||
|
:path current-path
|
||||||
|
:depth depth
|
||||||
|
:leaf leaf-segment
|
||||||
|
:children-fn (when-not is-leaf?
|
||||||
|
(fn []
|
||||||
|
(let [grouped-elements (sort-and-group-segments remaining-segments separator)]
|
||||||
|
(mapv (fn [[child-segment-name remaining-child-segments]]
|
||||||
|
(build-tree-node child-segment-name remaining-child-segments separator current-path (inc depth)))
|
||||||
|
grouped-elements))))}]
|
||||||
|
node))
|
||||||
|
|
||||||
|
(defn build-tree-root
|
||||||
|
"Builds the root level of the tree."
|
||||||
|
[segments separator]
|
||||||
|
(let [grouped-elements (sort-and-group-segments segments separator)]
|
||||||
|
(mapv (fn [[segment-name remaining-segments]]
|
||||||
|
(build-tree-node segment-name remaining-segments separator nil 0))
|
||||||
|
grouped-elements)))
|
||||||
|
|||||||
@@ -222,7 +222,7 @@
|
|||||||
:else
|
:else
|
||||||
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
||||||
|
|
||||||
;; We add an end-of-line when finish a paragraph
|
;; We add an end-of-line when finish a paragraph
|
||||||
new-acc
|
new-acc
|
||||||
(if (= (:type node) "paragraph")
|
(if (= (:type node) "paragraph")
|
||||||
(let [[hs ht] (first new-acc)]
|
(let [[hs ht] (first new-acc)]
|
||||||
|
|||||||
@@ -64,8 +64,33 @@
|
|||||||
java.time.temporal.TemporalAmount
|
java.time.temporal.TemporalAmount
|
||||||
java.time.temporal.TemporalUnit)))
|
java.time.temporal.TemporalUnit)))
|
||||||
|
|
||||||
|
(declare inst)
|
||||||
|
|
||||||
#?(:clj (def ^:dynamic *clock* (Clock/systemDefaultZone)))
|
#?(:clj (def ^:dynamic *clock* (Clock/systemDefaultZone)))
|
||||||
|
|
||||||
|
#?(:clj
|
||||||
|
(defn clock?
|
||||||
|
[o]
|
||||||
|
(instance? Clock o)))
|
||||||
|
|
||||||
|
#?(:clj
|
||||||
|
(defn get-system-clock
|
||||||
|
[]
|
||||||
|
(Clock/systemDefaultZone)))
|
||||||
|
|
||||||
|
#?(:clj
|
||||||
|
(defn offset-clock
|
||||||
|
[offset]
|
||||||
|
(Clock/offset ^Clock (Clock/systemDefaultZone) ^Duration offset)))
|
||||||
|
|
||||||
|
#?(:clj
|
||||||
|
(defn fixed-clock
|
||||||
|
[instant]
|
||||||
|
(Clock/fixed ^Instant (inst instant)
|
||||||
|
^ZoneId (ZoneId/of "Z"))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn now
|
(defn now
|
||||||
[]
|
[]
|
||||||
#?(:clj (Instant/now *clock*)
|
#?(:clj (Instant/now *clock*)
|
||||||
|
|||||||
@@ -140,7 +140,8 @@
|
|||||||
:layout-item-min-w
|
:layout-item-min-w
|
||||||
:layout-item-absolute
|
:layout-item-absolute
|
||||||
:layout-item-z-index
|
:layout-item-z-index
|
||||||
:layout-item-align-self})
|
:layout-item-align-self
|
||||||
|
:interactions})
|
||||||
|
|
||||||
(defn component-attr?
|
(defn component-attr?
|
||||||
"Check if some attribute is one that is involved in component syncrhonization.
|
"Check if some attribute is one that is involved in component syncrhonization.
|
||||||
|
|||||||
@@ -458,13 +458,13 @@
|
|||||||
(map #(cfh/components-nesting-loop? objects (:id %) (:id parent)))
|
(map #(cfh/components-nesting-loop? objects (:id %) (:id parent)))
|
||||||
(every? nil?)))]
|
(every? nil?)))]
|
||||||
(or
|
(or
|
||||||
;;We don't want to change the structure of component copies
|
;;We don't want to change the structure of component copies
|
||||||
(ctk/in-component-copy? parent)
|
(ctk/in-component-copy? parent)
|
||||||
(has-any-copy-parent? objects parent)
|
(has-any-copy-parent? objects parent)
|
||||||
;; If we are moving something containing a main instance the container can't be part of a component (neither main nor copy)
|
;; If we are moving something containing a main instance the container can't be part of a component (neither main nor copy)
|
||||||
(and selected-main-instance? parent-in-component?)
|
(and selected-main-instance? parent-in-component?)
|
||||||
;; Avoid placing a shape as a direct or indirect child of itself,
|
;; Avoid placing a shape as a direct or indirect child of itself,
|
||||||
;; or inside its main component if it's in a copy.
|
;; or inside its main component if it's in a copy.
|
||||||
comps-nesting-loop?)))
|
comps-nesting-loop?)))
|
||||||
|
|
||||||
(defn find-valid-parent-and-frame-ids
|
(defn find-valid-parent-and-frame-ids
|
||||||
|
|||||||
@@ -775,9 +775,9 @@
|
|||||||
file-data (cond-> file-data
|
file-data (cond-> file-data
|
||||||
(d/not-empty? used-components)
|
(d/not-empty? used-components)
|
||||||
(absorb-components used-components library-data))
|
(absorb-components used-components library-data))
|
||||||
;; Note that absorbed components may also be using colors
|
;; Note that absorbed components may also be using colors
|
||||||
;; and typographies. This is the reason of doing this first
|
;; and typographies. This is the reason of doing this first
|
||||||
;; and accumulating file data for the next ones.
|
;; and accumulating file data for the next ones.
|
||||||
|
|
||||||
used-colors (find-asset-type-usages file-data library-data :color)
|
used-colors (find-asset-type-usages file-data library-data :color)
|
||||||
file-data (cond-> file-data
|
file-data (cond-> file-data
|
||||||
@@ -1017,7 +1017,7 @@
|
|||||||
libs-to-show
|
libs-to-show
|
||||||
(-> libs-to-show
|
(-> libs-to-show
|
||||||
(add-component library-id component-id))))))
|
(add-component library-id component-id))))))
|
||||||
;; (find-used-components-cumulative page root)
|
;; (find-used-components-cumulative page root)
|
||||||
|
|
||||||
libs-to-show
|
libs-to-show
|
||||||
components))
|
components))
|
||||||
|
|||||||
@@ -306,7 +306,7 @@
|
|||||||
|
|
||||||
(-write-to [_ heap offset]
|
(-write-to [_ heap offset]
|
||||||
(let [buffer' (.-buffer ^js/DataView dbuffer)
|
(let [buffer' (.-buffer ^js/DataView dbuffer)
|
||||||
;; Calculate byte size: 4 bytes header + (size * FILL-U8-SIZE)
|
;; Calculate byte size: 4 bytes header + (size * FILL-U8-SIZE)
|
||||||
byte-size (+ 4 (* size FILL-U8-SIZE))
|
byte-size (+ 4 (* size FILL-U8-SIZE))
|
||||||
;; Create Uint32Array with exact size needed (convert bytes to u32 elements)
|
;; Create Uint32Array with exact size needed (convert bytes to u32 elements)
|
||||||
u32-array (js/Uint32Array. buffer' 0 (/ byte-size 4))]
|
u32-array (js/Uint32Array. buffer' 0 (/ byte-size 4))]
|
||||||
|
|||||||
@@ -13,14 +13,15 @@
|
|||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.schema.generators :as sg]))
|
[app.common.schema.generators :as sg]))
|
||||||
|
|
||||||
;; WARNING: options are not deleted when changing event or action type, so it can be
|
;; WARNING: options are not deleted when changing event or action
|
||||||
;; restored if the user changes it back later.
|
;; type, so it can be restored if the user changes it back later.
|
||||||
;;
|
;;
|
||||||
;; But that means that an interaction may have for example a delay or
|
;; But that means that an interaction may have for example a delay or
|
||||||
;; destination, even if its type does not require it (but a previous type did).
|
;; destination, even if its type does not require it (but a previous
|
||||||
|
;; type did).
|
||||||
;;
|
;;
|
||||||
;; So make sure to use has-delay/has-destination... functions, or similar,
|
;; So make sure to use has-delay/has-destination... functions, or
|
||||||
;; before reading them.
|
;; similar, before reading them.
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; SCHEMA
|
;; SCHEMA
|
||||||
@@ -452,16 +453,15 @@
|
|||||||
(gpt/point 0 0)))
|
(gpt/point 0 0)))
|
||||||
|
|
||||||
(defn calc-overlay-position
|
(defn calc-overlay-position
|
||||||
[interaction ;; interaction data
|
[interaction ; interaction data
|
||||||
shape ;; Shape with the interaction
|
shape ; Shape with the interaction
|
||||||
objects ;; the objects tree
|
objects ; the objects tree
|
||||||
relative-to-shape ;; the interaction position is realtive to this
|
relative-to-shape ; the interaction position is realtive to this shape
|
||||||
;; sape
|
base-frame ; the base frame of the current interaction
|
||||||
base-frame ;; the base frame of the current interaction
|
dest-frame ; the frame to display with this interaction
|
||||||
dest-frame ;; the frame to display with this interaction
|
frame-offset] ; if this interaction starts in a frame opened
|
||||||
frame-offset] ;; if this interaction starts in a frame opened
|
; on another interaction, this is the position
|
||||||
;; on another interaction, this is the position
|
; of that frame
|
||||||
;; of that frame
|
|
||||||
(assert (check-interaction interaction))
|
(assert (check-interaction interaction))
|
||||||
(assert (has-overlay-opts interaction)
|
(assert (has-overlay-opts interaction)
|
||||||
"expected compatible interaction map")
|
"expected compatible interaction map")
|
||||||
|
|||||||
@@ -382,9 +382,9 @@
|
|||||||
keep-ids? (:id shape)
|
keep-ids? (:id shape)
|
||||||
:else (uuid/next))
|
:else (uuid/next))
|
||||||
|
|
||||||
;; Assign the correct frame-id for the given parent. It's the parent-id (if parent is frame)
|
;; Assign the correct frame-id for the given parent. It's the parent-id (if parent is frame)
|
||||||
;; or the parent's frame-id otherwise. Only for the first cloned shapes. In recursive calls
|
;; or the parent's frame-id otherwise. Only for the first cloned shapes. In recursive calls
|
||||||
;; this is not needed.
|
;; this is not needed.
|
||||||
frame-id (cond
|
frame-id (cond
|
||||||
(and (nil? frame-id) (cfh/frame-shape? dest-objects parent-id))
|
(and (nil? frame-id) (cfh/frame-shape? dest-objects parent-id))
|
||||||
parent-id
|
parent-id
|
||||||
|
|||||||
@@ -393,7 +393,7 @@
|
|||||||
:else
|
:else
|
||||||
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
||||||
|
|
||||||
;; We add an end-of-line when finish a paragraph
|
;; We add an end-of-line when finish a paragraph
|
||||||
new-acc
|
new-acc
|
||||||
(if (= (:type node) "paragraph")
|
(if (= (:type node) "paragraph")
|
||||||
(let [[hs ht] (first new-acc)]
|
(let [[hs ht] (first new-acc)]
|
||||||
|
|||||||
@@ -47,6 +47,18 @@
|
|||||||
self-reference? (get token-references token-name)]
|
self-reference? (get token-references token-name)]
|
||||||
self-reference?))
|
self-reference?))
|
||||||
|
|
||||||
|
(defn references-token?
|
||||||
|
"Recursively check if a value references the token name. Handles strings, maps, and sequences."
|
||||||
|
[value token-name]
|
||||||
|
(cond
|
||||||
|
(string? value)
|
||||||
|
(boolean (some #(= % token-name) (find-token-value-references value)))
|
||||||
|
(map? value)
|
||||||
|
(some true? (map #(references-token? % token-name) (vals value)))
|
||||||
|
(sequential? value)
|
||||||
|
(some true? (map #(references-token? % token-name) value))
|
||||||
|
:else false))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; SCHEMA
|
;; SCHEMA
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@@ -99,7 +111,7 @@
|
|||||||
|
|
||||||
(def token-name-ref
|
(def token-name-ref
|
||||||
[:re {:title "TokenNameRef" :gen/gen sg/text}
|
[:re {:title "TokenNameRef" :gen/gen sg/text}
|
||||||
#"^(?!\$)([a-zA-Z0-9-$_]+\.?)*(?<!\.)$"])
|
#"^[a-zA-Z0-9_-][a-zA-Z0-9$_-]*(\.[a-zA-Z0-9$_-]+)*$"])
|
||||||
|
|
||||||
(def ^:private schema:color
|
(def ^:private schema:color
|
||||||
[:map
|
[:map
|
||||||
@@ -462,8 +474,8 @@
|
|||||||
:height #{:sizing :dimensions}
|
:height #{:sizing :dimensions}
|
||||||
:max-width #{:sizing :dimensions}
|
:max-width #{:sizing :dimensions}
|
||||||
:max-height #{:sizing :dimensions}
|
:max-height #{:sizing :dimensions}
|
||||||
:x #{:spacing :dimensions}
|
:x #{:dimensions}
|
||||||
:y #{:spacing :dimensions}
|
:y #{:dimensions}
|
||||||
:rotation #{:number :rotation}
|
:rotation #{:number :rotation}
|
||||||
:border-radius #{:border-radius :dimensions}
|
:border-radius #{:border-radius :dimensions}
|
||||||
:row-gap #{:spacing :dimensions}
|
:row-gap #{:spacing :dimensions}
|
||||||
@@ -475,6 +487,8 @@
|
|||||||
:vertical-margin #{:spacing :dimensions}
|
:vertical-margin #{:spacing :dimensions}
|
||||||
:sided-margins #{:spacing :dimensions}
|
:sided-margins #{:spacing :dimensions}
|
||||||
:line-height #{:line-height :number}
|
:line-height #{:line-height :number}
|
||||||
|
:opacity #{:opacity}
|
||||||
|
:stroke-width #{:stroke-width :dimensions}
|
||||||
:font-size #{:font-size}
|
:font-size #{:font-size}
|
||||||
:letter-spacing #{:letter-spacing}
|
:letter-spacing #{:letter-spacing}
|
||||||
:fill #{:color}
|
:fill #{:color}
|
||||||
@@ -558,3 +572,18 @@
|
|||||||
"Predicate if a shadow composite token is a reference value - a string pointing to another reference token."
|
"Predicate if a shadow composite token is a reference value - a string pointing to another reference token."
|
||||||
[token-value]
|
[token-value]
|
||||||
(string? token-value))
|
(string? token-value))
|
||||||
|
|
||||||
|
(defn update-token-value-references
|
||||||
|
"Recursively update token references within a token value, supporting complex token values (maps, sequences, strings)."
|
||||||
|
[value old-name new-name]
|
||||||
|
(cond
|
||||||
|
(string? value)
|
||||||
|
(str/replace value
|
||||||
|
(re-pattern (str "\\{" (str/replace old-name "." "\\.") "\\}"))
|
||||||
|
(str "{" new-name "}"))
|
||||||
|
(map? value)
|
||||||
|
(d/update-vals value #(update-token-value-references % old-name new-name))
|
||||||
|
(sequential? value)
|
||||||
|
(mapv #(update-token-value-references % old-name new-name) value)
|
||||||
|
:else
|
||||||
|
value))
|
||||||
|
|||||||
@@ -909,7 +909,8 @@ Will return a value that matches this schema:
|
|||||||
`:all` All of the nested sets are active
|
`:all` All of the nested sets are active
|
||||||
`:partial` Mixed active state of nested sets")
|
`:partial` Mixed active state of nested sets")
|
||||||
(get-tokens-in-active-sets [_] "set of set names that are active in the the active themes")
|
(get-tokens-in-active-sets [_] "set of set names that are active in the the active themes")
|
||||||
(get-all-tokens [_] "all tokens in the lib")
|
(get-all-tokens [_] "all tokens in the lib, as a sequence")
|
||||||
|
(get-all-tokens-map [_] "all tokens in the lib, as a map name -> token")
|
||||||
(get-tokens [_ set-id] "return a map of tokens in the set, indexed by token-name"))
|
(get-tokens [_ set-id] "return a map of tokens in the set, indexed by token-name"))
|
||||||
|
|
||||||
(declare parse-multi-set-dtcg-json)
|
(declare parse-multi-set-dtcg-json)
|
||||||
@@ -1306,6 +1307,10 @@ Will return a value that matches this schema:
|
|||||||
tokens))
|
tokens))
|
||||||
|
|
||||||
(get-all-tokens [this]
|
(get-all-tokens [this]
|
||||||
|
(mapcat #(vals (get-tokens- %))
|
||||||
|
(get-sets this)))
|
||||||
|
|
||||||
|
(get-all-tokens-map [this]
|
||||||
(reduce
|
(reduce
|
||||||
(fn [tokens' set]
|
(fn [tokens' set]
|
||||||
(into tokens' (map (fn [x] [(:name x) x]) (vals (get-tokens- set)))))
|
(into tokens' (map (fn [x] [(:name x) x]) (vals (get-tokens- set)))))
|
||||||
|
|||||||
@@ -693,7 +693,7 @@
|
|||||||
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
||||||
#{(:id main-child)}
|
#{(:id main-child)}
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
;; Update the attrs on all the content tree
|
;; Update the attrs on all the content tree
|
||||||
(-> shape
|
(-> shape
|
||||||
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
|
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
|
||||||
(assoc-in [:content :children 0 :children 0 :font-size] "32")
|
(assoc-in [:content :children 0 :children 0 :font-size] "32")
|
||||||
@@ -851,7 +851,7 @@
|
|||||||
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
||||||
#{(:id main-child)}
|
#{(:id main-child)}
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
;; Update the attrs on all the content tree
|
;; Update the attrs on all the content tree
|
||||||
(-> shape
|
(-> shape
|
||||||
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
|
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
|
||||||
(assoc-in [:content :children 0 :children 0 :font-size] "32")
|
(assoc-in [:content :children 0 :children 0 :font-size] "32")
|
||||||
|
|||||||
@@ -267,7 +267,7 @@
|
|||||||
page' (thf/current-page file')
|
page' (thf/current-page file')
|
||||||
objects' (:objects page')]
|
objects' (:objects page')]
|
||||||
|
|
||||||
;; ==== Check
|
;; ==== Check
|
||||||
(thf/validate-file! file')
|
(thf/validate-file! file')
|
||||||
(t/is (= (count (:components data)) 2))
|
(t/is (= (count (:components data)) 2))
|
||||||
(t/is (= (count (:components data')) 4))
|
(t/is (= (count (:components data')) 4))
|
||||||
|
|||||||
1291
common/yarn.lock
1291
common/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -179,9 +179,9 @@ RUN set -eux; \
|
|||||||
|
|
||||||
FROM base AS setup-utils
|
FROM base AS setup-utils
|
||||||
|
|
||||||
ENV CLJKONDO_VERSION=2025.07.28 \
|
ENV CLJKONDO_VERSION=2026.01.19 \
|
||||||
BABASHKA_VERSION=1.12.208 \
|
BABASHKA_VERSION=1.12.208 \
|
||||||
CLJFMT_VERSION=0.13.1
|
CLJFMT_VERSION=0.15.6
|
||||||
|
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
ARCH="$(dpkg --print-architecture)"; \
|
ARCH="$(dpkg --print-architecture)"; \
|
||||||
@@ -398,7 +398,6 @@ COPY files/Caddyfile /home/
|
|||||||
COPY files/selfsigned.crt /home/
|
COPY files/selfsigned.crt /home/
|
||||||
COPY files/selfsigned.key /home/
|
COPY files/selfsigned.key /home/
|
||||||
COPY files/start-tmux.sh /home/start-tmux.sh
|
COPY files/start-tmux.sh /home/start-tmux.sh
|
||||||
COPY files/start-tmux-back.sh /home/start-tmux-back.sh
|
|
||||||
COPY files/entrypoint.sh /home/entrypoint.sh
|
COPY files/entrypoint.sh /home/entrypoint.sh
|
||||||
COPY files/init.sh /home/init.sh
|
COPY files/init.sh /home/init.sh
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
{
|
{
|
||||||
auto_https off
|
auto_https off
|
||||||
}
|
}
|
||||||
|
|
||||||
localhost:3449 {
|
localhost:3449 {
|
||||||
reverse_proxy localhost:4449
|
reverse_proxy localhost:4449
|
||||||
tls /home/selfsigned.crt /home/selfsigned.key
|
tls /home/selfsigned.crt /home/selfsigned.key
|
||||||
|
header -Strict-Transport-Security
|
||||||
}
|
}
|
||||||
|
|
||||||
http://localhost:3450 {
|
http://localhost:3450 {
|
||||||
reverse_proxy localhost:4449
|
reverse_proxy localhost:4449
|
||||||
}
|
}
|
||||||
|
|
||||||
http://penpot-devenv-main:3450 {
|
http://penpot-devenv-main:3450 {
|
||||||
reverse_proxy localhost:4449
|
reverse_proxy localhost:4449
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,8 +141,14 @@ http {
|
|||||||
proxy_pass http://127.0.0.1:5000;
|
proxy_pass http://127.0.0.1:5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /nitrate/ {
|
location /control-center {
|
||||||
proxy_pass http://127.0.0.1:3000/;
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /wasm-playground {
|
location /wasm-playground {
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
sudo chown penpot:users /home/penpot
|
|
||||||
|
|
||||||
cd ~;
|
|
||||||
|
|
||||||
source ~/.bashrc
|
|
||||||
|
|
||||||
set -e;
|
|
||||||
|
|
||||||
echo "[start-tmux.sh] Installing node dependencies"
|
|
||||||
pushd ~/penpot/exporter/
|
|
||||||
yarn install
|
|
||||||
popd
|
|
||||||
|
|
||||||
tmux -2 new-session -d -s penpot
|
|
||||||
|
|
||||||
tmux rename-window -t penpot:0 'exporter'
|
|
||||||
tmux select-window -t penpot:0
|
|
||||||
tmux send-keys -t penpot 'cd penpot/exporter' enter C-l
|
|
||||||
tmux send-keys -t penpot 'rm -f target/app.js*' enter C-l
|
|
||||||
tmux send-keys -t penpot 'clojure -M:dev:shadow-cljs watch main' enter
|
|
||||||
|
|
||||||
tmux split-window -v
|
|
||||||
tmux send-keys -t penpot 'cd penpot/exporter' enter C-l
|
|
||||||
tmux send-keys -t penpot './scripts/wait-and-start.sh' enter
|
|
||||||
|
|
||||||
tmux new-window -t penpot:1 -n 'backend'
|
|
||||||
tmux select-window -t penpot:1
|
|
||||||
tmux send-keys -t penpot 'cd penpot/backend' enter C-l
|
|
||||||
tmux send-keys -t penpot './scripts/start-dev' enter
|
|
||||||
|
|
||||||
tmux -2 attach-session -t penpot
|
|
||||||
@@ -112,10 +112,6 @@ COPY --from=penpotapp/imagemagick:7.1.2-0 /opt/imagick /opt/imagick
|
|||||||
WORKDIR /opt/penpot/exporter
|
WORKDIR /opt/penpot/exporter
|
||||||
USER penpot:penpot
|
USER penpot:penpot
|
||||||
|
|
||||||
RUN set -ex; \
|
RUN ./setup
|
||||||
corepack install; \
|
|
||||||
yarn install; \
|
|
||||||
yarn run playwright install chromium; \
|
|
||||||
rm -rf /opt/penpot/.yarn
|
|
||||||
|
|
||||||
CMD ["node", "app.js"]
|
CMD ["node", "app.js"]
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ update_flags /var/www/app/js/config.js
|
|||||||
|
|
||||||
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
|
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
|
||||||
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061}
|
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061}
|
||||||
|
export PENPOT_NITRATE_URI=${PENPOT_NITRATE_URI:-http://penpot-nitrate:3000}
|
||||||
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE:-367001600} # Default to 350MiB
|
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE:-367001600} # Default to 350MiB
|
||||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
||||||
< /tmp/nginx.conf.template > /etc/nginx/nginx.conf
|
< /tmp/nginx.conf.template > /etc/nginx/nginx.conf
|
||||||
|
|
||||||
PENPOT_DEFAULT_INTERNAL_RESOLVER="$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf)"
|
PENPOT_DEFAULT_INTERNAL_RESOLVER="$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf)"
|
||||||
|
|||||||
@@ -139,12 +139,20 @@ http {
|
|||||||
proxy_pass $PENPOT_BACKEND_URI/ws/notifications;
|
proxy_pass $PENPOT_BACKEND_URI/ws/notifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /control-center {
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Real-IP $http_cf_connecting_ip;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_pass $PENPOT_NITRATE_URI$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
include /etc/nginx/overrides/server.d/*.conf;
|
include /etc/nginx/overrides/server.d/*.conf;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
include /etc/nginx/overrides/location.d/*.conf;
|
include /etc/nginx/overrides/location.d/*.conf;
|
||||||
|
|
||||||
location ~* \.(js|css|jpg|png|svg|gif|ttf|woff|woff2|wasm)$ {
|
location ~* \.(js|css|jpg|png|svg|gif|ttf|woff|woff2|wasm|map)$ {
|
||||||
add_header Cache-Control "public, max-age=604800" always; # 7 days
|
add_header Cache-Control "public, max-age=604800" always; # 7 days
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,8 +160,10 @@ http {
|
|||||||
return 301 " /404";
|
return 301 " /404";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_header X-Frame-Options SAMEORIGIN always;
|
||||||
add_header Cache-Control "no-store, no-cache, max-age=0" always;
|
add_header Cache-Control "no-store, no-cache, max-age=0" always;
|
||||||
try_files $uri /index.html$is_args$args /index.html =404;
|
try_files $uri /index.html$is_args$args /index.html =404;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,16 +10,15 @@ To view this site locally, first set up the environment:
|
|||||||
# only if necessary
|
# only if necessary
|
||||||
nvm install
|
nvm install
|
||||||
nvm use
|
nvm use
|
||||||
# only if necessary
|
|
||||||
corepack enable
|
corepack enable
|
||||||
|
|
||||||
yarn install
|
pnpm install
|
||||||
```
|
```
|
||||||
|
|
||||||
And launch a development server:
|
And launch a development server:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
yarn start
|
pnpm start
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then point a browser to [http://localhost:8080](http://localhost:8080).
|
You can then point a browser to [http://localhost:8080](http://localhost:8080).
|
||||||
|
|||||||
@@ -39,5 +39,5 @@
|
|||||||
"markdown-it-anchor": "^9.0.1",
|
"markdown-it-anchor": "^9.0.1",
|
||||||
"markdown-it-plantuml": "^1.4.1"
|
"markdown-it-plantuml": "^1.4.1"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@4.3.1"
|
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ desc: Create, deploy, and use the Penpot plugin API with our comprehensive docum
|
|||||||
|
|
||||||
# Penpot plugins API
|
# Penpot plugins API
|
||||||
|
|
||||||
We've got all the documentation you need for the API right <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/">here</a>.
|
We've got all the documentation you need for the API right <a target="_blank" href="https://doc.plugins.penpot.app/">here</a>.
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ desc: See the Penpot plugin API changelog for version 1.0! Find breaking changes
|
|||||||
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"></g-emoji> Epics and highlights</code>
|
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"></g-emoji> Epics and highlights</code>
|
||||||
- This marks the release of version 1.0, and from this point forward, we’ll do our best to avoid making any more breaking changes (or make deprecations backward compatible).
|
- This marks the release of version 1.0, and from this point forward, we’ll do our best to avoid making any more breaking changes (or make deprecations backward compatible).
|
||||||
- We’ve redone the documentation. You can check the API here:
|
- We’ve redone the documentation. You can check the API here:
|
||||||
[https://penpot-plugins-api-doc.pages.dev/](https://penpot-plugins-api-doc.pages.dev/)
|
[https://doc.plugins.penpot.app/](https://doc.plugins.penpot.app/)
|
||||||
- New samples repository with lots of samples to use the API:
|
- New samples repository with lots of samples to use the API:
|
||||||
[https://github.com/penpot/penpot-plugins-samples](https://github.com/penpot/penpot-plugins-samples)
|
[https://github.com/penpot/penpot-plugins-samples](https://github.com/penpot/penpot-plugins-samples)
|
||||||
|
|
||||||
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"></g-emoji> Breaking changes & Deprecations
|
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"></g-emoji> Breaking changes & Deprecations
|
||||||
|
|
||||||
- Changed types names to remove the Penpot prefix. So for example: <code class="language-js">PenpotShape</code> becomes <code class="language-js">Shape</code>; <code class="language-js">PenpotFile</code> becomes <code class="language-js">File</code>, and so on. Check the [API documentation](https://penpot-plugins-api-doc.pages.dev/) for more details.
|
- Changed types names to remove the Penpot prefix. So for example: <code class="language-js">PenpotShape</code> becomes <code class="language-js">Shape</code>; <code class="language-js">PenpotFile</code> becomes <code class="language-js">File</code>, and so on. Check the [API documentation](https://doc.plugins.penpot.app/) for more details.
|
||||||
- Changes on the <code class="language-js">penpot.on</code> and <code class="language-js">penpot.off</code> methods.
|
- Changes on the <code class="language-js">penpot.on</code> and <code class="language-js">penpot.off</code> methods.
|
||||||
Previously you had to send the original callback to the off method in order to remove an event listener. Now, <code class="language-js">penpot.on</code> will return an *id* that you can pass to the <code class="language-js">penpot.off</code> method in order to remove the listener.
|
Previously you had to send the original callback to the off method in order to remove an event listener. Now, <code class="language-js">penpot.on</code> will return an *id* that you can pass to the <code class="language-js">penpot.off</code> method in order to remove the listener.
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ There are two libraries that can help you with your plugin's development. They a
|
|||||||
|
|
||||||
### Plugin styles
|
### Plugin styles
|
||||||
|
|
||||||
<code class="language-js">@penpot/plugin-styles</code> contains styles to help build the UI for Penpot plugins. To check the styles go to <a target="_blank" href="https://penpot-plugins-styles.pages.dev/">Plugin styles</a>.
|
<code class="language-js">@penpot/plugin-styles</code> contains styles to help build the UI for Penpot plugins. To check the styles go to <a target="_blank" href="https://styles-doc.plugins.penpot.app/">Plugin styles</a>.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install @penpot/plugin-styles
|
npm install @penpot/plugin-styles
|
||||||
@@ -139,7 +139,7 @@ parent.postMessage(responseMessage, targetOrigin);
|
|||||||
|
|
||||||
By using these message-based events, any data retrieved through the Penpot API can be communicated to and from your plugin interface seamlessly.
|
By using these message-based events, any data retrieved through the Penpot API can be communicated to and from your plugin interface seamlessly.
|
||||||
|
|
||||||
For more detailed information, refer to the [Penpot Plugins API Documentation](https://penpot-plugins-api-doc.pages.dev/).
|
For more detailed information, refer to the [Penpot Plugins API Documentation](https://doc.plugins.penpot.app/).
|
||||||
|
|
||||||
## 2.5. Step 5. Build the plugin file
|
## 2.5. Step 5. Build the plugin file
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ penpot.library.local.createTypography();
|
|||||||
|
|
||||||
Penpot has dark and light modes, and you can easily add this to your plugin so your interface adapts to both themes. When you add theme support, your plugin will automatically sync with Penpot's interface settings, so the user experience is consistent no matter which mode is selected. This makes your plugin look better and also ensures it stays in line with Penpot's overall design.
|
Penpot has dark and light modes, and you can easily add this to your plugin so your interface adapts to both themes. When you add theme support, your plugin will automatically sync with Penpot's interface settings, so the user experience is consistent no matter which mode is selected. This makes your plugin look better and also ensures it stays in line with Penpot's overall design.
|
||||||
|
|
||||||
Just a heads-up: if you use the <a target="_blank" href="https://penpot-plugins-styles.pages.dev/">plugin-styles library</a>, many elements will automatically adapt to dark or light mode without any extra effort from you. However, if you need to customize specific elements, be sure to use the selectors provided in the <code class="language-bash">styles.css</code> of the example.
|
Just a heads-up: if you use the <a target="_blank" href="https://styles-doc.plugins.penpot.app/">plugin-styles library</a>, many elements will automatically adapt to dark or light mode without any extra effort from you. However, if you need to customize specific elements, be sure to use the selectors provided in the <code class="language-bash">styles.css</code> of the example.
|
||||||
|
|
||||||
<a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/theme">Theme example</a>
|
<a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/theme">Theme example</a>
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ The plugin <a target="_blank" href="https://www.npmjs.com/package/@penpot/plugin
|
|||||||
|
|
||||||
### Is the API ready to use the prototyping features?
|
### Is the API ready to use the prototyping features?
|
||||||
|
|
||||||
Absolutely! You can definitely create flows and interactions in the same elements as in the interface, like frames, shapes, and groups. Just check out the API documentation for the methods: createFlow, addInteraction, or removeInteraction. And if you need more help, you can always check out the <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/PenpotFlow">PenpotFlow</a> or <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/PenpotInteraction">PenpotInteraction</a> interfaces.
|
Absolutely! You can definitely create flows and interactions in the same elements as in the interface, like frames, shapes, and groups. Just check out the API documentation for the methods: createFlow, addInteraction, or removeInteraction. And if you need more help, you can always check out the <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Flow">Flow</a> or <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Interaction">Interaction</a> interfaces.
|
||||||
|
|
||||||
### Are there any security or quality criteria I should be aware of?
|
### Are there any security or quality criteria I should be aware of?
|
||||||
|
|
||||||
@@ -48,7 +48,8 @@ There are no set requirements. However, we can recommend the use of <a target="_
|
|||||||
|
|
||||||
### Is it necessary to create plugins with a UI?
|
### Is it necessary to create plugins with a UI?
|
||||||
|
|
||||||
No, it’s completely optional, in fact, we have an example of a plugin without UI. Try the plugin using this url to install it: <code class="language-js">https:\/\/create-palette-penpot-plugin.pages.dev/assets/manifest.json</code> or check the code <a target="_blank" href="https://github.com/penpot/penpot-plugins/tree/main/apps/create-palette-plugin">here</a>
|
No, it’s completely optional, in fact, we have an example of a plugin without UI. Try the plugin using this url to install it: <code class="language-js">https:\/\/create-palette.plugins.penpot.app/assets/manifest.json</code> or check the code <a target="_blank" href="https://github.com/penpot/penpot/tree/main/plugins/apps/create-palette-plugin">here</a>
|
||||||
|
|
||||||
|
|
||||||
### Can I create components?
|
### Can I create components?
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ Yes, it is possible to create components using:
|
|||||||
createComponent(shapes: Shape[]): LibraryComponent;
|
createComponent(shapes: Shape[]): LibraryComponent;
|
||||||
```
|
```
|
||||||
|
|
||||||
Take a look at the Penpot Library methods in the <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/Library">API documentation</a> or this <a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/components-library">simple example</a>.
|
Take a look at the Penpot Library methods in the <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Library">API documentation</a> or this <a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/components-library">simple example</a>.
|
||||||
|
|
||||||
### Is there a place where I can share my plugin?
|
### Is there a place where I can share my plugin?
|
||||||
|
|
||||||
|
|||||||
@@ -69,12 +69,13 @@ You need to provide the plugin's manifest URL for the installation. If there are
|
|||||||
|
|
||||||
| Name | URL |
|
| Name | URL |
|
||||||
| ------------- | ------------------------------------------------------------------- |
|
| ------------- | ------------------------------------------------------------------- |
|
||||||
| Lorem Ipsum | https://lorem-ipsum-penpot-plugin.pages.dev/assets/manifest.json |
|
| Color palette | https://create-palette.plugins.penpot.app/assets/manifest.json |
|
||||||
| Contrast | https://contrast-penpot-plugin.pages.dev/assets/manifest.json |
|
| Contrast | https://contrast.plugins.penpot.app/assets/manifest.json |
|
||||||
| Feather icons | https://icons-penpot-plugin.pages.dev/assets/manifest.json |
|
| Feather icons | https://icons.plugins.penpot.app/assets/manifest.json |
|
||||||
| Tables | https://table-penpot-plugin.pages.dev/assets/manifest.json |
|
| Lorem ipsum | https://lorem-ipsum.plugins.penpot.app/assets/manifest.json |
|
||||||
| Color palette | https://create-palette-penpot-plugin.pages.dev/assets/manifest.json |
|
| Rename layers | https://rename-layers.plugins.penpot.app/assets/manifest.json |
|
||||||
| Rename layers | https://rename-layers-penpot-plugin.pages.dev/assets/manifest.json |
|
| Tables | https://table.plugins.penpot.app/assets/manifest.json |
|
||||||
|
|
||||||
|
|
||||||
## 1.4. Plugin's basics
|
## 1.4. Plugin's basics
|
||||||
|
|
||||||
|
|||||||
2065
docs/pnpm-lock.yaml
generated
Normal file
2065
docs/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,5 +6,5 @@ corepack enable;
|
|||||||
corepack install;
|
corepack install;
|
||||||
|
|
||||||
rm -rf ./_dist
|
rm -rf ./_dist
|
||||||
yarn install
|
pnpm install
|
||||||
yarn run build
|
pnpm run build
|
||||||
|
|||||||
@@ -281,8 +281,8 @@ for how to define custom metadata and other ways of selecting tests.
|
|||||||
it, but for now we use shadow-cljs with <code class="language-text">package.json</code> scripts:
|
it, but for now we use shadow-cljs with <code class="language-text">package.json</code> scripts:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn run test
|
pnpm run test
|
||||||
yarn run test:watch
|
pnpm run test:watch
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Test output
|
#### Test output
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ repository:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# cd <repo>/frontend
|
# cd <repo>/frontend
|
||||||
yarn run translations
|
pnpm run translations
|
||||||
```
|
```
|
||||||
|
|
||||||
At Penpot core team we maintain manually the english and spanish .po files. All
|
At Penpot core team we maintain manually the english and spanish .po files. All
|
||||||
@@ -308,7 +308,7 @@ Ensure your development environment docker image is up to date.
|
|||||||
This is not required, but it may be convenient to compile Penpot in release mode before running the tests. This way they will be much quicker and stable. For this, go to the frontend window in the tmux session (<code class="language-bash">Ctrl + b 1</code>), interrupt the watch process with <code class="language-bash">Ctrl + C</code> and type:
|
This is not required, but it may be convenient to compile Penpot in release mode before running the tests. This way they will be much quicker and stable. For this, go to the frontend window in the tmux session (<code class="language-bash">Ctrl + b 1</code>), interrupt the watch process with <code class="language-bash">Ctrl + C</code> and type:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn run build:app
|
./scripts/build
|
||||||
```
|
```
|
||||||
|
|
||||||
Obviously, in this mode if you make changes to the source code, you will need to repeat the build manually each time. It may be useful to use wath mode when debugging a single test, and use release mode to run all the suite.
|
Obviously, in this mode if you make changes to the source code, you will need to repeat the build manually each time. It may be useful to use wath mode when debugging a single test, and use release mode to run all the suite.
|
||||||
@@ -328,17 +328,17 @@ Here's how to run the tests with a headless browser (i.e. within the terminal, n
|
|||||||
cd penpot/frontend
|
cd penpot/frontend
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Run the tests with <code class="language-bash">yarn</code>:
|
3. Run the tests with <code class="language-bash">pnpm</code>:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn test:e2e
|
pnpm run test:e2e
|
||||||
```
|
```
|
||||||
|
|
||||||
> 💡 **TIP:** By default, the tests will _not_ run in parallel. You can set the amount of workers to run the tests with <code class="language-bash">--workers</code>. Note that, depending on your machine, this might make some tests flaky.
|
> 💡 **TIP:** By default, the tests will _not_ run in parallel. You can set the amount of workers to run the tests with <code class="language-bash">--workers</code>. Note that, depending on your machine, this might make some tests flaky.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# run in parallel with 4 workers
|
# run in parallel with 4 workers
|
||||||
yarn test:e2e --workers 4
|
pnpm run test:e2e --workers 4
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Running the tests in Chromium
|
#### Running the tests in Chromium
|
||||||
@@ -356,7 +356,7 @@ npx playwright test --ui
|
|||||||
|
|
||||||
> ❗️ **IMPORTANT**: You might need to [install Playwright's browsers and dependencies](https://playwright.dev/docs/intro) in your host machine with: <code class="language-bash">npx playwright install --with-deps</code>. In case you are using a Linux distribution other than Ubuntu, [you might need to install the dependencies manually](https://github.com/microsoft/playwright/issues/11122).
|
> ❗️ **IMPORTANT**: You might need to [install Playwright's browsers and dependencies](https://playwright.dev/docs/intro) in your host machine with: <code class="language-bash">npx playwright install --with-deps</code>. In case you are using a Linux distribution other than Ubuntu, [you might need to install the dependencies manually](https://github.com/microsoft/playwright/issues/11122).
|
||||||
|
|
||||||
> You will also need yarn in your host nodejs. For this, do <code class="language-bash">corepack enable</code> and then just <code class="language-bash">yarn</code>.
|
> You will also need pnpm in your host nodejs. For this, do <code class="language-bash">corepack enable</code> and then just <code class="language-bash">pnpm</code>.
|
||||||
|
|
||||||
### How to write a test
|
### How to write a test
|
||||||
|
|
||||||
|
|||||||
@@ -677,7 +677,7 @@ The Storybook is available at the <code class="language-bash">/storybook</code>
|
|||||||
|
|
||||||
#### Local development
|
#### Local development
|
||||||
|
|
||||||
Use <code class="language-bash">yarn watch:storybook</code> to develop the Design System components with the help of Storybook.
|
Use <code class="language-bash">pnpm run watch:storybook</code> to develop the Design System components with the help of Storybook.
|
||||||
|
|
||||||
> **⚠️ WARNING**: Do stop any existing Shadow CLJS and asset compilation jobs (like the ones running at tabs <code class="language-bash">0</code> and <code class="language-bash">1</code> in the devenv tmux), because <code class="language-bash">watch:storybook</code> will spawn their own.
|
> **⚠️ WARNING**: Do stop any existing Shadow CLJS and asset compilation jobs (like the ones running at tabs <code class="language-bash">0</code> and <code class="language-bash">1</code> in the devenv tmux), because <code class="language-bash">watch:storybook</code> will spawn their own.
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user