Compare commits

..

287 commits

Author SHA1 Message Date
Liss-Bot
0e9de519ed Updates templates (auto-generated, on 22-Dec-2024) 2024-12-22 02:27:56 +00:00
Liss-Bot
e148e4a19e Updates template + source list in docs (auto-generated, on 15-Dec-2024) 2024-12-15 02:32:42 +00:00
Liss-Bot
ea8aa94b68 Updates templates (auto-generated, on 15-Dec-2024) 2024-12-15 02:32:41 +00:00
liss-bot
c67deabe01 docs: Updates contributors list 2024-12-15 02:26:24 +00:00
Liss-Bot
a9e0213b04 Updates templates (auto-generated, on 08-Dec-2024) 2024-12-08 02:32:22 +00:00
liss-bot
3ac840262f docs: Updates contributors list 2024-12-08 02:25:58 +00:00
liss-bot
e08f3d1932 docs: Updates contributors list 2024-12-01 02:31:02 +00:00
Liss-Bot
9ef4c78712 Updates template + source list in docs (auto-generated, on 24-Nov-2024) 2024-11-24 02:31:06 +00:00
Liss-Bot
f8728e7114 Updates templates (auto-generated, on 24-Nov-2024) 2024-11-24 02:31:06 +00:00
Liss-Bot
08c2e1ea97 Updates template + source list in docs (auto-generated, on 10-Nov-2024) 2024-11-10 02:26:40 +00:00
Liss-Bot
5d2f5dfe6c Updates templates (auto-generated, on 10-Nov-2024) 2024-11-10 02:26:40 +00:00
liss-bot
691e1f1f9f docs: Updates contributors list 2024-11-10 02:21:33 +00:00
Liss-Bot
f4bbca3679 Updates template + source list in docs (auto-generated, on 03-Nov-2024) 2024-11-03 02:28:20 +00:00
Liss-Bot
6767118122 Updates templates (auto-generated, on 03-Nov-2024) 2024-11-03 02:28:19 +00:00
liss-bot
39655e647d docs: Updates contributors list 2024-11-03 02:22:43 +00:00
Liss-Bot
24dd67a822 Updates template + source list in docs (auto-generated, on 27-Oct-2024) 2024-10-27 02:28:37 +00:00
Liss-Bot
b43c1b834b Updates templates (auto-generated, on 27-Oct-2024) 2024-10-27 02:28:37 +00:00
Liss-Bot
7e224be0fc Updates template + source list in docs (auto-generated, on 20-Oct-2024) 2024-10-20 02:29:00 +00:00
Liss-Bot
1ba9bce628 Updates templates (auto-generated, on 20-Oct-2024) 2024-10-20 02:28:59 +00:00
Liss-Bot
49e0c436d7 Updates template + source list in docs (auto-generated, on 13-Oct-2024) 2024-10-13 02:27:46 +00:00
Liss-Bot
5f24142c1a Updates templates (auto-generated, on 13-Oct-2024) 2024-10-13 02:27:46 +00:00
liss-bot
1f1b9d22ec docs: Updates contributors list 2024-10-06 02:22:42 +00:00
Liss-Bot
ae169f15c7 Updates template + source list in docs (auto-generated, on 29-Sep-2024) 2024-09-29 02:28:18 +00:00
Liss-Bot
066e964d27 Updates templates (auto-generated, on 29-Sep-2024) 2024-09-29 02:28:18 +00:00
liss-bot
91b2a67884 docs: Updates contributors list 2024-09-29 02:22:47 +00:00
Liss-Bot
afac2e8c58 Updates template + source list in docs (auto-generated, on 22-Sep-2024) 2024-09-22 02:30:13 +00:00
Liss-Bot
44188c5f5a Updates templates (auto-generated, on 22-Sep-2024) 2024-09-22 02:30:13 +00:00
Liss-Bot
8bed0a4176 Updates template + source list in docs (auto-generated, on 15-Sep-2024) 2024-09-15 02:27:19 +00:00
Liss-Bot
49bdaca29f Updates templates (auto-generated, on 15-Sep-2024) 2024-09-15 02:27:18 +00:00
Liss-Bot
f4208f9759 Updates template + source list in docs (auto-generated, on 08-Sep-2024) 2024-09-08 02:25:11 +00:00
Liss-Bot
7b636d57e7 Updates templates (auto-generated, on 08-Sep-2024) 2024-09-08 02:25:10 +00:00
liss-bot
7cf9661b3a docs: Updates contributors list 2024-09-08 02:20:13 +00:00
Liss-Bot
6f6b4fb878 Updates template + source list in docs (auto-generated, on 01-Sep-2024) 2024-09-01 02:27:55 +00:00
Liss-Bot
4dd9939791 Updates templates (auto-generated, on 01-Sep-2024) 2024-09-01 02:27:55 +00:00
liss-bot
437b2246fb docs: Updates contributors list 2024-09-01 02:22:35 +00:00
liss-bot
1e4e86849c docs: Updates contributors list 2024-08-25 02:17:43 +00:00
liss-bot
e4216c0481 docs: Updates contributors list 2024-08-18 02:15:35 +00:00
Liss-Bot
7168010566 Updates template + source list in docs (auto-generated, on 17-Aug-2024) 2024-08-17 14:20:29 +00:00
Liss-Bot
f15813c824 Updates templates (auto-generated, on 17-Aug-2024) 2024-08-17 14:20:29 +00:00
Alicia Sykes
a4232f9cd9
Merge pull request #62 from shmolf/issue-reporting
Issue reporting
2024-08-17 15:20:06 +01:00
Nicholas Browning
bc14830c63 feat: add links for reporting issues upstream 2024-08-12 05:46:09 +00:00
Liss-Bot
0ee3aed47e Updates template + source list in docs (auto-generated, on 11-Aug-2024) 2024-08-11 02:20:36 +00:00
Liss-Bot
98693427ff Updates templates (auto-generated, on 11-Aug-2024) 2024-08-11 02:20:35 +00:00
liss-bot
3651113817 docs: Updates contributors list 2024-08-11 02:17:18 +00:00
Liss-Bot
eadc54eafc Updates template + source list in docs (auto-generated, on 04-Aug-2024) 2024-08-04 02:18:58 +00:00
Liss-Bot
8968f2134d Updates templates (auto-generated, on 04-Aug-2024) 2024-08-04 02:18:57 +00:00
liss-bot
08672c0195 docs: Updates contributors list 2024-08-04 02:15:38 +00:00
Liss-Bot
2064397351 Updates template + source list in docs (auto-generated, on 28-Jul-2024) 2024-07-28 02:23:30 +00:00
Liss-Bot
38f019a8d1 Updates templates (auto-generated, on 28-Jul-2024) 2024-07-28 02:23:29 +00:00
liss-bot
a28ebdac5a docs: Updates contributors list 2024-07-28 02:15:18 +00:00
Liss-Bot
899c271f6c Updates template + source list in docs (auto-generated, on 21-Jul-2024) 2024-07-21 02:18:05 +00:00
Liss-Bot
8025b96bc2 Updates templates (auto-generated, on 21-Jul-2024) 2024-07-21 02:18:04 +00:00
liss-bot
104a27e3b5 docs: Updates contributors list 2024-07-21 02:14:37 +00:00
Liss-Bot
ea4d188a6b Updates template + source list in docs (auto-generated, on 14-Jul-2024) 2024-07-14 02:20:46 +00:00
Liss-Bot
3f4a4283bf Updates templates (auto-generated, on 14-Jul-2024) 2024-07-14 02:20:45 +00:00
Nicholas Browning
61690c6df9 feat: added DevContainer support 2024-07-10 23:02:33 +00:00
Liss-Bot
f1821c19f1 Updates template + source list in docs (auto-generated, on 07-Jul-2024) 2024-07-07 02:18:01 +00:00
Liss-Bot
32329d66b5 Updates templates (auto-generated, on 07-Jul-2024) 2024-07-07 02:18:00 +00:00
liss-bot
8e493b1925 docs: Updates contributors list 2024-07-07 02:14:34 +00:00
Liss-Bot
49c0cd8bfd Updates template + source list in docs (auto-generated, on 30-Jun-2024) 2024-06-30 02:17:57 +00:00
Liss-Bot
7180ca57b0 Updates templates (auto-generated, on 30-Jun-2024) 2024-06-30 02:17:56 +00:00
Liss-Bot
8cf0d8fa0c Updates template + source list in docs (auto-generated, on 23-Jun-2024) 2024-06-23 02:16:42 +00:00
Liss-Bot
2520110268 Updates templates (auto-generated, on 23-Jun-2024) 2024-06-23 02:16:41 +00:00
Liss-Bot
583e812353 Updates templates (auto-generated, on 16-Jun-2024) 2024-06-16 02:17:23 +00:00
Liss-Bot
b716bf5176 Updates template + source list in docs (auto-generated, on 09-Jun-2024) 2024-06-09 02:17:20 +00:00
Liss-Bot
7768d6823c Updates templates (auto-generated, on 09-Jun-2024) 2024-06-09 02:17:20 +00:00
liss-bot
3b3e0ccdd4 docs: Updates contributors list 2024-06-09 02:13:10 +00:00
Liss-Bot
3e660456ff Updates template + source list in docs (auto-generated, on 02-Jun-2024) 2024-06-02 02:16:10 +00:00
Liss-Bot
5b82a1b089 Updates templates (auto-generated, on 02-Jun-2024) 2024-06-02 02:16:09 +00:00
liss-bot
f482b23847 docs: Updates contributors list 2024-06-02 02:10:46 +00:00
Liss-Bot
420f5e8806 Updates templates (auto-generated, on 26-May-2024) 2024-05-26 02:15:30 +00:00
liss-bot
d7f1d2b75b docs: Updates contributors list 2024-05-26 02:10:13 +00:00
Liss-Bot
e85653797b Updates template + source list in docs (auto-generated, on 19-May-2024) 2024-05-19 02:15:02 +00:00
Liss-Bot
1253bc4e9b Updates templates (auto-generated, on 19-May-2024) 2024-05-19 02:15:01 +00:00
liss-bot
9c199f5e74 docs: Updates contributors list 2024-05-19 02:09:43 +00:00
liss-bot
ad9891dfb8 docs: Updates contributors list 2024-05-12 02:09:24 +00:00
Liss-Bot
24f8cf9207 Updates template + source list in docs (auto-generated, on 05-May-2024) 2024-05-05 02:13:53 +00:00
Liss-Bot
05d20bd285 Updates templates (auto-generated, on 05-May-2024) 2024-05-05 02:13:52 +00:00
liss-bot
3da76981d5 docs: Updates contributors list 2024-05-05 02:08:35 +00:00
Liss-Bot
02a9ecc1a7 Updates template + source list in docs (auto-generated, on 28-Apr-2024) 2024-04-28 02:14:29 +00:00
Liss-Bot
f1ef288797 Updates templates (auto-generated, on 28-Apr-2024) 2024-04-28 02:14:29 +00:00
liss-bot
3ca033028d docs: Updates contributors list 2024-04-28 02:08:56 +00:00
Liss-Bot
06c6c85cc2 Updates template + source list in docs (auto-generated, on 21-Apr-2024) 2024-04-21 02:12:42 +00:00
Liss-Bot
836046c87a Updates templates (auto-generated, on 21-Apr-2024) 2024-04-21 02:12:42 +00:00
liss-bot
5cbe7ccb5f docs: Updates contributors list 2024-04-21 02:08:02 +00:00
Liss-Bot
02a3a71121 Updates template + source list in docs (auto-generated, on 14-Apr-2024) 2024-04-14 02:37:55 +00:00
Liss-Bot
3dd30925e3 Updates templates (auto-generated, on 14-Apr-2024) 2024-04-14 02:37:54 +00:00
liss-bot
f17736781f docs: Updates contributors list 2024-04-14 02:27:28 +00:00
Liss-Bot
8c40a18c3c Updates template + source list in docs (auto-generated, on 07-Apr-2024) 2024-04-07 02:11:22 +00:00
Liss-Bot
fd0098891a Updates templates (auto-generated, on 07-Apr-2024) 2024-04-07 02:11:21 +00:00
liss-bot
c6e5aead14 docs: Updates contributors list 2024-04-07 02:07:29 +00:00
Liss-Bot
866a6fcc1e Updates template + source list in docs (auto-generated, on 31-Mar-2024) 2024-03-31 02:11:32 +00:00
Liss-Bot
1e70445571 Updates templates (auto-generated, on 31-Mar-2024) 2024-03-31 02:11:31 +00:00
liss-bot
492de549a1 docs: Updates contributors list 2024-03-31 02:07:33 +00:00
Liss-Bot
ccec4ab871 Updates templates (auto-generated, on 24-Mar-2024) 2024-03-24 02:11:41 +00:00
liss-bot
817a37a2ae docs: Updates contributors list 2024-03-24 02:07:39 +00:00
Liss-Bot
1f4b99fe5c Updates template + source list in docs (auto-generated, on 17-Mar-2024) 2024-03-17 02:09:29 +00:00
Liss-Bot
6089983035 Updates templates (auto-generated, on 17-Mar-2024) 2024-03-17 02:09:28 +00:00
liss-bot
01b7b30453 docs: Updates contributors list 2024-03-17 02:05:57 +00:00
Liss-Bot
52621f8ff8 Updates template + source list in docs (auto-generated, on 10-Mar-2024) 2024-03-10 02:11:28 +00:00
Liss-Bot
df2751fb6e Updates templates (auto-generated, on 10-Mar-2024) 2024-03-10 02:11:27 +00:00
liss-bot
c014e9ffd8 docs: Updates contributors list 2024-03-10 02:07:34 +00:00
Liss-Bot
bb0fd5eb9b Updates template + source list in docs (auto-generated, on 08-Mar-2024) 2024-03-08 06:22:31 +00:00
Liss-Bot
3ed1777f2a Updates templates (auto-generated, on 08-Mar-2024) 2024-03-08 06:22:30 +00:00
Alicia Sykes
37f3dd0d84
Merge pull request #49 from shmolf/handle-malformed-json 2024-03-08 06:22:13 +00:00
Nicholas Browning
9b403098dc fix: handle poorly formatted source JSON 2024-03-07 23:35:33 +00:00
Alicia Sykes
3aad43c944
Merge pull request #29 from shmolf/main
feat: normalize casing of categories for each of the templates
2024-03-07 11:06:21 +00:00
liss-bot
eb85f6e563 docs: Updates contributors list 2024-03-03 02:06:19 +00:00
Alicia Sykes
7edc2ef958
Bump count 2024-02-26 01:11:43 +00:00
liss-bot
32847c9e02 docs: Updates contributors list 2024-02-25 02:06:57 +00:00
Liss-Bot
9a4f19c98f Updates template + source list in docs (auto-generated, on 18-Feb-2024) 2024-02-18 02:09:50 +00:00
Liss-Bot
08da0508bf Updates templates (auto-generated, on 18-Feb-2024) 2024-02-18 02:09:49 +00:00
liss-bot
4e704b7009 docs: Updates contributors list 2024-02-18 02:06:31 +00:00
Liss-Bot
4d4e782e2e Updates template + source list in docs (auto-generated, on 11-Feb-2024) 2024-02-11 02:10:11 +00:00
Liss-Bot
fca8d9699f Updates templates (auto-generated, on 11-Feb-2024) 2024-02-11 02:10:10 +00:00
liss-bot
bb60a2ef38 docs: Updates contributors list 2024-02-11 02:06:56 +00:00
Liss-Bot
b0489fbffb Updates template + source list in docs (auto-generated, on 04-Feb-2024) 2024-02-04 02:11:15 +00:00
Liss-Bot
abc0994a01 Updates templates (auto-generated, on 04-Feb-2024) 2024-02-04 02:11:14 +00:00
liss-bot
4189db94ff docs: Updates contributors list 2024-02-04 02:07:34 +00:00
liss-bot
2f06b47bca docs: Updates contributors list 2024-01-28 02:06:41 +00:00
Liss-Bot
a1c4d9be59 Updates template + source list in docs (auto-generated, on 27-Jan-2024) 2024-01-27 12:27:11 +00:00
Liss-Bot
d8d8d5d660 Updates templates (auto-generated, on 27-Jan-2024) 2024-01-27 12:27:11 +00:00
Alicia Sykes
5ba11b000a
Updates contributing guidelines 2024-01-27 12:26:50 +00:00
Liss-Bot
15c4f0765a Updates template + source list in docs (auto-generated, on 21-Jan-2024) 2024-01-21 02:16:01 +00:00
Liss-Bot
4dbe46a5a2 Updates templates (auto-generated, on 21-Jan-2024) 2024-01-21 02:16:00 +00:00
liss-bot
c3fb7c3bdb docs: Updates contributors list 2024-01-21 02:10:54 +00:00
Nicholas Browning
e012ab36c9 feat: normalize casing of categories for each of the templates 2024-01-16 02:11:38 +00:00
Liss-Bot
711c0f2e6c Updates template + source list in docs (auto-generated, on 14-Jan-2024) 2024-01-14 18:38:39 +00:00
Liss-Bot
2aaec9dafa Updates templates (auto-generated, on 14-Jan-2024) 2024-01-14 18:38:39 +00:00
Alicia Sykes
1dcad82555
Merge pull request #28 from shmolf/main
feat: add my repo to the list
2024-01-14 18:38:17 +00:00
Liss-Bot
85890ed7c3 Updates templates (auto-generated, on 14-Jan-2024) 2024-01-14 02:15:46 +00:00
liss-bot
6ec745534d docs: Updates contributors list 2024-01-14 02:10:43 +00:00
Nicholas Browning
c8fbc9f37c feat: add my repo to the list 2024-01-12 04:58:02 +00:00
Liss-Bot
082de399ae Updates template + source list in docs (auto-generated, on 07-Jan-2024) 2024-01-07 02:15:45 +00:00
Liss-Bot
ddb89bdc19 Updates templates (auto-generated, on 07-Jan-2024) 2024-01-07 02:15:44 +00:00
liss-bot
b0cf0d8344 docs: Updates contributors list 2024-01-07 02:10:27 +00:00
Liss-Bot
e9dcc59ae7 Updates template + source list in docs (auto-generated, on 31-Dec-2023) 2023-12-31 02:14:28 +00:00
Liss-Bot
f2c32bf40f Updates templates (auto-generated, on 31-Dec-2023) 2023-12-31 02:14:27 +00:00
liss-bot
cefeb26cde docs: Updates contributors list 2023-12-31 02:09:09 +00:00
Liss-Bot
d6a7124f04 Updates template + source list in docs (auto-generated, on 24-Dec-2023) 2023-12-24 02:13:54 +00:00
Liss-Bot
0e81725af3 Updates templates (auto-generated, on 24-Dec-2023) 2023-12-24 02:13:54 +00:00
liss-bot
c5a7551db1 docs: Updates contributors list 2023-12-24 02:08:45 +00:00
Liss-Bot
fbb088137e Updates template + source list in docs (auto-generated, on 17-Dec-2023) 2023-12-17 02:14:57 +00:00
Liss-Bot
5d7082e782 Updates templates (auto-generated, on 17-Dec-2023) 2023-12-17 02:14:57 +00:00
liss-bot
08e553b0bd docs: Updates contributors list 2023-12-17 02:09:47 +00:00
Liss-Bot
50907b2f90 Updates template + source list in docs (auto-generated, on 10-Dec-2023) 2023-12-10 02:15:01 +00:00
Liss-Bot
61f695ee13 Updates templates (auto-generated, on 10-Dec-2023) 2023-12-10 02:15:00 +00:00
liss-bot
e02cc9236a docs: Updates contributors list 2023-12-10 02:09:48 +00:00
Liss-Bot
569d46e9e0 Updates template + source list in docs (auto-generated, on 03-Dec-2023) 2023-12-03 02:14:26 +00:00
Liss-Bot
198277148d Updates templates (auto-generated, on 03-Dec-2023) 2023-12-03 02:14:26 +00:00
liss-bot
adcf258f0a docs: Updates contributors list 2023-12-03 02:09:08 +00:00
Liss-Bot
dc0f0974b3 Updates template + source list in docs (auto-generated, on 26-Nov-2023) 2023-11-26 02:14:35 +00:00
Liss-Bot
1cca27623e Updates templates (auto-generated, on 26-Nov-2023) 2023-11-26 02:14:34 +00:00
liss-bot
96df96a8b5 docs: Updates contributors list 2023-11-26 02:09:05 +00:00
Liss-Bot
9460c1aeb5 Updates template + source list in docs (auto-generated, on 19-Nov-2023) 2023-11-19 02:14:57 +00:00
Liss-Bot
5277dc901a Updates templates (auto-generated, on 19-Nov-2023) 2023-11-19 02:14:57 +00:00
liss-bot
c83e724b7d docs: Updates contributors list 2023-11-19 02:09:44 +00:00
liss-bot
0422598add docs: Updates contributors list 2023-11-12 02:08:29 +00:00
Liss-Bot
a1aa958a4f Updates templates (auto-generated, on 06-Nov-2023) 2023-11-06 20:23:54 +00:00
Alicia Sykes
6631d03bf4
Merge pull request #21 from Benoniy/patch-1
Update homepage image download
2023-11-06 20:23:28 +00:00
Benjamin Ranson
b8341a0c8d
Update homepage image download
Homepage has moved its image location and therefore the template needs to be updated to ensure successful updates
2023-11-06 11:48:15 +00:00
Liss-Bot
16e3e060af Updates template + source list in docs (auto-generated, on 05-Nov-2023) 2023-11-05 02:12:30 +00:00
Liss-Bot
0137908151 Updates templates (auto-generated, on 05-Nov-2023) 2023-11-05 02:12:30 +00:00
liss-bot
d3836b15a4 docs: Updates contributors list 2023-11-05 02:08:10 +00:00
liss-bot
0ec0be8259 docs: Updates contributors list 2023-10-29 02:07:19 +00:00
Liss-Bot
3291b87ff2 Updates template + source list in docs (auto-generated, on 27-Oct-2023) 2023-10-27 20:45:15 +00:00
Liss-Bot
1986b1accb Updates templates (auto-generated, on 27-Oct-2023) 2023-10-27 20:45:14 +00:00
Alicia Sykes
f093e9aca2
Update README.md 2023-10-27 21:44:51 +01:00
Liss-Bot
ffe2e29b66 Updates template + source list in docs (auto-generated, on 22-Oct-2023) 2023-10-22 02:11:36 +00:00
Liss-Bot
7c1716a282 Updates templates (auto-generated, on 22-Oct-2023) 2023-10-22 02:11:35 +00:00
liss-bot
7bbbd992a1 docs: Updates contributors list 2023-10-15 02:07:42 +00:00
liss-bot
b945aa6145 docs: Updates contributors list 2023-10-08 02:07:28 +00:00
Liss-Bot
1ab9a0c65e Updates template + source list in docs (auto-generated, on 01-Oct-2023) 2023-10-01 02:13:59 +00:00
Liss-Bot
b198912d54 Updates templates (auto-generated, on 01-Oct-2023) 2023-10-01 02:13:58 +00:00
Liss-Bot
864b78c1e9 Updates template + source list in docs (auto-generated, on 23-Sep-2023) 2023-09-23 21:00:23 +00:00
Liss-Bot
3bcdef6d8c Updates templates (auto-generated, on 23-Sep-2023) 2023-09-23 21:00:23 +00:00
Alicia Sykes
20f130d7ac
Updates license and docs link in self-hosted index 2023-09-23 22:00:04 +01:00
Liss-Bot
bdb3c75e31 Updates template + source list in docs (auto-generated, on 17-Sep-2023) 2023-09-17 02:09:36 +00:00
Liss-Bot
9e133e31e8 Updates templates (auto-generated, on 17-Sep-2023) 2023-09-17 02:09:35 +00:00
Liss-Bot
185776f469 Updates template + source list in docs (auto-generated, on 10-Sep-2023) 2023-09-10 02:09:35 +00:00
Liss-Bot
e2727ee3eb Updates templates (auto-generated, on 10-Sep-2023) 2023-09-10 02:09:34 +00:00
liss-bot
e2394d761a docs: Updates contributors list 2023-09-03 02:05:37 +00:00
liss-bot
d735b522c6 docs: Updates contributors list 2023-09-02 22:30:18 +00:00
Alicia Sykes
d43b754892
Adds a workflow to insert credits 2023-09-02 23:29:55 +01:00
Alicia Sykes
da80e1e5f8
Adds credits section 2023-09-02 23:29:16 +01:00
Liss-Bot
5a3d437f76 Updates template + source list in docs (auto-generated, on 02-Sep-2023) 2023-09-02 22:24:27 +00:00
Liss-Bot
06d9523961 Updates templates (auto-generated, on 02-Sep-2023) 2023-09-02 22:24:26 +00:00
Alicia Sykes
6007f4b6ac
Merge pull request #16 from Lissy93/revert-11-patch-1
Revert "Update sources.csv"
2023-09-02 23:23:58 +01:00
Alicia Sykes
e84aca65c1
Revert "Update sources.csv" 2023-09-02 23:23:17 +01:00
Alicia Sykes
adacb8b0b5
Adds favicon to self-hosted start page 2023-09-02 23:20:28 +01:00
Alicia Sykes
2254faab58
Merge pull request #11 from tempusthales/patch-1
Update sources.csv
2023-09-02 23:12:13 +01:00
Liss-Bot
0fd4b964ae Updates template + source list in docs (auto-generated, on 27-Aug-2023) 2023-08-27 02:08:48 +00:00
Liss-Bot
cc27a7531a Updates templates (auto-generated, on 27-Aug-2023) 2023-08-27 02:08:48 +00:00
Liss-Bot
d4acdca18d Updates template + source list in docs (auto-generated, on 20-Aug-2023) 2023-08-20 02:07:11 +00:00
Liss-Bot
152370b57f Updates templates (auto-generated, on 20-Aug-2023) 2023-08-20 02:07:10 +00:00
Liss-Bot
d0f0b17e25 Updates template + source list in docs (auto-generated, on 13-Aug-2023) 2023-08-13 02:07:22 +00:00
Liss-Bot
85aee7b039 Updates templates (auto-generated, on 13-Aug-2023) 2023-08-13 02:07:21 +00:00
Liss-Bot
d3ecfa00f1 Updates template + source list in docs (auto-generated, on 06-Aug-2023) 2023-08-06 02:10:18 +00:00
Liss-Bot
e0a9e01861 Updates templates (auto-generated, on 06-Aug-2023) 2023-08-06 02:10:17 +00:00
Gilbert Palau
3bbecd83bc
Update sources.csv
Added Cloudflare Tunnel provides you with a secure way to connect your resources to Cloudflare without a publicly routable IP address. With Tunnel, you do not send traffic to an external IP — instead, a lightweight daemon in your infrastructure (cloudflared) creates outbound-only connections to Cloudflare's edge.
2023-07-31 00:55:10 -05:00
Liss-Bot
29edeaabe9 Updates template + source list in docs (auto-generated, on 30-Jul-2023) 2023-07-30 02:15:06 +00:00
Liss-Bot
1b14eb4ae2 Updates templates (auto-generated, on 30-Jul-2023) 2023-07-30 02:15:06 +00:00
Liss-Bot
81f8d008ce Updates template + source list in docs (auto-generated, on 23-Jul-2023) 2023-07-23 02:21:14 +00:00
Liss-Bot
b5d35d6483 Updates templates (auto-generated, on 23-Jul-2023) 2023-07-23 02:21:13 +00:00
Liss-Bot
87df4fdbc2 Updates template + source list in docs (auto-generated, on 16-Jul-2023) 2023-07-16 02:35:36 +00:00
Liss-Bot
8203b74671 Updates templates (auto-generated, on 16-Jul-2023) 2023-07-16 02:35:35 +00:00
Liss-Bot
3c23ed50e2 Updates template + source list in docs (auto-generated, on 08-Jul-2023) 2023-07-08 10:59:14 +00:00
Liss-Bot
406558c7cf Updates templates (auto-generated, on 08-Jul-2023) 2023-07-08 10:59:13 +00:00
Alicia Sykes
bac217a5fc
Merge pull request #8 from balzack/patch-1
added the databag messaging service
2023-07-08 11:58:56 +01:00
Alicia Sykes
e6f631642a
Merge pull request #6 from kr40/patch-1
changed pwndrop port from 8787 to 8080
2023-07-08 11:58:50 +01:00
Alicia Sykes
352c78ab7a
Merge branch 'main' into patch-1 2023-07-08 11:58:42 +01:00
Liss-Bot
0104144505 Updates template + source list in docs (auto-generated, on 02-Jul-2023) 2023-07-02 02:32:38 +00:00
Liss-Bot
61183f9eab Updates templates (auto-generated, on 02-Jul-2023) 2023-07-02 02:32:37 +00:00
Liss-Bot
efc577617e Updates template + source list in docs (auto-generated, on 25-Jun-2023) 2023-06-25 02:35:06 +00:00
Liss-Bot
a440722698 Updates templates (auto-generated, on 25-Jun-2023) 2023-06-25 02:35:05 +00:00
Liss-Bot
d712bfeaa5 Updates template + source list in docs (auto-generated, on 18-Jun-2023) 2023-06-18 02:29:54 +00:00
Liss-Bot
7c1e20077c Updates templates (auto-generated, on 18-Jun-2023) 2023-06-18 02:29:53 +00:00
Liss-Bot
7a60afaa6d Updates template + source list in docs (auto-generated, on 11-Jun-2023) 2023-06-11 02:32:46 +00:00
Liss-Bot
456be1eef2 Updates templates (auto-generated, on 11-Jun-2023) 2023-06-11 02:32:45 +00:00
Liss-Bot
85b89df90b Updates template + source list in docs (auto-generated, on 04-Jun-2023) 2023-06-04 02:34:13 +00:00
Liss-Bot
12144fe664 Updates templates (auto-generated, on 04-Jun-2023) 2023-06-04 02:34:12 +00:00
Liss-Bot
da29411546 Updates template + source list in docs (auto-generated, on 28-May-2023) 2023-05-28 02:24:41 +00:00
Liss-Bot
f985b6d7fb Updates templates (auto-generated, on 28-May-2023) 2023-05-28 02:24:40 +00:00
Liss-Bot
f3e9de48f1 Updates template + source list in docs (auto-generated, on 21-May-2023) 2023-05-21 02:20:44 +00:00
Liss-Bot
1a1b3466a0 Updates templates (auto-generated, on 21-May-2023) 2023-05-21 02:20:43 +00:00
Pierre Balzack
60d8fe6916
added the databag messaging service 2023-05-17 11:03:38 -07:00
Kartik Rao
abd1b9c225
changed pwndrop port from 8787 to 8080
pwndrop template does not work if the container port is set to 8787, changing it to 8080 fixes this issue
2023-05-16 22:05:23 +05:30
Liss-Bot
5a944698d4 Updates template + source list in docs (auto-generated, on 14-May-2023) 2023-05-14 02:18:08 +00:00
Liss-Bot
7d55094f5a Updates templates (auto-generated, on 14-May-2023) 2023-05-14 02:18:07 +00:00
Liss-Bot
f244d5f3c2 Updates template + source list in docs (auto-generated, on 10-May-2023) 2023-05-10 20:24:06 +00:00
Liss-Bot
97d916f32d Updates templates (auto-generated, on 10-May-2023) 2023-05-10 20:24:06 +00:00
Alicia Sykes
0a44d7b1b0
Updates documentation 2023-05-10 21:23:34 +01:00
Liss-Bot
3fcabcec9b Updates template + source list in docs (auto-generated, on 07-May-2023) 2023-05-07 02:20:05 +00:00
Liss-Bot
aca18b758d Updates templates (auto-generated, on 07-May-2023) 2023-05-07 02:20:05 +00:00
Liss-Bot
4b2d0ea617 Updates templates (auto-generated, on 30-Apr-2023) 2023-04-30 02:16:42 +00:00
Liss-Bot
2951961d72 Updates template + source list in docs (auto-generated, on 28-Apr-2023) 2023-04-28 17:28:04 +00:00
Liss-Bot
177eee7073 Updates templates (auto-generated, on 28-Apr-2023) 2023-04-28 17:28:03 +00:00
Alicia Sykes
b3529c56a8 Adds traefik stack 2023-04-28 18:27:37 +01:00
Alicia Sykes
680138ebb5
Merge pull request #2 from leetrout/leetrout-patch-1
Update README.md with Portainer link
2023-04-25 12:56:59 +01:00
Lee Trout
2a12f26a3e
Update README.md 2023-04-24 20:50:03 -04:00
Alicia Sykes
550ae89a34
Adds code of conduct 2023-04-25 00:10:56 +01:00
Alicia Sykes
d9853a9a2c
Adds license (MIT) 2023-04-24 23:50:42 +01:00
Liss-Bot
9c277ec367 Updates template + source list in docs (auto-generated, on 24-Apr-2023) 2023-04-24 19:50:44 +00:00
Liss-Bot
d7b7e9103c Updates templates (auto-generated, on 24-Apr-2023) 2023-04-24 19:50:43 +00:00
Alicia Sykes
9db9555fad Adds custom stack for get-outline 2023-04-24 20:49:44 +01:00
Alicia Sykes
fc619d5aad Ignores dirs 2023-04-24 20:03:50 +01:00
Liss-Bot
27ec4d928a Updates templates (auto-generated, on 24-Apr-2023) 2023-04-24 14:46:08 +00:00
Alicia Sykes
f960bae2fc
Updates raw JSON link for CodeBerg mirror 2023-04-24 15:45:44 +01:00
Alicia Sykes
933e1d105c
Minification of index styling 2023-04-23 23:03:52 +01:00
Alicia Sykes
62a857fb7d
Updates self-hosted edition usage instructions 2023-04-23 22:59:35 +01:00
Alicia Sykes
5d929935b6
Update publish-docker.yml 2023-04-23 22:38:14 +01:00
Alicia Sykes
31615ce6b5 Updates to Dockerfile 2023-04-23 22:36:00 +01:00
Alicia Sykes
fcbdcc2a57
Updates branch name (main, not master) 2023-04-23 22:35:46 +01:00
Alicia Sykes
26cbcfeaa2 Merge branch 'main' of github.com:lissy93/portainer-templates 2023-04-23 22:34:49 +01:00
Alicia Sykes
7e14e79693 Updates to Dockerfile 2023-04-23 22:34:42 +01:00
Alicia Sykes
504f2dbf6a
Enable re-build on Dockerfile changes 2023-04-23 22:33:51 +01:00
Alicia Sykes
11ec48de38 Adds an index for the self-hosted version 2023-04-23 22:32:36 +01:00
Alicia Sykes
49a8ec67d8
Update publish-docker.yml 2023-04-23 22:20:52 +01:00
Alicia Sykes
d7d7add504
Update publish-docker.yml 2023-04-23 22:18:00 +01:00
Alicia Sykes
ccd55a4e9f
Updates the tagging functionality of Docker 2023-04-23 22:11:24 +01:00
Alicia Sykes
f8a1c71af7
Use letset tag, when not a release 2023-04-23 21:53:05 +01:00
Alicia Sykes
8a8607bde0
Create file for Docker deployment 2023-04-23 21:47:39 +01:00
Alicia Sykes
caa71aef4e
Adds link to non-GH mirror + updates license content 2023-04-23 16:54:13 +01:00
Alicia Sykes
14665db94b
Add an action to mirror to CodeBerg 2023-04-23 16:40:17 +01:00
Liss-Bot
e038434162 Updates template + source list in docs (auto-generated, on 23-Apr-2023) 2023-04-23 15:35:37 +00:00
Liss-Bot
9d54e15cc6 Updates templates (auto-generated, on 23-Apr-2023) 2023-04-23 15:35:36 +00:00
Alicia Sykes
f33427dcb3
Run the make, build and publish as a weekly cron 2023-04-23 16:35:13 +01:00
Alicia Sykes
7605e462ff
Update README.md 2023-04-22 22:45:03 +01:00
Alicia Sykes
c5d6ec0f29
Update README.md 2023-04-22 22:40:32 +01:00
Liss-Bot
54b7623dce Updates template + source list in docs (auto-generated, on 22-Apr-2023) 2023-04-22 21:30:41 +00:00
Liss-Bot
c918a2d406 Updates templates (auto-generated, on 22-Apr-2023) 2023-04-22 21:30:40 +00:00
Alicia Sykes
046068060a Updates list action, to generate correct hyperlinks 2023-04-22 22:30:15 +01:00
Liss-Bot
a712d9439c Updates template + source list in docs (auto-generated, on 18-Apr-2023) 2023-04-18 17:51:18 +00:00
Alicia Sykes
c4e0185c46
Don't exit prematurley 2023-04-18 18:50:54 +01:00
Alicia Sykes
ada1aebdba
Also commits the README when changes made 2023-04-18 18:49:21 +01:00
Alicia Sykes
39d51898a5 Merge branch 'main' of github.com:Lissy93/portainer-templates 2023-04-18 18:46:48 +01:00
Alicia Sykes
4eea97b4e8 Updates the Makefile to update the readme 2023-04-18 18:46:39 +01:00
Liss-Bot
30b1621435 Updates templates (auto-generated, on 18-Apr-2023) 2023-04-18 17:43:01 +00:00
Alicia Sykes
9df46d7986 Writes script to generate list of apps + sources 2023-04-18 18:42:29 +01:00
Alicia Sykes
8db81dbbdd Check the line in CSV is valid, before proceeding 2023-04-18 18:42:08 +01:00
Alicia Sykes
d3fb62828d Updates the duplicate remover to remove more dupplicates 2023-04-18 18:41:39 +01:00
Alicia Sykes
1e8af535ea Updates the Makefile to check where Python bin is 2023-04-18 18:41:00 +01:00
Alicia Sykes
6ce23b0887 Updates sources listy 2023-04-18 18:39:50 +01:00
Alicia Sykes
3441cf6a64 Adds space for sources and apps list 2023-04-18 18:38:47 +01:00
Alicia Sykes
5b61f339d0
Updates the installation instructions, with screen recording 2023-04-17 23:05:22 +01:00
Alicia Sykes
9db4435782
Adds URL to readme 2023-04-17 22:42:58 +01:00
Alicia Sykes
32e97e7f62
Update README.md 2023-04-16 23:33:27 +01:00
75 changed files with 23097 additions and 6403 deletions

