mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-24 04:59:32 +00:00
Compare commits
905 Commits
v3.9.0-rc.
...
v3.12.13
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec376b2e47 | ||
|
|
a176562ad0 | ||
|
|
becf37b676 | ||
|
|
9b5efab8f8 | ||
|
|
e98a8ba599 | ||
|
|
7ddac50008 | ||
|
|
9837ae359f | ||
|
|
710a829dcb | ||
|
|
ccd84fa454 | ||
|
|
335b36d3a9 | ||
|
|
2be30fae00 | ||
|
|
db5cd21884 | ||
|
|
bfd3020031 | ||
|
|
344c36997a | ||
|
|
dfd9272b70 | ||
|
|
359f4520f5 | ||
|
|
aecf014f4e | ||
|
|
d2a89ddf84 | ||
|
|
c01fe153ae | ||
|
|
4f4a838799 | ||
|
|
ac6f2567eb | ||
|
|
05a5816ac6 | ||
|
|
9c8f6e9195 | ||
|
|
2fd001f6d2 | ||
|
|
d641d32413 | ||
|
|
18064ef6a2 | ||
|
|
5cb9216add | ||
|
|
91c36dc810 | ||
|
|
6efb02fa32 | ||
|
|
97313e4180 | ||
|
|
568ab24fd9 | ||
|
|
5a745efcd3 | ||
|
|
c651570e62 | ||
|
|
8980598085 | ||
|
|
c07c742feb | ||
|
|
1053abb9a9 | ||
|
|
2c9e57cbb1 | ||
|
|
c6eaa2c8a6 | ||
|
|
5ab5e913ee | ||
|
|
cea53ca476 | ||
|
|
58af09114b | ||
|
|
c4c0417e2d | ||
|
|
74f90e6947 | ||
|
|
ad5c339780 | ||
|
|
305823db00 | ||
|
|
baf58b298f | ||
|
|
c37367d018 | ||
|
|
1c98796e64 | ||
|
|
e686d9a6ea | ||
|
|
a1936b9d59 | ||
|
|
834f9c9337 | ||
|
|
615f8cfd3b | ||
|
|
8ed134105f | ||
|
|
5d6169b270 | ||
|
|
e83de8b938 | ||
|
|
ee55e039b2 | ||
|
|
086dd89144 | ||
|
|
68e5d4dd2c | ||
|
|
55a35c6bec | ||
|
|
d09b4885fe | ||
|
|
a46773e6d8 | ||
|
|
a422d0220c | ||
|
|
e5eba8430a | ||
|
|
3d235dc316 | ||
|
|
80d3b4be8c | ||
|
|
fe8b7480df | ||
|
|
cebfc3aaa0 | ||
|
|
f778b5a12d | ||
|
|
2244050160 | ||
|
|
9284e42b62 | ||
|
|
ee40120496 | ||
|
|
30cd2149ea | ||
|
|
395df36d57 | ||
|
|
79597ea0e5 | ||
|
|
283f39270a | ||
|
|
7d892bb19d | ||
|
|
a025f124f3 | ||
|
|
84f7287bf8 | ||
|
|
a58544b502 | ||
|
|
4d26175ebe | ||
|
|
78f0e6ff6b | ||
|
|
3af97af634 | ||
|
|
2c2663c8a4 | ||
|
|
1122b8a2f7 | ||
|
|
5b9f38948b | ||
|
|
507eb3b424 | ||
|
|
56fbc0ed6c | ||
|
|
7aaad314e3 | ||
|
|
356949dd54 | ||
|
|
9878baca53 | ||
|
|
9cbc7c2939 | ||
|
|
4680b63911 | ||
|
|
ce4a2d95f2 | ||
|
|
b2e048de8d | ||
|
|
d25a9d7515 | ||
|
|
dc130d3705 | ||
|
|
2391850218 | ||
|
|
c8f7ca920e | ||
|
|
e3e39af6fb | ||
|
|
f38114f5a5 | ||
|
|
1ee9d041df | ||
|
|
9c6f412f04 | ||
|
|
4fa0f2d04a | ||
|
|
e566a66ea4 | ||
|
|
58a42abc67 | ||
|
|
5676bd9d0d | ||
|
|
9691010e7b | ||
|
|
d19be3ad52 | ||
|
|
ec3cbf788b | ||
|
|
1282fd0b76 | ||
|
|
93430e5607 | ||
|
|
14201f4052 | ||
|
|
47979bf16d | ||
|
|
29530f3b17 | ||
|
|
af548e6ef8 | ||
|
|
ed24a9c990 | ||
|
|
cb1d86d08b | ||
|
|
88f3f628ef | ||
|
|
295bea37bc | ||
|
|
bd7d756254 | ||
|
|
4261147fe8 | ||
|
|
a70adc5eb3 | ||
|
|
0d51b04d79 | ||
|
|
06d40b8a81 | ||
|
|
2358510cba | ||
|
|
e6d13cb7d7 | ||
|
|
39e21c3f36 | ||
|
|
8da900ee72 | ||
|
|
9f4e81a1a3 | ||
|
|
0b918c2f51 | ||
|
|
085cd2a314 | ||
|
|
98d2399568 | ||
|
|
515d9a0008 | ||
|
|
aece1fa7d3 | ||
|
|
abc614ecfd | ||
|
|
1180d3fdde | ||
|
|
1639d1725a | ||
|
|
5df1deecbc | ||
|
|
fe3c0cf76e | ||
|
|
cc0df0182c | ||
|
|
eb354f639f | ||
|
|
02c530dcbe | ||
|
|
379b1de64f | ||
|
|
f3ff324925 | ||
|
|
0f2160222f | ||
|
|
ce3750c51c | ||
|
|
72a7ea6e91 | ||
|
|
4ad7e1f8e6 | ||
|
|
2007ba0c3b | ||
|
|
2009dc11db | ||
|
|
62f2196a0c | ||
|
|
e63c65da4f | ||
|
|
570a082227 | ||
|
|
9b1ede3a59 | ||
|
|
c445fc0f8a | ||
|
|
699493cf24 | ||
|
|
6c89686f31 | ||
|
|
f55b861849 | ||
|
|
adf82c04ad | ||
|
|
1b80956fe8 | ||
|
|
de9da8caf9 | ||
|
|
967f42dd89 | ||
|
|
95e8b29fa2 | ||
|
|
2e3c815e53 | ||
|
|
132707caa7 | ||
|
|
0dad616c38 | ||
|
|
c0882dffde | ||
|
|
5e082c647c | ||
|
|
285c3c2f5d | ||
|
|
dcb29a80fe | ||
|
|
b45ad19732 | ||
|
|
f12d453b5f | ||
|
|
8a00b711be | ||
|
|
56204efc7a | ||
|
|
da638c270f | ||
|
|
ad4b974274 | ||
|
|
943a05edcc | ||
|
|
1a28e65e50 | ||
|
|
cd3af7fa39 | ||
|
|
8ccb0c88db | ||
|
|
127880cf8d | ||
|
|
2e56086661 | ||
|
|
a129be0dbd | ||
|
|
12c0760cb3 | ||
|
|
9d3ed85ffd | ||
|
|
850d57d0d2 | ||
|
|
7981bec1ed | ||
|
|
76373a8597 | ||
|
|
9913e7b70b | ||
|
|
a08bb25bfa | ||
|
|
28ec164bc2 | ||
|
|
3d5ea8629c | ||
|
|
4aaf59d034 | ||
|
|
14850476c7 | ||
|
|
bf5b6170fa | ||
|
|
6f91591448 | ||
|
|
3c723bcba2 | ||
|
|
e7dd13cffa | ||
|
|
ad91630faa | ||
|
|
57f746b584 | ||
|
|
a55720091c | ||
|
|
b461635834 | ||
|
|
1375580651 | ||
|
|
3d20433ad1 | ||
|
|
58447c6456 | ||
|
|
c6273e9177 | ||
|
|
ffdc158d44 | ||
|
|
876c81fad8 | ||
|
|
028ee6d7b1 | ||
|
|
ec00548f1b | ||
|
|
c4dc03e4a8 | ||
|
|
3a510a77ec | ||
|
|
98a785fced | ||
|
|
c48654160d | ||
|
|
55b80132c4 | ||
|
|
1f0c168936 | ||
|
|
6715bc750f | ||
|
|
04a48a626b | ||
|
|
2f9f0da7c6 | ||
|
|
513c4f9e29 | ||
|
|
3f078517a0 | ||
|
|
37036f0fca | ||
|
|
5789aadb5c | ||
|
|
a768ed718a | ||
|
|
9c6092f31f | ||
|
|
40d294a247 | ||
|
|
72844e4edc | ||
|
|
db0a71125a | ||
|
|
da244af39d | ||
|
|
067f502d3c | ||
|
|
fffc6b1e4e | ||
|
|
9121c6a078 | ||
|
|
9c4e581d8b | ||
|
|
dfadd31f46 | ||
|
|
0cfa6fff43 | ||
|
|
d61671c1a0 | ||
|
|
d4f10a9af3 | ||
|
|
03861af893 | ||
|
|
ae531c445d | ||
|
|
4b26aeef9a | ||
|
|
1e47b79b50 | ||
|
|
0c223dcec4 | ||
|
|
0f4536c3d3 | ||
|
|
f43c584463 | ||
|
|
91c558ec83 | ||
|
|
9d45ab3246 | ||
|
|
34ff6eb567 | ||
|
|
8793c00438 | ||
|
|
d7981d5c3e | ||
|
|
bcaae3b67b | ||
|
|
046d9f9597 | ||
|
|
81bd0301d2 | ||
|
|
530e7e494f | ||
|
|
d402fd5690 | ||
|
|
eebec3b92f | ||
|
|
211c6585fa | ||
|
|
e1b5c40ca0 | ||
|
|
747a9b521b | ||
|
|
c2d72ad309 | ||
|
|
596181b622 | ||
|
|
77c5270e1e | ||
|
|
a663c14df8 | ||
|
|
3bd9f00268 | ||
|
|
1aadda735d | ||
|
|
12035208e2 | ||
|
|
df8a9f673c | ||
|
|
aa5c8a2c56 | ||
|
|
a84540e6bb | ||
|
|
fb91b64063 | ||
|
|
94cc77ebca | ||
|
|
aac6981304 | ||
|
|
ca05828b68 | ||
|
|
8ec6b4c59c | ||
|
|
f1be5f5341 | ||
|
|
714c264002 | ||
|
|
eca58097ef | ||
|
|
281146e22b | ||
|
|
f3a19a5d02 | ||
|
|
9b9b6937f4 | ||
|
|
f54c0b7dff | ||
|
|
36c58ad286 | ||
|
|
a67f633259 | ||
|
|
f39a607c1a | ||
|
|
0cc67ed2e5 | ||
|
|
5f8402c645 | ||
|
|
3ab87cd11e | ||
|
|
d5620d305d | ||
|
|
35ebc5e842 | ||
|
|
66276be1d2 | ||
|
|
47c0d522db | ||
|
|
b654883d1a | ||
|
|
b4f9d29129 | ||
|
|
bec6b961f3 | ||
|
|
2ce8f34306 | ||
|
|
30d1ae59ec | ||
|
|
ac7d4e3645 | ||
|
|
868c4001f6 | ||
|
|
e99c44d967 | ||
|
|
48a877f160 | ||
|
|
cea894a8bd | ||
|
|
087e7b9311 | ||
|
|
39ba498293 | ||
|
|
fe7390bd4d | ||
|
|
75af551435 | ||
|
|
ae2d3ebb48 | ||
|
|
5ff6c53715 | ||
|
|
3c94723b23 | ||
|
|
c6a2e3e328 | ||
|
|
2dc5e10878 | ||
|
|
4086dfcf56 | ||
|
|
7937c2bab0 | ||
|
|
5ffa8e9936 | ||
|
|
c431cee517 | ||
|
|
375f17e728 | ||
|
|
d3f658c874 | ||
|
|
5e340a4cdd | ||
|
|
409a5b9f99 | ||
|
|
fba305020b | ||
|
|
bd4ce3ac45 | ||
|
|
733de60f7c | ||
|
|
c365a44e01 | ||
|
|
e94f450bf0 | ||
|
|
d5efc9ddde | ||
|
|
68895ba4a5 | ||
|
|
139aa7a0fc | ||
|
|
4955157e13 | ||
|
|
f2dd5cc75e | ||
|
|
2ad634dbc6 | ||
|
|
de13f65a24 | ||
|
|
8994dde8f0 | ||
|
|
b7303a0828 | ||
|
|
5bc330162a | ||
|
|
0ebc0217f3 | ||
|
|
95c810b80a | ||
|
|
82d7fb883d | ||
|
|
b96e710543 | ||
|
|
24e5e85225 | ||
|
|
7b8f81f1b2 | ||
|
|
62e60fc7ab | ||
|
|
ccd3d4aded | ||
|
|
f0cf155b5c | ||
|
|
b66f67d889 | ||
|
|
e5dc07bde1 | ||
|
|
a955eb0fec | ||
|
|
c070af9681 | ||
|
|
c15e060ef2 | ||
|
|
6d6f2454a7 | ||
|
|
9ff44ed46b | ||
|
|
adf3ef61b8 | ||
|
|
832107e0b8 | ||
|
|
0ea1e71808 | ||
|
|
3b9b3f8ffa | ||
|
|
fda8823050 | ||
|
|
b5756cb14f | ||
|
|
617d3dbe52 | ||
|
|
c18beb1c7c | ||
|
|
a6957b919c | ||
|
|
816a362534 | ||
|
|
7ce3ebde4e | ||
|
|
cc2f83c4d9 | ||
|
|
6ce492049e | ||
|
|
a7999de4b0 | ||
|
|
d4bdfabf19 | ||
|
|
85030ab804 | ||
|
|
2a9bd00a50 | ||
|
|
1c2d76e651 | ||
|
|
a97f7d225a | ||
|
|
2781848aac | ||
|
|
d179da2bee | ||
|
|
60e7922734 | ||
|
|
1ece37ec3c | ||
|
|
8dad865146 | ||
|
|
80d15e782b | ||
|
|
d24e4c6518 | ||
|
|
6def46544c | ||
|
|
d66bae32d3 | ||
|
|
1b753a4020 | ||
|
|
25e6a74a0a | ||
|
|
afde00a4be | ||
|
|
0022d380bb | ||
|
|
d80d2ab934 | ||
|
|
13c1734753 | ||
|
|
bf33d6c34e | ||
|
|
1b02f9bd5d | ||
|
|
9f63c645ff | ||
|
|
abf271fb68 | ||
|
|
363755c3bf | ||
|
|
ba2db666aa | ||
|
|
9f3677b694 | ||
|
|
e6024c997f | ||
|
|
3cb83e2286 | ||
|
|
780d03e5e1 | ||
|
|
214114e6ce | ||
|
|
274d3fe679 | ||
|
|
0ecf86d8a3 | ||
|
|
c2d4390a72 | ||
|
|
7627d59d43 | ||
|
|
71ce9a6b37 | ||
|
|
232018c925 | ||
|
|
9dfbbe58ff | ||
|
|
fa9738a2e0 | ||
|
|
94ecbc5921 | ||
|
|
3c68d317d7 | ||
|
|
56d4edfb9d | ||
|
|
c6c037ff17 | ||
|
|
44feba4d89 | ||
|
|
962f2c7380 | ||
|
|
22007426aa | ||
|
|
008e9a92d3 | ||
|
|
41139ee2ab | ||
|
|
845c40d23c | ||
|
|
a22f26c4c8 | ||
|
|
99ff020f56 | ||
|
|
f863b42b71 | ||
|
|
2e713b459e | ||
|
|
923241ce1e | ||
|
|
3a8929b9d7 | ||
|
|
eb92d39d40 | ||
|
|
bdc62a007e | ||
|
|
4b35db6291 | ||
|
|
c8282b215d | ||
|
|
c123669828 | ||
|
|
781fd0a1cd | ||
|
|
9bd99605fb | ||
|
|
dc626bd4f0 | ||
|
|
aa27aeafa1 | ||
|
|
cdb25cd0e9 | ||
|
|
dc2d15fd9c | ||
|
|
55cb788380 | ||
|
|
0f3b7fe643 | ||
|
|
4b812350a8 | ||
|
|
aec37164de | ||
|
|
dec02bd8db | ||
|
|
1bd6a8ed9e | ||
|
|
2030f714fa | ||
|
|
4416646954 | ||
|
|
52ba9dc02a | ||
|
|
dad3d42d14 | ||
|
|
0d12f3043b | ||
|
|
1225786fc0 | ||
|
|
71496d5229 | ||
|
|
eb0aa20fe1 | ||
|
|
c34de3d0a3 | ||
|
|
54e0a9fc28 | ||
|
|
4bcd034b3d | ||
|
|
111bd29cc8 | ||
|
|
e038865693 | ||
|
|
dfd29dc37a | ||
|
|
b0fcd23ca6 | ||
|
|
f80b1d31f5 | ||
|
|
811ea5b92a | ||
|
|
f9dfbd5800 | ||
|
|
88f1c36929 | ||
|
|
8bbe771f5b | ||
|
|
c578fa63e5 | ||
|
|
4448b86b93 | ||
|
|
17badf95dc | ||
|
|
a267ee40d2 | ||
|
|
8ef645b3c2 | ||
|
|
35625b22f5 | ||
|
|
221dcefd6c | ||
|
|
9c74a9c1db | ||
|
|
55fc3920fc | ||
|
|
5d60b5eb8b | ||
|
|
049d5166e8 | ||
|
|
f4019db3d1 | ||
|
|
9f3732d35b | ||
|
|
b4f17ac3c6 | ||
|
|
978e35d335 | ||
|
|
22cbbec960 | ||
|
|
21f3a70788 | ||
|
|
b4c6f80e1c | ||
|
|
e1198c42eb | ||
|
|
e09fdbcef0 | ||
|
|
b708e79929 | ||
|
|
cbaecff3b7 | ||
|
|
4f7d2630af | ||
|
|
92d3860240 | ||
|
|
3757d5da9f | ||
|
|
1d38a885bb | ||
|
|
dbd767e8f1 | ||
|
|
8b83c38127 | ||
|
|
f1ea01e709 | ||
|
|
12a1aeb0f8 | ||
|
|
413150012f | ||
|
|
8ef5604ce8 | ||
|
|
42e50c800b | ||
|
|
8fbd08003c | ||
|
|
877577efdb | ||
|
|
a6f457749b | ||
|
|
9afb713df1 | ||
|
|
8f660c0276 | ||
|
|
a7e86d9afd | ||
|
|
462eea90c0 | ||
|
|
79c30dfc91 | ||
|
|
410a78b366 | ||
|
|
065807a0bc | ||
|
|
1d93658e56 | ||
|
|
2b7865e6ea | ||
|
|
0cdba8c329 | ||
|
|
11b317b788 | ||
|
|
fb955e15f4 | ||
|
|
ae2d141f0d | ||
|
|
68c983923e | ||
|
|
bf252f7f20 | ||
|
|
324038486f | ||
|
|
bef5da49cf | ||
|
|
24e7f547fa | ||
|
|
3ee3ab0ad1 | ||
|
|
f734154da8 | ||
|
|
7a053ce697 | ||
|
|
25f250310e | ||
|
|
4eca05bbba | ||
|
|
45b0f791bb | ||
|
|
42415a81c1 | ||
|
|
6882e83d1e | ||
|
|
d4b7318413 | ||
|
|
a2b4d400af | ||
|
|
f07868d24e | ||
|
|
d46ee049f4 | ||
|
|
c62eda5627 | ||
|
|
7683164ed2 | ||
|
|
7090c16575 | ||
|
|
9e634fed13 | ||
|
|
9bb125cebd | ||
|
|
0c4850b91d | ||
|
|
0eb7688c4d | ||
|
|
f47cdb68d9 | ||
|
|
d3c3cded37 | ||
|
|
e91ea4ecbe | ||
|
|
680b20d199 | ||
|
|
ec97e04fd4 | ||
|
|
b0b2657fe0 | ||
|
|
d27426fd8f | ||
|
|
d8206c0e3e | ||
|
|
3f1841a188 | ||
|
|
cb478e0dc8 | ||
|
|
02c42a7e3a | ||
|
|
ef40f7349e | ||
|
|
86eebb35cb | ||
|
|
a901388887 | ||
|
|
6cd1c5de38 | ||
|
|
7489f172a1 | ||
|
|
702798c275 | ||
|
|
430d51866c | ||
|
|
9d08421f01 | ||
|
|
f4051874b2 | ||
|
|
bbe0690056 | ||
|
|
772c0d1e41 | ||
|
|
8eb9ca0260 | ||
|
|
bd27afe0da | ||
|
|
a3af21275a | ||
|
|
61eb155d13 | ||
|
|
7932c1c4a9 | ||
|
|
f776fb83e7 | ||
|
|
a97521aba2 | ||
|
|
d1c0fe503e | ||
|
|
ed02c1ae36 | ||
|
|
9a67cf7355 | ||
|
|
755eeda364 | ||
|
|
136dee7747 | ||
|
|
e4e8428855 | ||
|
|
de8dc021f9 | ||
|
|
991587f252 | ||
|
|
8dbcf257c4 | ||
|
|
0b067364a9 | ||
|
|
5367bd6134 | ||
|
|
92228c4379 | ||
|
|
fb2c7896b3 | ||
|
|
23265d9091 | ||
|
|
2c9bb0e767 | ||
|
|
f9e8400d83 | ||
|
|
927a13cd76 | ||
|
|
51b3293e69 | ||
|
|
3f76cadea9 | ||
|
|
6dbf53b558 | ||
|
|
22e937c798 | ||
|
|
ac5cc8b299 | ||
|
|
c588ab723b | ||
|
|
4b2dfc051d | ||
|
|
5238c83f3f | ||
|
|
90bb580e50 | ||
|
|
f40e142704 | ||
|
|
a67618675d | ||
|
|
4fe436e4d1 | ||
|
|
683b8c966f | ||
|
|
28377a156d | ||
|
|
3dcc4faabb | ||
|
|
60a033f93a | ||
|
|
436bd73786 | ||
|
|
5c69ff3339 | ||
|
|
2105b1e7c4 | ||
|
|
523004e5b2 | ||
|
|
5e02c386ec | ||
|
|
b4501fe52d | ||
|
|
3c29eaa1b1 | ||
|
|
ee67e163b1 | ||
|
|
9662bc29fb | ||
|
|
96f2660b98 | ||
|
|
20f594c66c | ||
|
|
2b8d59dca3 | ||
|
|
d44047d109 | ||
|
|
57c4d33bd3 | ||
|
|
7a5377efe0 | ||
|
|
91e7cffccc | ||
|
|
df31e47313 | ||
|
|
cb9586270c | ||
|
|
21dfa5227c | ||
|
|
9d15d2be77 | ||
|
|
929c02d31f | ||
|
|
846185dd42 | ||
|
|
7bc2299a8e | ||
|
|
d40e131bd8 | ||
|
|
552c7297bf | ||
|
|
3f5fd23955 | ||
|
|
8b8566251e | ||
|
|
6db47def8e | ||
|
|
1d0edc7b25 | ||
|
|
f9a417638a | ||
|
|
984fe01551 | ||
|
|
d0cb350687 | ||
|
|
5f51011ce1 | ||
|
|
9ca125ac55 | ||
|
|
360f4f8c27 | ||
|
|
6501f71bd6 | ||
|
|
bf6b799dba | ||
|
|
5f57279283 | ||
|
|
5ed3565520 | ||
|
|
513fa90b8a | ||
|
|
a4d9b9689b | ||
|
|
1c05c0dcbb | ||
|
|
a1b49a3a6b | ||
|
|
6f57298cbb | ||
|
|
d8ce673088 | ||
|
|
4cd7af7a74 | ||
|
|
49c61b5992 | ||
|
|
e44d0550d2 | ||
|
|
17f82109b6 | ||
|
|
2d8888ae9b | ||
|
|
4abe9c6fb2 | ||
|
|
f9d94fa660 | ||
|
|
eaa13f4990 | ||
|
|
01fd5901fe | ||
|
|
3d6adeffc4 | ||
|
|
9066952759 | ||
|
|
6dd7f6274a | ||
|
|
7a8fe6d152 | ||
|
|
be507be3a9 | ||
|
|
657b97f190 | ||
|
|
9d7745cd9b | ||
|
|
3668f83693 | ||
|
|
a2d5d99c1f | ||
|
|
f379ef6a3b | ||
|
|
510a748749 | ||
|
|
550150d685 | ||
|
|
011ea9659e | ||
|
|
6eca7d948e | ||
|
|
90e639f119 | ||
|
|
86ac6461d1 | ||
|
|
18a95bf9ab | ||
|
|
7949bbe66d | ||
|
|
4b603c452a | ||
|
|
837f0634b6 | ||
|
|
78076f7854 | ||
|
|
719350cee1 | ||
|
|
4f6be3e6f5 | ||
|
|
8e61e9fecb | ||
|
|
2083285d78 | ||
|
|
034e86e2cb | ||
|
|
f4a2d5c652 | ||
|
|
534ccd6bf6 | ||
|
|
c17064f853 | ||
|
|
1e1566082f | ||
|
|
449548654d | ||
|
|
6fc99524f0 | ||
|
|
051629fad3 | ||
|
|
f957008c1c | ||
|
|
98e1deec88 | ||
|
|
99127652af | ||
|
|
e9b9e9e82c | ||
|
|
2ed5c3746e | ||
|
|
8902056fdb | ||
|
|
defa6ff6e8 | ||
|
|
eed44e81be | ||
|
|
1951aec5ec | ||
|
|
9c4e0b4107 | ||
|
|
c8deac660d | ||
|
|
4cc5ec9bd0 | ||
|
|
c41bef2e81 | ||
|
|
5b735cf960 | ||
|
|
604e960aa9 | ||
|
|
6c465aa1f2 | ||
|
|
c266832fdc | ||
|
|
906d8d0413 | ||
|
|
cb05fd4a3c | ||
|
|
2eda24799b | ||
|
|
41e221f0cb | ||
|
|
f75af035bb | ||
|
|
e9e6449edf | ||
|
|
f09d76da35 | ||
|
|
40dfe0919b | ||
|
|
85990dd074 | ||
|
|
38acc16e1c | ||
|
|
b7cc4c1e92 | ||
|
|
1f232d96d8 | ||
|
|
83508f165d | ||
|
|
7cc58e7e84 | ||
|
|
31d9740aac | ||
|
|
69891a64a0 | ||
|
|
0940309600 | ||
|
|
a762b1ed60 | ||
|
|
1b9d9d3a8b | ||
|
|
d9908b3d61 | ||
|
|
c40b80436a | ||
|
|
8f1e352bcc | ||
|
|
18e769b5e5 | ||
|
|
27af6459b3 | ||
|
|
2c4bfab01a | ||
|
|
e689be552b | ||
|
|
ad80e7f48b | ||
|
|
d81b75b084 | ||
|
|
90f1431047 | ||
|
|
61ea7dabae | ||
|
|
5d9f5f4a7d | ||
|
|
f956f612d3 | ||
|
|
3f5108268d | ||
|
|
4c0dfc3f30 | ||
|
|
1670fe9b1c | ||
|
|
300b28c0f2 | ||
|
|
e7038961ef | ||
|
|
24e77a5211 | ||
|
|
9df039fbc2 | ||
|
|
143cd46a81 | ||
|
|
680e9871ed | ||
|
|
d5ece58f71 | ||
|
|
d7bbb5c4b7 | ||
|
|
cf9c991c79 | ||
|
|
0f0d96195d | ||
|
|
3a562bb714 | ||
|
|
6381ba8478 | ||
|
|
9e3c14841a | ||
|
|
1917091338 | ||
|
|
b1bb508554 | ||
|
|
0a68a48fc5 | ||
|
|
d3af6792d0 | ||
|
|
44dc3b743e | ||
|
|
b469d2832d | ||
|
|
d844026c29 | ||
|
|
21b4990652 | ||
|
|
39e24bdc97 | ||
|
|
bc66b98176 | ||
|
|
d6d3fb46cc | ||
|
|
4040b334f5 | ||
|
|
d7e72519ef | ||
|
|
c7752f0be9 | ||
|
|
0ffe28a733 | ||
|
|
56f24fe317 | ||
|
|
341cde2781 | ||
|
|
33bb8d434d | ||
|
|
9f813b7385 | ||
|
|
02a336a25d | ||
|
|
88ed1446f4 | ||
|
|
c69312f128 | ||
|
|
c5bcff0e10 | ||
|
|
871d1e2440 | ||
|
|
1619afb938 | ||
|
|
25528913f1 | ||
|
|
7df532fa72 | ||
|
|
ef91441c76 | ||
|
|
aa6c56b63d | ||
|
|
18e899d15e | ||
|
|
63fa8924ae | ||
|
|
0e13e3bd81 | ||
|
|
372c0ed457 | ||
|
|
071077200b | ||
|
|
65579a2861 | ||
|
|
bb7603ae2a | ||
|
|
cce67d274e | ||
|
|
794329dcad | ||
|
|
e36fda3ff1 | ||
|
|
3832d33259 | ||
|
|
1f40c2ccf8 | ||
|
|
7350524456 | ||
|
|
a1a973a873 | ||
|
|
f2a915700c | ||
|
|
e184f99617 | ||
|
|
ab07adb14f | ||
|
|
6535c68276 | ||
|
|
dde2772e52 | ||
|
|
4a8fd309c5 | ||
|
|
b416849d9c | ||
|
|
bc321d8ced | ||
|
|
ac72c19d22 | ||
|
|
67fc2fd3c0 | ||
|
|
4acc59204c | ||
|
|
07cadb59e0 | ||
|
|
6fa4741c81 | ||
|
|
f4bac2382c | ||
|
|
67b72220c0 | ||
|
|
feeb14ea47 | ||
|
|
bdfb6f5f46 | ||
|
|
53491e9eaa | ||
|
|
f720de65fa | ||
|
|
3d70162a8d | ||
|
|
d3a1bbc3d0 | ||
|
|
0078574ee6 | ||
|
|
7cfd313531 | ||
|
|
e7919e9a1b | ||
|
|
98073202e9 | ||
|
|
8dee345f85 | ||
|
|
9161882f33 | ||
|
|
eef313665b | ||
|
|
53e70fbfcb | ||
|
|
05a1721499 | ||
|
|
2f772080b8 | ||
|
|
a5548c080c | ||
|
|
7e0a1ecc80 | ||
|
|
3f2dcccc07 | ||
|
|
adc5965b32 | ||
|
|
45919fc0cf | ||
|
|
dd6f4c4844 | ||
|
|
bb47db033f | ||
|
|
111ea78693 | ||
|
|
c17253589a | ||
|
|
7e6156f5dd | ||
|
|
d5cfb63f52 | ||
|
|
cab15055e7 | ||
|
|
9185910171 | ||
|
|
b4892e0caf | ||
|
|
83e0cafef9 | ||
|
|
7cb75506c3 | ||
|
|
ac6970ad40 | ||
|
|
5a95cc236c | ||
|
|
95c942f477 | ||
|
|
6088f2e573 | ||
|
|
fc705746c0 | ||
|
|
8182359fe4 | ||
|
|
e7ae15162c | ||
|
|
12ca20432d | ||
|
|
8b7406e168 | ||
|
|
9d6317f782 | ||
|
|
d8bdb73140 | ||
|
|
476db15431 | ||
|
|
20ce356296 | ||
|
|
ea594dcbc6 | ||
|
|
021b9746a8 | ||
|
|
c4615ae557 | ||
|
|
95a5089bdc | ||
|
|
cef1fba281 | ||
|
|
5c7859a258 | ||
|
|
986cdae5b0 | ||
|
|
3b11e28d6c | ||
|
|
eba63e8e76 | ||
|
|
2fc65e3b42 | ||
|
|
7d504ab2bf | ||
|
|
216c7efd42 | ||
|
|
8c4149db16 | ||
|
|
20ac8f69ea | ||
|
|
1db3d7a6fb | ||
|
|
b1c1138cf8 | ||
|
|
00b1a4f174 | ||
|
|
86cc665b58 | ||
|
|
e26dd578ef | ||
|
|
f1f3217052 | ||
|
|
8f14fd89ef | ||
|
|
26e4d52a61 | ||
|
|
319c647147 | ||
|
|
f4cd93bd36 | ||
|
|
5a80bb1d2a | ||
|
|
1126dcacf5 | ||
|
|
bdf9a73d19 | ||
|
|
1f73b83a79 | ||
|
|
73bd62c51e | ||
|
|
9acd5c94e8 | ||
|
|
6e85eac14b | ||
|
|
936baf676e | ||
|
|
867f06d813 | ||
|
|
7f9f440789 | ||
|
|
5a15e64471 | ||
|
|
c9aecd51f3 | ||
|
|
6ca1d978d4 | ||
|
|
a18c73bd7c | ||
|
|
26d86cbcb5 | ||
|
|
b24a5d9aca | ||
|
|
5305bc1ceb | ||
|
|
a672f0f56c | ||
|
|
9ab5e13e8f | ||
|
|
a49171f8cc | ||
|
|
65d8dc412a | ||
|
|
8600400632 | ||
|
|
11d10bee12 | ||
|
|
dbd16e8285 | ||
|
|
eb26787079 | ||
|
|
b0a7b1eb3d | ||
|
|
f994092d7f | ||
|
|
946d8e5be5 | ||
|
|
6d7c2ae74a | ||
|
|
1ba71b0b1b | ||
|
|
47c3af6a0e | ||
|
|
e5527a5aa5 | ||
|
|
9bb0dcd73f | ||
|
|
7ed1ced521 | ||
|
|
801b9c1483 | ||
|
|
3990bebca3 | ||
|
|
8a2de1001f |
@@ -20,12 +20,13 @@
|
|||||||
"svelte.svelte-vscode",
|
"svelte.svelte-vscode",
|
||||||
"ardenivanov.svelte-intellisense",
|
"ardenivanov.svelte-intellisense",
|
||||||
"Prisma.prisma",
|
"Prisma.prisma",
|
||||||
"bradlc.vscode-tailwindcss"
|
"bradlc.vscode-tailwindcss",
|
||||||
|
"waderyan.gitblame"
|
||||||
],
|
],
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
"forwardPorts": [3000, 3001],
|
"forwardPorts": [3000, 3001],
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
"postCreateCommand": "cp apps/api/.env.example pps/api/.env && pnpm install && pnpm db:push && pnpm db:seed",
|
"postCreateCommand": "cp apps/api/.env.example apps/api/.env && pnpm install && pnpm db:push && pnpm db:seed",
|
||||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||||
"remoteUser": "node",
|
"remoteUser": "node",
|
||||||
"features": {
|
"features": {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
|
.pnpm-store
|
||||||
build
|
build
|
||||||
.svelte-kit
|
.svelte-kit
|
||||||
package
|
package
|
||||||
@@ -7,6 +8,9 @@ package
|
|||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
dist
|
dist
|
||||||
client
|
|
||||||
apps/api/db/*.db
|
apps/api/db/*.db
|
||||||
local-serve
|
local-serve
|
||||||
|
apps/api/db/migration.db-journal
|
||||||
|
apps/api/core*
|
||||||
|
logs
|
||||||
|
others/certificates
|
||||||
|
|||||||
10
.github/ISSUE_TEMPLATE/--bug-report.yaml
vendored
10
.github/ISSUE_TEMPLATE/--bug-report.yaml
vendored
@@ -9,13 +9,21 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
Thanks for taking the time to fill out this bug report! Please fill the form in English
|
Thanks for taking the time to fill out this bug report! Please fill the form in English.
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Is there an existing issue for this?
|
label: Is there an existing issue for this?
|
||||||
options:
|
options:
|
||||||
- label: I have searched the existing issues
|
- label: I have searched the existing issues
|
||||||
required: true
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: repository
|
||||||
|
attributes:
|
||||||
|
label: Example public repository
|
||||||
|
description: "An example public git repository to reproduce the issue easily (if applicable)."
|
||||||
|
placeholder: "ex: https://github.com/coollabsio/coolify"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Description
|
label: Description
|
||||||
|
|||||||
93
.github/workflows/fluent-bit-release.yml
vendored
Normal file
93
.github/workflows/fluent-bit-release.yml
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
name: fluent-bit-release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- "others/fluentbit"
|
||||||
|
- ".github/workflows/fluent-bit-release.yml"
|
||||||
|
branches:
|
||||||
|
- next
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
arm64:
|
||||||
|
runs-on: [self-hosted, arm64]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- 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.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: others/fluentbit/
|
||||||
|
platforms: linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: coollabsio/coolify-fluent-bit:1.0.0-arm64
|
||||||
|
amd64:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
with:
|
||||||
|
context: others/fluentbit/
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: coollabsio/coolify-fluent-bit:1.0.0-amd64
|
||||||
|
aarch64:
|
||||||
|
runs-on: [self-hosted, arm64]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- 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.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: others/fluentbit/
|
||||||
|
platforms: linux/aarch64
|
||||||
|
push: true
|
||||||
|
tags: coollabsio/coolify-fluent-bit:1.0.0-aarch64
|
||||||
|
merge-manifest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [amd64, arm64, aarch64]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Create & publish manifest
|
||||||
|
run: |
|
||||||
|
docker manifest create coollabsio/coolify-fluent-bit:1.0.0 --amend coollabsio/coolify-fluent-bit:1.0.0-amd64 --amend coollabsio/coolify-fluent-bit:1.0.0-arm64 --amend coollabsio/coolify-fluent-bit:1.0.0-aarch64
|
||||||
|
docker manifest push coollabsio/coolify-fluent-bit:1.0.0
|
||||||
91
.github/workflows/pocketbase-release.yml
vendored
Normal file
91
.github/workflows/pocketbase-release.yml
vendored
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
name: pocketbase-release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- "others/pocketbase/*"
|
||||||
|
- ".github/workflows/pocketbase-release.yml"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
arm64:
|
||||||
|
runs-on: [self-hosted, arm64]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- 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.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: others/pocketbase/
|
||||||
|
platforms: linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: coollabsio/pocketbase:0.11.0-arm64
|
||||||
|
amd64:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
with:
|
||||||
|
context: others/pocketbase/
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: coollabsio/pocketbase:0.11.0-amd64
|
||||||
|
aarch64:
|
||||||
|
runs-on: [self-hosted, arm64]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- 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.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: others/pocketbase/
|
||||||
|
platforms: linux/aarch64
|
||||||
|
push: true
|
||||||
|
tags: coollabsio/pocketbase:0.11.0-aarch64
|
||||||
|
merge-manifest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [amd64, arm64, aarch64]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Create & publish manifest
|
||||||
|
run: |
|
||||||
|
docker manifest create coollabsio/pocketbase:0.11.0 --amend coollabsio/pocketbase:0.11.0-amd64 --amend coollabsio/pocketbase:0.11.0-arm64 --amend coollabsio/pocketbase:0.11.0-aarch64
|
||||||
|
docker manifest push coollabsio/pocketbase:0.11.0
|
||||||
90
.github/workflows/production-release.yml
vendored
90
.github/workflows/production-release.yml
vendored
@@ -5,11 +5,11 @@ on:
|
|||||||
types: [released]
|
types: [released]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
making-something-cool:
|
arm64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, arm64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -26,11 +26,87 @@ jobs:
|
|||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: coollabsio/coolify:latest,coollabsio/coolify:${{steps.package-version.outputs.current-version}}
|
tags: coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64
|
||||||
cache-from: type=registry,ref=coollabsio/coolify:buildcache
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-arm64
|
||||||
cache-to: type=registry,ref=coollabsio/coolify:buildcache,mode=max
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-arm64,mode=max
|
||||||
|
amd64:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Get current package version
|
||||||
|
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||||
|
id: package-version
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: coollabsio/coolify:${{steps.package-version.outputs.current-version}}-amd64
|
||||||
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-amd64
|
||||||
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-amd64,mode=max
|
||||||
|
aarch64:
|
||||||
|
runs-on: [self-hosted, arm64]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- 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.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Get current package version
|
||||||
|
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||||
|
id: package-version
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/aarch64
|
||||||
|
push: true
|
||||||
|
tags: coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64
|
||||||
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-aarch64
|
||||||
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-aarch64,mode=max
|
||||||
|
merge-manifest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [amd64, arm64, aarch64]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Get current package version
|
||||||
|
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||||
|
id: package-version
|
||||||
|
- name: Create & publish manifest
|
||||||
|
run: |
|
||||||
|
docker manifest create coollabsio/coolify:${{steps.package-version.outputs.current-version}} --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-amd64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64
|
||||||
|
docker manifest create coollabsio/coolify:latest --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-amd64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64
|
||||||
|
docker manifest push coollabsio/coolify:${{steps.package-version.outputs.current-version}}
|
||||||
|
docker manifest push coollabsio/coolify:latest
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
|
|||||||
67
.github/workflows/release-candidate.yml
vendored
67
.github/workflows/release-candidate.yml
vendored
@@ -1,17 +1,16 @@
|
|||||||
name: release-candidate
|
name: release-candidate
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [prereleased]
|
types: [prereleased]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
making-something-cool:
|
arm64-making-something-cool:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, arm64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: 'next'
|
ref: "next"
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -28,12 +27,64 @@ jobs:
|
|||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: coollabsio/coolify:${{github.event.release.name}}
|
tags: coollabsio/coolify:${{github.event.release.name}}-arm64
|
||||||
cache-from: type=registry,ref=coollabsio/coolify:buildcache-rc
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-rc-arm64
|
||||||
cache-to: type=registry,ref=coollabsio/coolify:buildcache-rc,mode=max
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-rc-arm64,mode=max
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
||||||
|
amd64-making-something-cool:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: "next"
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Get current package version
|
||||||
|
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||||
|
id: package-version
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: coollabsio/coolify:${{github.event.release.name}}-amd64
|
||||||
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-rc-amd64
|
||||||
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-rc-amd64,mode=max
|
||||||
|
- uses: sarisia/actions-status-discord@v1
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
||||||
|
merge-manifest-to-be-cool:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [arm64-making-something-cool, amd64-making-something-cool]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Create & publish manifest
|
||||||
|
run: |
|
||||||
|
docker manifest create coollabsio/coolify:${{github.event.release.name}} --amend coollabsio/coolify:${{github.event.release.name}}-amd64 --amend coollabsio/coolify:${{github.event.release.name}}-arm64
|
||||||
|
docker manifest push coollabsio/coolify:${{github.event.release.name}}
|
||||||
|
|
||||||
|
|||||||
70
.github/workflows/staging-release.yml
vendored
70
.github/workflows/staging-release.yml
vendored
@@ -2,15 +2,23 @@ name: staging-release
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
paths:
|
||||||
|
- "**"
|
||||||
|
- "!others/fluentbit"
|
||||||
|
- "!others/pocketbase"
|
||||||
|
- "!.github/workflows/fluent-bit-release.yml"
|
||||||
|
- "!.github/workflows/pocketbase-release.yml"
|
||||||
branches:
|
branches:
|
||||||
- next
|
- next
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
staging-release:
|
arm64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, arm64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: "next"
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -20,15 +28,65 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Get current package version
|
||||||
|
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||||
|
id: package-version
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: coollabsio/coolify:next-arm64
|
||||||
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-next-arm64
|
||||||
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-next-arm64,mode=max
|
||||||
|
amd64:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: "next"
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Get current package version
|
||||||
|
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||||
|
id: package-version
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
push: true
|
push: true
|
||||||
tags: coollabsio/coolify:next
|
tags: coollabsio/coolify:next-amd64
|
||||||
cache-from: type=registry,ref=coollabsio/coolify:buildcache-next
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-next-amd64
|
||||||
cache-to: type=registry,ref=coollabsio/coolify:buildcache-next,mode=max
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-next-amd64,mode=max
|
||||||
|
merge-manifest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [arm64, amd64]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Create & publish manifest
|
||||||
|
run: |
|
||||||
|
docker manifest create coollabsio/coolify:next --amend coollabsio/coolify:next-amd64 --amend coollabsio/coolify:next-arm64
|
||||||
|
docker manifest push coollabsio/coolify:next
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
|
|||||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -1,15 +1,25 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
.pnpm-store
|
.pnpm-store
|
||||||
build
|
/apps/ui/build
|
||||||
|
/build
|
||||||
.svelte-kit
|
.svelte-kit
|
||||||
package
|
package
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
dist
|
dist
|
||||||
client
|
|
||||||
apps/api/db/*.db
|
apps/api/db/*.db
|
||||||
local-serve
|
|
||||||
apps/api/db/migration.db-journal
|
apps/api/db/migration.db-journal
|
||||||
apps/api/core*
|
apps/api/core*
|
||||||
|
apps/server/build
|
||||||
|
apps/backup/backups/*
|
||||||
|
!apps/backup/backups/.gitkeep
|
||||||
|
/logs
|
||||||
|
others/certificates
|
||||||
|
backups/*
|
||||||
|
!backups/.gitkeep
|
||||||
|
|
||||||
|
# Trpc
|
||||||
|
apps/server/db/*.db
|
||||||
|
apps/server/db/*.db-journal
|
||||||
21
.vscode/settings.json
vendored
21
.vscode/settings.json
vendored
@@ -1,11 +1,22 @@
|
|||||||
{
|
{
|
||||||
"i18n-ally.localesPaths": ["src/lib/locales"],
|
"i18n-ally.localesPaths": [
|
||||||
|
"src/lib/locales"
|
||||||
|
],
|
||||||
"i18n-ally.keystyle": "nested",
|
"i18n-ally.keystyle": "nested",
|
||||||
"i18n-ally.extract.ignoredByFiles": {
|
"i18n-ally.extract.ignoredByFiles": {
|
||||||
"src\\routes\\__layout.svelte": ["Coolify", "coolLabs logo"]
|
"src\\routes\\__layout.svelte": [
|
||||||
|
"Coolify",
|
||||||
|
"coolLabs logo"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"i18n-ally.sourceLanguage": "en",
|
"i18n-ally.sourceLanguage": "en",
|
||||||
"i18n-ally.enabledFrameworks": ["svelte"],
|
"i18n-ally.enabledFrameworks": [
|
||||||
"i18n-ally.enabledParsers": ["js", "ts", "json"],
|
"svelte"
|
||||||
|
],
|
||||||
|
"i18n-ally.enabledParsers": [
|
||||||
|
"js",
|
||||||
|
"ts",
|
||||||
|
"json"
|
||||||
|
],
|
||||||
"i18n-ally.extract.autoDetect": true
|
"i18n-ally.extract.autoDetect": true
|
||||||
}
|
}
|
||||||
256
CONTRIBUTING.md
256
CONTRIBUTING.md
@@ -1,256 +0,0 @@
|
|||||||
# 👋 Welcome
|
|
||||||
|
|
||||||
First of all, thank you for considering contributing to my project! It means a lot 💜.
|
|
||||||
|
|
||||||
|
|
||||||
## 🙋 Want to help?
|
|
||||||
|
|
||||||
If you begin in GitHub contribution, you can find the [first contribution](https://github.com/firstcontributions/first-contributions) and follow this guide.
|
|
||||||
|
|
||||||
Follow the [introduction](#introduction) to get started then start contributing!
|
|
||||||
|
|
||||||
This is a little list of what you can do to help the project:
|
|
||||||
|
|
||||||
- [🧑💻 Develop your own ideas](#developer-contribution)
|
|
||||||
- [🌐 Translate the project](#translation)
|
|
||||||
|
|
||||||
## 👋 Introduction
|
|
||||||
|
|
||||||
### Setup with Github codespaces
|
|
||||||
|
|
||||||
If you have github codespaces enabled then you can just create a codespace and run `pnpm dev` to run your the dev environment. All the required dependencies and packages has been configured for you already.
|
|
||||||
|
|
||||||
### Setup with Gitpod
|
|
||||||
|
|
||||||
If you have a [Gitpod](https://gitpod.io), you can just create a workspace from this repository, run `pnpm install && pnpm db:push && pnpm db:seed` and then `pnpm dev`. All the required dependencies and packages has been configured for you already.
|
|
||||||
|
|
||||||
### Setup locally in your machine
|
|
||||||
|
|
||||||
> 🔴 At the moment, Coolify **doesn't support Windows**. You must use Linux or MacOS. Consider using Gitpod or Github Codespaces.
|
|
||||||
|
|
||||||
#### Recommended Pull Request Guideline
|
|
||||||
|
|
||||||
- Fork the project
|
|
||||||
- Clone your fork repo to local
|
|
||||||
- Create a new branch
|
|
||||||
- Push to your fork repo
|
|
||||||
- Create a pull request: https://github.com/coollabsio/coolify/compare
|
|
||||||
- Write a proper description
|
|
||||||
- Open the pull request to review against `next` branch
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 🧑💻 Developer contribution
|
|
||||||
## Technical skills required
|
|
||||||
|
|
||||||
- **Languages**: Node.js / Javascript / Typescript
|
|
||||||
- **Framework JS/TS**: [SvelteKit](https://kit.svelte.dev/) & [Fastify](https://www.fastify.io/)
|
|
||||||
- **Database ORM**: [Prisma.io](https://www.prisma.io/)
|
|
||||||
- **Docker Engine API**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## How to start after you set up your local fork?
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
1. Due to the lock file, this repository is best with [pnpm](https://pnpm.io). I recommend you try and use `pnpm` because it is cool and efficient!
|
|
||||||
|
|
||||||
2. You need to have [Docker Engine](https://docs.docker.com/engine/install/) installed locally.
|
|
||||||
3. You need to have [Docker Compose Plugin](https://docs.docker.com/compose/install/compose-plugin/) installed locally.
|
|
||||||
4. You need to have [GIT LFS Support](https://git-lfs.github.com/) installed locally.
|
|
||||||
|
|
||||||
Optional:
|
|
||||||
|
|
||||||
4. To test Heroku buildpacks, you need [pack](https://github.com/buildpacks/pack) binary installed locally.
|
|
||||||
|
|
||||||
### Steps for local setup
|
|
||||||
|
|
||||||
1. Copy `apps/api/.env.template` to `apps/api/.env.template` and set the `COOLIFY_APP_ID` environment variable to something cool.
|
|
||||||
2. Install dependencies with `pnpm install`.
|
|
||||||
3. Need to create a local SQlite database with `pnpm db:push`.
|
|
||||||
|
|
||||||
This will apply all migrations at `db/dev.db`.
|
|
||||||
|
|
||||||
4. Seed the database with base entities with `pnpm db:seed`
|
|
||||||
5. You can start coding after starting `pnpm dev`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Database migrations
|
|
||||||
|
|
||||||
During development, if you change the database layout, you need to run `pnpm db:push` to migrate the database and create types for Prisma. You also need to restart the development process.
|
|
||||||
|
|
||||||
If the schema is finalized, you need to create a migration file with `pnpm db:migrate <nameOfMigration>` where `nameOfMigration` is given by you. Make it sense. :)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## How to add new services
|
|
||||||
|
|
||||||
You can add any open-source and self-hostable software (service/application) to Coolify if the following statements are true:
|
|
||||||
|
|
||||||
- Self-hostable (obviously)
|
|
||||||
- Open-source
|
|
||||||
- Maintained (I do not want to add software full of bugs)
|
|
||||||
|
|
||||||
## Backend
|
|
||||||
|
|
||||||
There are 5 steps you should make on the backend side.
|
|
||||||
|
|
||||||
1. Create Prisma / database schema for the new service.
|
|
||||||
2. Add supported versions of the service.
|
|
||||||
3. Update global functions.
|
|
||||||
4. Create API endpoints.
|
|
||||||
5. Define automatically generated variables.
|
|
||||||
|
|
||||||
> I will use [Umami](https://umami.is/) as an example service.
|
|
||||||
|
|
||||||
### Create Prisma / Database schema for the new service.
|
|
||||||
|
|
||||||
You only need to do this if you store passwords or any persistent configuration. Mostly it is required by all services, but there are some exceptions, like NocoDB.
|
|
||||||
|
|
||||||
Update Prisma schema in [prisma/schema.prisma](prisma/schema.prisma).
|
|
||||||
|
|
||||||
- Add new model with the new service name.
|
|
||||||
- Make a relationship with `Service` model.
|
|
||||||
- In the `Service` model, the name of the new field should be with low-capital.
|
|
||||||
- If the service needs a database, define a `publicPort` field to be able to make it's database public, example field name in case of PostgreSQL: `postgresqlPublicPort`. It should be a optional field.
|
|
||||||
|
|
||||||
If you are finished with the Prisma schema, you should update the database schema with `pnpm db:push` command.
|
|
||||||
|
|
||||||
> You must restart the running development environment to be able to use the new model
|
|
||||||
|
|
||||||
> If you use VSCode/TLS, you probably need to restart the `Typescript Language Server` to get the new types loaded in the running environment.
|
|
||||||
|
|
||||||
### Add supported versions
|
|
||||||
|
|
||||||
Supported versions are hardcoded into Coolify (for now).
|
|
||||||
|
|
||||||
You need to update `supportedServiceTypesAndVersions` function at [apps/api/src/lib/services/supportedVersions.ts](apps/api/src/lib/services/supportedVersions.ts). Example JSON:
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
// Name used to identify the service internally
|
|
||||||
name: 'umami',
|
|
||||||
// Fancier name to show to the user
|
|
||||||
fancyName: 'Umami',
|
|
||||||
// Docker base image for the service
|
|
||||||
baseImage: 'ghcr.io/mikecao/umami',
|
|
||||||
// Optional: If there is any dependent image, you should list it here
|
|
||||||
images: [],
|
|
||||||
// Usable tags
|
|
||||||
versions: ['postgresql-latest'],
|
|
||||||
// Which tag is the recommended
|
|
||||||
recommendedVersion: 'postgresql-latest',
|
|
||||||
// Application's default port, Umami listens on 3000
|
|
||||||
ports: {
|
|
||||||
main: 3000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Add required functions/properties
|
|
||||||
|
|
||||||
1. Add the new service to the `includeServices` variable in [apps/api/src/lib/services/common.ts](apps/api/src/lib/services/common.ts), so it will be included in all places in the database queries where it is required.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const include: any = {
|
|
||||||
destinationDocker: true,
|
|
||||||
persistentStorage: true,
|
|
||||||
serviceSecret: true,
|
|
||||||
minio: true,
|
|
||||||
plausibleAnalytics: true,
|
|
||||||
vscodeserver: true,
|
|
||||||
wordpress: true,
|
|
||||||
ghost: true,
|
|
||||||
meiliSearch: true,
|
|
||||||
umami: true // This line!
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Update the database update query with the new service type to `configureServiceType` function in [apps/api/src/lib/services/common.ts](apps/api/src/lib/services/common.ts). This function defines the automatically generated variables (passwords, users, etc.) and it's encryption process (if applicable).
|
|
||||||
|
|
||||||
```js
|
|
||||||
[...]
|
|
||||||
else if (type === 'umami') {
|
|
||||||
const postgresqlUser = cuid();
|
|
||||||
const postgresqlPassword = encrypt(generatePassword());
|
|
||||||
const postgresqlDatabase = 'umami';
|
|
||||||
const hashSalt = encrypt(generatePassword(64));
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
umami: {
|
|
||||||
create: {
|
|
||||||
postgresqlDatabase,
|
|
||||||
postgresqlPassword,
|
|
||||||
postgresqlUser,
|
|
||||||
hashSalt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Add field details to [apps/api/src/lib/services/serviceFields.ts](apps/api/src/lib/services/serviceFields.ts), so every component will know what to do with the values (decrypt/show it by default/readonly)
|
|
||||||
|
|
||||||
```js
|
|
||||||
export const umami = [{
|
|
||||||
name: 'postgresqlUser',
|
|
||||||
isEditable: false,
|
|
||||||
isLowerCase: false,
|
|
||||||
isNumber: false,
|
|
||||||
isBoolean: false,
|
|
||||||
isEncrypted: false
|
|
||||||
}]
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Add service deletion query to `removeService` function in [apps/api/src/lib/services/common.ts](apps/api/src/lib/services/common.ts)
|
|
||||||
|
|
||||||
5. Add start process for the new service in [apps/api/src/lib/services/handlers.ts](apps/api/src/lib/services/handlers.ts)
|
|
||||||
|
|
||||||
> See startUmamiService() function as example.
|
|
||||||
|
|
||||||
6. Add the newly added start process to `startService` in [apps/api/src/routes/api/v1/services/handlers.ts](apps/api/src/routes/api/v1/services/handlers.ts)
|
|
||||||
|
|
||||||
7. You need to add a custom logo at [apps/ui/src/lib/components/svg/services](apps/ui/src/lib/components/svg/services) as a svelte component and export it in [apps/ui/src/lib/components/svg/services/index.ts](apps/ui/src/lib/components/svg/services/index.ts)
|
|
||||||
|
|
||||||
SVG is recommended, but you can use PNG as well. It should have the `isAbsolute` variable with the suitable CSS classes, primarily for sizing and positioning.
|
|
||||||
|
|
||||||
8. You need to include it the logo at:
|
|
||||||
|
|
||||||
- [apps/ui/src/lib/components/svg/services/ServiceIcons.svelte](apps/ui/src/lib/components/svg/services/ServiceIcons.svelte) with `isAbsolute`.
|
|
||||||
- [apps/ui/src/routes/services/[id]/_ServiceLinks.svelte](apps/ui/src/routes/services/[id]/_ServiceLinks.svelte) with the link to the docs/main site of the service
|
|
||||||
|
|
||||||
9. By default the URL and the name frontend forms are included in [apps/ui/src/routes/services/[id]/_Services/_Services.svelte](apps/ui/src/routes/services/[id]/_Services/_Services.svelte).
|
|
||||||
|
|
||||||
If you need to show more details on the frontend, such as users/passwords, you need to add Svelte component to [apps/ui/src/routes/services/[id]/_Services](apps/ui/src/routes/services/[id]/_Services) with an underscore.
|
|
||||||
|
|
||||||
> For example, see other [here](apps/ui/src/routes/services/[id]/_Services/_Umami.svelte).
|
|
||||||
|
|
||||||
|
|
||||||
Good job! 👏
|
|
||||||
|
|
||||||
<!-- # 🌐 Translate the project
|
|
||||||
|
|
||||||
The project use [sveltekit-i18n](https://github.com/sveltekit-i18n/lib) to translate the project.
|
|
||||||
It follows the [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) to name languages.
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
You must have gone throw all the [intro](#introduction) steps before you can start translating.
|
|
||||||
|
|
||||||
It's only an advice, but I recommend you to use:
|
|
||||||
|
|
||||||
- Visual Studio Code
|
|
||||||
- [i18n Ally for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=Lokalise.i18n-ally): ideal to see the progress of the translation.
|
|
||||||
- [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode): to get the syntax color for the project
|
|
||||||
|
|
||||||
### Adding a language
|
|
||||||
|
|
||||||
If your language doesn't appear in the [locales folder list](src/lib/locales/), follow the step below:
|
|
||||||
|
|
||||||
1. In `src/lib/locales/`, Copy paste `en.json` and rename it with your language (eg: `cz.json`).
|
|
||||||
2. In the [lang.json](src/lib/lang.json) file, add a line after the first bracket (`{`) with `"ISO of your language": "Language",` (eg: `"cz": "Czech",`).
|
|
||||||
3. Have fun translating! -->
|
|
||||||
48
CONTRIBUTION.md
Normal file
48
CONTRIBUTION.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
> "First, thanks for considering to contribute to my project.
|
||||||
|
It really means a lot! 😁" - [@andrasbacsai](https://github.com/andrasbacsai)
|
||||||
|
|
||||||
|
You can ask for guidance anytime on our
|
||||||
|
[Discord server](https://coollabs.io/discord) in the `#contribution` channel.
|
||||||
|
|
||||||
|
You'll need a set of skills to [get started](docs/contribution/GettingStarted.md).
|
||||||
|
|
||||||
|
## 1) Setup your development environment
|
||||||
|
|
||||||
|
- 🌟 [Container based](docs/dev_setup/Container.md) ← *Recomended*
|
||||||
|
- 📦 [DockerContainer](docs/dev_setup/DockerContiner.md) *WIP
|
||||||
|
- 🐙 [Github Codespaces](docs/dev_setup/GithubCodespaces.md)
|
||||||
|
- ☁️ [GitPod](docs/dev_setup/GitPod.md)
|
||||||
|
- 🍏 [Local Mac](docs/dev_setup/Mac.md)
|
||||||
|
|
||||||
|
## 2) Basic requirements
|
||||||
|
|
||||||
|
- [Install Pnpm](https://pnpm.io/installation)
|
||||||
|
- [Install Docker Engine](https://docs.docker.com/engine/install/)
|
||||||
|
- [Setup Docker Compose Plugin](https://docs.docker.com/compose/install/compose-plugin/)
|
||||||
|
- [Setup GIT LFS Support](https://git-lfs.github.com/)
|
||||||
|
|
||||||
|
## 3) Setup Coolify
|
||||||
|
|
||||||
|
- Copy `apps/api/.env.example` to `apps/api/.env`
|
||||||
|
- Edit `apps/api/.env`, set the `COOLIFY_APP_ID` environment variable to something cool.
|
||||||
|
- Run `pnpm install` to install dependencies.
|
||||||
|
- Run `pnpm db:push` to o create a local SQlite database. This will apply all migrations at `db/dev.db`.
|
||||||
|
- Run `pnpm db:seed` seed the database.
|
||||||
|
- Run `pnpm dev` start coding.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Or... Copy and paste commands bellow:
|
||||||
|
cp apps/api/.env.example apps/api/.env
|
||||||
|
pnpm install
|
||||||
|
pnpm db:push
|
||||||
|
pnpm db:seed
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4) Start Coding
|
||||||
|
|
||||||
|
You should be able to access `http://localhost:3000`.
|
||||||
|
|
||||||
|
1. Click `Register` and setup your first user.
|
||||||
46
Dockerfile
46
Dockerfile
@@ -1,46 +1,50 @@
|
|||||||
FROM node:18-alpine3.16 as build
|
ARG PNPM_VERSION=7.11.0
|
||||||
|
|
||||||
|
FROM node:18-slim as build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN apk add --no-cache curl
|
RUN apt update && apt -y install curl
|
||||||
RUN curl -sL https://unpkg.com/@pnpm/self-installer | node
|
RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION}
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN pnpm install
|
RUN pnpm install
|
||||||
RUN pnpm build
|
RUN pnpm build
|
||||||
|
|
||||||
# Production build
|
# Production build
|
||||||
FROM node:18-alpine3.16
|
FROM node:18-slim
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
# ENV PRISMA_QUERY_ENGINE_BINARY=/app/prisma-engines/query-engine \
|
|
||||||
# PRISMA_MIGRATION_ENGINE_BINARY=/app/prisma-engines/migration-engine \
|
|
||||||
# PRISMA_INTROSPECTION_ENGINE_BINARY=/app/prisma-engines/introspection-engine \
|
|
||||||
# PRISMA_FMT_BINARY=/app/prisma-engines/prisma-fmt \
|
|
||||||
# PRISMA_CLI_QUERY_ENGINE_TYPE=binary \
|
|
||||||
# PRISMA_CLIENT_ENGINE_TYPE=binary
|
|
||||||
|
|
||||||
# COPY --from=coollabsio/prisma-engine:3.15 /prisma-engines/query-engine /prisma-engines/migration-engine /prisma-engines/introspection-engine /prisma-engines/prisma-fmt /app/prisma-engines/
|
|
||||||
|
|
||||||
RUN apk add --no-cache git git-lfs openssh-client curl jq cmake sqlite openssl psmisc
|
|
||||||
RUN curl -sL https://unpkg.com/@pnpm/self-installer | node
|
|
||||||
|
|
||||||
RUN mkdir -p ~/.docker/cli-plugins/
|
|
||||||
# https://download.docker.com/linux/static/stable/
|
# https://download.docker.com/linux/static/stable/
|
||||||
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-20.10.9 -o /usr/bin/docker
|
ARG DOCKER_VERSION=20.10.18
|
||||||
# https://github.com/docker/compose/releases
|
# https://github.com/docker/compose/releases
|
||||||
# Reverted to 2.6.1 because of this https://github.com/docker/compose/issues/9704. 2.9.0 still has a bug.
|
# Reverted to 2.6.1 because of this https://github.com/docker/compose/issues/9704. 2.9.0 still has a bug.
|
||||||
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-compose-linux-2.6.1 -o ~/.docker/cli-plugins/docker-compose
|
ARG DOCKER_COMPOSE_VERSION=2.6.1
|
||||||
RUN chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker
|
# https://github.com/buildpacks/pack/releases
|
||||||
|
ARG PACK_VERSION=v0.27.0
|
||||||
|
|
||||||
RUN (curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.27.0/pack-v0.27.0-linux.tgz" | tar -C /usr/local/bin/ --no-same-owner -xzv pack)
|
RUN apt update && apt -y install --no-install-recommends ca-certificates git git-lfs openssh-client curl jq cmake sqlite3 openssl psmisc python3
|
||||||
|
RUN apt-get clean autoclean && apt-get autoremove --yes && rm -rf /var/lib/{apt,dpkg,cache,log}/
|
||||||
|
RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION}
|
||||||
|
RUN npm install -g npm@${PNPM_VERSION}
|
||||||
|
|
||||||
|
RUN mkdir -p ~/.docker/cli-plugins/
|
||||||
|
|
||||||
|
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-$DOCKER_VERSION -o /usr/bin/docker
|
||||||
|
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-compose-linux-$DOCKER_COMPOSE_VERSION -o ~/.docker/cli-plugins/docker-compose
|
||||||
|
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/pack-$PACK_VERSION -o /usr/local/bin/pack
|
||||||
|
|
||||||
|
RUN chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack
|
||||||
|
|
||||||
COPY --from=build /app/apps/api/build/ .
|
COPY --from=build /app/apps/api/build/ .
|
||||||
|
COPY --from=build /app/others/fluentbit/ ./fluentbit
|
||||||
COPY --from=build /app/apps/ui/build/ ./public
|
COPY --from=build /app/apps/ui/build/ ./public
|
||||||
COPY --from=build /app/apps/api/prisma/ ./prisma
|
COPY --from=build /app/apps/api/prisma/ ./prisma
|
||||||
COPY --from=build /app/apps/api/package.json .
|
COPY --from=build /app/apps/api/package.json .
|
||||||
COPY --from=build /app/docker-compose.yaml .
|
COPY --from=build /app/docker-compose.yaml .
|
||||||
|
COPY --from=build /app/apps/api/tags.json .
|
||||||
|
COPY --from=build /app/apps/api/templates.json .
|
||||||
|
|
||||||
RUN pnpm install -p
|
RUN pnpm install -p
|
||||||
|
|
||||||
|
|||||||
31
Dockerfile-dev
Normal file
31
Dockerfile-dev
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
FROM node:18-slim
|
||||||
|
ENV NODE_ENV development
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
ARG PNPM_VERSION=7.11.0
|
||||||
|
ARG NPM_VERSION=8.19.1
|
||||||
|
# https://download.docker.com/linux/static/stable/
|
||||||
|
ARG DOCKER_VERSION=20.10.18
|
||||||
|
# https://github.com/docker/compose/releases
|
||||||
|
# Reverted to 2.6.1 because of this https://github.com/docker/compose/issues/9704. 2.9.0 still has a bug.
|
||||||
|
ARG DOCKER_COMPOSE_VERSION=2.6.1
|
||||||
|
# https://github.com/buildpacks/pack/releases
|
||||||
|
ARG PACK_VERSION=v0.27.0
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION}
|
||||||
|
|
||||||
|
RUN apt update && apt -y install --no-install-recommends ca-certificates git git-lfs openssh-client curl jq cmake sqlite3 openssl psmisc python3
|
||||||
|
RUN apt-get clean autoclean && apt-get autoremove --yes && rm -rf /var/lib/{apt,dpkg,cache,log}/
|
||||||
|
RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION}
|
||||||
|
RUN npm install -g npm@${PNPM_VERSION}
|
||||||
|
|
||||||
|
RUN mkdir -p ~/.docker/cli-plugins/
|
||||||
|
|
||||||
|
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-$DOCKER_VERSION -o /usr/bin/docker
|
||||||
|
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-compose-linux-$DOCKER_COMPOSE_VERSION -o ~/.docker/cli-plugins/docker-compose
|
||||||
|
RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/pack-$PACK_VERSION -o /usr/local/bin/pack
|
||||||
|
|
||||||
|
RUN chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
ENV CHECKPOINT_DISABLE=1
|
||||||
33
README.md
33
README.md
@@ -77,6 +77,7 @@ Deploy your resource to:
|
|||||||
<a href="https://redis.io"><svg style="width:40px;height:40px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><defs ><path id="a" d="m45.536 38.764c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.813s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z" /><path id="b" d="m45.536 28.733c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.935c2.332-.837 3.14-.867 5.126-.14s12.35 4.853 14.312 5.57 2.037 1.31.024 2.36z" /></defs ><g transform="matrix(.848327 0 0 .848327 -7.883573 -9.449691)" ><use fill="#a41e11" xlink:href="#a" /><path d="m45.536 34.95c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.936c2.332-.836 3.14-.867 5.126-.14s12.35 4.852 14.31 5.582 2.037 1.31.024 2.36z" fill="#d82c20" /><use fill="#a41e11" xlink:href="#a" y="-6.218" /><use fill="#d82c20" xlink:href="#b" /><path d="m45.536 26.098c-2.013 1.05-12.44 5.337-14.66 6.495s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.815s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z" fill="#a41e11" /><use fill="#d82c20" xlink:href="#b" y="-6.449" /><g fill="#fff" ><path d="m29.096 20.712-1.182-1.965-3.774-.34 2.816-1.016-.845-1.56 2.636 1.03 2.486-.814-.672 1.612 2.534.95-3.268.34zm-6.296 3.912 8.74-1.342-2.64 3.872z" /><ellipse cx="20.444" cy="21.402" rx="4.672" ry="1.811" /></g ><path d="m42.132 21.138-5.17 2.042-.004-4.087z" fill="#7a0c00" /><path d="m36.963 23.18-.56.22-5.166-2.042 5.723-2.264z" fill="#ad2115" /></g ></svg ></a>
|
<a href="https://redis.io"><svg style="width:40px;height:40px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><defs ><path id="a" d="m45.536 38.764c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.813s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z" /><path id="b" d="m45.536 28.733c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.935c2.332-.837 3.14-.867 5.126-.14s12.35 4.853 14.312 5.57 2.037 1.31.024 2.36z" /></defs ><g transform="matrix(.848327 0 0 .848327 -7.883573 -9.449691)" ><use fill="#a41e11" xlink:href="#a" /><path d="m45.536 34.95c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.936c2.332-.836 3.14-.867 5.126-.14s12.35 4.852 14.31 5.582 2.037 1.31.024 2.36z" fill="#d82c20" /><use fill="#a41e11" xlink:href="#a" y="-6.218" /><use fill="#d82c20" xlink:href="#b" /><path d="m45.536 26.098c-2.013 1.05-12.44 5.337-14.66 6.495s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.815s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z" fill="#a41e11" /><use fill="#d82c20" xlink:href="#b" y="-6.449" /><g fill="#fff" ><path d="m29.096 20.712-1.182-1.965-3.774-.34 2.816-1.016-.845-1.56 2.636 1.03 2.486-.814-.672 1.612 2.534.95-3.268.34zm-6.296 3.912 8.74-1.342-2.64 3.872z" /><ellipse cx="20.444" cy="21.402" rx="4.672" ry="1.811" /></g ><path d="m42.132 21.138-5.17 2.042-.004-4.087z" fill="#7a0c00" /><path d="m36.963 23.18-.56.22-5.166-2.042 5.723-2.264z" fill="#ad2115" /></g ></svg ></a>
|
||||||
|
|
||||||
### Services
|
### Services
|
||||||
|
|
||||||
- [Appwrite](https://appwrite.io)
|
- [Appwrite](https://appwrite.io)
|
||||||
- [WordPress](https://docs.coollabs.io/coolify/services/wordpress)
|
- [WordPress](https://docs.coollabs.io/coolify/services/wordpress)
|
||||||
- [Ghost](https://ghost.org)
|
- [Ghost](https://ghost.org)
|
||||||
@@ -93,19 +94,39 @@ Deploy your resource to:
|
|||||||
- [Fider](https://fider.io)
|
- [Fider](https://fider.io)
|
||||||
- [Hasura](https://hasura.io)
|
- [Hasura](https://hasura.io)
|
||||||
- [GlitchTip](https://glitchtip.com)
|
- [GlitchTip](https://glitchtip.com)
|
||||||
|
- And more...
|
||||||
## Migration from v1
|
|
||||||
|
|
||||||
A fresh installation is necessary. v2 and v3 are not compatible with v1.
|
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
- Twitter: [@andrasbacsai](https://twitter.com/andrasbacsai)
|
- Mastodon: [@andrasbacsai@fosstodon.org](https://fosstodon.org/@andrasbacsai)
|
||||||
- Telegram: [@andrasbacsai](https://t.me/andrasbacsai)
|
- Telegram: [@andrasbacsai](https://t.me/andrasbacsai)
|
||||||
|
- Twitter: [@andrasbacsai](https://twitter.com/andrasbacsai)
|
||||||
- Email: [andras@coollabs.io](mailto:andras@coollabs.io)
|
- Email: [andras@coollabs.io](mailto:andras@coollabs.io)
|
||||||
- Discord: [Invitation](https://coollabs.io/discord)
|
- Discord: [Invitation](https://coollabs.io/discord)
|
||||||
|
|
||||||
## Financial Contributors
|
---
|
||||||
|
|
||||||
|
## ⚗️ Expertise Contributions
|
||||||
|
|
||||||
|
Coolify is developed under the [Apache License](./LICENSE) and you can help to make it grow.
|
||||||
|
Our community will be glad to have you on board!
|
||||||
|
|
||||||
|
Learn how to contribute to Coolify as as ...
|
||||||
|
|
||||||
|
→ [👩🏾💻 Software developer](./CONTRIBUTION.md)
|
||||||
|
|
||||||
|
→ [🧑🏻🏫 Translator](./docs/contribution/Translating.md)
|
||||||
|
|
||||||
|
<!--
|
||||||
|
→ 🧑🏽🎨 Designer
|
||||||
|
→ 🙋♀️ Community Managemer
|
||||||
|
→ 🧙🏻♂️ Text Content Creator
|
||||||
|
→ 👨🏼🎤 Video Content Creator
|
||||||
|
-->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💰 Financial Contributors
|
||||||
|
|
||||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
COOLIFY_APP_ID=local-dev
|
COOLIFY_APP_ID=local-dev
|
||||||
# 32 bits long secret key
|
# 32 bits long secret key
|
||||||
COOLIFY_SECRET_KEY=12341234123412341234123412341234
|
COOLIFY_SECRET_KEY=12341234123412341234123412341234
|
||||||
COOLIFY_DATABASE_URL=file:../db/dev.db
|
COOLIFY_DATABASE_URL=file:../db/dev.db
|
||||||
COOLIFY_SENTRY_DSN=
|
|
||||||
|
COOLIFY_IS_ON=docker
|
||||||
COOLIFY_IS_ON=docker
|
COOLIFY_WHITE_LABELED=false
|
||||||
COOLIFY_WHITE_LABELED=false
|
COOLIFY_WHITE_LABELED_ICON=
|
||||||
COOLIFY_WHITE_LABELED_ICON=
|
COOLIFY_AUTO_UPDATE=
|
||||||
COOLIFY_AUTO_UPDATE=
|
|
||||||
|
|||||||
4
apps/api/.gitignore
vendored
4
apps/api/.gitignore
vendored
@@ -8,4 +8,6 @@ package
|
|||||||
!.env.example
|
!.env.example
|
||||||
dist
|
dist
|
||||||
dev.db
|
dev.db
|
||||||
client
|
client
|
||||||
|
testTemplate.yaml
|
||||||
|
testTags.json
|
||||||
BIN
apps/api/db/dev.db.bak
Normal file
BIN
apps/api/db/dev.db.bak
Normal file
Binary file not shown.
983
apps/api/devTags.json
Normal file
983
apps/api/devTags.json
Normal file
@@ -0,0 +1,983 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "appsmith",
|
||||||
|
"image": "appsmith/appsmith-ce",
|
||||||
|
"tags": [
|
||||||
|
"v1.9.3",
|
||||||
|
"v1.9.1",
|
||||||
|
"v1.8.15",
|
||||||
|
"v1.8.12",
|
||||||
|
"v1.8.10",
|
||||||
|
"v1.8.9",
|
||||||
|
"v1.8.7",
|
||||||
|
"v1.8.5",
|
||||||
|
"v1.8.3",
|
||||||
|
"v1.8.0",
|
||||||
|
"v1.7.13",
|
||||||
|
"v1.7.11",
|
||||||
|
"v1.7.8",
|
||||||
|
"v1.7.6",
|
||||||
|
"v1.7.4",
|
||||||
|
"v1.7.2",
|
||||||
|
"v1.7.1",
|
||||||
|
"v1.6.22",
|
||||||
|
"v1.6.20",
|
||||||
|
"v1.6.19",
|
||||||
|
"v1.6.17",
|
||||||
|
"v1.6.15",
|
||||||
|
"v1.6.13",
|
||||||
|
"v1.6.11",
|
||||||
|
"v1.6.9",
|
||||||
|
"v1.6.7",
|
||||||
|
"v1.6.5",
|
||||||
|
"v1.6.3",
|
||||||
|
"v1.6.1",
|
||||||
|
"v1.5.30"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "appwrite",
|
||||||
|
"image": "appwrite/appwrite",
|
||||||
|
"tags": [
|
||||||
|
"1.2.0",
|
||||||
|
"1.1.2",
|
||||||
|
"1.1.0",
|
||||||
|
"1.0.3",
|
||||||
|
"1.0.1",
|
||||||
|
"1.0.0",
|
||||||
|
"0.15.2",
|
||||||
|
"0.15.0",
|
||||||
|
"0.14.2",
|
||||||
|
"0.14.0",
|
||||||
|
"0.13.4",
|
||||||
|
"0.13.2",
|
||||||
|
"0.13.0",
|
||||||
|
"0.12.3",
|
||||||
|
"0.12.1",
|
||||||
|
"0.12.0",
|
||||||
|
"0.11.2",
|
||||||
|
"0.11.0",
|
||||||
|
"0.10.4",
|
||||||
|
"0.10.2",
|
||||||
|
"0.10.0",
|
||||||
|
"0.9.3",
|
||||||
|
"0.9.1",
|
||||||
|
"0.8.0",
|
||||||
|
"0.7.1",
|
||||||
|
"0.6.2",
|
||||||
|
"0.6.0",
|
||||||
|
"0.5.2",
|
||||||
|
"0.5.0",
|
||||||
|
"0.3.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "directus-postgresql",
|
||||||
|
"image": "directus/directus",
|
||||||
|
"tags": [
|
||||||
|
"9.22.3",
|
||||||
|
"9.22.0",
|
||||||
|
"9.21.0",
|
||||||
|
"9.20.4",
|
||||||
|
"9.20.2",
|
||||||
|
"9.20.0",
|
||||||
|
"9.19.2",
|
||||||
|
"9.18.0",
|
||||||
|
"9.17.4",
|
||||||
|
"9.17.2",
|
||||||
|
"9.17.0",
|
||||||
|
"9.16.0",
|
||||||
|
"9.15.0",
|
||||||
|
"9.14.5",
|
||||||
|
"9.14.3",
|
||||||
|
"9.14.0",
|
||||||
|
"9.13.0",
|
||||||
|
"9.12.2",
|
||||||
|
"9.12.0",
|
||||||
|
"9.11.0",
|
||||||
|
"9.10.0",
|
||||||
|
"9.9.0",
|
||||||
|
"9.8.0",
|
||||||
|
"9.7.0",
|
||||||
|
"9.6.0",
|
||||||
|
"9.5.2",
|
||||||
|
"9.5.0",
|
||||||
|
"9.4.2",
|
||||||
|
"9.4.0",
|
||||||
|
"9.3.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fider",
|
||||||
|
"image": "getfider/fider",
|
||||||
|
"tags": [
|
||||||
|
"stable",
|
||||||
|
"master",
|
||||||
|
"main",
|
||||||
|
"dev",
|
||||||
|
"SHA_ee6e83cfaadadaa56ab76e089e01f5631af3506f",
|
||||||
|
"SHA_deb4f9b4f561d890d8a80e6872fea9a98a265cc6",
|
||||||
|
"SHA_d5cc307909d43447200483d76b5db74d8ed8349e",
|
||||||
|
"SHA_d1674476577a7fd3c88fc29f91c3f35f5bd6a260",
|
||||||
|
"SHA_d107cbb157abca6576110080736213efe0955cff",
|
||||||
|
"SHA_c9c55b2f5b33a76015241b97e03cfac1254b42a7",
|
||||||
|
"SHA_bcf451a3cb02d5c8a489fd30309249296057b084",
|
||||||
|
"SHA_bbfe419639514f949a042807addf0fde7d4de225",
|
||||||
|
"SHA_adc3afc4c7bcf96931a5f90cab65c282d860dbfd",
|
||||||
|
"SHA_ab5283ae95334f10b5041402dce79e333c472015",
|
||||||
|
"SHA_a3f4cb5ed0a4ee2d726705fc426636364aac17a1",
|
||||||
|
"SHA_a18224142bf51bc6463c3d22f45f62287902e9a6",
|
||||||
|
"SHA_8e5cff30d95963eaee2587488d351e0d658c8195",
|
||||||
|
"SHA_8cabe2817ce7ccaf2f0a9fdbb1b5d3411de87f81",
|
||||||
|
"SHA_7851f9da566132d87fa2a63004e78c3bc9c09c6c",
|
||||||
|
"SHA_6c0f2bed1754e9d579eb9575129a6e3dbc529c32",
|
||||||
|
"SHA_603508c8790d6a6fb1e852df1a58ead8e5b3ea6c",
|
||||||
|
"SHA_55efacf164a4749b50ee68ae8925e7dc9dfa3a0c",
|
||||||
|
"SHA_4bdd291ce61e5f5dfc063fa1b2d9be8c9ff1d4c4",
|
||||||
|
"SHA_3fba9cb6a9ceab0c78c6cff3220610f591f657cb",
|
||||||
|
"SHA_3d635b57606a9885babe91fe975b11429e0f2c38",
|
||||||
|
"SHA_3b794edbd9789a8aa38ecd3714bc536a675d3058",
|
||||||
|
"SHA_3570c454ad3252b690608f7bf8051737d8519f8a",
|
||||||
|
"SHA_263e2709fd145f3ea511e5557e170102899995b0",
|
||||||
|
"SHA_255c30ed012fc4c39ffc97efc1d3b00425b17c72",
|
||||||
|
"SHA_17f92b16ef790003338f0926fc8d791a9a61333c"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ghost-mariadb",
|
||||||
|
"image": "bitnami/ghost",
|
||||||
|
"tags": [
|
||||||
|
"5.30.1",
|
||||||
|
"5.30.0",
|
||||||
|
"5.29.0",
|
||||||
|
"5.28.0",
|
||||||
|
"5.27.0",
|
||||||
|
"5.26.4",
|
||||||
|
"5.26.3",
|
||||||
|
"5.26.2",
|
||||||
|
"5.26.1",
|
||||||
|
"5.26.0",
|
||||||
|
"5.25.5",
|
||||||
|
"5.25.4",
|
||||||
|
"5.25.3",
|
||||||
|
"5.25.2",
|
||||||
|
"5.25.1",
|
||||||
|
"5.25.0",
|
||||||
|
"5.24.2",
|
||||||
|
"5.24.1",
|
||||||
|
"5.24.0",
|
||||||
|
"5.23.0",
|
||||||
|
"5.22.11",
|
||||||
|
"5.22.10",
|
||||||
|
"5.22.9",
|
||||||
|
"5.22.8",
|
||||||
|
"5.22.7",
|
||||||
|
"5.22.6",
|
||||||
|
"5.22.5",
|
||||||
|
"5.22.4",
|
||||||
|
"5.22.3",
|
||||||
|
"5.22.2",
|
||||||
|
"4.48.8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ghost-mysql",
|
||||||
|
"image": "library/ghost",
|
||||||
|
"tags": [
|
||||||
|
"5.30.0",
|
||||||
|
"5.29.0",
|
||||||
|
"5.28.0",
|
||||||
|
"5.27.0",
|
||||||
|
"5.26.4",
|
||||||
|
"5.26.3",
|
||||||
|
"5.26.2",
|
||||||
|
"5.26.1",
|
||||||
|
"5.25.5",
|
||||||
|
"5.25.3",
|
||||||
|
"5.25.2",
|
||||||
|
"5.25.1",
|
||||||
|
"5.25.0",
|
||||||
|
"5.24.2",
|
||||||
|
"5.24.1",
|
||||||
|
"5.23.0",
|
||||||
|
"5.22.11",
|
||||||
|
"5.22.10",
|
||||||
|
"5.22.9",
|
||||||
|
"5.22.8",
|
||||||
|
"5.22.4",
|
||||||
|
"5.22.1",
|
||||||
|
"5.20.0",
|
||||||
|
"5.19.3",
|
||||||
|
"5.19.0",
|
||||||
|
"5.18.0",
|
||||||
|
"5.17.2",
|
||||||
|
"5.17.1",
|
||||||
|
"5.17.0",
|
||||||
|
"5.16.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ghost-only",
|
||||||
|
"image": "library/ghost",
|
||||||
|
"tags": [
|
||||||
|
"5.30.0",
|
||||||
|
"5.29.0",
|
||||||
|
"5.28.0",
|
||||||
|
"5.27.0",
|
||||||
|
"5.26.4",
|
||||||
|
"5.26.3",
|
||||||
|
"5.26.2",
|
||||||
|
"5.26.1",
|
||||||
|
"5.25.5",
|
||||||
|
"5.25.3",
|
||||||
|
"5.25.2",
|
||||||
|
"5.25.1",
|
||||||
|
"5.25.0",
|
||||||
|
"5.24.2",
|
||||||
|
"5.24.1",
|
||||||
|
"5.23.0",
|
||||||
|
"5.22.11",
|
||||||
|
"5.22.10",
|
||||||
|
"5.22.9",
|
||||||
|
"5.22.8",
|
||||||
|
"5.22.4",
|
||||||
|
"5.22.1",
|
||||||
|
"5.20.0",
|
||||||
|
"5.19.3",
|
||||||
|
"5.19.0",
|
||||||
|
"5.18.0",
|
||||||
|
"5.17.2",
|
||||||
|
"5.17.1",
|
||||||
|
"5.17.0",
|
||||||
|
"5.16.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "gitea",
|
||||||
|
"image": "gitea/gitea",
|
||||||
|
"tags": [
|
||||||
|
"1.18.0",
|
||||||
|
"1.17.4",
|
||||||
|
"1.17.3",
|
||||||
|
"1.17.2",
|
||||||
|
"1.17.1",
|
||||||
|
"1.17.0",
|
||||||
|
"1.16.9",
|
||||||
|
"1.16.8",
|
||||||
|
"1.16.7",
|
||||||
|
"1.16.6",
|
||||||
|
"1.16.5",
|
||||||
|
"1.16.4",
|
||||||
|
"1.16.3",
|
||||||
|
"1.16.2",
|
||||||
|
"1.16.1",
|
||||||
|
"1.16.0",
|
||||||
|
"1.15.11",
|
||||||
|
"1.15.10",
|
||||||
|
"1.15.9",
|
||||||
|
"1.15.8",
|
||||||
|
"1.15.7",
|
||||||
|
"1.15.6",
|
||||||
|
"1.15.5",
|
||||||
|
"1.15.4",
|
||||||
|
"1.15.3",
|
||||||
|
"1.15.2",
|
||||||
|
"1.15.1",
|
||||||
|
"1.15.0",
|
||||||
|
"1.14.7",
|
||||||
|
"1.14.6"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "glitchtip",
|
||||||
|
"image": "glitchtip/glitchtip",
|
||||||
|
"tags": [
|
||||||
|
"v3.0.2",
|
||||||
|
"v3.0.0",
|
||||||
|
"v2.0.7",
|
||||||
|
"v2.0.5",
|
||||||
|
"v2.0.2",
|
||||||
|
"v2.0.0",
|
||||||
|
"v1.12.4",
|
||||||
|
"v1.12.2",
|
||||||
|
"v1.12.0",
|
||||||
|
"v1.10.3",
|
||||||
|
"v1.10.1",
|
||||||
|
"v1.9.2",
|
||||||
|
"v1.9.0",
|
||||||
|
"v1.8.4",
|
||||||
|
"v1.8.2",
|
||||||
|
"v1.8.0",
|
||||||
|
"v1.7.1",
|
||||||
|
"v1.6.4",
|
||||||
|
"v1.6.2",
|
||||||
|
"v1.6.0",
|
||||||
|
"v1.5.3",
|
||||||
|
"v1.5.1",
|
||||||
|
"v1.4.1",
|
||||||
|
"v1.3.3",
|
||||||
|
"v1.3.1",
|
||||||
|
"v1.2.6",
|
||||||
|
"v1.2.4",
|
||||||
|
"v1.2.2",
|
||||||
|
"v1.2.0",
|
||||||
|
"v1.1.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "grafana",
|
||||||
|
"image": "grafana/grafana",
|
||||||
|
"tags": [
|
||||||
|
"9.3.2",
|
||||||
|
"9.3.1",
|
||||||
|
"9.3.0",
|
||||||
|
"9.2.8",
|
||||||
|
"9.2.7",
|
||||||
|
"9.2.6",
|
||||||
|
"9.2.5",
|
||||||
|
"9.2.4",
|
||||||
|
"9.2.3",
|
||||||
|
"9.2.2",
|
||||||
|
"9.2.1",
|
||||||
|
"9.2.0",
|
||||||
|
"9.1.8",
|
||||||
|
"9.1.7",
|
||||||
|
"9.1.6",
|
||||||
|
"9.1.5",
|
||||||
|
"9.1.4",
|
||||||
|
"9.1.3",
|
||||||
|
"9.1.2",
|
||||||
|
"9.1.1",
|
||||||
|
"9.1.0",
|
||||||
|
"9.0.9",
|
||||||
|
"9.0.8",
|
||||||
|
"9.0.7",
|
||||||
|
"9.0.6",
|
||||||
|
"9.0.5",
|
||||||
|
"9.0.4",
|
||||||
|
"9.0.3",
|
||||||
|
"9.0.2",
|
||||||
|
"9.0.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hasura",
|
||||||
|
"image": "hasura/graphql-engine",
|
||||||
|
"tags": [
|
||||||
|
"v2.16.1",
|
||||||
|
"v2.16.0",
|
||||||
|
"v2.15.2",
|
||||||
|
"v2.14.1",
|
||||||
|
"v2.13.2",
|
||||||
|
"v2.12.1",
|
||||||
|
"v2.11.3",
|
||||||
|
"v2.10.2",
|
||||||
|
"v2.9.0",
|
||||||
|
"v2.8.4",
|
||||||
|
"v2.8.3",
|
||||||
|
"v2.8.2",
|
||||||
|
"v2.8.1",
|
||||||
|
"v2.8.0",
|
||||||
|
"v2.7.0",
|
||||||
|
"v2.6.2",
|
||||||
|
"v2.6.1",
|
||||||
|
"v2.6.0",
|
||||||
|
"v2.5.2",
|
||||||
|
"v2.5.1",
|
||||||
|
"v2.5.0",
|
||||||
|
"v2.4.0",
|
||||||
|
"v2.3.1",
|
||||||
|
"v2.3.0",
|
||||||
|
"v2.2.2",
|
||||||
|
"v2.2.1",
|
||||||
|
"v2.2.0",
|
||||||
|
"v2.1.1",
|
||||||
|
"v2.1.0",
|
||||||
|
"v2.0.10"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "keycloak",
|
||||||
|
"image": "quay.io/keycloak/keycloak",
|
||||||
|
"tags": [
|
||||||
|
"9.0.3",
|
||||||
|
"9.0.0",
|
||||||
|
"8.0.1",
|
||||||
|
"7.0.0",
|
||||||
|
"6.0.1",
|
||||||
|
"6.0.0",
|
||||||
|
"20.0.3",
|
||||||
|
"20.0.2",
|
||||||
|
"20.0.1",
|
||||||
|
"20.0.0",
|
||||||
|
"19.0.3",
|
||||||
|
"19.0.1",
|
||||||
|
"19.0.0",
|
||||||
|
"18.0.1",
|
||||||
|
"18.0.0",
|
||||||
|
"17.0.1",
|
||||||
|
"17.0.0",
|
||||||
|
"16.1.0",
|
||||||
|
"15.1.1",
|
||||||
|
"15.0.2",
|
||||||
|
"15.0.0",
|
||||||
|
"13.0.1",
|
||||||
|
"12.0.4",
|
||||||
|
"12.0.2",
|
||||||
|
"12.0.0",
|
||||||
|
"11.0.2",
|
||||||
|
"11.0.0",
|
||||||
|
"10.0.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "languagetool",
|
||||||
|
"image": "silviof/docker-languagetool",
|
||||||
|
"tags": ["latest", "6.0", "5.8", "5.7", "5.6", "5.5", "5.4", "5.3"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lavalink",
|
||||||
|
"image": "fredboat/lavalink",
|
||||||
|
"tags": ["3.7.0", "3.6.1", "3.5.1", "v2.0.1"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "libretranslate",
|
||||||
|
"image": "libretranslate/libretranslate",
|
||||||
|
"tags": ["v1.3.8", "v1.3.6", "v1.3.4", "v1.3.2", "v1.3.0", "v1.2.8"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "meilisearch",
|
||||||
|
"image": "getmeili/meilisearch",
|
||||||
|
"tags": [
|
||||||
|
"v0.30.5",
|
||||||
|
"v0.30.3",
|
||||||
|
"v0.30.1",
|
||||||
|
"v0.30.0",
|
||||||
|
"v0.29.3",
|
||||||
|
"v0.29.1",
|
||||||
|
"v0.29.0",
|
||||||
|
"v0.28.1",
|
||||||
|
"v0.28.0",
|
||||||
|
"v0.27.1",
|
||||||
|
"v0.27.0",
|
||||||
|
"v0.26.1",
|
||||||
|
"v0.26.0",
|
||||||
|
"v0.25.1",
|
||||||
|
"v0.25.0",
|
||||||
|
"v0.23.1",
|
||||||
|
"v0.23.0",
|
||||||
|
"v0.21.1",
|
||||||
|
"v0.21.0",
|
||||||
|
"v0.20.0",
|
||||||
|
"v0.19.0",
|
||||||
|
"v0.18.1",
|
||||||
|
"v0.18.0",
|
||||||
|
"v0.17.0",
|
||||||
|
"v0.16.0",
|
||||||
|
"0.14.1",
|
||||||
|
"v0.14.1",
|
||||||
|
"v0.14.0",
|
||||||
|
"v0.12.0",
|
||||||
|
"v0.11.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "minio",
|
||||||
|
"image": "minio/minio",
|
||||||
|
"tags": [
|
||||||
|
"RELEASE.2023-01-12T02-06-16Z",
|
||||||
|
"RELEASE.2023-01-06T18-11-18Z",
|
||||||
|
"RELEASE.2023-01-02T09-40-09Z",
|
||||||
|
"RELEASE.2022-12-12T19-27-27Z",
|
||||||
|
"RELEASE.2022-12-07T00-56-37Z",
|
||||||
|
"RELEASE.2022-12-02T19-19-22Z",
|
||||||
|
"RELEASE.2022-11-29T23-40-49Z",
|
||||||
|
"RELEASE.2022-11-26T22-43-32Z",
|
||||||
|
"RELEASE.2022-11-17T23-20-09Z",
|
||||||
|
"RELEASE.2022-11-11T03-44-20Z",
|
||||||
|
"RELEASE.2022-11-10T18-20-21Z",
|
||||||
|
"RELEASE.2022-11-08T05-27-07Z",
|
||||||
|
"RELEASE.2022-10-29T06-21-33Z",
|
||||||
|
"RELEASE.2022-10-24T18-35-07Z.hotfix.7906ac5be",
|
||||||
|
"RELEASE.2022-10-24T18-35-07Z",
|
||||||
|
"RELEASE.2022-10-21T22-37-48Z",
|
||||||
|
"RELEASE.2022-10-20T00-55-09Z",
|
||||||
|
"RELEASE.2022-10-15T19-57-03Z",
|
||||||
|
"RELEASE.2022-10-08T20-11-00Z",
|
||||||
|
"RELEASE.2022-10-05T14-58-27Z",
|
||||||
|
"RELEASE.2022-10-02T19-29-29Z",
|
||||||
|
"RELEASE.2022-09-25T15-44-53Z",
|
||||||
|
"RELEASE.2022-09-22T18-57-27Z",
|
||||||
|
"RELEASE.2022-09-17T00-09-45Z.hotfix.f76e5da9f",
|
||||||
|
"RELEASE.2022-09-17T00-09-45Z.fips",
|
||||||
|
"RELEASE.2022-09-07T22-25-02Z.fips",
|
||||||
|
"RELEASE.2022-09-01T23-53-36Z.fips",
|
||||||
|
"RELEASE.2022-08-26T19-53-15Z.fips",
|
||||||
|
"RELEASE.2022-08-25T07-17-05Z.fips",
|
||||||
|
"RELEASE.2022-08-22T23-53-06Z.hotfix.5fa3967bb"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "n8n",
|
||||||
|
"image": "n8nio/n8n",
|
||||||
|
"tags": [
|
||||||
|
"0.210.1",
|
||||||
|
"0.210.0",
|
||||||
|
"0.209.4",
|
||||||
|
"0.209.3",
|
||||||
|
"0.209.2",
|
||||||
|
"0.209.1",
|
||||||
|
"0.209.0",
|
||||||
|
"0.208.1",
|
||||||
|
"0.208.0",
|
||||||
|
"0.207.1",
|
||||||
|
"0.207.0",
|
||||||
|
"0.206.1",
|
||||||
|
"0.205.0",
|
||||||
|
"0.204.0",
|
||||||
|
"0.203.1",
|
||||||
|
"0.202.1",
|
||||||
|
"0.202.0",
|
||||||
|
"0.201.0",
|
||||||
|
"0.200.1",
|
||||||
|
"0.199.0",
|
||||||
|
"0.198.1",
|
||||||
|
"0.198.0",
|
||||||
|
"0.197.1",
|
||||||
|
"0.197.0",
|
||||||
|
"0.196.0",
|
||||||
|
"0.195.5",
|
||||||
|
"0.195.4",
|
||||||
|
"0.195.3",
|
||||||
|
"0.195.2",
|
||||||
|
"0.195.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nocodb",
|
||||||
|
"image": "nocodb/nocodb",
|
||||||
|
"tags": [
|
||||||
|
"0.101.2",
|
||||||
|
"0.101.0",
|
||||||
|
"0.100.1",
|
||||||
|
"0.99.2",
|
||||||
|
"0.99.0",
|
||||||
|
"0.98.3",
|
||||||
|
"0.98.1",
|
||||||
|
"0.97.0",
|
||||||
|
"0.96.3",
|
||||||
|
"0.96.1",
|
||||||
|
"0.92.4",
|
||||||
|
"0.92.0",
|
||||||
|
"0.91.8",
|
||||||
|
"0.91.6",
|
||||||
|
"0.91.1",
|
||||||
|
"0.90.11",
|
||||||
|
"0.90.8",
|
||||||
|
"0.90.5",
|
||||||
|
"0.90.3",
|
||||||
|
"0.90.1",
|
||||||
|
"0.84.16",
|
||||||
|
"0.84.14",
|
||||||
|
"0.84.10",
|
||||||
|
"0.84.9",
|
||||||
|
"0.84.7",
|
||||||
|
"0.84.3",
|
||||||
|
"0.83.8",
|
||||||
|
"0.83.5",
|
||||||
|
"0.83.2",
|
||||||
|
"0.83.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "openblocks",
|
||||||
|
"image": "openblocksdev/openblocks-ce",
|
||||||
|
"tags": ["1.1.3", "1.1.1", "1.0.21"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "plausibleanalytics-arm",
|
||||||
|
"image": "plausible/analytics",
|
||||||
|
"tags": [
|
||||||
|
"v1.5.1",
|
||||||
|
"v1.5.0",
|
||||||
|
"v1.4.3",
|
||||||
|
"v1.4.1",
|
||||||
|
"v1.4.0",
|
||||||
|
"v1.3.0",
|
||||||
|
"v1.2.0",
|
||||||
|
"v1.1.0",
|
||||||
|
"v1.0.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "plausibleanalytics",
|
||||||
|
"image": "plausible/analytics",
|
||||||
|
"tags": [
|
||||||
|
"v1.5.1",
|
||||||
|
"v1.5.0",
|
||||||
|
"v1.4.3",
|
||||||
|
"v1.4.1",
|
||||||
|
"v1.4.0",
|
||||||
|
"v1.3.0",
|
||||||
|
"v1.2.0",
|
||||||
|
"v1.1.0",
|
||||||
|
"v1.0.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ "name": "pocketbase", "image": "coollabsio/pocketbase", "tags": ["0.11.0", "0.10.2", "0.8.0"] },
|
||||||
|
{
|
||||||
|
"name": "searxng",
|
||||||
|
"image": "searxng/searxng",
|
||||||
|
"tags": [
|
||||||
|
"2023.01.15-52d41559",
|
||||||
|
"2023.01.15-13b0c251",
|
||||||
|
"2023.01.14-b720a495",
|
||||||
|
"2023.01.14-449aebae",
|
||||||
|
"2023.01.14-18d895ff",
|
||||||
|
"2023.01.09-afd71a6c",
|
||||||
|
"2023.01.09-a90ed481",
|
||||||
|
"2023.01.08-54e63839",
|
||||||
|
"2023.01.08-4e735b28",
|
||||||
|
"2023.01.08-217395b8",
|
||||||
|
"2023.01.08-0c429d70",
|
||||||
|
"2023.01.07-cb7b0916",
|
||||||
|
"2023.01.07-a98c5156",
|
||||||
|
"2023.01.07-633ba8b1",
|
||||||
|
"2023.01.07-4e355564",
|
||||||
|
"2023.01.06-b241015e",
|
||||||
|
"2023.01.06-269a72ee",
|
||||||
|
"2023.01.05-aba969cc",
|
||||||
|
"2022.12.30-b6d98be7",
|
||||||
|
"2022.12.30-647a0aa9",
|
||||||
|
"2022.12.30-17516290",
|
||||||
|
"2022.12.29-d531f893",
|
||||||
|
"2022.12.29-9b31976c",
|
||||||
|
"2022.12.29-76cd808a",
|
||||||
|
"2022.12.29-3ec58b06",
|
||||||
|
"2022.12.29-174e6851",
|
||||||
|
"2022.12.26-0d489617",
|
||||||
|
"2022.12.23-e8f72d70",
|
||||||
|
"2022.12.23-a2d506d4",
|
||||||
|
"2022.12.22-d75ae7c8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "trilium",
|
||||||
|
"image": "zadam/trilium",
|
||||||
|
"tags": [
|
||||||
|
"0.58.4",
|
||||||
|
"0.57.4",
|
||||||
|
"0.57.2",
|
||||||
|
"0.56.1",
|
||||||
|
"0.55.1",
|
||||||
|
"0.54.2",
|
||||||
|
"0.53.2",
|
||||||
|
"0.52.4",
|
||||||
|
"0.52.2",
|
||||||
|
"0.51.2",
|
||||||
|
"0.50.3",
|
||||||
|
"0.50.1",
|
||||||
|
"0.49.5",
|
||||||
|
"0.49.3",
|
||||||
|
"0.48.9",
|
||||||
|
"0.48.7",
|
||||||
|
"0.48.4",
|
||||||
|
"0.48.2",
|
||||||
|
"0.47.8",
|
||||||
|
"0.47.6",
|
||||||
|
"0.47.4",
|
||||||
|
"0.47.2",
|
||||||
|
"0.46.7",
|
||||||
|
"0.46.5",
|
||||||
|
"0.45.10",
|
||||||
|
"0.45.9",
|
||||||
|
"0.45.7",
|
||||||
|
"0.45.5",
|
||||||
|
"0.45.3",
|
||||||
|
"0.44.8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "umami-postgresql",
|
||||||
|
"image": "ghcr.io/umami-software/umami",
|
||||||
|
"tags": [
|
||||||
|
"postgresql-v1.39.5",
|
||||||
|
"postgresql-v1.39.4",
|
||||||
|
"postgresql-v1.39.3",
|
||||||
|
"postgresql-v1.39.2",
|
||||||
|
"postgresql-v1.39.1",
|
||||||
|
"postgresql-v1.39.0",
|
||||||
|
"postgresql-v1.38.0",
|
||||||
|
"postgresql-v1.37.0",
|
||||||
|
"postgresql-v1.36.1",
|
||||||
|
"postgresql-v1.36.0",
|
||||||
|
"postgresql-v1.35.0",
|
||||||
|
"postgresql-v1.34.0",
|
||||||
|
"postgresql-v1.33.3",
|
||||||
|
"postgresql-latest",
|
||||||
|
"mysql-v1.39.5",
|
||||||
|
"mysql-v1.39.4",
|
||||||
|
"mysql-v1.39.3",
|
||||||
|
"mysql-v1.39.2",
|
||||||
|
"mysql-v1.39.1",
|
||||||
|
"mysql-v1.39.0",
|
||||||
|
"mysql-v1.38.0",
|
||||||
|
"mysql-v1.37.0",
|
||||||
|
"mysql-v1.36.1",
|
||||||
|
"mysql-v1.36.0",
|
||||||
|
"mysql-v1.35.0",
|
||||||
|
"mysql-v1.34.0",
|
||||||
|
"mysql-v1.33.3",
|
||||||
|
"mysql-latest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "umami",
|
||||||
|
"image": "ghcr.io/umami-software/umami",
|
||||||
|
"tags": [
|
||||||
|
"postgresql-v1.39.5",
|
||||||
|
"postgresql-v1.39.4",
|
||||||
|
"postgresql-v1.39.3",
|
||||||
|
"postgresql-v1.39.2",
|
||||||
|
"postgresql-v1.39.1",
|
||||||
|
"postgresql-v1.39.0",
|
||||||
|
"postgresql-v1.38.0",
|
||||||
|
"postgresql-v1.37.0",
|
||||||
|
"postgresql-v1.36.1",
|
||||||
|
"postgresql-v1.36.0",
|
||||||
|
"postgresql-v1.35.0",
|
||||||
|
"postgresql-v1.34.0",
|
||||||
|
"postgresql-v1.33.3",
|
||||||
|
"postgresql-latest",
|
||||||
|
"mysql-v1.39.5",
|
||||||
|
"mysql-v1.39.4",
|
||||||
|
"mysql-v1.39.3",
|
||||||
|
"mysql-v1.39.2",
|
||||||
|
"mysql-v1.39.1",
|
||||||
|
"mysql-v1.39.0",
|
||||||
|
"mysql-v1.38.0",
|
||||||
|
"mysql-v1.37.0",
|
||||||
|
"mysql-v1.36.1",
|
||||||
|
"mysql-v1.36.0",
|
||||||
|
"mysql-v1.35.0",
|
||||||
|
"mysql-v1.34.0",
|
||||||
|
"mysql-v1.33.3",
|
||||||
|
"mysql-latest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "uptimekuma",
|
||||||
|
"image": "louislam/uptime-kuma",
|
||||||
|
"tags": [
|
||||||
|
"1.19.4",
|
||||||
|
"1.19.3",
|
||||||
|
"1.19.2",
|
||||||
|
"1.19.1",
|
||||||
|
"1.19.0",
|
||||||
|
"1.18.5",
|
||||||
|
"1.18.4",
|
||||||
|
"1.18.3",
|
||||||
|
"1.18.2",
|
||||||
|
"1.18.1",
|
||||||
|
"1.18.0",
|
||||||
|
"1.17.1",
|
||||||
|
"1.17.0",
|
||||||
|
"1.16.1",
|
||||||
|
"1.16.0",
|
||||||
|
"1.15.1",
|
||||||
|
"1.15.0",
|
||||||
|
"1.14.1",
|
||||||
|
"1.14.0",
|
||||||
|
"1.13.2",
|
||||||
|
"1.13.1",
|
||||||
|
"1.13.0",
|
||||||
|
"1.12.1",
|
||||||
|
"1.11.4",
|
||||||
|
"1.11.3",
|
||||||
|
"1.11.2",
|
||||||
|
"1.11.1",
|
||||||
|
"1.11.0",
|
||||||
|
"1.10.2",
|
||||||
|
"1.10.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vaultwarden",
|
||||||
|
"image": "vaultwarden/server",
|
||||||
|
"tags": [
|
||||||
|
"1.27.0",
|
||||||
|
"1.26.0",
|
||||||
|
"1.25.2",
|
||||||
|
"1.25.1",
|
||||||
|
"1.25.0",
|
||||||
|
"1.24.0",
|
||||||
|
"1.23.1",
|
||||||
|
"1.23.0",
|
||||||
|
"1.22.2",
|
||||||
|
"1.22.1",
|
||||||
|
"1.22.0",
|
||||||
|
"1.21.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vscodeserver",
|
||||||
|
"image": "codercom/code-server",
|
||||||
|
"tags": [
|
||||||
|
"4.9.1",
|
||||||
|
"4.9.0",
|
||||||
|
"4.8.3",
|
||||||
|
"4.8.2",
|
||||||
|
"4.8.1",
|
||||||
|
"4.8.0",
|
||||||
|
"4.7.0",
|
||||||
|
"4.6.0",
|
||||||
|
"4.5.1",
|
||||||
|
"4.4.0",
|
||||||
|
"4.2.0",
|
||||||
|
"4.0.2",
|
||||||
|
"3.11.1",
|
||||||
|
"3.10.2",
|
||||||
|
"3.10.0",
|
||||||
|
"3.9.3",
|
||||||
|
"3.9.1",
|
||||||
|
"3.8.1",
|
||||||
|
"3.7.4",
|
||||||
|
"3.7.2",
|
||||||
|
"3.7.0",
|
||||||
|
"3.6.1",
|
||||||
|
"3.5.0",
|
||||||
|
"3.4.0",
|
||||||
|
"3.3.0",
|
||||||
|
"3.2.0",
|
||||||
|
"3.1.1",
|
||||||
|
"3.1.0",
|
||||||
|
"3.0.2",
|
||||||
|
"3.0.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "weblate",
|
||||||
|
"image": "weblate/weblate",
|
||||||
|
"tags": [
|
||||||
|
"latest",
|
||||||
|
"edge-2023-01-13-e824b551f23c3679467e38b06366744a06aa3b0c",
|
||||||
|
"edge-2023-01-13-468b996565e6b62edb78d40b515c476e0d860273",
|
||||||
|
"edge-2023-01-12-fe3d58b14f119eb5501220e9f096949c2e1ec2d3",
|
||||||
|
"edge-2023-01-12-112f75f9ee9e118ad493215f89742e6e091be8d0",
|
||||||
|
"edge-2023-01-11-f7bb190993e329d1529694e8cc7f5e0a80ccd615",
|
||||||
|
"edge-2023-01-11-e8ef3183aa7723f32c2b60c7c3b89910f2c7c593",
|
||||||
|
"edge-2023-01-11-155231f6cde18a65e3f35093d66dd0ce93aa7154",
|
||||||
|
"edge-2023-01-10-e47516e4022f87c019e61998b556b69111187aa9",
|
||||||
|
"edge-2023-01-10-98c6b38c746165adb27b2a8e93a74fa9ab64f17c",
|
||||||
|
"edge-2023-01-10-1df5c9dd96a6d8650f6881942fecbe33e1884295",
|
||||||
|
"edge-2023-01-09-7029b7b6c630be7cdac07d1629573dd2b81bc05f",
|
||||||
|
"edge-2023-01-09-4b05a878aa25b2c544a4e77027769b5934ec561f",
|
||||||
|
"edge-2023-01-07-df50259ff209720b0fd3c983bd7a5d01b564149c",
|
||||||
|
"edge-2023-01-07-719b3034de0ed369cd63554ea652f11142b9a540",
|
||||||
|
"edge-2023-01-07-14d4aec6cd8e1e6e0b7c1dc3a9cf4a74ccfc5e37",
|
||||||
|
"edge-2023-01-07-00f2d980c4388d799ba0df3a27c10bf941b7edd3",
|
||||||
|
"edge-2023-01-04-e92fe933b22e36e92f6de57a0ecb16752852d815",
|
||||||
|
"edge-2023-01-04-4c413c6ba33c39a6b7f3238f255bc09c69e96345",
|
||||||
|
"edge-2023-01-02-fd57652cce9dd2a12c7ff1dc644f5a969e57ea76",
|
||||||
|
"edge-2023-01-02-738d270784bca0cfaaef59e6cfb8c307f68a9939",
|
||||||
|
"edge-2023-01-02-3f6a7a183bbda4dc2e8d7e468370adc6f9e50527",
|
||||||
|
"edge-2023-01-01-5580a7a4755bf71403c85999fb4dff2078293403",
|
||||||
|
"edge-2022-12-29-8f422f23930880809a26d1207f9e43315ef54ad2",
|
||||||
|
"edge-2022-12-28-375fd99195711641246ccd09e3ce394d6928eaef",
|
||||||
|
"edge-2022-12-25-358c8ce6071f2781ae027bbc3a46d906ce08d9e7",
|
||||||
|
"edge-2022-12-24-3e1503494ce06ad6ff32f02db1a7d59224e5c860",
|
||||||
|
"edge-2022-12-21-cac4b09f943fe97700e3a33b7caf23277d2fcc11",
|
||||||
|
"edge-2022-12-21-3a8dd1bf66a7295f3512346bc1c97d55c5649dcf",
|
||||||
|
"edge-2022-12-16-e93caa3b014543b716b946f2c7fbf4a8f9be6099"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "whoogle",
|
||||||
|
"image": "benbusby/whoogle-search",
|
||||||
|
"tags": [
|
||||||
|
"0.8.0",
|
||||||
|
"0.7.3",
|
||||||
|
"0.7.1",
|
||||||
|
"0.6.0",
|
||||||
|
"0.5.3",
|
||||||
|
"0.5.1",
|
||||||
|
"0.4.1",
|
||||||
|
"0.3.2",
|
||||||
|
"v0.3.0",
|
||||||
|
"0.1.2",
|
||||||
|
"0.1.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wordpress-only",
|
||||||
|
"image": "library/wordpress",
|
||||||
|
"tags": [
|
||||||
|
"php8.2-fpm-alpine",
|
||||||
|
"php8.2-fpm",
|
||||||
|
"php8.2-apache",
|
||||||
|
"php8.2",
|
||||||
|
"php8.1-fpm-alpine",
|
||||||
|
"php8.1-fpm",
|
||||||
|
"php8.1-apache",
|
||||||
|
"php8.1",
|
||||||
|
"php8.0-fpm-alpine",
|
||||||
|
"php8.0-fpm",
|
||||||
|
"php8.0-apache",
|
||||||
|
"php8.0",
|
||||||
|
"php7.4-fpm-alpine",
|
||||||
|
"php7.4-fpm",
|
||||||
|
"php7.4-apache",
|
||||||
|
"php7.4",
|
||||||
|
"php7.3-fpm-alpine",
|
||||||
|
"php7.3-fpm",
|
||||||
|
"php7.3-apache",
|
||||||
|
"php7.3",
|
||||||
|
"php7.2-fpm-alpine",
|
||||||
|
"php7.2-fpm",
|
||||||
|
"php7.2-apache",
|
||||||
|
"php7.2",
|
||||||
|
"php7.1-fpm-alpine",
|
||||||
|
"php7.1-fpm",
|
||||||
|
"php7.1-apache",
|
||||||
|
"php7.1",
|
||||||
|
"php7.0-fpm-alpine",
|
||||||
|
"php7.0-fpm"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wordpress",
|
||||||
|
"image": "library/wordpress",
|
||||||
|
"tags": [
|
||||||
|
"php8.2-fpm-alpine",
|
||||||
|
"php8.2-fpm",
|
||||||
|
"php8.2-apache",
|
||||||
|
"php8.2",
|
||||||
|
"php8.1-fpm-alpine",
|
||||||
|
"php8.1-fpm",
|
||||||
|
"php8.1-apache",
|
||||||
|
"php8.1",
|
||||||
|
"php8.0-fpm-alpine",
|
||||||
|
"php8.0-fpm",
|
||||||
|
"php8.0-apache",
|
||||||
|
"php8.0",
|
||||||
|
"php7.4-fpm-alpine",
|
||||||
|
"php7.4-fpm",
|
||||||
|
"php7.4-apache",
|
||||||
|
"php7.4",
|
||||||
|
"php7.3-fpm-alpine",
|
||||||
|
"php7.3-fpm",
|
||||||
|
"php7.3-apache",
|
||||||
|
"php7.3",
|
||||||
|
"php7.2-fpm-alpine",
|
||||||
|
"php7.2-fpm",
|
||||||
|
"php7.2-apache",
|
||||||
|
"php7.2",
|
||||||
|
"php7.1-fpm-alpine",
|
||||||
|
"php7.1-fpm",
|
||||||
|
"php7.1-apache",
|
||||||
|
"php7.1",
|
||||||
|
"php7.0-fpm-alpine",
|
||||||
|
"php7.0-fpm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
3581
apps/api/devTemplates.yaml
Normal file
3581
apps/api/devTemplates.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,11 @@
|
|||||||
{
|
{
|
||||||
"watch": ["src"],
|
"watch": [
|
||||||
"ignore": ["src/**/*.test.ts"],
|
"src"
|
||||||
"ext": "ts,mjs,json,graphql",
|
],
|
||||||
"exec": "rimraf build && esbuild `find src \\( -name '*.ts' \\)` --minify=true --platform=node --outdir=build --format=cjs && node build",
|
"ignore": [
|
||||||
"legacyWatch": true
|
"src/**/*.test.ts"
|
||||||
}
|
],
|
||||||
|
"ext": "ts,mjs,json,graphql",
|
||||||
|
"exec": "rimraf build && esbuild `find src \\( -name '*.ts' \\)` --platform=node --outdir=build --format=cjs && node build",
|
||||||
|
"legacyWatch": true
|
||||||
|
}
|
||||||
@@ -1,73 +1,85 @@
|
|||||||
{
|
{
|
||||||
"name": "api",
|
"name": "api",
|
||||||
"description": "Coolify's Fastify API",
|
"description": "Coolify's Fastify API",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"db:push": "prisma db push && prisma generate",
|
"db:generate": "prisma generate",
|
||||||
"db:seed": "prisma db seed",
|
"db:push": "prisma db push && prisma generate",
|
||||||
"db:studio": "prisma studio",
|
"db:seed": "prisma db seed",
|
||||||
"db:migrate": "COOLIFY_DATABASE_URL=file:../db/migration.db prisma migrate dev --skip-seed --name",
|
"db:studio": "prisma studio",
|
||||||
"dev": "nodemon",
|
"db:migrate": "COOLIFY_DATABASE_URL=file:../db/migration.db prisma migrate dev --skip-seed --name",
|
||||||
"build": "rimraf build && esbuild `find src \\( -name '*.ts' \\)| grep -v client/` --platform=node --outdir=build --format=cjs",
|
"dev": "nodemon",
|
||||||
"format": "prettier --write 'src/**/*.{js,ts,json,md}'",
|
"build": "rimraf build && esbuild `find src \\( -name '*.ts' \\)| grep -v client/` --platform=node --outdir=build --format=cjs",
|
||||||
"lint": "prettier --check 'src/**/*.{js,ts,json,md}' && eslint --ignore-path .eslintignore .",
|
"format": "prettier --write 'src/**/*.{js,ts,json,md}'",
|
||||||
"start": "NODE_ENV=production npx -y prisma migrate deploy && npx prisma generate && npx prisma db seed && node index.js"
|
"lint": "prettier --check 'src/**/*.{js,ts,json,md}' && eslint --ignore-path .eslintignore .",
|
||||||
},
|
"start": "NODE_ENV=production pnpm prisma migrate deploy && pnpm prisma generate && pnpm prisma db seed && node index.js"
|
||||||
"dependencies": {
|
},
|
||||||
"@breejs/ts-worker": "2.0.0",
|
"dependencies": {
|
||||||
"@fastify/autoload": "5.2.0",
|
"@breejs/ts-worker": "2.0.0",
|
||||||
"@fastify/cookie": "8.1.0",
|
"@fastify/autoload": "5.7.0",
|
||||||
"@fastify/cors": "8.1.0",
|
"@fastify/cookie": "8.3.0",
|
||||||
"@fastify/env": "4.1.0",
|
"@fastify/cors": "8.2.0",
|
||||||
"@fastify/jwt": "6.3.2",
|
"@fastify/env": "4.2.0",
|
||||||
"@fastify/static": "6.5.0",
|
"@fastify/jwt": "6.5.0",
|
||||||
"@iarna/toml": "2.2.5",
|
"@fastify/multipart": "7.3.0",
|
||||||
"@ladjs/graceful": "3.0.2",
|
"@fastify/static": "6.6.0",
|
||||||
"@prisma/client": "3.15.2",
|
"@iarna/toml": "2.2.5",
|
||||||
"axios": "0.27.2",
|
"@ladjs/graceful": "3.2.1",
|
||||||
"bcryptjs": "2.4.3",
|
"@prisma/client": "4.8.1",
|
||||||
"bree": "9.1.2",
|
"@sentry/node": "7.30.0",
|
||||||
"cabin": "9.1.2",
|
"@sentry/tracing": "7.30.0",
|
||||||
"compare-versions": "5.0.1",
|
"axe": "11.2.1",
|
||||||
"cuid": "2.1.8",
|
"bcryptjs": "2.4.3",
|
||||||
"dayjs": "1.11.5",
|
"bree": "9.1.3",
|
||||||
"dockerode": "3.3.4",
|
"cabin": "11.1.1",
|
||||||
"dotenv-extended": "2.9.0",
|
"compare-versions": "5.0.1",
|
||||||
"execa": "6.1.0",
|
"csv-parse": "5.3.3",
|
||||||
"fastify": "4.5.3",
|
"csvtojson": "2.0.10",
|
||||||
"fastify-plugin": "4.2.1",
|
"cuid": "2.1.8",
|
||||||
"generate-password": "1.7.0",
|
"dayjs": "1.11.7",
|
||||||
"got": "12.3.1",
|
"dockerode": "3.3.4",
|
||||||
"is-ip": "5.0.0",
|
"dotenv-extended": "2.9.0",
|
||||||
"is-port-reachable": "4.0.0",
|
"execa": "6.1.0",
|
||||||
"js-yaml": "4.1.0",
|
"fastify": "4.11.0",
|
||||||
"jsonwebtoken": "8.5.1",
|
"fastify-plugin": "4.3.0",
|
||||||
"node-forge": "1.3.1",
|
"fastify-socket.io": "4.0.0",
|
||||||
"node-os-utils": "1.3.7",
|
"generate-password": "1.7.0",
|
||||||
"p-all": "4.0.0",
|
"got": "12.5.3",
|
||||||
"p-throttle": "5.0.0",
|
"is-ip": "5.0.0",
|
||||||
"public-ip": "6.0.1",
|
"is-port-reachable": "4.0.0",
|
||||||
"ssh-config": "4.1.6",
|
"js-yaml": "4.1.0",
|
||||||
"strip-ansi": "7.0.1",
|
"jsonwebtoken": "9.0.0",
|
||||||
"unique-names-generator": "4.7.1"
|
"minimist": "^1.2.7",
|
||||||
},
|
"node-forge": "1.3.1",
|
||||||
"devDependencies": {
|
"node-os-utils": "1.3.7",
|
||||||
"@types/node": "18.7.13",
|
"p-all": "4.0.0",
|
||||||
"@types/node-os-utils": "1.3.0",
|
"p-throttle": "5.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "5.35.1",
|
"prisma": "4.8.1",
|
||||||
"@typescript-eslint/parser": "5.35.1",
|
"public-ip": "6.0.1",
|
||||||
"esbuild": "0.15.5",
|
"pump": "3.0.0",
|
||||||
"eslint": "8.23.0",
|
"shell-quote": "^1.7.4",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"socket.io": "4.5.4",
|
||||||
"eslint-plugin-prettier": "4.2.1",
|
"ssh-config": "4.2.0",
|
||||||
"nodemon": "2.0.19",
|
"strip-ansi": "7.0.1",
|
||||||
"prettier": "2.7.1",
|
"unique-names-generator": "4.7.1"
|
||||||
"prisma": "3.15.2",
|
},
|
||||||
"rimraf": "3.0.2",
|
"devDependencies": {
|
||||||
"tsconfig-paths": "4.1.0",
|
"@types/node": "18.11.18",
|
||||||
"typescript": "4.7.4"
|
"@types/node-os-utils": "1.3.0",
|
||||||
},
|
"@typescript-eslint/eslint-plugin": "5.48.1",
|
||||||
"prisma": {
|
"@typescript-eslint/parser": "5.48.1",
|
||||||
"seed": "node prisma/seed.js"
|
"esbuild": "0.16.16",
|
||||||
}
|
"eslint": "8.31.0",
|
||||||
}
|
"eslint-config-prettier": "8.6.0",
|
||||||
|
"eslint-plugin-prettier": "4.2.1",
|
||||||
|
"nodemon": "2.0.20",
|
||||||
|
"prettier": "2.8.2",
|
||||||
|
"rimraf": "3.0.2",
|
||||||
|
"tsconfig-paths": "4.1.2",
|
||||||
|
"types-fastify-socket.io": "0.0.1",
|
||||||
|
"typescript": "4.9.4"
|
||||||
|
},
|
||||||
|
"prisma": {
|
||||||
|
"seed": "node prisma/seed.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_ApplicationSettings" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"debug" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"previews" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"autodeploy" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isBot" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isPublicRepository" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDBBranching" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isPublicRepository", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isPublicRepository", "previews", "updatedAt" FROM "ApplicationSettings";
|
||||||
|
DROP TABLE "ApplicationSettings";
|
||||||
|
ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings";
|
||||||
|
CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to alter the column `time` on the `BuildLog` table. The data in that column could be lost. The data in that column will be cast from `Int` to `BigInt`.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_BuildLog" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT,
|
||||||
|
"buildId" TEXT NOT NULL,
|
||||||
|
"line" TEXT NOT NULL,
|
||||||
|
"time" BIGINT NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_BuildLog" ("applicationId", "buildId", "id", "line", "time") SELECT "applicationId", "buildId", "id", "line", "time" FROM "BuildLog";
|
||||||
|
DROP TABLE "BuildLog";
|
||||||
|
ALTER TABLE "new_BuildLog" RENAME TO "BuildLog";
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ApplicationConnectedDatabase" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
"databaseId" TEXT,
|
||||||
|
"hostedDatabaseType" TEXT,
|
||||||
|
"hostedDatabaseHost" TEXT,
|
||||||
|
"hostedDatabasePort" INTEGER,
|
||||||
|
"hostedDatabaseName" TEXT,
|
||||||
|
"hostedDatabaseUser" TEXT,
|
||||||
|
"hostedDatabasePassword" TEXT,
|
||||||
|
"hostedDatabaseDBName" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ApplicationConnectedDatabase_databaseId_fkey" FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "ApplicationConnectedDatabase_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ApplicationConnectedDatabase_applicationId_key" ON "ApplicationConnectedDatabase"("applicationId");
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Setting" ADD COLUMN "isAPIDebuggingEnabled" BOOLEAN DEFAULT false;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "DatabaseSecret" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"value" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"databaseId" TEXT NOT NULL,
|
||||||
|
CONSTRAINT "DatabaseSecret_databaseId_fkey" FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "DatabaseSecret_name_databaseId_key" ON "DatabaseSecret"("name", "databaseId");
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Build" ADD COLUMN "previewApplicationId" TEXT;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "PreviewApplication" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"pullmergeRequestId" TEXT NOT NULL,
|
||||||
|
"sourceBranch" TEXT NOT NULL,
|
||||||
|
"isRandomDomain" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"customDomain" TEXT,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "PreviewApplication_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "PreviewApplication_applicationId_key" ON "PreviewApplication"("applicationId");
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Certificate" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"key" TEXT NOT NULL,
|
||||||
|
"cert" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"teamId" TEXT,
|
||||||
|
CONSTRAINT "Certificate_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_ApplicationSettings" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"debug" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"previews" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"autodeploy" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isBot" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isPublicRepository" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDBBranching" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isCustomSSL" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isDBBranching", "isPublicRepository", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isDBBranching", "isPublicRepository", "previews", "updatedAt" FROM "ApplicationSettings";
|
||||||
|
DROP TABLE "ApplicationSettings";
|
||||||
|
ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings";
|
||||||
|
CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_GitSource" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"forPublic" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"type" TEXT,
|
||||||
|
"apiUrl" TEXT,
|
||||||
|
"htmlUrl" TEXT,
|
||||||
|
"customPort" INTEGER NOT NULL DEFAULT 22,
|
||||||
|
"organization" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"githubAppId" TEXT,
|
||||||
|
"gitlabAppId" TEXT,
|
||||||
|
"isSystemWide" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt" FROM "GitSource";
|
||||||
|
DROP TABLE "GitSource";
|
||||||
|
ALTER TABLE "new_GitSource" RENAME TO "GitSource";
|
||||||
|
CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId");
|
||||||
|
CREATE UNIQUE INDEX "GitSource_gitlabAppId_key" ON "GitSource"("gitlabAppId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "PreviewApplication_applicationId_key";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Build" ADD COLUMN "sourceRepository" TEXT;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Application" ADD COLUMN "dockerComposeFile" TEXT;
|
||||||
|
ALTER TABLE "Application" ADD COLUMN "dockerComposeFileLocation" TEXT;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Application" ADD COLUMN "dockerComposeConfiguration" TEXT;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ServiceSetting" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"serviceId" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"value" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ServiceSetting_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ServiceSetting_serviceId_name_key" ON "ServiceSetting"("serviceId", "name");
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_ServicePersistentStorage" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"serviceId" TEXT NOT NULL,
|
||||||
|
"path" TEXT NOT NULL,
|
||||||
|
"volumeName" TEXT,
|
||||||
|
"predefined" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"containerId" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ServicePersistentStorage_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_ServicePersistentStorage" ("createdAt", "id", "path", "serviceId", "updatedAt") SELECT "createdAt", "id", "path", "serviceId", "updatedAt" FROM "ServicePersistentStorage";
|
||||||
|
DROP TABLE "ServicePersistentStorage";
|
||||||
|
ALTER TABLE "new_ServicePersistentStorage" RENAME TO "ServicePersistentStorage";
|
||||||
|
CREATE UNIQUE INDEX "ServicePersistentStorage_serviceId_path_key" ON "ServicePersistentStorage"("serviceId", "path");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `variableName` to the `ServiceSetting` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_ServiceSetting" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"serviceId" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"value" TEXT NOT NULL,
|
||||||
|
"variableName" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ServiceSetting_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_ServiceSetting" ("createdAt", "id", "name", "serviceId", "updatedAt", "value") SELECT "createdAt", "id", "name", "serviceId", "updatedAt", "value" FROM "ServiceSetting";
|
||||||
|
DROP TABLE "ServiceSetting";
|
||||||
|
ALTER TABLE "new_ServiceSetting" RENAME TO "ServiceSetting";
|
||||||
|
CREATE UNIQUE INDEX "ServiceSetting_serviceId_name_key" ON "ServiceSetting"("serviceId", "name");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Service" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"fqdn" TEXT,
|
||||||
|
"exposePort" INTEGER,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"type" TEXT,
|
||||||
|
"version" TEXT,
|
||||||
|
"templateVersion" TEXT NOT NULL DEFAULT '0.0.0',
|
||||||
|
"destinationDockerId" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "Service_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Service" ("createdAt", "destinationDockerId", "dualCerts", "exposePort", "fqdn", "id", "name", "type", "updatedAt", "version") SELECT "createdAt", "destinationDockerId", "dualCerts", "exposePort", "fqdn", "id", "name", "type", "updatedAt", "version" FROM "Service";
|
||||||
|
DROP TABLE "Service";
|
||||||
|
ALTER TABLE "new_Service" RENAME TO "Service";
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- A unique constraint covering the columns `[serviceId,containerId,path]` on the table `ServicePersistentStorage` will be added. If there are existing duplicate values, this will fail.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "ServicePersistentStorage_serviceId_path_key";
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ServicePersistentStorage_serviceId_containerId_path_key" ON "ServicePersistentStorage"("serviceId", "containerId", "path");
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Wordpress" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"extraConfig" TEXT,
|
||||||
|
"tablePrefix" TEXT,
|
||||||
|
"ownMysql" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"mysqlHost" TEXT,
|
||||||
|
"mysqlPort" INTEGER,
|
||||||
|
"mysqlUser" TEXT,
|
||||||
|
"mysqlPassword" TEXT,
|
||||||
|
"mysqlRootUser" TEXT,
|
||||||
|
"mysqlRootUserPassword" TEXT,
|
||||||
|
"mysqlDatabase" TEXT,
|
||||||
|
"mysqlPublicPort" INTEGER,
|
||||||
|
"ftpEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"ftpUser" TEXT,
|
||||||
|
"ftpPassword" TEXT,
|
||||||
|
"ftpPublicPort" INTEGER,
|
||||||
|
"ftpHostKey" TEXT,
|
||||||
|
"ftpHostKeyPrivate" TEXT,
|
||||||
|
"serviceId" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Wordpress" ("createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlHost", "mysqlPassword", "mysqlPort", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "ownMysql", "serviceId", "tablePrefix", "updatedAt") SELECT "createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlHost", "mysqlPassword", "mysqlPort", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "ownMysql", "serviceId", "tablePrefix", "updatedAt" FROM "Wordpress";
|
||||||
|
DROP TABLE "Wordpress";
|
||||||
|
ALTER TABLE "new_Wordpress" RENAME TO "Wordpress";
|
||||||
|
CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Setting" ADD COLUMN "proxyDefaultRedirect" TEXT;
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Setting" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"fqdn" TEXT,
|
||||||
|
"isAPIDebuggingEnabled" BOOLEAN DEFAULT false,
|
||||||
|
"isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"minPort" INTEGER NOT NULL DEFAULT 9000,
|
||||||
|
"maxPort" INTEGER NOT NULL DEFAULT 9100,
|
||||||
|
"proxyPassword" TEXT NOT NULL,
|
||||||
|
"proxyUser" TEXT NOT NULL,
|
||||||
|
"proxyHash" TEXT,
|
||||||
|
"proxyDefaultRedirect" TEXT,
|
||||||
|
"isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"DNSServers" TEXT,
|
||||||
|
"isTraefikUsed" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"ipv4" TEXT,
|
||||||
|
"ipv6" TEXT,
|
||||||
|
"arch" TEXT,
|
||||||
|
"concurrentBuilds" INTEGER NOT NULL DEFAULT 1,
|
||||||
|
"applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Setting" ("DNSServers", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "DNSServers", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting";
|
||||||
|
DROP TABLE "Setting";
|
||||||
|
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||||
|
CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
|
||||||
|
CREATE TABLE "new_ApplicationPersistentStorage" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
"path" TEXT NOT NULL,
|
||||||
|
"oldPath" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ApplicationPersistentStorage_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_ApplicationPersistentStorage" ("applicationId", "createdAt", "id", "path", "updatedAt") SELECT "applicationId", "createdAt", "id", "path", "updatedAt" FROM "ApplicationPersistentStorage";
|
||||||
|
DROP TABLE "ApplicationPersistentStorage";
|
||||||
|
ALTER TABLE "new_ApplicationPersistentStorage" RENAME TO "ApplicationPersistentStorage";
|
||||||
|
CREATE UNIQUE INDEX "ApplicationPersistentStorage_applicationId_path_key" ON "ApplicationPersistentStorage"("applicationId", "path");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `proxyHash` on the `Setting` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `proxyPassword` on the `Setting` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `proxyUser` on the `Setting` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Setting" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"fqdn" TEXT,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"minPort" INTEGER NOT NULL DEFAULT 9000,
|
||||||
|
"maxPort" INTEGER NOT NULL DEFAULT 9100,
|
||||||
|
"DNSServers" TEXT,
|
||||||
|
"ipv4" TEXT,
|
||||||
|
"ipv6" TEXT,
|
||||||
|
"arch" TEXT,
|
||||||
|
"concurrentBuilds" INTEGER NOT NULL DEFAULT 1,
|
||||||
|
"applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"proxyDefaultRedirect" TEXT,
|
||||||
|
"isAPIDebuggingEnabled" BOOLEAN DEFAULT false,
|
||||||
|
"isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isTraefikUsed" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt" FROM "Setting";
|
||||||
|
DROP TABLE "Setting";
|
||||||
|
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||||
|
CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "DockerRegistry" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"url" TEXT NOT NULL,
|
||||||
|
"username" TEXT,
|
||||||
|
"password" TEXT,
|
||||||
|
"isSystemWide" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"teamId" TEXT,
|
||||||
|
CONSTRAINT "DockerRegistry_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Application" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"fqdn" TEXT,
|
||||||
|
"repository" TEXT,
|
||||||
|
"configHash" TEXT,
|
||||||
|
"branch" TEXT,
|
||||||
|
"buildPack" TEXT,
|
||||||
|
"projectId" INTEGER,
|
||||||
|
"port" INTEGER,
|
||||||
|
"exposePort" INTEGER,
|
||||||
|
"installCommand" TEXT,
|
||||||
|
"buildCommand" TEXT,
|
||||||
|
"startCommand" TEXT,
|
||||||
|
"baseDirectory" TEXT,
|
||||||
|
"publishDirectory" TEXT,
|
||||||
|
"deploymentType" TEXT,
|
||||||
|
"phpModules" TEXT,
|
||||||
|
"pythonWSGI" TEXT,
|
||||||
|
"pythonModule" TEXT,
|
||||||
|
"pythonVariable" TEXT,
|
||||||
|
"dockerFileLocation" TEXT,
|
||||||
|
"denoMainFile" TEXT,
|
||||||
|
"denoOptions" TEXT,
|
||||||
|
"dockerComposeFile" TEXT,
|
||||||
|
"dockerComposeFileLocation" TEXT,
|
||||||
|
"dockerComposeConfiguration" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"destinationDockerId" TEXT,
|
||||||
|
"gitSourceId" TEXT,
|
||||||
|
"baseImage" TEXT,
|
||||||
|
"baseBuildImage" TEXT,
|
||||||
|
"dockerRegistryId" TEXT NOT NULL DEFAULT '0',
|
||||||
|
CONSTRAINT "Application_gitSourceId_fkey" FOREIGN KEY ("gitSourceId") REFERENCES "GitSource" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "Application_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "Application_dockerRegistryId_fkey" FOREIGN KEY ("dockerRegistryId") REFERENCES "DockerRegistry" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Application" ("baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "exposePort", "fqdn", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt") SELECT "baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "exposePort", "fqdn", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt" FROM "Application";
|
||||||
|
DROP TABLE "Application";
|
||||||
|
ALTER TABLE "new_Application" RENAME TO "Application";
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Setting" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"fqdn" TEXT,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"minPort" INTEGER NOT NULL DEFAULT 9000,
|
||||||
|
"maxPort" INTEGER NOT NULL DEFAULT 9100,
|
||||||
|
"DNSServers" TEXT,
|
||||||
|
"ipv4" TEXT,
|
||||||
|
"ipv6" TEXT,
|
||||||
|
"arch" TEXT,
|
||||||
|
"concurrentBuilds" INTEGER NOT NULL DEFAULT 1,
|
||||||
|
"applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"proxyDefaultRedirect" TEXT,
|
||||||
|
"doNotTrack" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isAPIDebuggingEnabled" BOOLEAN DEFAULT false,
|
||||||
|
"isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isTraefikUsed" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt" FROM "Setting";
|
||||||
|
DROP TABLE "Setting";
|
||||||
|
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||||
|
CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Setting" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"fqdn" TEXT,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"minPort" INTEGER NOT NULL DEFAULT 9000,
|
||||||
|
"maxPort" INTEGER NOT NULL DEFAULT 9100,
|
||||||
|
"DNSServers" TEXT,
|
||||||
|
"ipv4" TEXT,
|
||||||
|
"ipv6" TEXT,
|
||||||
|
"arch" TEXT,
|
||||||
|
"concurrentBuilds" INTEGER NOT NULL DEFAULT 1,
|
||||||
|
"applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"proxyDefaultRedirect" TEXT,
|
||||||
|
"doNotTrack" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isAPIDebuggingEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isTraefikUsed" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", coalesce("isAPIDebuggingEnabled", false) AS "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt" FROM "Setting";
|
||||||
|
DROP TABLE "Setting";
|
||||||
|
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||||
|
CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
|
||||||
|
CREATE TABLE "new_GlitchTip" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"postgresqlUser" TEXT NOT NULL,
|
||||||
|
"postgresqlPassword" TEXT NOT NULL,
|
||||||
|
"postgresqlDatabase" TEXT NOT NULL,
|
||||||
|
"postgresqlPublicPort" INTEGER,
|
||||||
|
"secretKeyBase" TEXT,
|
||||||
|
"defaultEmail" TEXT NOT NULL,
|
||||||
|
"defaultUsername" TEXT NOT NULL,
|
||||||
|
"defaultPassword" TEXT NOT NULL,
|
||||||
|
"defaultEmailFrom" TEXT NOT NULL DEFAULT 'glitchtip@domain.tdl',
|
||||||
|
"emailSmtpHost" TEXT DEFAULT 'domain.tdl',
|
||||||
|
"emailSmtpPort" INTEGER DEFAULT 25,
|
||||||
|
"emailSmtpUser" TEXT,
|
||||||
|
"emailSmtpPassword" TEXT,
|
||||||
|
"emailSmtpUseTls" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"emailSmtpUseSsl" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"emailBackend" TEXT,
|
||||||
|
"mailgunApiKey" TEXT,
|
||||||
|
"sendgridApiKey" TEXT,
|
||||||
|
"enableOpenUserRegistration" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"serviceId" TEXT NOT NULL,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "GlitchTip_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_GlitchTip" ("createdAt", "defaultEmail", "defaultEmailFrom", "defaultPassword", "defaultUsername", "emailBackend", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", "emailSmtpUseSsl", "emailSmtpUseTls", "emailSmtpUser", "enableOpenUserRegistration", "id", "mailgunApiKey", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "sendgridApiKey", "serviceId", "updatedAt") SELECT "createdAt", "defaultEmail", "defaultEmailFrom", "defaultPassword", "defaultUsername", "emailBackend", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", coalesce("emailSmtpUseSsl", false) AS "emailSmtpUseSsl", coalesce("emailSmtpUseTls", false) AS "emailSmtpUseTls", "emailSmtpUser", "enableOpenUserRegistration", "id", "mailgunApiKey", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "sendgridApiKey", "serviceId", "updatedAt" FROM "GlitchTip";
|
||||||
|
DROP TABLE "GlitchTip";
|
||||||
|
ALTER TABLE "new_GlitchTip" RENAME TO "GlitchTip";
|
||||||
|
CREATE UNIQUE INDEX "GlitchTip_serviceId_key" ON "GlitchTip"("serviceId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Setting" ADD COLUMN "sentryDSN" TEXT;
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Setting" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"fqdn" TEXT,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"minPort" INTEGER NOT NULL DEFAULT 9000,
|
||||||
|
"maxPort" INTEGER NOT NULL DEFAULT 9100,
|
||||||
|
"DNSServers" TEXT NOT NULL DEFAULT '1.1.1.1,8.8.8.8',
|
||||||
|
"ipv4" TEXT,
|
||||||
|
"ipv6" TEXT,
|
||||||
|
"arch" TEXT,
|
||||||
|
"concurrentBuilds" INTEGER NOT NULL DEFAULT 1,
|
||||||
|
"applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"proxyDefaultRedirect" TEXT,
|
||||||
|
"doNotTrack" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"sentryDSN" TEXT,
|
||||||
|
"isAPIDebuggingEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isTraefikUsed" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "sentryDSN", "updatedAt") SELECT coalesce("DNSServers", '1.1.1.1,8.8.8.8') AS "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "sentryDSN", "updatedAt" FROM "Setting";
|
||||||
|
DROP TABLE "Setting";
|
||||||
|
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||||
|
CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Setting" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"fqdn" TEXT,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"minPort" INTEGER NOT NULL DEFAULT 9000,
|
||||||
|
"maxPort" INTEGER NOT NULL DEFAULT 9100,
|
||||||
|
"DNSServers" TEXT NOT NULL DEFAULT '1.1.1.1,8.8.8.8',
|
||||||
|
"ipv4" TEXT,
|
||||||
|
"ipv6" TEXT,
|
||||||
|
"arch" TEXT,
|
||||||
|
"concurrentBuilds" INTEGER NOT NULL DEFAULT 1,
|
||||||
|
"applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"numberOfDockerImagesKeptLocally" INTEGER NOT NULL DEFAULT 3,
|
||||||
|
"proxyDefaultRedirect" TEXT,
|
||||||
|
"doNotTrack" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"sentryDSN" TEXT,
|
||||||
|
"previewSeparator" TEXT NOT NULL DEFAULT '.',
|
||||||
|
"isAPIDebuggingEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isTraefikUsed" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "numberOfDockerImagesKeptLocally", "proxyDefaultRedirect", "sentryDSN", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", 3, "proxyDefaultRedirect", "sentryDSN", "updatedAt" FROM "Setting";
|
||||||
|
DROP TABLE "Setting";
|
||||||
|
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||||
|
CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Application" ADD COLUMN "gitCommitHash" TEXT;
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `isSystemWide` on the `DockerRegistry` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_DockerRegistry" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"url" TEXT NOT NULL,
|
||||||
|
"username" TEXT,
|
||||||
|
"password" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"teamId" TEXT,
|
||||||
|
CONSTRAINT "DockerRegistry_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_DockerRegistry" ("createdAt", "id", "name", "password", "teamId", "updatedAt", "url", "username") SELECT "createdAt", "id", "name", "password", "teamId", "updatedAt", "url", "username" FROM "DockerRegistry";
|
||||||
|
DROP TABLE "DockerRegistry";
|
||||||
|
ALTER TABLE "new_DockerRegistry" RENAME TO "DockerRegistry";
|
||||||
|
CREATE TABLE "new_Application" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"fqdn" TEXT,
|
||||||
|
"repository" TEXT,
|
||||||
|
"configHash" TEXT,
|
||||||
|
"branch" TEXT,
|
||||||
|
"buildPack" TEXT,
|
||||||
|
"projectId" INTEGER,
|
||||||
|
"port" INTEGER,
|
||||||
|
"exposePort" INTEGER,
|
||||||
|
"installCommand" TEXT,
|
||||||
|
"buildCommand" TEXT,
|
||||||
|
"startCommand" TEXT,
|
||||||
|
"baseDirectory" TEXT,
|
||||||
|
"publishDirectory" TEXT,
|
||||||
|
"deploymentType" TEXT,
|
||||||
|
"phpModules" TEXT,
|
||||||
|
"pythonWSGI" TEXT,
|
||||||
|
"pythonModule" TEXT,
|
||||||
|
"pythonVariable" TEXT,
|
||||||
|
"dockerFileLocation" TEXT,
|
||||||
|
"denoMainFile" TEXT,
|
||||||
|
"denoOptions" TEXT,
|
||||||
|
"dockerComposeFile" TEXT,
|
||||||
|
"dockerComposeFileLocation" TEXT,
|
||||||
|
"dockerComposeConfiguration" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"destinationDockerId" TEXT,
|
||||||
|
"gitSourceId" TEXT,
|
||||||
|
"gitCommitHash" TEXT,
|
||||||
|
"baseImage" TEXT,
|
||||||
|
"baseBuildImage" TEXT,
|
||||||
|
"dockerRegistryId" TEXT,
|
||||||
|
CONSTRAINT "Application_gitSourceId_fkey" FOREIGN KEY ("gitSourceId") REFERENCES "GitSource" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "Application_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "Application_dockerRegistryId_fkey" FOREIGN KEY ("dockerRegistryId") REFERENCES "DockerRegistry" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Application" ("baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "dockerRegistryId", "exposePort", "fqdn", "gitCommitHash", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt") SELECT "baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "dockerRegistryId", "exposePort", "fqdn", "gitCommitHash", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt" FROM "Application";
|
||||||
|
DROP TABLE "Application";
|
||||||
|
ALTER TABLE "new_Application" RENAME TO "Application";
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Application" ADD COLUMN "simpleDockerfile" TEXT;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Application" ADD COLUMN "dockerRegistryImageName" TEXT;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_GitSource" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"forPublic" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"type" TEXT,
|
||||||
|
"apiUrl" TEXT,
|
||||||
|
"htmlUrl" TEXT,
|
||||||
|
"customPort" INTEGER NOT NULL DEFAULT 22,
|
||||||
|
"customUser" TEXT NOT NULL DEFAULT 'git',
|
||||||
|
"organization" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"githubAppId" TEXT,
|
||||||
|
"gitlabAppId" TEXT,
|
||||||
|
"isSystemWide" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "isSystemWide", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "isSystemWide", "name", "organization", "type", "updatedAt" FROM "GitSource";
|
||||||
|
DROP TABLE "GitSource";
|
||||||
|
ALTER TABLE "new_GitSource" RENAME TO "GitSource";
|
||||||
|
CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId");
|
||||||
|
CREATE UNIQUE INDEX "GitSource_gitlabAppId_key" ON "GitSource"("gitlabAppId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_ApplicationSettings" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"debug" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"previews" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"autodeploy" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isBot" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isPublicRepository" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDBBranching" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isCustomSSL" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isHttp2" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isCustomSSL", "isDBBranching", "isPublicRepository", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isCustomSSL", "isDBBranching", "isPublicRepository", "previews", "updatedAt" FROM "ApplicationSettings";
|
||||||
|
DROP TABLE "ApplicationSettings";
|
||||||
|
ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings";
|
||||||
|
CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -8,26 +8,40 @@ datasource db {
|
|||||||
url = env("COOLIFY_DATABASE_URL")
|
url = env("COOLIFY_DATABASE_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Certificate {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
key String
|
||||||
|
cert String
|
||||||
|
team Team? @relation(fields: [teamId], references: [id])
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
teamId String?
|
||||||
|
}
|
||||||
|
|
||||||
model Setting {
|
model Setting {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
fqdn String? @unique
|
fqdn String? @unique
|
||||||
isRegistrationEnabled Boolean @default(false)
|
dualCerts Boolean @default(false)
|
||||||
dualCerts Boolean @default(false)
|
minPort Int @default(9000)
|
||||||
minPort Int @default(9000)
|
maxPort Int @default(9100)
|
||||||
maxPort Int @default(9100)
|
DNSServers String @default("1.1.1.1,8.8.8.8")
|
||||||
proxyPassword String
|
ipv4 String?
|
||||||
proxyUser String
|
ipv6 String?
|
||||||
proxyHash String?
|
arch String?
|
||||||
isAutoUpdateEnabled Boolean @default(false)
|
concurrentBuilds Int @default(1)
|
||||||
isDNSCheckEnabled Boolean @default(true)
|
applicationStoragePathMigrationFinished Boolean @default(false)
|
||||||
DNSServers String?
|
numberOfDockerImagesKeptLocally Int @default(3)
|
||||||
isTraefikUsed Boolean @default(true)
|
proxyDefaultRedirect String?
|
||||||
createdAt DateTime @default(now())
|
doNotTrack Boolean @default(false)
|
||||||
updatedAt DateTime @updatedAt
|
sentryDSN String?
|
||||||
ipv4 String?
|
previewSeparator String @default(".")
|
||||||
ipv6 String?
|
isAPIDebuggingEnabled Boolean @default(false)
|
||||||
arch String?
|
isRegistrationEnabled Boolean @default(true)
|
||||||
concurrentBuilds Int @default(1)
|
isAutoUpdateEnabled Boolean @default(false)
|
||||||
|
isDNSCheckEnabled Boolean @default(true)
|
||||||
|
isTraefikUsed Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
@@ -69,6 +83,8 @@ model Team {
|
|||||||
gitLabApps GitlabApp[]
|
gitLabApps GitlabApp[]
|
||||||
service Service[]
|
service Service[]
|
||||||
users User[]
|
users User[]
|
||||||
|
certificate Certificate[]
|
||||||
|
dockerRegistry DockerRegistry[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model TeamInvitation {
|
model TeamInvitation {
|
||||||
@@ -82,41 +98,81 @@ model TeamInvitation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Application {
|
model Application {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
fqdn String?
|
fqdn String?
|
||||||
repository String?
|
repository String?
|
||||||
configHash String?
|
configHash String?
|
||||||
branch String?
|
branch String?
|
||||||
buildPack String?
|
buildPack String?
|
||||||
projectId Int?
|
projectId Int?
|
||||||
port Int?
|
port Int?
|
||||||
exposePort Int?
|
exposePort Int?
|
||||||
installCommand String?
|
installCommand String?
|
||||||
buildCommand String?
|
buildCommand String?
|
||||||
startCommand String?
|
startCommand String?
|
||||||
baseDirectory String?
|
baseDirectory String?
|
||||||
publishDirectory String?
|
publishDirectory String?
|
||||||
deploymentType String?
|
deploymentType String?
|
||||||
phpModules String?
|
phpModules String?
|
||||||
pythonWSGI String?
|
pythonWSGI String?
|
||||||
pythonModule String?
|
pythonModule String?
|
||||||
pythonVariable String?
|
pythonVariable String?
|
||||||
dockerFileLocation String?
|
dockerFileLocation String?
|
||||||
denoMainFile String?
|
denoMainFile String?
|
||||||
denoOptions String?
|
denoOptions String?
|
||||||
createdAt DateTime @default(now())
|
dockerComposeFile String?
|
||||||
updatedAt DateTime @updatedAt
|
dockerComposeFileLocation String?
|
||||||
destinationDockerId String?
|
dockerComposeConfiguration String?
|
||||||
gitSourceId String?
|
createdAt DateTime @default(now())
|
||||||
baseImage String?
|
updatedAt DateTime @updatedAt
|
||||||
baseBuildImage String?
|
destinationDockerId String?
|
||||||
gitSource GitSource? @relation(fields: [gitSourceId], references: [id])
|
gitSourceId String?
|
||||||
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
gitCommitHash String?
|
||||||
persistentStorage ApplicationPersistentStorage[]
|
baseImage String?
|
||||||
settings ApplicationSettings?
|
baseBuildImage String?
|
||||||
secrets Secret[]
|
settings ApplicationSettings?
|
||||||
teams Team[]
|
dockerRegistryId String?
|
||||||
|
dockerRegistryImageName String?
|
||||||
|
simpleDockerfile String?
|
||||||
|
|
||||||
|
persistentStorage ApplicationPersistentStorage[]
|
||||||
|
secrets Secret[]
|
||||||
|
teams Team[]
|
||||||
|
connectedDatabase ApplicationConnectedDatabase?
|
||||||
|
previewApplication PreviewApplication[]
|
||||||
|
gitSource GitSource? @relation(fields: [gitSourceId], references: [id])
|
||||||
|
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
||||||
|
dockerRegistry DockerRegistry? @relation(fields: [dockerRegistryId], references: [id])
|
||||||
|
}
|
||||||
|
|
||||||
|
model PreviewApplication {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
pullmergeRequestId String
|
||||||
|
sourceBranch String
|
||||||
|
isRandomDomain Boolean @default(false)
|
||||||
|
customDomain String?
|
||||||
|
applicationId String
|
||||||
|
application Application @relation(fields: [applicationId], references: [id])
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
model ApplicationConnectedDatabase {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
applicationId String @unique
|
||||||
|
databaseId String?
|
||||||
|
hostedDatabaseType String?
|
||||||
|
hostedDatabaseHost String?
|
||||||
|
hostedDatabasePort Int?
|
||||||
|
hostedDatabaseName String?
|
||||||
|
hostedDatabaseUser String?
|
||||||
|
hostedDatabasePassword String?
|
||||||
|
hostedDatabaseDBName String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
database Database? @relation(fields: [databaseId], references: [id])
|
||||||
|
application Application @relation(fields: [applicationId], references: [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
model ApplicationSettings {
|
model ApplicationSettings {
|
||||||
@@ -128,6 +184,9 @@ model ApplicationSettings {
|
|||||||
autodeploy Boolean @default(true)
|
autodeploy Boolean @default(true)
|
||||||
isBot Boolean @default(false)
|
isBot Boolean @default(false)
|
||||||
isPublicRepository Boolean @default(false)
|
isPublicRepository Boolean @default(false)
|
||||||
|
isDBBranching Boolean @default(false)
|
||||||
|
isCustomSSL Boolean @default(false)
|
||||||
|
isHttp2 Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
application Application @relation(fields: [applicationId], references: [id])
|
application Application @relation(fields: [applicationId], references: [id])
|
||||||
@@ -137,6 +196,7 @@ model ApplicationPersistentStorage {
|
|||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
applicationId String
|
applicationId String
|
||||||
path String
|
path String
|
||||||
|
oldPath Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
application Application @relation(fields: [applicationId], references: [id])
|
application Application @relation(fields: [applicationId], references: [id])
|
||||||
@@ -145,14 +205,17 @@ model ApplicationPersistentStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model ServicePersistentStorage {
|
model ServicePersistentStorage {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
serviceId String
|
serviceId String
|
||||||
path String
|
path String
|
||||||
createdAt DateTime @default(now())
|
volumeName String?
|
||||||
updatedAt DateTime @updatedAt
|
predefined Boolean @default(false)
|
||||||
service Service @relation(fields: [serviceId], references: [id])
|
containerId String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
service Service @relation(fields: [serviceId], references: [id])
|
||||||
|
|
||||||
@@unique([serviceId, path])
|
@@unique([serviceId, containerId, path])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Secret {
|
model Secret {
|
||||||
@@ -186,25 +249,27 @@ model BuildLog {
|
|||||||
applicationId String?
|
applicationId String?
|
||||||
buildId String
|
buildId String
|
||||||
line String
|
line String
|
||||||
time Int
|
time BigInt
|
||||||
}
|
}
|
||||||
|
|
||||||
model Build {
|
model Build {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
type String
|
type String
|
||||||
applicationId String?
|
applicationId String?
|
||||||
destinationDockerId String?
|
destinationDockerId String?
|
||||||
gitSourceId String?
|
gitSourceId String?
|
||||||
githubAppId String?
|
githubAppId String?
|
||||||
gitlabAppId String?
|
gitlabAppId String?
|
||||||
commit String?
|
commit String?
|
||||||
pullmergeRequestId String?
|
pullmergeRequestId String?
|
||||||
forceRebuild Boolean @default(false)
|
previewApplicationId String?
|
||||||
sourceBranch String?
|
forceRebuild Boolean @default(false)
|
||||||
branch String?
|
sourceBranch String?
|
||||||
status String? @default("queued")
|
sourceRepository String?
|
||||||
createdAt DateTime @default(now())
|
branch String?
|
||||||
updatedAt DateTime @updatedAt
|
status String? @default("queued")
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
model DestinationDocker {
|
model DestinationDocker {
|
||||||
@@ -240,6 +305,19 @@ model SshKey {
|
|||||||
destinationDocker DestinationDocker[]
|
destinationDocker DestinationDocker[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model DockerRegistry {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
url String
|
||||||
|
username String?
|
||||||
|
password String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
teamId String?
|
||||||
|
team Team? @relation(fields: [teamId], references: [id])
|
||||||
|
application Application[]
|
||||||
|
}
|
||||||
|
|
||||||
model GitSource {
|
model GitSource {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
@@ -248,11 +326,13 @@ model GitSource {
|
|||||||
apiUrl String?
|
apiUrl String?
|
||||||
htmlUrl String?
|
htmlUrl String?
|
||||||
customPort Int @default(22)
|
customPort Int @default(22)
|
||||||
|
customUser String @default("git")
|
||||||
organization String?
|
organization String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
githubAppId String? @unique
|
githubAppId String? @unique
|
||||||
gitlabAppId String? @unique
|
gitlabAppId String? @unique
|
||||||
|
isSystemWide Boolean @default(false)
|
||||||
gitlabApp GitlabApp? @relation(fields: [gitlabAppId], references: [id])
|
gitlabApp GitlabApp? @relation(fields: [gitlabAppId], references: [id])
|
||||||
githubApp GithubApp? @relation(fields: [githubAppId], references: [id])
|
githubApp GithubApp? @relation(fields: [githubAppId], references: [id])
|
||||||
application Application[]
|
application Application[]
|
||||||
@@ -291,22 +371,36 @@ model GitlabApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Database {
|
model Database {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
publicPort Int?
|
publicPort Int?
|
||||||
defaultDatabase String?
|
defaultDatabase String?
|
||||||
type String?
|
type String?
|
||||||
version String?
|
version String?
|
||||||
dbUser String?
|
dbUser String?
|
||||||
dbUserPassword String?
|
dbUserPassword String?
|
||||||
rootUser String?
|
rootUser String?
|
||||||
rootUserPassword String?
|
rootUserPassword String?
|
||||||
destinationDockerId String?
|
destinationDockerId String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
||||||
settings DatabaseSettings?
|
settings DatabaseSettings?
|
||||||
teams Team[]
|
teams Team[]
|
||||||
|
applicationConnectedDatabase ApplicationConnectedDatabase[]
|
||||||
|
databaseSecret DatabaseSecret[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model DatabaseSecret {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
value String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
databaseId String
|
||||||
|
database Database @relation(fields: [databaseId], references: [id])
|
||||||
|
|
||||||
|
@@unique([name, databaseId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model DatabaseSettings {
|
model DatabaseSettings {
|
||||||
@@ -327,12 +421,14 @@ model Service {
|
|||||||
dualCerts Boolean @default(false)
|
dualCerts Boolean @default(false)
|
||||||
type String?
|
type String?
|
||||||
version String?
|
version String?
|
||||||
|
templateVersion String @default("0.0.0")
|
||||||
destinationDockerId String?
|
destinationDockerId String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
||||||
persistentStorage ServicePersistentStorage[]
|
persistentStorage ServicePersistentStorage[]
|
||||||
serviceSecret ServiceSecret[]
|
serviceSecret ServiceSecret[]
|
||||||
|
serviceSetting ServiceSetting[]
|
||||||
teams Team[]
|
teams Team[]
|
||||||
|
|
||||||
fider Fider?
|
fider Fider?
|
||||||
@@ -352,6 +448,19 @@ model Service {
|
|||||||
taiga Taiga?
|
taiga Taiga?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model ServiceSetting {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
serviceId String
|
||||||
|
name String
|
||||||
|
value String
|
||||||
|
variableName String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
service Service @relation(fields: [serviceId], references: [id])
|
||||||
|
|
||||||
|
@@unique([serviceId, name])
|
||||||
|
}
|
||||||
|
|
||||||
model PlausibleAnalytics {
|
model PlausibleAnalytics {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
email String?
|
email String?
|
||||||
@@ -397,10 +506,10 @@ model Wordpress {
|
|||||||
ownMysql Boolean @default(false)
|
ownMysql Boolean @default(false)
|
||||||
mysqlHost String?
|
mysqlHost String?
|
||||||
mysqlPort Int?
|
mysqlPort Int?
|
||||||
mysqlUser String
|
mysqlUser String?
|
||||||
mysqlPassword String
|
mysqlPassword String?
|
||||||
mysqlRootUser String
|
mysqlRootUser String?
|
||||||
mysqlRootUserPassword String
|
mysqlRootUserPassword String?
|
||||||
mysqlDatabase String?
|
mysqlDatabase String?
|
||||||
mysqlPublicPort Int?
|
mysqlPublicPort Int?
|
||||||
ftpEnabled Boolean @default(false)
|
ftpEnabled Boolean @default(false)
|
||||||
@@ -540,8 +649,8 @@ model GlitchTip {
|
|||||||
emailSmtpPort Int? @default(25)
|
emailSmtpPort Int? @default(25)
|
||||||
emailSmtpUser String?
|
emailSmtpUser String?
|
||||||
emailSmtpPassword String?
|
emailSmtpPassword String?
|
||||||
emailSmtpUseTls Boolean? @default(false)
|
emailSmtpUseTls Boolean @default(false)
|
||||||
emailSmtpUseSsl Boolean? @default(false)
|
emailSmtpUseSsl Boolean @default(false)
|
||||||
emailBackend String?
|
emailBackend String?
|
||||||
mailgunApiKey String?
|
mailgunApiKey String?
|
||||||
sendgridApiKey String?
|
sendgridApiKey String?
|
||||||
|
|||||||
@@ -1,31 +1,18 @@
|
|||||||
const dotEnvExtended = require('dotenv-extended');
|
const dotEnvExtended = require('dotenv-extended');
|
||||||
dotEnvExtended.load();
|
dotEnvExtended.load();
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const generator = require('generate-password');
|
|
||||||
const cuid = require('cuid');
|
|
||||||
const { PrismaClient } = require('@prisma/client');
|
const { PrismaClient } = require('@prisma/client');
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
function generatePassword(length = 24) {
|
|
||||||
return generator.generate({
|
|
||||||
length,
|
|
||||||
numbers: true,
|
|
||||||
strict: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const algorithm = 'aes-256-ctr';
|
const algorithm = 'aes-256-ctr';
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
// Enable registration for the first user
|
// Enable registration for the first user
|
||||||
// Set initial HAProxy password
|
|
||||||
const settingsFound = await prisma.setting.findFirst({});
|
const settingsFound = await prisma.setting.findFirst({});
|
||||||
if (!settingsFound) {
|
if (!settingsFound) {
|
||||||
await prisma.setting.create({
|
await prisma.setting.create({
|
||||||
data: {
|
data: {
|
||||||
isRegistrationEnabled: true,
|
id: '0',
|
||||||
proxyPassword: encrypt(generatePassword()),
|
arch: process.arch,
|
||||||
proxyUser: cuid(),
|
|
||||||
arch: process.arch
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -34,11 +21,11 @@ async function main() {
|
|||||||
id: settingsFound.id
|
id: settingsFound.id
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
isTraefikUsed: true,
|
id: '0'
|
||||||
proxyHash: null
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Create local docker engine
|
||||||
const localDocker = await prisma.destinationDocker.findFirst({
|
const localDocker = await prisma.destinationDocker.findFirst({
|
||||||
where: { engine: '/var/run/docker.sock' }
|
where: { engine: '/var/run/docker.sock' }
|
||||||
});
|
});
|
||||||
@@ -55,23 +42,18 @@ async function main() {
|
|||||||
|
|
||||||
// Set auto-update based on env variable
|
// Set auto-update based on env variable
|
||||||
const isAutoUpdateEnabled = process.env['COOLIFY_AUTO_UPDATE'] === 'true';
|
const isAutoUpdateEnabled = process.env['COOLIFY_AUTO_UPDATE'] === 'true';
|
||||||
const settings = await prisma.setting.findFirst({});
|
await prisma.setting.update({
|
||||||
if (settings) {
|
where: {
|
||||||
await prisma.setting.update({
|
id: '0'
|
||||||
where: {
|
},
|
||||||
id: settings.id
|
data: {
|
||||||
},
|
isAutoUpdateEnabled
|
||||||
data: {
|
}
|
||||||
isAutoUpdateEnabled
|
});
|
||||||
}
|
// Create public github source
|
||||||
});
|
|
||||||
}
|
|
||||||
const github = await prisma.gitSource.findFirst({
|
const github = await prisma.gitSource.findFirst({
|
||||||
where: { htmlUrl: 'https://github.com', forPublic: true }
|
where: { htmlUrl: 'https://github.com', forPublic: true }
|
||||||
});
|
});
|
||||||
const gitlab = await prisma.gitSource.findFirst({
|
|
||||||
where: { htmlUrl: 'https://gitlab.com', forPublic: true }
|
|
||||||
});
|
|
||||||
if (!github) {
|
if (!github) {
|
||||||
await prisma.gitSource.create({
|
await prisma.gitSource.create({
|
||||||
data: {
|
data: {
|
||||||
@@ -83,6 +65,10 @@ async function main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Create public gitlab source
|
||||||
|
const gitlab = await prisma.gitSource.findFirst({
|
||||||
|
where: { htmlUrl: 'https://gitlab.com', forPublic: true }
|
||||||
|
});
|
||||||
if (!gitlab) {
|
if (!gitlab) {
|
||||||
await prisma.gitSource.create({
|
await prisma.gitSource.create({
|
||||||
data: {
|
data: {
|
||||||
@@ -94,6 +80,16 @@ async function main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Set new preview secrets
|
||||||
|
const secrets = await prisma.secret.findMany({ where: { isPRMRSecret: false } })
|
||||||
|
if (secrets.length > 0) {
|
||||||
|
for (const secret of secrets) {
|
||||||
|
const previewSecrets = await prisma.secret.findMany({ where: { applicationId: secret.applicationId, name: secret.name, isPRMRSecret: true } })
|
||||||
|
if (previewSecrets.length === 0) {
|
||||||
|
await prisma.secret.create({ data: { ...secret, id: undefined, isPRMRSecret: true } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
main()
|
main()
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
|||||||
67
apps/api/scripts/generateTags.mjs
Normal file
67
apps/api/scripts/generateTags.mjs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import fs from 'fs/promises';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
import got from 'got';
|
||||||
|
|
||||||
|
const repositories = [];
|
||||||
|
const templates = await fs.readFile('./apps/api/devTemplates.yaml', 'utf8');
|
||||||
|
const devTemplates = yaml.load(templates);
|
||||||
|
for (const template of devTemplates) {
|
||||||
|
let image = template.services['$$id'].image.replaceAll(':$$core_version', '');
|
||||||
|
if (!image.includes('/')) {
|
||||||
|
image = `library/${image}`;
|
||||||
|
}
|
||||||
|
repositories.push({ image, name: template.type });
|
||||||
|
}
|
||||||
|
const services = []
|
||||||
|
const numberOfTags = 30;
|
||||||
|
// const semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/g)
|
||||||
|
for (const repository of repositories) {
|
||||||
|
console.log('Querying', repository.name, 'at', repository.image);
|
||||||
|
let semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/g)
|
||||||
|
if (repository.name.startsWith('wordpress')) {
|
||||||
|
semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-php(0|[1-9]\d*)$/g)
|
||||||
|
}
|
||||||
|
if (repository.name.startsWith('minio')) {
|
||||||
|
semverRegex = new RegExp(/^RELEASE.*$/g)
|
||||||
|
}
|
||||||
|
if (repository.name.startsWith('fider')) {
|
||||||
|
semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-([0-9]+)$/g)
|
||||||
|
}
|
||||||
|
if (repository.name.startsWith('searxng')) {
|
||||||
|
semverRegex = new RegExp(/^\d{4}[\.\-](0?[1-9]|[12][0-9]|3[01])[\.\-](0?[1-9]|1[012]).*$/)
|
||||||
|
}
|
||||||
|
if (repository.name.startsWith('umami')) {
|
||||||
|
semverRegex = new RegExp(/^postgresql-v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-([0-9]+)$/g)
|
||||||
|
}
|
||||||
|
if (repository.image.includes('ghcr.io')) {
|
||||||
|
const { execaCommand } = await import('execa');
|
||||||
|
const { stdout } = await execaCommand(`docker run --rm quay.io/skopeo/stable list-tags docker://${repository.image}`);
|
||||||
|
if (stdout) {
|
||||||
|
const json = JSON.parse(stdout);
|
||||||
|
const semverTags = json.Tags.filter((tag) => semverRegex.test(tag))
|
||||||
|
let tags = semverTags.length > 10 ? semverTags.sort().reverse().slice(0, numberOfTags) : json.Tags.sort().reverse().slice(0, numberOfTags)
|
||||||
|
if (!tags.includes('latest')) {
|
||||||
|
tags.push('latest')
|
||||||
|
}
|
||||||
|
services.push({ name: repository.name, image: repository.image, tags })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const { token } = await got.get(`https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repository.image}:pull`).json()
|
||||||
|
let data = await got.get(`https://registry-1.docker.io/v2/${repository.image}/tags/list`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
}).json()
|
||||||
|
const semverTags = data.tags.filter((tag) => semverRegex.test(tag))
|
||||||
|
let tags = semverTags.length > 10 ? semverTags.sort().reverse().slice(0, numberOfTags) : data.tags.sort().reverse().slice(0, numberOfTags)
|
||||||
|
if (!tags.includes('latest')) {
|
||||||
|
tags.push('latest')
|
||||||
|
}
|
||||||
|
services.push({
|
||||||
|
name: repository.name,
|
||||||
|
image: repository.image,
|
||||||
|
tags
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await fs.writeFile('./apps/api/devTags.json', JSON.stringify(services));
|
||||||
@@ -3,176 +3,594 @@ import cors from '@fastify/cors';
|
|||||||
import serve from '@fastify/static';
|
import serve from '@fastify/static';
|
||||||
import env from '@fastify/env';
|
import env from '@fastify/env';
|
||||||
import cookie from '@fastify/cookie';
|
import cookie from '@fastify/cookie';
|
||||||
|
import multipart from '@fastify/multipart';
|
||||||
import path, { join } from 'path';
|
import path, { join } from 'path';
|
||||||
import autoLoad from '@fastify/autoload';
|
import autoLoad from '@fastify/autoload';
|
||||||
import { asyncExecShell, isDev, listSettings, prisma, version } from './lib/common';
|
import socketIO from 'fastify-socket.io';
|
||||||
|
import socketIOServer from './realtime';
|
||||||
|
|
||||||
|
import {
|
||||||
|
cleanupDockerStorage,
|
||||||
|
createRemoteEngineConfiguration,
|
||||||
|
decrypt,
|
||||||
|
executeCommand,
|
||||||
|
generateDatabaseConfiguration,
|
||||||
|
isDev,
|
||||||
|
listSettings,
|
||||||
|
prisma,
|
||||||
|
sentryDSN,
|
||||||
|
startTraefikProxy,
|
||||||
|
startTraefikTCPProxy,
|
||||||
|
version
|
||||||
|
} from './lib/common';
|
||||||
import { scheduler } from './lib/scheduler';
|
import { scheduler } from './lib/scheduler';
|
||||||
import { compareVersions } from 'compare-versions';
|
import { compareVersions } from 'compare-versions';
|
||||||
import Graceful from '@ladjs/graceful'
|
import Graceful from '@ladjs/graceful';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
import fs from 'fs/promises';
|
||||||
|
import { verifyRemoteDockerEngineFn } from './routes/api/v1/destinations/handlers';
|
||||||
|
import { checkContainer } from './lib/docker';
|
||||||
|
import { migrateApplicationPersistentStorage, migrateServicesToNewTemplate } from './lib';
|
||||||
|
import { refreshTags, refreshTemplates } from './routes/api/v1/handlers';
|
||||||
|
import * as Sentry from '@sentry/node';
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
config: {
|
config: {
|
||||||
COOLIFY_APP_ID: string,
|
COOLIFY_APP_ID: string;
|
||||||
COOLIFY_SECRET_KEY: string,
|
COOLIFY_SECRET_KEY: string;
|
||||||
COOLIFY_DATABASE_URL: string,
|
COOLIFY_DATABASE_URL: string;
|
||||||
COOLIFY_SENTRY_DSN: string,
|
COOLIFY_IS_ON: string;
|
||||||
COOLIFY_IS_ON: string,
|
COOLIFY_WHITE_LABELED: string;
|
||||||
COOLIFY_WHITE_LABELED: string,
|
COOLIFY_WHITE_LABELED_ICON: string | null;
|
||||||
COOLIFY_WHITE_LABELED_ICON: string | null,
|
COOLIFY_AUTO_UPDATE: string;
|
||||||
COOLIFY_AUTO_UPDATE: string,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const port = isDev ? 3001 : 3000;
|
const port = isDev ? 3001 : 3000;
|
||||||
const host = '0.0.0.0';
|
const host = '0.0.0.0';
|
||||||
const fastify = Fastify({
|
|
||||||
logger: false,
|
|
||||||
trustProxy: true
|
|
||||||
});
|
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
required: ['COOLIFY_SECRET_KEY', 'COOLIFY_DATABASE_URL', 'COOLIFY_IS_ON'],
|
|
||||||
properties: {
|
|
||||||
COOLIFY_APP_ID: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
COOLIFY_SECRET_KEY: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
COOLIFY_DATABASE_URL: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'file:../db/dev.db'
|
|
||||||
},
|
|
||||||
COOLIFY_SENTRY_DSN: {
|
|
||||||
type: 'string',
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
COOLIFY_IS_ON: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'docker'
|
|
||||||
},
|
|
||||||
COOLIFY_WHITE_LABELED: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'false'
|
|
||||||
},
|
|
||||||
COOLIFY_WHITE_LABELED_ICON: {
|
|
||||||
type: 'string',
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
COOLIFY_AUTO_UPDATE: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'false'
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
(async () => {
|
||||||
};
|
const settings = await prisma.setting.findFirst();
|
||||||
|
const fastify = Fastify({
|
||||||
const options = {
|
logger: settings?.isAPIDebuggingEnabled || false,
|
||||||
schema,
|
trustProxy: true
|
||||||
dotenv: true
|
|
||||||
};
|
|
||||||
fastify.register(env, options);
|
|
||||||
if (!isDev) {
|
|
||||||
fastify.register(serve, {
|
|
||||||
root: path.join(__dirname, './public'),
|
|
||||||
preCompressed: true
|
|
||||||
});
|
});
|
||||||
fastify.setNotFoundHandler(async function (request, reply) {
|
|
||||||
if (request.raw.url && request.raw.url.startsWith('/api')) {
|
const schema = {
|
||||||
return reply.status(404).send({
|
type: 'object',
|
||||||
success: false
|
required: ['COOLIFY_SECRET_KEY', 'COOLIFY_DATABASE_URL', 'COOLIFY_IS_ON'],
|
||||||
});
|
properties: {
|
||||||
|
COOLIFY_APP_ID: {
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
COOLIFY_SECRET_KEY: {
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
COOLIFY_DATABASE_URL: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'file:../db/dev.db'
|
||||||
|
},
|
||||||
|
COOLIFY_IS_ON: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'docker'
|
||||||
|
},
|
||||||
|
COOLIFY_WHITE_LABELED: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'false'
|
||||||
|
},
|
||||||
|
COOLIFY_WHITE_LABELED_ICON: {
|
||||||
|
type: 'string',
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
COOLIFY_AUTO_UPDATE: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'false'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return reply.status(200).sendFile('index.html');
|
};
|
||||||
|
const options = {
|
||||||
|
schema,
|
||||||
|
dotenv: true
|
||||||
|
};
|
||||||
|
fastify.register(env, options);
|
||||||
|
if (!isDev) {
|
||||||
|
fastify.register(serve, {
|
||||||
|
root: path.join(__dirname, './public'),
|
||||||
|
preCompressed: true
|
||||||
|
});
|
||||||
|
fastify.setNotFoundHandler(async function (request, reply) {
|
||||||
|
if (request.raw.url && request.raw.url.startsWith('/api')) {
|
||||||
|
return reply.status(404).send({
|
||||||
|
success: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return reply.status(200).sendFile('index.html');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fastify.register(multipart, { limits: { fileSize: 100000 } });
|
||||||
|
fastify.register(autoLoad, {
|
||||||
|
dir: join(__dirname, 'plugins')
|
||||||
});
|
});
|
||||||
}
|
fastify.register(autoLoad, {
|
||||||
fastify.register(autoLoad, {
|
dir: join(__dirname, 'routes')
|
||||||
dir: join(__dirname, 'plugins')
|
});
|
||||||
});
|
fastify.register(cookie);
|
||||||
fastify.register(autoLoad, {
|
fastify.register(cors);
|
||||||
dir: join(__dirname, 'routes')
|
fastify.register(socketIO, {
|
||||||
});
|
cors: {
|
||||||
|
origin: isDev ? '*' : ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// To detect allowed origins
|
||||||
|
// fastify.addHook('onRequest', async (request, reply) => {
|
||||||
|
// console.log(request.headers.host)
|
||||||
|
// let allowedList = ['coolify:3000'];
|
||||||
|
// const { ipv4, ipv6, fqdn } = await prisma.setting.findFirst({})
|
||||||
|
|
||||||
fastify.register(cookie)
|
// ipv4 && allowedList.push(`${ipv4}:3000`);
|
||||||
fastify.register(cors);
|
// ipv6 && allowedList.push(ipv6);
|
||||||
fastify.listen({ port, host }, async (err: any, address: any) => {
|
// fqdn && allowedList.push(getDomain(fqdn));
|
||||||
if (err) {
|
// isDev && allowedList.push('localhost:3000') && allowedList.push('localhost:3001') && allowedList.push('host.docker.internal:3001');
|
||||||
console.error(err);
|
// const remotes = await prisma.destinationDocker.findMany({ where: { remoteEngine: true, remoteVerified: true } })
|
||||||
|
// if (remotes.length > 0) {
|
||||||
|
// remotes.forEach(remote => {
|
||||||
|
// allowedList.push(`${remote.remoteIpAddress}:3000`);
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// if (!allowedList.includes(request.headers.host)) {
|
||||||
|
// // console.log('not allowed', request.headers.host)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fastify.listen({ port, host });
|
||||||
|
await socketIOServer(fastify);
|
||||||
|
console.log(`Coolify's API is listening on ${host}:${port}`);
|
||||||
|
|
||||||
|
migrateServicesToNewTemplate();
|
||||||
|
await migrateApplicationPersistentStorage();
|
||||||
|
await initServer();
|
||||||
|
|
||||||
|
const graceful = new Graceful({ brees: [scheduler] });
|
||||||
|
graceful.listen();
|
||||||
|
|
||||||
|
setInterval(async () => {
|
||||||
|
if (!scheduler.workers.has('deployApplication')) {
|
||||||
|
scheduler.run('deployApplication');
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
// autoUpdater
|
||||||
|
setInterval(async () => {
|
||||||
|
await autoUpdater();
|
||||||
|
}, 60000 * 15);
|
||||||
|
|
||||||
|
// cleanupStorage
|
||||||
|
setInterval(async () => {
|
||||||
|
await cleanupStorage();
|
||||||
|
}, 60000 * 15);
|
||||||
|
|
||||||
|
// checkProxies, checkFluentBit & refresh templates
|
||||||
|
setInterval(async () => {
|
||||||
|
await checkProxies();
|
||||||
|
await checkFluentBit();
|
||||||
|
}, 60000);
|
||||||
|
|
||||||
|
// Refresh and check templates
|
||||||
|
setInterval(async () => {
|
||||||
|
await refreshTemplates();
|
||||||
|
}, 60000);
|
||||||
|
|
||||||
|
setInterval(async () => {
|
||||||
|
await refreshTags();
|
||||||
|
}, 60000);
|
||||||
|
|
||||||
|
setInterval(
|
||||||
|
async () => {
|
||||||
|
await migrateServicesToNewTemplate();
|
||||||
|
},
|
||||||
|
isDev ? 10000 : 60000
|
||||||
|
);
|
||||||
|
|
||||||
|
setInterval(async () => {
|
||||||
|
await copySSLCertificates();
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
await Promise.all([getTagsTemplates(), getArch(), getIPAddress(), configureRemoteDockers()]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
console.log(`Coolify's API is listening on ${host}:${port}`);
|
})();
|
||||||
await initServer();
|
|
||||||
|
|
||||||
const graceful = new Graceful({ brees: [scheduler] });
|
|
||||||
graceful.listen();
|
|
||||||
|
|
||||||
setInterval(async () => {
|
|
||||||
if (!scheduler.workers.has('deployApplication')) {
|
|
||||||
scheduler.run('deployApplication');
|
|
||||||
}
|
|
||||||
if (!scheduler.workers.has('infrastructure')) {
|
|
||||||
scheduler.run('infrastructure');
|
|
||||||
}
|
|
||||||
}, 2000)
|
|
||||||
|
|
||||||
// autoUpdater
|
|
||||||
setInterval(async () => {
|
|
||||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:autoUpdater")
|
|
||||||
}, isDev ? 5000 : 60000 * 15)
|
|
||||||
|
|
||||||
// cleanupStorage
|
|
||||||
setInterval(async () => {
|
|
||||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:cleanupStorage")
|
|
||||||
}, isDev ? 6000 : 60000 * 10)
|
|
||||||
|
|
||||||
// checkProxies
|
|
||||||
setInterval(async () => {
|
|
||||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:checkProxies")
|
|
||||||
}, 10000)
|
|
||||||
|
|
||||||
// cleanupPrismaEngines
|
|
||||||
// setInterval(async () => {
|
|
||||||
// scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:cleanupPrismaEngines")
|
|
||||||
// }, 60000)
|
|
||||||
|
|
||||||
await getArch();
|
|
||||||
await getIPAddress();
|
|
||||||
});
|
|
||||||
async function getIPAddress() {
|
async function getIPAddress() {
|
||||||
const { publicIpv4, publicIpv6 } = await import('public-ip')
|
const { publicIpv4, publicIpv6 } = await import('public-ip');
|
||||||
try {
|
try {
|
||||||
const settings = await listSettings();
|
const settings = await listSettings();
|
||||||
if (!settings.ipv4) {
|
if (!settings.ipv4) {
|
||||||
const ipv4 = await publicIpv4({ timeout: 2000 })
|
const ipv4 = await publicIpv4({ timeout: 2000 });
|
||||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv4 } })
|
console.log(`Getting public IPv4 address...`);
|
||||||
|
await prisma.setting.update({ where: { id: settings.id }, data: { ipv4 } });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.ipv6) {
|
if (!settings.ipv6) {
|
||||||
const ipv6 = await publicIpv6({ timeout: 2000 })
|
const ipv6 = await publicIpv6({ timeout: 2000 });
|
||||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } })
|
console.log(`Getting public IPv6 address...`);
|
||||||
|
await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } });
|
||||||
}
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
async function getTagsTemplates() {
|
||||||
|
const { default: got } = await import('got');
|
||||||
|
try {
|
||||||
|
if (isDev) {
|
||||||
|
let templates = await fs.readFile('./devTemplates.yaml', 'utf8');
|
||||||
|
let tags = await fs.readFile('./devTags.json', 'utf8');
|
||||||
|
try {
|
||||||
|
if (await fs.stat('./testTemplate.yaml')) {
|
||||||
|
templates = templates + (await fs.readFile('./testTemplate.yaml', 'utf8'));
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
try {
|
||||||
|
if (await fs.stat('./testTags.json')) {
|
||||||
|
const testTags = await fs.readFile('./testTags.json', 'utf8');
|
||||||
|
if (testTags.length > 0) {
|
||||||
|
tags = JSON.stringify(JSON.parse(tags).concat(JSON.parse(testTags)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
} catch (error) { }
|
await fs.writeFile('./templates.json', JSON.stringify(yaml.load(templates)));
|
||||||
|
await fs.writeFile('./tags.json', tags);
|
||||||
|
console.log('[004] Tags and templates loaded in dev mode...');
|
||||||
|
} else {
|
||||||
|
const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text();
|
||||||
|
const response = await got
|
||||||
|
.get('https://get.coollabs.io/coolify/service-templates.yaml')
|
||||||
|
.text();
|
||||||
|
await fs.writeFile('/app/templates.json', JSON.stringify(yaml.load(response)));
|
||||||
|
await fs.writeFile('/app/tags.json', tags);
|
||||||
|
console.log('[004] Tags and templates loaded...');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Couldn't get latest templates.");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async function initServer() {
|
async function initServer() {
|
||||||
|
const appId = process.env['COOLIFY_APP_ID'];
|
||||||
|
const settings = await prisma.setting.findUnique({ where: { id: '0' } });
|
||||||
try {
|
try {
|
||||||
await asyncExecShell(`docker network create --attachable coolify`);
|
if (settings.doNotTrack === true) {
|
||||||
} catch (error) { }
|
console.log('[000] Telemetry disabled...');
|
||||||
|
} else {
|
||||||
|
if (settings.sentryDSN !== sentryDSN) {
|
||||||
|
await prisma.setting.update({ where: { id: '0' }, data: { sentryDSN } });
|
||||||
|
}
|
||||||
|
// Initialize Sentry
|
||||||
|
// Sentry.init({
|
||||||
|
// dsn: sentryDSN,
|
||||||
|
// environment: isDev ? 'development' : 'production',
|
||||||
|
// release: version
|
||||||
|
// });
|
||||||
|
// console.log('[000] Sentry initialized...')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
console.log(`[001] Initializing server...`);
|
||||||
|
await executeCommand({ command: `docker network create --attachable coolify` });
|
||||||
|
} catch (error) {}
|
||||||
|
try {
|
||||||
|
console.log(`[002] Cleanup stucked builds...`);
|
||||||
const isOlder = compareVersions('3.8.1', version);
|
const isOlder = compareVersions('3.8.1', version);
|
||||||
if (isOlder === 1) {
|
if (isOlder === 1) {
|
||||||
await prisma.build.updateMany({ where: { status: { in: ['running', 'queued'] } }, data: { status: 'failed' } });
|
await prisma.build.updateMany({
|
||||||
|
where: { status: { in: ['running', 'queued'] } },
|
||||||
|
data: { status: 'failed' }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) { }
|
} catch (error) {}
|
||||||
|
try {
|
||||||
|
console.log('[003] Cleaning up old build sources under /tmp/build-sources/...');
|
||||||
|
await fs.rm('/tmp/build-sources', { recursive: true, force: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getArch() {
|
async function getArch() {
|
||||||
try {
|
try {
|
||||||
const settings = await prisma.setting.findFirst({})
|
const settings = await prisma.setting.findFirst({});
|
||||||
if (settings && !settings.arch) {
|
if (settings && !settings.arch) {
|
||||||
await prisma.setting.update({ where: { id: settings.id }, data: { arch: process.arch } })
|
console.log(`Getting architecture...`);
|
||||||
|
await prisma.setting.update({ where: { id: settings.id }, data: { arch: process.arch } });
|
||||||
}
|
}
|
||||||
} catch (error) { }
|
} catch (error) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function configureRemoteDockers() {
|
||||||
|
try {
|
||||||
|
const remoteDocker = await prisma.destinationDocker.findMany({
|
||||||
|
where: { remoteVerified: true, remoteEngine: true }
|
||||||
|
});
|
||||||
|
if (remoteDocker.length > 0) {
|
||||||
|
console.log(`Verifying Remote Docker Engines...`);
|
||||||
|
for (const docker of remoteDocker) {
|
||||||
|
console.log('Verifying:', docker.remoteIpAddress);
|
||||||
|
await verifyRemoteDockerEngineFn(docker.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function autoUpdater() {
|
||||||
|
try {
|
||||||
|
const { default: got } = await import('got');
|
||||||
|
const currentVersion = version;
|
||||||
|
const { coolify } = await got
|
||||||
|
.get('https://get.coollabs.io/versions.json', {
|
||||||
|
searchParams: {
|
||||||
|
appId: process.env['COOLIFY_APP_ID'] || undefined,
|
||||||
|
version: currentVersion
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.json();
|
||||||
|
const latestVersion = coolify.main.version;
|
||||||
|
const isUpdateAvailable = compareVersions(latestVersion, currentVersion);
|
||||||
|
if (isUpdateAvailable === 1) {
|
||||||
|
const activeCount = 0;
|
||||||
|
if (activeCount === 0) {
|
||||||
|
if (!isDev) {
|
||||||
|
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
|
||||||
|
if (isAutoUpdateEnabled) {
|
||||||
|
await executeCommand({ command: `docker pull coollabsio/coolify:${latestVersion}` });
|
||||||
|
await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` });
|
||||||
|
await executeCommand({
|
||||||
|
command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env`
|
||||||
|
});
|
||||||
|
await executeCommand({
|
||||||
|
shell: true,
|
||||||
|
command: `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-fluentbit && docker rm coolify coolify-fluentbit && docker compose pull && docker compose up -d --force-recreate"`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('Updating (not really in dev mode).');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkFluentBit() {
|
||||||
|
try {
|
||||||
|
if (!isDev) {
|
||||||
|
const engine = '/var/run/docker.sock';
|
||||||
|
const { id } = await prisma.destinationDocker.findFirst({
|
||||||
|
where: { engine, network: 'coolify' }
|
||||||
|
});
|
||||||
|
const { found } = await checkContainer({
|
||||||
|
dockerId: id,
|
||||||
|
container: 'coolify-fluentbit',
|
||||||
|
remove: true
|
||||||
|
});
|
||||||
|
if (!found) {
|
||||||
|
await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` });
|
||||||
|
await executeCommand({ command: `docker compose up -d fluent-bit` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function checkProxies() {
|
||||||
|
try {
|
||||||
|
const { default: isReachable } = await import('is-port-reachable');
|
||||||
|
let portReachable;
|
||||||
|
|
||||||
|
const { arch, ipv4, ipv6 } = await listSettings();
|
||||||
|
|
||||||
|
// Coolify Proxy local
|
||||||
|
const engine = '/var/run/docker.sock';
|
||||||
|
const localDocker = await prisma.destinationDocker.findFirst({
|
||||||
|
where: { engine, network: 'coolify', isCoolifyProxyUsed: true }
|
||||||
|
});
|
||||||
|
if (localDocker) {
|
||||||
|
portReachable = await isReachable(80, { host: ipv4 || ipv6 });
|
||||||
|
if (!portReachable) {
|
||||||
|
await startTraefikProxy(localDocker.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Coolify Proxy remote
|
||||||
|
const remoteDocker = await prisma.destinationDocker.findMany({
|
||||||
|
where: { remoteEngine: true, remoteVerified: true }
|
||||||
|
});
|
||||||
|
if (remoteDocker.length > 0) {
|
||||||
|
for (const docker of remoteDocker) {
|
||||||
|
if (docker.isCoolifyProxyUsed) {
|
||||||
|
portReachable = await isReachable(80, { host: docker.remoteIpAddress });
|
||||||
|
if (!portReachable) {
|
||||||
|
await startTraefikProxy(docker.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await createRemoteEngineConfiguration(docker.id);
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TCP Proxies
|
||||||
|
const databasesWithPublicPort = await prisma.database.findMany({
|
||||||
|
where: { publicPort: { not: null } },
|
||||||
|
include: { settings: true, destinationDocker: true }
|
||||||
|
});
|
||||||
|
for (const database of databasesWithPublicPort) {
|
||||||
|
const { destinationDockerId, destinationDocker, publicPort, id } = database;
|
||||||
|
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
|
||||||
|
const { privatePort } = generateDatabaseConfiguration(database, arch);
|
||||||
|
await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const wordpressWithFtp = await prisma.wordpress.findMany({
|
||||||
|
where: { ftpPublicPort: { not: null } },
|
||||||
|
include: { service: { include: { destinationDocker: true } } }
|
||||||
|
});
|
||||||
|
for (const ftp of wordpressWithFtp) {
|
||||||
|
const { service, ftpPublicPort } = ftp;
|
||||||
|
const { destinationDockerId, destinationDocker, id } = service;
|
||||||
|
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
|
||||||
|
await startTraefikTCPProxy(destinationDocker, id, ftpPublicPort, 22, 'wordpressftp');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP Proxies
|
||||||
|
// const minioInstances = await prisma.minio.findMany({
|
||||||
|
// where: { publicPort: { not: null } },
|
||||||
|
// include: { service: { include: { destinationDocker: true } } }
|
||||||
|
// });
|
||||||
|
// for (const minio of minioInstances) {
|
||||||
|
// const { service, publicPort } = minio;
|
||||||
|
// const { destinationDockerId, destinationDocker, id } = service;
|
||||||
|
// if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
|
||||||
|
// await startTraefikTCPProxy(destinationDocker, id, publicPort, 9000);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copySSLCertificates() {
|
||||||
|
try {
|
||||||
|
const pAll = await import('p-all');
|
||||||
|
const actions = [];
|
||||||
|
const certificates = await prisma.certificate.findMany({ include: { team: true } });
|
||||||
|
const teamIds = certificates.map((c) => c.teamId);
|
||||||
|
const destinations = await prisma.destinationDocker.findMany({
|
||||||
|
where: { isCoolifyProxyUsed: true, teams: { some: { id: { in: [...teamIds] } } } }
|
||||||
|
});
|
||||||
|
for (const certificate of certificates) {
|
||||||
|
const { id, key, cert } = certificate;
|
||||||
|
const decryptedKey = decrypt(key);
|
||||||
|
await fs.writeFile(`/tmp/${id}-key.pem`, decryptedKey);
|
||||||
|
await fs.writeFile(`/tmp/${id}-cert.pem`, cert);
|
||||||
|
for (const destination of destinations) {
|
||||||
|
if (destination.remoteEngine) {
|
||||||
|
if (destination.remoteVerified) {
|
||||||
|
const { id: dockerId, remoteIpAddress } = destination;
|
||||||
|
actions.push(async () => copyRemoteCertificates(id, dockerId, remoteIpAddress));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actions.push(async () => copyLocalCertificates(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await pAll.default(actions, { concurrency: 1 });
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
await executeCommand({ command: `find /tmp/ -maxdepth 1 -type f -name '*-*.pem' -delete` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyRemoteCertificates(id: string, dockerId: string, remoteIpAddress: string) {
|
||||||
|
try {
|
||||||
|
await executeCommand({
|
||||||
|
command: `scp /tmp/${id}-cert.pem /tmp/${id}-key.pem ${remoteIpAddress}:/tmp/`
|
||||||
|
});
|
||||||
|
await executeCommand({
|
||||||
|
sshCommand: true,
|
||||||
|
shell: true,
|
||||||
|
dockerId,
|
||||||
|
command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`
|
||||||
|
});
|
||||||
|
await executeCommand({
|
||||||
|
sshCommand: true,
|
||||||
|
dockerId,
|
||||||
|
command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||||
|
});
|
||||||
|
await executeCommand({
|
||||||
|
sshCommand: true,
|
||||||
|
dockerId,
|
||||||
|
command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log({ error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function copyLocalCertificates(id: string) {
|
||||||
|
try {
|
||||||
|
await executeCommand({
|
||||||
|
command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`,
|
||||||
|
shell: true
|
||||||
|
});
|
||||||
|
await executeCommand({
|
||||||
|
command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||||
|
});
|
||||||
|
await executeCommand({
|
||||||
|
command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log({ error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cleanupStorage() {
|
||||||
|
const destinationDockers = await prisma.destinationDocker.findMany();
|
||||||
|
let enginesDone = new Set();
|
||||||
|
for (const destination of destinationDockers) {
|
||||||
|
if (enginesDone.has(destination.engine) || enginesDone.has(destination.remoteIpAddress)) return;
|
||||||
|
if (destination.engine) enginesDone.add(destination.engine);
|
||||||
|
if (destination.remoteIpAddress) enginesDone.add(destination.remoteIpAddress);
|
||||||
|
let force = false;
|
||||||
|
let lowDiskSpace = false;
|
||||||
|
try {
|
||||||
|
let stdout = null;
|
||||||
|
if (!isDev) {
|
||||||
|
const output = await executeCommand({
|
||||||
|
dockerId: destination.id,
|
||||||
|
command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'`,
|
||||||
|
shell: true
|
||||||
|
});
|
||||||
|
stdout = output.stdout;
|
||||||
|
} else {
|
||||||
|
const output = await executeCommand({
|
||||||
|
command: `df -kPT /`
|
||||||
|
});
|
||||||
|
stdout = output.stdout;
|
||||||
|
}
|
||||||
|
let lines = stdout.trim().split('\n');
|
||||||
|
let header = lines[0];
|
||||||
|
let regex =
|
||||||
|
/^Filesystem\s+|Type\s+|1024-blocks|\s+Used|\s+Available|\s+Capacity|\s+Mounted on\s*$/g;
|
||||||
|
const boundaries = [];
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = regex.exec(header))) {
|
||||||
|
boundaries.push(match[0].length);
|
||||||
|
}
|
||||||
|
|
||||||
|
boundaries[boundaries.length - 1] = -1;
|
||||||
|
const data = lines.slice(1).map((line) => {
|
||||||
|
const cl = boundaries.map((boundary) => {
|
||||||
|
const column = boundary > 0 ? line.slice(0, boundary) : line;
|
||||||
|
line = line.slice(boundary);
|
||||||
|
return column.trim();
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
capacity: Number.parseInt(cl[5], 10) / 100
|
||||||
|
};
|
||||||
|
});
|
||||||
|
if (data.length > 0) {
|
||||||
|
const { capacity } = data[0];
|
||||||
|
if (capacity > 0.8) {
|
||||||
|
lowDiskSpace = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
await cleanupDockerStorage(destination.id, lowDiskSpace, force);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,8 +3,26 @@ import crypto from 'crypto';
|
|||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
import { copyBaseConfigurationFiles, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common';
|
import {
|
||||||
import { createDirectories, decrypt, defaultComposeConfiguration, executeDockerCmd, getDomain, prisma, decryptApplication } from '../lib/common';
|
copyBaseConfigurationFiles,
|
||||||
|
makeLabelForSimpleDockerfile,
|
||||||
|
makeLabelForStandaloneApplication,
|
||||||
|
saveBuildLog,
|
||||||
|
saveDockerRegistryCredentials,
|
||||||
|
setDefaultConfiguration
|
||||||
|
} from '../lib/buildPacks/common';
|
||||||
|
import {
|
||||||
|
createDirectories,
|
||||||
|
decrypt,
|
||||||
|
defaultComposeConfiguration,
|
||||||
|
getDomain,
|
||||||
|
prisma,
|
||||||
|
decryptApplication,
|
||||||
|
isDev,
|
||||||
|
pushToRegistry,
|
||||||
|
executeCommand,
|
||||||
|
generateSecrets
|
||||||
|
} from '../lib/common';
|
||||||
import * as importers from '../lib/importers';
|
import * as importers from '../lib/importers';
|
||||||
import * as buildpacks from '../lib/buildPacks';
|
import * as buildpacks from '../lib/buildPacks';
|
||||||
|
|
||||||
@@ -14,71 +32,316 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
if (message === 'error') throw new Error('oops');
|
if (message === 'error') throw new Error('oops');
|
||||||
if (message === 'cancel') {
|
if (message === 'cancel') {
|
||||||
parentPort.postMessage('cancelled');
|
parentPort.postMessage('cancelled');
|
||||||
await prisma.$disconnect()
|
await prisma.$disconnect();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const pThrottle = await import('p-throttle')
|
const pThrottle = await import('p-throttle');
|
||||||
const throttle = pThrottle.default({
|
const throttle = pThrottle.default({
|
||||||
limit: 1,
|
limit: 1,
|
||||||
interval: 2000
|
interval: 2000
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const th = throttle(async () => {
|
const th = throttle(async () => {
|
||||||
try {
|
try {
|
||||||
const queuedBuilds = await prisma.build.findMany({ where: { status: { in: ['queued', 'running'] } }, orderBy: { createdAt: 'asc' } });
|
const queuedBuilds = await prisma.build.findMany({
|
||||||
const { concurrentBuilds } = await prisma.setting.findFirst({})
|
where: { status: { in: ['queued', 'running'] } },
|
||||||
|
orderBy: { createdAt: 'asc' }
|
||||||
|
});
|
||||||
|
const { concurrentBuilds } = await prisma.setting.findFirst({});
|
||||||
if (queuedBuilds.length > 0) {
|
if (queuedBuilds.length > 0) {
|
||||||
parentPort.postMessage({ deploying: true });
|
parentPort.postMessage({ deploying: true });
|
||||||
const concurrency = concurrentBuilds;
|
const concurrency = concurrentBuilds;
|
||||||
const pAll = await import('p-all');
|
const pAll = await import('p-all');
|
||||||
const actions = []
|
const actions = [];
|
||||||
|
|
||||||
for (const queueBuild of queuedBuilds) {
|
for (const queueBuild of queuedBuilds) {
|
||||||
actions.push(async () => {
|
actions.push(async () => {
|
||||||
let application = await prisma.application.findUnique({ where: { id: queueBuild.applicationId }, include: { destinationDocker: true, gitSource: { include: { githubApp: true, gitlabApp: true } }, persistentStorage: true, secrets: true, settings: true, teams: true } })
|
let application = await prisma.application.findUnique({
|
||||||
let { id: buildId, type, sourceBranch = null, pullmergeRequestId = null, forceRebuild } = queueBuild
|
where: { id: queueBuild.applicationId },
|
||||||
application = decryptApplication(application)
|
include: {
|
||||||
try {
|
dockerRegistry: true,
|
||||||
if (queueBuild.status === 'running') {
|
destinationDocker: true,
|
||||||
await saveBuildLog({ line: 'Building halted, restarting...', buildId, applicationId: application.id });
|
gitSource: { include: { githubApp: true, gitlabApp: true } },
|
||||||
|
persistentStorage: true,
|
||||||
|
secrets: true,
|
||||||
|
settings: true,
|
||||||
|
teams: true
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let {
|
||||||
|
id: buildId,
|
||||||
|
type,
|
||||||
|
gitSourceId,
|
||||||
|
sourceBranch = null,
|
||||||
|
pullmergeRequestId = null,
|
||||||
|
previewApplicationId = null,
|
||||||
|
forceRebuild,
|
||||||
|
sourceRepository = null
|
||||||
|
} = queueBuild;
|
||||||
|
application = decryptApplication(application);
|
||||||
|
|
||||||
|
if (!gitSourceId && application.simpleDockerfile) {
|
||||||
const {
|
const {
|
||||||
id: applicationId,
|
id: applicationId,
|
||||||
repository,
|
|
||||||
name,
|
|
||||||
destinationDocker,
|
destinationDocker,
|
||||||
destinationDockerId,
|
destinationDockerId,
|
||||||
gitSource,
|
|
||||||
configHash,
|
|
||||||
fqdn,
|
|
||||||
projectId,
|
|
||||||
secrets,
|
secrets,
|
||||||
phpModules,
|
|
||||||
settings,
|
|
||||||
persistentStorage,
|
|
||||||
pythonWSGI,
|
|
||||||
pythonModule,
|
|
||||||
pythonVariable,
|
|
||||||
denoOptions,
|
|
||||||
exposePort,
|
|
||||||
baseImage,
|
|
||||||
baseBuildImage,
|
|
||||||
deploymentType,
|
|
||||||
} = application
|
|
||||||
let {
|
|
||||||
branch,
|
|
||||||
buildPack,
|
|
||||||
port,
|
port,
|
||||||
installCommand,
|
persistentStorage,
|
||||||
buildCommand,
|
exposePort,
|
||||||
startCommand,
|
simpleDockerfile,
|
||||||
baseDirectory,
|
dockerRegistry
|
||||||
publishDirectory,
|
} = application;
|
||||||
dockerFileLocation,
|
const { workdir } = await createDirectories({ repository: applicationId, buildId });
|
||||||
denoMainFile
|
try {
|
||||||
} = application
|
if (queueBuild.status === 'running') {
|
||||||
|
await saveBuildLog({
|
||||||
|
line: 'Building halted, restarting...',
|
||||||
|
buildId,
|
||||||
|
applicationId: application.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const volumes =
|
||||||
|
persistentStorage?.map((storage) => {
|
||||||
|
if (storage.oldPath) {
|
||||||
|
return `${applicationId}${storage.path
|
||||||
|
.replace(/\//gi, '-')
|
||||||
|
.replace('-app', '')}:${storage.path}`;
|
||||||
|
}
|
||||||
|
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`;
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
if (destinationDockerId) {
|
||||||
|
await prisma.build.update({
|
||||||
|
where: { id: buildId },
|
||||||
|
data: { status: 'running' }
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const { stdout: containers } = await executeCommand({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker ps -a --filter 'label=com.docker.compose.service=${applicationId}' --format {{.ID}}`
|
||||||
|
});
|
||||||
|
if (containers) {
|
||||||
|
const containerArray = containers.split('\n');
|
||||||
|
if (containerArray.length > 0) {
|
||||||
|
for (const container of containerArray) {
|
||||||
|
await executeCommand({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker stop -t 0 ${container}`
|
||||||
|
});
|
||||||
|
await executeCommand({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker rm --force ${container}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
let envs = [];
|
||||||
|
if (secrets.length > 0) {
|
||||||
|
envs = [
|
||||||
|
...envs,
|
||||||
|
...generateSecrets(secrets, pullmergeRequestId, false, port)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
await fs.writeFile(`${workdir}/Dockerfile`, simpleDockerfile);
|
||||||
|
if (dockerRegistry) {
|
||||||
|
const { url, username, password } = dockerRegistry;
|
||||||
|
await saveDockerRegistryCredentials({ url, username, password, workdir });
|
||||||
|
}
|
||||||
|
|
||||||
|
const labels = makeLabelForSimpleDockerfile({
|
||||||
|
applicationId,
|
||||||
|
type,
|
||||||
|
port: exposePort ? `${exposePort}:${port}` : port
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const composeVolumes = volumes.map((volume) => {
|
||||||
|
return {
|
||||||
|
[`${volume.split(':')[0]}`]: {
|
||||||
|
name: volume.split(':')[0]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const composeFile = {
|
||||||
|
version: '3.8',
|
||||||
|
services: {
|
||||||
|
[applicationId]: {
|
||||||
|
build: {
|
||||||
|
context: workdir
|
||||||
|
},
|
||||||
|
image: `${applicationId}:${buildId}`,
|
||||||
|
container_name: applicationId,
|
||||||
|
volumes,
|
||||||
|
labels,
|
||||||
|
environment: envs,
|
||||||
|
depends_on: [],
|
||||||
|
expose: [port],
|
||||||
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
|
...defaultComposeConfiguration(destinationDocker.network)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
networks: {
|
||||||
|
[destinationDocker.network]: {
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: Object.assign({}, ...composeVolumes)
|
||||||
|
};
|
||||||
|
await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile));
|
||||||
|
await executeCommand({
|
||||||
|
debug: true,
|
||||||
|
dockerId: destinationDocker.id,
|
||||||
|
command: `docker compose --project-directory ${workdir} -f ${workdir}/docker-compose.yml up -d`
|
||||||
|
});
|
||||||
|
await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId });
|
||||||
|
} catch (error) {
|
||||||
|
await saveBuildLog({ line: error, buildId, applicationId });
|
||||||
|
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } });
|
||||||
|
if (foundBuild) {
|
||||||
|
await prisma.build.update({
|
||||||
|
where: { id: buildId },
|
||||||
|
data: {
|
||||||
|
status: 'failed'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } });
|
||||||
|
if (foundBuild) {
|
||||||
|
await prisma.build.update({
|
||||||
|
where: { id: buildId },
|
||||||
|
data: {
|
||||||
|
status: 'failed'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error !== 1) {
|
||||||
|
await saveBuildLog({ line: error, buildId, applicationId: application.id });
|
||||||
|
}
|
||||||
|
if (error instanceof Error) {
|
||||||
|
await saveBuildLog({
|
||||||
|
line: error.message,
|
||||||
|
buildId,
|
||||||
|
applicationId: application.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await fs.rm(workdir, { recursive: true, force: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (application.dockerRegistryImageName) {
|
||||||
|
const customTag = application.dockerRegistryImageName.split(':')[1] || buildId;
|
||||||
|
const imageName = application.dockerRegistryImageName.split(':')[0];
|
||||||
|
await saveBuildLog({
|
||||||
|
line: `Pushing ${imageName}:${customTag} to Docker Registry... It could take a while...`,
|
||||||
|
buildId,
|
||||||
|
applicationId: application.id
|
||||||
|
});
|
||||||
|
await pushToRegistry(application, workdir, buildId, imageName, customTag);
|
||||||
|
await saveBuildLog({ line: 'Success', buildId, applicationId: application.id });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.stdout) {
|
||||||
|
await saveBuildLog({ line: error.stdout, buildId, applicationId });
|
||||||
|
}
|
||||||
|
if (error.stderr) {
|
||||||
|
await saveBuildLog({ line: error.stderr, buildId, applicationId });
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await fs.rm(workdir, { recursive: true, force: true });
|
||||||
|
await prisma.build.update({
|
||||||
|
where: { id: buildId },
|
||||||
|
data: { status: 'success' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalApplicationId = application.id;
|
||||||
|
const {
|
||||||
|
id: applicationId,
|
||||||
|
name,
|
||||||
|
destinationDocker,
|
||||||
|
destinationDockerId,
|
||||||
|
gitSource,
|
||||||
|
configHash,
|
||||||
|
fqdn,
|
||||||
|
projectId,
|
||||||
|
secrets,
|
||||||
|
phpModules,
|
||||||
|
settings,
|
||||||
|
persistentStorage,
|
||||||
|
pythonWSGI,
|
||||||
|
pythonModule,
|
||||||
|
pythonVariable,
|
||||||
|
denoOptions,
|
||||||
|
exposePort,
|
||||||
|
baseImage,
|
||||||
|
baseBuildImage,
|
||||||
|
deploymentType,
|
||||||
|
gitCommitHash,
|
||||||
|
dockerRegistry
|
||||||
|
} = application;
|
||||||
|
|
||||||
|
let {
|
||||||
|
branch,
|
||||||
|
repository,
|
||||||
|
buildPack,
|
||||||
|
port,
|
||||||
|
installCommand,
|
||||||
|
buildCommand,
|
||||||
|
startCommand,
|
||||||
|
baseDirectory,
|
||||||
|
publishDirectory,
|
||||||
|
dockerFileLocation,
|
||||||
|
dockerComposeFileLocation,
|
||||||
|
dockerComposeConfiguration,
|
||||||
|
denoMainFile
|
||||||
|
} = application;
|
||||||
|
|
||||||
|
let imageId = applicationId;
|
||||||
|
let domain = getDomain(fqdn);
|
||||||
|
|
||||||
|
let location = null;
|
||||||
|
|
||||||
|
let tag = null;
|
||||||
|
let customTag = null;
|
||||||
|
let imageName = null;
|
||||||
|
|
||||||
|
let imageFoundLocally = false;
|
||||||
|
let imageFoundRemotely = false;
|
||||||
|
|
||||||
|
if (pullmergeRequestId) {
|
||||||
|
const previewApplications = await prisma.previewApplication.findMany({
|
||||||
|
where: { applicationId: originalApplicationId, pullmergeRequestId }
|
||||||
|
});
|
||||||
|
if (previewApplications.length > 0) {
|
||||||
|
previewApplicationId = previewApplications[0].id;
|
||||||
|
}
|
||||||
|
// Previews, we need to get the source branch and set subdomain
|
||||||
|
branch = sourceBranch;
|
||||||
|
domain = `${pullmergeRequestId}.${domain}`;
|
||||||
|
imageId = `${applicationId}-${pullmergeRequestId}`;
|
||||||
|
repository = sourceRepository || repository;
|
||||||
|
}
|
||||||
|
const { workdir, repodir } = await createDirectories({ repository, buildId });
|
||||||
|
try {
|
||||||
|
if (queueBuild.status === 'running') {
|
||||||
|
await saveBuildLog({
|
||||||
|
line: 'Building halted, restarting...',
|
||||||
|
buildId,
|
||||||
|
applicationId: application.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const currentHash = crypto
|
const currentHash = crypto
|
||||||
.createHash('sha256')
|
.createHash('sha256')
|
||||||
.update(
|
.update(
|
||||||
@@ -104,31 +367,26 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
)
|
)
|
||||||
.digest('hex');
|
.digest('hex');
|
||||||
const { debug } = settings;
|
const { debug } = settings;
|
||||||
if (concurrency === 1) {
|
if (!debug) {
|
||||||
await prisma.build.updateMany({
|
await saveBuildLog({
|
||||||
where: {
|
line: `Debug logging is disabled. Enable it above if necessary!`,
|
||||||
status: { in: ['queued', 'running'] },
|
buildId,
|
||||||
id: { not: buildId },
|
applicationId
|
||||||
applicationId,
|
|
||||||
createdAt: { lt: new Date(new Date().getTime() - 10 * 1000) }
|
|
||||||
},
|
|
||||||
data: { status: 'failed' }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let imageId = applicationId;
|
|
||||||
let domain = getDomain(fqdn);
|
|
||||||
const volumes =
|
const volumes =
|
||||||
persistentStorage?.map((storage) => {
|
persistentStorage?.map((storage) => {
|
||||||
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${buildPack !== 'docker' ? '/app' : ''
|
if (storage.oldPath) {
|
||||||
}${storage.path}`;
|
return `${applicationId}${storage.path
|
||||||
|
.replace(/\//gi, '-')
|
||||||
|
.replace('-app', '')}:${storage.path}`;
|
||||||
|
}
|
||||||
|
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`;
|
||||||
}) || [];
|
}) || [];
|
||||||
// Previews, we need to get the source branch and set subdomain
|
|
||||||
if (pullmergeRequestId) {
|
|
||||||
branch = sourceBranch;
|
|
||||||
domain = `${pullmergeRequestId}.${domain}`;
|
|
||||||
imageId = `${applicationId}-${pullmergeRequestId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
dockerComposeConfiguration = JSON.parse(dockerComposeConfiguration);
|
||||||
|
} catch (error) {}
|
||||||
let deployNeeded = true;
|
let deployNeeded = true;
|
||||||
let destinationType;
|
let destinationType;
|
||||||
|
|
||||||
@@ -136,8 +394,11 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
destinationType = 'docker';
|
destinationType = 'docker';
|
||||||
}
|
}
|
||||||
if (destinationType === 'docker') {
|
if (destinationType === 'docker') {
|
||||||
await prisma.build.update({ where: { id: buildId }, data: { status: 'running' } });
|
await prisma.build.update({
|
||||||
const { workdir, repodir } = await createDirectories({ repository, buildId });
|
where: { id: buildId },
|
||||||
|
data: { status: 'running' }
|
||||||
|
});
|
||||||
|
|
||||||
const configuration = await setDefaultConfiguration(application);
|
const configuration = await setDefaultConfiguration(application);
|
||||||
|
|
||||||
buildPack = configuration.buildPack;
|
buildPack = configuration.buildPack;
|
||||||
@@ -146,8 +407,9 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
startCommand = configuration.startCommand;
|
startCommand = configuration.startCommand;
|
||||||
buildCommand = configuration.buildCommand;
|
buildCommand = configuration.buildCommand;
|
||||||
publishDirectory = configuration.publishDirectory;
|
publishDirectory = configuration.publishDirectory;
|
||||||
baseDirectory = configuration.baseDirectory;
|
baseDirectory = configuration.baseDirectory || '';
|
||||||
dockerFileLocation = configuration.dockerFileLocation;
|
dockerFileLocation = configuration.dockerFileLocation;
|
||||||
|
dockerComposeFileLocation = configuration.dockerComposeFileLocation;
|
||||||
denoMainFile = configuration.denoMainFile;
|
denoMainFile = configuration.denoMainFile;
|
||||||
const commit = await importers[gitSource.type]({
|
const commit = await importers[gitSource.type]({
|
||||||
applicationId,
|
applicationId,
|
||||||
@@ -157,6 +419,9 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
githubAppId: gitSource.githubApp?.id,
|
githubAppId: gitSource.githubApp?.id,
|
||||||
gitlabAppId: gitSource.gitlabApp?.id,
|
gitlabAppId: gitSource.gitlabApp?.id,
|
||||||
customPort: gitSource.customPort,
|
customPort: gitSource.customPort,
|
||||||
|
customUser: gitSource.customUser,
|
||||||
|
gitCommitHash,
|
||||||
|
configuration,
|
||||||
repository,
|
repository,
|
||||||
branch,
|
branch,
|
||||||
buildId,
|
buildId,
|
||||||
@@ -170,22 +435,35 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
if (!commit) {
|
if (!commit) {
|
||||||
throw new Error('No commit found?');
|
throw new Error('No commit found?');
|
||||||
}
|
}
|
||||||
let tag = commit.slice(0, 7);
|
tag = commit.slice(0, 7);
|
||||||
if (pullmergeRequestId) {
|
if (pullmergeRequestId) {
|
||||||
tag = `${commit.slice(0, 7)}-${pullmergeRequestId}`;
|
tag = `${commit.slice(0, 7)}-${pullmergeRequestId}`;
|
||||||
}
|
}
|
||||||
|
if (application.dockerRegistryImageName) {
|
||||||
|
imageName = application.dockerRegistryImageName.split(':')[0];
|
||||||
|
customTag = application.dockerRegistryImageName.split(':')[1] || tag;
|
||||||
|
} else {
|
||||||
|
customTag = tag;
|
||||||
|
imageName = applicationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pullmergeRequestId) {
|
||||||
|
customTag = `${customTag}-${pullmergeRequestId}`;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await prisma.build.update({ where: { id: buildId }, data: { commit } });
|
await prisma.build.update({ where: { id: buildId }, data: { commit } });
|
||||||
} catch (err) {
|
} catch (err) {}
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pullmergeRequestId) {
|
if (!pullmergeRequestId) {
|
||||||
if (configHash !== currentHash) {
|
if (configHash !== currentHash) {
|
||||||
deployNeeded = true;
|
deployNeeded = true;
|
||||||
if (configHash) {
|
if (configHash) {
|
||||||
await saveBuildLog({ line: 'Configuration changed.', buildId, applicationId });
|
await saveBuildLog({
|
||||||
|
line: 'Configuration changed',
|
||||||
|
buildId,
|
||||||
|
applicationId
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
deployNeeded = false;
|
deployNeeded = false;
|
||||||
@@ -194,29 +472,78 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
deployNeeded = true;
|
deployNeeded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let imageFound = false;
|
|
||||||
try {
|
try {
|
||||||
await executeDockerCmd({
|
await executeCommand({
|
||||||
dockerId: destinationDocker.id,
|
dockerId: destinationDocker.id,
|
||||||
command: `docker image inspect ${applicationId}:${tag}`
|
command: `docker image inspect ${applicationId}:${tag}`
|
||||||
})
|
});
|
||||||
imageFound = true;
|
imageFoundLocally = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId, baseImage);
|
if (dockerRegistry) {
|
||||||
|
const { url, username, password } = dockerRegistry;
|
||||||
|
location = await saveDockerRegistryCredentials({
|
||||||
|
url,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
workdir
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (forceRebuild) deployNeeded = true
|
try {
|
||||||
if (!imageFound || deployNeeded) {
|
await executeCommand({
|
||||||
// if (true) {
|
dockerId: destinationDocker.id,
|
||||||
|
command: `docker ${
|
||||||
|
location ? `--config ${location}` : ''
|
||||||
|
} pull ${imageName}:${customTag}`
|
||||||
|
});
|
||||||
|
imageFoundRemotely = true;
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
let imageFound = `${applicationId}:${tag}`;
|
||||||
|
if (imageFoundRemotely) {
|
||||||
|
imageFound = `${imageName}:${customTag}`;
|
||||||
|
}
|
||||||
|
await copyBaseConfigurationFiles(
|
||||||
|
buildPack,
|
||||||
|
workdir,
|
||||||
|
buildId,
|
||||||
|
applicationId,
|
||||||
|
baseImage
|
||||||
|
);
|
||||||
|
const labels = makeLabelForStandaloneApplication({
|
||||||
|
applicationId,
|
||||||
|
fqdn,
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
pullmergeRequestId,
|
||||||
|
buildPack,
|
||||||
|
repository,
|
||||||
|
branch,
|
||||||
|
projectId,
|
||||||
|
port: exposePort ? `${exposePort}:${port}` : port,
|
||||||
|
commit,
|
||||||
|
installCommand,
|
||||||
|
buildCommand,
|
||||||
|
startCommand,
|
||||||
|
baseDirectory,
|
||||||
|
publishDirectory
|
||||||
|
});
|
||||||
|
if (forceRebuild) deployNeeded = true;
|
||||||
|
if ((!imageFoundLocally && !imageFoundRemotely) || deployNeeded) {
|
||||||
if (buildpacks[buildPack])
|
if (buildpacks[buildPack])
|
||||||
await buildpacks[buildPack]({
|
await buildpacks[buildPack]({
|
||||||
dockerId: destinationDocker.id,
|
dockerId: destinationDocker.id,
|
||||||
|
network: destinationDocker.network,
|
||||||
buildId,
|
buildId,
|
||||||
applicationId,
|
applicationId,
|
||||||
domain,
|
domain,
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
|
volumes,
|
||||||
|
labels,
|
||||||
pullmergeRequestId,
|
pullmergeRequestId,
|
||||||
buildPack,
|
buildPack,
|
||||||
repository,
|
repository,
|
||||||
@@ -238,142 +565,245 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
pythonModule,
|
pythonModule,
|
||||||
pythonVariable,
|
pythonVariable,
|
||||||
dockerFileLocation,
|
dockerFileLocation,
|
||||||
|
dockerComposeConfiguration,
|
||||||
|
dockerComposeFileLocation,
|
||||||
denoMainFile,
|
denoMainFile,
|
||||||
denoOptions,
|
denoOptions,
|
||||||
baseImage,
|
baseImage,
|
||||||
baseBuildImage,
|
baseBuildImage,
|
||||||
deploymentType
|
deploymentType,
|
||||||
|
forceRebuild
|
||||||
});
|
});
|
||||||
else {
|
else {
|
||||||
await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId });
|
await saveBuildLog({
|
||||||
|
line: `Build pack ${buildPack} not found`,
|
||||||
|
buildId,
|
||||||
|
applicationId
|
||||||
|
});
|
||||||
throw new Error(`Build pack ${buildPack} not found.`);
|
throw new Error(`Build pack ${buildPack} not found.`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await saveBuildLog({ line: 'Build image already available - no rebuild required.', buildId, applicationId });
|
if (imageFoundRemotely || deployNeeded) {
|
||||||
|
await saveBuildLog({
|
||||||
|
line: `Container image ${imageFound} found in Docker Registry - reuising it`,
|
||||||
|
buildId,
|
||||||
|
applicationId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (imageFoundLocally || deployNeeded) {
|
||||||
|
await saveBuildLog({
|
||||||
|
line: `Container image ${imageFound} found locally - reuising it`,
|
||||||
|
buildId,
|
||||||
|
applicationId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker stop -t 0 ${imageId}` })
|
if (buildPack === 'compose') {
|
||||||
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker rm ${imageId}` })
|
const fileYaml = `${workdir}${baseDirectory}${dockerComposeFileLocation}`;
|
||||||
} catch (error) {
|
try {
|
||||||
//
|
const { stdout: containers } = await executeCommand({
|
||||||
}
|
dockerId: destinationDockerId,
|
||||||
const envs = [
|
command: `docker ps -a --filter 'label=coolify.applicationId=${applicationId}' --format {{.ID}}`
|
||||||
`PORT=${port}`
|
});
|
||||||
];
|
if (containers) {
|
||||||
if (secrets.length > 0) {
|
const containerArray = containers.split('\n');
|
||||||
secrets.forEach((secret) => {
|
if (containerArray.length > 0) {
|
||||||
if (pullmergeRequestId) {
|
for (const container of containerArray) {
|
||||||
if (secret.isPRMRSecret) {
|
await executeCommand({
|
||||||
envs.push(`${secret.name}=${secret.value}`);
|
dockerId: destinationDockerId,
|
||||||
}
|
command: `docker stop -t 0 ${container}`
|
||||||
} else {
|
});
|
||||||
if (!secret.isPRMRSecret) {
|
await executeCommand({
|
||||||
envs.push(`${secret.name}=${secret.value}`);
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker rm --force ${container}`
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
} catch (error) {
|
||||||
}
|
//
|
||||||
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
|
}
|
||||||
const labels = makeLabelForStandaloneApplication({
|
try {
|
||||||
applicationId,
|
await executeCommand({
|
||||||
fqdn,
|
debug,
|
||||||
name,
|
buildId,
|
||||||
type,
|
applicationId,
|
||||||
pullmergeRequestId,
|
dockerId: destinationDocker.id,
|
||||||
buildPack,
|
command: `docker compose --project-directory ${workdir} -f ${fileYaml} up -d`
|
||||||
repository,
|
});
|
||||||
branch,
|
await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId });
|
||||||
projectId,
|
await prisma.build.update({
|
||||||
port: exposePort ? `${exposePort}:${port}` : port,
|
where: { id: buildId },
|
||||||
commit,
|
data: { status: 'success' }
|
||||||
installCommand,
|
});
|
||||||
buildCommand,
|
await prisma.application.update({
|
||||||
startCommand,
|
where: { id: applicationId },
|
||||||
baseDirectory,
|
data: { configHash: currentHash }
|
||||||
publishDirectory
|
});
|
||||||
});
|
} catch (error) {
|
||||||
let envFound = false;
|
await saveBuildLog({ line: error, buildId, applicationId });
|
||||||
try {
|
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } });
|
||||||
envFound = !!(await fs.stat(`${workdir}/.env`));
|
if (foundBuild) {
|
||||||
} catch (error) {
|
await prisma.build.update({
|
||||||
//
|
where: { id: buildId },
|
||||||
}
|
data: {
|
||||||
try {
|
status: 'failed'
|
||||||
await saveBuildLog({ line: 'Deployment started.', buildId, applicationId });
|
}
|
||||||
const composeVolumes = volumes.map((volume) => {
|
});
|
||||||
return {
|
}
|
||||||
[`${volume.split(':')[0]}`]: {
|
throw new Error(error);
|
||||||
name: volume.split(':')[0]
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const { stdout: containers } = await executeCommand({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker ps -a --filter 'label=com.docker.compose.service=${
|
||||||
|
pullmergeRequestId ? imageId : applicationId
|
||||||
|
}' --format {{.ID}}`
|
||||||
|
});
|
||||||
|
if (containers) {
|
||||||
|
const containerArray = containers.split('\n');
|
||||||
|
if (containerArray.length > 0) {
|
||||||
|
for (const container of containerArray) {
|
||||||
|
await executeCommand({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker stop -t 0 ${container}`
|
||||||
|
});
|
||||||
|
await executeCommand({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker rm --force ${container}`
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
let envs = [];
|
||||||
|
if (secrets.length > 0) {
|
||||||
|
envs = [
|
||||||
|
...envs,
|
||||||
|
...generateSecrets(secrets, pullmergeRequestId, false, port)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (dockerRegistry) {
|
||||||
|
const { url, username, password } = dockerRegistry;
|
||||||
|
await saveDockerRegistryCredentials({ url, username, password, workdir });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const composeVolumes = volumes.map((volume) => {
|
||||||
|
return {
|
||||||
|
[`${volume.split(':')[0]}`]: {
|
||||||
|
name: volume.split(':')[0]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const composeFile = {
|
||||||
|
version: '3.8',
|
||||||
|
services: {
|
||||||
|
[imageId]: {
|
||||||
|
image: imageFound,
|
||||||
|
container_name: imageId,
|
||||||
|
volumes,
|
||||||
|
environment: envs,
|
||||||
|
labels,
|
||||||
|
depends_on: [],
|
||||||
|
expose: [port],
|
||||||
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
|
...defaultComposeConfiguration(destinationDocker.network)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
networks: {
|
||||||
|
[destinationDocker.network]: {
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: Object.assign({}, ...composeVolumes)
|
||||||
};
|
};
|
||||||
});
|
await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile));
|
||||||
const composeFile = {
|
await executeCommand({
|
||||||
version: '3.8',
|
debug,
|
||||||
services: {
|
dockerId: destinationDocker.id,
|
||||||
[imageId]: {
|
command: `docker compose --project-directory ${workdir} -f ${workdir}/docker-compose.yml up -d`
|
||||||
image: `${applicationId}:${tag}`,
|
});
|
||||||
container_name: imageId,
|
await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId });
|
||||||
volumes,
|
} catch (error) {
|
||||||
env_file: envFound ? [`${workdir}/.env`] : [],
|
await saveBuildLog({ line: error, buildId, applicationId });
|
||||||
labels,
|
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } });
|
||||||
depends_on: [],
|
if (foundBuild) {
|
||||||
expose: [port],
|
await prisma.build.update({
|
||||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
where: { id: buildId },
|
||||||
// logging: {
|
data: {
|
||||||
// driver: 'fluentd',
|
status: 'failed'
|
||||||
// },
|
}
|
||||||
...defaultComposeConfiguration(destinationDocker.network),
|
});
|
||||||
}
|
}
|
||||||
},
|
throw new Error(error);
|
||||||
networks: {
|
}
|
||||||
[destinationDocker.network]: {
|
|
||||||
external: true
|
if (!pullmergeRequestId)
|
||||||
}
|
await prisma.application.update({
|
||||||
},
|
where: { id: applicationId },
|
||||||
volumes: Object.assign({}, ...composeVolumes)
|
data: { configHash: currentHash }
|
||||||
};
|
});
|
||||||
await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile));
|
|
||||||
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose --project-directory ${workdir} up -d` })
|
|
||||||
await saveBuildLog({ line: 'Deployment successful!', buildId, applicationId });
|
|
||||||
} catch (error) {
|
|
||||||
await saveBuildLog({ line: error, buildId, applicationId });
|
|
||||||
await prisma.build.updateMany({
|
|
||||||
where: { id: buildId, status: { in: ['queued', 'running'] } },
|
|
||||||
data: { status: 'failed' }
|
|
||||||
});
|
|
||||||
throw new Error(error);
|
|
||||||
}
|
}
|
||||||
await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
|
}
|
||||||
await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } });
|
} catch (error) {
|
||||||
if (!pullmergeRequestId) await prisma.application.update({
|
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } });
|
||||||
where: { id: applicationId },
|
if (foundBuild) {
|
||||||
data: { configHash: currentHash }
|
await prisma.build.update({
|
||||||
|
where: { id: buildId },
|
||||||
|
data: {
|
||||||
|
status: 'failed'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (error !== 1) {
|
||||||
|
await saveBuildLog({ line: error, buildId, applicationId: application.id });
|
||||||
|
}
|
||||||
|
if (error instanceof Error) {
|
||||||
|
await saveBuildLog({
|
||||||
|
line: error.message,
|
||||||
|
buildId,
|
||||||
|
applicationId: application.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await fs.rm(workdir, { recursive: true, force: true });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
catch (error) {
|
try {
|
||||||
await prisma.build.updateMany({
|
if (application.dockerRegistryImageName && (!imageFoundRemotely || forceRebuild)) {
|
||||||
where: { id: buildId, status: { in: ['queued', 'running'] } },
|
await saveBuildLog({
|
||||||
data: { status: 'failed' }
|
line: `Pushing ${imageName}:${customTag} to Docker Registry... It could take a while...`,
|
||||||
});
|
buildId,
|
||||||
await saveBuildLog({ line: error, buildId, applicationId: application.id });
|
applicationId: application.id
|
||||||
|
});
|
||||||
|
await pushToRegistry(application, workdir, tag, imageName, customTag);
|
||||||
|
await saveBuildLog({ line: 'Success', buildId, applicationId: application.id });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.stdout) {
|
||||||
|
await saveBuildLog({ line: error.stdout, buildId, applicationId });
|
||||||
|
}
|
||||||
|
if (error.stderr) {
|
||||||
|
await saveBuildLog({ line: error.stderr, buildId, applicationId });
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await fs.rm(workdir, { recursive: true, force: true });
|
||||||
|
await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
await pAll.default(actions, { concurrency });
|
||||||
await pAll.default(actions, { concurrency })
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
} finally {
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
await th()
|
await th();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else process.exit(0);
|
} else process.exit(0);
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -1,216 +0,0 @@
|
|||||||
import { parentPort } from 'node:worker_threads';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { compareVersions } from 'compare-versions';
|
|
||||||
import { asyncExecShell, cleanupDockerStorage, executeDockerCmd, isDev, prisma, startTraefikTCPProxy, generateDatabaseConfiguration, startTraefikProxy, listSettings, version } from '../lib/common';
|
|
||||||
|
|
||||||
async function autoUpdater() {
|
|
||||||
try {
|
|
||||||
const currentVersion = version;
|
|
||||||
const { data: versions } = await axios
|
|
||||||
.get(
|
|
||||||
`https://get.coollabs.io/versions.json`
|
|
||||||
, {
|
|
||||||
params: {
|
|
||||||
appId: process.env['COOLIFY_APP_ID'] || undefined,
|
|
||||||
version: currentVersion
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const latestVersion = versions['coolify'].main.version;
|
|
||||||
const isUpdateAvailable = compareVersions(latestVersion, currentVersion);
|
|
||||||
if (isUpdateAvailable === 1) {
|
|
||||||
const activeCount = 0
|
|
||||||
if (activeCount === 0) {
|
|
||||||
if (!isDev) {
|
|
||||||
console.log(`Updating Coolify to ${latestVersion}.`);
|
|
||||||
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
|
|
||||||
await asyncExecShell(`env | grep COOLIFY > .env`);
|
|
||||||
await asyncExecShell(
|
|
||||||
`sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=true' .env`
|
|
||||||
);
|
|
||||||
await asyncExecShell(
|
|
||||||
`docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify && docker rm coolify && docker compose up -d --force-recreate"`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.log('Updating (not really in dev mode).');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function checkProxies() {
|
|
||||||
try {
|
|
||||||
const { default: isReachable } = await import('is-port-reachable');
|
|
||||||
let portReachable;
|
|
||||||
|
|
||||||
const { arch, ipv4, ipv6 } = await listSettings();
|
|
||||||
// Coolify Proxy local
|
|
||||||
const engine = '/var/run/docker.sock';
|
|
||||||
const localDocker = await prisma.destinationDocker.findFirst({
|
|
||||||
where: { engine, network: 'coolify' }
|
|
||||||
});
|
|
||||||
if (localDocker && localDocker.isCoolifyProxyUsed) {
|
|
||||||
portReachable = await isReachable(80, { host: ipv4 || ipv6 })
|
|
||||||
if (!portReachable) {
|
|
||||||
await startTraefikProxy(localDocker.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TCP Proxies
|
|
||||||
const databasesWithPublicPort = await prisma.database.findMany({
|
|
||||||
where: { publicPort: { not: null } },
|
|
||||||
include: { settings: true, destinationDocker: true }
|
|
||||||
});
|
|
||||||
for (const database of databasesWithPublicPort) {
|
|
||||||
const { destinationDockerId, destinationDocker, publicPort, id } = database;
|
|
||||||
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
|
|
||||||
const { privatePort } = generateDatabaseConfiguration(database, arch);
|
|
||||||
portReachable = await isReachable(publicPort, { host: destinationDocker.remoteIpAddress || ipv4 || ipv6 })
|
|
||||||
if (!portReachable) {
|
|
||||||
await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const wordpressWithFtp = await prisma.wordpress.findMany({
|
|
||||||
where: { ftpPublicPort: { not: null } },
|
|
||||||
include: { service: { include: { destinationDocker: true } } }
|
|
||||||
});
|
|
||||||
for (const ftp of wordpressWithFtp) {
|
|
||||||
const { service, ftpPublicPort } = ftp;
|
|
||||||
const { destinationDockerId, destinationDocker, id } = service;
|
|
||||||
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
|
|
||||||
portReachable = await isReachable(ftpPublicPort, { host: destinationDocker.remoteIpAddress || ipv4 || ipv6 })
|
|
||||||
if (!portReachable) {
|
|
||||||
await startTraefikTCPProxy(destinationDocker, id, ftpPublicPort, 22, 'wordpressftp');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP Proxies
|
|
||||||
const minioInstances = await prisma.minio.findMany({
|
|
||||||
where: { publicPort: { not: null } },
|
|
||||||
include: { service: { include: { destinationDocker: true } } }
|
|
||||||
});
|
|
||||||
for (const minio of minioInstances) {
|
|
||||||
const { service, publicPort } = minio;
|
|
||||||
const { destinationDockerId, destinationDocker, id } = service;
|
|
||||||
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
|
|
||||||
portReachable = await isReachable(publicPort, { host: destinationDocker.remoteIpAddress || ipv4 || ipv6 })
|
|
||||||
if (!portReachable) {
|
|
||||||
await startTraefikTCPProxy(destinationDocker, id, publicPort, 9000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function cleanupPrismaEngines() {
|
|
||||||
if (!isDev) {
|
|
||||||
try {
|
|
||||||
const { stdout } = await asyncExecShell(`ps -ef | grep /app/prisma-engines/query-engine | grep -v grep | wc -l | xargs`)
|
|
||||||
if (stdout.trim() != null && stdout.trim() != '' && Number(stdout.trim()) > 1) {
|
|
||||||
await asyncExecShell(`killall -q -e /app/prisma-engines/query-engine -o 1m`)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function cleanupStorage() {
|
|
||||||
const destinationDockers = await prisma.destinationDocker.findMany();
|
|
||||||
let enginesDone = new Set()
|
|
||||||
for (const destination of destinationDockers) {
|
|
||||||
if (enginesDone.has(destination.engine) || enginesDone.has(destination.remoteIpAddress)) return
|
|
||||||
if (destination.engine) enginesDone.add(destination.engine)
|
|
||||||
if (destination.remoteIpAddress) enginesDone.add(destination.remoteIpAddress)
|
|
||||||
|
|
||||||
let lowDiskSpace = false;
|
|
||||||
try {
|
|
||||||
let stdout = null
|
|
||||||
if (!isDev) {
|
|
||||||
const output = await executeDockerCmd({ dockerId: destination.id, command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'` })
|
|
||||||
stdout = output.stdout;
|
|
||||||
} else {
|
|
||||||
const output = await asyncExecShell(
|
|
||||||
`df -kPT /`
|
|
||||||
);
|
|
||||||
stdout = output.stdout;
|
|
||||||
}
|
|
||||||
let lines = stdout.trim().split('\n');
|
|
||||||
let header = lines[0];
|
|
||||||
let regex =
|
|
||||||
/^Filesystem\s+|Type\s+|1024-blocks|\s+Used|\s+Available|\s+Capacity|\s+Mounted on\s*$/g;
|
|
||||||
const boundaries = [];
|
|
||||||
let match;
|
|
||||||
|
|
||||||
while ((match = regex.exec(header))) {
|
|
||||||
boundaries.push(match[0].length);
|
|
||||||
}
|
|
||||||
|
|
||||||
boundaries[boundaries.length - 1] = -1;
|
|
||||||
const data = lines.slice(1).map((line) => {
|
|
||||||
const cl = boundaries.map((boundary) => {
|
|
||||||
const column = boundary > 0 ? line.slice(0, boundary) : line;
|
|
||||||
line = line.slice(boundary);
|
|
||||||
return column.trim();
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
capacity: Number.parseInt(cl[5], 10) / 100
|
|
||||||
};
|
|
||||||
});
|
|
||||||
if (data.length > 0) {
|
|
||||||
const { capacity } = data[0];
|
|
||||||
if (capacity > 0.8) {
|
|
||||||
lowDiskSpace = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
await cleanupDockerStorage(destination.id, lowDiskSpace, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
let status = {
|
|
||||||
cleanupStorage: false,
|
|
||||||
autoUpdater: false
|
|
||||||
}
|
|
||||||
if (parentPort) {
|
|
||||||
parentPort.on('message', async (message) => {
|
|
||||||
if (parentPort) {
|
|
||||||
if (message === 'error') throw new Error('oops');
|
|
||||||
if (message === 'cancel') {
|
|
||||||
parentPort.postMessage('cancelled');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
if (message === 'action:cleanupStorage') {
|
|
||||||
if (!status.autoUpdater) {
|
|
||||||
status.cleanupStorage = true
|
|
||||||
await cleanupStorage();
|
|
||||||
status.cleanupStorage = false
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (message === 'action:cleanupPrismaEngines') {
|
|
||||||
await cleanupPrismaEngines();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (message === 'action:checkProxies') {
|
|
||||||
await checkProxies();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (message === 'action:autoUpdater') {
|
|
||||||
if (!status.cleanupStorage) {
|
|
||||||
status.autoUpdater = true
|
|
||||||
await autoUpdater();
|
|
||||||
status.autoUpdater = false
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else process.exit(0);
|
|
||||||
})();
|
|
||||||
530
apps/api/src/lib.ts
Normal file
530
apps/api/src/lib.ts
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
import cuid from "cuid";
|
||||||
|
import { decrypt, encrypt, fixType, generatePassword, generateToken, prisma } from "./lib/common";
|
||||||
|
import { getTemplates } from "./lib/services";
|
||||||
|
|
||||||
|
export async function migrateApplicationPersistentStorage() {
|
||||||
|
const settings = await prisma.setting.findFirst()
|
||||||
|
if (settings) {
|
||||||
|
const { id: settingsId, applicationStoragePathMigrationFinished } = settings
|
||||||
|
try {
|
||||||
|
if (!applicationStoragePathMigrationFinished) {
|
||||||
|
const applications = await prisma.application.findMany({ include: { persistentStorage: true } });
|
||||||
|
for (const application of applications) {
|
||||||
|
if (application.persistentStorage && application.persistentStorage.length > 0 && application?.buildPack !== 'docker') {
|
||||||
|
for (const storage of application.persistentStorage) {
|
||||||
|
let { id, path } = storage
|
||||||
|
if (!path.startsWith('/app')) {
|
||||||
|
path = `/app${path}`
|
||||||
|
await prisma.applicationPersistentStorage.update({ where: { id }, data: { path, oldPath: true } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
} finally {
|
||||||
|
await prisma.setting.update({ where: { id: settingsId }, data: { applicationStoragePathMigrationFinished: true } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function migrateServicesToNewTemplate() {
|
||||||
|
// This function migrates old hardcoded services to the new template based services
|
||||||
|
try {
|
||||||
|
let templates = await getTemplates()
|
||||||
|
const services: any = await prisma.service.findMany({
|
||||||
|
include: {
|
||||||
|
destinationDocker: true,
|
||||||
|
persistentStorage: true,
|
||||||
|
serviceSecret: true,
|
||||||
|
serviceSetting: true,
|
||||||
|
minio: true,
|
||||||
|
plausibleAnalytics: true,
|
||||||
|
vscodeserver: true,
|
||||||
|
wordpress: true,
|
||||||
|
ghost: true,
|
||||||
|
meiliSearch: true,
|
||||||
|
umami: true,
|
||||||
|
hasura: true,
|
||||||
|
fider: true,
|
||||||
|
moodle: true,
|
||||||
|
appwrite: true,
|
||||||
|
glitchTip: true,
|
||||||
|
searxng: true,
|
||||||
|
weblate: true,
|
||||||
|
taiga: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
for (const service of services) {
|
||||||
|
try {
|
||||||
|
const { id } = service
|
||||||
|
if (!service.type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let template = templates.find(t => fixType(t.type) === fixType(service.type));
|
||||||
|
if (template) {
|
||||||
|
template = JSON.parse(JSON.stringify(template).replaceAll('$$id', service.id))
|
||||||
|
if (service.type === 'plausibleanalytics' && service.plausibleAnalytics) await plausibleAnalytics(service, template)
|
||||||
|
if (service.type === 'fider' && service.fider) await fider(service, template)
|
||||||
|
if (service.type === 'minio' && service.minio) await minio(service, template)
|
||||||
|
if (service.type === 'vscodeserver' && service.vscodeserver) await vscodeserver(service, template)
|
||||||
|
if (service.type === 'wordpress' && service.wordpress) await wordpress(service, template)
|
||||||
|
if (service.type === 'ghost' && service.ghost) await ghost(service, template)
|
||||||
|
if (service.type === 'meilisearch' && service.meiliSearch) await meilisearch(service, template)
|
||||||
|
if (service.type === 'umami' && service.umami) await umami(service, template)
|
||||||
|
if (service.type === 'hasura' && service.hasura) await hasura(service, template)
|
||||||
|
if (service.type === 'glitchTip' && service.glitchTip) await glitchtip(service, template)
|
||||||
|
if (service.type === 'searxng' && service.searxng) await searxng(service, template)
|
||||||
|
if (service.type === 'weblate' && service.weblate) await weblate(service, template)
|
||||||
|
if (service.type === 'appwrite' && service.appwrite) await appwrite(service, template)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await createVolumes(service, template);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
if (template.variables) {
|
||||||
|
if (template.variables.length > 0) {
|
||||||
|
for (const variable of template.variables) {
|
||||||
|
const { defaultValue } = variable;
|
||||||
|
const regex = /^\$\$.*\((\d+)\)$/g;
|
||||||
|
const length = Number(regex.exec(defaultValue)?.[1]) || undefined
|
||||||
|
if (variable.defaultValue.startsWith('$$generate_password')) {
|
||||||
|
variable.value = generatePassword({ length });
|
||||||
|
} else if (variable.defaultValue.startsWith('$$generate_hex')) {
|
||||||
|
variable.value = generatePassword({ length, isHex: true });
|
||||||
|
} else if (variable.defaultValue.startsWith('$$generate_username')) {
|
||||||
|
variable.value = cuid();
|
||||||
|
} else if (variable.defaultValue.startsWith('$$generate_token')) {
|
||||||
|
variable.value = generateToken()
|
||||||
|
} else {
|
||||||
|
variable.value = variable.defaultValue || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const variable of template.variables) {
|
||||||
|
if (variable.id.startsWith('$$secret_')) {
|
||||||
|
const found = await prisma.serviceSecret.findFirst({ where: { name: variable.name, serviceId: id } })
|
||||||
|
if (!found) {
|
||||||
|
await prisma.serviceSecret.create({
|
||||||
|
data: { name: variable.name, value: encrypt(variable.value) || '', service: { connect: { id } } }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (variable.id.startsWith('$$config_')) {
|
||||||
|
const found = await prisma.serviceSetting.findFirst({ where: { name: variable.name, serviceId: id } })
|
||||||
|
if (!found) {
|
||||||
|
await prisma.serviceSetting.create({
|
||||||
|
data: { name: variable.name, value: variable.value.toString(), variableName: variable.id, service: { connect: { id } } }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const s of Object.keys(template.services)) {
|
||||||
|
if (service.type === 'plausibleanalytics') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (template.services[s].volumes) {
|
||||||
|
for (const volume of template.services[s].volumes) {
|
||||||
|
const [volumeName, path] = volume.split(':')
|
||||||
|
if (!volumeName.startsWith('/')) {
|
||||||
|
const found = await prisma.servicePersistentStorage.findFirst({ where: { volumeName, serviceId: id } })
|
||||||
|
if (!found) {
|
||||||
|
await prisma.servicePersistentStorage.create({
|
||||||
|
data: { volumeName, path, containerId: s, predefined: true, service: { connect: { id } } }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await prisma.service.update({ where: { id }, data: { templateVersion: template.templateVersion } })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function appwrite(service: any, template: any) {
|
||||||
|
const { opensslKeyV1, executorSecret, mariadbHost, mariadbPort, mariadbUser, mariadbPassword, mariadbRootUserPassword, mariadbDatabase } = service.appwrite
|
||||||
|
|
||||||
|
const secrets = [
|
||||||
|
`_APP_EXECUTOR_SECRET@@@${executorSecret}`,
|
||||||
|
`_APP_OPENSSL_KEY_V1@@@${opensslKeyV1}`,
|
||||||
|
`_APP_DB_PASS@@@${mariadbPassword}`,
|
||||||
|
`_APP_DB_ROOT_PASS@@@${mariadbRootUserPassword}`,
|
||||||
|
]
|
||||||
|
|
||||||
|
const settings = [
|
||||||
|
`_APP_DB_HOST@@@${mariadbHost}`,
|
||||||
|
`_APP_DB_PORT@@@${mariadbPort}`,
|
||||||
|
`_APP_DB_USER@@@${mariadbUser}`,
|
||||||
|
`_APP_DB_SCHEMA@@@${mariadbDatabase}`,
|
||||||
|
]
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
await migrateSettings(settings, service, template);
|
||||||
|
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { appwrite: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function weblate(service: any, template: any) {
|
||||||
|
const { adminPassword, postgresqlUser, postgresqlPassword, postgresqlDatabase } = service.weblate
|
||||||
|
|
||||||
|
const secrets = [
|
||||||
|
`WEBLATE_ADMIN_PASSWORD@@@${adminPassword}`,
|
||||||
|
`POSTGRES_PASSWORD@@@${postgresqlPassword}`,
|
||||||
|
]
|
||||||
|
|
||||||
|
const settings = [
|
||||||
|
`WEBLATE_SITE_DOMAIN@@@$$generate_domain`,
|
||||||
|
`POSTGRES_USER@@@${postgresqlUser}`,
|
||||||
|
`POSTGRES_DATABASE@@@${postgresqlDatabase}`,
|
||||||
|
`POSTGRES_DB@@@${postgresqlDatabase}`,
|
||||||
|
`POSTGRES_HOST@@@$$id-postgres`,
|
||||||
|
`POSTGRES_PORT@@@5432`,
|
||||||
|
`REDIS_HOST@@@$$id-redis`,
|
||||||
|
]
|
||||||
|
await migrateSettings(settings, service, template);
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { weblate: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function searxng(service: any, template: any) {
|
||||||
|
const { secretKey, redisPassword } = service.searxng
|
||||||
|
|
||||||
|
const secrets = [
|
||||||
|
`SECRET_KEY@@@${secretKey}`,
|
||||||
|
`REDIS_PASSWORD@@@${redisPassword}`,
|
||||||
|
]
|
||||||
|
|
||||||
|
const settings = [
|
||||||
|
`SEARXNG_BASE_URL@@@$$generate_fqdn`
|
||||||
|
]
|
||||||
|
await migrateSettings(settings, service, template);
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { searxng: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function glitchtip(service: any, template: any) {
|
||||||
|
const { postgresqlUser, postgresqlPassword, postgresqlDatabase, secretKeyBase, defaultEmail, defaultUsername, defaultPassword, defaultEmailFrom, emailSmtpHost, emailSmtpPort, emailSmtpUser, emailSmtpPassword, emailSmtpUseTls, emailSmtpUseSsl, emailBackend, mailgunApiKey, sendgridApiKey, enableOpenUserRegistration } = service.glitchTip
|
||||||
|
const { id } = service
|
||||||
|
|
||||||
|
const secrets = [
|
||||||
|
`POSTGRES_PASSWORD@@@${postgresqlPassword}`,
|
||||||
|
`SECRET_KEY@@@${secretKeyBase}`,
|
||||||
|
`MAILGUN_API_KEY@@@${mailgunApiKey}`,
|
||||||
|
`SENDGRID_API_KEY@@@${sendgridApiKey}`,
|
||||||
|
`DJANGO_SUPERUSER_PASSWORD@@@${defaultPassword}`,
|
||||||
|
emailSmtpUser && emailSmtpPassword && emailSmtpHost && emailSmtpPort && `EMAIL_URL@@@${encrypt(`smtp://${emailSmtpUser}:${decrypt(emailSmtpPassword)}@${emailSmtpHost}:${emailSmtpPort}`)} || ''`,
|
||||||
|
`DATABASE_URL@@@${encrypt(`postgres://${postgresqlUser}:${decrypt(postgresqlPassword)}@${id}-postgresql:5432/${postgresqlDatabase}`)}`,
|
||||||
|
`REDIS_URL@@@${encrypt(`redis://${id}-redis:6379`)}`
|
||||||
|
]
|
||||||
|
const settings = [
|
||||||
|
`POSTGRES_USER@@@${postgresqlUser}`,
|
||||||
|
`POSTGRES_DB@@@${postgresqlDatabase}`,
|
||||||
|
`DEFAULT_FROM_EMAIL@@@${defaultEmailFrom}`,
|
||||||
|
`EMAIL_USE_TLS@@@${emailSmtpUseTls}`,
|
||||||
|
`EMAIL_USE_SSL@@@${emailSmtpUseSsl}`,
|
||||||
|
`EMAIL_BACKEND@@@${emailBackend}`,
|
||||||
|
`ENABLE_OPEN_USER_REGISTRATION@@@${enableOpenUserRegistration}`,
|
||||||
|
`DJANGO_SUPERUSER_EMAIL@@@${defaultEmail}`,
|
||||||
|
`DJANGO_SUPERUSER_USERNAME@@@${defaultUsername}`,
|
||||||
|
]
|
||||||
|
await migrateSettings(settings, service, template);
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
|
await prisma.service.update({ where: { id: service.id }, data: { type: 'glitchtip' } })
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { glitchTip: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function hasura(service: any, template: any) {
|
||||||
|
const { postgresqlUser, postgresqlPassword, postgresqlDatabase, graphQLAdminPassword } = service.hasura
|
||||||
|
const { id } = service
|
||||||
|
|
||||||
|
const secrets = [
|
||||||
|
`HASURA_GRAPHQL_ADMIN_SECRET@@@${graphQLAdminPassword}`,
|
||||||
|
`HASURA_GRAPHQL_METADATA_DATABASE_URL@@@${encrypt(`postgres://${postgresqlUser}:${decrypt(postgresqlPassword)}@${id}-postgresql:5432/${postgresqlDatabase}`)}`,
|
||||||
|
`POSTGRES_PASSWORD@@@${postgresqlPassword}`,
|
||||||
|
]
|
||||||
|
const settings = [
|
||||||
|
`POSTGRES_USER@@@${postgresqlUser}`,
|
||||||
|
`POSTGRES_DB@@@${postgresqlDatabase}`,
|
||||||
|
]
|
||||||
|
await migrateSettings(settings, service, template);
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { hasura: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function umami(service: any, template: any) {
|
||||||
|
const { postgresqlUser, postgresqlPassword, postgresqlDatabase, umamiAdminPassword, hashSalt } = service.umami
|
||||||
|
const { id } = service
|
||||||
|
const secrets = [
|
||||||
|
`HASH_SALT@@@${hashSalt}`,
|
||||||
|
`POSTGRES_PASSWORD@@@${postgresqlPassword}`,
|
||||||
|
`ADMIN_PASSWORD@@@${umamiAdminPassword}`,
|
||||||
|
`DATABASE_URL@@@${encrypt(`postgres://${postgresqlUser}:${decrypt(postgresqlPassword)}@${id}-postgresql:5432/${postgresqlDatabase}`)}`,
|
||||||
|
]
|
||||||
|
const settings = [
|
||||||
|
`DATABASE_TYPE@@@postgresql`,
|
||||||
|
`POSTGRES_USER@@@${postgresqlUser}`,
|
||||||
|
`POSTGRES_DB@@@${postgresqlDatabase}`,
|
||||||
|
]
|
||||||
|
await migrateSettings(settings, service, template);
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
|
await prisma.service.update({ where: { id: service.id }, data: { type: "umami-postgresql" } })
|
||||||
|
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { umami: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function meilisearch(service: any, template: any) {
|
||||||
|
const { masterKey } = service.meiliSearch
|
||||||
|
|
||||||
|
const secrets = [
|
||||||
|
`MEILI_MASTER_KEY@@@${masterKey}`,
|
||||||
|
]
|
||||||
|
|
||||||
|
// await migrateSettings(settings, service, template);
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { meiliSearch: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function ghost(service: any, template: any) {
|
||||||
|
const { defaultEmail, defaultPassword, mariadbUser, mariadbPassword, mariadbRootUser, mariadbRootUserPassword, mariadbDatabase } = service.ghost
|
||||||
|
const { fqdn } = service
|
||||||
|
|
||||||
|
const isHttps = fqdn.startsWith('https://');
|
||||||
|
|
||||||
|
const secrets = [
|
||||||
|
`GHOST_PASSWORD@@@${defaultPassword}`,
|
||||||
|
`MARIADB_PASSWORD@@@${mariadbPassword}`,
|
||||||
|
`MARIADB_ROOT_PASSWORD@@@${mariadbRootUserPassword}`,
|
||||||
|
`GHOST_DATABASE_PASSWORD@@@${mariadbPassword}`,
|
||||||
|
]
|
||||||
|
const settings = [
|
||||||
|
`GHOST_EMAIL@@@${defaultEmail}`,
|
||||||
|
`GHOST_DATABASE_HOST@@@${service.id}-mariadb`,
|
||||||
|
`GHOST_DATABASE_USER@@@${mariadbUser}`,
|
||||||
|
`GHOST_DATABASE_NAME@@@${mariadbDatabase}`,
|
||||||
|
`GHOST_DATABASE_PORT_NUMBER@@@3306`,
|
||||||
|
`MARIADB_USER@@@${mariadbUser}`,
|
||||||
|
`MARIADB_DATABASE@@@${mariadbDatabase}`,
|
||||||
|
`MARIADB_ROOT_USER@@@${mariadbRootUser}`,
|
||||||
|
`GHOST_HOST@@@$$generate_domain`,
|
||||||
|
`url@@@$$generate_fqdn`,
|
||||||
|
`GHOST_ENABLE_HTTPS@@@${isHttps ? 'yes' : 'no'}`
|
||||||
|
]
|
||||||
|
await migrateSettings(settings, service, template);
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
|
await prisma.service.update({ where: { id: service.id }, data: { type: "ghost-mariadb" } })
|
||||||
|
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { ghost: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function wordpress(service: any, template: any) {
|
||||||
|
const { extraConfig, tablePrefix, ownMysql, mysqlHost, mysqlPort, mysqlUser, mysqlPassword, mysqlRootUser, mysqlRootUserPassword, mysqlDatabase, ftpEnabled, ftpUser, ftpPassword, ftpPublicPort, ftpHostKey, ftpHostKeyPrivate } = service.wordpress
|
||||||
|
|
||||||
|
let settings = []
|
||||||
|
let secrets = []
|
||||||
|
if (ownMysql) {
|
||||||
|
secrets = [
|
||||||
|
`WORDPRESS_DB_PASSWORD@@@${mysqlPassword}`,
|
||||||
|
ftpPassword && `COOLIFY_FTP_PASSWORD@@@${ftpPassword}`,
|
||||||
|
ftpHostKeyPrivate && `COOLIFY_FTP_HOST_KEY_PRIVATE@@@${ftpHostKeyPrivate}`,
|
||||||
|
ftpHostKey && `COOLIFY_FTP_HOST_KEY@@@${ftpHostKey}`,
|
||||||
|
]
|
||||||
|
settings = [
|
||||||
|
`WORDPRESS_CONFIG_EXTRA@@@${extraConfig}`,
|
||||||
|
`WORDPRESS_DB_HOST@@@${mysqlHost}`,
|
||||||
|
`WORDPRESS_DB_PORT@@@${mysqlPort}`,
|
||||||
|
`WORDPRESS_DB_USER@@@${mysqlUser}`,
|
||||||
|
`WORDPRESS_DB_NAME@@@${mysqlDatabase}`,
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
secrets = [
|
||||||
|
`MYSQL_ROOT_PASSWORD@@@${mysqlRootUserPassword}`,
|
||||||
|
`MYSQL_PASSWORD@@@${mysqlPassword}`,
|
||||||
|
ftpPassword && `COOLIFY_FTP_PASSWORD@@@${ftpPassword}`,
|
||||||
|
ftpHostKeyPrivate && `COOLIFY_FTP_HOST_KEY_PRIVATE@@@${ftpHostKeyPrivate}`,
|
||||||
|
ftpHostKey && `COOLIFY_FTP_HOST_KEY@@@${ftpHostKey}`,
|
||||||
|
]
|
||||||
|
settings = [
|
||||||
|
`MYSQL_ROOT_USER@@@${mysqlRootUser}`,
|
||||||
|
`MYSQL_USER@@@${mysqlUser}`,
|
||||||
|
`MYSQL_DATABASE@@@${mysqlDatabase}`,
|
||||||
|
`MYSQL_HOST@@@${service.id}-mysql`,
|
||||||
|
`MYSQL_PORT@@@${mysqlPort}`,
|
||||||
|
`WORDPRESS_CONFIG_EXTRA@@@${extraConfig}`,
|
||||||
|
`WORDPRESS_TABLE_PREFIX@@@${tablePrefix}`,
|
||||||
|
`WORDPRESS_DB_HOST@@@${service.id}-mysql`,
|
||||||
|
`COOLIFY_OWN_DB@@@${ownMysql}`,
|
||||||
|
`COOLIFY_FTP_ENABLED@@@${ftpEnabled}`,
|
||||||
|
`COOLIFY_FTP_USER@@@${ftpUser}`,
|
||||||
|
`COOLIFY_FTP_PUBLIC_PORT@@@${ftpPublicPort}`,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
await migrateSettings(settings, service, template);
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
if (ownMysql) {
|
||||||
|
await prisma.service.update({ where: { id: service.id }, data: { type: "wordpress-only" } })
|
||||||
|
}
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { wordpress: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function vscodeserver(service: any, template: any) {
|
||||||
|
const { password } = service.vscodeserver
|
||||||
|
|
||||||
|
const secrets = [
|
||||||
|
`PASSWORD@@@${password}`,
|
||||||
|
]
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { vscodeserver: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function minio(service: any, template: any) {
|
||||||
|
const { rootUser, rootUserPassword, apiFqdn } = service.minio
|
||||||
|
const secrets = [
|
||||||
|
`MINIO_ROOT_PASSWORD@@@${rootUserPassword}`,
|
||||||
|
]
|
||||||
|
const settings = [
|
||||||
|
`MINIO_ROOT_USER@@@${rootUser}`,
|
||||||
|
`MINIO_SERVER_URL@@@${apiFqdn}`,
|
||||||
|
`MINIO_BROWSER_REDIRECT_URL@@@$$generate_fqdn`,
|
||||||
|
`MINIO_DOMAIN@@@$$generate_domain`,
|
||||||
|
]
|
||||||
|
await migrateSettings(settings, service, template);
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { minio: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function fider(service: any, template: any) {
|
||||||
|
const { postgresqlUser, postgresqlPassword, postgresqlDatabase, jwtSecret, emailNoreply, emailMailgunApiKey, emailMailgunDomain, emailMailgunRegion, emailSmtpHost, emailSmtpPort, emailSmtpUser, emailSmtpPassword, emailSmtpEnableStartTls } = service.fider
|
||||||
|
const { id } = service
|
||||||
|
const secrets = [
|
||||||
|
`JWT_SECRET@@@${jwtSecret}`,
|
||||||
|
emailMailgunApiKey && `EMAIL_MAILGUN_API@@@${emailMailgunApiKey}`,
|
||||||
|
emailSmtpPassword && `EMAIL_SMTP_PASSWORD@@@${emailSmtpPassword}`,
|
||||||
|
`POSTGRES_PASSWORD@@@${postgresqlPassword}`,
|
||||||
|
`DATABASE_URL@@@${encrypt(`postgresql://${postgresqlUser}:${decrypt(postgresqlPassword)}@${id}-postgresql:5432/${postgresqlDatabase}?sslmode=disable`)}`
|
||||||
|
]
|
||||||
|
const settings = [
|
||||||
|
`BASE_URL@@@$$generate_fqdn`,
|
||||||
|
`EMAIL_NOREPLY@@@${emailNoreply || 'noreply@example.com'}`,
|
||||||
|
`EMAIL_MAILGUN_DOMAIN@@@${emailMailgunDomain || ''}`,
|
||||||
|
`EMAIL_MAILGUN_REGION@@@${emailMailgunRegion || ''}`,
|
||||||
|
`EMAIL_SMTP_HOST@@@${emailSmtpHost || ''}`,
|
||||||
|
`EMAIL_SMTP_PORT@@@${emailSmtpPort || 587}`,
|
||||||
|
`EMAIL_SMTP_USER@@@${emailSmtpUser || ''}`,
|
||||||
|
`EMAIL_SMTP_PASSWORD@@@${emailSmtpPassword || ''}`,
|
||||||
|
`EMAIL_SMTP_ENABLE_STARTTLS@@@${emailSmtpEnableStartTls || 'false'}`,
|
||||||
|
`POSTGRES_USER@@@${postgresqlUser}`,
|
||||||
|
`POSTGRES_DB@@@${postgresqlDatabase}`,
|
||||||
|
]
|
||||||
|
await migrateSettings(settings, service, template);
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { fider: { disconnect: true } } })
|
||||||
|
|
||||||
|
}
|
||||||
|
async function plausibleAnalytics(service: any, template: any) {
|
||||||
|
const { email, username, password, postgresqlUser, postgresqlPassword, postgresqlDatabase, secretKeyBase, scriptName } = service.plausibleAnalytics;
|
||||||
|
const { id } = service
|
||||||
|
|
||||||
|
const settings = [
|
||||||
|
`BASE_URL@@@$$generate_fqdn`,
|
||||||
|
`ADMIN_USER_EMAIL@@@${email}`,
|
||||||
|
`ADMIN_USER_NAME@@@${username}`,
|
||||||
|
`DISABLE_AUTH@@@false`,
|
||||||
|
`DISABLE_REGISTRATION@@@true`,
|
||||||
|
`POSTGRESQL_USERNAME@@@${postgresqlUser}`,
|
||||||
|
`POSTGRESQL_DATABASE@@@${postgresqlDatabase}`,
|
||||||
|
`SCRIPT_NAME@@@${scriptName}`,
|
||||||
|
]
|
||||||
|
const secrets = [
|
||||||
|
`ADMIN_USER_PWD@@@${password}`,
|
||||||
|
`SECRET_KEY_BASE@@@${secretKeyBase}`,
|
||||||
|
`POSTGRESQL_PASSWORD@@@${postgresqlPassword}`,
|
||||||
|
`DATABASE_URL@@@${encrypt(`postgres://${postgresqlUser}:${decrypt(postgresqlPassword)}@${id}-postgresql:5432/${postgresqlDatabase}`)}`,
|
||||||
|
]
|
||||||
|
await migrateSettings(settings, service, template);
|
||||||
|
await migrateSecrets(secrets, service);
|
||||||
|
|
||||||
|
// Disconnect old service data
|
||||||
|
// await prisma.service.update({ where: { id: service.id }, data: { plausibleAnalytics: { disconnect: true } } })
|
||||||
|
}
|
||||||
|
async function migrateSettings(settings: any[], service: any, template: any) {
|
||||||
|
for (const setting of settings) {
|
||||||
|
try {
|
||||||
|
if (!setting) continue;
|
||||||
|
let [name, value] = setting.split('@@@')
|
||||||
|
let minio = name
|
||||||
|
if (name === 'MINIO_SERVER_URL') {
|
||||||
|
name = 'coolify_fqdn_minio_console'
|
||||||
|
}
|
||||||
|
if (!value || value === 'null') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let variableName = template.variables.find((v: any) => v.name === name)?.id
|
||||||
|
if (!variableName) {
|
||||||
|
variableName = `$$config_${name.toLowerCase()}`
|
||||||
|
}
|
||||||
|
// console.log('Migrating setting', name, value, 'for service', service.id, ', service name:', service.name, 'variableName: ', variableName)
|
||||||
|
|
||||||
|
await prisma.serviceSetting.findFirst({ where: { name: minio, serviceId: service.id } }) || await prisma.serviceSetting.create({ data: { name: minio, value, variableName, service: { connect: { id: service.id } } } })
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function migrateSecrets(secrets: any[], service: any) {
|
||||||
|
for (const secret of secrets) {
|
||||||
|
try {
|
||||||
|
if (!secret) continue;
|
||||||
|
let [name, value] = secret.split('@@@')
|
||||||
|
if (!value || value === 'null') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// console.log('Migrating secret', name, value, 'for service', service.id, ', service name:', service.name)
|
||||||
|
await prisma.serviceSecret.findFirst({ where: { name, serviceId: service.id } }) || await prisma.serviceSecret.create({ data: { name, value, service: { connect: { id: service.id } } } })
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function createVolumes(service: any, template: any) {
|
||||||
|
const volumes = [];
|
||||||
|
for (const s of Object.keys(template.services)) {
|
||||||
|
if (template.services[s].volumes && template.services[s].volumes.length > 0) {
|
||||||
|
for (const volume of template.services[s].volumes) {
|
||||||
|
let volumeName = volume.split(':')[0]
|
||||||
|
const volumePath = volume.split(':')[1]
|
||||||
|
let volumeService = s
|
||||||
|
if (service.type === 'plausibleanalytics' && service.plausibleAnalytics?.id) {
|
||||||
|
let volumeId = volumeName.split('-')[0]
|
||||||
|
volumeName = volumeName.replace(volumeId, service.plausibleAnalytics.id)
|
||||||
|
}
|
||||||
|
volumes.push(`${volumeName}@@@${volumePath}@@@${volumeService}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const volume of volumes) {
|
||||||
|
const [volumeName, path, containerId] = volume.split('@@@')
|
||||||
|
// console.log('Creating volume', volumeName, path, containerId, 'for service', service.id, ', service name:', service.name)
|
||||||
|
await prisma.servicePersistentStorage.findFirst({ where: { volumeName, serviceId: service.id } }) || await prisma.servicePersistentStorage.create({ data: { volumeName, path, containerId, predefined: true, service: { connect: { id: service.id } } } })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,18 @@
|
|||||||
import { base64Encode, executeDockerCmd, generateTimestamp, getDomain, isDev, prisma, version } from "../common";
|
import {
|
||||||
|
base64Encode,
|
||||||
|
decrypt,
|
||||||
|
encrypt,
|
||||||
|
executeCommand,
|
||||||
|
generateSecrets,
|
||||||
|
generateTimestamp,
|
||||||
|
getDomain,
|
||||||
|
isARM,
|
||||||
|
isDev,
|
||||||
|
prisma,
|
||||||
|
version
|
||||||
|
} from '../common';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { day } from "../dayjs";
|
import { day } from '../dayjs';
|
||||||
|
|
||||||
const staticApps = ['static', 'react', 'vuejs', 'svelte', 'gatsby', 'astro', 'eleventy'];
|
const staticApps = ['static', 'react', 'vuejs', 'svelte', 'gatsby', 'astro', 'eleventy'];
|
||||||
const nodeBased = [
|
const nodeBased = [
|
||||||
@@ -17,7 +29,10 @@ const nodeBased = [
|
|||||||
'nextjs'
|
'nextjs'
|
||||||
];
|
];
|
||||||
|
|
||||||
export function setDefaultBaseImage(buildPack: string | null, deploymentType: string | null = null) {
|
export function setDefaultBaseImage(
|
||||||
|
buildPack: string | null,
|
||||||
|
deploymentType: string | null = null
|
||||||
|
) {
|
||||||
const nodeVersions = [
|
const nodeVersions = [
|
||||||
{
|
{
|
||||||
value: 'node:lts',
|
value: 'node:lts',
|
||||||
@@ -52,6 +67,14 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
|||||||
{
|
{
|
||||||
value: 'webdevops/apache:alpine',
|
value: 'webdevops/apache:alpine',
|
||||||
label: 'webdevops/apache:alpine'
|
label: 'webdevops/apache:alpine'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'nginx:alpine',
|
||||||
|
label: 'nginx:alpine'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'httpd:alpine',
|
||||||
|
label: 'httpd:alpine (Apache)'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const rustVersions = [
|
const rustVersions = [
|
||||||
@@ -89,6 +112,22 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
const phpVersions = [
|
const phpVersions = [
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-apache:8.2',
|
||||||
|
label: 'webdevops/php-apache:8.2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-nginx:8.2',
|
||||||
|
label: 'webdevops/php-nginx:8.2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-apache:8.1',
|
||||||
|
label: 'webdevops/php-apache:8.1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-nginx:8.1',
|
||||||
|
label: 'webdevops/php-nginx:8.1'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: 'webdevops/php-apache:8.0',
|
value: 'webdevops/php-apache:8.0',
|
||||||
label: 'webdevops/php-apache:8.0'
|
label: 'webdevops/php-apache:8.0'
|
||||||
@@ -145,6 +184,22 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
|||||||
value: 'webdevops/php-nginx:5.6',
|
value: 'webdevops/php-nginx:5.6',
|
||||||
label: 'webdevops/php-nginx:5.6'
|
label: 'webdevops/php-nginx:5.6'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-apache:8.2-alpine',
|
||||||
|
label: 'webdevops/php-apache:8.2-alpine'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-nginx:8.2-alpine',
|
||||||
|
label: 'webdevops/php-nginx:8.2-alpine'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-apache:8.1-alpine',
|
||||||
|
label: 'webdevops/php-apache:8.1-alpine'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-nginx:8.1-alpine',
|
||||||
|
label: 'webdevops/php-nginx:8.1-alpine'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: 'webdevops/php-apache:8.0-alpine',
|
value: 'webdevops/php-apache:8.0-alpine',
|
||||||
label: 'webdevops/php-apache:8.0-alpine'
|
label: 'webdevops/php-apache:8.0-alpine'
|
||||||
@@ -182,8 +237,20 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
|||||||
label: 'webdevops/php-apache:7.1-alpine'
|
label: 'webdevops/php-apache:7.1-alpine'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'webdevops/php-nginx:7.1-alpine',
|
value: 'php:8.1-fpm',
|
||||||
label: 'webdevops/php-nginx:7.1-alpine'
|
label: 'php:8.1-fpm'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'php:8.0-fpm',
|
||||||
|
label: 'php:8.0-fpm'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'php:8.1-fpm-alpine',
|
||||||
|
label: 'php:8.1-fpm-alpine'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'php:8.0-fpm-alpine',
|
||||||
|
label: 'php:8.0-fpm-alpine'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const pythonVersions = [
|
const pythonVersions = [
|
||||||
@@ -264,8 +331,8 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
|||||||
{
|
{
|
||||||
value: 'heroku/builder-classic:22',
|
value: 'heroku/builder-classic:22',
|
||||||
label: 'heroku/builder-classic:22'
|
label: 'heroku/builder-classic:22'
|
||||||
},
|
}
|
||||||
]
|
];
|
||||||
let payload: any = {
|
let payload: any = {
|
||||||
baseImage: null,
|
baseImage: null,
|
||||||
baseBuildImage: null,
|
baseBuildImage: null,
|
||||||
@@ -274,8 +341,10 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
|||||||
};
|
};
|
||||||
if (nodeBased.includes(buildPack)) {
|
if (nodeBased.includes(buildPack)) {
|
||||||
if (deploymentType === 'static') {
|
if (deploymentType === 'static') {
|
||||||
payload.baseImage = 'webdevops/nginx:alpine';
|
payload.baseImage = isARM(process.arch) ? 'nginx:alpine' : 'webdevops/nginx:alpine';
|
||||||
payload.baseImages = staticVersions;
|
payload.baseImages = isARM(process.arch)
|
||||||
|
? staticVersions.filter((version) => !version.value.includes('webdevops'))
|
||||||
|
: staticVersions;
|
||||||
payload.baseBuildImage = 'node:lts';
|
payload.baseBuildImage = 'node:lts';
|
||||||
payload.baseBuildImages = nodeVersions;
|
payload.baseBuildImages = nodeVersions;
|
||||||
} else {
|
} else {
|
||||||
@@ -286,8 +355,10 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (staticApps.includes(buildPack)) {
|
if (staticApps.includes(buildPack)) {
|
||||||
payload.baseImage = 'webdevops/nginx:alpine';
|
payload.baseImage = isARM(process.arch) ? 'nginx:alpine' : 'webdevops/nginx:alpine';
|
||||||
payload.baseImages = staticVersions;
|
payload.baseImages = isARM(process.arch)
|
||||||
|
? staticVersions.filter((version) => !version.value.includes('webdevops'))
|
||||||
|
: staticVersions;
|
||||||
payload.baseBuildImage = 'node:lts';
|
payload.baseBuildImage = 'node:lts';
|
||||||
payload.baseBuildImages = nodeVersions;
|
payload.baseBuildImages = nodeVersions;
|
||||||
}
|
}
|
||||||
@@ -305,18 +376,26 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
|||||||
payload.baseImage = 'denoland/deno:latest';
|
payload.baseImage = 'denoland/deno:latest';
|
||||||
}
|
}
|
||||||
if (buildPack === 'php') {
|
if (buildPack === 'php') {
|
||||||
payload.baseImage = 'webdevops/php-apache:8.0-alpine';
|
payload.baseImage = isARM(process.arch)
|
||||||
payload.baseImages = phpVersions;
|
? 'php:8.1-fpm-alpine'
|
||||||
|
: 'webdevops/php-apache:8.2-alpine';
|
||||||
|
payload.baseImages = isARM(process.arch)
|
||||||
|
? phpVersions.filter((version) => !version.value.includes('webdevops'))
|
||||||
|
: phpVersions;
|
||||||
}
|
}
|
||||||
if (buildPack === 'laravel') {
|
if (buildPack === 'laravel') {
|
||||||
payload.baseImage = 'webdevops/php-apache:8.0-alpine';
|
payload.baseImage = isARM(process.arch)
|
||||||
|
? 'php:8.1-fpm-alpine'
|
||||||
|
: 'webdevops/php-apache:8.2-alpine';
|
||||||
|
payload.baseImages = isARM(process.arch)
|
||||||
|
? phpVersions.filter((version) => !version.value.includes('webdevops'))
|
||||||
|
: phpVersions;
|
||||||
payload.baseBuildImage = 'node:18';
|
payload.baseBuildImage = 'node:18';
|
||||||
payload.baseBuildImages = nodeVersions;
|
payload.baseBuildImages = nodeVersions;
|
||||||
}
|
}
|
||||||
if (buildPack === 'heroku') {
|
if (buildPack === 'heroku') {
|
||||||
payload.baseImage = 'heroku/buildpacks:20';
|
payload.baseImage = 'heroku/buildpacks:20';
|
||||||
payload.baseImages = herokuVersions;
|
payload.baseImages = herokuVersions;
|
||||||
|
|
||||||
}
|
}
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
@@ -331,6 +410,7 @@ export const setDefaultConfiguration = async (data: any) => {
|
|||||||
publishDirectory,
|
publishDirectory,
|
||||||
baseDirectory,
|
baseDirectory,
|
||||||
dockerFileLocation,
|
dockerFileLocation,
|
||||||
|
dockerComposeFileLocation,
|
||||||
denoMainFile
|
denoMainFile
|
||||||
} = data;
|
} = data;
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
@@ -352,7 +432,8 @@ export const setDefaultConfiguration = async (data: any) => {
|
|||||||
if (!publishDirectory) publishDirectory = template?.publishDirectory || null;
|
if (!publishDirectory) publishDirectory = template?.publishDirectory || null;
|
||||||
if (baseDirectory) {
|
if (baseDirectory) {
|
||||||
if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`;
|
if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`;
|
||||||
if (!baseDirectory.endsWith('/')) baseDirectory = `${baseDirectory}/`;
|
if (baseDirectory.endsWith('/') && baseDirectory !== '/')
|
||||||
|
baseDirectory = baseDirectory.slice(0, -1);
|
||||||
}
|
}
|
||||||
if (dockerFileLocation) {
|
if (dockerFileLocation) {
|
||||||
if (!dockerFileLocation.startsWith('/')) dockerFileLocation = `/${dockerFileLocation}`;
|
if (!dockerFileLocation.startsWith('/')) dockerFileLocation = `/${dockerFileLocation}`;
|
||||||
@@ -360,6 +441,14 @@ export const setDefaultConfiguration = async (data: any) => {
|
|||||||
} else {
|
} else {
|
||||||
dockerFileLocation = '/Dockerfile';
|
dockerFileLocation = '/Dockerfile';
|
||||||
}
|
}
|
||||||
|
if (dockerComposeFileLocation) {
|
||||||
|
if (!dockerComposeFileLocation.startsWith('/'))
|
||||||
|
dockerComposeFileLocation = `/${dockerComposeFileLocation}`;
|
||||||
|
if (dockerComposeFileLocation.endsWith('/'))
|
||||||
|
dockerComposeFileLocation = dockerComposeFileLocation.slice(0, -1);
|
||||||
|
} else {
|
||||||
|
dockerComposeFileLocation = '/Dockerfile';
|
||||||
|
}
|
||||||
if (!denoMainFile) {
|
if (!denoMainFile) {
|
||||||
denoMainFile = 'main.ts';
|
denoMainFile = 'main.ts';
|
||||||
}
|
}
|
||||||
@@ -373,6 +462,7 @@ export const setDefaultConfiguration = async (data: any) => {
|
|||||||
publishDirectory,
|
publishDirectory,
|
||||||
baseDirectory,
|
baseDirectory,
|
||||||
dockerFileLocation,
|
dockerFileLocation,
|
||||||
|
dockerComposeFileLocation,
|
||||||
denoMainFile
|
denoMainFile
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -419,7 +509,6 @@ export const scanningTemplates = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const saveBuildLog = async ({
|
export const saveBuildLog = async ({
|
||||||
line,
|
line,
|
||||||
buildId,
|
buildId,
|
||||||
@@ -429,17 +518,46 @@ export const saveBuildLog = async ({
|
|||||||
buildId: string;
|
buildId: string;
|
||||||
applicationId: string;
|
applicationId: string;
|
||||||
}): Promise<any> => {
|
}): Promise<any> => {
|
||||||
|
if (buildId === 'undefined' || buildId === 'null' || !buildId) return;
|
||||||
|
if (applicationId === 'undefined' || applicationId === 'null' || !applicationId) return;
|
||||||
|
const { default: got } = await import('got');
|
||||||
|
if (typeof line === 'object' && line) {
|
||||||
|
if (line.shortMessage) {
|
||||||
|
line = line.shortMessage + '\n' + line.stderr;
|
||||||
|
} else {
|
||||||
|
line = JSON.stringify(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (line && typeof line === 'string' && line.includes('ghs_')) {
|
if (line && typeof line === 'string' && line.includes('ghs_')) {
|
||||||
const regex = /ghs_.*@/g;
|
const regex = /ghs_.*@/g;
|
||||||
line = line.replace(regex, '<SENSITIVE_DATA_DELETED>@');
|
line = line.replace(regex, '<SENSITIVE_DATA_DELETED>@');
|
||||||
}
|
}
|
||||||
const addTimestamp = `[${generateTimestamp()}] ${line}`;
|
const addTimestamp = `[${generateTimestamp()}] ${line}`;
|
||||||
if (isDev) console.debug(`[${applicationId}] ${addTimestamp}`);
|
const fluentBitUrl = isDev
|
||||||
return await prisma.buildLog.create({
|
? process.env.COOLIFY_CONTAINER_DEV === 'true'
|
||||||
data: {
|
? 'http://coolify-fluentbit:24224'
|
||||||
line: addTimestamp, buildId, time: Number(day().valueOf()), applicationId
|
: 'http://localhost:24224'
|
||||||
}
|
: 'http://coolify-fluentbit:24224';
|
||||||
});
|
|
||||||
|
if (isDev && !process.env.COOLIFY_CONTAINER_DEV) {
|
||||||
|
console.debug(`[${applicationId}] ${addTimestamp}`);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return await got.post(`${fluentBitUrl}/${applicationId}_buildlog_${buildId}.csv`, {
|
||||||
|
json: {
|
||||||
|
line: encrypt(line)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return await prisma.buildLog.create({
|
||||||
|
data: {
|
||||||
|
line: addTimestamp,
|
||||||
|
buildId,
|
||||||
|
time: Number(day().valueOf()),
|
||||||
|
applicationId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function copyBaseConfigurationFiles(
|
export async function copyBaseConfigurationFiles(
|
||||||
@@ -511,8 +629,8 @@ export async function copyBaseConfigurationFiles(
|
|||||||
`
|
`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// TODO: Add more configuration files for other buildpacks, like apache2, etc.
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -525,7 +643,29 @@ export function checkPnpm(installCommand = null, buildCommand = null, startComma
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function saveDockerRegistryCredentials({ url, username, password, workdir }) {
|
||||||
|
if (!username || !password) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let decryptedPassword = decrypt(password);
|
||||||
|
const location = `${workdir}/.docker`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.mkdir(`${workdir}/.docker`);
|
||||||
|
} catch (error) {
|
||||||
|
// console.log(error);
|
||||||
|
}
|
||||||
|
const payload = JSON.stringify({
|
||||||
|
auths: {
|
||||||
|
[url]: {
|
||||||
|
auth: Buffer.from(`${username}:${decryptedPassword}`).toString('base64')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await fs.writeFile(`${location}/config.json`, payload);
|
||||||
|
return location;
|
||||||
|
}
|
||||||
export async function buildImage({
|
export async function buildImage({
|
||||||
applicationId,
|
applicationId,
|
||||||
tag,
|
tag,
|
||||||
@@ -534,58 +674,52 @@ export async function buildImage({
|
|||||||
dockerId,
|
dockerId,
|
||||||
isCache = false,
|
isCache = false,
|
||||||
debug = false,
|
debug = false,
|
||||||
dockerFileLocation = '/Dockerfile'
|
dockerFileLocation = '/Dockerfile',
|
||||||
|
commit,
|
||||||
|
forceRebuild = false
|
||||||
}) {
|
}) {
|
||||||
if (isCache) {
|
if (isCache) {
|
||||||
await saveBuildLog({ line: `Building cache image started.`, buildId, applicationId });
|
await saveBuildLog({ line: `Building cache image...`, buildId, applicationId });
|
||||||
} else {
|
} else {
|
||||||
await saveBuildLog({ line: `Building image started.`, buildId, applicationId });
|
await saveBuildLog({ line: `Building production image...`, buildId, applicationId });
|
||||||
}
|
}
|
||||||
if (!debug && isCache) {
|
const dockerFile = isCache ? `${dockerFileLocation}-cache` : `${dockerFileLocation}`;
|
||||||
await saveBuildLog({
|
const cache = `${applicationId}:${tag}${isCache ? '-cache' : ''}`;
|
||||||
line: `Debug turned off. To see more details, allow it in the configuration.`,
|
let location = null;
|
||||||
buildId,
|
|
||||||
applicationId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const dockerFile = isCache ? `${dockerFileLocation}-cache` : `${dockerFileLocation}`
|
|
||||||
const cache = `${applicationId}:${tag}${isCache ? '-cache' : ''}`
|
|
||||||
await executeDockerCmd({ debug, buildId, applicationId, dockerId, command: `docker build --progress plain -f ${workdir}/${dockerFile} -t ${cache} ${workdir}` })
|
|
||||||
const { status } = await prisma.build.findUnique({ where: { id: buildId } })
|
|
||||||
if (status === 'canceled') {
|
|
||||||
throw new Error('Deployment canceled.')
|
|
||||||
}
|
|
||||||
if (isCache) {
|
|
||||||
await saveBuildLog({ line: `Building cache image successful.`, buildId, applicationId });
|
|
||||||
} else {
|
|
||||||
await saveBuildLog({ line: `Building image successful.`, buildId, applicationId });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function streamEvents({ stream, docker, buildId, applicationId, debug }) {
|
const { dockerRegistry } = await prisma.application.findUnique({
|
||||||
await new Promise((resolve, reject) => {
|
where: { id: applicationId },
|
||||||
docker.engine.modem.followProgress(stream, onFinished, onProgress);
|
select: { dockerRegistry: true }
|
||||||
function onFinished(err, res) {
|
|
||||||
if (err) reject(err);
|
|
||||||
resolve(res);
|
|
||||||
}
|
|
||||||
async function onProgress(event) {
|
|
||||||
if (event.error) {
|
|
||||||
reject(event.error);
|
|
||||||
} else if (event.stream) {
|
|
||||||
if (event.stream !== '\n') {
|
|
||||||
if (debug)
|
|
||||||
await saveBuildLog({
|
|
||||||
line: `${event.stream.replace('\n', '')}`,
|
|
||||||
buildId,
|
|
||||||
applicationId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
if (dockerRegistry) {
|
||||||
|
const { url, username, password } = dockerRegistry;
|
||||||
|
location = await saveDockerRegistryCredentials({ url, username, password, workdir });
|
||||||
|
}
|
||||||
|
|
||||||
|
await executeCommand({
|
||||||
|
stream: true,
|
||||||
|
debug,
|
||||||
|
buildId,
|
||||||
|
applicationId,
|
||||||
|
dockerId,
|
||||||
|
command: `docker ${location ? `--config ${location}` : ''} build ${
|
||||||
|
forceRebuild ? '--no-cache' : ''
|
||||||
|
} --progress plain -f ${workdir}/${dockerFile} -t ${cache} --build-arg SOURCE_COMMIT=${commit} ${workdir}`
|
||||||
|
});
|
||||||
|
|
||||||
|
const { status } = await prisma.build.findUnique({ where: { id: buildId } });
|
||||||
|
if (status === 'canceled') {
|
||||||
|
throw new Error('Canceled.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function makeLabelForSimpleDockerfile({ applicationId, port, type }) {
|
||||||
|
return [
|
||||||
|
'coolify.managed=true',
|
||||||
|
`coolify.version=${version}`,
|
||||||
|
`coolify.applicationId=${applicationId}`,
|
||||||
|
`coolify.type=standalone-application`
|
||||||
|
];
|
||||||
|
}
|
||||||
export function makeLabelForStandaloneApplication({
|
export function makeLabelForStandaloneApplication({
|
||||||
applicationId,
|
applicationId,
|
||||||
fqdn,
|
fqdn,
|
||||||
@@ -612,7 +746,9 @@ export function makeLabelForStandaloneApplication({
|
|||||||
return [
|
return [
|
||||||
'coolify.managed=true',
|
'coolify.managed=true',
|
||||||
`coolify.version=${version}`,
|
`coolify.version=${version}`,
|
||||||
|
`coolify.applicationId=${applicationId}`,
|
||||||
`coolify.type=standalone-application`,
|
`coolify.type=standalone-application`,
|
||||||
|
`coolify.name=${name}`,
|
||||||
`coolify.configuration=${base64Encode(
|
`coolify.configuration=${base64Encode(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
applicationId,
|
applicationId,
|
||||||
@@ -646,26 +782,14 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
|||||||
secrets,
|
secrets,
|
||||||
pullmergeRequestId
|
pullmergeRequestId
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
|
|
||||||
const isPnpm = checkPnpm(installCommand, buildCommand);
|
const isPnpm = checkPnpm(installCommand, buildCommand);
|
||||||
const Dockerfile: Array<string> = [];
|
const Dockerfile: Array<string> = [];
|
||||||
Dockerfile.push(`FROM ${imageForBuild}`);
|
Dockerfile.push(`FROM ${imageForBuild}`);
|
||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||||
if (secret.isBuildSecret) {
|
Dockerfile.push(env);
|
||||||
if (pullmergeRequestId) {
|
|
||||||
if (secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
@@ -682,24 +806,13 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
|||||||
|
|
||||||
export async function buildCacheImageForLaravel(data, imageForBuild) {
|
export async function buildCacheImageForLaravel(data, imageForBuild) {
|
||||||
const { workdir, buildId, secrets, pullmergeRequestId } = data;
|
const { workdir, buildId, secrets, pullmergeRequestId } = data;
|
||||||
|
|
||||||
const Dockerfile: Array<string> = [];
|
const Dockerfile: Array<string> = [];
|
||||||
Dockerfile.push(`FROM ${imageForBuild}`);
|
Dockerfile.push(`FROM ${imageForBuild}`);
|
||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||||
if (secret.isBuildSecret) {
|
Dockerfile.push(env);
|
||||||
if (pullmergeRequestId) {
|
|
||||||
if (secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY *.json *.mix.js /app/`);
|
Dockerfile.push(`COPY *.json *.mix.js /app/`);
|
||||||
@@ -710,11 +823,7 @@ export async function buildCacheImageForLaravel(data, imageForBuild) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function buildCacheImageWithCargo(data, imageForBuild) {
|
export async function buildCacheImageWithCargo(data, imageForBuild) {
|
||||||
const {
|
const { applicationId, workdir, buildId } = data;
|
||||||
applicationId,
|
|
||||||
workdir,
|
|
||||||
buildId,
|
|
||||||
} = data;
|
|
||||||
|
|
||||||
const Dockerfile: Array<string> = [];
|
const Dockerfile: Array<string> = [];
|
||||||
Dockerfile.push(`FROM ${imageForBuild} as planner-${applicationId}`);
|
Dockerfile.push(`FROM ${imageForBuild} as planner-${applicationId}`);
|
||||||
@@ -731,4 +840,4 @@ export async function buildCacheImageWithCargo(data, imageForBuild) {
|
|||||||
Dockerfile.push('RUN cargo chef cook --release --recipe-path recipe.json');
|
Dockerfile.push('RUN cargo chef cook --release --recipe-path recipe.json');
|
||||||
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
||||||
await buildImage({ ...data, isCache: true });
|
await buildImage({ ...data, isCache: true });
|
||||||
}
|
}
|
||||||
|
|||||||
136
apps/api/src/lib/buildPacks/compose.ts
Normal file
136
apps/api/src/lib/buildPacks/compose.ts
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import { defaultComposeConfiguration, executeCommand, generateSecrets } from '../common';
|
||||||
|
import { saveBuildLog } from './common';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
|
export default async function (data) {
|
||||||
|
let {
|
||||||
|
applicationId,
|
||||||
|
debug,
|
||||||
|
buildId,
|
||||||
|
dockerId,
|
||||||
|
network,
|
||||||
|
volumes,
|
||||||
|
labels,
|
||||||
|
workdir,
|
||||||
|
baseDirectory,
|
||||||
|
secrets,
|
||||||
|
pullmergeRequestId,
|
||||||
|
dockerComposeConfiguration,
|
||||||
|
dockerComposeFileLocation
|
||||||
|
} = data;
|
||||||
|
const fileYaml = `${workdir}${baseDirectory}${dockerComposeFileLocation}`;
|
||||||
|
const dockerComposeRaw = await fs.readFile(fileYaml, 'utf8');
|
||||||
|
const dockerComposeYaml = yaml.load(dockerComposeRaw);
|
||||||
|
if (!dockerComposeYaml.services) {
|
||||||
|
throw 'No Services found in docker-compose file.';
|
||||||
|
}
|
||||||
|
let envs = [];
|
||||||
|
let buildEnvs = [];
|
||||||
|
if (secrets.length > 0) {
|
||||||
|
envs = [...envs, ...generateSecrets(secrets, pullmergeRequestId, false, null)];
|
||||||
|
buildEnvs = [...buildEnvs, ...generateSecrets(secrets, pullmergeRequestId, true, null, true)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const composeVolumes = [];
|
||||||
|
if (volumes.length > 0) {
|
||||||
|
for (const volume of volumes) {
|
||||||
|
let [v, path] = volume.split(':');
|
||||||
|
composeVolumes[v] = {
|
||||||
|
name: v
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let networks = {};
|
||||||
|
for (let [key, value] of Object.entries(dockerComposeYaml.services)) {
|
||||||
|
value['container_name'] = `${applicationId}-${key}`;
|
||||||
|
|
||||||
|
let environment = typeof value['environment'] === 'undefined' ? [] : value['environment'];
|
||||||
|
if (Object.keys(environment).length > 0) {
|
||||||
|
environment = Object.entries(environment).map(([key, value]) => `${key}=${value}`);
|
||||||
|
}
|
||||||
|
value['environment'] = [...environment, ...envs];
|
||||||
|
|
||||||
|
let build = typeof value['build'] === 'undefined' ? [] : value['build'];
|
||||||
|
if (typeof build === 'string') {
|
||||||
|
build = { context: build };
|
||||||
|
}
|
||||||
|
const buildArgs = typeof build['args'] === 'undefined' ? [] : build['args'];
|
||||||
|
let finalArgs = [...buildEnvs];
|
||||||
|
if (Object.keys(buildArgs).length > 0) {
|
||||||
|
for (const arg of buildArgs) {
|
||||||
|
const [key, _] = arg.split('=');
|
||||||
|
if (finalArgs.filter((env) => env.startsWith(key)).length === 0) {
|
||||||
|
finalArgs.push(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value['build'] = {
|
||||||
|
...build,
|
||||||
|
args: finalArgs
|
||||||
|
};
|
||||||
|
|
||||||
|
value['labels'] = labels;
|
||||||
|
// TODO: If we support separated volume for each service, we need to add it here
|
||||||
|
if (value['volumes']?.length > 0) {
|
||||||
|
value['volumes'] = value['volumes'].map((volume) => {
|
||||||
|
let [v, path, permission] = volume.split(':');
|
||||||
|
if (!path) {
|
||||||
|
path = v;
|
||||||
|
v = `${applicationId}${v.replace(/\//gi, '-').replace(/\./gi, '')}`;
|
||||||
|
} else {
|
||||||
|
v = `${applicationId}${v.replace(/\//gi, '-').replace(/\./gi, '')}`;
|
||||||
|
}
|
||||||
|
composeVolumes[v] = {
|
||||||
|
name: v
|
||||||
|
};
|
||||||
|
return `${v}:${path}${permission ? ':' + permission : ''}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (volumes.length > 0) {
|
||||||
|
for (const volume of volumes) {
|
||||||
|
value['volumes'].push(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dockerComposeConfiguration[key].port) {
|
||||||
|
value['expose'] = [dockerComposeConfiguration[key].port];
|
||||||
|
}
|
||||||
|
if (value['networks']?.length > 0) {
|
||||||
|
value['networks'].forEach((network) => {
|
||||||
|
networks[network] = {
|
||||||
|
name: network
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
value['networks'] = [...(value['networks'] || ''), network];
|
||||||
|
dockerComposeYaml.services[key] = {
|
||||||
|
...dockerComposeYaml.services[key],
|
||||||
|
restart: defaultComposeConfiguration(network).restart,
|
||||||
|
deploy: defaultComposeConfiguration(network).deploy
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (Object.keys(composeVolumes).length > 0) {
|
||||||
|
dockerComposeYaml['volumes'] = { ...composeVolumes };
|
||||||
|
}
|
||||||
|
dockerComposeYaml['networks'] = Object.assign({ ...networks }, { [network]: { external: true } });
|
||||||
|
|
||||||
|
await fs.writeFile(fileYaml, yaml.dump(dockerComposeYaml));
|
||||||
|
console.log(yaml.dump(dockerComposeYaml));
|
||||||
|
await executeCommand({
|
||||||
|
debug,
|
||||||
|
buildId,
|
||||||
|
applicationId,
|
||||||
|
dockerId,
|
||||||
|
command: `docker compose --project-directory ${workdir} -f ${fileYaml} pull`
|
||||||
|
});
|
||||||
|
await saveBuildLog({ line: 'Pulling images from Compose file...', buildId, applicationId });
|
||||||
|
await executeCommand({
|
||||||
|
debug,
|
||||||
|
buildId,
|
||||||
|
applicationId,
|
||||||
|
dockerId,
|
||||||
|
command: `docker compose --project-directory ${workdir} -f ${fileYaml} build --progress plain`
|
||||||
|
});
|
||||||
|
await saveBuildLog({ line: 'Building images from Compose file...', buildId, applicationId });
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
import { generateSecrets } from '../common';
|
||||||
import { buildImage } from './common';
|
import { buildImage } from './common';
|
||||||
|
|
||||||
const createDockerfile = async (data, image): Promise<void> => {
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
@@ -24,18 +25,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||||
if (secret.isBuildSecret) {
|
Dockerfile.push(env);
|
||||||
if (pullmergeRequestId) {
|
|
||||||
if (secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (depsFound) {
|
if (depsFound) {
|
||||||
@@ -46,7 +37,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push(`RUN deno cache ${denoMainFile}`);
|
Dockerfile.push(`RUN deno cache ${denoMainFile}`);
|
||||||
Dockerfile.push(`ENV NO_COLOR true`);
|
Dockerfile.push(`ENV NO_COLOR true`);
|
||||||
Dockerfile.push(`EXPOSE ${port}`);
|
Dockerfile.push(`EXPOSE ${port}`);
|
||||||
Dockerfile.push(`CMD deno run ${denoOptions ? denoOptions.split(' ') : ''} ${denoMainFile}`);
|
Dockerfile.push(`CMD deno run ${denoOptions || ''} ${denoMainFile}`);
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,52 +1,27 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
import { generateSecrets } from '../common';
|
||||||
import { buildImage } from './common';
|
import { buildImage } from './common';
|
||||||
|
|
||||||
export default async function (data) {
|
export default async function (data) {
|
||||||
let {
|
let { workdir, buildId, baseDirectory, secrets, pullmergeRequestId, dockerFileLocation } = data;
|
||||||
applicationId,
|
const file = `${workdir}${baseDirectory}${dockerFileLocation}`;
|
||||||
debug,
|
data.workdir = `${workdir}${baseDirectory}`;
|
||||||
tag,
|
const DockerfileRaw = await fs.readFile(`${file}`, 'utf8');
|
||||||
workdir,
|
const Dockerfile: Array<string> = DockerfileRaw.toString().trim().split('\n');
|
||||||
buildId,
|
Dockerfile.forEach((line, index) => {
|
||||||
baseDirectory,
|
if (line.startsWith('FROM')) {
|
||||||
secrets,
|
Dockerfile.splice(index + 1, 0, `LABEL coolify.buildId=${buildId}`);
|
||||||
pullmergeRequestId,
|
|
||||||
dockerFileLocation
|
|
||||||
} = data
|
|
||||||
try {
|
|
||||||
const file = `${workdir}${dockerFileLocation}`;
|
|
||||||
let dockerFileOut = `${workdir}`;
|
|
||||||
if (baseDirectory) {
|
|
||||||
dockerFileOut = `${workdir}${baseDirectory}`;
|
|
||||||
workdir = `${workdir}${baseDirectory}`;
|
|
||||||
}
|
}
|
||||||
const Dockerfile: Array<string> = (await fs.readFile(`${file}`, 'utf8'))
|
});
|
||||||
.toString()
|
if (secrets.length > 0) {
|
||||||
.trim()
|
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||||
.split('\n');
|
Dockerfile.forEach((line, index) => {
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
if (line.startsWith('FROM')) {
|
||||||
if (secrets.length > 0) {
|
Dockerfile.splice(index + 1, 0, env);
|
||||||
secrets.forEach((secret) => {
|
|
||||||
if (secret.isBuildSecret) {
|
|
||||||
if (
|
|
||||||
(pullmergeRequestId && secret.isPRMRSecret) ||
|
|
||||||
(!pullmergeRequestId && !secret.isPRMRSecret)
|
|
||||||
) {
|
|
||||||
Dockerfile.unshift(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
|
|
||||||
Dockerfile.forEach((line, index) => {
|
|
||||||
if (line.startsWith('FROM')) {
|
|
||||||
Dockerfile.splice(index + 1, 0, `ARG ${secret.name}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
await fs.writeFile(`${dockerFileOut}${dockerFileLocation}`, Dockerfile.join('\n'));
|
|
||||||
await buildImage(data);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
await fs.writeFile(`${data.workdir}${dockerFileLocation}`, Dockerfile.join('\n'));
|
||||||
|
await buildImage(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,17 @@
|
|||||||
import { executeDockerCmd, prisma } from "../common"
|
import { executeCommand } from "../common"
|
||||||
import { saveBuildLog } from "./common";
|
import { saveBuildLog } from "./common";
|
||||||
|
|
||||||
export default async function (data: any): Promise<void> {
|
export default async function (data: any): Promise<void> {
|
||||||
const { buildId, applicationId, tag, dockerId, debug, workdir } = data
|
const { buildId, applicationId, tag, dockerId, debug, workdir, baseDirectory, baseImage } = data
|
||||||
try {
|
try {
|
||||||
|
await saveBuildLog({ line: `Building production image...`, buildId, applicationId });
|
||||||
await saveBuildLog({ line: `Building image started.`, buildId, applicationId });
|
await executeCommand({
|
||||||
const { stdout } = await executeDockerCmd({
|
buildId,
|
||||||
|
debug,
|
||||||
dockerId,
|
dockerId,
|
||||||
command: `pack build -p ${workdir} ${applicationId}:${tag} --builder heroku/buildpacks:20`
|
command: `pack build -p ${workdir}${baseDirectory} ${applicationId}:${tag} --builder ${baseImage}`
|
||||||
})
|
})
|
||||||
if (debug) {
|
|
||||||
const array = stdout.split('\n')
|
|
||||||
for (const line of array) {
|
|
||||||
if (line !== '\n') {
|
|
||||||
await saveBuildLog({
|
|
||||||
line: `${line.replace('\n', '')}`,
|
|
||||||
buildId,
|
|
||||||
applicationId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await saveBuildLog({ line: `Building image successful.`, buildId, applicationId });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const array = error.stdout.split('\n')
|
|
||||||
for (const line of array) {
|
|
||||||
if (line !== '\n') {
|
|
||||||
await saveBuildLog({
|
|
||||||
line: `${line.replace('\n', '')}`,
|
|
||||||
buildId,
|
|
||||||
applicationId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import python from './python';
|
|||||||
import deno from './deno';
|
import deno from './deno';
|
||||||
import laravel from './laravel';
|
import laravel from './laravel';
|
||||||
import heroku from './heroku';
|
import heroku from './heroku';
|
||||||
|
import compose from './compose'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
node,
|
node,
|
||||||
@@ -35,5 +36,6 @@ export {
|
|||||||
python,
|
python,
|
||||||
deno,
|
deno,
|
||||||
laravel,
|
laravel,
|
||||||
heroku
|
heroku,
|
||||||
|
compose
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
import { generateSecrets } from '../common';
|
||||||
import { buildCacheImageForLaravel, buildImage } from './common';
|
import { buildCacheImageForLaravel, buildImage } from './common';
|
||||||
|
|
||||||
const createDockerfile = async (data, image): Promise<void> => {
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
const { workdir, applicationId, tag, buildId, port } = data;
|
const { workdir, applicationId, tag, buildId, port, secrets, pullmergeRequestId } = data;
|
||||||
const Dockerfile: Array<string> = [];
|
const Dockerfile: Array<string> = [];
|
||||||
|
|
||||||
Dockerfile.push(`FROM ${image}`);
|
Dockerfile.push(`FROM ${image}`);
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
|
if (secrets.length > 0) {
|
||||||
|
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||||
|
Dockerfile.push(env);
|
||||||
|
});
|
||||||
|
}
|
||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
Dockerfile.push(`ENV WEB_DOCUMENT_ROOT /app/public`);
|
Dockerfile.push(`ENV WEB_DOCUMENT_ROOT /app/public`);
|
||||||
Dockerfile.push(`COPY --chown=application:application composer.* ./`);
|
Dockerfile.push(`COPY --chown=application:application composer.* ./`);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
import { generateSecrets } from '../common';
|
||||||
import { buildCacheImageWithNode, buildImage, checkPnpm } from './common';
|
import { buildCacheImageWithNode, buildImage, checkPnpm } from './common';
|
||||||
|
|
||||||
const createDockerfile = async (data, image): Promise<void> => {
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
@@ -24,18 +25,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||||
if (secret.isBuildSecret) {
|
Dockerfile.push(env);
|
||||||
if (pullmergeRequestId) {
|
|
||||||
if (secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
import { generateSecrets } from '../common';
|
||||||
import { buildImage, checkPnpm } from './common';
|
import { buildImage, checkPnpm } from './common';
|
||||||
|
|
||||||
const createDockerfile = async (data, image): Promise<void> => {
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
@@ -20,18 +21,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||||
if (secret.isBuildSecret) {
|
Dockerfile.push(env);
|
||||||
if (pullmergeRequestId) {
|
|
||||||
if (secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
import { generateSecrets } from '../common';
|
||||||
import { buildCacheImageWithNode, buildImage, checkPnpm } from './common';
|
import { buildCacheImageWithNode, buildImage, checkPnpm } from './common';
|
||||||
|
|
||||||
const createDockerfile = async (data, image): Promise<void> => {
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
@@ -24,18 +25,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||||
if (secret.isBuildSecret) {
|
Dockerfile.push(env);
|
||||||
if (pullmergeRequestId) {
|
|
||||||
if (secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
import { generateSecrets } from '../common';
|
||||||
import { buildImage } from './common';
|
import { buildImage } from './common';
|
||||||
|
|
||||||
const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
||||||
@@ -13,18 +14,8 @@ const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
|||||||
Dockerfile.push(`FROM ${image}`);
|
Dockerfile.push(`FROM ${image}`);
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||||
if (secret.isBuildSecret) {
|
Dockerfile.push(env);
|
||||||
if (pullmergeRequestId) {
|
|
||||||
if (secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
import { generateSecrets } from '../common';
|
||||||
import { buildImage } from './common';
|
import { buildImage } from './common';
|
||||||
|
|
||||||
const createDockerfile = async (data, image): Promise<void> => {
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
@@ -18,18 +19,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||||
if (secret.isBuildSecret) {
|
Dockerfile.push(env);
|
||||||
if (pullmergeRequestId) {
|
|
||||||
if (secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (pythonWSGI?.toLowerCase() === 'gunicorn') {
|
if (pythonWSGI?.toLowerCase() === 'gunicorn') {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import TOML from '@iarna/toml';
|
import TOML from '@iarna/toml';
|
||||||
import { asyncExecShell } from '../common';
|
import { executeCommand } from '../common';
|
||||||
import { buildCacheImageWithCargo, buildImage } from './common';
|
import { buildCacheImageWithCargo, buildImage } from './common';
|
||||||
|
|
||||||
const createDockerfile = async (data, image, name): Promise<void> => {
|
const createDockerfile = async (data, image, name): Promise<void> => {
|
||||||
@@ -28,7 +28,7 @@ const createDockerfile = async (data, image, name): Promise<void> => {
|
|||||||
export default async function (data) {
|
export default async function (data) {
|
||||||
try {
|
try {
|
||||||
const { workdir, baseImage, baseBuildImage } = data;
|
const { workdir, baseImage, baseBuildImage } = data;
|
||||||
const { stdout: cargoToml } = await asyncExecShell(`cat ${workdir}/Cargo.toml`);
|
const { stdout: cargoToml } = await executeCommand({ command: `cat ${workdir}/Cargo.toml` });
|
||||||
const parsedToml: any = TOML.parse(cargoToml);
|
const parsedToml: any = TOML.parse(cargoToml);
|
||||||
const name = parsedToml.package.name;
|
const name = parsedToml.package.name;
|
||||||
await buildCacheImageWithCargo(data, baseBuildImage);
|
await buildCacheImageWithCargo(data, baseBuildImage);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
import { generateSecrets } from '../common';
|
||||||
import { buildCacheImageWithNode, buildImage } from './common';
|
import { buildCacheImageWithNode, buildImage } from './common';
|
||||||
|
|
||||||
const createDockerfile = async (data, image): Promise<void> => {
|
const createDockerfile = async (data, image): Promise<void> => {
|
||||||
@@ -18,21 +19,15 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
const Dockerfile: Array<string> = [];
|
const Dockerfile: Array<string> = [];
|
||||||
|
|
||||||
Dockerfile.push(`FROM ${image}`);
|
Dockerfile.push(`FROM ${image}`);
|
||||||
Dockerfile.push('WORKDIR /app');
|
if (baseImage?.includes('httpd')) {
|
||||||
|
Dockerfile.push('WORKDIR /usr/local/apache2/htdocs/');
|
||||||
|
} else {
|
||||||
|
Dockerfile.push('WORKDIR /app');
|
||||||
|
}
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
if (secrets.length > 0) {
|
if (secrets.length > 0) {
|
||||||
secrets.forEach((secret) => {
|
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||||
if (secret.isBuildSecret) {
|
Dockerfile.push(env);
|
||||||
if (pullmergeRequestId) {
|
|
||||||
if (secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!secret.isPRMRSecret) {
|
|
||||||
Dockerfile.push(`ARG ${secret.name}=${secret.value}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (buildCommand) {
|
if (buildCommand) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
import { executeDockerCmd } from './common';
|
import { executeCommand } from './common';
|
||||||
|
|
||||||
export function formatLabelsOnDocker(data) {
|
export function formatLabelsOnDocker(data) {
|
||||||
return data.trim().split('\n').map(a => JSON.parse(a)).map((container) => {
|
return data.trim().split('\n').map(a => JSON.parse(a)).map((container) => {
|
||||||
@@ -13,45 +13,56 @@ export function formatLabelsOnDocker(data) {
|
|||||||
return container
|
return container
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export async function checkContainer({ dockerId, container, remove = false }: { dockerId: string, container: string, remove?: boolean }): Promise<boolean> {
|
export async function checkContainer({ dockerId, container, remove = false }: { dockerId: string, container: string, remove?: boolean }): Promise<{ found: boolean, status?: { isExited: boolean, isRunning: boolean, isRestarting: boolean } }> {
|
||||||
let containerFound = false;
|
let containerFound = false;
|
||||||
try {
|
try {
|
||||||
const { stdout } = await executeDockerCmd({
|
const { stdout } = await executeCommand({
|
||||||
dockerId,
|
dockerId,
|
||||||
command:
|
command:
|
||||||
`docker inspect --format '{{json .State}}' ${container}`
|
`docker inspect --format '{{json .State}}' ${container}`
|
||||||
});
|
});
|
||||||
|
containerFound = true
|
||||||
const parsedStdout = JSON.parse(stdout);
|
const parsedStdout = JSON.parse(stdout);
|
||||||
const status = parsedStdout.Status;
|
const status = parsedStdout.Status;
|
||||||
const isRunning = status === 'running';
|
const isRunning = status === 'running';
|
||||||
|
const isRestarting = status === 'restarting'
|
||||||
|
const isExited = status === 'exited'
|
||||||
if (status === 'created') {
|
if (status === 'created') {
|
||||||
await executeDockerCmd({
|
await executeCommand({
|
||||||
dockerId,
|
dockerId,
|
||||||
command:
|
command:
|
||||||
`docker rm ${container}`
|
`docker rm ${container}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (remove && status === 'exited') {
|
if (remove && status === 'exited') {
|
||||||
await executeDockerCmd({
|
await executeCommand({
|
||||||
dockerId,
|
dockerId,
|
||||||
command:
|
command:
|
||||||
`docker rm ${container}`
|
`docker rm ${container}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isRunning) {
|
|
||||||
containerFound = true;
|
return {
|
||||||
}
|
found: containerFound,
|
||||||
|
status: {
|
||||||
|
isRunning,
|
||||||
|
isRestarting,
|
||||||
|
isExited
|
||||||
|
}
|
||||||
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Container not found
|
// Container not found
|
||||||
}
|
}
|
||||||
return containerFound;
|
return {
|
||||||
|
found: false
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isContainerExited(dockerId: string, containerName: string): Promise<boolean> {
|
export async function isContainerExited(dockerId: string, containerName: string): Promise<boolean> {
|
||||||
let isExited = false;
|
let isExited = false;
|
||||||
try {
|
try {
|
||||||
const { stdout } = await executeDockerCmd({ dockerId, command: `docker inspect -f '{{.State.Status}}' ${containerName}` })
|
const { stdout } = await executeCommand({ dockerId, command: `docker inspect -f '{{.State.Status}}' ${containerName}` })
|
||||||
if (stdout.trim() === 'exited') {
|
if (stdout.trim() === 'exited') {
|
||||||
isExited = true;
|
isExited = true;
|
||||||
}
|
}
|
||||||
@@ -70,13 +81,15 @@ export async function removeContainer({
|
|||||||
dockerId: string;
|
dockerId: string;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await executeDockerCmd({ dockerId, command: `docker inspect --format '{{json .State}}' ${id}` })
|
const { stdout } = await executeCommand({ dockerId, command: `docker inspect --format '{{json .State}}' ${id}` })
|
||||||
if (JSON.parse(stdout).Running) {
|
if (JSON.parse(stdout).Running) {
|
||||||
await executeDockerCmd({ dockerId, command: `docker stop -t 0 ${id}` })
|
await executeCommand({ dockerId, command: `docker stop -t 0 ${id}` })
|
||||||
await executeDockerCmd({ dockerId, command: `docker rm ${id}` })
|
await executeCommand({ dockerId, command: `docker rm ${id}` })
|
||||||
|
}
|
||||||
|
if (JSON.parse(stdout).Status === 'exited') {
|
||||||
|
await executeCommand({ dockerId, command: `docker rm ${id}` })
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import jsonwebtoken from 'jsonwebtoken';
|
import jsonwebtoken from 'jsonwebtoken';
|
||||||
import { saveBuildLog } from '../buildPacks/common';
|
import { saveBuildLog } from '../buildPacks/common';
|
||||||
import { asyncExecShell, decrypt, prisma } from '../common';
|
import { decrypt, executeCommand, prisma } from '../common';
|
||||||
|
|
||||||
export default async function ({
|
export default async function ({
|
||||||
applicationId,
|
applicationId,
|
||||||
@@ -9,6 +9,7 @@ export default async function ({
|
|||||||
githubAppId,
|
githubAppId,
|
||||||
repository,
|
repository,
|
||||||
apiUrl,
|
apiUrl,
|
||||||
|
gitCommitHash,
|
||||||
htmlUrl,
|
htmlUrl,
|
||||||
branch,
|
branch,
|
||||||
buildId,
|
buildId,
|
||||||
@@ -20,6 +21,7 @@ export default async function ({
|
|||||||
githubAppId: string;
|
githubAppId: string;
|
||||||
repository: string;
|
repository: string;
|
||||||
apiUrl: string;
|
apiUrl: string;
|
||||||
|
gitCommitHash?: string;
|
||||||
htmlUrl: string;
|
htmlUrl: string;
|
||||||
branch: string;
|
branch: string;
|
||||||
buildId: string;
|
buildId: string;
|
||||||
@@ -28,16 +30,24 @@ export default async function ({
|
|||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
const { default: got } = await import('got')
|
const { default: got } = await import('got')
|
||||||
const url = htmlUrl.replace('https://', '').replace('http://', '');
|
const url = htmlUrl.replace('https://', '').replace('http://', '');
|
||||||
await saveBuildLog({ line: 'GitHub importer started.', buildId, applicationId });
|
|
||||||
if (forPublic) {
|
if (forPublic) {
|
||||||
await saveBuildLog({
|
await saveBuildLog({
|
||||||
line: `Cloning ${repository}:${branch} branch.`,
|
line: `Cloning ${repository}:${branch}...`,
|
||||||
buildId,
|
buildId,
|
||||||
applicationId
|
applicationId
|
||||||
});
|
});
|
||||||
await asyncExecShell(
|
if (gitCommitHash) {
|
||||||
`git clone -q -b ${branch} https://${url}/${repository}.git ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && git lfs pull && cd .. `
|
await saveBuildLog({
|
||||||
);
|
line: `Checking out ${gitCommitHash} commit...`,
|
||||||
|
buildId,
|
||||||
|
applicationId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await executeCommand({
|
||||||
|
command:
|
||||||
|
`git clone -q -b ${branch} https://${url}/${repository}.git ${workdir}/ && cd ${workdir} && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `,
|
||||||
|
shell: true
|
||||||
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const body = await prisma.githubApp.findUnique({ where: { id: githubAppId } });
|
const body = await prisma.githubApp.findUnique({ where: { id: githubAppId } });
|
||||||
@@ -62,17 +72,23 @@ export default async function ({
|
|||||||
})
|
})
|
||||||
.json();
|
.json();
|
||||||
await saveBuildLog({
|
await saveBuildLog({
|
||||||
line: `Cloning ${repository}:${branch} branch.`,
|
line: `Cloning ${repository}:${branch}...`,
|
||||||
buildId,
|
buildId,
|
||||||
applicationId
|
applicationId
|
||||||
});
|
});
|
||||||
await asyncExecShell(
|
if (gitCommitHash) {
|
||||||
`git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git --config core.sshCommand="ssh -p ${customPort}" ${workdir}/ && cd ${workdir} && git submodule update --init --recursive && git lfs pull && cd .. `
|
await saveBuildLog({
|
||||||
);
|
line: `Checking out ${gitCommitHash} commit...`,
|
||||||
|
buildId,
|
||||||
|
applicationId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await executeCommand({
|
||||||
|
command:
|
||||||
|
`git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git --config core.sshCommand="ssh -p ${customPort}" ${workdir}/ && cd ${workdir} && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `,
|
||||||
|
shell: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
const { stdout: commit } = await executeCommand({ command: `cd ${workdir}/ && git rev-parse HEAD`, shell: true });
|
||||||
|
|
||||||
return commit.replace('\n', '');
|
return commit.replace('\n', '');
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
import { saveBuildLog } from "../buildPacks/common";
|
import { saveBuildLog } from "../buildPacks/common";
|
||||||
import { asyncExecShell } from "../common";
|
import { executeCommand } from "../common";
|
||||||
|
|
||||||
export default async function ({
|
export default async function ({
|
||||||
applicationId,
|
applicationId,
|
||||||
workdir,
|
workdir,
|
||||||
repodir,
|
repodir,
|
||||||
htmlUrl,
|
htmlUrl,
|
||||||
|
gitCommitHash,
|
||||||
repository,
|
repository,
|
||||||
branch,
|
branch,
|
||||||
buildId,
|
buildId,
|
||||||
privateSshKey,
|
privateSshKey,
|
||||||
customPort
|
customPort,
|
||||||
|
forPublic,
|
||||||
|
customUser,
|
||||||
}: {
|
}: {
|
||||||
applicationId: string;
|
applicationId: string;
|
||||||
workdir: string;
|
workdir: string;
|
||||||
@@ -19,23 +22,44 @@ export default async function ({
|
|||||||
branch: string;
|
branch: string;
|
||||||
buildId: string;
|
buildId: string;
|
||||||
repodir: string;
|
repodir: string;
|
||||||
|
gitCommitHash: string;
|
||||||
privateSshKey: string;
|
privateSshKey: string;
|
||||||
customPort: number;
|
customPort: number;
|
||||||
|
forPublic: boolean;
|
||||||
|
customUser: string;
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
const url = htmlUrl.replace('https://', '').replace('http://', '').replace(/\/$/, '');
|
const url = htmlUrl.replace('https://', '').replace('http://', '').replace(/\/$/, '');
|
||||||
await saveBuildLog({ line: 'GitLab importer started.', buildId, applicationId });
|
if (!forPublic) {
|
||||||
await asyncExecShell(`echo '${privateSshKey}' > ${repodir}/id.rsa`);
|
await executeCommand({ command: `echo '${privateSshKey}' > ${repodir}/id.rsa`, shell: true });
|
||||||
await asyncExecShell(`chmod 600 ${repodir}/id.rsa`);
|
await executeCommand({ command: `chmod 600 ${repodir}/id.rsa` });
|
||||||
|
}
|
||||||
|
|
||||||
await saveBuildLog({
|
await saveBuildLog({
|
||||||
line: `Cloning ${repository}:${branch} branch.`,
|
line: `Cloning ${repository}:${branch}...`,
|
||||||
buildId,
|
buildId,
|
||||||
applicationId
|
applicationId
|
||||||
});
|
});
|
||||||
|
if (gitCommitHash) {
|
||||||
|
await saveBuildLog({
|
||||||
|
line: `Checking out ${gitCommitHash} commit...`,
|
||||||
|
buildId,
|
||||||
|
applicationId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (forPublic) {
|
||||||
|
await executeCommand({
|
||||||
|
command:
|
||||||
|
`git clone -q -b ${branch} https://${url}/${repository}.git ${workdir}/ && cd ${workdir}/ && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `, shell: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await executeCommand({
|
||||||
|
command:
|
||||||
|
`git clone -q -b ${branch} ${customUser}@${url}:${repository}.git --config core.sshCommand="ssh -p ${customPort} -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `, shell: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
await asyncExecShell(
|
const { stdout: commit } = await executeCommand({ command: `cd ${workdir}/ && git rev-parse HEAD`, shell: true });
|
||||||
`git clone -q -b ${branch} git@${url}:${repository}.git --config core.sshCommand="ssh -p ${customPort} -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git submodule update --init --recursive && git lfs pull && cd .. `
|
|
||||||
);
|
|
||||||
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
|
||||||
return commit.replace('\n', '');
|
return commit.replace('\n', '');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,17 +9,16 @@ Bree.extend(TSBree);
|
|||||||
|
|
||||||
const options: any = {
|
const options: any = {
|
||||||
defaultExtension: 'js',
|
defaultExtension: 'js',
|
||||||
// logger: new Cabin(),
|
|
||||||
logger: false,
|
logger: false,
|
||||||
workerMessageHandler: async ({ name, message }) => {
|
// logger: false,
|
||||||
if (name === 'deployApplication' && message?.deploying) {
|
// workerMessageHandler: async ({ name, message }) => {
|
||||||
if (scheduler.workers.has('autoUpdater') || scheduler.workers.has('cleanupStorage')) {
|
// if (name === 'deployApplication' && message?.deploying) {
|
||||||
scheduler.workers.get('deployApplication').postMessage('cancel')
|
// if (scheduler.workers.has('autoUpdater') || scheduler.workers.has('cleanupStorage')) {
|
||||||
}
|
// scheduler.workers.get('deployApplication').postMessage('cancel')
|
||||||
}
|
// }
|
||||||
},
|
// }
|
||||||
|
// },
|
||||||
jobs: [
|
jobs: [
|
||||||
{ name: 'infrastructure' },
|
|
||||||
{ name: 'deployApplication' },
|
{ name: 'deployApplication' },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,47 @@
|
|||||||
import { createDirectories, getServiceFromDB, getServiceImage, getServiceMainPort, makeLabelForServices } from "./common";
|
import { isARM, isDev } from './common';
|
||||||
|
import fs from 'fs/promises';
|
||||||
export async function defaultServiceConfigurations({ id, teamId }) {
|
export async function getTemplates() {
|
||||||
const service = await getServiceFromDB({ id, teamId });
|
const templatePath = isDev ? './templates.json' : '/app/templates.json';
|
||||||
const { destinationDockerId, destinationDocker, type, serviceSecret } = service;
|
const open = await fs.open(templatePath, 'r');
|
||||||
|
try {
|
||||||
const network = destinationDockerId && destinationDocker.network;
|
let data = await open.readFile({ encoding: 'utf-8' });
|
||||||
const port = getServiceMainPort(type);
|
let jsonData = JSON.parse(data);
|
||||||
|
if (isARM(process.arch)) {
|
||||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
jsonData = jsonData.filter((d) => d.arch !== 'amd64');
|
||||||
|
}
|
||||||
const image = getServiceImage(type);
|
return jsonData;
|
||||||
let secrets = [];
|
} catch (error) {
|
||||||
if (serviceSecret.length > 0) {
|
return [];
|
||||||
serviceSecret.forEach((secret) => {
|
} finally {
|
||||||
secrets.push(`${secret.name}=${secret.value}`);
|
await open?.close();
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
return { ...service, network, port, workdir, image, secrets }
|
const compareSemanticVersions = (a: string, b: string) => {
|
||||||
}
|
const a1 = a.split('.');
|
||||||
|
const b1 = b.split('.');
|
||||||
|
const len = Math.min(a1.length, b1.length);
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
const a2 = +a1[i] || 0;
|
||||||
|
const b2 = +b1[i] || 0;
|
||||||
|
if (a2 !== b2) {
|
||||||
|
return a2 > b2 ? 1 : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b1.length - a1.length;
|
||||||
|
};
|
||||||
|
export async function getTags(type: string) {
|
||||||
|
try {
|
||||||
|
if (type) {
|
||||||
|
const tagsPath = isDev ? './tags.json' : '/app/tags.json';
|
||||||
|
const data = await fs.readFile(tagsPath, 'utf8');
|
||||||
|
let tags = JSON.parse(data);
|
||||||
|
if (tags) {
|
||||||
|
tags = tags.find((tag: any) => tag.name.includes(type));
|
||||||
|
tags.tags = tags.tags.sort(compareSemanticVersions).reverse();
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,367 +1,9 @@
|
|||||||
|
|
||||||
import cuid from 'cuid';
|
import { decrypt, prisma } from '../common';
|
||||||
import { encrypt, generatePassword, prisma } from '../common';
|
|
||||||
|
|
||||||
export const includeServices: any = {
|
|
||||||
destinationDocker: true,
|
|
||||||
persistentStorage: true,
|
|
||||||
serviceSecret: true,
|
|
||||||
minio: true,
|
|
||||||
plausibleAnalytics: true,
|
|
||||||
vscodeserver: true,
|
|
||||||
wordpress: true,
|
|
||||||
ghost: true,
|
|
||||||
meiliSearch: true,
|
|
||||||
umami: true,
|
|
||||||
hasura: true,
|
|
||||||
fider: true,
|
|
||||||
moodle: true,
|
|
||||||
appwrite: true,
|
|
||||||
glitchTip: true,
|
|
||||||
searxng: true,
|
|
||||||
weblate: true,
|
|
||||||
taiga: true
|
|
||||||
};
|
|
||||||
export async function configureServiceType({
|
|
||||||
id,
|
|
||||||
type
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
type: string;
|
|
||||||
}): Promise<void> {
|
|
||||||
if (type === 'plausibleanalytics') {
|
|
||||||
const password = encrypt(generatePassword({}));
|
|
||||||
const postgresqlUser = cuid();
|
|
||||||
const postgresqlPassword = encrypt(generatePassword({}));
|
|
||||||
const postgresqlDatabase = 'plausibleanalytics';
|
|
||||||
const secretKeyBase = encrypt(generatePassword({ length: 64 }));
|
|
||||||
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
plausibleAnalytics: {
|
|
||||||
create: {
|
|
||||||
postgresqlDatabase,
|
|
||||||
postgresqlUser,
|
|
||||||
postgresqlPassword,
|
|
||||||
password,
|
|
||||||
secretKeyBase
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'nocodb') {
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: { type }
|
|
||||||
});
|
|
||||||
} else if (type === 'minio') {
|
|
||||||
const rootUser = cuid();
|
|
||||||
const rootUserPassword = encrypt(generatePassword({}));
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: { type, minio: { create: { rootUser, rootUserPassword } } }
|
|
||||||
});
|
|
||||||
} else if (type === 'vscodeserver') {
|
|
||||||
const password = encrypt(generatePassword({}));
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: { type, vscodeserver: { create: { password } } }
|
|
||||||
});
|
|
||||||
} else if (type === 'wordpress') {
|
|
||||||
const mysqlUser = cuid();
|
|
||||||
const mysqlPassword = encrypt(generatePassword({}));
|
|
||||||
const mysqlRootUser = cuid();
|
|
||||||
const mysqlRootUserPassword = encrypt(generatePassword({}));
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
wordpress: { create: { mysqlPassword, mysqlRootUserPassword, mysqlRootUser, mysqlUser } }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'vaultwarden') {
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'languagetool') {
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'n8n') {
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'uptimekuma') {
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'ghost') {
|
|
||||||
const defaultEmail = `${cuid()}@example.com`;
|
|
||||||
const defaultPassword = encrypt(generatePassword({}));
|
|
||||||
const mariadbUser = cuid();
|
|
||||||
const mariadbPassword = encrypt(generatePassword({}));
|
|
||||||
const mariadbRootUser = cuid();
|
|
||||||
const mariadbRootUserPassword = encrypt(generatePassword({}));
|
|
||||||
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
ghost: {
|
|
||||||
create: {
|
|
||||||
defaultEmail,
|
|
||||||
defaultPassword,
|
|
||||||
mariadbUser,
|
|
||||||
mariadbPassword,
|
|
||||||
mariadbRootUser,
|
|
||||||
mariadbRootUserPassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'meilisearch') {
|
|
||||||
const masterKey = encrypt(generatePassword({ length: 32 }));
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
meiliSearch: { create: { masterKey } }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'umami') {
|
|
||||||
const umamiAdminPassword = encrypt(generatePassword({}));
|
|
||||||
const postgresqlUser = cuid();
|
|
||||||
const postgresqlPassword = encrypt(generatePassword({}));
|
|
||||||
const postgresqlDatabase = 'umami';
|
|
||||||
const hashSalt = encrypt(generatePassword({ length: 64 }));
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
umami: {
|
|
||||||
create: {
|
|
||||||
umamiAdminPassword,
|
|
||||||
postgresqlDatabase,
|
|
||||||
postgresqlPassword,
|
|
||||||
postgresqlUser,
|
|
||||||
hashSalt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'hasura') {
|
|
||||||
const postgresqlUser = cuid();
|
|
||||||
const postgresqlPassword = encrypt(generatePassword({}));
|
|
||||||
const postgresqlDatabase = 'hasura';
|
|
||||||
const graphQLAdminPassword = encrypt(generatePassword({}));
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
hasura: {
|
|
||||||
create: {
|
|
||||||
postgresqlDatabase,
|
|
||||||
postgresqlPassword,
|
|
||||||
postgresqlUser,
|
|
||||||
graphQLAdminPassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'fider') {
|
|
||||||
const postgresqlUser = cuid();
|
|
||||||
const postgresqlPassword = encrypt(generatePassword({}));
|
|
||||||
const postgresqlDatabase = 'fider';
|
|
||||||
const jwtSecret = encrypt(generatePassword({ length: 64, symbols: true }));
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
fider: {
|
|
||||||
create: {
|
|
||||||
postgresqlDatabase,
|
|
||||||
postgresqlPassword,
|
|
||||||
postgresqlUser,
|
|
||||||
jwtSecret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'moodle') {
|
|
||||||
const defaultUsername = cuid();
|
|
||||||
const defaultPassword = encrypt(generatePassword({}));
|
|
||||||
const defaultEmail = `${cuid()} @example.com`;
|
|
||||||
const mariadbUser = cuid();
|
|
||||||
const mariadbPassword = encrypt(generatePassword({}));
|
|
||||||
const mariadbDatabase = 'moodle_db';
|
|
||||||
const mariadbRootUser = cuid();
|
|
||||||
const mariadbRootUserPassword = encrypt(generatePassword({}));
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
moodle: {
|
|
||||||
create: {
|
|
||||||
defaultUsername,
|
|
||||||
defaultPassword,
|
|
||||||
defaultEmail,
|
|
||||||
mariadbUser,
|
|
||||||
mariadbPassword,
|
|
||||||
mariadbDatabase,
|
|
||||||
mariadbRootUser,
|
|
||||||
mariadbRootUserPassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'appwrite') {
|
|
||||||
const opensslKeyV1 = encrypt(generatePassword({}));
|
|
||||||
const executorSecret = encrypt(generatePassword({}));
|
|
||||||
const redisPassword = encrypt(generatePassword({}));
|
|
||||||
const mariadbHost = `${id}-mariadb`
|
|
||||||
const mariadbUser = cuid();
|
|
||||||
const mariadbPassword = encrypt(generatePassword({}));
|
|
||||||
const mariadbDatabase = 'appwrite';
|
|
||||||
const mariadbRootUser = cuid();
|
|
||||||
const mariadbRootUserPassword = encrypt(generatePassword({}));
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
appwrite: {
|
|
||||||
create: {
|
|
||||||
opensslKeyV1,
|
|
||||||
executorSecret,
|
|
||||||
redisPassword,
|
|
||||||
mariadbHost,
|
|
||||||
mariadbUser,
|
|
||||||
mariadbPassword,
|
|
||||||
mariadbDatabase,
|
|
||||||
mariadbRootUser,
|
|
||||||
mariadbRootUserPassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'glitchTip') {
|
|
||||||
const defaultUsername = cuid();
|
|
||||||
const defaultEmail = `${defaultUsername}@example.com`;
|
|
||||||
const defaultPassword = encrypt(generatePassword({}));
|
|
||||||
const postgresqlUser = cuid();
|
|
||||||
const postgresqlPassword = encrypt(generatePassword({}));
|
|
||||||
const postgresqlDatabase = 'glitchTip';
|
|
||||||
const secretKeyBase = encrypt(generatePassword({ length: 64 }));
|
|
||||||
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
glitchTip: {
|
|
||||||
create: {
|
|
||||||
postgresqlDatabase,
|
|
||||||
postgresqlUser,
|
|
||||||
postgresqlPassword,
|
|
||||||
secretKeyBase,
|
|
||||||
defaultEmail,
|
|
||||||
defaultUsername,
|
|
||||||
defaultPassword,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'searxng') {
|
|
||||||
const secretKey = encrypt(generatePassword({ length: 32, isHex: true }))
|
|
||||||
const redisPassword = encrypt(generatePassword({}));
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
searxng: {
|
|
||||||
create: {
|
|
||||||
secretKey,
|
|
||||||
redisPassword,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'weblate') {
|
|
||||||
const adminPassword = encrypt(generatePassword({}))
|
|
||||||
const postgresqlUser = cuid();
|
|
||||||
const postgresqlPassword = encrypt(generatePassword({}));
|
|
||||||
const postgresqlDatabase = 'weblate';
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
weblate: {
|
|
||||||
create: {
|
|
||||||
adminPassword,
|
|
||||||
postgresqlHost: `${id}-postgresql`,
|
|
||||||
postgresqlPort: 5432,
|
|
||||||
postgresqlUser,
|
|
||||||
postgresqlPassword,
|
|
||||||
postgresqlDatabase,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (type === 'taiga') {
|
|
||||||
const secretKey = encrypt(generatePassword({}))
|
|
||||||
const erlangSecret = encrypt(generatePassword({}))
|
|
||||||
const rabbitMQUser = cuid();
|
|
||||||
const djangoAdminUser = cuid();
|
|
||||||
const djangoAdminPassword = encrypt(generatePassword({}))
|
|
||||||
const rabbitMQPassword = encrypt(generatePassword({}))
|
|
||||||
const postgresqlUser = cuid();
|
|
||||||
const postgresqlPassword = encrypt(generatePassword({}));
|
|
||||||
const postgresqlDatabase = 'taiga';
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
taiga: {
|
|
||||||
create: {
|
|
||||||
secretKey,
|
|
||||||
erlangSecret,
|
|
||||||
djangoAdminUser,
|
|
||||||
djangoAdminPassword,
|
|
||||||
rabbitMQUser,
|
|
||||||
rabbitMQPassword,
|
|
||||||
postgresqlHost: `${id}-postgresql`,
|
|
||||||
postgresqlPort: 5432,
|
|
||||||
postgresqlUser,
|
|
||||||
postgresqlPassword,
|
|
||||||
postgresqlDatabase,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await prisma.service.update({
|
|
||||||
where: { id },
|
|
||||||
data: {
|
|
||||||
type
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function removeService({ id }: { id: string }): Promise<void> {
|
export async function removeService({ id }: { id: string }): Promise<void> {
|
||||||
await prisma.serviceSecret.deleteMany({ where: { serviceId: id } });
|
await prisma.serviceSecret.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.serviceSetting.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } });
|
await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
|
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.fider.deleteMany({ where: { serviceId: id } });
|
await prisma.fider.deleteMany({ where: { serviceId: id } });
|
||||||
@@ -378,6 +20,20 @@ export async function removeService({ id }: { id: string }): Promise<void> {
|
|||||||
await prisma.searxng.deleteMany({ where: { serviceId: id } });
|
await prisma.searxng.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.weblate.deleteMany({ where: { serviceId: id } });
|
await prisma.weblate.deleteMany({ where: { serviceId: id } });
|
||||||
await prisma.taiga.deleteMany({ where: { serviceId: id } });
|
await prisma.taiga.deleteMany({ where: { serviceId: id } });
|
||||||
|
|
||||||
await prisma.service.delete({ where: { id } });
|
await prisma.service.delete({ where: { id } });
|
||||||
|
}
|
||||||
|
export async function verifyAndDecryptServiceSecrets(id: string) {
|
||||||
|
const secrets = await prisma.serviceSecret.findMany({ where: { serviceId: id } })
|
||||||
|
let decryptedSecrets = secrets.map(secret => {
|
||||||
|
const { name, value } = secret
|
||||||
|
if (value) {
|
||||||
|
let rawValue = decrypt(value)
|
||||||
|
rawValue = rawValue.replaceAll(/\$/gi, '$$$')
|
||||||
|
return { name, value: rawValue }
|
||||||
|
}
|
||||||
|
return { name, value }
|
||||||
|
|
||||||
|
})
|
||||||
|
return decryptedSecrets
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,215 +0,0 @@
|
|||||||
export const supportedServiceTypesAndVersions = [
|
|
||||||
{
|
|
||||||
name: 'plausibleanalytics',
|
|
||||||
fancyName: 'Plausible Analytics',
|
|
||||||
baseImage: 'plausible/analytics',
|
|
||||||
images: ['bitnami/postgresql:13.2.0', 'yandex/clickhouse-server:21.3.2.5'],
|
|
||||||
versions: ['latest', 'stable'],
|
|
||||||
recommendedVersion: 'stable',
|
|
||||||
ports: {
|
|
||||||
main: 8000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'nocodb',
|
|
||||||
fancyName: 'NocoDB',
|
|
||||||
baseImage: 'nocodb/nocodb',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'minio',
|
|
||||||
fancyName: 'MinIO',
|
|
||||||
baseImage: 'minio/minio',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 9001
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vscodeserver',
|
|
||||||
fancyName: 'VSCode Server',
|
|
||||||
baseImage: 'codercom/code-server',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'wordpress',
|
|
||||||
fancyName: 'Wordpress',
|
|
||||||
baseImage: 'wordpress',
|
|
||||||
images: ['bitnami/mysql:5.7'],
|
|
||||||
versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 80
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vaultwarden',
|
|
||||||
fancyName: 'Vaultwarden',
|
|
||||||
baseImage: 'vaultwarden/server',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 80
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'languagetool',
|
|
||||||
fancyName: 'LanguageTool',
|
|
||||||
baseImage: 'silviof/docker-languagetool',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8010
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'n8n',
|
|
||||||
fancyName: 'n8n',
|
|
||||||
baseImage: 'n8nio/n8n',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 5678
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'uptimekuma',
|
|
||||||
fancyName: 'Uptime Kuma',
|
|
||||||
baseImage: 'louislam/uptime-kuma',
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 3001
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ghost',
|
|
||||||
fancyName: 'Ghost',
|
|
||||||
baseImage: 'bitnami/ghost',
|
|
||||||
images: ['bitnami/mariadb'],
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 2368
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'meilisearch',
|
|
||||||
fancyName: 'Meilisearch',
|
|
||||||
baseImage: 'getmeili/meilisearch',
|
|
||||||
images: [],
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 7700
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'umami',
|
|
||||||
fancyName: 'Umami',
|
|
||||||
baseImage: 'ghcr.io/mikecao/umami',
|
|
||||||
images: ['postgres:12-alpine'],
|
|
||||||
versions: ['postgresql-latest'],
|
|
||||||
recommendedVersion: 'postgresql-latest',
|
|
||||||
ports: {
|
|
||||||
main: 3000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'hasura',
|
|
||||||
fancyName: 'Hasura',
|
|
||||||
baseImage: 'hasura/graphql-engine',
|
|
||||||
images: ['postgres:12-alpine'],
|
|
||||||
versions: ['latest', 'v2.10.0', 'v2.5.1'],
|
|
||||||
recommendedVersion: 'v2.10.0',
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'fider',
|
|
||||||
fancyName: 'Fider',
|
|
||||||
baseImage: 'getfider/fider',
|
|
||||||
images: ['postgres:12-alpine'],
|
|
||||||
versions: ['stable'],
|
|
||||||
recommendedVersion: 'stable',
|
|
||||||
ports: {
|
|
||||||
main: 3000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'appwrite',
|
|
||||||
fancyName: 'Appwrite',
|
|
||||||
baseImage: 'appwrite/appwrite',
|
|
||||||
images: ['mariadb:10.7', 'redis:6.2-alpine', 'appwrite/telegraf:1.4.0'],
|
|
||||||
versions: ['latest', '0.15.3'],
|
|
||||||
recommendedVersion: '0.15.3',
|
|
||||||
ports: {
|
|
||||||
main: 80
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// name: 'moodle',
|
|
||||||
// fancyName: 'Moodle',
|
|
||||||
// baseImage: 'bitnami/moodle',
|
|
||||||
// images: [],
|
|
||||||
// versions: ['latest', 'v4.0.2'],
|
|
||||||
// recommendedVersion: 'latest',
|
|
||||||
// ports: {
|
|
||||||
// main: 8080
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
{
|
|
||||||
name: 'glitchTip',
|
|
||||||
fancyName: 'GlitchTip',
|
|
||||||
baseImage: 'glitchtip/glitchtip',
|
|
||||||
images: ['postgres:14-alpine', 'redis:7-alpine'],
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'searxng',
|
|
||||||
fancyName: 'SearXNG',
|
|
||||||
baseImage: 'searxng/searxng',
|
|
||||||
images: [],
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'weblate',
|
|
||||||
fancyName: 'Weblate',
|
|
||||||
baseImage: 'weblate/weblate',
|
|
||||||
images: ['postgres:14-alpine', 'redis:6-alpine'],
|
|
||||||
versions: ['latest'],
|
|
||||||
recommendedVersion: 'latest',
|
|
||||||
ports: {
|
|
||||||
main: 8080
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// name: 'taiga',
|
|
||||||
// fancyName: 'Taiga',
|
|
||||||
// baseImage: 'taigaio/taiga-front',
|
|
||||||
// images: ['postgres:12.3', 'rabbitmq:3.8-management-alpine', 'taigaio/taiga-back', 'taigaio/taiga-events', 'taigaio/taiga-protected'],
|
|
||||||
// versions: ['latest'],
|
|
||||||
// recommendedVersion: 'latest',
|
|
||||||
// ports: {
|
|
||||||
// main: 80
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
];
|
|
||||||
@@ -21,7 +21,6 @@ export default fp<FastifyJWTOptions>(async (fastify, opts) => {
|
|||||||
try {
|
try {
|
||||||
await request.jwtVerify()
|
await request.jwtVerify()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
|
||||||
reply.send(err)
|
reply.send(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
29
apps/api/src/realtime/index.ts
Normal file
29
apps/api/src/realtime/index.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
export default async (fastify) => {
|
||||||
|
fastify.io.use((socket, next) => {
|
||||||
|
const { token } = socket.handshake.auth;
|
||||||
|
if (token && fastify.jwt.verify(token)) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
return next(new Error("unauthorized event"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fastify.io.on('connection', (socket: any) => {
|
||||||
|
const { token } = socket.handshake.auth;
|
||||||
|
const { teamId } = fastify.jwt.decode(token);
|
||||||
|
socket.join(teamId);
|
||||||
|
// console.info('Socket connected!', socket.id)
|
||||||
|
// console.info('Socket joined team!', teamId)
|
||||||
|
// socket.on('message', (message) => {
|
||||||
|
// console.log(message)
|
||||||
|
// })
|
||||||
|
// socket.on('error', (err) => {
|
||||||
|
// console.log(err)
|
||||||
|
// })
|
||||||
|
})
|
||||||
|
// fastify.io.on("error", (err) => {
|
||||||
|
// if (err && err.message === "unauthorized event") {
|
||||||
|
// fastify.io.disconnect();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
|||||||
import { FastifyPluginAsync } from 'fastify';
|
import { FastifyPluginAsync } from 'fastify';
|
||||||
import { OnlyId } from '../../../../types';
|
import { OnlyId } from '../../../../types';
|
||||||
import { cancelDeployment, checkDNS, checkDomain, checkRepository, deleteApplication, deleteSecret, deleteStorage, deployApplication, getApplication, getApplicationLogs, getApplicationStatus, getBuildIdLogs, getBuildLogs, getBuildPack, getGitHubToken, getGitLabSSHKey, getImages, getPreviews, getSecrets, getStorages, getUsage, listApplications, newApplication, restartApplication, saveApplication, saveApplicationSettings, saveApplicationSource, saveBuildPack, saveDeployKey, saveDestination, saveGitLabSSHKey, saveRepository, saveSecret, saveStorage, stopApplication, stopPreviewApplication } from './handlers';
|
import { cancelDeployment, checkDNS, checkDomain, checkRepository, cleanupUnconfiguredApplications, deleteApplication, deleteSecret, deleteStorage, deployApplication, getApplication, getApplicationLogs, getApplicationStatus, getBuildIdLogs, getBuildPack, getBuilds, getDockerImages, getGitHubToken, getGitLabSSHKey, getImages, getPreviews, getPreviewStatus, getSecrets, getStorages, getUsage, getUsageByContainer, listApplications, loadPreviews, newApplication, restartApplication, restartPreview, saveApplication, saveApplicationSettings, saveApplicationSource, saveBuildPack, saveConnectedDatabase, saveDeployKey, saveDestination, saveGitLabSSHKey, saveRegistry, saveRepository, saveSecret, saveStorage, stopApplication, stopPreviewApplication, updatePreviewSecret, updateSecret } from './handlers';
|
||||||
|
|
||||||
import type { CancelDeployment, CheckDNS, CheckDomain, CheckRepository, DeleteApplication, DeleteSecret, DeleteStorage, DeployApplication, GetApplicationLogs, GetBuildIdLogs, GetBuildLogs, GetImages, SaveApplication, SaveApplicationSettings, SaveApplicationSource, SaveDeployKey, SaveDestination, SaveSecret, SaveStorage, StopPreviewApplication } from './types';
|
import type { CancelDeployment, CheckDNS, CheckDomain, CheckRepository, DeleteApplication, DeleteSecret, DeleteStorage, DeployApplication, GetApplicationLogs, GetBuildIdLogs, GetBuilds, GetImages, RestartApplication, RestartPreviewApplication, SaveApplication, SaveApplicationSettings, SaveApplicationSource, SaveDeployKey, SaveDestination, SaveSecret, SaveStorage, StopPreviewApplication } from './types';
|
||||||
|
|
||||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||||
fastify.addHook('onRequest', async (request) => {
|
fastify.addHook('onRequest', async (request) => {
|
||||||
@@ -11,6 +11,8 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
fastify.get('/', async (request) => await listApplications(request));
|
fastify.get('/', async (request) => await listApplications(request));
|
||||||
fastify.post<GetImages>('/images', async (request) => await getImages(request));
|
fastify.post<GetImages>('/images', async (request) => await getImages(request));
|
||||||
|
|
||||||
|
fastify.post<any>('/cleanup/unconfigured', async (request) => await cleanupUnconfiguredApplications(request));
|
||||||
|
|
||||||
fastify.post('/new', async (request, reply) => await newApplication(request, reply));
|
fastify.post('/new', async (request, reply) => await newApplication(request, reply));
|
||||||
|
|
||||||
fastify.get<OnlyId>('/:id', async (request) => await getApplication(request));
|
fastify.get<OnlyId>('/:id', async (request) => await getApplication(request));
|
||||||
@@ -19,7 +21,7 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
|
|
||||||
fastify.get<OnlyId>('/:id/status', async (request) => await getApplicationStatus(request));
|
fastify.get<OnlyId>('/:id/status', async (request) => await getApplicationStatus(request));
|
||||||
|
|
||||||
fastify.post<OnlyId>('/:id/restart', async (request, reply) => await restartApplication(request, reply));
|
fastify.post<RestartApplication>('/:id/restart', async (request, reply) => await restartApplication(request, reply));
|
||||||
fastify.post<OnlyId>('/:id/stop', async (request, reply) => await stopApplication(request, reply));
|
fastify.post<OnlyId>('/:id/stop', async (request, reply) => await stopApplication(request, reply));
|
||||||
fastify.post<StopPreviewApplication>('/:id/stop/preview', async (request, reply) => await stopPreviewApplication(request, reply));
|
fastify.post<StopPreviewApplication>('/:id/stop/preview', async (request, reply) => await stopPreviewApplication(request, reply));
|
||||||
|
|
||||||
@@ -30,6 +32,8 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
|
|
||||||
fastify.get<OnlyId>('/:id/secrets', async (request) => await getSecrets(request));
|
fastify.get<OnlyId>('/:id/secrets', async (request) => await getSecrets(request));
|
||||||
fastify.post<SaveSecret>('/:id/secrets', async (request, reply) => await saveSecret(request, reply));
|
fastify.post<SaveSecret>('/:id/secrets', async (request, reply) => await saveSecret(request, reply));
|
||||||
|
fastify.put<SaveSecret>('/:id/secrets', async (request, reply) => await updateSecret(request, reply));
|
||||||
|
fastify.put<SaveSecret>('/:id/secrets/preview', async (request, reply) => await updatePreviewSecret(request, reply));
|
||||||
fastify.delete<DeleteSecret>('/:id/secrets', async (request) => await deleteSecret(request));
|
fastify.delete<DeleteSecret>('/:id/secrets', async (request) => await deleteSecret(request));
|
||||||
|
|
||||||
fastify.get<OnlyId>('/:id/storages', async (request) => await getStorages(request));
|
fastify.get<OnlyId>('/:id/storages', async (request) => await getStorages(request));
|
||||||
@@ -37,12 +41,18 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
fastify.delete<DeleteStorage>('/:id/storages', async (request) => await deleteStorage(request));
|
fastify.delete<DeleteStorage>('/:id/storages', async (request) => await deleteStorage(request));
|
||||||
|
|
||||||
fastify.get<OnlyId>('/:id/previews', async (request) => await getPreviews(request));
|
fastify.get<OnlyId>('/:id/previews', async (request) => await getPreviews(request));
|
||||||
|
fastify.post<OnlyId>('/:id/previews/load', async (request) => await loadPreviews(request));
|
||||||
|
fastify.get<RestartPreviewApplication>('/:id/previews/:pullmergeRequestId/status', async (request) => await getPreviewStatus(request));
|
||||||
|
fastify.post<RestartPreviewApplication>('/:id/previews/:pullmergeRequestId/restart', async (request, reply) => await restartPreview(request, reply));
|
||||||
|
|
||||||
fastify.get<GetApplicationLogs>('/:id/logs', async (request) => await getApplicationLogs(request));
|
fastify.get<GetApplicationLogs>('/:id/logs/:containerId', async (request) => await getApplicationLogs(request));
|
||||||
fastify.get<GetBuildLogs>('/:id/logs/build', async (request) => await getBuildLogs(request));
|
fastify.get<GetBuilds>('/:id/logs/build', async (request) => await getBuilds(request));
|
||||||
fastify.get<GetBuildIdLogs>('/:id/logs/build/:buildId', async (request) => await getBuildIdLogs(request));
|
fastify.get<GetBuildIdLogs>('/:id/logs/build/:buildId', async (request) => await getBuildIdLogs(request));
|
||||||
|
|
||||||
fastify.get('/:id/usage', async (request) => await getUsage(request))
|
fastify.get('/:id/usage', async (request) => await getUsage(request))
|
||||||
|
fastify.get('/:id/usage/:containerId', async (request) => await getUsageByContainer(request))
|
||||||
|
|
||||||
|
fastify.get('/:id/images', async (request) => await getDockerImages(request))
|
||||||
|
|
||||||
fastify.post<DeployApplication>('/:id/deploy', async (request) => await deployApplication(request))
|
fastify.post<DeployApplication>('/:id/deploy', async (request) => await deployApplication(request))
|
||||||
fastify.post<CancelDeployment>('/:id/cancel', async (request, reply) => await cancelDeployment(request, reply));
|
fastify.post<CancelDeployment>('/:id/cancel', async (request, reply) => await cancelDeployment(request, reply));
|
||||||
@@ -55,6 +65,10 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
fastify.get('/:id/configuration/buildpack', async (request) => await getBuildPack(request));
|
fastify.get('/:id/configuration/buildpack', async (request) => await getBuildPack(request));
|
||||||
fastify.post('/:id/configuration/buildpack', async (request, reply) => await saveBuildPack(request, reply));
|
fastify.post('/:id/configuration/buildpack', async (request, reply) => await saveBuildPack(request, reply));
|
||||||
|
|
||||||
|
fastify.post('/:id/configuration/registry', async (request, reply) => await saveRegistry(request, reply));
|
||||||
|
|
||||||
|
fastify.post('/:id/configuration/database', async (request, reply) => await saveConnectedDatabase(request, reply));
|
||||||
|
|
||||||
fastify.get<OnlyId>('/:id/configuration/sshkey', async (request) => await getGitLabSSHKey(request));
|
fastify.get<OnlyId>('/:id/configuration/sshkey', async (request) => await getGitLabSSHKey(request));
|
||||||
fastify.post<OnlyId>('/:id/configuration/sshkey', async (request, reply) => await saveGitLabSSHKey(request, reply));
|
fastify.post<OnlyId>('/:id/configuration/sshkey', async (request, reply) => await saveGitLabSSHKey(request, reply));
|
||||||
|
|
||||||
|
|||||||
@@ -1,128 +1,170 @@
|
|||||||
import type { OnlyId } from "../../../../types";
|
import type { OnlyId } from '../../../../types';
|
||||||
|
|
||||||
export interface SaveApplication extends OnlyId {
|
export interface SaveApplication extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
name: string,
|
name: string;
|
||||||
buildPack: string,
|
buildPack: string;
|
||||||
fqdn: string,
|
fqdn: string;
|
||||||
port: number,
|
port: number;
|
||||||
exposePort: number,
|
exposePort: number;
|
||||||
installCommand: string,
|
installCommand: string;
|
||||||
buildCommand: string,
|
buildCommand: string;
|
||||||
startCommand: string,
|
startCommand: string;
|
||||||
baseDirectory: string,
|
baseDirectory: string;
|
||||||
publishDirectory: string,
|
publishDirectory: string;
|
||||||
pythonWSGI: string,
|
pythonWSGI: string;
|
||||||
pythonModule: string,
|
pythonModule: string;
|
||||||
pythonVariable: string,
|
pythonVariable: string;
|
||||||
dockerFileLocation: string,
|
dockerFileLocation: string;
|
||||||
denoMainFile: string,
|
denoMainFile: string;
|
||||||
denoOptions: string,
|
denoOptions: string;
|
||||||
baseImage: string,
|
baseImage: string;
|
||||||
baseBuildImage: string,
|
gitCommitHash: string;
|
||||||
deploymentType: string
|
baseBuildImage: string;
|
||||||
}
|
deploymentType: string;
|
||||||
|
baseDatabaseBranch: string;
|
||||||
|
dockerComposeFile: string;
|
||||||
|
dockerComposeFileLocation: string;
|
||||||
|
dockerComposeConfiguration: string;
|
||||||
|
simpleDockerfile: string;
|
||||||
|
dockerRegistryImageName: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export interface SaveApplicationSettings extends OnlyId {
|
export interface SaveApplicationSettings extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string };
|
||||||
Body: { debug: boolean; previews: boolean; dualCerts: boolean; autodeploy: boolean; branch: string; projectId: number; isBot: boolean; };
|
Body: {
|
||||||
|
debug: boolean;
|
||||||
|
previews: boolean;
|
||||||
|
dualCerts: boolean;
|
||||||
|
autodeploy: boolean;
|
||||||
|
branch: string;
|
||||||
|
projectId: number;
|
||||||
|
isBot: boolean;
|
||||||
|
isDBBranching: boolean;
|
||||||
|
isCustomSSL: boolean;
|
||||||
|
isHttp2: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export interface DeleteApplication extends OnlyId {
|
export interface DeleteApplication extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string };
|
||||||
Body: { force: boolean }
|
Body: { force: boolean };
|
||||||
}
|
}
|
||||||
export interface CheckDomain extends OnlyId {
|
export interface CheckDomain extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string };
|
||||||
}
|
}
|
||||||
export interface CheckDNS extends OnlyId {
|
export interface CheckDNS extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string };
|
||||||
Body: {
|
Body: {
|
||||||
exposePort: number,
|
exposePort: number;
|
||||||
fqdn: string,
|
fqdn: string;
|
||||||
forceSave: boolean,
|
forceSave: boolean;
|
||||||
dualCerts: boolean
|
dualCerts: boolean;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface DeployApplication {
|
export interface DeployApplication {
|
||||||
Querystring: { domain: string }
|
Querystring: { domain: string };
|
||||||
Body: { pullmergeRequestId: string | null, branch: string, forceRebuild?: boolean }
|
Body: { pullmergeRequestId: string | null; branch: string; forceRebuild?: boolean };
|
||||||
}
|
}
|
||||||
export interface GetImages {
|
export interface GetImages {
|
||||||
Body: { buildPack: string, deploymentType: string }
|
Body: { buildPack: string; deploymentType: string };
|
||||||
}
|
}
|
||||||
export interface SaveApplicationSource extends OnlyId {
|
export interface SaveApplicationSource extends OnlyId {
|
||||||
Body: { gitSourceId?: string | null, forPublic?: boolean, type?: string }
|
Body: {
|
||||||
|
gitSourceId?: string | null;
|
||||||
|
forPublic?: boolean;
|
||||||
|
type?: string;
|
||||||
|
simpleDockerfile?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export interface CheckRepository extends OnlyId {
|
export interface CheckRepository extends OnlyId {
|
||||||
Querystring: { repository: string, branch: string }
|
Querystring: { repository: string; branch: string };
|
||||||
}
|
}
|
||||||
export interface SaveDestination extends OnlyId {
|
export interface SaveDestination extends OnlyId {
|
||||||
Body: { destinationId: string }
|
Body: { destinationId: string };
|
||||||
}
|
}
|
||||||
export interface SaveSecret extends OnlyId {
|
export interface SaveSecret extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
name: string,
|
name: string;
|
||||||
value: string,
|
value: string;
|
||||||
isBuildSecret: boolean,
|
isBuildSecret: boolean;
|
||||||
isPRMRSecret: boolean,
|
previewSecret: boolean;
|
||||||
isNew: boolean
|
isNew: boolean;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface DeleteSecret extends OnlyId {
|
export interface DeleteSecret extends OnlyId {
|
||||||
Body: { name: string }
|
Body: { name: string };
|
||||||
}
|
}
|
||||||
export interface SaveStorage extends OnlyId {
|
export interface SaveStorage extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
path: string,
|
path: string;
|
||||||
newStorage: boolean,
|
newStorage: boolean;
|
||||||
storageId: string
|
storageId: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface DeleteStorage extends OnlyId {
|
export interface DeleteStorage extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
path: string,
|
path: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface GetApplicationLogs extends OnlyId {
|
export interface GetApplicationLogs {
|
||||||
Querystring: {
|
Params: {
|
||||||
since: number,
|
id: string;
|
||||||
}
|
containerId: string;
|
||||||
|
};
|
||||||
|
Querystring: {
|
||||||
|
since: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export interface GetBuildLogs extends OnlyId {
|
export interface GetBuilds extends OnlyId {
|
||||||
Querystring: {
|
Querystring: {
|
||||||
buildId: string
|
buildId: string;
|
||||||
skip: number,
|
skip: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface GetBuildIdLogs {
|
export interface GetBuildIdLogs {
|
||||||
Params: {
|
Params: {
|
||||||
buildId: string
|
id: string;
|
||||||
},
|
buildId: string;
|
||||||
Querystring: {
|
};
|
||||||
sequence: number
|
Querystring: {
|
||||||
}
|
sequence: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export interface SaveDeployKey extends OnlyId {
|
export interface SaveDeployKey extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
deployKeyId: number
|
deployKeyId: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface CancelDeployment {
|
export interface CancelDeployment {
|
||||||
Body: {
|
Body: {
|
||||||
buildId: string,
|
buildId: string;
|
||||||
applicationId: string
|
applicationId: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
export interface DeployApplication extends OnlyId {
|
export interface DeployApplication extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
pullmergeRequestId: string | null,
|
pullmergeRequestId: string | null;
|
||||||
branch: string,
|
branch: string;
|
||||||
forceRebuild?: boolean
|
forceRebuild?: boolean;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StopPreviewApplication extends OnlyId {
|
export interface StopPreviewApplication extends OnlyId {
|
||||||
Body: {
|
Body: {
|
||||||
pullmergeRequestId: string | null,
|
pullmergeRequestId: string | null;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
export interface RestartPreviewApplication {
|
||||||
|
Params: {
|
||||||
|
id: string;
|
||||||
|
pullmergeRequestId: string | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export interface RestartApplication {
|
||||||
|
Params: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
Body: {
|
||||||
|
imageId: string | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,22 +1,31 @@
|
|||||||
import { FastifyPluginAsync } from 'fastify';
|
import { FastifyPluginAsync } from 'fastify';
|
||||||
import { errorHandler, listSettings, version } from '../../../../lib/common';
|
import { errorHandler, isARM, listSettings, version } from '../../../../lib/common';
|
||||||
|
|
||||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||||
fastify.get('/', async () => {
|
fastify.addHook('onRequest', async (request) => {
|
||||||
const settings = await listSettings()
|
try {
|
||||||
try {
|
await request.jwtVerify();
|
||||||
return {
|
} catch (error) {
|
||||||
ipv4: settings.ipv4,
|
return;
|
||||||
ipv6: settings.ipv6,
|
}
|
||||||
version,
|
});
|
||||||
whiteLabeled: process.env.COOLIFY_WHITE_LABELED === 'true',
|
fastify.get('/', async (request) => {
|
||||||
whiteLabeledIcon: process.env.COOLIFY_WHITE_LABELED_ICON,
|
const teamId = request.user?.teamId;
|
||||||
}
|
const settings = await listSettings();
|
||||||
} catch ({ status, message }) {
|
try {
|
||||||
return errorHandler({ status, message })
|
return {
|
||||||
}
|
ipv4: teamId ? settings.ipv4 : null,
|
||||||
});
|
ipv6: teamId ? settings.ipv6 : null,
|
||||||
|
version,
|
||||||
|
whiteLabeled: process.env.COOLIFY_WHITE_LABELED === 'true',
|
||||||
|
whiteLabeledIcon: process.env.COOLIFY_WHITE_LABELED_ICON,
|
||||||
|
isRegistrationEnabled: settings.isRegistrationEnabled,
|
||||||
|
isARM: isARM(process.arch)
|
||||||
|
};
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message });
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default root;
|
export default root;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,9 @@
|
|||||||
import { FastifyPluginAsync } from 'fastify';
|
import { FastifyPluginAsync } from 'fastify';
|
||||||
import { deleteDatabase, getDatabase, getDatabaseLogs, getDatabaseStatus, getDatabaseTypes, getDatabaseUsage, getVersions, listDatabases, newDatabase, saveDatabase, saveDatabaseDestination, saveDatabaseSettings, saveDatabaseType, saveVersion, startDatabase, stopDatabase } from './handlers';
|
import { cleanupUnconfiguredDatabases, deleteDatabase, deleteDatabaseSecret, getDatabase, getDatabaseLogs, getDatabaseSecrets, getDatabaseStatus, getDatabaseTypes, getDatabaseUsage, getVersions, listDatabases, newDatabase, saveDatabase, saveDatabaseDestination, saveDatabaseSecret, saveDatabaseSettings, saveDatabaseType, saveVersion, startDatabase, stopDatabase } from './handlers';
|
||||||
|
|
||||||
import type { DeleteDatabase, GetDatabaseLogs, OnlyId, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSettings, SaveVersion } from '../../../../types';
|
import type { OnlyId } from '../../../../types';
|
||||||
import type { SaveDatabaseType } from './types';
|
|
||||||
|
import type { DeleteDatabase, SaveDatabaseType, DeleteDatabaseSecret, GetDatabaseLogs, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSecret, SaveDatabaseSettings, SaveVersion } from './types';
|
||||||
|
|
||||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||||
fastify.addHook('onRequest', async (request) => {
|
fastify.addHook('onRequest', async (request) => {
|
||||||
@@ -11,6 +12,8 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
fastify.get('/', async (request) => await listDatabases(request));
|
fastify.get('/', async (request) => await listDatabases(request));
|
||||||
fastify.post('/new', async (request, reply) => await newDatabase(request, reply));
|
fastify.post('/new', async (request, reply) => await newDatabase(request, reply));
|
||||||
|
|
||||||
|
fastify.post<any>('/cleanup/unconfigured', async (request) => await cleanupUnconfiguredDatabases(request));
|
||||||
|
|
||||||
fastify.get<OnlyId>('/:id', async (request) => await getDatabase(request));
|
fastify.get<OnlyId>('/:id', async (request) => await getDatabase(request));
|
||||||
fastify.post<SaveDatabase>('/:id', async (request, reply) => await saveDatabase(request, reply));
|
fastify.post<SaveDatabase>('/:id', async (request, reply) => await saveDatabase(request, reply));
|
||||||
fastify.delete<DeleteDatabase>('/:id', async (request) => await deleteDatabase(request));
|
fastify.delete<DeleteDatabase>('/:id', async (request) => await deleteDatabase(request));
|
||||||
@@ -19,6 +22,10 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
|
|
||||||
fastify.post<SaveDatabaseSettings>('/:id/settings', async (request) => await saveDatabaseSettings(request));
|
fastify.post<SaveDatabaseSettings>('/:id/settings', async (request) => await saveDatabaseSettings(request));
|
||||||
|
|
||||||
|
fastify.get<OnlyId>('/:id/secrets', async (request) => await getDatabaseSecrets(request));
|
||||||
|
fastify.post<SaveDatabaseSecret>('/:id/secrets', async (request, reply) => await saveDatabaseSecret(request, reply));
|
||||||
|
fastify.delete<DeleteDatabaseSecret>('/:id/secrets', async (request) => await deleteDatabaseSecret(request));
|
||||||
|
|
||||||
fastify.get('/:id/configuration/type', async (request) => await getDatabaseTypes(request));
|
fastify.get('/:id/configuration/type', async (request) => await getDatabaseTypes(request));
|
||||||
fastify.post<SaveDatabaseType>('/:id/configuration/type', async (request, reply) => await saveDatabaseType(request, reply));
|
fastify.post<SaveDatabaseType>('/:id/configuration/type', async (request, reply) => await saveDatabaseType(request, reply));
|
||||||
|
|
||||||
|
|||||||
@@ -5,4 +5,51 @@ export interface SaveDatabaseType extends OnlyId {
|
|||||||
}
|
}
|
||||||
export interface DeleteDatabase extends OnlyId {
|
export interface DeleteDatabase extends OnlyId {
|
||||||
Body: { force: string }
|
Body: { force: string }
|
||||||
}
|
}
|
||||||
|
export interface SaveVersion extends OnlyId {
|
||||||
|
Body: {
|
||||||
|
version: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SaveDatabaseDestination extends OnlyId {
|
||||||
|
Body: {
|
||||||
|
destinationId: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface GetDatabaseLogs extends OnlyId {
|
||||||
|
Querystring: {
|
||||||
|
since: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SaveDatabase extends OnlyId {
|
||||||
|
Body: {
|
||||||
|
name: string,
|
||||||
|
defaultDatabase: string,
|
||||||
|
dbUser: string,
|
||||||
|
dbUserPassword: string,
|
||||||
|
rootUser: string,
|
||||||
|
rootUserPassword: string,
|
||||||
|
version: string,
|
||||||
|
isRunning: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SaveDatabaseSettings extends OnlyId {
|
||||||
|
Body: {
|
||||||
|
isPublic: boolean,
|
||||||
|
appendOnly: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SaveDatabaseSecret extends OnlyId {
|
||||||
|
Body: {
|
||||||
|
name: string,
|
||||||
|
value: string,
|
||||||
|
isNew: string,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface DeleteDatabaseSecret extends OnlyId {
|
||||||
|
Body: {
|
||||||
|
name: string,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import sshConfig from 'ssh-config'
|
|||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
|
|
||||||
import { asyncExecShell, createRemoteEngineConfiguration, decrypt, errorHandler, executeDockerCmd, listSettings, prisma, startTraefikProxy, stopTraefikProxy } from '../../../../lib/common';
|
import { createRemoteEngineConfiguration, decrypt, errorHandler, executeCommand, listSettings, prisma, startTraefikProxy, stopTraefikProxy } from '../../../../lib/common';
|
||||||
import { checkContainer } from '../../../../lib/docker';
|
import { checkContainer } from '../../../../lib/docker';
|
||||||
|
|
||||||
import type { OnlyId } from '../../../../types';
|
import type { OnlyId } from '../../../../types';
|
||||||
@@ -30,7 +30,6 @@ export async function listDestinations(request: FastifyRequest<ListDestinations>
|
|||||||
destinations
|
destinations
|
||||||
}
|
}
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
console.log({ status, message })
|
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,9 +79,9 @@ export async function newDestination(request: FastifyRequest<NewDestination>, re
|
|||||||
let { name, network, engine, isCoolifyProxyUsed, remoteIpAddress, remoteUser, remotePort } = request.body
|
let { name, network, engine, isCoolifyProxyUsed, remoteIpAddress, remoteUser, remotePort } = request.body
|
||||||
if (id === 'new') {
|
if (id === 'new') {
|
||||||
if (engine) {
|
if (engine) {
|
||||||
const { stdout } = await asyncExecShell(`DOCKER_HOST=unix:///var/run/docker.sock docker network ls --filter 'name=^${network}$' --format '{{json .}}'`);
|
const { stdout } = await await executeCommand({ command: `docker network ls --filter 'name=^${network}$' --format '{{json .}}'` });
|
||||||
if (stdout === '') {
|
if (stdout === '') {
|
||||||
await asyncExecShell(`DOCKER_HOST=unix:///var/run/docker.sock docker network create --attachable ${network}`);
|
await await executeCommand({ command: `docker network create --attachable ${network}` });
|
||||||
}
|
}
|
||||||
await prisma.destinationDocker.create({
|
await prisma.destinationDocker.create({
|
||||||
data: { name, teams: { connect: { id: teamId } }, engine, network, isCoolifyProxyUsed }
|
data: { name, teams: { connect: { id: teamId } }, engine, network, isCoolifyProxyUsed }
|
||||||
@@ -104,7 +103,7 @@ export async function newDestination(request: FastifyRequest<NewDestination>, re
|
|||||||
return reply.code(201).send({ id: destination.id });
|
return reply.code(201).send({ id: destination.id });
|
||||||
} else {
|
} else {
|
||||||
const destination = await prisma.destinationDocker.create({
|
const destination = await prisma.destinationDocker.create({
|
||||||
data: { name, teams: { connect: { id: teamId } }, engine, network, isCoolifyProxyUsed, remoteEngine: true, remoteIpAddress, remoteUser, remotePort }
|
data: { name, teams: { connect: { id: teamId } }, engine, network, isCoolifyProxyUsed, remoteEngine: true, remoteIpAddress, remoteUser, remotePort: Number(remotePort) }
|
||||||
});
|
});
|
||||||
return reply.code(201).send({ id: destination.id })
|
return reply.code(201).send({ id: destination.id })
|
||||||
}
|
}
|
||||||
@@ -114,7 +113,6 @@ export async function newDestination(request: FastifyRequest<NewDestination>, re
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
console.log({ status, message })
|
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,13 +122,13 @@ export async function deleteDestination(request: FastifyRequest<OnlyId>) {
|
|||||||
const { network, remoteVerified, engine, isCoolifyProxyUsed } = await prisma.destinationDocker.findUnique({ where: { id } });
|
const { network, remoteVerified, engine, isCoolifyProxyUsed } = await prisma.destinationDocker.findUnique({ where: { id } });
|
||||||
if (isCoolifyProxyUsed) {
|
if (isCoolifyProxyUsed) {
|
||||||
if (engine || remoteVerified) {
|
if (engine || remoteVerified) {
|
||||||
const { stdout: found } = await executeDockerCmd({
|
const { stdout: found } = await executeCommand({
|
||||||
dockerId: id,
|
dockerId: id,
|
||||||
command: `docker ps -a --filter network=${network} --filter name=coolify-proxy --format '{{.}}'`
|
command: `docker ps -a --filter network=${network} --filter name=coolify-proxy --format '{{.}}'`
|
||||||
})
|
})
|
||||||
if (found) {
|
if (found) {
|
||||||
await executeDockerCmd({ dockerId: id, command: `docker network disconnect ${network} coolify-proxy` })
|
await executeCommand({ dockerId: id, command: `docker network disconnect ${network} coolify-proxy` })
|
||||||
await executeDockerCmd({ dockerId: id, command: `docker network rm ${network}` })
|
await executeCommand({ dockerId: id, command: `docker network rm ${network}` })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,7 +160,6 @@ export async function startProxy(request: FastifyRequest<Proxy>) {
|
|||||||
await startTraefikProxy(id);
|
await startTraefikProxy(id);
|
||||||
return {}
|
return {}
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
console.log({ status, message })
|
|
||||||
await stopTraefikProxy(id);
|
await stopTraefikProxy(id);
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
@@ -205,27 +202,65 @@ export async function assignSSHKey(request: FastifyRequest) {
|
|||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function verifyRemoteDockerEngine(request: FastifyRequest, reply: FastifyReply) {
|
export async function verifyRemoteDockerEngineFn(id: string) {
|
||||||
|
const { remoteIpAddress, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } })
|
||||||
|
const daemonJson = `daemon-${id}.json`
|
||||||
try {
|
try {
|
||||||
const { id } = request.params;
|
await executeCommand({ sshCommand: true, command: `docker network inspect ${network}`, dockerId: id });
|
||||||
await createRemoteEngineConfiguration(id);
|
} catch (error) {
|
||||||
|
await executeCommand({ command: `docker network create --attachable ${network}`, dockerId: id });
|
||||||
|
}
|
||||||
|
|
||||||
const { remoteIpAddress, remoteUser, network } = await prisma.destinationDocker.findFirst({ where: { id } })
|
try {
|
||||||
const host = `ssh://${remoteUser}@${remoteIpAddress}`
|
await executeCommand({ sshCommand: true, command: `docker network inspect coolify-infra`, dockerId: id });
|
||||||
const { stdout } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=${network}' --no-trunc --format "{{json .}}"`);
|
} catch (error) {
|
||||||
|
await executeCommand({ command: `docker network create --attachable coolify-infra`, dockerId: id });
|
||||||
|
}
|
||||||
|
|
||||||
if (!stdout) {
|
if (isCoolifyProxyUsed) await startTraefikProxy(id);
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`);
|
let isUpdated = false;
|
||||||
|
let daemonJsonParsed = {
|
||||||
|
"live-restore": true,
|
||||||
|
"features": {
|
||||||
|
"buildkit": true
|
||||||
}
|
}
|
||||||
const { stdout:coolifyNetwork } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=coolify-infra' --no-trunc --format "{{json .}}"`);
|
};
|
||||||
|
try {
|
||||||
|
const { stdout: daemonJson } = await executeCommand({ sshCommand: true, dockerId: id, command: `cat /etc/docker/daemon.json` });
|
||||||
|
daemonJsonParsed = JSON.parse(daemonJson);
|
||||||
|
if (!daemonJsonParsed['live-restore'] || daemonJsonParsed['live-restore'] !== true) {
|
||||||
|
isUpdated = true;
|
||||||
|
daemonJsonParsed['live-restore'] = true
|
||||||
|
|
||||||
if (!coolifyNetwork) {
|
}
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable coolify-infra`);
|
if (!daemonJsonParsed?.features?.buildkit) {
|
||||||
|
isUpdated = true;
|
||||||
|
daemonJsonParsed.features = {
|
||||||
|
buildkit: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
isUpdated = true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (isUpdated) {
|
||||||
|
await executeCommand({ shell: true, command: `echo '${JSON.stringify(daemonJsonParsed, null, 2)}' > /tmp/${daemonJson}` })
|
||||||
|
await executeCommand({ dockerId: id, command: `scp /tmp/${daemonJson} ${remoteIpAddress}-remote:/etc/docker/daemon.json` });
|
||||||
|
await executeCommand({ command: `rm /tmp/${daemonJson}` })
|
||||||
|
await executeCommand({ sshCommand: true, dockerId: id, command: `systemctl restart docker` });
|
||||||
}
|
}
|
||||||
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } })
|
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } })
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Error while verifying remote docker engine')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function verifyRemoteDockerEngine(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||||
|
const { id } = request.params;
|
||||||
|
try {
|
||||||
|
await verifyRemoteDockerEngineFn(id);
|
||||||
return reply.code(201).send()
|
return reply.code(201).send()
|
||||||
|
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
|
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: false } })
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,7 +269,7 @@ export async function getDestinationStatus(request: FastifyRequest<OnlyId>) {
|
|||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
const destination = await prisma.destinationDocker.findUnique({ where: { id } })
|
const destination = await prisma.destinationDocker.findUnique({ where: { id } })
|
||||||
const isRunning = await checkContainer({ dockerId: destination.id, container: 'coolify-proxy' })
|
const { found: isRunning } = await checkContainer({ dockerId: destination.id, container: 'coolify-proxy', remove: true })
|
||||||
return {
|
return {
|
||||||
isRunning
|
isRunning
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
|
|
||||||
fastify.post('/:id/configuration/sshKey', async (request) => await assignSSHKey(request));
|
fastify.post('/:id/configuration/sshKey', async (request) => await assignSSHKey(request));
|
||||||
|
|
||||||
fastify.post('/:id/verify', async (request, reply) => await verifyRemoteDockerEngine(request, reply));
|
fastify.post<OnlyId>('/:id/verify', async (request, reply) => await verifyRemoteDockerEngine(request, reply));
|
||||||
};
|
};
|
||||||
|
|
||||||
export default root;
|
export default root;
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
import os from 'node:os';
|
|
||||||
import osu from 'node-os-utils';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { compareVersions } from 'compare-versions';
|
import { compareVersions } from 'compare-versions';
|
||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { asyncExecShell, asyncSleep, cleanupDockerStorage, errorHandler, isDev, listSettings, prisma, uniqueName, version } from '../../../lib/common';
|
import fs from 'fs/promises';
|
||||||
import { supportedServiceTypesAndVersions } from '../../../lib/services/supportedVersions';
|
import yaml from 'js-yaml';
|
||||||
|
import {
|
||||||
|
asyncSleep,
|
||||||
|
cleanupDockerStorage,
|
||||||
|
errorHandler,
|
||||||
|
isDev,
|
||||||
|
listSettings,
|
||||||
|
prisma,
|
||||||
|
uniqueName,
|
||||||
|
version,
|
||||||
|
sentryDSN,
|
||||||
|
executeCommand
|
||||||
|
} from '../../../lib/common';
|
||||||
|
import { scheduler } from '../../../lib/scheduler';
|
||||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||||
import type { Login, Update } from '.';
|
import type { Login, Update } from '.';
|
||||||
import type { GetCurrentUser } from './types';
|
import type { GetCurrentUser } from './types';
|
||||||
@@ -15,36 +25,129 @@ export async function hashPassword(password: string): Promise<string> {
|
|||||||
return bcrypt.hash(password, saltRounds);
|
return bcrypt.hash(password, saltRounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cleanupManually() {
|
export async function backup(request: FastifyRequest) {
|
||||||
try {
|
try {
|
||||||
const destination = await prisma.destinationDocker.findFirst({ where: { engine: '/var/run/docker.sock' } })
|
const { backupData } = request.params;
|
||||||
await cleanupDockerStorage(destination.id, true, true)
|
let std = null;
|
||||||
return {}
|
const [id, backupType, type, zipped, storage] = backupData.split(':');
|
||||||
|
console.log(id, backupType, type, zipped, storage);
|
||||||
|
const database = await prisma.database.findUnique({ where: { id } });
|
||||||
|
if (database) {
|
||||||
|
// await executeDockerCmd({
|
||||||
|
// dockerId: database.destinationDockerId,
|
||||||
|
// command: `docker pull coollabsio/backup:latest`,
|
||||||
|
// })
|
||||||
|
std = await executeCommand({
|
||||||
|
dockerId: database.destinationDockerId,
|
||||||
|
command: `docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v coolify-local-backup:/app/backups -e CONTAINERS_TO_BACKUP="${backupData}" coollabsio/backup`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (std.stdout) {
|
||||||
|
return std.stdout;
|
||||||
|
}
|
||||||
|
if (std.stderr) {
|
||||||
|
return std.stderr;
|
||||||
|
}
|
||||||
|
return 'nope';
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function cleanupManually(request: FastifyRequest) {
|
||||||
|
try {
|
||||||
|
const { serverId } = request.body;
|
||||||
|
const destination = await prisma.destinationDocker.findUnique({
|
||||||
|
where: { id: serverId }
|
||||||
|
});
|
||||||
|
await cleanupDockerStorage(destination.id, true, true);
|
||||||
|
return {};
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function refreshTags() {
|
||||||
|
try {
|
||||||
|
const { default: got } = await import('got');
|
||||||
|
try {
|
||||||
|
if (isDev) {
|
||||||
|
let tags = await fs.readFile('./devTags.json', 'utf8');
|
||||||
|
try {
|
||||||
|
if (await fs.stat('./testTags.json')) {
|
||||||
|
const testTags = await fs.readFile('./testTags.json', 'utf8');
|
||||||
|
if (testTags.length > 0) {
|
||||||
|
tags = JSON.parse(tags).concat(JSON.parse(testTags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
await fs.writeFile('./tags.json', tags);
|
||||||
|
} else {
|
||||||
|
const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text();
|
||||||
|
await fs.writeFile('/app/tags.json', tags);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function refreshTemplates() {
|
||||||
|
try {
|
||||||
|
const { default: got } = await import('got');
|
||||||
|
try {
|
||||||
|
if (isDev) {
|
||||||
|
let templates = await fs.readFile('./devTemplates.yaml', 'utf8');
|
||||||
|
try {
|
||||||
|
if (await fs.stat('./testTemplate.yaml')) {
|
||||||
|
templates = templates + (await fs.readFile('./testTemplate.yaml', 'utf8'));
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
const response = await fs.readFile('./devTemplates.yaml', 'utf8');
|
||||||
|
await fs.writeFile('./templates.json', JSON.stringify(yaml.load(response)));
|
||||||
|
} else {
|
||||||
|
const response = await got
|
||||||
|
.get('https://get.coollabs.io/coolify/service-templates.yaml')
|
||||||
|
.text();
|
||||||
|
await fs.writeFile('/app/templates.json', JSON.stringify(yaml.load(response)));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function checkUpdate(request: FastifyRequest) {
|
export async function checkUpdate(request: FastifyRequest) {
|
||||||
try {
|
try {
|
||||||
const isStaging = request.hostname === 'staging.coolify.io'
|
const { default: got } = await import('got');
|
||||||
|
const isStaging =
|
||||||
|
request.hostname === 'staging.coolify.io' || request.hostname === 'arm.coolify.io';
|
||||||
const currentVersion = version;
|
const currentVersion = version;
|
||||||
const { data: versions } = await axios.get(
|
const { coolify } = await got
|
||||||
`https://get.coollabs.io/versions.json?appId=${process.env['COOLIFY_APP_ID']}&version=${currentVersion}`
|
.get('https://get.coollabs.io/versions.json', {
|
||||||
);
|
searchParams: {
|
||||||
const latestVersion = versions['coolify'].main.version
|
appId: process.env['COOLIFY_APP_ID'] || undefined,
|
||||||
|
version: currentVersion
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.json();
|
||||||
|
const latestVersion = coolify.main.version;
|
||||||
const isUpdateAvailable = compareVersions(latestVersion, currentVersion);
|
const isUpdateAvailable = compareVersions(latestVersion, currentVersion);
|
||||||
if (isStaging) {
|
if (isStaging) {
|
||||||
return {
|
return {
|
||||||
isUpdateAvailable: true,
|
isUpdateAvailable: true,
|
||||||
latestVersion: 'next'
|
latestVersion: 'next'
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
isUpdateAvailable: isStaging ? true : isUpdateAvailable === 1,
|
isUpdateAvailable: isStaging ? true : isUpdateAvailable === 1,
|
||||||
latestVersion
|
latestVersion
|
||||||
};
|
};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,25 +155,37 @@ export async function update(request: FastifyRequest<Update>) {
|
|||||||
const { latestVersion } = request.body;
|
const { latestVersion } = request.body;
|
||||||
try {
|
try {
|
||||||
if (!isDev) {
|
if (!isDev) {
|
||||||
const { isAutoUpdateEnabled } = (await prisma.setting.findFirst()) || {
|
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
|
||||||
isAutoUpdateEnabled: false
|
await executeCommand({ command: `docker pull coollabsio/coolify:${latestVersion}` });
|
||||||
};
|
await executeCommand({ shell: true, command: `env | grep COOLIFY > .env` });
|
||||||
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
|
await executeCommand({
|
||||||
await asyncExecShell(`env | grep COOLIFY > .env`);
|
command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env`
|
||||||
await asyncExecShell(
|
});
|
||||||
`sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env`
|
await executeCommand({
|
||||||
);
|
shell: true,
|
||||||
await asyncExecShell(
|
command: `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-fluentbit && docker rm coolify coolify-fluentbit && docker compose pull && docker compose up -d --force-recreate"`
|
||||||
`docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify && docker rm coolify && docker compose up -d --force-recreate"`
|
});
|
||||||
);
|
|
||||||
return {};
|
return {};
|
||||||
} else {
|
} else {
|
||||||
console.log(latestVersion);
|
|
||||||
await asyncSleep(2000);
|
await asyncSleep(2000);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function resetQueue(request: FastifyRequest<any>) {
|
||||||
|
try {
|
||||||
|
const teamId = request.user.teamId;
|
||||||
|
if (teamId === '0') {
|
||||||
|
await prisma.build.updateMany({
|
||||||
|
where: { status: { in: ['queued', 'running'] } },
|
||||||
|
data: { status: 'canceled' }
|
||||||
|
});
|
||||||
|
scheduler.workers.get('deployApplication').postMessage('cancel');
|
||||||
|
}
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function restartCoolify(request: FastifyRequest<any>) {
|
export async function restartCoolify(request: FastifyRequest<any>) {
|
||||||
@@ -78,61 +193,87 @@ export async function restartCoolify(request: FastifyRequest<any>) {
|
|||||||
const teamId = request.user.teamId;
|
const teamId = request.user.teamId;
|
||||||
if (teamId === '0') {
|
if (teamId === '0') {
|
||||||
if (!isDev) {
|
if (!isDev) {
|
||||||
await asyncExecShell(`docker restart coolify`);
|
await executeCommand({ command: `docker restart coolify` });
|
||||||
return {};
|
return {};
|
||||||
} else {
|
} else {
|
||||||
console.log('Restarting Coolify')
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw { status: 500, message: 'You are not authorized to restart Coolify.' };
|
throw {
|
||||||
} catch ({ status, message }) {
|
status: 500,
|
||||||
return errorHandler({ status, message })
|
message: 'You are not authorized to restart Coolify.'
|
||||||
}
|
|
||||||
}
|
|
||||||
export async function showUsage() {
|
|
||||||
try {
|
|
||||||
return {
|
|
||||||
usage: {
|
|
||||||
uptime: os.uptime(),
|
|
||||||
memory: await osu.mem.info(),
|
|
||||||
cpu: {
|
|
||||||
load: os.loadavg(),
|
|
||||||
usage: await osu.cpu.usage(),
|
|
||||||
count: os.cpus().length
|
|
||||||
},
|
|
||||||
disk: await osu.drive.info('/')
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function showDashboard(request: FastifyRequest) {
|
export async function showDashboard(request: FastifyRequest) {
|
||||||
try {
|
try {
|
||||||
const userId = request.user.userId;
|
const userId = request.user.userId;
|
||||||
const teamId = request.user.teamId;
|
const teamId = request.user.teamId;
|
||||||
const applications = await prisma.application.findMany({
|
let applications = await prisma.application.findMany({
|
||||||
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
include: { settings: true }
|
include: { settings: true, destinationDocker: true, teams: true }
|
||||||
});
|
});
|
||||||
const databases = await prisma.database.findMany({
|
const databases = await prisma.database.findMany({
|
||||||
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
include: { settings: true }
|
include: { settings: true, destinationDocker: true, teams: true }
|
||||||
});
|
});
|
||||||
const services = await prisma.service.findMany({
|
const services = await prisma.service.findMany({
|
||||||
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
|
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
|
include: { destinationDocker: true, teams: true }
|
||||||
|
});
|
||||||
|
const gitSources = await prisma.gitSource.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
|
{ isSystemWide: true }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
include: { teams: true }
|
||||||
|
});
|
||||||
|
const destinations = await prisma.destinationDocker.findMany({
|
||||||
|
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
|
include: { teams: true }
|
||||||
});
|
});
|
||||||
const settings = await listSettings();
|
const settings = await listSettings();
|
||||||
|
|
||||||
|
let foundUnconfiguredApplication = false;
|
||||||
|
for (const application of applications) {
|
||||||
|
if (
|
||||||
|
((!application.buildPack || !application.branch) && !application.simpleDockerfile) ||
|
||||||
|
!application.destinationDockerId ||
|
||||||
|
(!application.settings?.isBot && !application?.fqdn && application.buildPack !== 'compose')
|
||||||
|
) {
|
||||||
|
foundUnconfiguredApplication = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let foundUnconfiguredService = false;
|
||||||
|
for (const service of services) {
|
||||||
|
if (!service.fqdn) {
|
||||||
|
foundUnconfiguredService = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let foundUnconfiguredDatabase = false;
|
||||||
|
for (const database of databases) {
|
||||||
|
if (!database.version) {
|
||||||
|
foundUnconfiguredDatabase = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
|
foundUnconfiguredApplication,
|
||||||
|
foundUnconfiguredDatabase,
|
||||||
|
foundUnconfiguredService,
|
||||||
applications,
|
applications,
|
||||||
databases,
|
databases,
|
||||||
services,
|
services,
|
||||||
settings,
|
gitSources,
|
||||||
|
destinations,
|
||||||
|
settings
|
||||||
};
|
};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,13 +294,16 @@ export async function login(request: FastifyRequest<Login>, reply: FastifyReply)
|
|||||||
if (!userFound && isLogin) {
|
if (!userFound && isLogin) {
|
||||||
throw { status: 500, message: 'User not found.' };
|
throw { status: 500, message: 'User not found.' };
|
||||||
}
|
}
|
||||||
const { isRegistrationEnabled, id } = await prisma.setting.findFirst()
|
const { isRegistrationEnabled, id } = await prisma.setting.findFirst();
|
||||||
let uid = cuid();
|
let uid = cuid();
|
||||||
let permission = 'read';
|
let permission = 'read';
|
||||||
let isAdmin = false;
|
let isAdmin = false;
|
||||||
|
|
||||||
if (users === 0) {
|
if (users === 0) {
|
||||||
await prisma.setting.update({ where: { id }, data: { isRegistrationEnabled: false } });
|
await prisma.setting.update({
|
||||||
|
where: { id },
|
||||||
|
data: { isRegistrationEnabled: false }
|
||||||
|
});
|
||||||
uid = '0';
|
uid = '0';
|
||||||
}
|
}
|
||||||
if (userFound) {
|
if (userFound) {
|
||||||
@@ -264,14 +408,14 @@ export async function login(request: FastifyRequest<Login>, reply: FastifyReply)
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getCurrentUser(request: FastifyRequest<GetCurrentUser>, fastify) {
|
export async function getCurrentUser(request: FastifyRequest<GetCurrentUser>, fastify) {
|
||||||
let token = null
|
let token = null;
|
||||||
const { teamId } = request.query
|
const { teamId } = request.query;
|
||||||
try {
|
try {
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
where: { id: request.user.userId }
|
where: { id: request.user.userId }
|
||||||
})
|
});
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw "User not found";
|
throw 'User not found';
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw { status: 401, message: error };
|
throw { status: 401, message: error };
|
||||||
@@ -281,27 +425,29 @@ export async function getCurrentUser(request: FastifyRequest<GetCurrentUser>, fa
|
|||||||
const user = await prisma.user.findFirst({
|
const user = await prisma.user.findFirst({
|
||||||
where: { id: request.user.userId, teams: { some: { id: teamId } } },
|
where: { id: request.user.userId, teams: { some: { id: teamId } } },
|
||||||
include: { teams: true, permission: true }
|
include: { teams: true, permission: true }
|
||||||
})
|
});
|
||||||
if (user) {
|
if (user) {
|
||||||
const permission = user.permission.find(p => p.teamId === teamId).permission
|
const permission = user.permission.find((p) => p.teamId === teamId).permission;
|
||||||
const payload = {
|
const payload = {
|
||||||
...request.user,
|
...request.user,
|
||||||
teamId,
|
teamId,
|
||||||
permission: permission || null,
|
permission: permission || null,
|
||||||
isAdmin: permission === 'owner' || permission === 'admin'
|
isAdmin: permission === 'owner' || permission === 'admin'
|
||||||
|
};
|
||||||
}
|
token = fastify.jwt.sign(payload);
|
||||||
token = fastify.jwt.sign(payload)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// No new token -> not switching teams
|
// No new token -> not switching teams
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const pendingInvitations = await prisma.teamInvitation.findMany({
|
||||||
|
where: { uid: request.user.userId }
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
settings: await prisma.setting.findFirst(),
|
settings: await prisma.setting.findUnique({ where: { id: '0' } }),
|
||||||
supportedServiceTypesAndVersions,
|
sentryDSN,
|
||||||
|
pendingInvitations,
|
||||||
token,
|
token,
|
||||||
...request.user
|
...request.user
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import { decrypt, errorHandler, prisma, uniqueName } from '../../../../lib/commo
|
|||||||
import { day } from '../../../../lib/dayjs';
|
import { day } from '../../../../lib/dayjs';
|
||||||
|
|
||||||
import type { OnlyId } from '../../../../types';
|
import type { OnlyId } from '../../../../types';
|
||||||
import type { BodyId, InviteToTeam, SaveTeam, SetPermission } from './types';
|
import type { BodyId, DeleteUserFromTeam, InviteToTeam, SaveTeam, SetPermission } from './types';
|
||||||
|
|
||||||
export async function listTeams(request: FastifyRequest) {
|
|
||||||
|
export async function listAccounts(request: FastifyRequest) {
|
||||||
try {
|
try {
|
||||||
const userId = request.user.userId;
|
const userId = request.user.userId;
|
||||||
const teamId = request.user.teamId;
|
const teamId = request.user.teamId;
|
||||||
@@ -15,10 +16,24 @@ export async function listTeams(request: FastifyRequest) {
|
|||||||
where: { id: userId },
|
where: { id: userId },
|
||||||
select: { id: true, email: true, teams: true }
|
select: { id: true, email: true, teams: true }
|
||||||
});
|
});
|
||||||
let accounts = [];
|
let accounts = await prisma.user.findMany({ where: { teams: { some: { id: teamId } } }, select: { id: true, email: true, teams: true } });
|
||||||
let allTeams = [];
|
|
||||||
if (teamId === '0') {
|
if (teamId === '0') {
|
||||||
accounts = await prisma.user.findMany({ select: { id: true, email: true, teams: true } });
|
accounts = await prisma.user.findMany({ select: { id: true, email: true, teams: true } });
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
account,
|
||||||
|
accounts
|
||||||
|
};
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function listTeams(request: FastifyRequest) {
|
||||||
|
try {
|
||||||
|
const userId = request.user.userId;
|
||||||
|
const teamId = request.user.teamId;
|
||||||
|
let allTeams = [];
|
||||||
|
if (teamId === '0') {
|
||||||
allTeams = await prisma.team.findMany({
|
allTeams = await prisma.team.findMany({
|
||||||
where: { users: { none: { id: userId } } },
|
where: { users: { none: { id: userId } } },
|
||||||
include: { permissions: true }
|
include: { permissions: true }
|
||||||
@@ -28,18 +43,30 @@ export async function listTeams(request: FastifyRequest) {
|
|||||||
where: { users: { some: { id: userId } } },
|
where: { users: { some: { id: userId } } },
|
||||||
include: { permissions: true }
|
include: { permissions: true }
|
||||||
});
|
});
|
||||||
const invitations = await prisma.teamInvitation.findMany({ where: { uid: userId } });
|
|
||||||
return {
|
return {
|
||||||
ownTeams,
|
ownTeams,
|
||||||
allTeams,
|
allTeams,
|
||||||
invitations,
|
|
||||||
account,
|
|
||||||
accounts
|
|
||||||
};
|
};
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export async function removeUserFromTeam(request: FastifyRequest<DeleteUserFromTeam>, reply: FastifyReply) {
|
||||||
|
try {
|
||||||
|
const { uid } = request.body;
|
||||||
|
const { id } = request.params;
|
||||||
|
const userId = request.user.userId;
|
||||||
|
const foundUser = await prisma.team.findMany({ where: { id, users: { some: { id: userId } } } });
|
||||||
|
if (foundUser.length === 0) {
|
||||||
|
return errorHandler({ status: 404, message: 'Team not found' });
|
||||||
|
}
|
||||||
|
await prisma.team.update({ where: { id }, data: { users: { disconnect: { id: uid } } } });
|
||||||
|
await prisma.permission.deleteMany({ where: { teamId: id, userId: uid } })
|
||||||
|
return reply.code(201).send()
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message })
|
||||||
|
}
|
||||||
|
}
|
||||||
export async function deleteTeam(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
export async function deleteTeam(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const userId = request.user.userId;
|
const userId = request.user.userId;
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
import { FastifyPluginAsync } from 'fastify';
|
import { FastifyPluginAsync } from 'fastify';
|
||||||
import { acceptInvitation, changePassword, deleteTeam, getTeam, inviteToTeam, listTeams, newTeam, removeUser, revokeInvitation, saveTeam, setPermission } from './handlers';
|
import { acceptInvitation, changePassword, deleteTeam, getTeam, inviteToTeam, listAccounts, listTeams, newTeam, removeUser, removeUserFromTeam, revokeInvitation, saveTeam, setPermission } from './handlers';
|
||||||
|
|
||||||
import type { OnlyId } from '../../../../types';
|
import type { OnlyId } from '../../../../types';
|
||||||
import type { BodyId, InviteToTeam, SaveTeam, SetPermission } from './types';
|
import type { BodyId, DeleteUserFromTeam, InviteToTeam, SaveTeam, SetPermission } from './types';
|
||||||
|
|
||||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||||
fastify.addHook('onRequest', async (request) => {
|
fastify.addHook('onRequest', async (request) => {
|
||||||
return await request.jwtVerify()
|
return await request.jwtVerify()
|
||||||
})
|
})
|
||||||
fastify.get('/', async (request) => await listTeams(request));
|
|
||||||
|
fastify.get('/', async (request) => await listAccounts(request));
|
||||||
fastify.post('/new', async (request, reply) => await newTeam(request, reply));
|
fastify.post('/new', async (request, reply) => await newTeam(request, reply));
|
||||||
|
fastify.get('/teams', async (request) => await listTeams(request));
|
||||||
|
|
||||||
fastify.get<OnlyId>('/team/:id', async (request, reply) => await getTeam(request, reply));
|
fastify.get<OnlyId>('/team/:id', async (request, reply) => await getTeam(request, reply));
|
||||||
fastify.post<SaveTeam>('/team/:id', async (request, reply) => await saveTeam(request, reply));
|
fastify.post<SaveTeam>('/team/:id', async (request, reply) => await saveTeam(request, reply));
|
||||||
fastify.delete<OnlyId>('/team/:id', async (request, reply) => await deleteTeam(request, reply));
|
fastify.delete<OnlyId>('/team/:id', async (request, reply) => await deleteTeam(request, reply));
|
||||||
|
fastify.post<DeleteUserFromTeam>('/team/:id/user/remove', async (request, reply) => await removeUserFromTeam(request, reply));
|
||||||
|
|
||||||
fastify.post<InviteToTeam>('/team/:id/invitation/invite', async (request, reply) => await inviteToTeam(request, reply))
|
fastify.post<InviteToTeam>('/team/:id/invitation/invite', async (request, reply) => await inviteToTeam(request, reply))
|
||||||
fastify.post<BodyId>('/team/:id/invitation/accept', async (request) => await acceptInvitation(request));
|
fastify.post<BodyId>('/team/:id/invitation/accept', async (request) => await acceptInvitation(request));
|
||||||
@@ -23,7 +26,6 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
|
|
||||||
fastify.delete<BodyId>('/user/remove', async (request, reply) => await removeUser(request, reply));
|
fastify.delete<BodyId>('/user/remove', async (request, reply) => await removeUser(request, reply));
|
||||||
fastify.post<BodyId>('/user/password', async (request, reply) => await changePassword(request, reply));
|
fastify.post<BodyId>('/user/password', async (request, reply) => await changePassword(request, reply));
|
||||||
// fastify.delete('/user', async (request, reply) => await deleteUser(request, reply));
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user