add penpot

This commit is contained in:
janson 2022-11-18 17:02:09 +08:00
parent c5b00768d6
commit 3229a2c9c5
13 changed files with 641 additions and 0 deletions

View File

@ -0,0 +1,18 @@
include $(TOPDIR)/rules.mk
PKG_VERSION:=1.0.0-20221114
PKG_RELEASE:=
LUCI_TITLE:=LuCI support for Penpot
LUCI_PKGARCH:=all
LUCI_DEPENDS:=+docker +luci-lib-taskd docker-compose
define Package/luci-app-penpot/conffiles
/etc/config/penpot
endef
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -0,0 +1,8 @@
module("luci.controller.penpot", package.seeall)
function index()
entry({"admin", "services", "penpot"}, alias("admin", "services", "penpot", "config"), _("Penpot"), 30).dependent = true
entry({"admin", "services", "penpot", "config"}, cbi("penpot/config"), _("Config"), 10).leaf = true
entry({"admin", "services", "penpot", "tool"}, form("penpot/tool"), _("Tool"), 30).leaf = true
end

View File

@ -0,0 +1,63 @@
--[[
LuCI - Lua Configuration Interface
]]--
local taskd = require "luci.model.tasks"
local penpot_model = require "luci.model.penpot"
local m, s, o
m = taskd.docker_map("penpot", "penpot", "/usr/libexec/istorec/penpot.sh",
translate("Penpot"),
translate("Penpot is the first Open Source design and prototyping platform meant for cross-domain teams.")
.. translate("Official website:") .. ' <a href=\"https://penpot.app/\" target=\"_blank\">https://penpot.app/</a>')
s = m:section(SimpleSection, translate("Service Status"), translate("Penpot status:"))
s:append(Template("penpot/status"))
s = m:section(TypedSection, "main", translate("Setup"), translate("The following parameters will only take effect during installation or upgrade:"))
s.addremove=false
s.anonymous=true
o = s:option(Value, "http_port", translate("HTTP Port").."<b>*</b>")
o.default = "9001"
o.datatype = "string"
local blocks = penpot_model.blocks()
local home = penpot_model.home()
o = s:option(Value, "config_path", translate("Config path").."<b>*</b>")
o.rmempty = false
o.datatype = "string"
local paths, default_path = penpot_model.find_paths(blocks, home, "Configs")
for _, val in pairs(paths) do
o:value(val, val)
end
o.default = default_path
o = s:option(Value, "public_uri", "PUBLIC_URI")
o.datatype = "string"
o = s:option(Value, "redis_uri", "REDIS_URI")
o.datatype = "string"
o = s:option(Value, "db_uri", "DB_URI")
o.datatype = "string"
o = s:option(Value, "db_name", "DB_NAME")
o.datatype = "string"
o = s:option(Value, "db_username", "DB_USERNAME")
o.datatype = "string"
o = s:option(Value, "db_password", "DB_PASSWORD")
o.password = true
o.datatype = "string"
o = s:option(Value, "smtp_default_from", "SMTP_DEFAULT_FROM")
o.datatype = "string"
o = s:option(Value, "smtp_default_reply_to", "SMTP_DEFAULT_REPLY_TO")
o.datatype = "string"
return m

View File

@ -0,0 +1,61 @@
--[[
LuCI - Lua Configuration Interface
]]--
local http = require 'luci.http'
m=SimpleForm("Tools")
m.submit = false
m.reset = false
s = m:section(SimpleSection)
o = s:option(Value, "action", translate("Action").."<b>*</b>")
o.rmempty = false
o.datatype = "string"
o:value("create-user", "create-user")
o.default = "create-user"
local data = {}
o = s:option(Value, "email", "Email")
o.datatype = "string"
o.placeholder = "email@address"
o:depends("action", "create-user")
o = s:option(Value, "password", "Password")
o.password = true
o.datatype = "string"
o:depends("action", "create-user")
o = s:option(Value, "fullname", "Your Full Name")
o.datatype = "string"
o:depends("action", "create-user")
local t=Template("penpot/tool")
m:append(t)
local btn_do = s:option(Button, "_do")
btn_do.render = function(self, section, scope)
self.inputstyle = "add"
self.title = " "
self.inputtitle = translate("Execute")
Button.render(self, section, scope)
end
btn_do.write = function(self, section, value)
local action = m:get(section, "action")
if action == "create-user" then
local email = m:get(section, "email")
local password = m:get(section, "password")
local fullname = m:get(section, "fullname")
if email ~= nil and password ~= nil and fullname ~= nil then
local cmd = string.format("/usr/libexec/istorec/penpot.sh %s %s %s %s", action, email, password, fullname)
cmd = "/etc/init.d/tasks task_add penpot " .. luci.util.shellquote(cmd) .. " >/dev/null 2>&1"
os.execute(cmd)
t.show_log_taskid = "penpot"
end
end
end
return m

