🎉 Reorganize technical documentation.

This commit is contained in:
Andrey Antukh 2021-03-03 16:32:24 +01:00 committed by Andrés Moya
parent 84bbb29a4e
commit 0b916550ca
21 changed files with 800 additions and 857 deletions

View File

@ -8,7 +8,9 @@ templateClass: tmpl-developer-guide
{%- if loop.first -%}<ul>{%- endif -%}
<li>
<a href="{{ child.url }}">{{ child.data.title }}</a>
{%- if page.url.includes(child.url) -%}
{{ show_children(child) }}
{%- endif -%}
{%- if child.url == page.url -%}
{{ content | toc(tags=['h2', 'h3']) | stripHash | safe }}
{%- endif -%}
@ -19,7 +21,7 @@ templateClass: tmpl-developer-guide
<div class="main-container with-sidebar">
<aside id="stickySidebar" class="sidebar">
{%- set root = '/developer-guide/index' | find -%}
{%- set root = '/technical-guide/index' | find -%}
<div id="toc">
<div class="header mobile" id="toc-title">{{ root.data.title }}</div>
<a class="header" href="{{ root.url }}">{{ root.data.title }}</a>

View File

@ -148,6 +148,7 @@ pre {
padding: 1em;
margin: .5em 0;
background-color: #f6f6f6;
overflow: auto;
}
.highlight-line {
display: block;
@ -370,6 +371,17 @@ a[href].direct-link:focus:visited,
color: var(--primary);
}
[id]::before {
visibility: hidden;
content: '';
}
:target[id]::before {
content: '';
display: block;
visibility: hidden;
height: 94px;
}
/* Particular classes */
@ -381,6 +393,7 @@ a[href].direct-link:focus:visited,
.main-container {
margin: auto;
max-width: 80rem;
min-height: 60vh;
}
.main-container.with-sidebar {
/* display: grid;
@ -635,11 +648,11 @@ a[href].direct-link:focus:visited,
}
.main-container h2 {
margin-top: 0rem;
padding-top: 7rem;
padding-top: 4rem;
}
.main-container h3 {
margin-top: 0rem;
padding-top: 7rem;
padding-top: 2rem;
}

View File

@ -1,253 +0,0 @@
---
title: 2. Configuration guide
---
# Configuration Guide #
This section intends to explain all available configuration options.
## Backend ##
The default approach for pass options to backend application is using
environment variables. Almost all environment variables starts with
the `PENPOT_` prefix.
NOTE: All the examples that comes with values, they represent the
**default** values.
### Configuration Options
#### Database Connection
```bash
PENPOT_DATABASE_USERNAME=penpot
PENPOT_DATABASE_PASSWORD=penpot
PENPOT_DATABASE_URI=postgresql://127.0.0.1/penpot
```
The username and password are optional.
#### Email (SMTP)
```bash
PENPOT_SMTP_DEFAULT_REPLY_TO=no-reply@example.com
PENPOT_SMTP_DEFAULT_FROM=no-reply@example.com
# When not enabled, the emails are printed to the console.
PENPOT_SMTP_ENABLED=false
PENPOT_SMTP_HOST=<host>
PENPOT_SMTP_PORT=25
PENPOT_SMTP_USER=<username>
PENPOT_SMTP_PASSWORD=<password>
PENPOT_SMTP_SSL=false
PENPOT_SMTP_TLS=false
```
#### Storage (assets)
Assets storage is implemented using "plugable" backends. Currently
there are three backends available: `db`, `fs` and `s3` (for AWS S3).
##### fs backend
The default backend is: **fs**.
```bash
PENPOT_STORAGE_BACKEND=fs
PENPOT_STORAGE_FS_DIRECTORY=resources/public/assets`
```
The fs backend is hightly coupled with nginx way to serve files using
`x-accel-redirect` and for correctly configuring it you will need to
touch your nginx config for correctly expose the directory specified
in `PENPOT_STORAGE_FS_DIRECTORY` environment.
For more concrete example look at the devenv nginx configurtion
located in `<repo-root>/docker/devenv/files/nginx.conf`.
**NOTE**: The **fs** storage backend is used for store temporal files
when a user uploads an image and that image need to be processed for
creating thumbnails. So is **hightly recommeded** setting up a correct
directory for this backend independently if it is used as main backend
or not.
##### db backend
In some circumstances or just for convenience you can use the `db`
backend that stores all media uploaded by the user directly inside the
database. This backend, at expenses of some overhead, facilitates the
backups, because with this backend all that you need to backup is the
postgresql database. Convenient for small installations and personal
use.
```bash
PENPOT_STORAGE_BACKEND=db
```
##### s3 backend
And finally, you can use AWS S3 service as backend for assets
storage. For this you will need to have AWS credentials, an bucket and
the region of the bucket.
```bash
AWS_ACCESS_KEY_ID=<you-access-key-id-here>
AWS_SECRET_ACCESS_KEY=<your-secret-access-key-here>
PENPOT_STORAGE_BACKEND=s3
PENPOT_STORAGE_S3_REGION=<aws-region>
PENPOT_STORAGE_S3_BUCKET=<bucket-name>
```
Right now, only `eu-central-1` region is supported. If you need others, open an issue.
#### Redis
The redis configuration is very simple, just provide with a valid redis URI. Redis is used
mainly for websocket notifications coordination.
```bash
PENPOT_REDIS_URI=redis://localhost/0
```
#### HTTP Server
```bash
PENPOT_HTTP_SERVER_PORT=6060
PENPOT_PUBLIC_URI=http://localhost:3449
PENPOT_REGISTRATION_ENABLED=true
# comma-separated domains, defaults to `""` which means that all domains are allowed)
PENPOT_REGISTRATION_DOMAIN_WHITELIST=""
```
When disabling registation, remember to also do it [in frontend](#auth-with-3rd-party-2).
#### Server REPL
The production environment by default starts a server REPL where you
can connect and perform diagnosis operations. For this you will need
`netcat` or `telnet` installed in the server.
```bash
$ rlwrap netcat localhost 6062
user=>
```
The default configuration is:
```bash
PENPOT_SREPL_HOST=127.0.0.1
PENPOT_SREPL_PORT=6062
```
#### Auth with 3rd party
**NOTE**: a part of setting this configuration on backend, frontend
application will also require configuration tweaks for make it work.
##### Google
```bash
PENPOT_GOOGLE_CLIENT_ID=<client-id>
PENPOT_GOOGLE_CLIENT_SECRET=<client-secret>
```
##### Gitlab
```bash
PENPOT_GITLAB_BASE_URI=https://gitlab.com
PENPOT_GITLAB_CLIENT_ID=<client-id>
PENPOT_GITLAB_CLIENT_SECRET=<client-secret>
```
##### Github
```bash
PENPOT_GITHUB_CLIENT_ID=<client-id>
PENPOT_GITHUB_CLIENT_SECRET=<client-secret>
```
##### LDAP
```bash
PENPOT_LDAP_AUTH_HOST=
PENPOT_LDAP_AUTH_PORT=
PENPOT_LDAP_AUTH_VERSION=3
PENPOT_LDAP_BIND_DN=
PENPOT_LDAP_BIND_PASSWORD=
PENPOT_LDAP_AUTH_SSL=false
PENPOT_LDAP_AUTH_STARTTLS=false
PENPOT_LDAP_AUTH_BASE_DN=
PENPOT_LDAP_AUTH_USER_QUERY=(|(uid=$username)(mail=$username))
PENPOT_LDAP_AUTH_USERNAME_ATTRIBUTE=uid
PENPOT_LDAP_AUTH_EMAIL_ATTRIBUTE=mail
PENPOT_LDAP_AUTH_FULLNAME_ATTRIBUTE=displayName
PENPOT_LDAP_AUTH_AVATAR_ATTRIBUTE=jpegPhoto
```
## Frontend ##
In comparison with backend frontend only has a few number of runtime
configuration options and are located in the
`<dist-root>/js/config.js` file. This file is completly optional; if
it exists, it is loaded by the main index.html.
The `config.js` consists in a bunch of globar variables that are read
by the frontend application on the bootstrap.
### Auth with 3rd party
If any of the following variables are defined, they will enable the
corresponding auth button in the login page
```js
var penpotGoogleClientID = "<google-client-id-here>";
var penpotGitlabClientID = "<gitlab-client-id-here>";
var penpotGithubClientID = "<github-client-id-here>";
var penpotLoginWithLDAP = <true|false>;
```
You can disable new user registration, if you want only 3rd party
authorized users:
```js
var penpotRegistrationEnabled = <true|false>;
```
**NOTE:** The configuration should match the backend configuration for
respective services.
### Demo warning and Demo users
It is possible to display a warning message on a demo environment and
disable/enable demo users:
```js
var penpotDemoWarning = <true|false>;
var penpotAllowDemoUsers = <true|false>;
```
**NOTE:** The configuration for demo users should match the backend
configuration.
## Exporter ##
The exporter application only have a single configuration option and
it can be provided using environment variables in the same way as
backend.
```bash
PENPOT_PUBLIC_URI=http://pubic-domain
```
This environment variable indicates where the exporter can access to
the public frontend application (because it uses special pages from it
to render the shapes in the underlying headless web browser).

View File

@ -1,65 +0,0 @@
---
title: 3.4. Common guide
---
# Common guide
This section intends to have articles that related to both frontend
and backend, such as: code style hints, architecture dicisions, etc...
## Assertions
Penpot source code has this types of assertions:
**assert**: just using the clojure builtin `assert` macro.
Example:
```clojure
(assert (number? 3) "optional message")
```
This asserts are only executed on development mode. On production
environment all assets like this will be ignored by runtime.
**spec/assert**: using the `app.common.spec/assert` macro.
Also, if you are using clojure.spec, you have the spec based
`clojure.spec.alpha/assert` macro. In the same way as the
`clojure.core/assert`, on production environment this asserts will be
removed by the compiler/runtime.
Example:
````clojure
(require '[clojure.spec.alpha :as s]
'[app.common.spec :as us])
(s/def ::number number?)
(us/assert ::number 3)
```
In the same way as the `assert` macro, this performs the spec
assertion only on development build. On production this code will
completely removed.
**spec/verify**: An assertion type that is executed always.
Example:
```clojure
(require '[app.common.spec :as us])
(us/verify ::number 3)
```
This macro enables you have assetions on production code.
**Why don't use the `clojure.spec.alpha/assert` instead of the `app.common.spec/assert`?**
The Penpot variant does not peforms additional runtime checks for know
if asserts are disabled in "runtime". As a result it generates much
simplier code at development and production builds.

View File

@ -1,138 +0,0 @@
---
title: 3.1. Development environment
---
# Development environment
## System requirements
You should have `docker` and `docker-compose` installed in your system
in order to set up properly the development enviroment.
In debian like linux distributions you can install it executing:
```bash
sudo apt-get install docker docker-compose
```
Start and enable docker environment:
```bash
sudo systemctl start docker
sudo systemctl enable docker
```
Add your user to the docker group:
```bash
sudo usermod -aG docker $USER
```
And finally, increment user watches:
```
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
```
NOTE: you probably need to login again for group change take the effect.
## Start the devenv
**Requires a minimum knowledge of tmux usage in order to use that
development environment.**
For start it, staying in this repository, execute:
```bash
./manage.sh pull-devenv
./manage.sh run-devenv
```
This will do the following:
- Pulls the latest devenv image.
- Starts all the containers in the background.
- Attaches to the **devenv** container and executes the tmux session.
- The tmux session automatically starts all the necessary services.
You can execute the individual steps manully if you want:
```bash
./manage.sh build-devenv # builds the devenv docker image (not necessary in normal sircumstances)
./manage.sh start-devenv # starts background running containers
./manage.sh run-devenv # enters to new tmux session inside of one of the running containers
./manage.sh stop-devenv # stops background running containers
./manage.sh drop-devenv # removes all the volumes, containers and networks used by the devenv
```
Now having the the container running and tmux open inside the
container, you are free to execute any commands and open many shells
as you want.
You can create a new shell just pressing the **Ctr+b c** shortcut. And
**Ctrl+b w** for switch between windows, **Ctrl+b &** for kill the
current window.
For more info: https://tmuxcheatsheet.com/
### Inside the tmux session
#### gulp
The styles and many related tasks are executed thanks to gulp and they are
executed in the tmux **window 0**. This is a normal gulp watcher with some
additional tasks.
#### shadow-cljs
The frontend build process is located on the tmux **window 1**.
**Shadow-cljs** is used for build and serve the frontend code. For
more information, please refer to `02-Frontend-Developer-Guide.md`.
By default the **window 1** executes the shadow-cljs watch process,
that starts a new JVM/Clojure instance if there is no one running.
Finally, you can start a REPL linked to the instance and the current
connected browser, by opening a third window with `Ctrl+c` and running
`npx shadow-cljs cljs-repl main`.
#### exporter
The exporter app (clojurescript app running in nodejs) is located in
**window 2**, and you can go directly to it using `ctrl+b 2` shortcut.
There you will found the window split in two slices. On the top slice
you will have the build process (using shadow-cljs in the same way as
frontend application), and on the bot slice the script that launeches
the node process.
If some reason scripts does not stars correctly, you can manually
execute `node target/app.js ` to start the exporter app.
#### backend
The backend related environment is located in the tmux **window 3**,
and you can go directly to it using `ctrl+b 2` shortcut.
By default the backend will be started in non-interactive mode for
convenience but you can just press `Ctrl+c` and execute `./scripts/repl`
for start the repl.
On the REPL you have this helper functions:
- `(start)`: start all the environment
- `(stop)`: stops the environment
- `(restart)`: stops, reload and start again.
And many other that are defined in the `dev/user.clj` file.
If some exception is raised when code is reloaded, just use
`(repl/refresh-all)` in order to finish correctly the code swaping and
later use `(restart)` again.
For more information, please refer to: `03-Backend-Guide.md`.

View File

@ -1,17 +0,0 @@
---
title: 3. Core developer guide
---
# Core developer guide
This is a generic "getting started" guide for the Penpot platform. It
intends to explain how to get the development environment up and
running with many additional tips.
The main development environment consists in a docker compose
configuration that starts the external services and the development
container (called **devenv**).
We use tmux script in order to multiplex the single terminal and run
both the backend and frontend in the same container.

View File

@ -1,170 +0,0 @@
---
title: 3.6. Miscellaneous
---
# Miscellaneous
## Collaborative Edition & Persistence protocol
This is a collection of design notes for collaborative edition feature
and persistence protocol (STILL IN CONSTRUCTION).
### Persistence Operations
This is a page data structure:
```clojure
{:version 2
:options {}
:rmap
{:id1 :default
:id2 :default
:id3 :id1}
:objects
{:root
{:type :root
:shapes [:id1 :id2]}
:id1
{:type :canvas
:shapes [:id3]}
:id2 {:type :rect}
:id3 {:type :circle}}}
```
This is a potential list of persistent ops:
```clojure
{:type :mod-obj
:operations [<op>, ...]
{:type :add-obj
:id <uuid>
:parent <uuid>
:obj <shape-object>}
{:type :mod-obj
:id <uuid>
:operations [<op>, ...]}
{:type :mov-obj
:id <uuid>
:frame-id <uuid>}
{:type :del-obj
:id <uuid>}
```
This is a potential list of operations:
```clojure
{:type :set
:attr <any>
:val <any>}
{:type :abs-order
:id <uuid>
:index <int>}
{:type :rel-order
:id <uuid>
:loc <one-of:up,down,top,bottom>}
```
### Ephemeral communication (Websocket protocol)
#### `join`
Sent by clients for notify joining a concrete page-id inside a file.
```clojure
{:type :join
:page-id <id>
:version <number>
}
```
Will cause:
- A posible `:page-changes`.
- Broadcast `:joined` message to all users of the file.
The `joined` message has this aspect:
```clojure
{:type :joined
:page-id <id>
:user-id <id>
}
```
#### `who`
Sent by clients for request the list of users in the channel.
```clojure
{:type :who}
```
Will cause:
- Reply to the client with the current users list:
```clojure
{:type :who
:users #{<id>,...}}
```
This will be sent all the time user joins or leaves the channel for
maintain the frontend updated with the lates participants. This
message is also sent at the beggining of connection from server to
client.
#### `pointer-update`
This is sent by client to server and then, broadcasted to the rest of
channel participants.
```clojure
{:type :pointer-update
:page-id <id>
:x <number>
:y <number>
}
```
The server broadcast message will look like:
```clojure
{:type :pointer-update
:user-id <id>
:page-id <id>
:x <number>
:y <number>
}
```
#### `:page-snapshot`
A message that server sends to client for notify page changes. It can be sent
on `join` and when a page change is commited to the database.
```clojure
{:type :page-snapshot
:user-id <id>
:page-id <id>
:version <number>
:operations [<op>, ...]
}
```
This message is only sent to users that does not perform this change.

View File

@ -1,50 +0,0 @@
---
title: 3.5. Testing guide
---
# Testing guide
## Backend / Common
You can run the tests directly with:
```bash
~/penpot/backend$ clojure -M:dev:tests
```
Alternatively, you can run them from a REPL. First starting a REPL.
```bash
~/penpot/backend$ scripts/repl
```
And then:
```bash
user=> (run-tests)
user=> (run-tests 'namespace)
user=> (run-tests 'namespace/test)
```
## Frontend
Frontend tests have to be compiled first, and then run with node.
```bash
npx shadow-cljs compile tests && node target/tests.js
```
Or run the watch (that automatically runs the test):
```bash
npx shadow-cljs watch tests
```
## Linter
We can execute the linter for the whole codebase with the following command:
```bash
clj-kondo --lint common:backend/src:frontend/src
```

View File

@ -1,4 +0,0 @@
{
"layout": "layouts/developer-guide.njk",
"tags": "developer-guide"
}

View File

@ -1,72 +0,0 @@
---
title: Developer guide
eleventyNavigation:
key: Developer guide
order: 3
---
# Developer guide
This documentation intends to explain how to get penpot application and run it
locally, to test it or make changes to it.
> If you only want to run it or change external parts, the simplest approach is
to use the official docker image, as explained below.
> If you want to modify the core application, see instead the
[Development Environment guide](/developer-guide/core-developer/development-environment).
## Install Docker ##
Skip this section if you already have docker installed, up and running.
You can install docker and its dependencies from your distribution repository
with:
```bash
sudo apt-get install docker docker-compose
```
Or follow installation instructions from docker.com; (for Debian
https://docs.docker.com/engine/install/debian/).
Ensure that the docker is started and optionally enable it to start with the
system:
```bash
sudo systemctl start docker
sudo systemctl enable docker
```
And finally, add your user to the docker group:
```bash
sudo usermod -aG docker $USER
```
This will make use of the docker without `sudo` command all the time.
NOTE: probably you will need to re-login again to make this change take effect.
## Start penpot application ##
You can create it from scratch or take a base from the [penpot repository][1]
[1]: https://raw.githubusercontent.com/penpot/penpot/develop/docker/images/docker-compose.yaml
```bash
wget https://raw.githubusercontent.com/penpot/penpot/develop/docker/images/docker-compose.yaml
```
And then:
```bash
docker-compose -p penpot -f docker-compose.yaml up
```
The docker compose file contains the essential configuration for getting the
application running, and many essential configurations already explained in the
comments. All other configuration options are explained in the [Configuration
guide](/developer-guide/configuration).

View File

@ -18,23 +18,23 @@ eleventyNavigation:
<span class="advice"> WIP </span>
</a>
</li>
<li>
<a href="/developer-guide/">
<h3>Developer guide →</h3>
<p>Comprehensive documentation about architecture, configuration and core.</p>
</a>
</li>
<li>
<a href="/faqs">
<h3>Frequently asked questions →</h3>
<p>Get quick answers to usual questions about "why and how" Penpot.</p>
</a>
</li>
<li>
<a href="/technical-guide/">
<h3>Technical guide →</h3>
<p>Comprehensive documentation about installation, configuration and architecture.</p>
</a>
</li>
<li class="no-link">
<!-- a href="" -->
<h3>Contact us →</h3>
<p>Write us at <a href="mailto:info@penpot.app" target="_blank">info@penpot.app</a> or join the <a href="https://github.com/penpot/penpot/discussions" target="_blank">team discusions</a>.</p>
<!-- p>Write us an email, join team discussions or chat with us.</p-->
<h3>Contact us →</h3>
<p>Write us at <a href="mailto:info@penpot.app" target="_blank">info@penpot.app</a> or join the <a href="https://github.com/penpot/penpot/discussions" target="_blank">team discusions</a>.</p>
<!-- p>Write us an email, join team discussions or chat with us.</p-->
<!-- /a -->
</li>
<li class="github">

View File

@ -1,5 +1,5 @@
---
title: 1. Architecture
title: 3. Architecture
---
# Architecture
@ -97,7 +97,7 @@ worker, that may be used to queue tasks to be scheduled and executed when the
backend is idle. Other tasks are email sending, collecting data for telemetry
and detecting unused media attachment, for removing them from the file storage.
## Broker
## PubSub
To manage subscriptions to a file, to be notified of changes, we use a redis
server as a pub/sub broker. Whenever a user visits a file and opens a

View File

@ -0,0 +1,331 @@
---
title: 2. Configuration
---
# Configuration #
This section intends to explain all available configuration options.
The main and unique approach to configure penpot application is
using environment variables. All penpot related variables start with
`PENPOT_` prefix.
**NOTE**: All the examples that have values represent the
**default** values, and the examples that do not have values are
optional.
## Common ##
This section will list all common configuration that affects backend
and frontend, and they need to be in sync to work correctly.
### Registration ###
Penpot comes with an option to completely disable the registration
process or restrict it to some domains.
If you want to completelly disable registration, use the following
variable in both fontend & backend:
```bash
# backend & frontend
PENPOT_REGISTRATION_ENABLED=false
```
You also can restict the registrations to a closed list of domains:
```bash
# comma separated list of domains (backend only)
PENPOT_REGISTRATION_DOMAIN_WHITELIST=""
```
### Demo users ###
Penpot comes with facilities for fast creation of demo users without the need
of a registration process. The demo users by default have an expiration time of
7 days, and once expired they are completly deleted with all the generated
content. Very useful for testing or demostration purposes.
You can enable demo users using the following variable:
```
# backend & frontend
PENPOT_ALLOW_DEMO_USERS=true
```
### OAuth Providers
#### Google
Allow use google as oauth provider:
On backend:
```bash
# Backend & Frontend
PENPOT_GOOGLE_CLIENT_ID=<client-id>
# Backend only:
PENPOT_GOOGLE_CLIENT_SECRET=<client-secret>
```
#### Gitlab
Allows use gitlab as oauth provider:
```bash
# Backend & Frontend
PENPOT_GITLAB_CLIENT_ID=<client-id>
# Backend only
PENPOT_GITLAB_BASE_URI=https://gitlab.com
PENPOT_GITLAB_CLIENT_SECRET=<client-secret>
```
#### Github
Allows use github as oauth provider:
```bash
# Backend & Frontend
PENPOT_GITHUB_CLIENT_ID=<client-id>
# Backend only
PENPOT_GITHUB_CLIENT_SECRET=<client-secret>
```
### LDAP ###
Penpot comes with support for *Lightweight Directory Access Protocol*
(LDAP). This is the example configuration we use internally for testing
this authentication backend.
```bash
# Backend
PENPOT_LDAP_HOST=ldap
PENPOT_LDAP_PORT=10389
PENPOT_LDAP_SSL=false
PENPOT_LDAP_STARTTLS=false
PENPOT_LDAP_BASE_DN=ou=people,dc=planetexpress,dc=com
PENPOT_LDAP_BIND_DN=cn=admin,dc=planetexpress,dc=com
PENPOT_LDAP_BIND_PASSWORD=GoodNewsEveryone
PENPOT_LDAP_ATTRS_USERNAME=uid
PENPOT_LDAP_ATTRS_EMAIL=mail
PENPOT_LDAP_ATTRS_FULLNAME=cn
PENPOT_LDAP_ATTRS_PHOTO=jpegPhoto
# Frontend
PENPOT_LOGIN_WITH_LDAP=true
```
If you miss something, please open an issue and we discuss it.
## Backend ##
This section enumerates the backend only configuration variables.
### Database
We only support PostgreSQL and we highly recommend >=13 version. If
you are using official docker images this is already solved for you.
Essential database configuration:
```bash
PENPOT_DATABASE_USERNAME=penpot
PENPOT_DATABASE_PASSWORD=penpot
PENPOT_DATABASE_URI=postgresql://127.0.0.1/penpot
```
The username and password are optional.
### Email (SMTP)
By default, when no SMTP (email) is configured, the email will be printed to
the console, which means that the emails will be shown in the stdout. If you
have an SMTP service, uncomment the appropiate settings section in
`docker-compose.yml` and configure those environment variables.
Setting up the default FROM and REPLY-TO:
```bash
PENPOT_SMTP_DEFAULT_REPLY_TO=Penpot <no-reply@example.com>
PENPOT_SMTP_DEFAULT_FROM=Penpot <no-reply@example.com>
```
Enable SMTP:
```bash
PENPOT_SMTP_ENABLED=true
PENPOT_SMTP_HOST=<host>
PENPOT_SMTP_PORT=587
PENPOT_SMTP_USER=<username>
PENPOT_SMTP_PASSWORD=<password>
PENPOT_SMTP_TLS=true
```
### Storage
Storage refers to storage used for store the user uploaded assets.
Assets storage is implemented using "plugable" backends. Currently
there are three backends available: `db`, `fs` and `s3` (for AWS S3).
#### FS Backend (default) ####
This is the default backend when you use the official docker images and
the default configuration looks like this:
```bash
PENPOT_STORAGE_BACKEND=fs
PENPOT_STORAGE_FS_DIRECTORY=/opt/data/assets
```
The main downside of this backend is the hard dependency on nginx
approach to serve files managed by an application (not a simple
directory serving static files). But you should not worry about this
unless you want to install it outside the docker container and
configure the nginx yourself.
In case you want undestand how it internally works, you can take a look
on the [nginx configuration file][1] used in the docker images.
[1]: https://github.com/penpot/penpot/blob/main/docker/images/files/nginx.conf
#### DB Backend ####
This backend stores all the user uploaded assets on the database and
is ideal for small setups when you don't want to worry about backups
apart from the database.
In case you want to use this backend, proceed using the following
environment variable:
```bash
PENPOT_STORAGE_BACKEND=db
```
#### AWS S3 Backend ####
This backend uses AWS S3 bucket for store the user uploaded
assets. For use it you should have an appropriate account on AWS cloud
and have the credentials, region and the bucket.
This is how configuration looks for S3 backend:
```bash
# AWS Credentials
AWS_ACCESS_KEY_ID=<you-access-key-id-here>
AWS_SECRET_ACCESS_KEY=<your-secret-access-key-here>
# Backend configuration
PENPOT_STORAGE_BACKEND=s3
PENPOT_STORAGE_S3_REGION=<aws-region>
PENPOT_STORAGE_S3_BUCKET=<bucket-name>
```
Right now, only `eu-central-1` region is supported. If you need
others, open an issue.
### Redis
The redis configuration is very simple, just provide with a valid
redis URI. Redis is used mainly for websocket notifications
coordination.
```bash
PENPOT_REDIS_URI=redis://localhost/0
```
If you are using the official docker compose file, this is already
configured.
### HTTP
You can set the port where the backend http server will listen for requests.
```bash
PENPOT_HTTP_SERVER_PORT=6060
```
Additionally, you probably will need to set the `PENPOT_PUBLIC_URI`
environment variable in case you go to serve penpot to the users, and
it should point to public URI where users will access the application:
```bash
PENPOT_PUBLIC_URI=http://localhost:9001
```
### Server REPL
This is a more advanced setting, because it allows to set a port where the
server REPL will listen. Server REPL is very useful for performing diagnosis,
executing code on the running process, and/or making hotfixes of isolated pure
functions without restarting the process.
The default configuration is:
```bash
PENPOT_SREPL_HOST=127.0.0.1
PENPOT_SREPL_PORT=6062
```
You can connect to the repl using `netcat` or `telnet` in combination
with `rlwrap`. Example:
```bash
$ rlwrap netcat localhost 6062
user=>
```
## Frontend ##
In comparison with backend, frontend only has a small number of runtime
configuration options, and they are located in the `<dist>/js/config.js`
file.
If you are using the official docker images, the best approach to set
any configuration is using environment variables, and the image
automatically generates the `config.js` from them.
**NOTE**: many frontend related configuration variables are explained in the
[Common](#common) section, this section explains **frontend only** options.
### Demo warning ###
If you want to show a warning in the register and login page saying
that this is a demostration purpose instance (no backups, periodical
data wipe, ...), set the following variable:
```bash
PENPOT_DEMO_WARNING=true
```
## Exporter ##
The exporter application only have a single configuration option and
it can be provided using environment variables in the same way as
backend.
```bash
PENPOT_PUBLIC_URI=http://pubic-domain
```
This environment variable indicates where the exporter can access to
the public frontend application (because it uses special pages from it
to render the shapes in the underlying headless web browser).

View File

@ -1,14 +1,14 @@
---
title: 3.3. Backend guide
title: 4.4. Backend Guide
---
# Backend guide
# Backend guide #
This guide intends to explain the essential details of the backend
application.
## Fixtures
## Fixtures ##
This is a development feature that allows populate the database with a
good amount of content (usually used for just test the application or
@ -34,22 +34,8 @@ in the aplication. All users uses the following pattern:
Where `N` is a number from 0 to 5 on the default fixture parameters.
If you have a REPL access to the running process, you can execute it
from there:
```clojure
(require 'app.cli.fixtures)
(app.cli.fixtures/run :small)
```
To access to the running process repl you usually will execute this
command:
```bash
rlwrap netcat localhost 6062
```
## Migrations
## Migrations ##
The database migrations are located in two directories:
@ -69,7 +55,7 @@ Examples:
0026-mod-profile-table-add-is-active-field
```
**NOTE**: if table name has more than one words, we still use `-` as a separator.
**NOTE**: if table name has more than one word, we still use `-` as a separator.
If you need to have a global overview of the all schema of the database you can extract it
using postgresql:
@ -79,3 +65,40 @@ using postgresql:
pg_dump -h postgres -s > schema.sql
```
## Tests ##
You can run the tests directly with:
```bash
~/penpot/backend$ clojure -M:dev:tests
```
Alternatively, you can run them from a REPL. First start a REPL.
```bash
~/penpot/backend$ scripts/repl
```
And then:
```bash
user=> (run-tests)
user=> (run-tests 'namespace)
user=> (run-tests 'namespace/test)
```
## Linter ##
There are no watch process for the linter; you will need to execute it
manually. We use [clj-kondo][kondo] for linting purposes and the
repository already comes with base configuration.
[kondo]: https://github.com/clj-kondo/clj-kondo
You can run **clj-kondo** as-is (is included in the devenv image):
```bash
cd penpot/backend;
clj-kondo --lint src
```

View File

@ -0,0 +1,67 @@
---
title: 4.2. Common Guide
---
# Common guide
This section has articles related to all submodules (frontend, backend and
exporter) such as: code style hints, architecture decisions, etc...
## Assertions
Penpot source code has this types of assertions:
### **assert**
Just using the clojure builtin `assert` macro.
Example:
```clojure
(assert (number? 3) "optional message")
```
This asserts are only executed in development mode. In production
environment all asserts like this will be ignored by runtime.
### **spec/assert**
Using the `app.common.spec/assert` macro.
Also, if you are using clojure.spec, you have the spec based
`clojure.spec.alpha/assert` macro. In the same way as the
`clojure.core/assert`, in production environment these asserts will
be removed by the compiler/runtime.
Example:
```clojure
(require '[clojure.spec.alpha :as s]
'[app.common.spec :as us])
(s/def ::number number?)
(us/assert ::number 3)
```
**Why don't use the `clojure.spec.alpha/assert` instead of the `app.common.spec/assert`?**
The Penpot variant does not peforms additional runtime checks for know
if asserts are disabled in "runtime". As a result it generates much
simpler code at development and production builds.
### **spec/verify**
An assertion type that is always executed.
Example:
```clojure
(require '[app.common.spec :as us])
(us/verify ::number 3)
```
This macro enables you to have assertions on production code.

View File

@ -0,0 +1,135 @@
---
title: 4.1. Environment
---
# Development environment
## System requirements
You should have `docker` and `docker-compose` installed in your system
in order to set up properly the development enviroment.
You can [look here][1] for complete instructions.
[1]: /technical-guide/getting-started/#docker
Optionally, increment user watches:
```
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
```
## Getting Started
**It requires a minimum knowledge of [tmux](https://github.com/tmux/tmux/wiki)
usage in order to use the development environment.**
To start it, clone penpot repository, and execute:
```bash
./manage.sh pull-devenv
./manage.sh run-devenv
```
This will do the following:
1. Pull the latest devenv image from dockerhub.
2. Start all the containers in the background.
3. Attach the terminal to the **devenv** container and execute the tmux session.
4. The tmux session automatically starts all the necessary services.
This is an incomplete list of devenv related subcommands found on
manage.sh script:
```bash
./manage.sh build-devenv # builds the devenv docker image (called by run-devenv automatically when needed)
./manage.sh start-devenv # starts background running containers
./manage.sh run-devenv # enters to new tmux session inside of one of the running containers
./manage.sh stop-devenv # stops background running containers
./manage.sh drop-devenv # removes all the containers, volumes and networks used by the devenv
```
Having the the container running and tmux opened inside the container,
you are free to execute any commands and open many shells as you want.
You can create a new shell just pressing the **Ctr+b c** shortcut. And
**Ctrl+b w** for switch between windows, **Ctrl+b &** for kill the
current window.
For more info: https://tmuxcheatsheet.com/
<!-- ## Inside the tmux session -->
<!-- By default, the tmux session opens 4 windows: -->
<!-- - **gulp** (0): responsible of build, watch (and other related) of -->
<!-- styles, images, fonts and templates. -->
<!-- - **frontend** (1): responsible of cljs compilation process of frontend. -->
<!-- - **exporter** (2): responsible of cljs compilation process of exporter. -->
<!-- - **backend** (3): responsible of starting the backend jvm process. -->
### Frontend
The frontend build process is located on the tmux **window 0** and
**window 1**. On the **window 0** we have the gulp process responsible
of watching and building styles, fonts, icon-spreads and templates.
On the **window 1** we can found the **shadow-cljs** process that is
responsible on watch and build frontend clojurescript code.
Additionally to the watch process you probably want to be able open a REPL
process on the frontend application, for this case you can split the window
and execute this:
```bash
npx shadow-cljs cljs-repl main
```
### Exporter
The exporter build process is located in the **window 2** and in the
same way as frontend application, it is build and watched using
**shadow-cljs**.
The main difference is that exporter will be executed in a nodejs, on
the server side instead of browser.
There you will found the window split in two slices. On the top slice
you will have the build process and on the bot slice shell ready to
execute the generated bundle.
You can start the exporter process executing:
```bash
node target/app.js
```
This process does not start automatically.
### Backend
The backend related process is located in the tmux **window 3**, and
you can go directly to it using `ctrl+b 3` shortcut.
By default the backend will be started in non-interactive mode for convenience
but you can just press `Ctrl+c` and execute this to start the repl:
```bash
./scripts/repl
```
On the REPL you have these helper functions:
- `(start)`: start all the environment
- `(stop)`: stops the environment
- `(restart)`: stops, reload and start again.
And many other that are defined in the `dev/user.clj` file.
If some exception is raised when code is reloaded, just use
`(repl/refresh-all)` in order to finish correctly the code swaping and
later use `(restart)` again.

View File

@ -1,34 +1,33 @@
---
title: 3.2. Frontend guide
title: 4.3. Frontend Guide
---
# Frontend guide
# Frontend Guide
This guide intends to explain the essential details of the frontend
application.
## Visual debug mode and utilities
## Icons & Assets
Debugging a problem in the viewport algorithms for grouping and
rotating is difficult. We have set a visual debug mode that displays
some annotations on screen, to help understanding what's happening.
The icons used on the frontend application are loaded using svgsprite
(properly handled by the gulp watch task). All icons should be on SVG
format located in `resources/images/icons`. The gulp task will
generate the sprite and the embedd it into the `index.html` file.
To activate it, open the javascript console and type
Then, you can reference the icon from the sprite using the
`app.builtins.icons/icon-xref` macro:
```javascript
app.util.debug.toggle_debug("option")
```clojure
(ns some.namespace
(:require-macros [app.main.ui.icons :refer [icon-xref]]))
(icon-xref :arrow)
```
Current options are `bounding-boxes`, `group`, `events` and
`rotation-handler`.
For performance reasons, all used icons are statically defined in the
`src/app/main/ui/icons.cljs` file.
You can also activate or deactivate all visual aids with
```javascript
app.util.debug.debug_all()
app.util.debug.debug_none()
```
## Logging, Tracing & Debugging
@ -45,16 +44,19 @@ type of the data.
```
An alternative is using the pprint function, usefull for pretty
printing a medium-big data sturcture for completly understand it.
printing a medium-big data structure to completely understand it.
```clojure
;; on the ns part
(:require [cljs.pprint :refer [pprint]])
;; On the code
(pprint expression)
; Outputs a clojure value as a string, nicely formatted and with data type information.
;; => Outputs a clojure value as a string, nicely formatted and with data type information.
```
Use the js native functions for printing data. The clj->js converts
the clojure data sturcture to js data sturcture and it is
the clojure data structure to js data structure and it is
inspeccionable in the devtools console.
```clojure
@ -78,11 +80,11 @@ the console.
Additionally to the traditional way of putting traces in the code, we
have a logging framework with steroids. It is usefull for casual
debugging (as replacement for a `prn` and `js/console.log`) and as a
debugging (as replacement for a `prn` and `js/console.log`) and for
permanent traces in the code.
You have the ability to specify the logging level per namespace and
all logging is ellided in production build.
all logging is elided in production build.
Lets start with a simple example:
@ -90,7 +92,7 @@ Lets start with a simple example:
(ns some.ns
(:require [app.util.logging :as log]))
;; This function sets the level to the current namespace; messages
;; This function sets the level of the current namespace; messages
;; with level behind this will not be printed.
(log/set-level! :info)
@ -105,7 +107,7 @@ Lets start with a simple example:
(log/trace :msg "trace message")
```
Each macro accept arbitrary number of key values pairs:
Each macro accepts an arbitrary number of key values pairs:
```clojure
(log/info :foo "bar" :msg "test" :value 1 :items #{1 2 3})
@ -126,7 +128,8 @@ Some keys ara treated as special cases for helping in debugging:
```
## Access to clojure from javascript console
### Access to clojure from js console
The penpot namespace of the main application is exported, so that is
accessible from javascript console in Chrome developer tools. Object
@ -139,7 +142,7 @@ app.main.store.emit_BANG_(app.main.data.workspace.reset_zoom)
```
## Debug state and objects
### Debug state and objects
There are also some useful functions to visualize the global state or
any complex object. To use them from clojure:
@ -169,31 +172,33 @@ dbg(js_expression) // equivalent to cljs.core.clj__GT_js(js_expression)
```
## Icons & Assets
The icons used on the frontend application are loaded using svgsprite
(properly handled by the gulp watch task). All icons should be on SVG
format located in `resources/images/icons`. The gulp task will
generate the sprite and the embedd it into the `index.html`.
Then, you can reference the icon from the sprite using the
`app.builtins.icons/icon-xref` macro:
## Workspace visual debug
```clojure
(ns some.namespace
(:require-macros [app.main.ui.icons :refer [icon-xref]]))
Debugging a problem in the viewport algorithms for grouping and
rotating is difficult. We have set a visual debug mode that displays
some annotations on screen, to help understanding what's happening.
(icon-xref :arrow)
To activate it, open the javascript console and type:
```js
app.util.debug.toggle_debug("option")
```
For performance reasons, all used icons are statically defined in the
`src/app/main/ui/icons.cljs` file.
Current options are `bounding-boxes`, `group`, `events` and
`rotation-handler`.
You can also activate or deactivate all visual aids with
```js
app.util.debug.debug_all()
app.util.debug.debug_none()
```
## Translations (I18N) ##
### How it Works ###
### How it works ###
All the translation strings of this application are stored in
`resources/locales.json` file. It has a self explanatory format that
@ -202,13 +207,13 @@ looks like this:
```json
{
"auth.email-or-username" : {
"used-in" : [ "src/app/main/ui/auth/login.cljs:61" ],
"used-in" : [ "src/app/main/ui/auth/login.cljs" ],
"translations" : {
"en" : "Email or Username",
"fr" : "adresse email ou nom d'utilisateur"
}
},
"ds.num-projects" : {
"labels.num-projects" : {
"translations": {
"en": ["1 project", "%s projects"]
}
@ -229,7 +234,7 @@ of that file, and just add a simple key-value entry pairs like this:
The file is automatically bundled into the `index.html` file on
compile time (in development and production). The bundled content is a
simplified version of this data structure for avoid load unnecesary
simplified version of this data structure for avoid loading unnecesary
data.
The development environment has a watch process that detect changes on
@ -241,27 +246,24 @@ If you have used the short (key-value) format, the watch process will
automatically convert it to the apropriate format before generate the
`index.html`.
Finally, when you have finished to adding texts, execute the following
command for reformat the file, and track the usage locations (the
Finally, when you have finished adding texts, execute the following
command to reformat the file, and track the usage locations (the
"used-in" list) before commiting the file into the repository:
```bash
clojure -Adev locales.clj collect src/app/main/ resources/locales.json
# cd <repo>/frontend
yarn run collect-locales
```
NOTE: Later, we will need to think and implement the way to export and
import to other formats (mainly for transifex and similar services
import to other formats (mainly for Transifex and similar services
compatibility).
### How to use it ###
You have two aproaches for translate strings: one for general purpose
and other specific for React components (that leverages reactivity for
language changes).
The `app.util.i18n/tr` is the general purpose function. This is a
simple use case example:
You need to use the `app.util.i18n/tr` function for lookup translation
strings:
```clojure
(require '[app.util.i18n :refer [tr])
@ -277,10 +279,23 @@ allow the system know when to show the plural:
```clojure
(require '[app.util.i18n :as i18n :refer [tr]])
(tr "ds.num-projects" (i18n/c 10))
(tr "labels.num-projects" (i18n/c 10))
;; => "10 projects"
(tr "ds.num-projects" (i18n/c 1))
(tr "labels.num-projects" (i18n/c 1))
;; => "1 project"
```
## Tests ##
Frontend tests have to be compiled first, and then run with node.
```bash
npx shadow-cljs compile tests && node target/tests.js
```
Or run the watch (that automatically runs the test):
```bash
npx shadow-cljs watch tests
```

View File

@ -0,0 +1,14 @@
---
title: 4. Developer Guide
---
# Developer Guide
This section is intended for people wanting to mess with the
code. The [Dev Env][1] section explains how to setup the common
development enviroment that we (the core team) use internally.
The rest of sections are a list categorized of probably not deeply
related HOWTO articles about dev-centric subjects.
[1]: /technical-guide/developer/devenv/

View File

@ -0,0 +1,82 @@
---
title: 1. Getting Started.
---
# Getting Started #
This section details everything you need to know to get Penpot up and
running in production environments. Although it can be installed in
many ways, the recommended approach is using **docker** and
**docker-compose**.
## Install Docker ##
**Skip this section if you already have docker installed, up and running.**
Probably the best approach to install docker is following the official docker
installation guide: https://docs.docker.com/engine/install/
Optionally, after installing docker, you can tweak your sistem for
avoid constanly using of **sudo**. The easy, single step way is
executong the following command that will add the current user to the
docker group:
```bash
sudo usermod -aG docker $USER
```
*NOTE*: probably you will need to re-login again to make this change take effect.
For more advanced setup, docker already has a guide for [rootleass docker][1]
[1]: https://docs.docker.com/engine/security/rootless/
## Start Penpot ##
As first step you will need to obtain the `docker-compose.yaml`
file. You can create it from scratch or start with the [default
one][2] from penpot repository:
[2]: https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml
```bash
wget https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml
```
The default compose file has all essential configuration variables
already set and they are pretty auto-explained. So we can proceed starting
the application without touching anything:
```bash
docker-compose -p penpot -f docker-compose.yaml up -d
```
This will start listening on http://localhost:9001
## Configuration ##
If you started the application without configuring SMTP, you probably
need to create a user in order be able login into the application. You
can create an additional, already activated user using this command:
```bash
docker exec -ti penpot_penpot-backend_1 ./manage.sh create-profile
```
In general, the application is ready to be used without email
configuration; for example when no smtp configuration is found you
should be able to change the user email (in profile section) without
the email validation step.
For more advanced setups, look a the [Configuration][3] section.
[3]: /technical-guide/configuration/

26
technical-guide/index.md Normal file
View File

@ -0,0 +1,26 @@
---
title: Overview
eleventyNavigation:
key: Technical Guide
order: 4
---
# Overview
This documentation intends to explain how to get penpot application
and run it locally, to test it or make changes to it.
The official installation guide is described in the [Getting
Started][1] section; and complemented with the [Configuration][2]
section for more advanced setups.
[1]: /technical-guide/getting-started/
[2]: /technical-guide/configuration/
If you are a developer and want to get into the code, we recommend the
[Developer Guide][3] that explains how to properly setup a development
environment and many other dev-oriented documentation.
[3]: /technical-guide/developer/

View File

@ -0,0 +1,4 @@
{
"layout": "layouts/technical-guide.njk",
"tags": "technical-guide"
}