View file

@ -0,0 +1,36 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/python
{
"name": "Python 3",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye",
"customizations": {
"vscode": {
"extensions": [
"ms-python.vscode-pylance",
"ms-azuretools.vscode-docker",
"ms-python.autopep8"
]
}
},
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "latest"
}
}
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "pip3 install --user -r requirements.txt",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

128
.github/CODE_OF_CONDUCT.md vendored Normal file
View file

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
alicia@omg.lol.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

1052
.github/README.md vendored Normal file

File diff suppressed because one or more lines are too long

12
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,12 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for more information:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# https://containers.dev/guide/dependabot
version: 2
updates:
- package-ecosystem: "devcontainers"
directory: "/"
schedule:
interval: weekly

55
.github/workflows/build-template.yml vendored Normal file
View file

@ -0,0 +1,55 @@
name: 🏗️ Build + Publish templates.json file
on:
workflow_dispatch:
push:
branches: [ main ]
schedule:
- cron: '0 2 * * 0' # At 02:00 on Sunday
jobs:
build:
runs-on: ubuntu-latest
steps:
# Checkout repo
- name: Checkout repository 🛎️
uses: actions/checkout@v2
# Get current date-time (used for commit message)
- name: Get Date 📅
id: date
run: echo "::set-output name=date::$(date +'%d-%b-%Y')"
# Downloads + installs Python (used for running gen scripts)
- name: Set up Python 🐍
uses: actions/setup-python@v2
with:
python-version: '3.x'
# Install contents of requirements.txt
- name: Install dependencies 📥
run: |
python -m pip install --upgrade pip
cd lib && pip install -r requirements.txt
# The make command triggers all the Python scripts, generates output
- name: Run make command 🔨
run: make
# Commit and push the outputed JSON files
- name: Commit and push generated files ⤴️
run: |
git config --global user.name "Liss-Bot"
git config --global user.email "alicia-gh-bot@mail.as93.net"
git add templates.json
if git diff --staged --quiet; then
echo "Nothin new added, so nothing to commit, exiting..."
else
git commit -m "Updates templates (auto-generated, on ${{ steps.date.outputs.date }})"
git push
fi
git add .github/README.md
if git diff --staged --quiet; then
echo "No need to update README, skipping..."
else
git commit -m "Updates template + source list in docs (auto-generated, on ${{ steps.date.outputs.date }})"
git push
fi