View File

@ -0,0 +1,54 @@
local util = require "luci.util"
local jsonc = require "luci.jsonc"
local penpot = {}
penpot.blocks = function()
local f = io.popen("lsblk -s -f -b -o NAME,FSSIZE,MOUNTPOINT --json", "r")
local vals = {}
if f then
local ret = f:read("*all")
f:close()
local obj = jsonc.parse(ret)
for _, val in pairs(obj["blockdevices"]) do
local fsize = val["fssize"]
if fsize ~= nil and string.len(fsize) > 10 and val["mountpoint"] then
-- fsize > 1G
vals[#vals+1] = val["mountpoint"]
end
end
end
return vals
end
penpot.home = function()
local uci = require "luci.model.uci".cursor()
local home_dirs = {}
home_dirs["main_dir"] = uci:get_first("quickstart", "main", "main_dir", "/root")
home_dirs["Configs"] = uci:get_first("quickstart", "main", "conf_dir", home_dirs["main_dir"].."/Configs")
home_dirs["Public"] = uci:get_first("quickstart", "main", "dl_dir", home_dirs["main_dir"].."/Public")
home_dirs["Caches"] = uci:get_first("quickstart", "main", "tmp_dir", home_dirs["main_dir"].."/Caches")
return home_dirs
end
penpot.find_paths = function(blocks, home_dirs, path_name)
local default_path = ''
local configs = {}
default_path = home_dirs[path_name] .. "/Penpot"
if #blocks == 0 then
table.insert(configs, default_path)
else
for _, val in pairs(blocks) do
table.insert(configs, val .. "/" .. path_name .. "/Penpot")
end
local without_conf_dir = "/root/" .. path_name .. "/Penpot"
if default_path == without_conf_dir then
default_path = configs[1]
end
end
return configs, default_path
end
return penpot

View File

@ -0,0 +1,31 @@
<%
local util = require "luci.util"
local container_status = util.trim(util.exec("/usr/libexec/istorec/penpot.sh status"))
local container_install = (string.len(container_status) > 0)
local container_running = container_status == "running"
-%>
<div class="cbi-value">
<label class="cbi-value-title"><%:Status%></label>
<div class="cbi-value-field">
<% if container_running then %>
<button class="cbi-button cbi-button-success" disabled="true"><%:Penpot is running%></button>
<% else %>
<button class="cbi-button cbi-button-negative" disabled="true"><%:Penpot is not running%></button>
<% end %>
</div>
</div>
<%
if container_running then
local port=util.trim(util.exec("/usr/libexec/istorec/penpot.sh port"))
if port == "" then
port="9001"
end
-%>
<div class="cbi-value cbi-value-last">
<label class="cbi-value-title">&nbsp;</label>
<div class="cbi-value-field">
<input type="button" class="btn cbi-button cbi-button-apply" name="start" value="<%:Open Penpot%>" onclick="window.open('http://'+location.hostname+':<%=port%>', '_blank')">
</div>
</div>
<% end %>

View File

@ -0,0 +1,11 @@
<%+tasks/embed%>
<script>
window.addEventListener("load", function(){
const taskd = window.taskd;
<% if self.show_log_taskid then -%>
taskd.show_log("<%=self.show_log_taskid%>");
<%- end %>
});
</script>

View File

@ -0,0 +1,42 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Official website:"
msgstr "官方网站:"
msgid "Penpot is the first Open Source design and prototyping platform meant for cross-domain teams."
msgstr "Penpot 是第一个开源的便于团队协作的在线设计平台。"
msgid "Config path"
msgstr "配置文件路径"
msgid "HTTP Port"
msgstr "HTTP 端口"
msgid "Service Status"
msgstr "服务状态"
msgid "Penpot status:"
msgstr "Penpot 的状态信息如下:"
msgid "Setup"
msgstr "安装配置"
msgid "The following parameters will only take effect during installation or upgrade:"
msgstr "以下参数只在安装或者升级时才会生效:"
msgid "Status"
msgstr "状态"
msgid "Penpot is running"
msgstr "Penpot 运行中"
msgid "Penpot is not running"
msgstr "Penpot 未运行"
msgid "Open Penpot"
msgstr "打开 Penpot"
msgid "Console"
msgstr "控制台"

View File

@ -0,0 +1,12 @@
config main
option 'http_port' '9001'
option 'config_path' ''
option 'public_uri' 'http://localhost:9001'
option 'redis_uri' 'redis://penpot-redis/0'
option 'db_uri' 'postgresql://penpot-postgres/penpot'
option 'db_name' 'penpot'
option 'db_username' 'penpot'
option 'db_password' 'penpot'
option 'smtp_default_from' 'no-reply@example.com'
option 'smtp_default_reply_to' 'no-reply@example.com'

View File

@ -0,0 +1,76 @@
#!/bin/sh
# Author Xiaobao(xiaobao@linkease.com)
ACTION=${1}
shift 1
do_install() {
local config=`uci get penpot.@main[0].config_path 2>/dev/null`
if [ -z "$config" ]; then
echo "config path is empty!"
exit 1
fi
mkdir -p $config
lua /usr/libexec/istorec/penpot_template.lua penpot /usr/share/penpot/config.template.env $config/config.env
RET=$?
if [ ! "$RET" = "0" ]; then
echo "convert config.env failed"
exit 1
fi
lua /usr/libexec/istorec/penpot_template.lua penpot /usr/share/penpot/docker-compose.template.yaml $config/docker-compose.yaml
RET=$?
if [ ! "$RET" = "0" ]; then
echo "convert config.env failed"
exit 1
fi
cd $config
docker-compose down
docker-compose up -d
echo "Wait 120 to intialize"
sleep 120
}
usage() {
echo "usage: $0 sub-command"
echo "where sub-command is one of:"
echo " install Install the penpot"
echo " upgrade Upgrade the penpot"
echo " rm/start/stop/restart Remove/Start/Stop/Restart the penpot"
echo " status Penpot status"
echo " port Penpot port"
}
case ${ACTION} in
"install")
do_install
;;
"upgrade")
do_install
;;
"rm")
local config=`uci get penpot.@main[0].config_path 2>/dev/null`
cd $config && docker-compose down
;;
"start" | "stop" | "restart")
local config=`uci get penpot.@main[0].config_path 2>/dev/null`
cd $config && docker-compose ${ACTION}
;;
"status")
docker ps --all -f 'name=penpot_penpot-frontend_1' --format '{{.State}}'
;;
"port")
http_port=`uci get penpot.@main[0].http_port 2>/dev/null`
echo $http_port
;;
"create-user")
echo docker exec -ti penpot_penpot-backend_1 ./manage.sh create-profile -u "${1}" -p "${2}" -n "${3}"
docker exec -ti penpot_penpot-backend_1 ./manage.sh create-profile -u "${1}" -p "${2}" -n "${3}"
;;
*)
usage
exit 1
;;
esac

