mirror of
https://github.com/penpot/penpot.git
synced 2025-12-28 17:09:03 -05:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8affefbbab | ||
|
|
0225919a45 | ||
|
|
5155cf2b23 | ||
|
|
7403f60366 | ||
|
|
a8c34ccc1a | ||
|
|
8c501db2fa | ||
|
|
d2fbb9dfa7 | ||
|
|
05d6d2fcd4 | ||
|
|
61800d8945 | ||
|
|
f450c9dbe3 | ||
|
|
e3b3fa3342 | ||
|
|
fe04f3e45d | ||
|
|
363c1d5b56 | ||
|
|
3ee3df9b24 | ||
|
|
332657bd1b | ||
|
|
953f770fdd | ||
|
|
c83b9ea305 |
41
CHANGES.md
41
CHANGES.md
@@ -1,14 +1,30 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.5.4
|
||||
|
||||
### :sparkles: New features
|
||||
|
||||
- Add support for WEBP format on shape export [Github #6053](https://github.com/penpot/penpot/pull/6053) and [Github #6074](https://github.com/penpot/penpot/pull/6074)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix feature loading on workspace when opening a file in a background
|
||||
tab [Taiga #10377](https://tree.taiga.io/project/penpot/issue/10377)
|
||||
- Fix minor inconsistencies on RPC `get-file-libraries` and `get-file`
|
||||
methods (add missing team-id prop)
|
||||
- Fix problem with viewer role and inspect mode [Taiga #9751](https://tree.taiga.io/project/penpot/issue/9751)
|
||||
- Fix error when clicking on a comment at the viewer's sidebar [Taiga #10465](https://tree.taiga.io/project/penpot/issue/10465)
|
||||
|
||||
## 2.5.3
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Component sync issues with multiple tabs [Taiga #10471](https://tree.taiga.io/project/penpot/issue/10471)
|
||||
|
||||
## 2.5.2
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
|
||||
### :sparkles: New features
|
||||
|
||||
- When the workspace is empty, set default the board creation tool [Taiga #9425](https://tree.taiga.io/project/penpot/us/9425)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
@@ -20,22 +36,12 @@
|
||||
|
||||
## 2.5.1
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
|
||||
### :sparkles: New features
|
||||
|
||||
- Improve Nginx entryponit to get the resolvers dinamically by default
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
## 2.5.0
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
Although this is not a breaking change, we believe it’s important to highlight it in this
|
||||
@@ -64,9 +70,6 @@ If you have a big database and many cores available, you can reduce the time of
|
||||
all files by increasing paralelizacion changing the `max-jobs` value from 1 to N (where N
|
||||
is a number of cores)
|
||||
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
|
||||
### :sparkles: New features
|
||||
|
||||
- [GRADIENTS] New gradients UI with multi-stop support. [Taiga #3418](https://tree.taiga.io/project/penpot/epic/3418)
|
||||
|
||||
@@ -323,6 +323,7 @@
|
||||
|
||||
file (-> (get-file cfg id :project-id project-id)
|
||||
(assoc :permissions perms)
|
||||
(assoc :team-id (:id team))
|
||||
(check-version!))]
|
||||
|
||||
(-> (cfeat/get-team-enabled-features cf/flags team)
|
||||
@@ -613,6 +614,7 @@
|
||||
SELECT l.id,
|
||||
l.features,
|
||||
l.project_id,
|
||||
p.team_id,
|
||||
l.created_at,
|
||||
l.modified_at,
|
||||
l.deleted_at,
|
||||
@@ -622,6 +624,7 @@
|
||||
l.synced_at,
|
||||
l.is_shared
|
||||
FROM libs AS l
|
||||
INNER JOIN project AS p ON (p.id = l.project_id)
|
||||
WHERE l.deleted_at IS NULL OR l.deleted_at > now();")
|
||||
|
||||
(defn get-file-libraries
|
||||
|
||||
@@ -1722,13 +1722,26 @@
|
||||
(pcb/update-shapes
|
||||
[shape-copy-id]
|
||||
(fn [shape-copy objects]
|
||||
(let [ids-map
|
||||
(let [component-page
|
||||
(ctf/get-component-page main-container main-component)
|
||||
|
||||
component-swap-children
|
||||
(->> shape-main
|
||||
:shapes
|
||||
(map #(get (:objects component-page) %))
|
||||
(filter #(some? (ctk/get-swap-slot %)))
|
||||
(group-by ctk/get-swap-slot))
|
||||
|
||||
ids-map
|
||||
(into {}
|
||||
(comp
|
||||
(map #(get objects %))
|
||||
(keep
|
||||
(fn [copy-shape]
|
||||
(let [main-shape (ctf/get-ref-shape main-container main-component copy-shape)]
|
||||
(let [main-shape
|
||||
(if (some? (ctk/get-swap-slot copy-shape))
|
||||
(first (get component-swap-children (ctk/get-swap-slot copy-shape)))
|
||||
(ctf/get-ref-shape main-container main-component copy-shape))]
|
||||
[(:id main-shape) (:id copy-shape)]))))
|
||||
(:shapes shape-copy))
|
||||
|
||||
@@ -1744,7 +1757,8 @@
|
||||
main-cells (-> shape-main (ctl/remap-grid-cells ids-map) :layout-grid-cells)]
|
||||
(-> shape-copy
|
||||
(assoc :layout-grid-cells
|
||||
(ctl/merge-cells copy-cells main-cells omit-touched?)))))
|
||||
(ctl/merge-cells main-cells copy-cells omit-touched?))
|
||||
(ctl/assign-cells objects))))
|
||||
{:ignore-touched true :with-objects? true})))
|
||||
|
||||
(defn- update-grid-main-attrs
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
(:require
|
||||
[app.common.schema :as sm]))
|
||||
|
||||
(def types #{:png :jpeg :svg :pdf})
|
||||
(def types #{:png :jpeg :webp :svg :pdf})
|
||||
|
||||
(def schema:export
|
||||
[:map {:title "ShapeExport"}
|
||||
|
||||
@@ -1643,15 +1643,10 @@
|
||||
untouched as possible"
|
||||
[target-cells source-cells omit-touched?]
|
||||
(if omit-touched?
|
||||
(letfn [(get-data [cells id]
|
||||
(dissoc (get cells id) :row :column :row-span :column-span))
|
||||
|
||||
(merge-cells [source-cell target-cell]
|
||||
(letfn [(merge-cells [source-cell target-cell]
|
||||
(-> source-cell
|
||||
(d/patch-object
|
||||
(dissoc target-cell :shapes :row :column :row-span :column-span))
|
||||
(cond-> (d/not-empty? (:shapes target-cell))
|
||||
(assoc :shapes (:shapes target-cell)))))]
|
||||
(dissoc target-cell :row :column :row-span :column-span))))]
|
||||
(let [deleted-cells
|
||||
(into #{}
|
||||
(filter #(not (contains? source-cells %)))
|
||||
@@ -1659,10 +1654,7 @@
|
||||
|
||||
touched-cells
|
||||
(into #{}
|
||||
(filter #(and
|
||||
(not (contains? deleted-cells %))
|
||||
(not= (get-data source-cells %)
|
||||
(get-data target-cells %))))
|
||||
(filter #(not (contains? deleted-cells %)))
|
||||
(keys target-cells))]
|
||||
|
||||
(->> touched-cells
|
||||
|
||||
@@ -12,7 +12,7 @@ templateClass: tmpl-contributing-guide
|
||||
{{ show_children(child) }}
|
||||
{%- endif -%}
|
||||
{%- if child.url == page.url -%}
|
||||
{{ content | toc(tags=['h2', 'h3']) | safe }}
|
||||
{{ content | toc(tags=['h2', 'h3']) | stripHash | safe }}
|
||||
{%- endif -%}
|
||||
</li>
|
||||
{%- if loop.last -%}</ul>{%- endif -%}
|
||||
|
||||
@@ -12,7 +12,7 @@ templateClass: tmpl-user-guide
|
||||
{{ show_children(child) }}
|
||||
{%- endif -%}
|
||||
{%- if child.url == page.url -%}
|
||||
{{ content | toc(tags=['h2', 'h3']) | safe }}
|
||||
{{ content | toc(tags=['h2', 'h3']) | stripHash | safe }}
|
||||
{%- endif -%}
|
||||
</li>
|
||||
{%- if loop.last -%}</ul>{%- endif -%}
|
||||
|
||||
@@ -1,548 +0,0 @@
|
||||
---
|
||||
title: 1. Self-hosting Guide
|
||||
---
|
||||
|
||||
# Self-hosting Guide
|
||||
|
||||
This guide explains how to get your own Penpot instance, running on a machine you control,
|
||||
to test it, use it by you or your team, or even customize and extend it any way you like.
|
||||
|
||||
If you need more context you can look at the <a
|
||||
href="https://community.penpot.app/t/self-hosting-penpot-i/2336" target="_blank">post
|
||||
about self-hosting</a> in Penpot community.
|
||||
|
||||
**There is absolutely no difference between <a
|
||||
href="https://design.penpot.app">our SaaS offer</a> for Penpot and your
|
||||
self-hosted Penpot platform!**
|
||||
|
||||
There are three main options for creating a Penpot instance:
|
||||
|
||||
1. Using the platform of our partner <a href="https://elest.io/open-source/penpot" target="_blank">Elestio</a>.
|
||||
2. Using <a href="https://docker.com" target="_blank">Docker</a> tool.
|
||||
3. Using <a href="https://kubernetes.io/" target="_blank">Kubernetes</a>.
|
||||
|
||||
<p class="advice">
|
||||
The recommended way is to use Elestio, since it's simpler, fully automatic and still greatly flexible.
|
||||
Use Docker if you already know the tool, if need full control of the process or have extra requirements
|
||||
and do not want to depend on any external provider, or need to do any special customization.
|
||||
</p>
|
||||
|
||||
Or you can try <a href="#unofficial-self-host-options">other options</a>,
|
||||
offered by Penpot community.
|
||||
|
||||
## Recommended settings
|
||||
To self-host Penpot, you’ll need a server with the following specifications:
|
||||
|
||||
* **CPU:** 1-2 CPUs
|
||||
* **RAM:** 4 GiB of RAM
|
||||
* **Disk Space:** Disk requirements depend on your usage. Disk usage primarily involves the database and any files uploaded by users.
|
||||
|
||||
This setup should be sufficient for a smooth experience with typical usage (your mileage may vary).
|
||||
|
||||
## Install with Elestio
|
||||
|
||||
This section explains how to get Penpot up and running using <a href="https://elest.io/open-source/penpot"
|
||||
target="_blank">Elestio</a>.
|
||||
|
||||
This platform offers a fully managed service for on-premise instances of a selection of
|
||||
open-source software! This means you can deploy a dedicated instance of Penpot in just 3
|
||||
minutes. You’ll be relieved of the need to worry about DNS configuration, SMTP, backups,
|
||||
SSL certificates, OS & Penpot upgrades, and much more.
|
||||
|
||||
It uses the same Docker configuration as the other installation option, below, so all
|
||||
customization options are the same.
|
||||
|
||||
### Get an Elestio account
|
||||
|
||||
<p class="advice">
|
||||
Skip this section if you already have an Elestio account.
|
||||
</p>
|
||||
|
||||
To create your Elestio account <a href="https://dash.elest.io/deploy?soft=Penpot&id=121"
|
||||
target="_blank">click here</a>. You can choose to deploy on any one of five leading cloud
|
||||
providers or on-premise.
|
||||
|
||||
### Deploy Penpot using Elestio
|
||||
|
||||
Now you can Create your service in “Services”:
|
||||
1. Look for Penpot.
|
||||
2. Select a Service Cloud Provider.
|
||||
3. Select Service Cloud Region.
|
||||
4. Select Service Plan (for a team of 20 you should be fine with 2GB RAM).
|
||||
5. Select Elestio Service Support.
|
||||
6. Provide Service Name (this will show in the URL of your instance) & Admin email (used
|
||||
to create the admin account).
|
||||
7. Select Advanced Configuration options (you can also do this later).
|
||||
8. Hit “Create Service” on the bottom right.
|
||||
|
||||
It will take a couple of minutes to get the instance launched. When the status turns to
|
||||
“Service is running” you are ready to get started.
|
||||
|
||||
By clicking on the Service you go to all the details and configuration options.
|
||||
|
||||
In Network/CNAME you can find the URL of your instance. Copy and paste this into a browser
|
||||
and start using Penpot.
|
||||
|
||||
### Configure Penpot with Elestio
|
||||
|
||||
If you want to make changes to your Penpot setup click on the “Update config” button in
|
||||
Software. Here you can see the “Docker compose” used to create the instance. In “ENV” top
|
||||
middle left you can make configuration changes that will be reflected in the Docker
|
||||
compose.
|
||||
|
||||
In this file, a “#” at the start of the line means it is text and not considered part of
|
||||
the configuration. This means you will need to delete it to get some of the configuration
|
||||
options to work. Once you made all your changes hit “Update & restart”. After a couple of
|
||||
minutes, your changes will be active.
|
||||
|
||||
You can find all configuration options in the [Configuration][1] section.
|
||||
|
||||
Get in contact with us through <a href="mailto:support@penpot.app">support@penpot.app</a>
|
||||
if you have any questions or need help.
|
||||
|
||||
|
||||
### Update Penpot
|
||||
|
||||
Elestio will update your instance automatically to the latest release unless you don't
|
||||
want this. In that case you need to “Disable auto updates” in Software auto updates.
|
||||
|
||||
|
||||
## Install with Docker
|
||||
|
||||
This section details everything you need to know to get Penpot up and running in
|
||||
production environments using Docker. For this, we provide a series of *Dockerfiles* and a
|
||||
*docker-compose* file that orchestrate all.
|
||||
|
||||
### Install Docker
|
||||
|
||||
<p class="advice">
|
||||
Skip this section if you already have docker installed, up and running.
|
||||
</p>
|
||||
|
||||
Currently, Docker comes into two different flavours:
|
||||
|
||||
#### Docker Desktop
|
||||
|
||||
This is the only option to have Docker in a Windows or MacOS. Recently it's also available
|
||||
for Linux, in the most popular distributions (Debian, Ubuntu and Fedora).
|
||||
|
||||
You can install it following the <a href="https://docs.docker.com/desktop/"
|
||||
target="_blank">official guide</a>.
|
||||
|
||||
Docker Desktop has a graphical control panel (GUI) to manage the service and view the
|
||||
containers, images and volumes. But need the command line (Terminal in Linux and Mac, or
|
||||
PowerShell in Windows) to build and run the containers, and execute other operations.
|
||||
|
||||
It already includes **docker compose** utility, needed by Penpot.
|
||||
|
||||
#### Docker Engine
|
||||
|
||||
This is the classic and default Docker setup for Linux machines, and the only option for a
|
||||
Linux VPS without graphical interface.
|
||||
|
||||
You can install it following the <a href="https://docs.docker.com/engine/"
|
||||
target="_blank">official guide</a>.
|
||||
|
||||
And you also need the [docker
|
||||
compose](https://docs.docker.com/compose/cli-command/#installing-compose-v2) (V2)
|
||||
plugin. You can use the old **docker-compose** tool, but all the documentation supposes
|
||||
you are using the V2.
|
||||
|
||||
You can easily check which version of **docker compose** you have. If you can execute
|
||||
<code class="language-bash">docker compose</code> command, then you have V2. If you need to write <code class="language-bash">docker-compose</code> (with a
|
||||
<code class="language-bash">-</code>) for it to work, you have the old version.
|
||||
|
||||
### Start Penpot
|
||||
|
||||
As first step you will need to obtain the <code class="language-bash">docker-compose.yaml</code> file. You can download it
|
||||
<a
|
||||
href="https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml"
|
||||
target="_blank">from Penpot repository</a>.
|
||||
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml
|
||||
```
|
||||
or
|
||||
```bash
|
||||
curl -o docker-compose.yaml https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml
|
||||
```
|
||||
|
||||
Then simply launch composer:
|
||||
|
||||
```bash
|
||||
docker compose -p penpot -f docker-compose.yaml up -d
|
||||
```
|
||||
|
||||
At the end it will start listening on http://localhost:9001
|
||||
|
||||
<p class="advice">
|
||||
If you don't change anything, by default this will use the latest image published in dockerhub.
|
||||
</p>
|
||||
|
||||
If you want to have more control over the version (which is recommended), you can use the PENPOT_VERSION envvar in the common ways:
|
||||
- setting the value in the .env file
|
||||
- or passing the envvar in the command line
|
||||
|
||||
```bash
|
||||
PENPOT_VERSION=2.4.3 docker compose -p penpot -f docker-compose.yaml up -d
|
||||
```
|
||||
|
||||
### Stop Penpot
|
||||
|
||||
If you want to stop running Penpot, just type
|
||||
|
||||
```bash
|
||||
docker compose -p penpot -f docker-compose.yaml down
|
||||
```
|
||||
|
||||
### Configure Penpot with Docker
|
||||
|
||||
The configuration is defined using flags and environment variables in the <code class="language-bash">docker-compose.yaml</code>
|
||||
file. The default downloaded file comes with the essential flags and variables already set,
|
||||
and other ones commented out with some explanations.
|
||||
|
||||
You can find all configuration options in the [Configuration][1] section.
|
||||
|
||||
### Using the CLI for administrative tasks
|
||||
|
||||
Penpot provides a script (`manage.py`) with some administrative tasks to perform in the server.
|
||||
|
||||
**NOTE**: this script will only work with the <code class="language-bash">enable-prepl-server</code>
|
||||
flag set in the docker-compose.yaml file. For older versions of docker-compose.yaml file,
|
||||
this flag is set in the backend service.
|
||||
|
||||
For instance, if the registration is disabled, the only way to create a new user is with this script:
|
||||
|
||||
```bash
|
||||
docker exec -ti penpot-penpot-backend-1 python3 manage.py create-profile
|
||||
```
|
||||
|
||||
**NOTE:** the exact container name depends on your docker version and platform.
|
||||
For example it could be <code class="language-bash">penpot-penpot-backend-1</code> or <code class="language-bash">penpot_penpot-backend-1</code>.
|
||||
You can check the correct name executing <code class="language-bash">docker ps</code>.
|
||||
|
||||
### Update Penpot
|
||||
|
||||
To get the latest version of Penpot in your local installation, you just need to
|
||||
execute:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yaml pull
|
||||
```
|
||||
|
||||
This will fetch the latest images. When you do <code class="language-bash">docker compose up</code> again, the containers will be recreated with the latest version.
|
||||
|
||||
<p class="advice">
|
||||
It is strongly recommended to update the Penpot version in small increments, rather than updating between two distant versions.
|
||||
</p>
|
||||
|
||||
**Important: Upgrade from version 1.x to 2.0**
|
||||
|
||||
The migration to version 2.0, due to the incorporation of the new v2 components, includes
|
||||
an additional process that runs automatically as soon as the application starts. If your
|
||||
on-premises Penpot instance contains a significant amount of data (such as hundreds of
|
||||
penpot files, especially those utilizing SVG components and assets extensively), this
|
||||
process may take a few minutes.
|
||||
|
||||
In some cases, such as when the script encounters an error, it may be convenient to run
|
||||
the process manually. To do this, you can disable the automatic migration process using
|
||||
the <code class="language-bash">disable-v2-migration</code> flag in <code
|
||||
class="language-bash">PENPOT_FLAGS</code> environment variable. You can then execute the
|
||||
migration process manually with the following command:
|
||||
|
||||
```bash
|
||||
docker exec -ti <container-name-or-id> ./run.sh app.migrations.v2
|
||||
```
|
||||
|
||||
**IMPORTANT:** this script should be executed on passing from 1.19.x to 2.0.x. Executing
|
||||
it on versions greater or equal to 2.1 of penpot will not work correctly. It is known that
|
||||
this script is removed since 2.4.3
|
||||
|
||||
|
||||
### Backup Penpot
|
||||
|
||||
Penpot uses <a href="https://docs.docker.com/storage/volumes" target="_blank">Docker
|
||||
volumes</a> to store all persistent data. This allows you to delete and recreate
|
||||
containers whenever you want without losing information.
|
||||
|
||||
This also means you need to do regular backups of the contents of the volumes. You cannot
|
||||
directly copy the contents of the volume data folder. Docker provides you a <a
|
||||
href="https://docs.docker.com/storage/volumes/#back-up-restore-or-migrate-data-volumes"
|
||||
target="_blank">volume backup procedure</a>, that uses a temporary container to mount one
|
||||
or more volumes, and copy their data to an archive file stored outside of the container.
|
||||
|
||||
If you use Docker Desktop, <a
|
||||
href="https://www.docker.com/blog/back-up-and-share-docker-volumes-with-this-extension/"
|
||||
target="_blank">there is an extension</a> that may ease the backup process.
|
||||
|
||||
If you use the default **docker compose** file, there are two volumes used: one for the
|
||||
Postgres database and another one for the assets uploaded by your users (images and svg
|
||||
clips). There may be more volumes if you enable other features, as explained in the file
|
||||
itself.
|
||||
|
||||
### Configure the proxy
|
||||
|
||||
Your host configuration needs to make a proxy to http://localhost:9001.
|
||||
|
||||
#### Example with NGINX
|
||||
|
||||
```bash
|
||||
server {
|
||||
listen 80;
|
||||
server_name penpot.mycompany.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name penpot.mycompany.com;
|
||||
|
||||
# This value should be in sync with the corresponding in the docker-compose.yml
|
||||
# PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 31457280
|
||||
client_max_body_size 31457280;
|
||||
|
||||
# Logs: Configure your logs following the best practices inside your company
|
||||
access_log /path/to/penpot.access.log;
|
||||
error_log /path/to/penpot.error.log;
|
||||
|
||||
# TLS: Configure your TLS following the best practices inside your company
|
||||
ssl_certificate /path/to/fullchain;
|
||||
ssl_certificate_key /path/to/privkey;
|
||||
|
||||
# Websockets
|
||||
location /ws/notifications {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_pass http://localhost:9001/ws/notifications;
|
||||
}
|
||||
|
||||
# Proxy pass
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Scheme $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://localhost:9001/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example with CADDY SERVER
|
||||
|
||||
```bash
|
||||
penpot.mycompany.com {
|
||||
reverse_proxy :9001
|
||||
tls /path/to/fullchain.pem /path/to/privkey.pem
|
||||
log {
|
||||
output file /path/to/penpot.log
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Knowing how to do Penpot troubleshooting can be very useful; on the one hand, it helps to create issues easier to resolve, since they include relevant information from the beginning which also makes them get solved faster; on the other hand, many times troubleshooting gives the necessary information to resolve a problem autonomously, without even creating an issue.
|
||||
|
||||
Troubleshooting requires patience and practice; you have to read the stacktrace carefully, even if it looks like a mess at first. It takes some practice to learn how to read the traces properly and extract important information.
|
||||
|
||||
If your Penpot installation is not working as intended, there are several places to look up searching for hints:
|
||||
|
||||
**Docker logs**
|
||||
|
||||
Check if all containers are up and running:
|
||||
```bash
|
||||
docker compose -p penpot -f docker-compose.yaml ps
|
||||
```
|
||||
|
||||
Check logs of all Penpot:
|
||||
```bash
|
||||
docker compose -p penpot -f docker-compose.yaml logs -f
|
||||
```
|
||||
|
||||
If there is too much information and you'd like to check just one service at a time:
|
||||
```bash
|
||||
docker compose -p penpot -f docker-compose.yaml logs penpot-frontend -f
|
||||
```
|
||||
|
||||
You can always check the logs form a specific container:
|
||||
```bash
|
||||
docker logs -f penpot-penpot-postgres-1
|
||||
```
|
||||
|
||||
**Browser logs**
|
||||
|
||||
The browser provides as well useful information to corner the issue.
|
||||
|
||||
First, use the devtools to ensure which version and flags you're using. Go to your Penpot instance in the browser and press F12; you'll see the devtools. In the <code class="language-bash">Console</code>, you can see the exact version that's being used.
|
||||
|
||||
<figure>
|
||||
<a href="/img/dev-tools-1.png" target="_blank">
|
||||
<img src="/img/dev-tools-1.png" alt="Devtools > Console" />
|
||||
</a>
|
||||
</figure>
|
||||
|
||||
Other interesting tab in the devtools is the <code class="language-bash">Network</code> tab, to check if there is a request that throws errors.
|
||||
|
||||
<figure>
|
||||
<a href="/img/dev-tools-2.png" target="_blank">
|
||||
<img src="/img/dev-tools-2.png" alt="Devtools > Network" />
|
||||
</a>
|
||||
</figure>
|
||||
|
||||
**Penpot Report**
|
||||
|
||||
When Penpot crashes, it provides a report with very useful information. Don't miss it!
|
||||
|
||||
<figure>
|
||||
<a href="/img/penpot-report.png" target="_blank">
|
||||
<img src="/img/penpot-report.png" alt="Penpot report" />
|
||||
</a>
|
||||
</figure>
|
||||
|
||||
## Install with Kubernetes
|
||||
|
||||
This section details everything you need to know to get Penpot up and running in
|
||||
production environments using a Kubernetes cluster of your choice. To do this, we have
|
||||
created a <a href="https://helm.sh/" target="_blank">Helm</a> repository with everything
|
||||
you need.
|
||||
|
||||
Therefore, your prerequisite will be to have a Kubernetes cluster on which we can install
|
||||
Helm.
|
||||
|
||||
### What is Helm
|
||||
|
||||
*Helm* is the package manager for Kubernetes. A *Chart* is a Helm package. It contains
|
||||
all of the resource definitions necessary to run an application, tool, or service inside
|
||||
of a Kubernetes cluster. Think of it like the Kubernetes equivalent of a Homebrew
|
||||
formula, an Apt dpkg, or a Yum RPM file.
|
||||
|
||||
A Repository is the place where charts can be collected and shared. It's like Perl's CPAN
|
||||
archive or the Fedora Package Database, but for Kubernetes packages.
|
||||
|
||||
A Release is an instance of a chart running in a Kubernetes cluster. One chart can often
|
||||
be installed many times into the same cluster. And each time it is installed, a new
|
||||
release is created. Consider a MySQL chart. If you want two databases running in your
|
||||
cluster, you can install that chart twice. Each one will have its own release, which will
|
||||
in turn have its own release name.
|
||||
|
||||
With these concepts in mind, we can now explain Helm like this:
|
||||
|
||||
> Helm installs charts into Kubernetes clusters, creating a new release for each
|
||||
> installation. To find new charts, you can search Helm chart repositories.
|
||||
|
||||
|
||||
### Install Helm
|
||||
|
||||
<p class="advice">
|
||||
Skip this section if you already have Helm installed in your system.
|
||||
</p>
|
||||
|
||||
You can install Helm by following the <a href="https://helm.sh/docs/intro/install/" target="_blank">official guide</a>.
|
||||
There are different ways to install Helm, depending on your infrastructure and operating
|
||||
system.
|
||||
|
||||
|
||||
### Add Penpot repository
|
||||
|
||||
To add the Penpot Helm repository, run the following command:
|
||||
|
||||
```bash
|
||||
helm repo add penpot http://helm.penpot.app
|
||||
```
|
||||
|
||||
This will add the Penpot repository to your Helm configuration, so you can install all
|
||||
the Penpot charts stored there.
|
||||
|
||||
|
||||
### Install Penpot Chart
|
||||
|
||||
To install the chart with the release name `my-release`:
|
||||
|
||||
```bash
|
||||
helm install my-release penpot/penpot
|
||||
```
|
||||
|
||||
You can customize the installation specify each parameter using the `--set key=value[,key=value]`
|
||||
argument to helm install. For example,
|
||||
|
||||
```bash
|
||||
helm install my-release \
|
||||
--set global.postgresqlEnabled=true \
|
||||
--set global.redisEnabled=true \
|
||||
--set persistence.assets.enabled=true \
|
||||
penpot/penpot
|
||||
```
|
||||
|
||||
Alternatively, a YAML file that specifies the values for the above parameters can be
|
||||
provided while installing the chart. For example,
|
||||
|
||||
```bash
|
||||
helm install my-release -f values.yaml penpot/penpot
|
||||
```
|
||||
|
||||
|
||||
### Configure Penpot with Helm Chart
|
||||
|
||||
In the previous section we have shown how to configure penpot during installation by
|
||||
using parameters or by using a yaml file.
|
||||
|
||||
The default values are defined in the
|
||||
<a href="https://github.com/penpot/penpot-helm/blob/main/charts/penpot/values.yaml" target="_blank">`values.yml`</a>
|
||||
file itself, which you can use as a basis for creating your own settings.
|
||||
|
||||
You can also consult the list of parameters on the
|
||||
<a href="https://artifacthub.io/packages/helm/penpot/penpot#parameters" target="_blank">ArtifactHub page of the project</a>.
|
||||
|
||||
|
||||
### Upgrade Penpot
|
||||
|
||||
When a new version of Penpot's chart is released, or when you want to change the
|
||||
configuration of your release, you can use the helm upgrade command.
|
||||
|
||||
```bash
|
||||
helm upgrade my-release -f values.yaml penpot/penpot
|
||||
```
|
||||
|
||||
An upgrade takes an existing release and upgrades it according to the information you
|
||||
provide. Because Kubernetes charts can be large and complex, Helm tries to perform the
|
||||
least invasive upgrade. It will only update things that have changed since the last
|
||||
release.
|
||||
|
||||
After each upgrade, a new *revision* will be generated. You can check the revision
|
||||
history of a release with `helm history my-release` and go back to the previous revision
|
||||
if something went wrong with `helm rollback my-release 1` (`1` is the revision number of
|
||||
the previous release revision).
|
||||
|
||||
|
||||
### Backup Penpot
|
||||
|
||||
The Penpot's Helm Chart uses different Persistent Volumes to store all persistent data.
|
||||
This allows you to delete and recreate the instance whenever you want without losing
|
||||
information.
|
||||
|
||||
You back up data from a Persistent Volume via snapshots, so you will want to ensure that
|
||||
your container storage interface (CSI) supports volume snapshots. There are a couple of
|
||||
different options for the CSI driver that you choose. All of the major cloud providers
|
||||
have their respective CSI drivers.
|
||||
|
||||
At last, there are two Persistent Volumes used: one for the Postgres database and another
|
||||
one for the assets uploaded by your users (images and svg clips). There may be more
|
||||
volumes if you enable other features, as explained in the file itself.
|
||||
|
||||
You have to back up your custom settings too (the yaml file or the list of parameters you
|
||||
are using during you setup).
|
||||
|
||||
|
||||
## Unofficial self-host options
|
||||
|
||||
There are some other options, **NOT SUPPORTED BY PENPOT**:
|
||||
|
||||
* Install with <a href="https://community.penpot.app/t/how-to-develop-penpot-with-podman-penpotman/2113" target="_blank">Podman</a> instead of Docker.
|
||||
* Try the under development <a href="https://github.com/author-more/penpot-desktop/releases/latest" target="_blank">Penpot Desktop app</a>.
|
||||
* Try a simple Kubernetes Deployment option <a href="https://github.com/degola/penpot-kubernetes" target="_blank">penpot-kubernetes</a>.
|
||||
* Or try a fully manual installation if you have a really specific use case.. For help, you can look at the [Architecture][2] section and the <a href="https://github.com/penpot/penpot/tree/develop/docker/images" target="_blank">Docker configuration files</a>.
|
||||
|
||||
[1]: /technical-guide/configuration/
|
||||
[2]: /technical-guide/developer/architecture
|
||||
239
docs/technical-guide/getting-started/docker.md
Normal file
239
docs/technical-guide/getting-started/docker.md
Normal file
@@ -0,0 +1,239 @@
|
||||
---
|
||||
title: 1.3 Install with Docker
|
||||
---
|
||||
|
||||
# Install with Docker
|
||||
|
||||
This section details everything you need to know to get Penpot up and running in
|
||||
production environments using Docker. For this, we provide a series of *Dockerfiles* and a
|
||||
*docker-compose* file that orchestrate all.
|
||||
|
||||
## Install Docker
|
||||
|
||||
<p class="advice">
|
||||
Skip this section if you already have docker installed, up and running.
|
||||
</p>
|
||||
|
||||
Currently, Docker comes into two different flavours:
|
||||
|
||||
### Docker Desktop
|
||||
|
||||
This is the only option to have Docker in a Windows or MacOS. Recently it's also available
|
||||
for Linux, in the most popular distributions (Debian, Ubuntu and Fedora).
|
||||
|
||||
You can install it following the <a href="https://docs.docker.com/desktop/"
|
||||
target="_blank">official guide</a>.
|
||||
|
||||
Docker Desktop has a graphical control panel (GUI) to manage the service and view the
|
||||
containers, images and volumes. But you need the command line (Terminal in Linux and Mac, or
|
||||
PowerShell in Windows) to build and run the containers, and execute other operations.
|
||||
|
||||
It already includes **docker compose** utility, needed by Penpot.
|
||||
|
||||
### Docker Engine
|
||||
|
||||
This is the classic and default Docker setup for Linux machines, and the only option for a
|
||||
Linux VPS without graphical interface.
|
||||
|
||||
You can install it following the <a href="https://docs.docker.com/engine/"
|
||||
target="_blank">official guide</a>.
|
||||
|
||||
And you also need the [docker
|
||||
compose](https://docs.docker.com/compose/cli-command/#installing-compose-v2) (V2)
|
||||
plugin. You can use the old **docker-compose** tool, but all the documentation supposes
|
||||
you are using the V2.
|
||||
|
||||
You can easily check which version of **docker compose** you have. If you can execute
|
||||
<code class="language-bash">docker compose</code> command, then you have V2. If you need to write <code class="language-bash">docker-compose</code> (with a
|
||||
<code class="language-bash">-</code>) for it to work, you have the old version.
|
||||
|
||||
## Start Penpot
|
||||
|
||||
As a first step you will need to obtain the <code class="language-bash">docker-compose.yaml</code> file. You can download it
|
||||
<a
|
||||
href="https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml"
|
||||
target="_blank">from the Penpot repository</a>.
|
||||
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml
|
||||
```
|
||||
or
|
||||
```bash
|
||||
curl -o docker-compose.yaml https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml
|
||||
```
|
||||
|
||||
Then simply launch composer:
|
||||
|
||||
```bash
|
||||
docker compose -p penpot -f docker-compose.yaml up -d
|
||||
```
|
||||
|
||||
At the end it will start listening on http://localhost:9001
|
||||
|
||||
<p class="advice">
|
||||
If you don't change anything, by default this will use the latest image published in dockerhub.
|
||||
</p>
|
||||
|
||||
If you want to have more control over the version (which is recommended), you can use the PENPOT_VERSION envvar in the common ways:
|
||||
- setting the value in the .env file
|
||||
- or passing the envvar in the command line
|
||||
|
||||
```bash
|
||||
PENPOT_VERSION=2.4.3 docker compose -p penpot -f docker-compose.yaml up -d
|
||||
```
|
||||
|
||||
## Stop Penpot
|
||||
|
||||
If you want to stop running Penpot, just type
|
||||
|
||||
```bash
|
||||
docker compose -p penpot -f docker-compose.yaml down
|
||||
```
|
||||
|
||||
## Configure Penpot with Docker
|
||||
|
||||
The configuration is defined using flags and environment variables in the <code class="language-bash">docker-compose.yaml</code>
|
||||
file. The default downloaded file comes with the essential flags and variables already set,
|
||||
and other ones commented out with some explanations.
|
||||
|
||||
You can find all configuration options in the [Configuration][1] section.
|
||||
|
||||
## Using the CLI for administrative tasks
|
||||
|
||||
Penpot provides a script (`manage.py`) with some administrative tasks to perform in the server.
|
||||
|
||||
**NOTE**: this script will only work with the <code class="language-bash">enable-prepl-server</code>
|
||||
flag set in the docker-compose.yaml file. For older versions of docker-compose.yaml file,
|
||||
this flag is set in the backend service.
|
||||
|
||||
For instance, if the registration is disabled, the only way to create a new user is with this script:
|
||||
|
||||
```bash
|
||||
docker exec -ti penpot-penpot-backend-1 python3 manage.py create-profile
|
||||
```
|
||||
|
||||
**NOTE:** the exact container name depends on your docker version and platform.
|
||||
For example it could be <code class="language-bash">penpot-penpot-backend-1</code> or <code class="language-bash">penpot_penpot-backend-1</code>.
|
||||
You can check the correct name executing <code class="language-bash">docker ps</code>.
|
||||
|
||||
## Update Penpot
|
||||
|
||||
To get the latest version of Penpot in your local installation, you just need to
|
||||
execute:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yaml pull
|
||||
```
|
||||
|
||||
This will fetch the latest images. When you do <code class="language-bash">docker compose up</code> again, the containers will be recreated with the latest version.
|
||||
|
||||
<p class="advice">
|
||||
It is strongly recommended to update the Penpot version in small increments, rather than updating between two distant versions.
|
||||
</p>
|
||||
|
||||
**Important: Upgrade from version 1.x to 2.0**
|
||||
|
||||
The migration to version 2.0, due to the incorporation of the new v2 components, includes
|
||||
an additional process that runs automatically as soon as the application starts. If your
|
||||
on-premises Penpot instance contains a significant amount of data (such as hundreds of
|
||||
penpot files, especially those utilizing SVG components and assets extensively), this
|
||||
process may take a few minutes.
|
||||
|
||||
In some cases, such as when the script encounters an error, it may be convenient to run
|
||||
the process manually. To do this, you can disable the automatic migration process using
|
||||
the <code class="language-bash">disable-v2-migration</code> flag in <code
|
||||
class="language-bash">PENPOT_FLAGS</code> environment variable. You can then execute the
|
||||
migration process manually with the following command:
|
||||
|
||||
```bash
|
||||
docker exec -ti <container-name-or-id> ./run.sh app.migrations.v2
|
||||
```
|
||||
|
||||
**IMPORTANT:** this script should be executed on passing from 1.19.x to 2.0.x. Executing
|
||||
it on versions greater or equal to 2.1 of penpot will not work correctly. It is known that
|
||||
this script is removed since 2.4.3
|
||||
|
||||
|
||||
## Backup Penpot
|
||||
|
||||
Penpot uses <a href="https://docs.docker.com/storage/volumes" target="_blank">Docker
|
||||
volumes</a> to store all persistent data. This allows you to delete and recreate
|
||||
containers whenever you want without losing information.
|
||||
|
||||
This also means you need to do regular backups of the contents of the volumes. You cannot
|
||||
directly copy the contents of the volume data folder. Docker provides you a <a
|
||||
href="https://docs.docker.com/storage/volumes/#back-up-restore-or-migrate-data-volumes"
|
||||
target="_blank">volume backup procedure</a>, that uses a temporary container to mount one
|
||||
or more volumes, and copy their data to an archive file stored outside of the container.
|
||||
|
||||
If you use Docker Desktop, <a
|
||||
href="https://www.docker.com/blog/back-up-and-share-docker-volumes-with-this-extension/"
|
||||
target="_blank">there is an extension</a> that may ease the backup process.
|
||||
|
||||
If you use the default **docker compose** file, there are two volumes used: one for the
|
||||
Postgres database and another one for the assets uploaded by your users (images and svg
|
||||
clips). There may be more volumes if you enable other features, as explained in the file
|
||||
itself.
|
||||
|
||||
## Configure the proxy
|
||||
|
||||
Your host configuration needs to make a proxy to http://localhost:9001.
|
||||
|
||||
### Example with NGINX
|
||||
|
||||
```bash
|
||||
server {
|
||||
listen 80;
|
||||
server_name penpot.mycompany.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name penpot.mycompany.com;
|
||||
|
||||
# This value should be in sync with the corresponding in the docker-compose.yml
|
||||
# PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 31457280
|
||||
client_max_body_size 31457280;
|
||||
|
||||
# Logs: Configure your logs following the best practices inside your company
|
||||
access_log /path/to/penpot.access.log;
|
||||
error_log /path/to/penpot.error.log;
|
||||
|
||||
# TLS: Configure your TLS following the best practices inside your company
|
||||
ssl_certificate /path/to/fullchain;
|
||||
ssl_certificate_key /path/to/privkey;
|
||||
|
||||
# Websockets
|
||||
location /ws/notifications {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_pass http://localhost:9001/ws/notifications;
|
||||
}
|
||||
|
||||
# Proxy pass
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Scheme $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://localhost:9001/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example with CADDY SERVER
|
||||
|
||||
```bash
|
||||
penpot.mycompany.com {
|
||||
reverse_proxy :9001
|
||||
tls /path/to/fullchain.pem /path/to/privkey.pem
|
||||
log {
|
||||
output file /path/to/penpot.log
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[1]: /technical-guide/configuration/
|
||||
68
docs/technical-guide/getting-started/elestio.md
Normal file
68
docs/technical-guide/getting-started/elestio.md
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: 1.2 Install with Elestio
|
||||
---
|
||||
|
||||
# Install with Elestio
|
||||
|
||||
This section explains how to get Penpot up and running using <a href="https://elest.io/open-source/penpot"
|
||||
target="_blank">Elestio</a>.
|
||||
|
||||
This platform offers a fully managed service for on-premise instances of a selection of
|
||||
open-source software! This means you can deploy a dedicated instance of Penpot in just 3
|
||||
minutes. You’ll be relieved of the need to worry about DNS configuration, SMTP, backups,
|
||||
SSL certificates, OS & Penpot upgrades, and much more.
|
||||
|
||||
## Get an Elestio account
|
||||
|
||||
<p class="advice">
|
||||
Skip this section if you already have an Elestio account.
|
||||
</p>
|
||||
|
||||
To create your Elestio account <a href="https://dash.elest.io/deploy?soft=Penpot&id=121"
|
||||
target="_blank">click here</a>. You can choose to deploy on any one of five leading cloud
|
||||
providers or on-premise.
|
||||
|
||||
## Deploy Penpot using Elestio
|
||||
|
||||
Now you can Create your service in “Services”:
|
||||
1. Look for Penpot.
|
||||
2. Select a Service Cloud Provider.
|
||||
3. Select Service Cloud Region.
|
||||
4. Select Service Plan (for a team of 20 you should be fine with 2GB RAM).
|
||||
5. Select Elestio Service Support.
|
||||
6. Provide Service Name (this will show in the URL of your instance) & Admin email (used
|
||||
to create the admin account).
|
||||
7. Select Advanced Configuration options (you can also do this later).
|
||||
8. Hit “Create Service” on the bottom right.
|
||||
|
||||
It will take a couple of minutes to get the instance launched. When the status turns to
|
||||
“Service is running” you are ready to get started.
|
||||
|
||||
By clicking on the Service you go to all the details and configuration options.
|
||||
|
||||
In Network/CNAME you can find the URL of your instance. Copy and paste this into a browser
|
||||
and start using Penpot.
|
||||
|
||||
## Configure Penpot with Elestio
|
||||
|
||||
If you want to make changes to your Penpot setup click on the “Update config” button in
|
||||
Software. Here you can see the “Docker compose” used to create the instance. In “ENV” top
|
||||
middle left you can make configuration changes that will be reflected in the Docker
|
||||
compose.
|
||||
|
||||
In this file, a “#” at the start of the line means it is text and not considered part of
|
||||
the configuration. This means you will need to delete it to get some of the configuration
|
||||
options to work. Once you made all your changes hit “Update & restart”. After a couple of
|
||||
minutes, your changes will be active.
|
||||
|
||||
You can find all configuration options in the [Configuration][1] section.
|
||||
|
||||
Get in contact with us through <a href="mailto:support@penpot.app">support@penpot.app</a>
|
||||
if you have any questions or need help.
|
||||
|
||||
## Update Penpot
|
||||
|
||||
Elestio will update your instance automatically to the latest release unless you don't
|
||||
want this. In that case you need to “Disable auto updates” in Software auto updates.
|
||||
|
||||
[1]: /technical-guide/configuration/
|
||||
31
docs/technical-guide/getting-started/index.md
Normal file
31
docs/technical-guide/getting-started/index.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
title: 1. Self-hosting Guide
|
||||
---
|
||||
|
||||
# Self-hosting Guide
|
||||
|
||||
This guide explains how to get your own Penpot instance, running on a machine you control,
|
||||
to test it, use it by you or your team, or even customize and extend it any way you like.
|
||||
|
||||
If you need more context you can look at the <a
|
||||
href="https://community.penpot.app/t/self-hosting-penpot-i/2336" target="_blank">post
|
||||
about self-hosting</a> in Penpot community.
|
||||
|
||||
**There is absolutely no difference between <a
|
||||
href="https://design.penpot.app">our SaaS offer</a> for Penpot and your
|
||||
self-hosted Penpot platform!**
|
||||
|
||||
There are three main options for creating a Penpot instance:
|
||||
|
||||
1. Using the platform of our partner <a href="https://elest.io/open-source/penpot" target="_blank">Elestio</a>.
|
||||
2. Using <a href="https://docker.com" target="_blank">Docker</a> tool.
|
||||
3. Using <a href="https://kubernetes.io/" target="_blank">Kubernetes</a>.
|
||||
|
||||
<p class="advice">
|
||||
The recommended way is to use Elestio, since it's simpler, fully automatic and still greatly flexible.
|
||||
Use Docker if you already know the tool, if need full control of the process or have extra requirements
|
||||
and do not want to depend on any external provider, or need to do any special customization.
|
||||
</p>
|
||||
|
||||
Or you can try <a href="#unofficial-self-host-options">other options</a>,
|
||||
offered by Penpot community.
|
||||
136
docs/technical-guide/getting-started/kubernetes.md
Normal file
136
docs/technical-guide/getting-started/kubernetes.md
Normal file
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: 1.4 Install with Kubernetes
|
||||
---
|
||||
|
||||
# Install with Kubernetes
|
||||
|
||||
This section details everything you need to know to get Penpot up and running in
|
||||
production environments using a Kubernetes cluster of your choice. To do this, we have
|
||||
created a <a href="https://helm.sh/" target="_blank">Helm</a> repository with everything
|
||||
you need.
|
||||
|
||||
Therefore, your prerequisite will be to have a Kubernetes cluster on which we can install
|
||||
Helm.
|
||||
|
||||
## What is Helm
|
||||
|
||||
*Helm* is the package manager for Kubernetes. A *Chart* is a Helm package. It contains
|
||||
all of the resource definitions necessary to run an application, tool, or service inside
|
||||
of a Kubernetes cluster. Think of it like the Kubernetes equivalent of a Homebrew
|
||||
formula, an Apt dpkg, or a Yum RPM file.
|
||||
|
||||
A Repository is the place where charts can be collected and shared. It's like Perl's CPAN
|
||||
archive or the Fedora Package Database, but for Kubernetes packages.
|
||||
|
||||
A Release is an instance of a chart running in a Kubernetes cluster. One chart can often
|
||||
be installed many times into the same cluster. And each time it is installed, a new
|
||||
release is created. Consider a MySQL chart. If you want two databases running in your
|
||||
cluster, you can install that chart twice. Each one will have its own release, which will
|
||||
in turn have its own release name.
|
||||
|
||||
With these concepts in mind, we can now explain Helm like this:
|
||||
|
||||
> Helm installs charts into Kubernetes clusters, creating a new release for each
|
||||
> installation. To find new charts, you can search Helm chart repositories.
|
||||
|
||||
|
||||
## Install Helm
|
||||
|
||||
<p class="advice">
|
||||
Skip this section if you already have Helm installed in your system.
|
||||
</p>
|
||||
|
||||
You can install Helm by following the <a href="https://helm.sh/docs/intro/install/" target="_blank">official guide</a>.
|
||||
There are different ways to install Helm, depending on your infrastructure and operating
|
||||
system.
|
||||
|
||||
|
||||
## Add Penpot repository
|
||||
|
||||
To add the Penpot Helm repository, run the following command:
|
||||
|
||||
```bash
|
||||
helm repo add penpot http://helm.penpot.app
|
||||
```
|
||||
|
||||
This will add the Penpot repository to your Helm configuration, so you can install all
|
||||
the Penpot charts stored there.
|
||||
|
||||
|
||||
## Install Penpot Chart
|
||||
|
||||
To install the chart with the release name `my-release`:
|
||||
|
||||
```bash
|
||||
helm install my-release penpot/penpot
|
||||
```
|
||||
|
||||
You can customize the installation by specifying each parameter using the `--set key=value[,key=value]`
|
||||
argument to helm install. For example,
|
||||
|
||||
```bash
|
||||
helm install my-release \
|
||||
--set global.postgresqlEnabled=true \
|
||||
--set global.redisEnabled=true \
|
||||
--set persistence.assets.enabled=true \
|
||||
penpot/penpot
|
||||
```
|
||||
|
||||
Alternatively, a YAML file that specifies the values for the above parameters can be
|
||||
provided while installing the chart. For example,
|
||||
|
||||
```bash
|
||||
helm install my-release -f values.yaml penpot/penpot
|
||||
```
|
||||
|
||||
|
||||
## Configure Penpot with Helm Chart
|
||||
|
||||
In the previous section we have shown how to configure penpot during installation by
|
||||
using parameters or by using a yaml file.
|
||||
|
||||
The default values are defined in the
|
||||
<a href="https://github.com/penpot/penpot-helm/blob/main/charts/penpot/values.yaml" target="_blank">`values.yml`</a>
|
||||
file itself, which you can use as a basis for creating your own settings.
|
||||
|
||||
You can also consult the list of parameters on the
|
||||
<a href="https://artifacthub.io/packages/helm/penpot/penpot#parameters" target="_blank">ArtifactHub page of the project</a>.
|
||||
|
||||
|
||||
## Upgrade Penpot
|
||||
|
||||
When a new version of Penpot's chart is released, or when you want to change the
|
||||
configuration of your release, you can use the helm upgrade command.
|
||||
|
||||
```bash
|
||||
helm upgrade my-release -f values.yaml penpot/penpot
|
||||
```
|
||||
|
||||
An upgrade takes an existing release and upgrades it according to the information you
|
||||
provide. Because Kubernetes charts can be large and complex, Helm tries to perform the
|
||||
least invasive upgrade. It will only update things that have changed since the last
|
||||
release.
|
||||
|
||||
After each upgrade, a new *revision* will be generated. You can check the revision
|
||||
history of a release with `helm history my-release` and go back to the previous revision
|
||||
if something went wrong with `helm rollback my-release 1` (`1` is the revision number of
|
||||
the previous release revision).
|
||||
|
||||
|
||||
## Backup Penpot
|
||||
|
||||
The Penpot's Helm Chart uses different Persistent Volumes to store all persistent data.
|
||||
This allows you to delete and recreate the instance whenever you want without losing
|
||||
information.
|
||||
|
||||
You back up data from a Persistent Volume via snapshots, so you will want to ensure that
|
||||
your container storage interface (CSI) supports volume snapshots. There are a couple of
|
||||
different options for the CSI driver that you choose. All of the major cloud providers
|
||||
have their respective CSI drivers.
|
||||
|
||||
At last, there are two Persistent Volumes used: one for the Postgres database and another
|
||||
one for the assets uploaded by your users (images and svg clips). There may be more
|
||||
volumes if you enable other features, as explained in the file itself.
|
||||
|
||||
You have to back up your custom settings too (the yaml file or the list of parameters you
|
||||
are using during you setup).
|
||||
13
docs/technical-guide/getting-started/recommended-settings.md
Normal file
13
docs/technical-guide/getting-started/recommended-settings.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
title: 1.1 Recommended Settings
|
||||
---
|
||||
|
||||
# Recommended settings
|
||||
|
||||
To self-host Penpot, you’ll need a server with the following specifications:
|
||||
|
||||
* **CPU:** 1-2 CPUs
|
||||
* **RAM:** 4 GiB of RAM
|
||||
* **Disk Space:** Disk requirements depend on your usage. Disk usage primarily involves the database and any files uploaded by users.
|
||||
|
||||
This setup should be sufficient for a smooth experience with typical usage (your mileage may vary).
|
||||
14
docs/technical-guide/getting-started/unofficial-options.md
Normal file
14
docs/technical-guide/getting-started/unofficial-options.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: 1.5 Unofficial self-host options
|
||||
---
|
||||
|
||||
# Unofficial self-host options
|
||||
|
||||
There are some other options, **NOT SUPPORTED BY PENPOT**:
|
||||
|
||||
* Install with <a href="https://community.penpot.app/t/how-to-develop-penpot-with-podman-penpotman/2113" target="_blank">Podman</a> instead of Docker.
|
||||
* Try the under development <a href="https://github.com/author-more/penpot-desktop/releases/latest" target="_blank">Penpot Desktop app</a>.
|
||||
* Try a simple Kubernetes Deployment option <a href="https://github.com/degola/penpot-kubernetes" target="_blank">penpot-kubernetes</a>.
|
||||
* Or try a fully manual installation if you have a really specific use case.. For help, you can look at the [Architecture][1] section and the <a href="https://github.com/penpot/penpot/tree/develop/docker/images" target="_blank">Docker configuration files</a>.
|
||||
|
||||
[1]: /technical-guide/developer/architecture
|
||||
@@ -37,6 +37,11 @@ Also, if you are a developer, you can get into the code, to explore it, learn ho
|
||||
or extend it and contribute with new functionality. For this, we have a different Docker installation.
|
||||
In the [Developer Guide][6] you can find how to setup a development environment and many other dev-oriented documentation.
|
||||
|
||||
## Troubleshooting Penpot
|
||||
|
||||
The [Troubleshooting][8] section guides you through the different logs in Penpot so you can easily identify
|
||||
any issue that may arise as well as report it comprehensively.
|
||||
|
||||
[1]: /technical-guide/getting-started/#install-with-elestio
|
||||
[2]: /technical-guide/getting-started/#install-with-docker
|
||||
[3]: /technical-guide/configuration/
|
||||
@@ -44,3 +49,4 @@ In the [Developer Guide][6] you can find how to setup a development environment
|
||||
[5]: /technical-guide/integration/
|
||||
[6]: /technical-guide/developer/
|
||||
[7]: /technical-guide/getting-started/#install-with-kubernetes
|
||||
[8]: /technical-guide/troubleshooting/
|
||||
|
||||
62
docs/technical-guide/troubleshooting.md
Normal file
62
docs/technical-guide/troubleshooting.md
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: 5. Troubleshooting Penpot
|
||||
---
|
||||
|
||||
# Troubleshooting Penpot
|
||||
|
||||
Knowing how to do Penpot troubleshooting can be very useful; on the one hand, it helps to create issues easier to resolve,
|
||||
since they include relevant information from the beginning which also makes them get solved faster;
|
||||
on the other hand, many times troubleshooting gives the necessary information to resolve a problem autonomously,
|
||||
without even creating an issue.
|
||||
|
||||
Troubleshooting requires patience and practice; you have to read the stacktrace carefully, even if it looks like a mess at first.
|
||||
It takes some practice to learn how to read the traces properly and extract important information.
|
||||
|
||||
So, if your Penpot installation is not working as intended, there are several places to look up searching for hints.
|
||||
|
||||
## Browser logs
|
||||
|
||||
Regardless of the type of installation you have performed, you can find useful information about Penpot in your browser.
|
||||
|
||||
First, use the devtools to ensure which version and flags you're using. Go to your Penpot instance in the browser and press F12;
|
||||
you'll see the devtools. In the <code class="language-bash">Console</code>, you can see the exact version that's being used.
|
||||
|
||||

|
||||
|
||||
Other interesting tab in the devtools is the <code class="language-bash">Network</code> tab, to check if there is a request that throws errors.
|
||||
|
||||

|
||||
|
||||
## Penpot report
|
||||
|
||||
When Penpot crashes, it provides a report with very useful information. Don't miss it!
|
||||
|
||||

|
||||
|
||||
## Docker logs
|
||||
|
||||
If you are using the Docker installation, this is an easy way to take a look at the logs.
|
||||
|
||||
Check if all containers are up and running:
|
||||
|
||||
```bash
|
||||
docker compose -p penpot -f docker-compose.yaml ps
|
||||
```
|
||||
|
||||
Check logs of all Penpot:
|
||||
|
||||
```bash
|
||||
docker compose -p penpot -f docker-compose.yaml logs -f
|
||||
```
|
||||
|
||||
If there is too much information and you'd like to check just one service at a time:
|
||||
|
||||
```bash
|
||||
docker compose -p penpot -f docker-compose.yaml logs penpot-frontend -f
|
||||
```
|
||||
|
||||
You can always check the logs form a specific container:
|
||||
|
||||
```bash
|
||||
docker logs -f penpot-penpot-postgres-1
|
||||
```
|
||||
@@ -27,7 +27,7 @@ title: 07· Exporting objects
|
||||
<ul>
|
||||
<li><strong>Size</strong> - Options for the most common sizing scales.</li>
|
||||
<li><strong>Suffix</strong> - Especially useful if you are exporting at different scales.</li>
|
||||
<li><strong>File format</strong> - PNG, SVG, JPEG, PDF.</li>
|
||||
<li><strong>File format</strong> - PNG, JPEG, WEBP, SVG, PDF.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="export-multiple-elements">Exporting multiple elements</h2>
|
||||
|
||||
@@ -166,7 +166,7 @@ a design.</p>
|
||||
|
||||
<h2 id="curves">Curves (freehand)</h2>
|
||||
<p>The curve tool allows a path to be created directly in a freehand mode.
|
||||
Select the curve tool by clicking on the icon at the toolbar or pressing <kbd>Ctrl/⌘</kbd> + <kbd>c</kbd>.
|
||||
Select the curve tool by clicking on the icon at the toolbar or pressing <kbd>Shift/⇧</kbd> + <kbd>c</kbd>.
|
||||
<p>The path created will contain a lot of points, but it is edited the same way as any other curve.</p>
|
||||
|
||||
<h2 id="paths">Paths (bezier)</h2>
|
||||
@@ -206,7 +206,7 @@ You can choose to edit individual nodes or create new ones. Press <kbd>Esc</kbd>
|
||||
<h3>Insert images</h3>
|
||||
<p>There are several options for inserting an image into a Penpot file:</p>
|
||||
<ul>
|
||||
<li>Use the <strong>image tool</strong> at the toolbar or press <kbd>K</kbd> to inspect images in your file system.</li>
|
||||
<li>Use the <strong>image tool</strong> at the toolbar or press <kbd>K</kbd> to insert images in your file system.</li>
|
||||
<li><strong>Drag</strong> an image from your computer to the viewport.</li>
|
||||
<li>Copy an image & paste it or drag it right from a <strong>browser</strong>.</li>
|
||||
<li>Drag an image from a Penpot <strong>library</strong>.</li>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::suffix ::us/string)
|
||||
(s/def ::type #{:jpeg :png :pdf :svg})
|
||||
(s/def ::type #{:png :jpeg :webp :pdf :svg})
|
||||
(s/def ::page-id ::us/uuid)
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::share-id ::us/uuid)
|
||||
@@ -40,6 +40,7 @@
|
||||
(case type
|
||||
:png (rb/render params on-object)
|
||||
:jpeg (rb/render params on-object)
|
||||
:webp (rb/render params on-object)
|
||||
:pdf (rp/render params on-object)
|
||||
:svg (rs/render params on-object)))
|
||||
|
||||
|
||||
@@ -34,7 +34,11 @@
|
||||
(bw/wait-for node)
|
||||
(case type
|
||||
:png (bw/screenshot node {:omit-background? true :type type :path path})
|
||||
:jpeg (bw/screenshot node {:omit-background? false :type type :path path}))
|
||||
:jpeg (bw/screenshot node {:omit-background? false :type type :path path})
|
||||
:webp (p/let [png-path (sh/tempfile :prefix "penpot.tmp.render.bitmap." :suffix ".png")]
|
||||
;; playwright only supports jpg and png, we need to convert it afterwards
|
||||
(bw/screenshot node {:omit-background? true :type :png :path png-path})
|
||||
(sh/run-cmd! (str "convert " png-path " -quality 100 WEBP:" path))))
|
||||
(on-object (assoc object :path path))))
|
||||
|
||||
(render [uri page]
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
(case type
|
||||
:png ".png"
|
||||
:jpeg ".jpg"
|
||||
:webp ".webp"
|
||||
:svg ".svg"
|
||||
:pdf ".pdf"
|
||||
:zip ".zip"))
|
||||
@@ -26,6 +27,7 @@
|
||||
:pdf "application/pdf"
|
||||
:svg "image/svg+xml"
|
||||
:jpeg "image/jpeg"
|
||||
:png "image/png"))
|
||||
:png "image/png"
|
||||
:webp "image/webp"))
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,9 @@
|
||||
(declare process-message)
|
||||
|
||||
(defn initialize
|
||||
[]
|
||||
[team-id]
|
||||
(assert (uuid? team-id) "expected uuid instance for `team-id`")
|
||||
|
||||
(ptk/reify ::initialize
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
@@ -46,8 +48,8 @@
|
||||
profile-id (:profile-id state)]
|
||||
|
||||
(->> (rx/merge
|
||||
(rx/of (fetch-projects)
|
||||
(df/fetch-fonts))
|
||||
(rx/of (fetch-projects team-id)
|
||||
(df/fetch-fonts team-id))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dws/message))
|
||||
(rx/map deref)
|
||||
@@ -60,8 +62,8 @@
|
||||
(rx/take-until stopper))))))
|
||||
|
||||
(defn finalize
|
||||
[]
|
||||
(ptk/data-event ::finalize {}))
|
||||
[team-id]
|
||||
(ptk/data-event ::finalize {:team-id team-id}))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Data Fetching (context aware: current team)
|
||||
@@ -69,7 +71,7 @@
|
||||
|
||||
;; --- EVENT: fetch-projects
|
||||
|
||||
(defn projects-fetched
|
||||
(defn- projects-fetched
|
||||
[projects]
|
||||
(ptk/reify ::projects-fetched
|
||||
ptk/UpdateEvent
|
||||
@@ -80,13 +82,12 @@
|
||||
projects))))
|
||||
|
||||
(defn fetch-projects
|
||||
[]
|
||||
[team-id]
|
||||
(ptk/reify ::fetch-projects
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)]
|
||||
(->> (rp/cmd! :get-projects {:team-id team-id})
|
||||
(rx/map projects-fetched))))))
|
||||
(watch [_ _ _]
|
||||
(->> (rp/cmd! :get-projects {:team-id team-id})
|
||||
(rx/map projects-fetched)))))
|
||||
|
||||
;; --- EVENT: search
|
||||
|
||||
@@ -115,7 +116,7 @@
|
||||
|
||||
;; --- EVENT: recent-files
|
||||
|
||||
(defn recent-files-fetched
|
||||
(defn- recent-files-fetched
|
||||
[files]
|
||||
(ptk/reify ::recent-files-fetched
|
||||
ptk/UpdateEvent
|
||||
@@ -126,13 +127,14 @@
|
||||
(update :files d/merge files))))))
|
||||
|
||||
(defn fetch-recent-files
|
||||
[]
|
||||
(ptk/reify ::fetch-recent-files
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)]
|
||||
(->> (rp/cmd! :get-team-recent-files {:team-id team-id})
|
||||
(rx/map recent-files-fetched))))))
|
||||
([] (fetch-recent-files nil))
|
||||
([team-id]
|
||||
(ptk/reify ::fetch-recent-files
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(when-let [team-id (or team-id (:current-team-id state))]
|
||||
(->> (rp/cmd! :get-team-recent-files {:team-id team-id})
|
||||
(rx/map recent-files-fetched)))))))
|
||||
|
||||
;; --- EVENT: fetch-template-files
|
||||
|
||||
|
||||
@@ -266,10 +266,10 @@
|
||||
(defn export-shapes-event
|
||||
[exports origin]
|
||||
(let [types (reduce (fn [counts {:keys [type]}]
|
||||
(if (#{:png :pdf :svg :jpeg} type)
|
||||
(if (#{:png :jpeg :webp :svg :pdf} type)
|
||||
(update counts type inc)
|
||||
counts))
|
||||
{:png 0, :pdf 0, :svg 0, :jpeg 0}
|
||||
{:png 0, :jpeg 0, :webp 0, :pdf 0, :svg 0}
|
||||
exports)]
|
||||
(ptk/event
|
||||
::ev/event (merge types
|
||||
|
||||
@@ -73,13 +73,12 @@
|
||||
(fonts/register! :custom fonts))))))
|
||||
|
||||
(defn fetch-fonts
|
||||
[]
|
||||
(ptk/reify ::load-team-fonts
|
||||
[team-id]
|
||||
(ptk/reify ::fetch-fonts
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)]
|
||||
(->> (rp/cmd! :get-font-variants {:team-id team-id})
|
||||
(rx/map fonts-fetched))))))
|
||||
(watch [_ _ _]
|
||||
(->> (rp/cmd! :get-font-variants {:team-id team-id})
|
||||
(rx/map fonts-fetched)))))
|
||||
|
||||
(defn process-upload
|
||||
"Given a seq of blobs and the team id, creates a ready-to-use fonts
|
||||
|
||||
@@ -64,13 +64,14 @@
|
||||
(update :profiles merge (d/index-by :id members))))))
|
||||
|
||||
(defn fetch-members
|
||||
[]
|
||||
(ptk/reify ::fetch-members
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)]
|
||||
(->> (rp/cmd! :get-team-members {:team-id team-id})
|
||||
(rx/map (partial members-fetched team-id)))))))
|
||||
([] (fetch-members nil))
|
||||
([team-id]
|
||||
(ptk/reify ::fetch-members
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(when-let [team-id (or team-id (:current-team-id state))]
|
||||
(->> (rp/cmd! :get-team-members {:team-id team-id})
|
||||
(rx/map (partial members-fetched team-id))))))))
|
||||
|
||||
(defn- invitations-fetched
|
||||
[team-id invitations]
|
||||
@@ -88,41 +89,20 @@
|
||||
(->> (rp/cmd! :get-team-invitations {:team-id team-id})
|
||||
(rx/map (partial invitations-fetched team-id)))))))
|
||||
|
||||
(defn set-current-team
|
||||
[{:keys [id permissions features] :as team}]
|
||||
(ptk/reify ::set-current-team
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
;; FIXME: redundant operation, only necessary on workspace
|
||||
;; until workspace initialization is refactored
|
||||
(update-in [:teams id] merge team)
|
||||
(assoc :permissions permissions)
|
||||
;; FIXME: this is a redundant operation that only needed by
|
||||
;; workspace; ti will not be needed after workspace
|
||||
;; bootstrap & urls refactor
|
||||
(assoc :current-team-id id)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (features/initialize (or features #{}))))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(swap! storage/global assoc ::current-team-id id))))
|
||||
|
||||
(defn- team-initialized
|
||||
[]
|
||||
[team-id]
|
||||
(ptk/reify ::team-initialized
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)
|
||||
teams (get state :teams)
|
||||
team (get teams team-id)]
|
||||
(let [teams (get state :teams)
|
||||
team (get teams team-id)]
|
||||
(if (not team)
|
||||
(rx/throw (ex/error :type :authentication))
|
||||
(rx/of (set-current-team team)
|
||||
(fetch-members)))))))
|
||||
(let [permissions (get team :permissions)
|
||||
features (get team :features)]
|
||||
(rx/of #(assoc % :permissions permissions)
|
||||
(features/initialize (or features #{}))
|
||||
(fetch-members team-id))))))))
|
||||
|
||||
(defn initialize-team
|
||||
[team-id]
|
||||
@@ -138,8 +118,7 @@
|
||||
(rx/of (fetch-teams))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::teams-fetched))
|
||||
(rx/observe-on :async)
|
||||
(rx/map team-initialized)))
|
||||
(rx/map (partial team-initialized team-id))))
|
||||
(rx/take-until stopper))))))
|
||||
|
||||
(defn finalize-team
|
||||
@@ -169,7 +148,7 @@
|
||||
params (assoc params :team-id team-id)]
|
||||
(->> (rp/cmd! :update-team-member-role params)
|
||||
(rx/mapcat (fn [_]
|
||||
(rx/of (fetch-members)
|
||||
(rx/of (fetch-members team-id)
|
||||
(fetch-teams)
|
||||
(ptk/data-event ::ev/event
|
||||
{::ev/name "update-team-member-role"
|
||||
@@ -187,7 +166,7 @@
|
||||
params (assoc params :team-id team-id)]
|
||||
(->> (rp/cmd! :delete-team-member params)
|
||||
(rx/mapcat (fn [_]
|
||||
(rx/of (fetch-members)
|
||||
(rx/of (fetch-members team-id)
|
||||
(fetch-teams)
|
||||
(ptk/data-event ::ev/event
|
||||
{::ev/name "delete-team-member"
|
||||
|
||||
@@ -291,7 +291,8 @@
|
||||
(watch [_ state stream]
|
||||
(let [features (features/get-team-enabled-features state)
|
||||
render-wasm? (contains? features "render-wasm/v1")
|
||||
stopper-s (rx/filter (ptk/type? ::finalize-workspace) stream)]
|
||||
stopper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
|
||||
team-id (:current-team-id state)]
|
||||
|
||||
(->> (rx/concat
|
||||
;; Firstly load wasm module if it is enabled and fonts
|
||||
@@ -305,7 +306,7 @@
|
||||
(rx/filter (ptk/type? ::df/fonts-loaded))
|
||||
(rx/take 1)
|
||||
(rx/ignore))
|
||||
(rx/of (df/fetch-fonts)))
|
||||
(rx/of (df/fetch-fonts team-id)))
|
||||
|
||||
;; Then fetch file and thumbnails
|
||||
(->> (rx/zip (rp/cmd! :get-file {:id file-id :features features})
|
||||
@@ -335,7 +336,7 @@
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(log/debug :hint "initialize-workspace" :file-id file-id)
|
||||
(log/debug :hint "initialize-workspace" :file-id (dm/str file-id))
|
||||
(let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
|
||||
rparams (rt/get-params state)]
|
||||
|
||||
|
||||
@@ -1186,20 +1186,19 @@
|
||||
(ctf/used-assets-changed-since file-data library sync-date))))))
|
||||
|
||||
(defn notify-sync-file
|
||||
;; file-id is the id of the modified library
|
||||
[file-id]
|
||||
(dm/assert! (uuid? file-id))
|
||||
(ptk/reify ::notify-sync-file
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [file (dsh/lookup-file state file-id)
|
||||
|
||||
(let [file (dsh/lookup-file state (:current-file-id state))
|
||||
file-data (get file :data)
|
||||
ignore-until (get file :ignore-sync-until)
|
||||
|
||||
libraries-need-sync
|
||||
(filter #(seq (assets-need-sync % file-data ignore-until))
|
||||
(vals (get state :files)))
|
||||
|
||||
do-more-info
|
||||
#(modal/show! :libraries-dialog {:starting-tab "updates" :file-id file-id})
|
||||
|
||||
|
||||
@@ -317,6 +317,6 @@
|
||||
(ptk/reify ::handle-library-change
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(when (contains? (:libraries state) file-id)
|
||||
(when (contains? (:files state) file-id)
|
||||
(rx/of (dwl/ext-library-changed file-id modified-at revn changes)
|
||||
(dwl/notify-sync-file file-id))))))
|
||||
|
||||
@@ -210,42 +210,45 @@
|
||||
(swap! storage/session dissoc :plugin-url))))))
|
||||
|
||||
(defn use-templates-import
|
||||
[can-edit? template-url default-project-id]
|
||||
(mf/with-layout-effect
|
||||
[can-edit? template-url default-project-id]
|
||||
(when (and (some? template-url) (some? default-project-id))
|
||||
(if can-edit?
|
||||
(let [valid-url? (and (str/ends-with? template-url ".penpot")
|
||||
(str/starts-with? template-url cf/templates-uri))
|
||||
template-name (when valid-url? (subs template-url (count cf/templates-uri)))
|
||||
on-import #(st/emit! (dpj/fetch-files default-project-id)
|
||||
(dd/fetch-recent-files)
|
||||
(dd/fetch-projects)
|
||||
(dd/clear-selected-files)
|
||||
(ptk/event ::ev/event {::ev/name "install-template-from-link-finished"
|
||||
:name template-name
|
||||
:url template-url}))]
|
||||
(if valid-url?
|
||||
(do
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "install-template-from-link" :name template-name :url template-url}))
|
||||
(->> (http/send! {:method :get
|
||||
:uri template-url
|
||||
:response-type :blob
|
||||
:omit-default-headers true})
|
||||
(rx/subs!
|
||||
(fn [result]
|
||||
(if (or (< (:status result) 200) (>= (:status result) 300))
|
||||
(st/emit! (notif/error (tr "dashboard.import.error")))
|
||||
(st/emit! (modal/show
|
||||
{:type :import
|
||||
:project-id default-project-id
|
||||
:entries [{:name template-name :uri (wapi/create-uri (:body result))}]
|
||||
:on-finish-import on-import})))))))
|
||||
(st/emit! (notif/error (tr "dashboard.import.bad-url")))))
|
||||
(st/emit! (notif/error (tr "dashboard.import.no-perms"))))
|
||||
[can-edit? template-url project]
|
||||
(let [project-id (get project :id)
|
||||
team-id (get project :team-id)]
|
||||
(mf/with-layout-effect [can-edit? template-url project-id team-id]
|
||||
(when (and (some? template-url)
|
||||
(some? project-id)
|
||||
(some? team-id))
|
||||
(if can-edit?
|
||||
(let [valid-url? (and (str/ends-with? template-url ".penpot")
|
||||
(str/starts-with? template-url cf/templates-uri))
|
||||
template-name (when valid-url? (subs template-url (count cf/templates-uri)))
|
||||
on-import #(st/emit! (dpj/fetch-files project-id)
|
||||
(dd/fetch-recent-files team-id)
|
||||
(dd/fetch-projects team-id)
|
||||
(dd/clear-selected-files)
|
||||
(ptk/event ::ev/event {::ev/name "install-template-from-link-finished"
|
||||
:name template-name
|
||||
:url template-url}))]
|
||||
(if valid-url?
|
||||
(do
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "install-template-from-link" :name template-name :url template-url}))
|
||||
(->> (http/send! {:method :get
|
||||
:uri template-url
|
||||
:response-type :blob
|
||||
:omit-default-headers true})
|
||||
(rx/subs!
|
||||
(fn [result]
|
||||
(if (or (< (:status result) 200) (>= (:status result) 300))
|
||||
(st/emit! (notif/error (tr "dashboard.import.error")))
|
||||
(st/emit! (modal/show
|
||||
{:type :import
|
||||
:project-id project-id
|
||||
:entries [{:name template-name :uri (wapi/create-uri (:body result))}]
|
||||
:on-finish-import on-import})))))))
|
||||
(st/emit! (notif/error (tr "dashboard.import.bad-url")))))
|
||||
(st/emit! (notif/error (tr "dashboard.import.no-perms"))))
|
||||
|
||||
(binding [storage/*sync* true]
|
||||
(swap! storage/session dissoc :template-url)))))
|
||||
(binding [storage/*sync* true]
|
||||
(swap! storage/session dissoc :template-url))))))
|
||||
|
||||
(mf/defc dashboard*
|
||||
{::mf/props :obj}
|
||||
@@ -270,10 +273,10 @@
|
||||
|
||||
(hooks/use-shortcuts ::dashboard sc/shortcuts)
|
||||
|
||||
(mf/with-effect []
|
||||
(st/emit! (dd/initialize))
|
||||
(mf/with-effect [team-id]
|
||||
(st/emit! (dd/initialize team-id))
|
||||
(fn []
|
||||
(st/emit! (dd/finalize))))
|
||||
(st/emit! (dd/finalize team-id))))
|
||||
|
||||
(mf/with-effect []
|
||||
(let [key (events/listen goog/global "keydown"
|
||||
@@ -285,7 +288,7 @@
|
||||
(events/unlistenByKey key))))
|
||||
|
||||
(use-plugin-register plugin-url team-id (:id default-project))
|
||||
(use-templates-import can-edit? template-url (:id default-project))
|
||||
(use-templates-import can-edit? template-url default-project)
|
||||
|
||||
[:& (mf/provider ctx/current-project-id) {:value project-id}
|
||||
[:> modal-container*]
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
(st/emit! (dcm/go-to-dashboard-files
|
||||
{:project-id project-id
|
||||
:team-id team-id}))
|
||||
(st/emit! (dd/fetch-recent-files)
|
||||
(st/emit! (dd/fetch-recent-files team-id)
|
||||
(dd/clear-selected-files))))
|
||||
|
||||
on-move-accept
|
||||
|
||||
@@ -566,8 +566,9 @@
|
||||
|
||||
on-finish-import
|
||||
(mf/use-fn
|
||||
(mf/deps team-id)
|
||||
(fn []
|
||||
(st/emit! (dd/fetch-recent-files)
|
||||
(st/emit! (dd/fetch-recent-files team-id)
|
||||
(dd/clear-selected-files))))
|
||||
|
||||
import-files (use-import-file project-id on-finish-import)
|
||||
@@ -608,9 +609,10 @@
|
||||
|
||||
on-drop-success
|
||||
(mf/use-fn
|
||||
(mf/deps team-id)
|
||||
(fn []
|
||||
(st/emit! (ntf/success (tr "dashboard.success-move-file"))
|
||||
(dd/fetch-recent-files)
|
||||
(dd/fetch-recent-files team-id)
|
||||
(dd/clear-selected-files))))
|
||||
|
||||
on-drop
|
||||
|
||||
@@ -344,11 +344,13 @@
|
||||
|
||||
continue-template
|
||||
(mf/use-fn
|
||||
(mf/deps on-finish-import)
|
||||
(fn [template]
|
||||
(let [on-success
|
||||
(fn [_event]
|
||||
(reset! status* :import-success)
|
||||
(st/emit! (dd/fetch-recent-files)))
|
||||
(when (fn? on-finish-import)
|
||||
(on-finish-import)))
|
||||
|
||||
on-error
|
||||
(fn [cause]
|
||||
@@ -479,8 +481,6 @@
|
||||
[:> import-entry* {:entry (assoc template :status status)
|
||||
:can-be-deleted false}])]
|
||||
|
||||
;; (prn "import-dialog" status)
|
||||
|
||||
[:div {:class (stl/css :modal-footer)}
|
||||
[:div {:class (stl/css :action-buttons)}
|
||||
(when (= :analyze status)
|
||||
|
||||
@@ -105,7 +105,8 @@
|
||||
[{:keys [project is-first team files can-edit]}]
|
||||
(let [locale (mf/deref i18n/locale)
|
||||
|
||||
project-id (:id project)
|
||||
project-id (get project :id)
|
||||
team-id (get team :id)
|
||||
|
||||
file-count (or (:count project) 0)
|
||||
is-draft? (:is-default project)
|
||||
@@ -191,11 +192,11 @@
|
||||
|
||||
on-import
|
||||
(mf/use-fn
|
||||
(mf/deps project-id)
|
||||
(mf/deps project-id team-id)
|
||||
(fn []
|
||||
(st/emit! (dpj/fetch-files project-id)
|
||||
(dd/fetch-recent-files)
|
||||
(dd/fetch-projects)
|
||||
(dd/fetch-recent-files team-id)
|
||||
(dd/fetch-projects team-id)
|
||||
(dd/clear-selected-files))))
|
||||
|
||||
handle-create-click
|
||||
@@ -317,6 +318,8 @@
|
||||
(sort-by :modified-at)
|
||||
(reverse)))
|
||||
|
||||
team-id (get team :id)
|
||||
|
||||
recent-map (mf/deref ref:recent-files)
|
||||
permisions (:permissions team)
|
||||
|
||||
@@ -327,7 +330,7 @@
|
||||
show-team-hero* (mf/use-state #(get storage/global ::show-team-hero true))
|
||||
show-team-hero? (deref show-team-hero*)
|
||||
|
||||
is-my-penpot (= (:default-team-id profile) (:id team))
|
||||
is-my-penpot (= (:default-team-id profile) team-id)
|
||||
is-defalt-team? (:is-default team)
|
||||
|
||||
on-close
|
||||
@@ -346,8 +349,8 @@
|
||||
(:name team))]
|
||||
(dom/set-html-title (tr "title.dashboard.projects" tname))))
|
||||
|
||||
(mf/with-effect []
|
||||
(st/emit! (dd/fetch-recent-files)
|
||||
(mf/with-effect [team-id]
|
||||
(st/emit! (dd/fetch-recent-files team-id)
|
||||
(dd/clear-selected-files)))
|
||||
|
||||
(when (seq projects)
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
[template team-id project-id default-project-id section]
|
||||
(letfn [(on-finish []
|
||||
(st/emit!
|
||||
(dd/fetch-recent-files team-id)
|
||||
(ptk/event ::ev/event {::ev/name "import-template-finish"
|
||||
::ev/origin "dashboard"
|
||||
:template (:name template)
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
scale-enabled?
|
||||
(mf/use-callback
|
||||
(fn [export]
|
||||
(#{:png :jpeg} (:type export))))
|
||||
(#{:png :jpeg :webp} (:type export))))
|
||||
|
||||
in-progress? (:in-progress xstate)
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
|
||||
format-options [{:value "png" :label "PNG"}
|
||||
{:value "jpeg" :label "JPG"}
|
||||
{:value "webp" :label "WEBP"}
|
||||
{:value "svg" :label "SVG"}
|
||||
{:value "pdf" :label "PDF"}]]
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
.element-group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
grid-template-columns: repeat(9, 1fr);
|
||||
column-gap: $s-4;
|
||||
.action-btn {
|
||||
@extend .button-tertiary;
|
||||
@@ -64,13 +64,13 @@
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
grid-column: span 7;
|
||||
grid-column: span 8;
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
}
|
||||
|
||||
.format-select {
|
||||
grid-column: span 2;
|
||||
grid-column: span 3;
|
||||
padding: 0;
|
||||
|
||||
.dropdown-upwards {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.main.data.comments :as dcmt]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.comments :as dwcm]
|
||||
[app.main.refs :as refs]
|
||||
@@ -112,9 +113,11 @@
|
||||
|
||||
on-thread-click
|
||||
(mf/use-fn
|
||||
(mf/deps page-id)
|
||||
(mf/deps page-id from-viewer)
|
||||
(fn [thread]
|
||||
(st/emit! (dwcm/navigate-to-comment thread))))]
|
||||
(if from-viewer
|
||||
(st/emit! (with-meta (dcmt/open-thread thread) {::ev/origin "viewer"}))
|
||||
(st/emit! (dwcm/navigate-to-comment thread)))))]
|
||||
|
||||
[:div {:class (stl/css-case :comments-section true
|
||||
:from-viewer from-viewer)}
|
||||
|
||||
@@ -568,7 +568,6 @@
|
||||
[{:keys [starting-tab file-id] :as props :or {starting-tab :libraries}}]
|
||||
(let [files (mf/deref refs/files)
|
||||
file (get files file-id)
|
||||
team-id (:team-id file)
|
||||
shared? (:is-shared file)
|
||||
|
||||
linked-libraries
|
||||
@@ -616,8 +615,8 @@
|
||||
:id "updates"
|
||||
:content updates-tab}]]
|
||||
|
||||
(mf/with-effect [team-id]
|
||||
(st/emit! (dtm/fetch-shared-files team-id)))
|
||||
(mf/with-effect []
|
||||
(st/emit! (dtm/fetch-shared-files)))
|
||||
|
||||
[:div {:class (stl/css :modal-overlay)
|
||||
:on-click close-dialog-outside
|
||||
|
||||
@@ -197,16 +197,25 @@
|
||||
:id "inspect"
|
||||
:content inspect-content}])]
|
||||
|
||||
(mf/with-effect [permissions]
|
||||
(when-not (:can-edit permissions)
|
||||
(on-change-tab :inspect)))
|
||||
|
||||
[:div {:class (stl/css :tool-window)}
|
||||
[:> tab-switcher* {:tabs tabs
|
||||
:default-selected "info"
|
||||
:on-change-tab on-change-tab
|
||||
:selected (name options-mode)
|
||||
:class (stl/css :options-tab-switcher)}]]))
|
||||
(if (:can-edit permissions)
|
||||
[:> tab-switcher* {:tabs tabs
|
||||
:default-selected "info"
|
||||
:on-change-tab on-change-tab
|
||||
:selected (name options-mode)
|
||||
:class (stl/css :options-tab-switcher)}]
|
||||
|
||||
[:div {:class (stl/css-case :element-options true
|
||||
:inspect-options true
|
||||
:read-only true)}
|
||||
[:& hrs/right-sidebar {:page-id page-id
|
||||
:objects objects
|
||||
:file-id file-id
|
||||
:frame shape-parent-frame
|
||||
:shapes selected-shapes
|
||||
:on-change-section on-change-section
|
||||
:on-expand on-expand
|
||||
:from :workspace}]])]))
|
||||
|
||||
;; TODO: this need optimizations, selected-objects and
|
||||
;; selected-objects-with-children are derefed always but they only
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
padding-top: $s-8;
|
||||
}
|
||||
|
||||
.read-only {
|
||||
grid-template-areas: "right-sidebar";
|
||||
padding: var(--sp-s);
|
||||
}
|
||||
|
||||
.design-options,
|
||||
.interaction-options {
|
||||
overflow: auto;
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
scale-enabled?
|
||||
(mf/use-fn
|
||||
(fn [export]
|
||||
(#{:png :jpeg} (:type export))))
|
||||
(#{:png :jpeg :webp} (:type export))))
|
||||
|
||||
on-download
|
||||
(mf/use-fn
|
||||
@@ -173,6 +173,7 @@
|
||||
|
||||
format-options [{:value "png" :label "PNG"}
|
||||
{:value "jpeg" :label "JPG"}
|
||||
{:value "webp" :label "WEBP"}
|
||||
{:value "svg" :label "SVG"}
|
||||
{:value "pdf" :label "PDF"}]]
|
||||
|
||||
|
||||
@@ -32,18 +32,18 @@
|
||||
|
||||
.element-group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
grid-template-columns: repeat(9, 1fr);
|
||||
column-gap: $s-4;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
grid-column: span 7;
|
||||
grid-column: span 8;
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
}
|
||||
|
||||
.format-select {
|
||||
grid-column: span 2;
|
||||
grid-column: span 3;
|
||||
padding: 0;
|
||||
|
||||
.dropdown-upwards {
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
show-selrect? (and selrect (empty? drawing) (not text-editing?))
|
||||
show-measures? (and (not transform)
|
||||
(not node-editing?)
|
||||
(or show-distances? mode-inspect?))
|
||||
(or show-distances? mode-inspect? read-only?))
|
||||
show-artboard-names? (contains? layout :display-artboard-names)
|
||||
hide-ui? (contains? layout :hide-ui)
|
||||
show-rulers? (and (contains? layout :rulers) (not hide-ui?))
|
||||
|
||||
@@ -261,7 +261,7 @@
|
||||
:hidden hidden})))
|
||||
|
||||
;; export interface Export {
|
||||
;; type: 'png' | 'jpeg' | 'svg' | 'pdf';
|
||||
;; type: 'png' | 'jpeg' | 'webp' | 'svg' | 'pdf';
|
||||
;; scale: number;
|
||||
;; suffix: string;
|
||||
;; }
|
||||
|
||||
@@ -243,7 +243,7 @@
|
||||
|
||||
|
||||
;; export interface Export {
|
||||
;; type: 'png' | 'jpeg' | 'svg' | 'pdf';
|
||||
;; type: 'png' | 'jpeg' | 'webp' | 'svg' | 'pdf';
|
||||
;; scale: number;
|
||||
;; suffix: string;
|
||||
;; }
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.uri :as u]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.data.team :as dtm]
|
||||
[app.main.features :as features]
|
||||
[app.main.render :as render]
|
||||
[app.main.repo :as repo]
|
||||
@@ -30,6 +29,20 @@
|
||||
|
||||
(log/setup! {:app :info})
|
||||
|
||||
(defn set-current-team
|
||||
[{:keys [id permissions features] :as team}]
|
||||
(ptk/reify ::set-current-team
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc :permissions permissions)
|
||||
(update :teams assoc id team)
|
||||
(assoc :current-team-id id)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (features/initialize (or features #{}))))))
|
||||
|
||||
(defn- fetch-team
|
||||
[& {:keys [file-id]}]
|
||||
(ptk/reify ::fetch-team
|
||||
@@ -37,7 +50,7 @@
|
||||
(watch [_ _ _]
|
||||
(->> (repo/cmd! :get-team {:file-id file-id})
|
||||
(rx/mapcat (fn [team]
|
||||
(rx/of (dtm/set-current-team team)
|
||||
(rx/of (set-current-team team)
|
||||
(ptk/data-event ::team-fetched team))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
Reference in New Issue
Block a user