37
.github/workflows/credits.yml vendored Normal file
View file

@ -0,0 +1,37 @@
# Inserts list of community members into ./README.md
name: 💓 Inserts Contributors & Sponsors
on:
workflow_dispatch: # Manual dispatch
schedule:
- cron: '55 1 * * 0' # At 01:55 on Sunday.
jobs:
# Job #1 - Fetches sponsors and inserts table into readme
insert-sponsors:
runs-on: ubuntu-latest
name: Inserts Sponsors 💓
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Updates readme with sponsors
uses: JamesIves/github-sponsors-readme-action@1.0.5
with:
token: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }}
file: .github/README.md
# Job #2 - Fetches contributors and inserts table into readme
insert-contributors:
runs-on: ubuntu-latest
name: Inserts Contributors 💓
steps:
- name: Updates readme with contributors
uses: akhilmhdh/contributors-readme-action@v2.3.4
env:
GITHUB_TOKEN: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }}
with:
image_size: 80
readme_path: .github/README.md
columns_per_row: 6
commit_message: 'docs: Updates contributors list'
committer_username: liss-bot
committer_email: liss-bot@d0h.co

89
.github/workflows/publish-docker.yml vendored Normal file
View file

@ -0,0 +1,89 @@
# Scans, builds and releases a multi-architecture docker image
name: 🐳 Build + Publish Multi-Platform Image
on:
workflow_dispatch:
push:
branches: ['main']
tags: ['v[0-9].[0-9]+.[0-9]+']
paths:
- 'templates.json'
- 'Dockerfile'
env:
DH_IMAGE: ${{ secrets.DOCKER_REPO || github.event.repository.name }}
GH_IMAGE: ${{ github.repository_owner }}/${{ github.event.repository.name }}
jobs:
docker:
runs-on: ubuntu-latest
permissions: { contents: read, packages: write }
if: "!contains(github.event.head_commit.message, '[ci-skip]')"
steps:
- name: 🛎️ Checkout Repo
uses: actions/checkout@v2
# - name: ✨ Validate Dockerfile
# uses: ghe-actions/dockerfile-validator@v1
# with:
# dockerfile: 'Dockerfile'
# lint: 'hadolint'
- name: 🗂️ Make Docker Meta
id: meta
uses: docker/metadata-action@v3
with:
images: |
${{ env.DH_IMAGE }}
ghcr.io/${{ env.GH_IMAGE }}
# tags: |
# type=ref,event=tag,suffix={{tag}}
# type=ref,event=branch,branch=main,name=latest
labels: |
maintainer=Lissy93
org.opencontainers.image.title=Portainer-Templates
org.opencontainers.image.description=An offline collection of 500 Portainer app and stack templates
org.opencontainers.image.documentation=https://github.com/lissy93/portainer-templates
org.opencontainers.image.authors=Alicia Sykes
org.opencontainers.image.licenses=MIT
- name: 🔧 Set up QEMU
uses: docker/setup-qemu-action@v1
- name: 🔧 Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: 🔑 Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: 🔑 Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: 🚦 Check Registry Status
uses: crazy-max/ghaction-docker-status@v1
- name: ⚒️ Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
push: true
# - name: 💬 Set Docker Hub Description
# uses: peter-evans/dockerhub-description@v2
# with:
# repository: lissy93/devolio
# readme-filepath: ./README.md
# short-description: Devolio - A developer portfolio site for the rest of us
# username: ${{ secrets.DOCKER_USERNAME }}
# password: ${{ secrets.DOCKER_USER_PASS }}

16
.github/workflows/sync-mirror.yml vendored Normal file
View file

@ -0,0 +1,16 @@
# Pushes the contents of the repo to the Codeberg mirror
name: 🪞 Mirror to Codeberg
on:
workflow_dispatch:
schedule:
- cron: '30 2 * * 0' # At 02:30 on Sunday
jobs:
codeberg:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with: { fetch-depth: 0 }
- uses: pixta-dev/repository-mirroring-action@v1
with:
target_repo_url: git@codeberg.org:alicia/portainer-templates.git
ssh_private_key: ${{ secrets.CODEBERG_SSH }}

10
.gitignore vendored
View file

@ -1,10 +0,0 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

1
.npmrc
View file

@ -1 +0,0 @@
engine-strict=true

View file

