mirror of
https://github.com/penpot/penpot.git
synced 2026-01-30 17:21:56 -05:00
Compare commits
311 Commits
andy-docs-
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91671afb7a | ||
|
|
838194f9e5 | ||
|
|
68cf2ecc57 | ||
|
|
3e4f70f37b | ||
|
|
b8fdbd1ef8 | ||
|
|
32454f5959 | ||
|
|
b881e36875 | ||
|
|
0bb74ed722 | ||
|
|
b40e775a70 | ||
|
|
2b4e315744 | ||
|
|
4ca82821c1 | ||
|
|
cc81e56d82 | ||
|
|
a90f672a5e | ||
|
|
2b00e4eec9 | ||
|
|
3b86d7c1b1 | ||
|
|
3cb716ec30 | ||
|
|
a9e2fc8d94 | ||
|
|
f76598f638 | ||
|
|
17ffd9a5d0 | ||
|
|
eacc033567 | ||
|
|
71c349479f | ||
|
|
18aca16f98 | ||
|
|
c6465e27e3 | ||
|
|
1834a18263 | ||
|
|
d220d07875 | ||
|
|
fda31624c1 | ||
|
|
7f640569bd | ||
|
|
91f1323802 | ||
|
|
dbd4a2366f | ||
|
|
cbb6d098a7 | ||
|
|
b6f5000d1c | ||
|
|
0527124f2f | ||
|
|
faf91ac70d | ||
|
|
9ca76c745f | ||
|
|
89935e2174 | ||
|
|
7f27e0326d | ||
|
|
9c539dfb2f | ||
|
|
50a4cf8b99 | ||
|
|
f5996a7235 | ||
|
|
e8fd4698c9 | ||
|
|
0ab126748f | ||
|
|
71a5ab9913 | ||
|
|
61969f3eb5 | ||
|
|
bd2ef8057e | ||
|
|
9808b6ca57 | ||
|
|
2523096fdd | ||
|
|
de41cb5488 | ||
|
|
8e63c4e3e8 | ||
|
|
b40ccaf030 | ||
|
|
7d3ac38749 | ||
|
|
d5abc52dac | ||
|
|
8d1bc6c50c | ||
|
|
3112b240a0 | ||
|
|
56fd66b91a | ||
|
|
3b96eb5476 | ||
|
|
2a7c24f6fd | ||
|
|
7a842ce36a | ||
|
|
ea25c5db99 | ||
|
|
947aa22dee | ||
|
|
ce1796eb02 | ||
|
|
6f0685ba8e | ||
|
|
1ce0b60e3d | ||
|
|
d433fd25c1 | ||
|
|
bb0e9b47cb | ||
|
|
5209a8b423 | ||
|
|
f4f4f5bbb5 | ||
|
|
ef80901400 | ||
|
|
c5f03d711a | ||
|
|
5306bed548 | ||
|
|
92a319ddd1 | ||
|
|
68a6d4c9a8 | ||
|
|
72cc5ee349 | ||
|
|
804695b48b | ||
|
|
20c8fbf314 | ||
|
|
e02536f8d4 | ||
|
|
3eeaaab17e | ||
|
|
3dc9e28230 | ||
|
|
d9c56da705 | ||
|
|
75248aec4e | ||
|
|
f0d9429775 | ||
|
|
62ecf48bdb | ||
|
|
18de7f1db6 | ||
|
|
2b2941bd25 | ||
|
|
f2d561eff7 | ||
|
|
418b65a287 | ||
|
|
d4e7810eba | ||
|
|
1d1d32ad39 | ||
|
|
fb08dc65c8 | ||
|
|
927ac93fa7 | ||
|
|
e546a7c614 | ||
|
|
058c20c2e2 | ||
|
|
68a77e9cc8 | ||
|
|
e3148ea20e | ||
|
|
5da9bbea62 | ||
|
|
5016b2a7bf | ||
|
|
089d1667b6 | ||
|
|
4ad5282063 | ||
|
|
d0e79c94b4 | ||
|
|
43ae213659 | ||
|
|
d112c0a33b | ||
|
|
7b86518afa | ||
|
|
9991901ed8 | ||
|
|
3d0c6ad421 | ||
|
|
dc973dac36 | ||
|
|
4467827218 | ||
|
|
6470db8d5f | ||
|
|
dc44156b53 | ||
|
|
f0e53d70ae | ||
|
|
ef73a263b2 | ||
|
|
9b1e007a49 | ||
|
|
ea8632e56a | ||
|
|
2d00e64ede | ||
|
|
1246250198 | ||
|
|
34f2943dcd | ||
|
|
3fb78116b8 | ||
|
|
072e415b9e | ||
|
|
67a904824c | ||
|
|
835ea97be7 | ||
|
|
68184209be | ||
|
|
d2295862b4 | ||
|
|
23cbf33d1b | ||
|
|
b4ff0ccf3a | ||
|
|
6c6666a39a | ||
|
|
f94c9cdb02 | ||
|
|
8637c46ba1 | ||
|
|
5d7d23a2c7 | ||
|
|
a1a3966d7b | ||
|
|
c1335961b4 | ||
|
|
eaf64b6e16 | ||
|
|
560a0d09d5 | ||
|
|
aab1d97c4c | ||
|
|
499aac31a4 | ||
|
|
962d7839a2 | ||
|
|
83387701a0 | ||
|
|
5775fa61ba | ||
|
|
b70eb768e0 | ||
|
|
5b1766835f | ||
|
|
4397ede5c1 | ||
|
|
ff25df0457 | ||
|
|
e0910db99e | ||
|
|
079b3fbfad | ||
|
|
299f628951 | ||
|
|
32d0fe6463 | ||
|
|
cecd3d4a90 | ||
|
|
1c2c0987f5 | ||
|
|
0418147e74 | ||
|
|
9e0ba4429a | ||
|
|
7499a5bca6 | ||
|
|
6cd5bc76d7 | ||
|
|
bbe6ee2e19 | ||
|
|
fb6d8309b6 | ||
|
|
b7c2d9a079 | ||
|
|
aeb34a6f64 | ||
|
|
6fa0c3af0c | ||
|
|
260b9fb040 | ||
|
|
884954f4ff | ||
|
|
689467bcf9 | ||
|
|
7724450037 | ||
|
|
368fa954ce | ||
|
|
6fd0f5377c | ||
|
|
eb54bc485e | ||
|
|
12c24a36b4 | ||
|
|
324d54ad28 | ||
|
|
f42ff27f3d | ||
|
|
0ecb2bc838 | ||
|
|
2c1cc89f53 | ||
|
|
498b0b30fe | ||
|
|
89f40dcda2 | ||
|
|
e92f3fb3cb | ||
|
|
5193cfd56e | ||
|
|
813d5d8e69 | ||
|
|
84f1ff092d | ||
|
|
ccac7bd510 | ||
|
|
f2b082b93e | ||
|
|
d73197625d | ||
|
|
1f41bef4a9 | ||
|
|
fdf5bb250b | ||
|
|
786736fadd | ||
|
|
1ff6e00398 | ||
|
|
25455523ad | ||
|
|
8dfeb21978 | ||
|
|
43d1d127dc | ||
|
|
8bd3ef717c | ||
|
|
53bc647783 | ||
|
|
ad2833bb7a | ||
|
|
21911e898f | ||
|
|
538073debf | ||
|
|
6029f9bb51 | ||
|
|
e0fd8bac81 | ||
|
|
34737ddfc9 | ||
|
|
a8dfd19338 | ||
|
|
e33e8a8c3b | ||
|
|
214b0efa02 | ||
|
|
661436ecae | ||
|
|
c411aefc6c | ||
|
|
311e124658 | ||
|
|
afc914f486 | ||
|
|
0d5fe6e527 | ||
|
|
e7230d9da4 | ||
|
|
5054f6bc38 | ||
|
|
84f750da0d | ||
|
|
38396ba299 | ||
|
|
b1997a83b3 | ||
|
|
68f5671eab | ||
|
|
92976143bb | ||
|
|
dd2d03e6a0 | ||
|
|
fd675e0194 | ||
|
|
a3119bef5e | ||
|
|
c60d74df62 | ||
|
|
d593e299e3 | ||
|
|
4a8e02987f | ||
|
|
ee766e85a0 | ||
|
|
35e3b7f19a | ||
|
|
1810df232b | ||
|
|
3e99ad036c | ||
|
|
042a3a4080 | ||
|
|
f0687fd1f7 | ||
|
|
2c9159288f | ||
|
|
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
|
||||||
6
.gitignore
vendored
6
.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/
|
||||||
@@ -62,6 +66,7 @@
|
|||||||
/frontend/resources/public/*
|
/frontend/resources/public/*
|
||||||
/frontend/storybook-static/
|
/frontend/storybook-static/
|
||||||
/frontend/target/
|
/frontend/target/
|
||||||
|
/frontend/test-results/
|
||||||
/other/
|
/other/
|
||||||
/scripts/
|
/scripts/
|
||||||
/telemetry/
|
/telemetry/
|
||||||
@@ -72,6 +77,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
|
|
||||||
42
CHANGES.md
42
CHANGES.md
@@ -1,5 +1,38 @@
|
|||||||
# 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)
|
||||||
|
- Fix viewer can update library [Taiga #13186](https://tree.taiga.io/project/penpot/issue/13186)
|
||||||
|
|
||||||
## 2.13.0 (Unreleased)
|
## 2.13.0 (Unreleased)
|
||||||
|
|
||||||
### :boom: Breaking changes & Deprecations
|
### :boom: Breaking changes & Deprecations
|
||||||
@@ -23,12 +56,14 @@
|
|||||||
- 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)
|
||||||
- Fix missing text color token from selected shapes in selected colors list [Taiga #12956](https://tree.taiga.io/project/penpot/issue/12956)
|
- Fix missing text color token from selected shapes in selected colors list [Taiga #12956](https://tree.taiga.io/project/penpot/issue/12956)
|
||||||
- Fix dropdown option width in Guides columns dropdown [Taiga #12959](https://tree.taiga.io/project/penpot/issue/12959)
|
- Fix dropdown option width in Guides columns dropdown [Taiga #12959](https://tree.taiga.io/project/penpot/issue/12959)
|
||||||
- Fix typos on download modal [Taiga #12865](https://tree.taiga.io/project/penpot/issue/12865)
|
- Fix typos on download modal [Taiga #12865](https://tree.taiga.io/project/penpot/issue/12865)
|
||||||
|
- Fix problem with text editor maintaining previous styles [Taiga #12835](https://tree.taiga.io/project/penpot/issue/12835)
|
||||||
- Fix unhandled exception tokens creation dialog [Github #8110](https://github.com/penpot/penpot/issues/8110)
|
- Fix unhandled exception tokens creation dialog [Github #8110](https://github.com/penpot/penpot/issues/8110)
|
||||||
- Fix allow negative spread values on shadow token creation [Taiga #13167](https://tree.taiga.io/project/penpot/issue/13167)
|
- Fix allow negative spread values on shadow token creation [Taiga #13167](https://tree.taiga.io/project/penpot/issue/13167)
|
||||||
- Fix spanish translations on import export token modal [Taiga #13171](https://tree.taiga.io/project/penpot/issue/13171)
|
- Fix spanish translations on import export token modal [Taiga #13171](https://tree.taiga.io/project/penpot/issue/13171)
|
||||||
@@ -36,7 +71,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
|
||||||
|
|
||||||
@@ -46,7 +82,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
|
||||||
@@ -58,7 +93,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
|
||||||
@@ -91,7 +125,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
|
||||||
@@ -155,7 +188,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 %}
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
export PENPOT_MANAGEMENT_API_KEY=super-secret-management-api-key
|
export PENPOT_NITRATE_SHARED_KEY=super-secret-nitrate-api-key
|
||||||
|
export PENPOT_EXPORTER_SHARED_KEY=super-secret-exporter-api-key
|
||||||
export PENPOT_SECRET_KEY=super-secret-devenv-key
|
export PENPOT_SECRET_KEY=super-secret-devenv-key
|
||||||
|
|
||||||
|
# DEPRECATED: only used for subscriptions
|
||||||
|
export PENPOT_MANAGEMENT_API_KEY=super-secret-management-api-key
|
||||||
|
|
||||||
export PENPOT_HOST=devenv
|
export PENPOT_HOST=devenv
|
||||||
export PENPOT_PUBLIC_URI=https://localhost:3449
|
export PENPOT_PUBLIC_URI=https://localhost:3449
|
||||||
|
|
||||||
@@ -13,6 +18,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 +61,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))))
|
||||||
[]
|
[]
|
||||||
|
|||||||
@@ -102,6 +102,8 @@
|
|||||||
[:http-server-io-threads {:optional true} ::sm/int]
|
[:http-server-io-threads {:optional true} ::sm/int]
|
||||||
[:http-server-max-worker-threads {:optional true} ::sm/int]
|
[:http-server-max-worker-threads {:optional true} ::sm/int]
|
||||||
|
|
||||||
|
[:exporter-shared-key {:optional true} :string]
|
||||||
|
[:nitrate-shared-key {:optional true} :string]
|
||||||
[:management-api-key {:optional true} :string]
|
[:management-api-key {:optional true} :string]
|
||||||
|
|
||||||
[:telemetry-uri {:optional true} :string]
|
[:telemetry-uri {:optional true} :string]
|
||||||
@@ -225,6 +227,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)))))
|
||||||
|
|||||||
@@ -13,13 +13,13 @@
|
|||||||
[app.common.time :as ct]
|
[app.common.time :as ct]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.http.middleware :as mw]
|
|
||||||
[app.main :as-alias main]
|
[app.main :as-alias main]
|
||||||
[app.rpc.commands.profile :as cmd.profile]
|
[app.rpc.commands.profile :as cmd.profile]
|
||||||
[app.setup :as-alias setup]
|
[app.setup :as-alias setup]
|
||||||
[app.tokens :as tokens]
|
[app.tokens :as tokens]
|
||||||
[app.worker :as-alias wrk]
|
[app.worker :as-alias wrk]
|
||||||
[integrant.core :as ig]
|
[integrant.core :as ig]
|
||||||
|
[yetti.request :as yreq]
|
||||||
[yetti.response :as-alias yres]))
|
[yetti.response :as-alias yres]))
|
||||||
|
|
||||||
;; ---- ROUTES
|
;; ---- ROUTES
|
||||||
@@ -49,28 +49,40 @@
|
|||||||
(fn [cfg request]
|
(fn [cfg request]
|
||||||
(db/tx-run! cfg handler request)))))})
|
(db/tx-run! cfg handler request)))))})
|
||||||
|
|
||||||
|
(def ^:private shared-key-auth
|
||||||
|
{:name ::shared-key-auth
|
||||||
|
:compile
|
||||||
|
(fn [_ _]
|
||||||
|
(fn [handler key]
|
||||||
|
(if key
|
||||||
|
(fn [request]
|
||||||
|
(if-let [key' (yreq/get-header request "x-shared-key")]
|
||||||
|
(if (= key key')
|
||||||
|
(handler request)
|
||||||
|
{::yres/status 403})
|
||||||
|
{::yres/status 403}))
|
||||||
|
(fn [_ _]
|
||||||
|
{::yres/status 403}))))})
|
||||||
|
|
||||||
(defmethod ig/init-key ::routes
|
(defmethod ig/init-key ::routes
|
||||||
[_ {:keys [::setup/props] :as cfg}]
|
[_ cfg]
|
||||||
|
|
||||||
(let [management-key (or (cf/get :management-api-key)
|
["" {:middleware [[shared-key-auth (cf/get :management-api-key)]
|
||||||
(get props :management-key))]
|
[default-system cfg]
|
||||||
|
[transaction]]}
|
||||||
|
["/authenticate"
|
||||||
|
{:handler authenticate
|
||||||
|
:allowed-methods #{:post}}]
|
||||||
|
|
||||||
["" {:middleware [[mw/shared-key-auth management-key]
|
["/get-customer"
|
||||||
[default-system cfg]
|
{:handler get-customer
|
||||||
[transaction]]}
|
:transaction true
|
||||||
["/authenticate"
|
:allowed-methods #{:post}}]
|
||||||
{:handler authenticate
|
|
||||||
:allowed-methods #{:post}}]
|
|
||||||
|
|
||||||
["/get-customer"
|
["/update-customer"
|
||||||
{:handler get-customer
|
{:handler update-customer
|
||||||
:transaction true
|
:allowed-methods #{:post}
|
||||||
:allowed-methods #{:post}}]
|
:transaction true}]])
|
||||||
|
|
||||||
["/update-customer"
|
|
||||||
{:handler update-customer
|
|
||||||
:allowed-methods #{:post}
|
|
||||||
:transaction true}]]))
|
|
||||||
|
|
||||||
;; ---- HELPERS
|
;; ---- HELPERS
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
[app.http.errors :as errors]
|
[app.http.errors :as errors]
|
||||||
[app.tokens :as tokens]
|
[app.tokens :as tokens]
|
||||||
[app.util.pointer-map :as pmap]
|
[app.util.pointer-map :as pmap]
|
||||||
[buddy.core.codecs :as bc]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[yetti.adapter :as yt]
|
[yetti.adapter :as yt]
|
||||||
[yetti.middleware :as ymw]
|
[yetti.middleware :as ymw]
|
||||||
@@ -301,16 +300,20 @@
|
|||||||
:compile (constantly wrap-auth)})
|
:compile (constantly wrap-auth)})
|
||||||
|
|
||||||
(defn- wrap-shared-key-auth
|
(defn- wrap-shared-key-auth
|
||||||
[handler shared-key]
|
[handler keys]
|
||||||
(if shared-key
|
(if (seq keys)
|
||||||
(let [shared-key (if (string? shared-key)
|
(fn [request]
|
||||||
shared-key
|
(if-let [[key-id key] (some-> (yreq/get-header request "x-shared-key")
|
||||||
(bc/bytes->b64-str shared-key true))]
|
(str/split #"\s+" 2))]
|
||||||
(fn [request]
|
(let [key-id (-> key-id str/lower keyword)]
|
||||||
(let [key (yreq/get-header request "x-shared-key")]
|
(if (and (string? key)
|
||||||
(if (= key shared-key)
|
(contains? keys key-id)
|
||||||
(handler (assoc request ::http/auth-with-shared-key true))
|
(= key (get keys key-id)))
|
||||||
{::yres/status 403}))))
|
(-> request
|
||||||
|
(assoc ::http/auth-key-id key-id)
|
||||||
|
(handler))
|
||||||
|
{::yres/status 403}))
|
||||||
|
{::yres/status 403}))
|
||||||
(fn [_ _]
|
(fn [_ _]
|
||||||
{::yres/status 403})))
|
{::yres/status 403})))
|
||||||
|
|
||||||
|
|||||||
@@ -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]
|
||||||
@@ -139,10 +140,14 @@
|
|||||||
client-version (get-client-version request)
|
client-version (get-client-version request)
|
||||||
client-user-agent (get-client-user-agent request)
|
client-user-agent (get-client-user-agent request)
|
||||||
session-id (get-external-session-id request)
|
session-id (get-external-session-id request)
|
||||||
token-id (::actoken/id request)]
|
key-id (::http/auth-key-id request)
|
||||||
|
token-id (::actoken/id request)
|
||||||
|
token-type (::actoken/type request)]
|
||||||
(d/without-nils
|
(d/without-nils
|
||||||
{:external-session-id session-id
|
{:external-session-id session-id
|
||||||
|
:initiator (or key-id "app")
|
||||||
:access-token-id (some-> token-id str)
|
:access-token-id (some-> token-id str)
|
||||||
|
:access-token-type (some-> token-type str)
|
||||||
:client-event-origin client-event-origin
|
:client-event-origin client-event-origin
|
||||||
:client-user-agent client-user-agent
|
:client-user-agent client-user-agent
|
||||||
:client-version client-version
|
:client-version client-version
|
||||||
@@ -223,7 +228,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 +241,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 +258,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 +273,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 +327,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)))))))
|
||||||
|
|||||||
@@ -275,8 +275,7 @@
|
|||||||
::email/whitelist (ig/ref ::email/whitelist)}
|
::email/whitelist (ig/ref ::email/whitelist)}
|
||||||
|
|
||||||
::mgmt/routes
|
::mgmt/routes
|
||||||
{::db/pool (ig/ref ::db/pool)
|
{::db/pool (ig/ref ::db/pool)}
|
||||||
::setup/props (ig/ref ::setup/props)}
|
|
||||||
|
|
||||||
:app.http/router
|
:app.http/router
|
||||||
{::session/manager (ig/ref ::session/manager)
|
{::session/manager (ig/ref ::session/manager)
|
||||||
@@ -323,6 +322,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 +339,10 @@
|
|||||||
::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)
|
||||||
|
::setup/shared-keys (ig/ref ::setup/shared-keys)}
|
||||||
|
|
||||||
: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,17 +352,18 @@
|
|||||||
::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)}
|
||||||
|
|
||||||
::rpc/routes
|
::rpc/routes
|
||||||
{::rpc/methods (ig/ref :app.rpc/methods)
|
{::rpc/methods (ig/ref :app.rpc/methods)
|
||||||
::rpc/management-methods (ig/ref :app.rpc/management-methods)
|
::rpc/management-methods (ig/ref :app.rpc/management-methods)
|
||||||
|
|
||||||
;; FIXME: revisit if db/pool is necessary here
|
;; FIXME: revisit if db/pool is necessary here
|
||||||
::db/pool (ig/ref ::db/pool)
|
::db/pool (ig/ref ::db/pool)
|
||||||
::session/manager (ig/ref ::session/manager)
|
::session/manager (ig/ref ::session/manager)
|
||||||
::setup/props (ig/ref ::setup/props)}
|
::setup/shared-keys (ig/ref ::setup/shared-keys)}
|
||||||
|
|
||||||
::wrk/registry
|
::wrk/registry
|
||||||
{::mtx/metrics (ig/ref ::mtx/metrics)
|
{::mtx/metrics (ig/ref ::mtx/metrics)
|
||||||
@@ -446,6 +451,11 @@
|
|||||||
;; module requires the migrations to run before initialize.
|
;; module requires the migrations to run before initialize.
|
||||||
::migrations (ig/ref :app.migrations/migrations)}
|
::migrations (ig/ref :app.migrations/migrations)}
|
||||||
|
|
||||||
|
::setup/shared-keys
|
||||||
|
{::setup/props (ig/ref ::setup/props)
|
||||||
|
:nitrate (cf/get :nitrate-shared-key)
|
||||||
|
:exporter (cf/get :exporter-shared-key)}
|
||||||
|
|
||||||
::setup/clock
|
::setup/clock
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|||||||
130
backend/src/app/nitrate.clj
Normal file
130
backend/src/app/nitrate.clj
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
(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 shared-key profile-id]
|
||||||
|
(fn []
|
||||||
|
(http/req! cfg {:method method
|
||||||
|
:headers {"content-type" "application/json"
|
||||||
|
"accept" "application/json"
|
||||||
|
"x-shared-key" shared-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
|
||||||
|
[cfg method uri schema {:keys [::rpc/profile-id] :as params}]
|
||||||
|
(let [shared-key (-> cfg ::setup/shared-keys :nitrate)
|
||||||
|
full-http-call (-> (request-builder cfg method uri shared-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
|
||||||
|
[_ cfg]
|
||||||
|
(when (contains? cf/flags :nitrate)
|
||||||
|
{:get-team-org (partial get-team-org cfg)
|
||||||
|
:is-valid-user (partial is-valid-user cfg)}))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; 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))))
|
||||||
@@ -92,11 +92,11 @@
|
|||||||
(fn [{:keys [params path-params method] :as request}]
|
(fn [{:keys [params path-params method] :as request}]
|
||||||
(let [handler-name (:type path-params)
|
(let [handler-name (:type path-params)
|
||||||
etag (yreq/get-header request "if-none-match")
|
etag (yreq/get-header request "if-none-match")
|
||||||
|
|
||||||
|
key-id (get request ::http/auth-key-id)
|
||||||
profile-id (or (::session/profile-id request)
|
profile-id (or (::session/profile-id request)
|
||||||
(::actoken/profile-id request)
|
(::actoken/profile-id request)
|
||||||
(if (::http/auth-with-shared-key request)
|
(if key-id uuid/zero nil))
|
||||||
uuid/zero
|
|
||||||
nil))
|
|
||||||
|
|
||||||
ip-addr (inet/parse-request request)
|
ip-addr (inet/parse-request request)
|
||||||
|
|
||||||
@@ -298,10 +298,12 @@
|
|||||||
|
|
||||||
(defn- resolve-management-methods
|
(defn- resolve-management-methods
|
||||||
[cfg]
|
[cfg]
|
||||||
(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
|
mods (cond->> (list 'app.rpc.management.exporter)
|
||||||
'app.rpc.management.subscription
|
(contains? cf/flags :nitrate)
|
||||||
'app.rpc.management.exporter)
|
(cons 'app.rpc.management.nitrate))]
|
||||||
|
|
||||||
|
(->> (apply sv/scan-ns mods)
|
||||||
(map (partial process-method cfg "management" wrap-management))
|
(map (partial process-method cfg "management" wrap-management))
|
||||||
(into {}))))
|
(into {}))))
|
||||||
|
|
||||||
@@ -345,23 +347,20 @@
|
|||||||
|
|
||||||
(defmethod ig/assert-key ::routes
|
(defmethod ig/assert-key ::routes
|
||||||
[_ params]
|
[_ params]
|
||||||
|
(assert (map? (::setup/shared-keys params)))
|
||||||
(assert (db/pool? (::db/pool params)) "expect valid database pool")
|
(assert (db/pool? (::db/pool params)) "expect valid database pool")
|
||||||
(assert (some? (::setup/props params)))
|
|
||||||
(assert (session/manager? (::session/manager params)) "expect valid session manager")
|
(assert (session/manager? (::session/manager params)) "expect valid session manager")
|
||||||
(assert (valid-methods? (::methods params)) "expect valid methods map")
|
(assert (valid-methods? (::methods params)) "expect valid methods map")
|
||||||
(assert (valid-methods? (::management-methods params)) "expect valid methods map"))
|
(assert (valid-methods? (::management-methods params)) "expect valid methods map"))
|
||||||
|
|
||||||
(defmethod ig/init-key ::routes
|
(defmethod ig/init-key ::routes
|
||||||
[_ {:keys [::methods ::management-methods ::setup/props] :as cfg}]
|
[_ {:keys [::methods ::management-methods ::setup/shared-keys] :as cfg}]
|
||||||
|
|
||||||
(let [public-uri (cf/get :public-uri)
|
|
||||||
management-key (or (cf/get :management-api-key)
|
|
||||||
(get props :management-key))]
|
|
||||||
|
|
||||||
|
(let [public-uri (cf/get :public-uri)]
|
||||||
["/api"
|
["/api"
|
||||||
["/management"
|
["/management"
|
||||||
["/methods/:type"
|
["/methods/:type"
|
||||||
{:middleware [[mw/shared-key-auth management-key]
|
{:middleware [[mw/shared-key-auth shared-keys]
|
||||||
[session/authz cfg]]
|
[session/authz cfg]]
|
||||||
:handler (make-rpc-handler management-methods)}]
|
:handler (make-rpc-handler management-methods)}]
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
(db/insert-many! pool :audit-log event-columns events))))
|
(db/insert-many! pool :audit-log event-columns events))))
|
||||||
|
|
||||||
(def valid-event-types
|
(def valid-event-types
|
||||||
#{"action" "identify"})
|
#{"action" "identify" "trigger"})
|
||||||
|
|
||||||
(def schema:event
|
(def schema:event
|
||||||
[:map {:title "Event"}
|
[:map {:title "Event"}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
82
backend/src/app/rpc/management/nitrate.clj
Normal file
82
backend/src/app/rpc/management/nitrate.clj
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
;; 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 RPC API. Provides authenticated access to
|
||||||
|
organization management and token validation endpoints."
|
||||||
|
(:require
|
||||||
|
[app.common.schema :as sm]
|
||||||
|
[app.common.types.profile :refer [schema:profile]]
|
||||||
|
[app.common.types.team :refer [schema:team]]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[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
|
||||||
|
|
||||||
|
(sv/defmethod ::authenticate
|
||||||
|
"Authenticate the current user"
|
||||||
|
{::doc/added "2.14"
|
||||||
|
::sm/params [:map]
|
||||||
|
::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 IS TRUE
|
||||||
|
AND t.is_default IS FALSE
|
||||||
|
AND t.deleted_at IS NULL;")
|
||||||
|
|
||||||
|
(def ^:private schema:get-teams-result
|
||||||
|
[:vector schema:team])
|
||||||
|
|
||||||
|
(sv/defmethod ::get-teams
|
||||||
|
"List teams for which current user is owner"
|
||||||
|
{::doc/added "2.14"
|
||||||
|
::sm/params [:map]
|
||||||
|
::sm/result schema:get-teams-result}
|
||||||
|
[cfg {:keys [::rpc/profile-id]}]
|
||||||
|
(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"
|
||||||
|
{::doc/added "2.14"
|
||||||
|
::sm/params schema:notify-team-change
|
||||||
|
::rpc/auth false}
|
||||||
|
[cfg {:keys [id organization-id organization-name]}]
|
||||||
|
(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})))
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
;; 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.subscription
|
|
||||||
(:require
|
|
||||||
[app.common.logging :as l]
|
|
||||||
[app.common.schema :as sm]
|
|
||||||
[app.common.schema.generators :as sg]
|
|
||||||
[app.common.time :as ct]
|
|
||||||
[app.db :as db]
|
|
||||||
[app.rpc :as-alias rpc]
|
|
||||||
[app.rpc.commands.profile :as profile]
|
|
||||||
[app.rpc.doc :as doc]
|
|
||||||
[app.util.services :as sv]))
|
|
||||||
|
|
||||||
;; ---- RPC METHOD: AUTHENTICATE
|
|
||||||
|
|
||||||
(def ^:private
|
|
||||||
schema:authenticate-params
|
|
||||||
[:map {:title "authenticate-params"}])
|
|
||||||
|
|
||||||
(def ^:private
|
|
||||||
schema:authenticate-result
|
|
||||||
[:map {:title "authenticate-result"}
|
|
||||||
[:profile-id ::sm/uuid]])
|
|
||||||
|
|
||||||
(sv/defmethod ::auth
|
|
||||||
{::doc/added "2.12"
|
|
||||||
::sm/params schema:authenticate-params
|
|
||||||
::sm/result schema:authenticate-result}
|
|
||||||
[_ {:keys [::rpc/profile-id]}]
|
|
||||||
{:profile-id profile-id})
|
|
||||||
|
|
||||||
;; ---- RPC METHOD: GET-CUSTOMER
|
|
||||||
|
|
||||||
;; FIXME: move to app.common.time
|
|
||||||
(def ^:private schema:timestamp
|
|
||||||
(sm/type-schema
|
|
||||||
{:type ::timestamp
|
|
||||||
:pred ct/inst?
|
|
||||||
:type-properties
|
|
||||||
{:title "inst"
|
|
||||||
:description "The same as :app.common.time/inst but encodes to epoch"
|
|
||||||
:error/message "should be an instant"
|
|
||||||
:gen/gen (->> (sg/small-int)
|
|
||||||
(sg/fmap (fn [v] (ct/inst v))))
|
|
||||||
:decode/string #(some-> % ct/inst)
|
|
||||||
:encode/string #(some-> % inst-ms)
|
|
||||||
:decode/json #(some-> % ct/inst)
|
|
||||||
:encode/json #(some-> % inst-ms)}}))
|
|
||||||
|
|
||||||
(def ^:private schema:subscription
|
|
||||||
[:map {:title "Subscription"}
|
|
||||||
[:id ::sm/text]
|
|
||||||
[:customer-id ::sm/text]
|
|
||||||
[:type [:enum
|
|
||||||
"unlimited"
|
|
||||||
"professional"
|
|
||||||
"enterprise"]]
|
|
||||||
[:status [:enum
|
|
||||||
"active"
|
|
||||||
"canceled"
|
|
||||||
"incomplete"
|
|
||||||
"incomplete_expired"
|
|
||||||
"past_due"
|
|
||||||
"paused"
|
|
||||||
"trialing"
|
|
||||||
"unpaid"]]
|
|
||||||
|
|
||||||
[:billing-period [:enum
|
|
||||||
"month"
|
|
||||||
"day"
|
|
||||||
"week"
|
|
||||||
"year"]]
|
|
||||||
[:quantity :int]
|
|
||||||
[:description [:maybe ::sm/text]]
|
|
||||||
[:created-at schema:timestamp]
|
|
||||||
[:start-date [:maybe schema:timestamp]]
|
|
||||||
[:ended-at [:maybe schema:timestamp]]
|
|
||||||
[:trial-end [:maybe schema:timestamp]]
|
|
||||||
[:trial-start [:maybe schema:timestamp]]
|
|
||||||
[:cancel-at [:maybe schema:timestamp]]
|
|
||||||
[:canceled-at [:maybe schema:timestamp]]
|
|
||||||
[:current-period-end [:maybe schema:timestamp]]
|
|
||||||
[:current-period-start [:maybe schema:timestamp]]
|
|
||||||
[:cancel-at-period-end :boolean]
|
|
||||||
|
|
||||||
[:cancellation-details
|
|
||||||
[:map {:title "CancellationDetails"}
|
|
||||||
[:comment [:maybe ::sm/text]]
|
|
||||||
[:reason [:maybe ::sm/text]]
|
|
||||||
[:feedback [:maybe
|
|
||||||
[:enum
|
|
||||||
"customer_service"
|
|
||||||
"low_quality"
|
|
||||||
"missing_feature"
|
|
||||||
"other"
|
|
||||||
"switched_service"
|
|
||||||
"too_complex"
|
|
||||||
"too_expensive"
|
|
||||||
"unused"]]]]]])
|
|
||||||
|
|
||||||
(def ^:private sql:get-customer-slots
|
|
||||||
"WITH teams AS (
|
|
||||||
SELECT tpr.team_id AS id,
|
|
||||||
tpr.profile_id AS profile_id
|
|
||||||
FROM team_profile_rel AS tpr
|
|
||||||
WHERE tpr.is_owner IS true
|
|
||||||
AND tpr.profile_id = ?
|
|
||||||
), teams_with_slots AS (
|
|
||||||
SELECT tpr.team_id AS id,
|
|
||||||
count(*) AS total
|
|
||||||
FROM team_profile_rel AS tpr
|
|
||||||
WHERE tpr.team_id IN (SELECT id FROM teams)
|
|
||||||
AND tpr.can_edit IS true
|
|
||||||
GROUP BY 1
|
|
||||||
ORDER BY 2
|
|
||||||
)
|
|
||||||
SELECT max(total) AS total FROM teams_with_slots;")
|
|
||||||
|
|
||||||
(defn- get-customer-slots
|
|
||||||
[cfg profile-id]
|
|
||||||
(let [result (db/exec-one! cfg [sql:get-customer-slots profile-id])]
|
|
||||||
(:total result)))
|
|
||||||
|
|
||||||
(def ^:private schema:get-customer-params
|
|
||||||
[:map])
|
|
||||||
|
|
||||||
(def ^:private schema:get-customer-result
|
|
||||||
[:map
|
|
||||||
[:id ::sm/uuid]
|
|
||||||
[:name :string]
|
|
||||||
[:num-editors ::sm/int]
|
|
||||||
[:subscription {:optional true} schema:subscription]])
|
|
||||||
|
|
||||||
(sv/defmethod ::get-customer
|
|
||||||
{::doc/added "2.12"
|
|
||||||
::sm/params schema:get-customer-params
|
|
||||||
::sm/result schema:get-customer-result}
|
|
||||||
[cfg {:keys [::rpc/profile-id]}]
|
|
||||||
(let [profile (profile/get-profile cfg profile-id)]
|
|
||||||
{:id (get profile :id)
|
|
||||||
:name (get profile :fullname)
|
|
||||||
:email (get profile :email)
|
|
||||||
:num-editors (get-customer-slots cfg profile-id)
|
|
||||||
:subscription (-> profile :props :subscription)}))
|
|
||||||
|
|
||||||
|
|
||||||
;; ---- RPC METHOD: GET-CUSTOMER
|
|
||||||
|
|
||||||
(def ^:private schema:update-customer-params
|
|
||||||
[:map
|
|
||||||
[:subscription [:maybe schema:subscription]]])
|
|
||||||
|
|
||||||
(def ^:private schema:update-customer-result
|
|
||||||
[:map])
|
|
||||||
|
|
||||||
(sv/defmethod ::update-customer
|
|
||||||
{::doc/added "2.12"
|
|
||||||
::sm/params schema:update-customer-params
|
|
||||||
::sm/result schema:update-customer-result}
|
|
||||||
[cfg {:keys [::rpc/profile-id subscription]}]
|
|
||||||
(let [{:keys [props] :as profile}
|
|
||||||
(profile/get-profile cfg profile-id ::db/for-update true)
|
|
||||||
|
|
||||||
props
|
|
||||||
(assoc props :subscription subscription)]
|
|
||||||
|
|
||||||
(l/dbg :hint "update customer"
|
|
||||||
:profile-id (str profile-id)
|
|
||||||
:subscription-type (get subscription :type)
|
|
||||||
:subscription-status (get subscription :status)
|
|
||||||
:subscription-quantity (get subscription :quantity))
|
|
||||||
|
|
||||||
(db/update! cfg :profile
|
|
||||||
{:props (db/tjson props)}
|
|
||||||
{:id profile-id}
|
|
||||||
{::db/return-keys false})
|
|
||||||
|
|
||||||
nil))
|
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
[app.setup.templates]
|
[app.setup.templates]
|
||||||
[buddy.core.codecs :as bc]
|
[buddy.core.codecs :as bc]
|
||||||
[buddy.core.nonce :as bn]
|
[buddy.core.nonce :as bn]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[integrant.core :as ig]))
|
[integrant.core :as ig]))
|
||||||
|
|
||||||
(defn- generate-random-key
|
(defn- generate-random-key
|
||||||
@@ -88,7 +89,38 @@
|
|||||||
(-> (get-all-props conn)
|
(-> (get-all-props conn)
|
||||||
(assoc :secret-key secret)
|
(assoc :secret-key secret)
|
||||||
(assoc :tokens-key (keys/derive secret :salt "tokens"))
|
(assoc :tokens-key (keys/derive secret :salt "tokens"))
|
||||||
(assoc :management-key (keys/derive secret :salt "management"))
|
|
||||||
(update :instance-id handle-instance-id conn (db/read-only? pool)))))))
|
(update :instance-id handle-instance-id conn (db/read-only? pool)))))))
|
||||||
|
|
||||||
(sm/register! ::props [:map-of :keyword ::sm/any])
|
(sm/register! ::props [:map-of :keyword ::sm/any])
|
||||||
|
|
||||||
|
|
||||||
|
(defmethod ig/init-key ::shared-keys
|
||||||
|
[_ {:keys [::props] :as cfg}]
|
||||||
|
(let [secret (get props :secret-key)]
|
||||||
|
(d/without-nils
|
||||||
|
{:exporter
|
||||||
|
(let [key (or (get cfg :exporter)
|
||||||
|
(-> (keys/derive secret :salt "exporter")
|
||||||
|
(bc/bytes->b64-str true)))]
|
||||||
|
(if (or (str/empty? key)
|
||||||
|
(str/blank? key))
|
||||||
|
(do
|
||||||
|
(l/wrn :hint "exporter key is disabled because empty string found")
|
||||||
|
nil)
|
||||||
|
(do
|
||||||
|
(l/inf :hint "exporter key initialized" :key (d/obfuscate-string key))
|
||||||
|
key)))
|
||||||
|
|
||||||
|
:nitrate
|
||||||
|
(let [key (or (get cfg :nitrate)
|
||||||
|
(-> (keys/derive secret :salt "nitrate")
|
||||||
|
(bc/bytes->b64-str true)))]
|
||||||
|
(if (or (str/empty? key)
|
||||||
|
(str/blank? key))
|
||||||
|
(do
|
||||||
|
(l/wrn :hint "nitrate key is disabled because empty string found")
|
||||||
|
nil)
|
||||||
|
(do
|
||||||
|
(l/inf :hint "nitrate key initialized" :key (d/obfuscate-string key))
|
||||||
|
key)))})))
|
||||||
|
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
(t/deftest shared-key-auth
|
(t/deftest shared-key-auth
|
||||||
(let [handler (#'app.http.middleware/wrap-shared-key-auth
|
(let [handler (#'app.http.middleware/wrap-shared-key-auth
|
||||||
(fn [req] {::yres/status 200})
|
(fn [req] {::yres/status 200})
|
||||||
"secret-key")]
|
{:test1 "secret-key"})]
|
||||||
|
|
||||||
(let [response (handler (->DummyRequest {} {}))]
|
(let [response (handler (->DummyRequest {} {}))]
|
||||||
(t/is (= 403 (::yres/status response))))
|
(t/is (= 403 (::yres/status response))))
|
||||||
@@ -95,6 +95,9 @@
|
|||||||
(t/is (= 403 (::yres/status response))))
|
(t/is (= 403 (::yres/status response))))
|
||||||
|
|
||||||
(let [response (handler (->DummyRequest {"x-shared-key" "secret-key"} {}))]
|
(let [response (handler (->DummyRequest {"x-shared-key" "secret-key"} {}))]
|
||||||
|
(t/is (= 403 (::yres/status response))))
|
||||||
|
|
||||||
|
(let [response (handler (->DummyRequest {"x-shared-key" "test1 secret-key"} {}))]
|
||||||
(t/is (= 200 (::yres/status response))))))
|
(t/is (= 200 (::yres/status response))))))
|
||||||
|
|
||||||
(t/deftest access-token-authz
|
(t/deftest access-token-authz
|
||||||
|
|||||||
@@ -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)))))
|
||||||
|
|||||||
@@ -526,20 +526,25 @@
|
|||||||
ids))
|
ids))
|
||||||
|
|
||||||
(defn clean-loops
|
(defn clean-loops
|
||||||
"Clean a list of ids from circular references."
|
"Clean a list of ids from circular references. Optimized fast-path for single selections."
|
||||||
[objects ids]
|
[objects ids]
|
||||||
(let [parent-selected?
|
(if (<= (count ids) 1)
|
||||||
(fn [id]
|
;; For single selection, there can't be circularity; return as ordered-set.
|
||||||
(let [parents (get-parent-ids objects id)]
|
(into (d/ordered-set) ids)
|
||||||
(some ids parents)))
|
(let [ids-set (if (set? ids) ids (set ids))
|
||||||
|
parent-selected?
|
||||||
|
(fn [id]
|
||||||
|
;; Stop early as soon as we find any selected parent
|
||||||
|
(let [parents (get-parent-ids objects id)]
|
||||||
|
(some #(contains? ids-set %) parents)))
|
||||||
|
|
||||||
add-element
|
add-element
|
||||||
(fn [result id]
|
(fn [result id]
|
||||||
(cond-> result
|
(cond-> result
|
||||||
(not (parent-selected? id))
|
(not (parent-selected? id))
|
||||||
(conj id)))]
|
(conj id)))]
|
||||||
|
|
||||||
(reduce add-element (d/ordered-set) ids)))
|
(reduce add-element (d/ordered-set) ids))))
|
||||||
|
|
||||||
(defn- indexed-shapes
|
(defn- indexed-shapes
|
||||||
"Retrieves a vector with the indexes for each element in the layer
|
"Retrieves a vector with the indexes for each element in the layer
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -134,6 +135,8 @@
|
|||||||
:subscriptions
|
:subscriptions
|
||||||
:subscriptions-old
|
:subscriptions-old
|
||||||
:inspect-styles
|
:inspect-styles
|
||||||
|
;; Enable performance logs in devconsole (disabled by default)
|
||||||
|
:perf-logs
|
||||||
|
|
||||||
;; Security layer middleware that filters request by fetch
|
;; Security layer middleware that filters request by fetch
|
||||||
;; metadata headers
|
;; metadata headers
|
||||||
@@ -145,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))
|
||||||
|
|||||||
@@ -124,33 +124,51 @@
|
|||||||
|
|
||||||
(defn adjust-to-viewport
|
(defn adjust-to-viewport
|
||||||
([viewport srect] (adjust-to-viewport viewport srect nil))
|
([viewport srect] (adjust-to-viewport viewport srect nil))
|
||||||
([viewport srect {:keys [padding] :or {padding 0}}]
|
([viewport srect {:keys [padding min-zoom] :or {padding 0 min-zoom nil}}]
|
||||||
(let [gprop (/ (:width viewport)
|
(let [gprop (/ (:width viewport)
|
||||||
(:height viewport))
|
(:height viewport))
|
||||||
srect (-> srect
|
srect-padded (-> srect
|
||||||
(update :x #(- % padding))
|
(update :x #(- % padding))
|
||||||
(update :y #(- % padding))
|
(update :y #(- % padding))
|
||||||
(update :width #(+ % padding padding))
|
(update :width #(+ % padding padding))
|
||||||
(update :height #(+ % padding padding)))
|
(update :height #(+ % padding padding)))
|
||||||
width (:width srect)
|
width (:width srect-padded)
|
||||||
height (:height srect)
|
height (:height srect-padded)
|
||||||
lprop (/ width height)]
|
lprop (/ width height)
|
||||||
(cond
|
adjusted-rect
|
||||||
(> gprop lprop)
|
(cond
|
||||||
(let [width' (* (/ width lprop) gprop)
|
(> gprop lprop)
|
||||||
padding (/ (- width' width) 2)]
|
(let [width' (* (/ width lprop) gprop)
|
||||||
(-> srect
|
padding (/ (- width' width) 2)]
|
||||||
(update :x #(- % padding))
|
(-> srect-padded
|
||||||
(assoc :width width')
|
(update :x #(- % padding))
|
||||||
(grc/update-rect :position)))
|
(assoc :width width')
|
||||||
|
(grc/update-rect :position)))
|
||||||
|
|
||||||
(< gprop lprop)
|
(< gprop lprop)
|
||||||
(let [height' (/ (* height lprop) gprop)
|
(let [height' (/ (* height lprop) gprop)
|
||||||
padding (/ (- height' height) 2)]
|
padding (/ (- height' height) 2)]
|
||||||
(-> srect
|
(-> srect-padded
|
||||||
(update :y #(- % padding))
|
(update :y #(- % padding))
|
||||||
(assoc :height height')
|
(assoc :height height')
|
||||||
(grc/update-rect :position)))
|
(grc/update-rect :position)))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(grc/update-rect srect :position)))))
|
(grc/update-rect srect-padded :position))]
|
||||||
|
;; If min-zoom is specified and the resulting zoom would be below it,
|
||||||
|
;; return a rect with the original top-left corner centered in the viewport
|
||||||
|
;; instead of using the aspect-ratio-adjusted rect (which can push coords
|
||||||
|
;; extremely far with extreme aspect ratios).
|
||||||
|
(if (and (some? min-zoom)
|
||||||
|
(< (/ (:width viewport) (:width adjusted-rect)) min-zoom))
|
||||||
|
(let [anchor-x (:x srect)
|
||||||
|
anchor-y (:y srect)
|
||||||
|
vbox-width (/ (:width viewport) min-zoom)
|
||||||
|
vbox-height (/ (:height viewport) min-zoom)]
|
||||||
|
(-> adjusted-rect
|
||||||
|
(assoc :x (- anchor-x (/ vbox-width 2))
|
||||||
|
:y (- anchor-y (/ vbox-height 2))
|
||||||
|
:width vbox-width
|
||||||
|
:height vbox-height)
|
||||||
|
(grc/update-rect :position)))
|
||||||
|
adjusted-rect))))
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -19,3 +19,10 @@
|
|||||||
|
|
||||||
(def schema:role
|
(def schema:role
|
||||||
[::sm/one-of {:title "TeamRole"} valid-roles])
|
[::sm/one-of {:title "TeamRole"} valid-roles])
|
||||||
|
|
||||||
|
;; FIXME: specify more fields
|
||||||
|
(def schema:team
|
||||||
|
[:map {:title "Team"}
|
||||||
|
[:id ::sm/uuid]
|
||||||
|
[:name :string]])
|
||||||
|
|
||||||
|
|||||||
@@ -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,10 @@
|
|||||||
: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}
|
:min-width #{:sizing :dimensions}
|
||||||
:y #{:spacing :dimensions}
|
:min-height #{:sizing :dimensions}
|
||||||
|
:x #{: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 +489,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 +574,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.
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user