View File

@ -0,0 +1,96 @@
local util = require "luci.util"
local tparser = require "luci.template.parser"
local uci = require "luci.model.uci"
local nixio = require "nixio"
local tostring, pairs, loadstring = tostring, pairs, loadstring
local setmetatable, loadfile = setmetatable, loadfile
local getfenv, setfenv, rawget = getfenv, setfenv, rawget
local assert, type, error = assert, type, error
local default_ctx = {tostring = tostring}
local from_string = function(template)
return Template(default_ctx, nil, template)
end
local from_file = function(template_file)
return Template(default_ctx, template_file)
end
-- Template class
Template = util.class()
-- Constructor - Reads and compiles the template on-demand
function Template.__init__(self, viewns, name, template)
if name then
self.name = name
else
self.name = "[string]"
end
-- Create a new namespace for this template
self.viewns = viewns
-- Compile template
local err
local sourcefile
if name then
sourcefile = name
self.template, _, err = tparser.parse(sourcefile)
else
sourcefile = "[string]"
self.template, _, err = tparser.parse_string(template)
end
-- If we have no valid template throw error, otherwise cache the template
if not self.template then
error("Failed to load template '" .. self.name .. "'.\n" ..
"Error while parsing template '" .. sourcefile .. "':\n" ..
(err or "Unknown syntax error"))
end
end
-- Renders a template
function Template.render(self, scope)
-- Put our predefined objects in the scope of the template
setfenv(self.template, setmetatable({}, {__index =
function(tbl, key)
return rawget(tbl, key) or self.viewns[key] or scope[key]
end}))
-- Now finally render the thing
local stat, err = util.copcall(self.template)
if not stat then
error("Failed to execute template '" .. self.name .. "'.\n" ..
"A runtime error occurred: " .. tostring(err or "(nil)"))
end
end
if #arg == 3 then
local cur = uci.cursor()
local configs = {}
cur:foreach(arg[1], "main", function(s)
for k, v in pairs(s) do
configs[k] = v
end
end)
if not nixio.fs.access(arg[2]) then
print(arg[2] .. " not found")
os.exit(10)
end
local target = io.open(arg[3], "w")
if not target then
print(arg[3] .. " can not write")
end
configs.write = function(data)
target:write(data)
end
from_file(arg[2]):render(configs)
target:close()
else
print("penpot_template.lua [appname] [template-in] [template-out]")
end