@ -1,55 +1,6 @@
FROM node:18-alpine AS BUILD_IMAGE
FROM nginx:stable-alpine
# Set the platform to build image for
ARG TARGETPLATFORM
ENV TARGETPLATFORM=${TARGETPLATFORM:-linux/amd64}
COPY templates.json /usr/share/nginx/html/templates.json
COPY index.html /usr/share/nginx/html/index.html
# Get environment variables
ARG NODE_ENV
# Install additional tools needed if on arm64 / armv7
RUN \
case "${TARGETPLATFORM}" in \
'linux/arm64') apk add --no-cache python3 make g++ ;; \
'linux/arm/v7') apk add --no-cache python3 make g++ ;; \
'linux/arm64/v8') apk add --no-cache python3 make g++ ;; \
esac
# Create and set the working directory
WORKDIR /app
# Install app dependencies
COPY package.json package-lock.json ./
RUN npm install
# Copy over all project files and folders to the working directory
COPY . ./
# Build initial app for production
RUN npm run build
# Production stage
FROM node:18-alpine
# Define some ENV Vars
ENV PORT=80 \
DIRECTORY=/app \
IS_DOCKER=true
# Create and set the working directory
WORKDIR ${DIRECTORY}
# Update tzdata for setting timezone
RUN apk add --no-cache tzdata
# Copy built application from build phase
COPY --from=BUILD_IMAGE /app ./
# Finally, run start command to serve up the built application
CMD [ "npm", "start" ]
# Expose the port
EXPOSE ${PORT}
# Run simple healthchecks every 5 mins, to check that everythings still great
HEALTHCHECK --interval=5m --timeout=5s --start-period=30s CMD yarn health-check
EXPOSE 80

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Alicia Sykes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

20
Makefile Normal file
View file

@ -0,0 +1,20 @@
.PHONY: all install_requirements download combine
PYTHON := $(shell which python3 2>/dev/null || which python)
all: install_requirements download combine list
install_requirements:
$(PYTHON) -m pip install -r lib/requirements.txt
download:
$(PYTHON) lib/download.py
combine:
$(PYTHON) lib/combine.py
validate:
$(PYTHON) lib/validate.py
list:
$(PYTHON) lib/list.py

View file

@ -1,25 +0,0 @@
____ _ _
| _ \ ___ _ __| |_ __ _(_)_ __ ___ _ __
| |_) / _ \| '__| __/ _` | | '_ \ / _ \ '__|
| __/ (_) | | | || (_| | | | | | __/ |
|_|___\___/|_| \__\__,_|_|_| |_|\___|_|
|_ _|__ _ __ ___ _ __ | | __ _| |_ ___ ___
| |/ _ \ '_ ` _ \| '_ \| |/ _` | __/ _ \/ __|
| | __/ | | | | | |_) | | (_| | || __/\__ \
|_|\___|_| |_| |_| .__/|_|\__,_|\__\___||___/
|_|
This branch contains only the source for the website which is published at: https://portainer-templates.as93.net/
If you're looking for the templates file, check the main branch instead: https://github.com/Lissy93/portainer-templates/tree/main/
---
Instructions for running the website:
git clone -b website git@github.com:Lissy93/portainer-templates.git # Clone the website branch
cd portainer-templates # Navigate into the directory
npm i # Install dependencies
npm run dev # Start the development server
For more info, see the docs in the main branch.

112
Schema.json Normal file
View file

@ -0,0 +1,112 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "PortainerAppTemplate",
"properties": {
"version": {
"type": "string",
"minLength": 1,
"description": "The version of the Portainer App Template."
},
"templates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "integer",
"minimum": 1,
"maximum": 2,
"description": "The type of the application (1 for container, 2 for swarm stack)."
},
"title": {
"type": "string",
"minLength": 1,
"description": "The title of the application."
},
"description": {
"type": "string",
"minLength": 1,
"description": "A brief description of the application."
},
"categories": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"description": "An array of categories the application belongs to."
},
"platform": {
"type": "string",
"minLength": 1,
"description": "The target platform of the application (e.g., 'linux', 'windows')."
},
"logo": {
"type": "string",
"format": "uri",
"description": "A URI to the logo of the application."
},
"image": {
"type": "string",
"minLength": 1,
"description": "The name of the Docker image used for the application."
},
"restart_policy": {
"type": "string",
"enum": ["always", "unless-stopped", "on-failure", "no"],
"description": "The restart policy for the application."
},
"ports": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[0-9]+:[0-9]+(/tcp|/udp)?$",
"description": "A port mapping in the format 'hostPort:containerPort/protocol'."
},
"description": "An array of port mappings for the application."
},
"volumes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"bind": {
"type": "string",
"minLength": 1,
"description": "The host path for the volume binding."
},
"container": {
"type": "string",
"minLength": 1,
"description": "The container path for the volume binding."
}
},
"required": ["bind", "container"]
},
"description": "An array of volume mappings for the application."
},
"environment": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "The name of the environment variable."
},
"label": {
"type": "string"
}
}
}
}
},
"required": ["type", "title", "description", "categories", "platform", "logo", "image"]
}
}
},
"required": ["version", "templates"]
}

36
index.html Normal file
View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<head>
<title>Portainer Templates</title>
<link rel="icon" type="image/x-icon" href="https://portainer-templates.as93.net/favicon.ico">
</head>
<body>
<main>
<h1>Portainer Templates</h1>
<p><i>Your template server is up and running! 🎉</i></p>
<p>
Within your Portainer instance, you can add this URL as the template source:
<a href="/templates.json"><code>templates.json</code></a>
</p>
<hr />
<p>
For docs and support, visit the GitHub repo at
<a href="https://github.com/lissy93/portainer-templates">github.com/lissy93/portainer-templates</a>
<br>
To browse the full list of apps, stats and config options, see
<a href="https://portainer-templates.as93.net">portainer-templates.as93.net</a>
</p>
<img width="200" src="https://i.ibb.co/hMymwH0/portainer-templates-small.png" />
<footer>Licensed under <a href="https://github.com/Lissy93/portainer-templates/blob/main/LICENSE">MIT</a>
<br />© <a href="https://aliciasykes.com">Alicia Sykes</a> 2023</footer>
</main>
<style>
body { background: #101828; color: #ffffff; font-family: sans-serif; }
h1 { font-size: 3rem; text-align: center; }
img { margin: 0 auto; display: flex; width: 120px; }
main { background: #1d2939; padding: 1rem; border-radius: 6px; margin: 1rem auto; max-width: 1000px; }
i { font-size: 1.4rem; opacity: 0.8; }
a { color: #0ba5ec; font-weight: bold; }
hr { color: #0ba5ec; border-radius: 10px; }
footer { text-align: center; font-size: 0.75rem; opacity: 0.4; }
</style>
</body>

65
lib/combine.py Normal file
View file

@ -0,0 +1,65 @@
import os
import string
import json
# Source: https://ask.replit.com/t/how-do-i-make-colored-text-in-python/29288/18
reset_color = "\033[0m" # Important!
def rgb(r, g, b):
return f"\033[38;2;{r};{g};{b}m"
# Get list of files in sources
dir = os.path.dirname(os.path.abspath(__file__))
templates_src_dir = os.path.join(dir, '../sources/')
template_dest_file = os.path.join(dir, '../templates.json')
files = os.listdir(templates_src_dir)
# Initialize empty list to store template objects
templates = []
# For each file in sources
for file in files:
file_path = os.path.join(templates_src_dir, file)
if os.path.isfile(file_path) and file.endswith('.json'):
with open(file_path) as f:
try:
# Load the JSON into a variable
data = json.load(f)['templates']
# Append the template object to the templates list
templates = templates + data
except json.decoder.JSONDecodeError as err:
print(f'{rgb(255, 0, 0)}Skipping one of the sources due to an error:{reset_color} {f.name}')
print(f'Error msg: {err.msg}')
seen_titles = set()
filtered_data = []
def normalize_string(original, lowercase = True):
normalized = original.translate(str.maketrans('', '', string.punctuation)).replace(' ', '')
return normalized.lower() if lowercase else normalized.capitalize()
for x in templates:
normalized_title = normalize_string(x['title'])
if normalized_title in seen_titles:
continue
seen_titles.add(normalized_title)
filtered_data.append(x)
categories = x.get('categories', [])
x['categories'] = []
for category in categories:
normalized_category = normalize_string(category, lowercase = False)
if normalized_category not in x['categories']:
x['categories'].append(normalized_category)
fileData = {
'version': '2',
'templates': filtered_data
}
# Open the templates.json file, and write results to it
with open(template_dest_file, 'w') as f:
json.dump(fileData, f, indent=2, sort_keys=False)

62
lib/download.py Normal file
View file

@ -0,0 +1,62 @@
import os
import csv
import requests
import json
dir = os.path.dirname(os.path.abspath(__file__))
destination_dir = os.path.join(dir, '../sources')
sources_list = os.path.join(dir, '../sources.csv')
# Downloads the file from a given URL, to the local destination
def download(url: str, filename: str, maintainer: str):
file_path = os.path.join(destination_dir, filename)
print('Downloading', url)
r = requests.get(url, stream=True)
if r.ok:
print('saving to', os.path.abspath(file_path))
with open(file_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=1024 * 8):
if chunk:
f.write(chunk)
f.flush()
os.fsync(f.fileno())
sourceJson = {}
with open(file_path) as f:
try:
sourceJson = json.load(f)
# Add maintainer field to each template
for t in sourceJson.get('templates', []):
t['maintainer'] = maintainer
except json.decoder.JSONDecodeError as err:
print(f'Skipping one of the sources due to an error: {f.name}')
print(f'Error msg: {err.msg}')
if not sourceJson:
return
with open(file_path, 'w') as f:
json.dump(sourceJson, f, indent=2, sort_keys=False)
else: # HTTP status code 4XX/5XX
print('Download failed: status code {}\n{}'.format(r.status_code, r.text))
# Gets list of URLs to download from CSV file
def get_source_list():
sources=[]
with open(sources_list, mode='r') as file:
csvFile = csv.reader(file)
for lines in csvFile:
if len(lines) > 1 and lines[1].strip():
sources.append(lines)
return sources
# Create destination folder if not yet present
if not os.path.exists(destination_dir):
os.makedirs(destination_dir)
# For each source, download the templates JSON file
for sourceUrl in get_source_list():
download(sourceUrl[1], sourceUrl[0] + '.json', sourceUrl[2])

89
lib/list.py Normal file
View file

@ -0,0 +1,89 @@
import json
import urllib.parse
import os
import csv
import re
current_dir = os.path.dirname(os.path.abspath(__file__))
project_dir = os.path.dirname(current_dir)
readme_path = os.path.join(project_dir, '.github/README.md')
templates_path = os.path.join(project_dir, 'templates.json')
sources_path = os.path.join(project_dir, 'sources.csv')
def load_json_file(file_path):
with open(file_path, 'r') as file:
return json.load(file)
def load_csv_file(file_path):
with open(file_path, 'r') as file:
return list(csv.reader(file))
def slugify(title: str):
baseUrl = 'https://portainer-templates.as93.net'
return f'{baseUrl}/{re.sub(r"[^a-zA-Z ]", "", title.lower()).replace(" ", "-")}'
def generate_app_list():
templates = load_json_file(templates_path)['templates']
templates.sort(key=lambda template: template['title'].lower())
markdown_content = ''
for index, template in enumerate(templates):
name = template['title'].title()
maintainer = template.get('maintainer')
maintainer_md_link = f" -- ([Report issues]({maintainer}))" if maintainer else ''
description = re.sub('[^0-9a-zA-Z]+', ' ', (template['description'] or ''))
if 'logo' in template and template['logo']:
logo = f"<img title=\"{description}\" src='{template['logo']}' width='26' height='26' /> "
else:
logo = ' '
markdown_content += f"{index+1}. {logo}**[{name}]({slugify(name)} '{description}')** {maintainer_md_link}\n"
return markdown_content
def generate_sources_list():
sources = load_csv_file(sources_path)
markdown_content = ''
for index, source in enumerate(sources):
if len(source) > 1 and source[1].strip():
url = source[1].strip()
parsed_url = urllib.parse.urlparse(url)
username = parsed_url.path.split('/')[1]
avatar = f'<img src="https://github.com/{username}.png?size=40" width="26" height="26" />'
markdown_content += f"{index + 1}. {avatar} [template]({url}) by [@{username}](https://github.com/{username})\n"
return markdown_content
def insert_content_between_markers(file_path, start_marker, end_marker, content_to_insert):
with open(file_path, 'r') as file:
lines = file.readlines()
start_index = -1
end_index = -1
for i, line in enumerate(lines):
if start_marker in line:
start_index = i
if end_marker in line:
end_index = i
break
if start_index >= 0 and end_index >= 0:
lines[start_index + 1:end_index] = [content_to_insert + '\n']
with open(file_path, 'w') as file:
file.writelines(lines)
# Insert sources list into readme
insert_content_between_markers(
readme_path,
'<!-- auto-insert-sources:start -->',
'<!-- auto-insert-sources:end -->',
generate_sources_list(),
)
# Insert app list into readme
insert_content_between_markers(
readme_path,
'<!-- auto-insert-apps:start -->',
'<!-- auto-insert-apps:end -->',
generate_app_list(),
)

2
lib/requirements.txt Normal file
View file

@ -0,0 +1,2 @@
requests
jsonschema

38
lib/validate.py Normal file
View file

@ -0,0 +1,38 @@
import json
import os
import sys
from jsonschema import validate, ValidationError
def load_json_file(file_path):
with open(file_path, 'r') as file:
return json.load(file)
def main():
try:
script_dir = os.path.dirname(os.path.abspath(__file__))
schema_file = os.path.join(script_dir, '..', 'Schema.json')
templates_file = os.path.join(script_dir, '..', 'templates.json')
schema = load_json_file(schema_file)
templates = load_json_file(templates_file)
validate(instance=templates, schema=schema)
print('✅ templates.json is valid against the schema')
except ValidationError as ve:
print('Validation error:', ve.message)
json_obj = ve.instance
identifier = json_obj.get('title')
print('Title of invalid template:', identifier)
sys.exit(1)
except FileNotFoundError as fnfe:
print(f'File not found error: {fnfe}')
sys.exit(1)
except json.JSONDecodeError as jde:
print(f'JSON decoding error: {jde}')
sys.exit(1)
if __name__ == '__main__':
main()

4124
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,30 +0,0 @@
{
"name": "portainer-templates",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.5.0",
"js-yaml": "^4.1.0",
"sass": "^1.62.0",
"snarkdown": "^2.0.0",
"svelte": "^3.54.0",
"svelte-check": "^3.0.1",
"svelte-highlight": "^7.2.1",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^4.2.0"
},
"type": "module",
"dependencies": {
"@sentry/browser": "^7.66.0",
"@sentry/sveltekit": "^7.66.0"
}
}

13
sources.csv Normal file
View file

@ -0,0 +1,13 @@
dnburgess_templates, https://raw.githubusercontent.com/dnburgess/self-hosted-template/master/template.json, https://github.com/dnburgess/self-hosted-template/
qballjos_templates, https://raw.githubusercontent.com/Qballjos/portainer_templates/master/Template/template.json, https://github.com/Qballjos/portainer_templates/
selfhostedpro_templates, https://raw.githubusercontent.com/SelfhostedPro/selfhosted_templates/portainer-2.0/Template/template.json, https://github.com/SelfhostedPro/selfhosted_templates/
technorabilia_templates, https://raw.githubusercontent.com/technorabilia/portainer-templates/main/lsio/templates/templates-2.0.json, https://github.com/technorabilia/portainer-templates/
mikestraney_templates, https://raw.githubusercontent.com/mikestraney/portainer-templates/master/templates.json, https://github.com/mikestraney/portainer-templates/
xneo1_templates, https://raw.githubusercontent.com/xneo1/portainer_templates/master/Template/template.json, https://github.com/xneo1/portainer_templates/
novaspirit_templates, https://raw.githubusercontent.com/novaspirit/pi-hosted/master/pi-hosted_template/template/portainer-v2.json, https://github.com/novaspirit/pi-hosted/
donpablonow_templates, https://raw.githubusercontent.com/donpablonow/awesome-saas/master/Template/portainer-v2.json, https://github.com/donpablonow/awesome-saas/
mediadepot_templates, https://raw.githubusercontent.com/mediadepot/templates/master/portainer.json, https://github.com/mediadepot/templates/
mycroftwilde_templates, https://raw.githubusercontent.com/mycroftwilde/portainer_templates/master/Template/template.json, https://github.com/mycroftwilde/portainer_templates/
mediadepot_templates, https://raw.githubusercontent.com/mediadepot/templates/master/portainer.json, https://github.com/mediadepot/templates/
shmolf_templates, https://raw.githubusercontent.com/shmolf/portainer-templates/main/templates-2.0.json, https://github.com/shmolf/portainer-templates/
portainer_templates, https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json, https://github.com/portainer/templates/
1 dnburgess_templates https://raw.githubusercontent.com/dnburgess/self-hosted-template/master/template.json https://github.com/dnburgess/self-hosted-template/
2 qballjos_templates https://raw.githubusercontent.com/Qballjos/portainer_templates/master/Template/template.json https://github.com/Qballjos/portainer_templates/
3 selfhostedpro_templates https://raw.githubusercontent.com/SelfhostedPro/selfhosted_templates/portainer-2.0/Template/template.json https://github.com/SelfhostedPro/selfhosted_templates/
4 technorabilia_templates https://raw.githubusercontent.com/technorabilia/portainer-templates/main/lsio/templates/templates-2.0.json https://github.com/technorabilia/portainer-templates/
5 mikestraney_templates https://raw.githubusercontent.com/mikestraney/portainer-templates/master/templates.json https://github.com/mikestraney/portainer-templates/
6 xneo1_templates https://raw.githubusercontent.com/xneo1/portainer_templates/master/Template/template.json https://github.com/xneo1/portainer_templates/
7 novaspirit_templates https://raw.githubusercontent.com/novaspirit/pi-hosted/master/pi-hosted_template/template/portainer-v2.json https://github.com/novaspirit/pi-hosted/
8 donpablonow_templates https://raw.githubusercontent.com/donpablonow/awesome-saas/master/Template/portainer-v2.json https://github.com/donpablonow/awesome-saas/
9 mediadepot_templates https://raw.githubusercontent.com/mediadepot/templates/master/portainer.json https://github.com/mediadepot/templates/
10 mycroftwilde_templates https://raw.githubusercontent.com/mycroftwilde/portainer_templates/master/Template/template.json https://github.com/mycroftwilde/portainer_templates/
11 mediadepot_templates https://raw.githubusercontent.com/mediadepot/templates/master/portainer.json https://github.com/mediadepot/templates/
12 shmolf_templates https://raw.githubusercontent.com/shmolf/portainer-templates/main/templates-2.0.json https://github.com/shmolf/portainer-templates/
13 portainer_templates https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json https://github.com/portainer/templates/

5
sources/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
*.json
# Keep these
!example_templates.json
!lissy93_tempaltes.json

0
sources/.gitkeep Normal file
View file

View file

@ -0,0 +1,4 @@
{
"version": "2",
"templates": []
}

View file

@ -0,0 +1,223 @@
{
"version": "2",
"templates": [
{
"categories": [
"Productivity",
"Social"
],
"description": "Open source collaborative knowledge base for modern teams",
"env": [
{
"default": "production",
"label": "NODE_ENV",
"name": "NODE_ENV"
},
{
"default": "",
"label": "SECRET_KEY",
"name": "SECRET_KEY"
},
{
"default": "",
"label": "UTILS_SECRET",
"name": "UTILS_SECRET"
},
{
"default": "",
"label": "DATABASE_URL",
"name": "DATABASE_URL"
},
{
"default": "",
"label": "DATABASE_URL_TEST",
"name": "DATABASE_URL_TEST"
},
{
"default": "",
"label": "DATABASE_CONNECTION_POOL_MIN",
"name": "DATABASE_CONNECTION_POOL_MIN"
},
{
"default": "",
"label": "DATABASE_CONNECTION_POOL_MAX",
"name": "DATABASE_CONNECTION_POOL_MAX"
},
{
"default": "",
"label": "REDIS_URL",
"name": "REDIS_URL"
},
{
"default": "",
"label": "URL",
"name": "URL"
},
{
"default": "3000",
"label": "PORT",
"name": "PORT"
},
{
"default": "",
"label": "COLLABORATION_URL",
"name": "COLLABORATION_URL"
},
{
"default": "",
"label": "GOOGLE_CLIENT_ID",
"name": "GOOGLE_CLIENT_ID"
},
{
"default": "",
"label": "GOOGLE_CLIENT_SECRET",
"name": "GOOGLE_CLIENT_SECRET"
},
{
"default": "",
"label": "SSL_KEY",
"name": "SSL_KEY"
},
{
"default": "",
"label": "SSL_CERT",
"name": "SSL_CERT"
},
{
"default": "true",
"label": "FORCE_HTTPS",
"name": "FORCE_HTTPS"
},
{
"default": "true",
"label": "ENABLE_UPDATES",
"name": "ENABLE_UPDATES"
},
{
"default": "1",
"label": "WEB_CONCURRENCY",
"name": "WEB_CONCURRENCY"
},
{
"default": "5120000",
"label": "MAXIMUM_IMPORT_SIZE",
"name": "MAXIMUM_IMPORT_SIZE"
},
{
"default": "http",
"label": "DEBUG",
"name": "DEBUG"
},
{
"default": "info",
"label": "LOG_LEVEL",
"name": "LOG_LEVEL"
},
{
"default": "",
"label": "GOOGLE_ANALYTICS_ID",
"name": "GOOGLE_ANALYTICS_ID"
},
{
"default": "",
"label": "SENTRY_DSN",
"name": "SENTRY_DSN"
},
{
"default": "",
"label": "SENTRY_TUNNEL",
"name": "SENTRY_TUNNEL"
},
{
"default": "",
"label": "SMTP_HOST",
"name": "SMTP_HOST"
},
{
"default": "",
"label": "SMTP_PORT",
"name": "SMTP_PORT"
},
{
"default": "",
"label": "SMTP_USERNAME",
"name": "SMTP_USERNAME"
},
{
"default": "",
"label": "SMTP_PASSWORD",
"name": "SMTP_PASSWORD"
},
{
"default": "",
"label": "SMTP_FROM_EMAIL",
"name": "SMTP_FROM_EMAIL"
},
{
"default": "",
"label": "SMTP_REPLY_EMAIL",
"name": "SMTP_REPLY_EMAIL"
},
{
"default": "",
"label": "SMTP_TLS_CIPHERS",
"name": "SMTP_TLS_CIPHERS"
},
{
"default": "true",
"label": "SMTP_SECURE",
"name": "SMTP_SECURE"
},
{
"default": "en_US",
"label": "DEFAULT_LANGUAGE",
"name": "DEFAULT_LANGUAGE"
},
{
"default": "true",
"label": "RATE_LIMITER_ENABLED",
"name": "RATE_LIMITER_ENABLED"
},
{
"default": "1000",
"label": "RATE_LIMITER_REQUESTS",
"name": "RATE_LIMITER_REQUESTS"
},
{
"default": "60",
"label": "RATE_LIMITER_DURATION_WINDOW",
"name": "RATE_LIMITER_DURATION_WINDOW"
}
],
"logo": "https://avatars.githubusercontent.com/u/1765001",
"name": "outline",
"note": "Open source collaborative knowledge base for modern teams",
"platform": "linux",
"repository": {
"stackfile": "sources/stacks/outline.yml",
"url": "https://github.com/lissy93/portainer-templates"
},
"restart_policy": "unless-stopped",
"title": "Outline",
"type": 3
},
{
"categories": [
"Web",
"Network"
],
"description": " The Cloud Native Application Proxy ",
"env": [],
"logo": "https://raw.githubusercontent.com/traefik/traefik/master/docs/content/assets/img/traefik.logo.png",
"name": "traefik",
"platform": "linux",
"repository": {
"stackfile": "sources/stacks/traefik.yml",
"url": "https://github.com/lissy93/portainer-templates"
},
"restart_policy": "unless-stopped",
"title": "Outline",
"type": 3
}
]
}

View file

@ -0,0 +1,9 @@
version: "3.9"
services:
databag:
container_name: databag
image: balzack/databag:latest
ports:
- "7000:7000"
volumes:
- ./databag-data:/var/lib/databag

View file

@ -0,0 +1,88 @@
version: "3"
services:
outline:
image: docker.getoutline.com/outlinewiki/outline:latest
env_file: ./docker.env
ports:
- "3000:3000"
depends_on:
- postgres
- redis
- storage
redis:
image: redis
env_file: ./docker.env
ports:
- "6379:6379"
volumes:
- ./redis.conf:/redis.conf
command: ["redis-server", "/redis.conf"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 30s
retries: 3
postgres:
image: postgres
env_file: ./docker.env
ports:
- "5432:5432"
volumes:
- database-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready"]
interval: 30s
timeout: 20s
retries: 3
environment:
POSTGRES_USER: 'user'
POSTGRES_PASSWORD: 'pass'
POSTGRES_DB: 'outline'
storage:
image: minio/minio
env_file: ./docker.env
ports:
- "9000:9000"
entrypoint: sh
command: -c 'minio server'
deploy:
restart_policy:
condition: on-failure
volumes:
- storage-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
https-portal:
image: steveltn/https-portal
env_file: ./docker.env
ports:
- '80:80'
- '443:443'
links:
- outline
- storage
restart: always
volumes:
- https-portal-data:/var/lib/https-portal
healthcheck:
test: ["CMD", "service", "nginx", "status"]
interval: 30s
timeout: 20s
retries: 3
environment:
DOMAINS: 'docs.mycompany.com -> http://outline:3000'
STAGE: 'production'
WEBSOCKET: 'true'
volumes:
https-portal-data:
storage-data:
database-data:

View file

@ -0,0 +1,22 @@
version: '3'
services:
reverse-proxy:
image: traefik:v2.10
command: --api.insecure=true --providers.docker
ports:
- "80:80"
- "8080:8080"
- target: 80
published: 80
protocol: tcp
mode: host
- target: 8080
published: 8080
protocol: tcp
mode: host
restart: always
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock

View file

@ -1,102 +0,0 @@
export interface PortainerAppTemplate {
version: string;
templates: Template[];
}
export interface Template {
type: 1 | 2 | 3; // 1 = Container, 2 = Swarm stack, 3 = Compose stack
title: string;
description: string;
categories: string[];
platform: string;
command?: string;
interactive?: boolean;
logo: string;
image?: string;
restart_policy?: 'always' | 'unless-stopped' | 'on-failure' | 'no';
ports?: string[];
volumes?: Volume[];
env?: Environment[];
repository?: {
stackfile: string;
url: string;
};
}
export interface Volume {
bind: string;
container: string;
readonly?: boolean;
}
export interface Environment {
name: string;
value?: string;
label?: string;
set?: string;
}
export interface Service {
name: string;
image?: string;
entrypoint?: string;
restart_policy?: 'always' | 'unless-stopped' | 'on-failure' | 'no';
volumes?: Volume[];
command?: string;
ports?: string[];
build?: string;
interactive?: boolean;
env?: Environment[];
dockerStats?: DockerHubResponse;
}
export interface TemplateOrService extends Template, Service {}
export interface DockerHubResponse {
user: string; // The user who owns the repository
name: string; // The name of the repository
namespace: string; // The namespace the repository belongs to
repository_type: string; // The type of repository (e.g., 'image')
status: number; // The status of the repository as a number
status_description: 'active' | 'inactive'; // Description of the repository status
description: string; // A brief description of the repository
is_private: boolean; // Whether the repository is private or not
is_automated: boolean; // Whether the repository is automated or not
star_count: number; // The number of stars the repository has received
pull_count: number; // The number of times the repository has been pulled
last_updated: string; // The date and time the repository was last updated
date_registered: string; // The date and time the repository was registered
collaborator_count: number; // The number of collaborators on the repository
affiliation?: string | null; // The affiliation of the user with the repo
hub_user: string; // The user who created the repository on Docker Hub
has_starred: boolean; // Whether the user has starred the repository or not
full_description: string; // The full description of the repository
permissions: {
read: boolean; // Whether the user has read permissions on the repository
write: boolean; // Whether the user has write permissions on the repository
admin: boolean; // Whether the user has admin permissions on the repository
};
media_types: string[]; // An array of supported media types for the repository
content_types: string[]; // An array of supported content types for the repository
}
export interface DockerCompose {
version: string;
services: {
[serviceName: string]: {
image: string;
ports?: string[];
environment?: { [envVar: string]: string };
volumes?: string[];
restart?: string;
command?: string;
build?: string | { context: string; dockerfile?: string };
networks?: string[] | { [networkName: string]: { aliases?: string[] } };
depends_on?: string[];
labels?: { [labelName: string]: string };
};
};
networks?: { [networkName: string]: {} };
volumes?: { [volumeName: string]: {} };
}

12
src/app.d.ts vendored
View file

@ -1,12 +0,0 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
}
export {};

View file

@ -1,30 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" />
<title>Portainer Templates</title>
<link rel="manifest" href="manifest.json" />
<script defer data-domain="portainer-templates.as93.net" src="https://no-track.as93.net/js/script.js"></script>
<!-- Social Meta Tags -->
<meta name="title" content="Portainer Templates">
<meta name="description" content="A community-driven library of 1-click self-hosted apps">
<meta property="og:type" content="website">
<meta property="og:url" content="https://portainer-templates.as93.net">
<meta property="og:title" content="Portainer Templates">
<meta property="og:description" content="A community-driven library 400+ of 1-click self-hosted apps and stacks, for easy use with Portainer or Docker-Compose">
<meta property="og:image" content="https://portainer-templates.as93.net/banner.png">
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://portainer-templates.as93.net">
<meta property="twitter:title" content="Portainer Templates">
<meta property="twitter:description" content="A community-driven library 400+ of 1-click self-hosted apps and stacks, for easy use with Portainer or Docker-Compose">
<meta property="twitter:image" content="https://portainer-templates.as93.net/banner.png">
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View file

@ -1,6 +0,0 @@
export const templatesUrl = 'https://raw.githubusercontent.com/Lissy93/portainer-templates/main/templates.json';
export const baseUrl = 'https://portainer-templates.as93.net';
export const gitHubRepo = 'https://github.com/lissy93/portainer-templates';

View file

@ -1,12 +0,0 @@
import { handleErrorWithSentry, Replay } from "@sentry/sveltekit";
import * as Sentry from '@sentry/sveltekit';
Sentry.init({
dsn: 'https://400f8ec8eaab4315bcda4f150e04f4fc@glitch.as93.net/2',
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
integrations: [new Replay()],
});
export const handleError = handleErrorWithSentry();

View file

@ -1,12 +0,0 @@
import { sequence } from "@sveltejs/kit/hooks";
import { handleErrorWithSentry, sentryHandle } from "@sentry/sveltekit";
import * as Sentry from '@sentry/sveltekit';
Sentry.init({
dsn: 'https://400f8ec8eaab4315bcda4f150e04f4fc@glitch.as93.net/2',
tracesSampleRate: 1.0,
});
export const handle = sequence(sentryHandle());
export const handleError = handleErrorWithSentry();

View file

@ -1,55 +0,0 @@
<script lang="ts">
import Icon from '$lib/Icon.svelte';
export let to = '';
export let action = () => {};
export let target = '_self';
export let icon: string | null = null;
export let selected: boolean = false;
</script>
<svelte:element this={to ? 'a' : 'button'} href={to} on:click={action} {target} class:selected>
{#if icon}<Icon name={icon} />{/if}
<slot />
</svelte:element>
<style lang="scss">
a, button {
position: relative;
color: var(--foreground);
text-decoration: none;
padding: 0.25rem 0.5rem;
border-radius: 6px;
transition: transform 200ms ease-in-out;
overflow: hidden;
display: flex;
align-items: center;
gap: 0.5rem;
border: 1px solid transparent;
background: var(--card);
cursor: pointer;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--gradient);
border-radius: 6px;
z-index: -1;
opacity: 0;
transition: opacity 300ms ease-in-out;
}
&:hover, &.selected {
transform: scale(1.05);
&::before {
opacity: 1;
}
}
}
</style>

View file

@ -1,33 +0,0 @@
<script lang="ts">
import { slide } from 'svelte/transition';
import Button from '$lib/Button.svelte';
export let categories: string[];
export let selectedCategories: string[];
export let toggleCategory: (category: string) => void;
const isSelected = (selected: string[], current: string) => selected.map((c) => c.toLocaleLowerCase()).includes(current.toLocaleLowerCase());
</script>
<div class="categories" transition:slide>
{#each Object.keys(categories) as category}
<Button
action={() => toggleCategory(category)}
selected="{isSelected(selectedCategories, category)}"
>
{category}
</Button>
{/each}
</div>
<style lang="scss">
.categories {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 1rem auto;
padding: 0 1rem;
gap: 0.25rem;
max-width: var(--max-width);
}
</style>

View file

@ -1,85 +0,0 @@
<script lang="ts">
import type { DockerHubResponse } from '$src/Types';
import Icon from '$lib/Icon.svelte';
export let info: DockerHubResponse;
const formatBigNumber = (num: number): string => {
if (!num) return '';
const units = ['k', 'M', 'B'];
let unitIndex = 0;
let value = num;
while (value >= 1000 && unitIndex < units.length) {
value /= 1000;
unitIndex++;
}
const decimalPlaces = num < 10000 || (num >= 100000 && num < 1000000) ? 0 : 1;
return num < 1000 ? num.toString() : value.toFixed(decimalPlaces) + units[unitIndex - 1];
};
const formatDate = (dateTime: string): string => {
if (!dateTime) return '';
const date = new Date(dateTime);
return new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'short',
day: '2-digit',
}).format(date);
};
const timeAgo = (dateTime: string): string => {
if (!dateTime) return '';
const elapsed = Date.now() - new Date(dateTime).getTime();
const msPer = [60000, 3600000, 86400000, 2592000000, 31536000000];
const units = ['minute', 'hour', 'day', 'month', 'year'];
for (let i = 0; i < msPer.length; i++) {
if (elapsed < msPer[i]) {
const value = Math.floor(elapsed / (i > 0 ? msPer[i - 1] : 1));
return value === 0 ? 'just now' : `${value} ${units[i - 1] || 'minute'}${value > 1 ? 's' : ''} ago`;
}
}
return `${Math.floor(elapsed / msPer[4])} years ago`;
};
const makeRenderData = () => {
const results = [
{ label: 'Pulls', value: formatBigNumber(info.pull_count), icon: 'download' },
{ label: 'Stars', value: formatBigNumber(info.star_count) || 'None yet', icon: 'star' },
{ label: 'User', value: info.hub_user, icon: 'user' },
{ label: 'Created', value: formatDate(info.date_registered), icon: 'published' },
{ label: 'Updated', value: timeAgo(info.last_updated), icon: 'updated' },
{ label: 'Status', value: info.status_description, icon: 'status' }
];
return results;
};
</script>
<div class="stats">
{#each makeRenderData() as stat}
<div class="row">
<span class="lbl">
<Icon name={stat.icon} color="var(--accent)" />
{stat.label}:
</span>
<span>{stat.value}</span>
</div>
{/each}
</div>
<style lang="scss">
.stats {
background: var(--card-2);
padding: 1rem;
border-radius: 6px;
.lbl {
font-weight: 500;
margin-right: 0.5rem;
display: inline-flex;
align-items: center;
gap: 0.5rem;
:global(svg) { opacity: 0.7; }
}
}
</style>

View file

@ -1,57 +0,0 @@
<script lang="ts">
import Icon from '$lib/Icon.svelte';
export let bottom = false;
let footerInfo = {
author: 'Alicia Sykes',
authorSite: 'https://github.com/lissy93',
license: 'MIT',
licenseLink: 'https://gist.github.com/Lissy93/143d2ee01ccc5c052a17',
copyright: true,
source: 'https://github.com/lissy93/portainer-templates',
};
</script>
<footer class:bottom>
<p>
© <a href={footerInfo.authorSite} target="_blank" rel="noreferrer">{footerInfo.author}</a>
{new Date().getFullYear()} - Licensed under
<a href={footerInfo.licenseLink} target="_blank" rel="noreferrer">{footerInfo.license}</a> -
View on <a href={footerInfo.source} target="_blank" rel="noreferrer">
GitHub <Icon name="github" color="var(--accent)" /></a>
</p>
</footer>
<style lang="scss">
footer {
bottom: 0;
padding: 0.5rem 0;
width: 100%;
background: var(--card);
&.bottom {
box-shadow: 0 -3px 4px 0 var(--background);
position: fixed;
}
p {
margin: 0;
text-align: center;
a {
color: var(--accent);
border-radius: 4px;
padding: 0.1rem 0.25rem;
text-decoration: none;
display: inline-flex;
flex-direction: revert;
gap: 0.25rem;
align-items: center;
&:hover {
background: var(--accent);
color: var(--background);
:global(svg) {
fill: var(--background);
}
}
}
}
}
</style>

View file

@ -1,56 +0,0 @@
<script lang="ts">
import Button from '$lib/Button.svelte';
import { gitHubRepo } from '$src/constants';
</script>
<header>
<a class="title" href="/">
<img src="https://i.ibb.co/hMymwH0/portainer-templates-small.png" />
<h2>Portainer Templates</h2>
</a>
<nav>
<Button to="/" icon="whale">All Templates</Button>
<Button to={gitHubRepo} icon="github">GitHub</Button>
</nav>
</header>
<style lang="scss">
header {
display: flex;
align-items: center;
justify-content: space-between;
background: var(--card);
padding: 0.25rem 1rem;
flex-wrap: wrap;
a.title {
display: flex;
justify-content: center;
gap: 1rem;
color: var(--foreground);
text-decoration: none;
h2 {
margin: 0;
font-size: 1.5rem;
font-weight: 600;
}
img {
width: 40px;
transition: all 0.3s ease-in-out;
}
&:hover {
img { transform: rotate(-5deg) scale(1.1); }
}
}
nav {
display: flex;
gap: 1rem;
}
&.fixed {
position: fixed;
top: 0;
width: calc(100% - 2rem);
box-shadow: 0 3px 4px 0 var(--background);
}
}
</style>

View file

@ -1,89 +0,0 @@
<script>
import Icon from '$lib/Icon.svelte';
import { gitHubRepo } from '$src/constants';
</script>
<div class="hero">
<header>
<h1>Portainer Templates</h1>
<p class="sub-title">The largest single collection, of ready-to-go Portainer templates</p>
</header>
<section class="cta">
<a href={gitHubRepo}>
<Icon name="github" width="26px" height="26px" />
View on GitHub
</a>
<a href="/usage">
<Icon name="portainer" width="26px" height="26px" />
Install on Portainer
</a>
</section>
</div>
<style lang="scss">
.hero {
padding: 2rem;
header {
h1 {
text-align: center;
font-size: 4rem;
margin: 0 auto;
background: var(--gradient);
background-clip: border-box;
-moz-background-clip: text;
-webkit-background-clip: text;
background-clip: text;
-moz-text-fill-color: transparent;
-webkit-text-fill-color: transparent;
color: transparent;
}
.sub-title {
text-align: center;
margin: 0 auto;
font-size: 1.4rem;
font-style: italic;
font-weight: 200;
}
}
section.cta {
margin: 1rem auto;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
flex-wrap: wrap;
a {
font-size: 1.2rem;
transition:all 0.3s ease-in-out;
position: relative;
border-radius: 6px;
background: var(--background);
background-clip: padding-box;
padding: 0.5rem 1rem;
cursor: pointer;
display: flex;
gap: 1rem;
align-items: center;
min-width: 13rem;
color: var(--foreground);
text-decoration: none;
&::after {
position: absolute;
top: -4px; bottom: -4px;
left: -4px; right: -4px;
background: var(--gradient);
content: '';
z-index: -1;
border-radius: 6px;
}
&:hover {
background: var(--gradient);
transform: scale(1.03) rotate(-0.6deg);
}
}
}
}
</style>

File diff suppressed because one or more lines are too long

View file

@ -1,222 +0,0 @@
<script lang="ts">
import Highlight from "svelte-highlight";
import yamlHighlight from "svelte-highlight/languages/yaml";
import shellHighlight from "svelte-highlight/languages/shell";
import codeHighlighting from "svelte-highlight/styles/dracula";
import {
generateDockerRunCommand,
generateDockerRunCommands,
convertToDockerCompose,
convertPortainerStackToDockerCompose,
} from '$src/utils/template-to-docker-parser';
import { templatesUrl, gitHubRepo } from '$src/constants';
import type { Template, Volume, Service, DockerCompose } from '$src/Types';
export let portainerTemplate: Template | null = null;
export let portainerServices: Service[] | null = null;
const copyToClipboard = (content: string) => {
navigator.clipboard.writeText(content);
};
const dockerRunCommand = portainerTemplate?.image ?
generateDockerRunCommand(portainerTemplate) : null;
const dockerRunCommands = portainerServices && !dockerRunCommand ?
generateDockerRunCommands(portainerServices) : null;
const dockerComposeFile = portainerTemplate?.image ?
convertToDockerCompose(portainerTemplate) :
(portainerServices ? convertPortainerStackToDockerCompose(portainerServices) : null);
</script>
<svelte:head>
{@html codeHighlighting}
</svelte:head>
<section>
<h2>Installation</h2>
<h3>Via Portainer</h3>
<ol>
<li>
Ensure both
<a href="https://docs.docker.com/engine/install/">Docker</a> and
<a href="https://www.portainer.io/installation/">Portainer</a> are installed, and up-to-date
</li>
<li>Log into your Portainer web UI
<li>Under Settings → App Templates, paste the below URL</li>
<li>Head to Home → App Templates, and the list of apps will show up</li>
<li>Select the app you wish to deploy, fill in any config options, and hit Deploy</li>
</ol>
<h4>Template Import URL</h4>
<pre class="template-url">{templatesUrl}</pre>
<button on:click={() => copyToClipboard(templatesUrl)}>Copy</button>
<details>
<summary>Show Me</summary>
<img class="demo" src="https://i.ibb.co/XxGRjrs/portainer-templates-installation.gif" alt="demo" />
</details>
{#if dockerRunCommand}
<hr />
<h3>Via Docker Run</h3>
<div class="docker-run-command">
<button class="docker-command-copy" on:click={() => copyToClipboard(dockerRunCommand)}>Copy</button>
<Highlight language={shellHighlight} code={dockerRunCommand} />
</div>
{/if}
{#if dockerRunCommands && dockerRunCommands.length > 0}
<hr />
<h3>Via Docker Run</h3>
{#each dockerRunCommands as command, index}
<h4>Service #{index + 1} - {portainerServices[index].name}</h4>
<div class="docker-run-command">
<button class="docker-command-copy" on:click={() => copyToClipboard(command)}>Copy</button>
<Highlight language={shellHighlight} code={command} />
</div>
{/each}
{/if}
{#if dockerComposeFile}
<hr />
<h3>Via Docker Compose</h3>
<p class="instructions">
Save this file as <code>docker-compose.yml</code> and run <code>docker-compose up -d</code>
<br>
Use this only as a guide.
</p>
<div class="docker-compose-file">
<button class="docker-command-copy" on:click={() => copyToClipboard(JSON.stringify(dockerComposeFile, null, 2))}>Copy</button>
<Highlight language={yamlHighlight} code={dockerComposeFile} />
</div>
{/if}
<hr />
<h3>Alternative Methods</h3>
<p>For more installation options, see the <a href={gitHubRepo}>Documentation</a> in the GitHub repo</p>
</section>
<style lang="scss">
section {
background: var(--card);
padding: 1rem;
border-radius: 6px;
margin: 1rem auto;
max-width: 1000px;
transition: all 0.2s ease-in-out;
h2 {
margin: 0;
font-size: 2rem;
}
h3 {
font-size: 1.5rem;
margin: 0.5rem 0;
}
h4 {
margin: 0.5rem 0;
}
p {
margin: 0;
}
ol {
margin: 0.5rem;
padding: 0;
list-style: none;
li {
counter-increment: item;
}
li:before {
content: counter(item);
color: var(--accent);
margin-right: 0.5rem;
font-weight: 600;
width: 1ch;
text-align: center;
display: inline-block;
}
}
hr {
opacity: 0.5;
margin: 1.5rem auto;
height: 2px;
border: none;
background: var(--accent);
}
pre {
background: var(--card-2);
padding: 0.25rem 0.5rem;
font-size: 1.1rem;
width: fit-content;
margin: 0.5rem 0;
display: inline;
border-radius: 6px;
&.template-url {
white-space: normal;
}
}
button {
background: var(--background);
padding: 0.25rem 0.5rem;
border-radius: 6px;
border: none;
color: var(--foreground);
font-family: Kanit;
font-size: 1.1rem;
cursor: pointer;
transition: all 0.2s ease-in-out;
&:hover {
background: var(--gradient);
transform: scale(1.1) rotate(-1deg);
}
}
a {
color: var(--accent);
}
details {
summary {
cursor: pointer;
font-weight: bold;
&:hover {
color: var(--accent);
}
}
}
.demo {
display: block;
margin: 0.5rem auto;
border-radius: 6px;
max-width: 50rem;
}
.docker-run-command, .docker-compose-file {
background: var(--card-2);
position: relative;
padding: 0.5rem;
pre {
font-size: 1rem;
}
.docker-command-copy {
position: absolute;
right: 0.5rem;
top: 0.5rem;
}
}
.instructions {
margin-bottom: 0.5rem;
font-size: 1rem;
code {
border-radius: 6px;
padding: 0 0.25rem;
background: var(--card-2);
}
}
:global(.hljs) {
background: var(--card-2);
font-size: 1.1rem;
padding: 0;
}
}
</style>

View file

@ -1,60 +0,0 @@
<script lang="ts">
export let searchTerm: string;
export let isCategoriesVisible: boolean;
export let toggleCategories: () => void;
</script>
<div class="title-row">
<h2>Template List</h2>
<div class="filters">
<button on:click={toggleCategories}>
{isCategoriesVisible ? '▲' : '▼'} Categories
</button>
<input type="text" placeholder="Search..." bind:value={searchTerm} />
</div>
</div>
<style lang="scss">
.title-row {
display: flex;
align-items: center;
justify-content: space-between;
margin: 1rem auto;
padding: 0 1rem;
max-width: var(--max-width);
flex-wrap: wrap;
h2 {
font-size: 2rem;
margin: 0;
}
.filters {
input {
background: var(--card);
border: 1px solid transparent;
color: var(--foreground);
padding: 0.5rem 0.75rem;
border-radius: 6px;
transition:all 0.3s ease-in-out;
&:focus, &:hover {
box-shadow: var(--shadow);
}
}
}
button {
color: var(--foreground);
border: 1px solid transparent;
padding: 0 0.3rem;
margin: 0.25rem;
line-height: 2rem;
border-radius: 6px;
text-transform: capitalize;
background: var(--card);
transition: all 0.3s ease-in-out;
cursor: pointer;
font-size: 0.9rem;
&:hover, &.selected {
background: var(--gradient);
}
}
}
</style>

View file

@ -1,85 +0,0 @@
<script lang="ts">
import { slide } from 'svelte/transition';
import snarkdown from 'snarkdown';
export let content: string | null = null;
export let multiContent: { name: string, content: string, description: string, visible: false }[] | null = null;
let showDocs = false;
const toggleDocs = () => {
showDocs = !showDocs;
};
</script>
<section class="docker-docs">
<h2>Container Documentation</h2>
{#if content}
<button on:click={toggleDocs}>{ showDocs ? 'Hide' : 'Expand' } Content</button>
{#if showDocs}
<p transition:slide>{@html snarkdown(content)}</p>
{/if}
{:else if multiContent && multiContent.length > 0}
{#each multiContent as { name, description, content, visible }}
<h3>{name} Documentation</h3>
<p class="desc">{description || ''}</p>
<button on:click={() => visible = !visible}>{ visible ? 'Hide' : 'Expand' } {name}</button>
{#if visible}
<p transition:slide>{@html snarkdown(content)}</p>
{/if}
{/each}
{/if}
</section>
<style lang="scss">
.docker-docs {
background: var(--card);
padding: 1rem;
border-radius: 6px;
margin: 1rem auto;
max-width: 1000px;
transition: all 0.2s ease-in-out;
button {
background: var(--background);
padding: 0.25rem 0.5rem;
border-radius: 6px;
border: none;
color: var(--foreground);
font-family: Kanit;
font-size: 1.2rem;
cursor: pointer;
transition: all 0.2s ease-in-out;
&:hover {
background: var(--gradient);
transform: scale(1.1) rotate(-1deg);
}
}
h2 {
font-size: 2rem;
margin: 0;
}
h3 {
margin: 0.5rem 0;
text-transform: capitalize;
}
.desc {
opacity: 0.7;
margin: 0.5rem 0;
font-style: italic;
}
:global(img) {
max-width: 100%;
}
:global(a) {
color: var(--accent);
text-decoration: none;
}
:global(pre) {
background: var(--card-2);
padding: 1rem;
border-radius: 6px;
overflow: auto;
}
}
</style>

View file

@ -1,36 +0,0 @@
<div class="nout">
<h3>No Results 😢</h3>
<p>
<i>There weren't any templates found that matched the currently applied filters.</i><br><br>
Check the raw <a href="https://github.com/Lissy93/portainer-templates/blob/main/templates.json"><code>templates.json</code></a> file
to see all results.
If you still can't find what you're looking for, why not
<a href="https://github.com/Lissy93/portainer-templates#editing">submit a template</a>?
Feel free to raise a ticket if you need support.
</p>
</div>
<style lang="scss">
.nout {
background: var(--card);
border-radius: 6px;
min-height: 8rem;
margin: 1rem auto 5rem auto;
padding: 1rem;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
max-width: 650px;
h3 {
margin: 0;
font-size: 3rem;
}
p {
font-size: 1.1rem;
code, a {
color: var(--accent);
}
}
}
</style>

View file

@ -1,56 +0,0 @@
<script lang="ts">
export let searchTerm: string;
export let selectedCategories: string[];
export let clearSearch: () => void;
export let numResults: number;
export let totalResults: number;
</script>
<div class="search-summary">
{#if searchTerm}
<p>
Showing {numResults} of {totalResults}
results, matching "<i>{searchTerm}</i>"
{selectedCategories.length ? `in categories: ${selectedCategories.join(', ')}` : ''}
</p>
{:else if selectedCategories.length}
<p>
Showing {numResults} of {totalResults}
results, matching categories: {selectedCategories.join(', ')}
</p>
{:else}
<p>Click an app to view info, stats and usage docs</p>
{/if}
{#if searchTerm || selectedCategories.length}
<button on:click={clearSearch}> Clear Filters</button>
{/if}
</div>
<style lang="scss">
.search-summary {
margin: 0 1rem;
font-size: 0.9rem;
display: flex;
gap: 1rem;
align-items: center;
p {
opacity: 0.75;
margin: 0;
}
button {
background: var(--gradient);
outline: none;
border: none;
padding: 0.25rem 0.5rem;
border-radius: 6px;
color: var(--foreground);
font-weight: 800;
cursor: pointer;
transition: all 0.2s ease-in-out;
&:hover {
transform: scale(1.1);
}
}
}
</style>

View file

@ -1,113 +0,0 @@
<script lang="ts">
import type { TemplateOrService } from '$src/Types';
export let template: TemplateOrService;
</script>
<div class="stats">
{#if template.type}
<span class="lbl">Type</span>
{#if template.type === 1}
<span class="val">Container</span>
{:else if template.type === 2}
<span class="val">Swarm</span>
{:else if template.type === 3}
<span class="val">Kubernetes</span>
{:else}
<span class="val">Unknown</span>
{/if}
{/if}
{#if template.platform}
<span class="lbl">Platform</span>
<code class="val">{template.platform}</code>
{/if}
{#if template.image}
<span class="lbl">Image</span>
<code class="val">{template.image}</code>
{/if}
{#if template.command}
<span class="lbl">Command</span>
<code class="val">{template.command}</code>
{/if}
{#if typeof template.interactive === 'boolean'}
<span class="lbl">Interactive</span>
<code class="val">{template.interactive ? 'Yes' : 'No'}</code>
{/if}
{#if template.ports}
<span class="lbl">Ports</span>
<p class="val">
{#each template.ports as port}<code>{port}</code>{/each}
</p>
{/if}
{#if template.volumes}
<span class="lbl">Volumes</span>
<p class="val">
{#each template.volumes as volume}
<code>
{volume.container || volume}{volume?.bind? ' : ' + volume.bind : ''}
</code>{/each}
</p>
{/if}
{#if template.restart_policy}
<span class="lbl">Restart Policy</span>
<code class="val">{template.restart_policy}</code>
{/if}
{#if template.repository}
<span class="lbl">Sourced</span>
<a class="val" href={template.repository.url}>Repo</a>
{/if}
{#if template.entrypoint}
<span class="lbl">Entrypoint</span>
<code class="val">{template.entrypoint}</code>
{/if}
{#if template.build}
<span class="lbl">Build</span>
<code class="val">{template.build}</code>
{/if}
{#if template.env}
<span class="lbl">Env Vars</span>
<p class="val">
{#each template.env as env}<code>{env.name}={env.set || env.value || env.default || '\'\''}</code>{/each}
</p>
{/if}
</div>
<style lang="scss">
.stats {
min-width: 15rem;
padding: 0.5rem;
gap: 0.5rem;
border-radius: 6px;
display: grid;
grid-template-columns: 1fr auto;
place-items: baseline;
background: var(--card-2);
.lbl {
font-weight: 400;
font-style: normal;
}
.val {
max-width: 10rem;
overflow: hidden;
white-space:nowrap;
text-overflow: ellipsis;
}
span {
font-style: italic;
}
p {
margin: 0;
display: flex;
flex-direction: column;
}
a {
color: var(--accent);
}
}
</style>

View file

@ -1,81 +0,0 @@
<script lang="ts">
import type { Template } from '$src/Types';
export let templates: Template[];
import { lazyLoad } from '$lib/lazy-load';
const slugify = (title: string) => {
return `/${title.toLowerCase().replace(/[^a-zA-Z ]/g, "").replaceAll(' ', '-')}`;
}
</script>
<section class="templates">
{#each templates as template (template.title)}
<a class="template-card" href={slugify(template.title)}>
<h3>{template.title}</h3>
<div class="template-summary">
<div class="left">
<img class="loading" use:lazyLoad={template.logo} alt={template.title} />
</div>
<div class="txt">
<p class="description" title={template.description}>{template.description}</p>
</div>
</div>
</a>
{/each}
</section>
<style lang="scss">
section.templates {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1rem;
margin: 1rem auto;
padding: 0 1rem;
max-width: var(--max-width);
.template-card {
padding: 1rem;
border-radius: 6px;
background: var(--card);
display: flex;
flex-direction: column;
gap: 1rem;
transition:all 0.3s ease-in-out;
max-width: 28rem;
text-decoration: none;
color: var(--foreground);
&:hover {
box-shadow: var(--shadow);
}
.template-summary {
display: flex;
gap: 1rem;
align-items: start;
}
p, h3 {
margin: 0;
}
img {
width: 64px;
max-height: 64px;
border-radius: 6px;
&.loading {
padding: 0.2rem;
background: var(--card-2);
border-radius: 6px;
height: 64px;
}
}
.description {
font-style: italic;
font-weight: 200;
overflow: hidden;
word-break: break-word;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 5;
line-clamp: 5;
}
}
}
</style>

View file

@ -1,60 +0,0 @@
<script lang="ts">
import { gitHubRepo } from '$src/constants';
export let templateName: string;
</script>
<section>
<h2>Template not Found 😢</h2>
<p class="subtitle">It doesn't look like there was a templated named "<i>{templateName}</i>"</p>
<p>
You can try <a href="/">searching for another</a>, or if you think there's a mistake somewhere,
please open an issue on the <a href={gitHubRepo} target="_blank">Github Repo</a>.
</p>
<a class="back-home" href="/">Back Home</a>
</section>
<style lang="scss">
section {
background: var(--card);
padding: 1rem;
border-radius: 6px;
margin: 1rem auto;
max-width: 1000px;
transition: all 0.2s ease-in-out;
h2 {
margin: 0;
font-size: 3rem;
text-align: center;
}
p {
margin: 1rem auto;
font-size: 1.1rem;
opacity: 0.8;
text-align: center;
max-width: 40rem;
a {
color: var(--accent);
}
}
.back-home {
background: var(--background);
padding: 0.25rem 0.5rem;
margin: 0 auto;
display: block;
width: fit-content;
border-radius: 6px;
border: none;
color: var(--foreground);
font-family: Kanit;
font-size: 1.5rem;
cursor: pointer;
transition: all 0.2s ease-in-out;
text-decoration: none;
&:hover {
background: var(--gradient);
transform: scale(1.1) rotate(-1deg);
}
}
}
</style>

View file

@ -1,34 +0,0 @@
// See how the options work here: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
let options = {
root: null,
rootMargin: "0px",
threshold: 0
}
export const lazyLoad = (image: any, src: string) => {
const loaded = () => {
image.classList.remove("loading");
image.style.opacity = "1";
};
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
image.src = src;
if (image.complete) {
loaded();
} else {
image.addEventListener("load", loaded);
}
}
},
options
);
observer.observe(image);
return {
destroy() {
observer.disconnect();
image.removeEventListener("load", loaded);
},
};
};

View file

@ -1,78 +0,0 @@
<script lang="ts">
import { browser } from '$app/environment';
import { page, navigating } from '$app/stores';
import { tick } from 'svelte';
import Header from '$lib/Header.svelte';
import Footer from '$lib/Footer.svelte';
let bottom = false;
let showNav = false;
const scrollVisible = (): boolean => {
return browser ?
document.documentElement.clientHeight >= document.documentElement.scrollHeight
: false;
};
$: {
updateFooter();
if($navigating) updateFooter();
showNav = !['/', '/index'].includes($page.url.pathname)
}
async function updateFooter() {
await tick();
bottom = scrollVisible();
}
</script>
<svelte:head>
<title>Portainer Templates</title>
<meta name="description" content="A community-driven library of 1-click self-hosted apps" />
<meta property="og:title" content="Portainer Templates" />
<meta property="og:description" content="A community-driven library of 1-click self-hosted apps" />
<meta property="og:type" content="website" />
<meta property="og:url" content="{import.meta.env.VITE_PUBLIC_BASE_URL}/" />
<meta property="og:image" content="{import.meta.env.VITE_PUBLIC_BASE_URL}/banner.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Portainer Templates" />
<meta name="twitter:description" content="A community-driven library of 1-click self-hosted apps" />
<meta name="twitter:image" content="{import.meta.env.VITE_PUBLIC_BASE_URL}/banner.png" />
<link rel="canonical" href="{import.meta.env.VITE_PUBLIC_BASE_URL}" />
<meta name="theme-color" content="#0ba5ec" />
</svelte:head>
{#if showNav}
<Header />
{/if}
<main>
<slot></slot>
</main>
<Footer {bottom} />
<style lang="scss">
@import url('https://fonts.googleapis.com/css2?family=Kanit:wght@200;400;800&display=swap');
:global(body) {
--background: #101828;
--foreground: #ffffff;
--accent: #0ba5ec;
--card: #1d2939;
--card-2: #192432;
--shadow: 1px 1px 3px 3px #0B9AEC8F;
--gradient: linear-gradient(to right,#0B9AEC 0%,#6EDFDE 100%);
--max-width: 1800px;
margin: 0;
font-family: 'Kanit', sans-serif;
color: var(--foreground);
background: var(--background);
}
:global(::selection) {
background: var(--accent);
color: var(--background);
}
main {
padding: 2rem;
}
</style>

View file

@ -1,32 +0,0 @@
import { templates } from '$src/store';
import { templatesUrl } from '$src/constants';
const makeCategories = (templates) => {
// Get categories from templates
const categories = templates.reduce((acc, { categories: templateCategories }) => {
(templateCategories || []).forEach((category) => {
acc[category] = (acc[category] || 0) + 1;
});
return acc;
}, {});
// Sort categories by count, and remove categories with only 1 template
const sortedCategories = Object.fromEntries(
Object.entries(categories)
.filter(([, value]) => value > 3)
.sort(([, a], [, b]) => b - a)
);
return sortedCategories;
};
export const load = async () => {
const data = await fetch(templatesUrl).then((res) => res.json());
templates.set(data.templates);
return {
templates: data.templates,
categories: makeCategories(data.templates),
}
};

View file

@ -1,97 +0,0 @@
<script lang="ts">
import { page } from '$app/stores'
import Hero from '$lib/Hero.svelte';
import ListFilter from '$lib/ListFilter.svelte';
import Categories from '$lib/Categories.svelte';
import SearchSummary from '$lib/SearchSummary.svelte';
import Templates from '$lib/TemplateList.svelte';
import NoResults from '$lib/NoResults.svelte';
import Footer from '$lib/Footer.svelte';
import type { Template } from '$src/Types';
export let data;
const preSelectedCategories = $page.url.searchParams.get('categories');
let searchTerm = '';
let selectedCategories: string[] = preSelectedCategories?.split(',') || [];
let showCategories = !!preSelectedCategories || false;
$: filteredTemplates = data.templates.filter((template: Template) => {
const compareStr = (str1: string, str2: string) =>
(str1 || '').toLowerCase().includes(str2.toLowerCase());
if (selectedCategories.length) {
const templateCategories = (template.categories || []).map((c) => c.toLowerCase());
const hasSelectedCategory = selectedCategories.some((cat) =>
templateCategories.includes(cat.toLocaleLowerCase())
);
if (!hasSelectedCategory) return false;
}
return (
compareStr(template.title, searchTerm) ||
compareStr(template.description, searchTerm) ||
compareStr((template.categories || []).join(''), searchTerm)
);
});
const showHideCategoryList = () => {
showCategories = !showCategories;
};
const toggleCategory = (category: string) => {
if (selectedCategories.includes(category)) {
selectedCategories = selectedCategories.filter((cat) => cat !== category);
} else {
selectedCategories = [...selectedCategories, category];
}
};
const clearSearch = () => {
searchTerm = '';
selectedCategories = [];
}
</script>
<!-- Main title, and CTA buttons -->
<Hero />
<!-- Search bar, and Templates sub-title -->
<ListFilter
bind:searchTerm={searchTerm}
toggleCategories={showHideCategoryList}
isCategoriesVisible={showCategories}
/>
<!-- List of categories to filter by -->
{#if showCategories}
<Categories
categories={data.categories}
selectedCategories={selectedCategories}
toggleCategory={toggleCategory}
/>
{/if}
<!-- Text showing num results, and users search term + filters -->
<SearchSummary
searchTerm={searchTerm}
selectedCategories={selectedCategories}
clearSearch={clearSearch}
numResults={filteredTemplates.length}
totalResults={data.templates.length}
/>
<!-- List of available templates (filtered, if needed) -->
<Templates templates={filteredTemplates} />
<!-- If there are no templates matching search term, show lil message -->
{#if !filteredTemplates.length}
<NoResults />
{/if}

View file

@ -1,109 +0,0 @@
import yaml from 'js-yaml';
import { get } from 'svelte/store';
import { templatesUrl } from '$src/constants';
import { templates } from '$src/store';
/* Based on the current page name, find the corresponding template */
const findTemplate = (templates: any, slug: string) => {
return templates.find((temp: Template) =>
temp.title.toLowerCase().replace(/[^a-zA-Z ]/g, "").replaceAll(' ', '-') === slug
);
};
/* With a given image name, fetch stats from DockerHub registry */
const getDockerHubStats = async (image: string): Promise<DockerHubResponse | null> => {
if (!image) return null;
const [imageName, tag] = image.split(':');
const [namespace, repo] = imageName.includes('/') ? imageName.split('/') : ['library', imageName];
const apiEndpoint = `https://hub.docker.com/v2/repositories/${namespace}/${repo}/`;
return await fetch(apiEndpoint)
.then((res) => res.json())
.then((data) => {
return data;
})
.catch((err) => {
return null;
});
}
const getServices = async (template): Promise<Service[]> => {
try {
if (template?.repository) {
const { url: repoUrl, stackfile } = template.repository;
const path = `${repoUrl.replace('github.com', 'raw.githubusercontent.com')}/HEAD/${stackfile}`;
const response = await fetch(path);
const data = await response.text();
const parsedData = yaml.load(data);
const someServices: Service[] = [];
if (!parsedData.services) return [];
Object.keys(parsedData.services).forEach((service) => {
const serviceData = parsedData.services[service];
someServices.push({
name: service,
image: serviceData.image,
entrypoint: serviceData.entrypoint,
command: serviceData.command,
ports: serviceData.ports,
build: serviceData.build,
interactive: serviceData.interactive,
volumes: serviceData.volumes?.map((vol) => ({
bind: vol.split(':')[0],
container: vol.split(':')[1],
})),
restart_policy: serviceData.restart,
env: Object.keys(serviceData.environment || {}).map((envName) => {
if (typeof envName === 'string') {
const nowItsArray = serviceData.environment[envName].split('=') || [];
return { name: nowItsArray[0] || '', value: nowItsArray[1] || '' }
}
return { name: envName, value: serviceData.environment[envName] }
}),
});
});
return someServices;
} else {
return [];
}
} catch (error) {
console.error('Error fetching or parsing YAML:', error);
return [];
}
};
/* Format results for returning to component */
const returnResults = async (templates, templateSlug) => {
// Find template, based on slug
let template = findTemplate(get(templates), templateSlug);
// Fetch service info from associated stackfile, if it exists
let services = template?.repository ? await getServices(template) : [];
// If only 1 service, merge it with the template
if (services.length === 1) {
template = {...template, ...services[0]};
} else if (services.length > 1) {
// If made up from multiple services, fetch Docker info for each image
services = await Promise.all(
services.map(async (service) => {
const dockerStats = await getDockerHubStats(service.image);
return { ...service, dockerStats };
})
);
}
// If image specified, fetch Docker image info from DockerHub
const dockerStats = template?.image ? await getDockerHubStats(template.image) : null;
return { template, dockerStats, services }
};
export const load = async ({ params }) => {
const templateSlug = params.slug as string;
if (get(templates) && get(templates).length > 0) {
return returnResults(templates, templateSlug);
} else {
const data = await fetch(templatesUrl).then((res) => res.json());
templates.set(data.templates);
return returnResults(templates, templateSlug);
}
};