View File

@ -0,0 +1,96 @@
## Should be set to the public domain where penpot is going to be served.
##
## NOTE: If you are going to serve it under different domain than
## 'localhost' without HTTPS, consider setting the
## `disable-secure-session-cookies' flag on the 'PENPOT_FLAGS'
## setting.
PENPOT_PUBLIC_URI=<%=public_uri%>
## Feature flags.
PENPOT_FLAGS=enable-registration enable-login disable-email-verification disable-secure-session-cookies
## Temporal workaround because of bad builtin default
PENPOT_HTTP_SERVER_HOST=0.0.0.0
## Standard database connection parameters (only postgresql is supported):
PENPOT_DATABASE_URI=<%=db_uri%>
PENPOT_DATABASE_USERNAME=<%=db_username%>
PENPOT_DATABASE_PASSWORD=<%=db_password%>
## Redis is used for the websockets notifications.
PENPOT_REDIS_URI=<%=redis_uri%>
## By default, files uploaded by users are stored in local
## filesystem. But it can be configured to store in AWS S3.
PENPOT_ASSETS_STORAGE_BACKEND=assets-fs
PENPOT_STORAGE_ASSETS_FS_DIRECTORY=/opt/data/assets
## Telemetry. When enabled, a periodical process will send anonymous
## data about this instance. Telemetry data will enable us to learn on
## how the application is used, based on real scenarios. If you want
## to help us, please leave it enabled.
PENPOT_TELEMETRY_ENABLED=true
## Email sending configuration. By default, emails are printed in the
## console, but for production usage is recommended to setup a real
## SMTP provider. Emails are used to confirm user registrations.
PENPOT_SMTP_DEFAULT_FROM=<%=smtp_default_from%>
PENPOT_SMTP_DEFAULT_REPLY_TO=<%=smtp_default_reply_to%>
# PENPOT_SMTP_HOST=
# PENPOT_SMTP_PORT=
# PENPOT_SMTP_USERNAME=
# PENPOT_SMTP_PASSWORD=
# PENPOT_SMTP_TLS=true
# PENPOT_SMTP_SSL=false
## Comma separated list of allowed domains to register. Empty to allow
## all.
# PENPOT_REGISTRATION_DOMAIN_WHITELIST=""
## Authentication providers
## Google
# PENPOT_GOOGLE_CLIENT_ID=
# PENPOT_GOOGLE_CLIENT_SECRET=
## GitHub
# PENPOT_GITHUB_CLIENT_ID=
# PENPOT_GITHUB_CLIENT_SECRET=
## GitLab
# PENPOT_GITLAB_BASE_URI=https://gitlab.com
# PENPOT_GITLAB_CLIENT_ID=
# PENPOT_GITLAB_CLIENT_SECRET=
## OpenID Connect (since 1.5.0)
# PENPOT_OIDC_BASE_URI=
# PENPOT_OIDC_CLIENT_ID=
# PENPOT_OIDC_CLIENT_SECRET=
## LDAP
##
## NOTE: to enable ldap, you will need to put 'enable-login-with-ldap'
## on the 'PENPOT_FLAGS' environment variable.
# 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

View File

@ -0,0 +1,73 @@
---
version: "3.5"
networks:
penpot:
services:
penpot-frontend:
image: "penpotapp/frontend:latest"
ports:
- <%=http_port%>:80
volumes:
- <%=config_path%>/data/asserts:/opt/data
env_file:
- config.env
depends_on:
- penpot-backend
- penpot-exporter
networks:
- penpot
penpot-backend:
image: "penpotapp/backend:latest"
volumes:
- <%=config_path%>/data/asserts:/opt/data
depends_on:
- penpot-postgres
- penpot-redis
env_file:
- config.env
networks:
- penpot
penpot-exporter:
image: "penpotapp/exporter:latest"
env_file:
- config.env
environment:
# Don't touch it; this uses internal docker network to
# communicate with the frontend.
- PENPOT_PUBLIC_URI=http://penpot-frontend
networks:
- penpot
penpot-postgres:
image: "postgres:14"
restart: always
stop_signal: SIGINT
environment:
- POSTGRES_INITDB_ARGS=--data-checksums
- POSTGRES_DB=<%=db_name%>
- POSTGRES_USER=<%=db_username%>
- POSTGRES_PASSWORD=<%=db_password%>
volumes:
- <%=config_path%>/data/postgres:/var/lib/postgresql/data
networks:
- penpot
penpot-redis:
image: redis:7
restart: always
networks:
- penpot