View file

@ -1,202 +0,0 @@
<script lang="ts">
import { page } from '$app/stores';
import ServiceStats from '$lib/ServiceStats.svelte';
import TemplateNotFound from '$lib/TemplateNotFound.svelte';
import DockerStats from '$lib/DockerStats.svelte';
import MdContent from '$lib/MdContent.svelte';
import InstallationInstructions from '$lib/InstallationInstructions.svelte';
import type { Template, Service, DockerHubResponse } from '$src/Types';
const urlSlug = $page.params.slug;
const template = $page.data.template as Template;
const dockerStats = $page.data.dockerStats as DockerHubResponse;
const services = $page.data.services as Service[];
const makeMultiDoc = (services: Service[]) => {
return services.map((s) => {
return s?.dockerStats?.full_description ? {
name: s.name,
description: s.dockerStats.description,
content: s.dockerStats.full_description,
visible: false,
} : null;
}).filter((thingy) => thingy !== null);
};
const makeMetaDescription = () => {
return `Installation guide for ${template.title}, using Portainer, Docker Run or Docker-Compose. `
+`Portainer-Templates is a community driven repository of Portainer Templates for Self-Hosted apps. \n`
+`${template.description}`;
}
</script>
<svelte:head>
<title>{template.title} | Portainer Templates</title>
<meta name="description" content={makeMetaDescription()} />
<meta property="og:title" content="{template.title} | Portainer Templates" />
<meta property="og:description" content={makeMetaDescription()} />
<meta property="og:url" content="{import.meta.env.VITE_PUBLIC_BASE_URL}/{urlSlug}" />
<meta name="twitter:title" content="{template.title} | Portainer Templates" />
<meta name="twitter:description" content={makeMetaDescription()} />
<link rel="canonical" href="{import.meta.env.VITE_PUBLIC_BASE_URL}/{urlSlug}" />
</svelte:head>
{#if template}
<section class="summary-section">
<h1>
{#if template.logo} <img src={template.logo} /> {/if}
{template.title}
</h1>
{#if template.categories || template.category }
<p class="tags">
{#each (template.categories || template.category || []) as tag}
<a href="/?categories={tag}"><span>{tag}</span></a>
{/each}
</p>
{/if}
<div class="content">
<div class="left">
<p class="description">{template.description}</p>
{#await template then returnedTemplate}
{#if dockerStats && dockerStats.name}
<DockerStats info={dockerStats} />
{/if}
{/await}
</div>
<ServiceStats template={template} />
</div>
</section>
{#await services then returnedServices}
{#if returnedServices && returnedServices.length > 1}
<section class="service-section">
<h2>Services</h2>
<div class="service-list">
{#each returnedServices as service}
<div class="service-each">
<h3>{service.name}</h3>
<div class="service-data">
<ServiceStats template={service} />
{#if service.dockerStats && service.dockerStats.name}
<DockerStats info={service.dockerStats} />
{/if}
</div>
</div>
{/each}
</div>
</section>
{/if}
{/await}
<InstallationInstructions portainerTemplate={template} portainerServices={services || null} />
{#if dockerStats?.full_description}
<MdContent content={dockerStats.full_description} />
{:else if services.length > 0}
<MdContent multiContent={makeMultiDoc(services)} />
{/if}
{:else}
<TemplateNotFound templateName={urlSlug} />
{/if}
<style lang="scss">
section {
max-width: 1000px;
margin: 1rem auto;
}
.summary-section {
background: var(--card);
border-radius: 6px;
padding: 1rem;
display: flex;
flex-direction: column;
h1 {
font-size: 4rem;
margin: 0;
display: flex;
align-items: center;
gap: 1rem;
}
img {
border-radius: 6px;
width: 64px;
max-height: 64px;
}
.tags {
display: flex;
margin: 0;
gap: 0.5rem;
a {
color: var(--foreground);
text-decoration: none;
transition: all 0.2s ease-in-out;
span {
&:before {
content: '#';
opacity: 0.5;
}
&:not(:last-child)::after {
content: ',';
margin-right: 0.5rem;
}
}
&:hover {
color: var(--accent);
transform: scale(1.08);
}
}
}
}
.content {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: space-between;
margin-top: 1rem;
.left {
flex: 1;
display: flex;
flex-direction: column;
gap: 1rem;
}
p.description {
background: var(--card-2);
padding: 1rem;
border-radius: 6px;
margin: 0;
}
}
.service-section {
background: var(--card);
border-radius: 6px;
padding: 1rem;
h2 {
margin: 0;
font-size: 2rem;
}
.service-list {
display: flex;
gap: 2rem;
flex-wrap: wrap;
h3 {
margin: 0.5rem 0;
font-weight: 400;
font-size: 2rem;
}
.service-each {
.service-data {
display: flex;
gap: 1rem;
}
}
}
}
</style>

View file

@ -1,41 +0,0 @@
import type { RequestHandler } from '@sveltejs/kit';
import { templatesUrl, baseUrl } from '$src/constants';
import type { Template } from '$src/Types';
const fetchData = async () => {
const data = await fetch(templatesUrl).then((res) => res.json());
return await data.templates.map((d: Template) => `${baseUrl}/${d.title.toLowerCase().replace(/[^a-zA-Z ]/g, "").replaceAll(' ', '-')}`);
};
const generationDate = () => {
const date = new Date();
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
}
export async function GET() {
const data = await fetchData();
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>${baseUrl}</loc>
<lastmod>${generationDate()}</lastmod>
<changefreq>weekly</changefreq>
<priority>1</priority>
</url>
${data.map((url: string) => `
<url>
<loc>${url}</loc>
<lastmod>${generationDate()}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>`)
.join('')}
</urlset>`;
return new Response(sitemap, {
headers: { 'Content-Type': 'application/xml' }
}
);
}

View file

@ -1,5 +0,0 @@
<script lang="ts">
import InstallationInstructions from '$lib/InstallationInstructions.svelte';
</script>
<InstallationInstructions />

View file

@ -1,3 +0,0 @@
import { writable } from 'svelte/store'
export const templates = writable([]);

View file

@ -1,88 +0,0 @@
import yaml from 'js-yaml';
import type { Template, Volume, Service, DockerCompose } from '$src/Types';
export const generateDockerRunCommand = (template: Template) => {
let command = `docker run -d \\ \n`;
if (template.ports) {
template.ports.forEach((port) => {
command += ` -p ${port} \\\n`;
});
}
if (template.env) {
template.env.forEach((env) => {
command += ` -e ${env.name}=\${${env.name}} \\\n`;
});
}
if (template.volumes) {
template.volumes.forEach((volume: Volume) => {
const readOnly = volume.readonly ? ":ro" : "";
command += ` -v ${volume.bind}:${volume.container}${readOnly} \\\n`;
});
}
if (template.restart_policy) {
command += ` --restart=${template.restart_policy} \\\n`;
}
command += ` ${template.image}`;
return command;
};
export const generateDockerRunCommands = (stack: Service[]) => {
const commands = stack.filter((s) => s.image).map((service) => {
let cmd = `docker run --name ${service.name} -d \\\n`;
if (service.command) {
cmd += ` ${service.command} \\\n`;
}
if (service.env) {
service.env.forEach((envVar) => {
cmd += ` -e "${envVar.value}" \\\n`;
});
}
if (service.ports) {
service.ports.forEach((port) => {
cmd += ` -p ${port} \\\n`;
});
}
if (service.volumes) {
service.volumes.forEach((volume) => {
cmd += ` -v ${volume.bind}:${volume.container} \\\n`;
});
}
if (service.restart_policy) {
cmd += ` --restart=${service.restart_policy} \\\n`;
}
cmd += ` ${service.image}`;
return cmd;
});
return commands;
}
export const convertToDockerCompose = (template: Template) => {
const serviceName = template.title.toLowerCase().replace(/[^a-z0-9]+/g, "-");
const dockerCompose: DockerCompose = {
version: "3.8",
services: { [serviceName]: { image: template.image } },
};
if (template.ports && template.ports.length > 0) {
dockerCompose.services[serviceName].ports = template.ports.map((port) => port.replace('/', ':'));
}
if (template.env && template.env.length > 0) {
dockerCompose.services[serviceName].environment = template.env.reduce((envVars, envVar) => {
envVars[envVar.name] = envVar.set || "";
return envVars;
}, {});
}
if (template.volumes && template.volumes.length > 0) {
dockerCompose.services[serviceName].volumes = template.volumes.map(
(volume) => `${volume.bind || ""}:${volume.container}`
);
}
return yaml.dump(dockerCompose);
};
export const convertPortainerStackToDockerCompose = (stack: Service[]) => {
const composeStack = stack.map(({ dockerStats, ...s }) => s);
return yaml.dump(composeStack);
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View file

@ -1,19 +0,0 @@
{
"name": "Portainer Templates",
"short_name": "Portainer Templates",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#6EDFDE",
"background_color": "#101828",
"display": "standalone"
}

View file

@ -1,21 +0,0 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter(),
alias: {
'$src/*': 'src/*',
},
}
};
export default config;

20859
templates.json Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,17 +0,0 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

View file

@ -1,7 +0,0 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { sentrySvelteKit } from '@sentry/sveltekit';
export default defineConfig({
plugins: [sveltekit(), sentrySvelteKit()],
});