mirror of
https://github.com/fleetbase/fleetbase.git
synced 2025-12-30 19:07:07 +00:00
Compare commits
606 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a33c46ecd8 | ||
|
|
3fe1e0a439 | ||
|
|
7ae150137a | ||
|
|
fd1ee4cccc | ||
|
|
ca2bab9261 | ||
|
|
8c0d5f04c2 | ||
|
|
18f813c95b | ||
|
|
ca6004c191 | ||
|
|
cd5a7370b9 | ||
|
|
989312f3f8 | ||
|
|
9fe1af84a7 | ||
|
|
b4ff469c8b | ||
|
|
5d54ffbfda | ||
|
|
445d782fe6 | ||
|
|
4ffc721ecf | ||
|
|
4623910ebf | ||
|
|
af8e30ad7f | ||
|
|
f38d65028e | ||
|
|
eb90b77151 | ||
|
|
536f36487b | ||
|
|
93dc4afc03 | ||
|
|
9fc53de9fe | ||
|
|
bd37005d4b | ||
|
|
f15d509cd1 | ||
|
|
1755c19d6b | ||
|
|
ae300f9004 | ||
|
|
0ad5877271 | ||
|
|
5ffd0461e8 | ||
|
|
50a686ab80 | ||
|
|
1f7937b6ea | ||
|
|
90a0e09321 | ||
|
|
304933af7e | ||
|
|
96e6439d1e | ||
|
|
0ae5f26a63 | ||
|
|
3e90246dfd | ||
|
|
ad055cc4e6 | ||
|
|
a35c0354b1 | ||
|
|
e9bf831ce8 | ||
|
|
2c20db4155 | ||
|
|
801d73215f | ||
|
|
ed1609ccf4 | ||
|
|
82ae99eb84 | ||
|
|
7874edf0f3 | ||
|
|
7281174d12 | ||
|
|
cc4e28cd00 | ||
|
|
bbd1e1bdfe | ||
|
|
4191d6a0bb | ||
|
|
dba2b7afc5 | ||
|
|
a6e6625ae1 | ||
|
|
3777569f22 | ||
|
|
fa5b6700a3 | ||
|
|
6cce6a9db2 | ||
|
|
29bd3ce217 | ||
|
|
39d405cb57 | ||
|
|
9f88c7bc79 | ||
|
|
b3816c394a | ||
|
|
34462c61c4 | ||
|
|
080302eb86 | ||
|
|
b063cf6338 | ||
|
|
b6dca79251 | ||
|
|
c35af4d47a | ||
|
|
2a89659cc3 | ||
|
|
f8ae75d767 | ||
|
|
924448a4d0 | ||
|
|
5426ac374d | ||
|
|
73f56b7958 | ||
|
|
095eb87e14 | ||
|
|
fabc16612b | ||
|
|
3389cba935 | ||
|
|
099ea57c39 | ||
|
|
5492bc7299 | ||
|
|
00b037a385 | ||
|
|
151fea2505 | ||
|
|
08f901d865 | ||
|
|
116873a1ce | ||
|
|
20a1793447 | ||
|
|
72ab83dc7a | ||
|
|
171e365ba6 | ||
|
|
009f2d6e53 | ||
|
|
4a59318feb | ||
|
|
8920039b40 | ||
|
|
cd9be05714 | ||
|
|
fbe35545e9 | ||
|
|
56ab967d7a | ||
|
|
79604c7981 | ||
|
|
312eb1aa6f | ||
|
|
6572a59120 | ||
|
|
fa536c6183 | ||
|
|
d4626be332 | ||
|
|
ffc54ecdbb | ||
|
|
9bf097b88b | ||
|
|
52c8df8b66 | ||
|
|
908e0eb9ee | ||
|
|
6438138913 | ||
|
|
aee06a2146 | ||
|
|
ccacc6c597 | ||
|
|
62c396b789 | ||
|
|
295da5f331 | ||
|
|
e775ccc2c8 | ||
|
|
bd0759881f | ||
|
|
830ae69b1d | ||
|
|
3b9a80866f | ||
|
|
76badbf949 | ||
|
|
f968556585 | ||
|
|
6d01eab305 | ||
|
|
9406446db1 | ||
|
|
565db7bbce | ||
|
|
003cb467e8 | ||
|
|
8e1b281e77 | ||
|
|
af4507cc87 | ||
|
|
2a2c7d8426 | ||
|
|
077a4298b9 | ||
|
|
af840d30d0 | ||
|
|
a253c0c6f7 | ||
|
|
0fc1d6068d | ||
|
|
6993510d08 | ||
|
|
4fcf09c1b8 | ||
|
|
f89596a74b | ||
|
|
d89e93f248 | ||
|
|
2f0c15bc93 | ||
|
|
ae6c07006b | ||
|
|
d754641493 | ||
|
|
bb0d706006 | ||
|
|
652bc363e1 | ||
|
|
363309af61 | ||
|
|
83791ea91c | ||
|
|
8aae8cd025 | ||
|
|
6fb0353813 | ||
|
|
c73c8e0f54 | ||
|
|
84ddf730c2 | ||
|
|
9da0d6b5c4 | ||
|
|
52bf61b3be | ||
|
|
9ff3d9e85f | ||
|
|
b6bad3212d | ||
|
|
a43b86807b | ||
|
|
9cc0e35ac3 | ||
|
|
2e555b8dc3 | ||
|
|
948ba4aa6d | ||
|
|
46bd448ec7 | ||
|
|
e588242cb9 | ||
|
|
58600a357a | ||
|
|
4dd8a7b2ff | ||
|
|
54803909a4 | ||
|
|
e437ee04fb | ||
|
|
80d2ebb544 | ||
|
|
300e157bc1 | ||
|
|
26509008dc | ||
|
|
4119eb1717 | ||
|
|
27661a3888 | ||
|
|
a4f69409fd | ||
|
|
da9caf57f1 | ||
|
|
acfa34b09f | ||
|
|
356b5076e5 | ||
|
|
038382928b | ||
|
|
f459f2e7e3 | ||
|
|
7f9b09f673 | ||
|
|
440040fecb | ||
|
|
bc31bf4c4c | ||
|
|
def361c2af | ||
|
|
be382fcbd3 | ||
|
|
f42de06dcf | ||
|
|
dc2b4e1aee | ||
|
|
c7348766df | ||
|
|
e67149db89 | ||
|
|
4765bcfbd9 | ||
|
|
019be89e06 | ||
|
|
db0c56d8f0 | ||
|
|
8de660d51a | ||
|
|
67f22aafbe | ||
|
|
1ff89ca16a | ||
|
|
c6777efb2d | ||
|
|
7d1f594f87 | ||
|
|
da6485987a | ||
|
|
b623d613f1 | ||
|
|
d1a77c95d0 | ||
|
|
bc7c6c12ad | ||
|
|
50bfe55583 | ||
|
|
bd1a61912f | ||
|
|
9d49730d52 | ||
|
|
fd38f2bef6 | ||
|
|
0fa3a25d59 | ||
|
|
0917d7cce9 | ||
|
|
d33aa21418 | ||
|
|
e8f5638cad | ||
|
|
e216f11f7c | ||
|
|
41064be1ff | ||
|
|
ae17ccb199 | ||
|
|
0fbf901c9d | ||
|
|
00806d4f0c | ||
|
|
683353517d | ||
|
|
27b1af6c40 | ||
|
|
ab8cbf9ea6 | ||
|
|
e8249adddd | ||
|
|
b2c44842ce | ||
|
|
f9e6c8d50d | ||
|
|
6cd0625753 | ||
|
|
21ea3f5644 | ||
|
|
727b2e399f | ||
|
|
ec913f82d7 | ||
|
|
9bb2ea15e0 | ||
|
|
575e4e0952 | ||
|
|
f2058e0d58 | ||
|
|
9669e917ab | ||
|
|
2b6a200811 | ||
|
|
1d92764958 | ||
|
|
13db00c39f | ||
|
|
eedeb8f209 | ||
|
|
bac95cca00 | ||
|
|
520e9771b4 | ||
|
|
75742de7ac | ||
|
|
8628695b0b | ||
|
|
74fd369854 | ||
|
|
5d43df0e1b | ||
|
|
c96a3eb2bf | ||
|
|
4424ed1754 | ||
|
|
6c8b09db61 | ||
|
|
8d5e649646 | ||
|
|
6844e61dd4 | ||
|
|
7acf97a5e0 | ||
|
|
6a68c85d94 | ||
|
|
914a5abacb | ||
|
|
fdcd3bb805 | ||
|
|
5a4d6846b5 | ||
|
|
2e8a6de7ea | ||
|
|
f52c1a17b8 | ||
|
|
c44e9186d5 | ||
|
|
02408b3a12 | ||
|
|
a3d3cb05cf | ||
|
|
604365fc5f | ||
|
|
5d47dc676f | ||
|
|
50b58a5955 | ||
|
|
948db173c9 | ||
|
|
4f56e04166 | ||
|
|
d9705bf4f4 | ||
|
|
5535067d87 | ||
|
|
aab4e9f1f6 | ||
|
|
43329a0ed2 | ||
|
|
427816d2dd | ||
|
|
3d9d5cbe9d | ||
|
|
3fc5bdb32e | ||
|
|
a60f75655d | ||
|
|
0984af400a | ||
|
|
7f900fe218 | ||
|
|
3aaf382384 | ||
|
|
f589b1cf0a | ||
|
|
bce1cbf22d | ||
|
|
6327603857 | ||
|
|
7204ddb3a9 | ||
|
|
3a72cacc5c | ||
|
|
2bc9745b95 | ||
|
|
fc1e610a54 | ||
|
|
4a2b3e094f | ||
|
|
313906d36a | ||
|
|
d1d5b87c21 | ||
|
|
8dc7d9ff87 | ||
|
|
008faa1ca7 | ||
|
|
6aab30b358 | ||
|
|
30ab3aab09 | ||
|
|
df3c93803d | ||
|
|
3d1e20439f | ||
|
|
646f10713f | ||
|
|
5db723b003 | ||
|
|
74770883fe | ||
|
|
3ebedab90a | ||
|
|
d0864e822b | ||
|
|
f8b564acc9 | ||
|
|
fd604c99e0 | ||
|
|
bc0ba3e3fb | ||
|
|
ecbbe002cc | ||
|
|
1f068a890a | ||
|
|
3aa365e7ef | ||
|
|
bb1444dc31 | ||
|
|
09996a4398 | ||
|
|
22043a53e3 | ||
|
|
acde54c6ae | ||
|
|
e6961089ca | ||
|
|
880facdaef | ||
|
|
87c1a56fd1 | ||
|
|
dba816463f | ||
|
|
270371ce9c | ||
|
|
9f37b15d8a | ||
|
|
59f01832ef | ||
|
|
3ac3ad97f3 | ||
|
|
ad067bd32c | ||
|
|
9bd0cc17d9 | ||
|
|
fc947d833a | ||
|
|
ff159a04ee | ||
|
|
c5ac611000 | ||
|
|
863e27bd7b | ||
|
|
63c9bc0880 | ||
|
|
6183378717 | ||
|
|
ea35a6f81f | ||
|
|
5fb4851544 | ||
|
|
e348b02611 | ||
|
|
3f779969c3 | ||
|
|
9a23c64dde | ||
|
|
b2098d0cb8 | ||
|
|
850cc1e20d | ||
|
|
6dd7ca401c | ||
|
|
fa51d03cfd | ||
|
|
092ebf0589 | ||
|
|
4f4fdeaafc | ||
|
|
39747601d0 | ||
|
|
321ef64229 | ||
|
|
1b8f7a663d | ||
|
|
2a5a68f620 | ||
|
|
d314964776 | ||
|
|
3033f6f4cf | ||
|
|
9fbc3252c6 | ||
|
|
9c1655167d | ||
|
|
25ce216e3b | ||
|
|
6bf3dbad2d | ||
|
|
9a8c67a1b7 | ||
|
|
eabd83c8ad | ||
|
|
16e59f3465 | ||
|
|
f9d362b1be | ||
|
|
e4f64021d7 | ||
|
|
941c6d03d9 | ||
|
|
2331c6902e | ||
|
|
be9faea3bc | ||
|
|
e8d7c021f5 | ||
|
|
8c86eed5fc | ||
|
|
a2ce7b5a65 | ||
|
|
48069177df | ||
|
|
c5574a25ae | ||
|
|
0a3ad2f0a6 | ||
|
|
d60760104d | ||
|
|
88b4acff67 | ||
|
|
4d2179129d | ||
|
|
5c7e0a1c56 | ||
|
|
65a6894d4b | ||
|
|
e3acd28c18 | ||
|
|
313b6e63a8 | ||
|
|
a7ed7ee935 | ||
|
|
1e28d9d8d8 | ||
|
|
0c31b54fde | ||
|
|
f6b83e5638 | ||
|
|
b30ee818fc | ||
|
|
6ec9ad59d3 | ||
|
|
10ff2e066b | ||
|
|
f56db88ad6 | ||
|
|
033cf5cfe0 | ||
|
|
4a4dc76e60 | ||
|
|
a52af94b00 | ||
|
|
9c4daf7a68 | ||
|
|
a8904ba112 | ||
|
|
6880664d9e | ||
|
|
99b30d7f58 | ||
|
|
c8539fd2a0 | ||
|
|
cb1aec40fd | ||
|
|
b728b366a0 | ||
|
|
5cfc3f1cc7 | ||
|
|
e7b5282aa3 | ||
|
|
7ffb7ac24a | ||
|
|
6e4a9edd7d | ||
|
|
d9d01c8bbc | ||
|
|
c54d75fa0f | ||
|
|
91904c3836 | ||
|
|
0e075e3b24 | ||
|
|
c56bea95d0 | ||
|
|
f1cec329bc | ||
|
|
34dc365dd2 | ||
|
|
74a782f4ea | ||
|
|
a8b2042d85 | ||
|
|
cc52a40660 | ||
|
|
4fb2dec8c3 | ||
|
|
0db3634ab3 | ||
|
|
1c0d324ba9 | ||
|
|
096cc1097a | ||
|
|
eca881156a | ||
|
|
40b499bcb2 | ||
|
|
c6e7dcae8b | ||
|
|
4bba957123 | ||
|
|
33b6174247 | ||
|
|
79d80184cd | ||
|
|
8c1f96ef0e | ||
|
|
c139c00404 | ||
|
|
92032ca56c | ||
|
|
c265c6fbda | ||
|
|
d8ae53d811 | ||
|
|
729e347a78 | ||
|
|
3453ccffd4 | ||
|
|
3d54ada328 | ||
|
|
e9f6f875a1 | ||
|
|
fa45669a6c | ||
|
|
280ff14af0 | ||
|
|
4ac088bc72 | ||
|
|
a8eb55d68e | ||
|
|
4a0a775429 | ||
|
|
36e85548f2 | ||
|
|
5b805b1e2f | ||
|
|
b041b4d555 | ||
|
|
b30c101bea | ||
|
|
e932cdd9c6 | ||
|
|
a7ff7094b6 | ||
|
|
8514203b88 | ||
|
|
17c7759a47 | ||
|
|
690c9d7486 | ||
|
|
6deeb9fd0a | ||
|
|
4dbe344bb5 | ||
|
|
216e6a9a18 | ||
|
|
5855df6a5d | ||
|
|
782c1aa706 | ||
|
|
32cca548c3 | ||
|
|
1761b45911 | ||
|
|
93ac555427 | ||
|
|
04b6a1f47e | ||
|
|
843e9a21cc | ||
|
|
220b486c3d | ||
|
|
60690ec334 | ||
|
|
a71c3c46d8 | ||
|
|
f956f853e1 | ||
|
|
c2efb2ac4d | ||
|
|
bb0951feba | ||
|
|
fca83a25e5 | ||
|
|
f0f3800d3c | ||
|
|
2edb3f66e1 | ||
|
|
36b15f1544 | ||
|
|
60f8f33d5c | ||
|
|
6ed95b8cd7 | ||
|
|
079cfabeb5 | ||
|
|
d8d8063620 | ||
|
|
46b3554fce | ||
|
|
d604acebe4 | ||
|
|
962f46d4e2 | ||
|
|
664d7f19cd | ||
|
|
06dcb90499 | ||
|
|
6d0ac8496d | ||
|
|
10f8e694fe | ||
|
|
8e68ed1aeb | ||
|
|
267184df5f | ||
|
|
98d96a50ab | ||
|
|
51f8e4d289 | ||
|
|
04b01c734d | ||
|
|
405e299f07 | ||
|
|
aea4b781b8 | ||
|
|
35e81f05cd | ||
|
|
9d388892b1 | ||
|
|
becb39b447 | ||
|
|
f490a552c4 | ||
|
|
95381ba140 | ||
|
|
7b190d7718 | ||
|
|
92c153cf93 | ||
|
|
ef662fbd5e | ||
|
|
a5d05de27c | ||
|
|
5a6f44aee5 | ||
|
|
60cbc41b36 | ||
|
|
81e3d8466d | ||
|
|
4f9096a94a | ||
|
|
122fcf80cb | ||
|
|
50b5f54a63 | ||
|
|
243032469d | ||
|
|
42d26a6d81 | ||
|
|
e2b2097855 | ||
|
|
dbcc2fe8ef | ||
|
|
d34b845719 | ||
|
|
a8e050b20c | ||
|
|
9b68458114 | ||
|
|
eb12313614 | ||
|
|
68ee8be32c | ||
|
|
6f35a2b456 | ||
|
|
4bdd25288a | ||
|
|
5804f65ef6 | ||
|
|
4db3677b6d | ||
|
|
475c03c077 | ||
|
|
00b49a7c04 | ||
|
|
768fa6fc27 | ||
|
|
ed72c2afdd | ||
|
|
3aa5e2f89e | ||
|
|
2656c764a1 | ||
|
|
c217d6dc44 | ||
|
|
c68ec787c1 | ||
|
|
22a2f2048e | ||
|
|
c53d360ef0 | ||
|
|
406ca11199 | ||
|
|
9c604edcf5 | ||
|
|
cb96b4112c | ||
|
|
a502e1f5f3 | ||
|
|
27e10a2b33 | ||
|
|
1c70b2f62c | ||
|
|
a8ba45fc73 | ||
|
|
f6a7edf6f4 | ||
|
|
9889c4b4aa | ||
|
|
8bdababe5e | ||
|
|
a3184b3c6e | ||
|
|
1de56e08fa | ||
|
|
475b7ddda0 | ||
|
|
f9ba04198c | ||
|
|
e8fce1b1fc | ||
|
|
174dd83542 | ||
|
|
629d8bdd26 | ||
|
|
045bcc1648 | ||
|
|
a0e9a912f6 | ||
|
|
cbed2de0d5 | ||
|
|
557666252b | ||
|
|
7cf544bd30 | ||
|
|
5bf9e691aa | ||
|
|
011f658086 | ||
|
|
08b50cbfc1 | ||
|
|
6c0888ffae | ||
|
|
de4b088c31 | ||
|
|
1e5ea0e2fa | ||
|
|
0f8f7bb423 | ||
|
|
1b4993cb93 | ||
|
|
2d905e50c9 | ||
|
|
98b9afd151 | ||
|
|
03e6a87696 | ||
|
|
947746e4f1 | ||
|
|
d819516a17 | ||
|
|
3a34ff1627 | ||
|
|
cb53cb7270 | ||
|
|
f75efa6c07 | ||
|
|
90b1987ade | ||
|
|
bc854c5d6c | ||
|
|
b467f30d69 | ||
|
|
74ed29ad8c | ||
|
|
04125ebfb3 | ||
|
|
05746fccd0 | ||
|
|
c90df0218e | ||
|
|
03bbae856a | ||
|
|
71d27375c6 | ||
|
|
75a1a1b389 | ||
|
|
fc5a5df5c7 | ||
|
|
3dfa7a4076 | ||
|
|
45387cb360 | ||
|
|
6425180c40 | ||
|
|
98f3a2ceba | ||
|
|
275b1bcab8 | ||
|
|
e61ace5bcb | ||
|
|
b5d6ba2114 | ||
|
|
7b62ff9d50 | ||
|
|
b601136754 | ||
|
|
b1da01d4f6 | ||
|
|
80b43ffa72 | ||
|
|
c1545dbd14 | ||
|
|
452f8c4bd5 | ||
|
|
4de6ea1e30 | ||
|
|
de374069dd | ||
|
|
dd5accecef | ||
|
|
cae2774632 | ||
|
|
b399d0092c | ||
|
|
b056a07fa2 | ||
|
|
90a590e156 | ||
|
|
bcb1470138 | ||
|
|
9cba77709f | ||
|
|
0cab9b816b | ||
|
|
ba6cdd9db3 | ||
|
|
915293d9b1 | ||
|
|
fe41262c89 | ||
|
|
5cc39304e0 | ||
|
|
b7d87c5ad7 | ||
|
|
fa9a43ae49 | ||
|
|
ef5464aede | ||
|
|
25601cee70 | ||
|
|
3ebc6d11cc | ||
|
|
27f1b26684 | ||
|
|
3adc943450 | ||
|
|
2a8c5cb130 | ||
|
|
8212eceb50 | ||
|
|
4ffc2c750e | ||
|
|
b55f37ffdf | ||
|
|
f60e72e96d | ||
|
|
d46d22e6cc | ||
|
|
190394e853 | ||
|
|
96dfd6e48a | ||
|
|
07606b8d15 | ||
|
|
1d75130724 | ||
|
|
1bb3569518 | ||
|
|
f974b07000 | ||
|
|
fdcd526f7b | ||
|
|
fb93443aff | ||
|
|
3c122d6897 | ||
|
|
d73a381141 | ||
|
|
8b23afc08a | ||
|
|
32b9db4da9 | ||
|
|
dd95ab14d4 | ||
|
|
46ea479102 | ||
|
|
ed5fe522fe | ||
|
|
68ce0167e3 | ||
|
|
554c5cdbea | ||
|
|
95df545e62 | ||
|
|
d55edb73eb | ||
|
|
2578a5302f | ||
|
|
827040336c | ||
|
|
c919b13943 | ||
|
|
afac4c7788 | ||
|
|
d1dee5789e | ||
|
|
797de1d38c | ||
|
|
10dcc4bab8 | ||
|
|
59aab8c9cc | ||
|
|
c7707d6c8e | ||
|
|
9809ebe28e | ||
|
|
5dbce3edfd | ||
|
|
61bedf737b | ||
|
|
49b8858cad | ||
|
|
7c698ee8c9 | ||
|
|
38679fcd07 | ||
|
|
6fcee898d8 | ||
|
|
c9920cceb6 | ||
|
|
2b98d488fc | ||
|
|
59f4188465 | ||
|
|
ad8adbf1c3 | ||
|
|
4439c75158 | ||
|
|
26345ab075 | ||
|
|
a0cee3444b | ||
|
|
b0abd6129f |
@@ -14,6 +14,7 @@ concourse/
|
||||
infra/*
|
||||
vagrant/*
|
||||
docker/Dockerfile
|
||||
docker/database/
|
||||
deploy/*
|
||||
media/*
|
||||
data/*
|
||||
@@ -23,4 +24,4 @@ docker-compose-prod.yml
|
||||
docker-compose.yml
|
||||
$virtualenv.tar.gz
|
||||
$node_modules.tar.gz
|
||||
docker-compose.override.yml
|
||||
docker-compose.override.yml
|
||||
|
||||
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Custom issue template
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
17
.github/ISSUE_TEMPLATE/📚-docs-feedback.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/📚-docs-feedback.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: "\U0001F4DA Docs Feedback"
|
||||
about: Improve Fleetbase Documentation
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the improvement**
|
||||
A clear and concise description of documentation improvement .
|
||||
|
||||
**Related Page**
|
||||
Which section and page is this improvement related to? .
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the improvement here.
|
||||
20
.github/ISSUE_TEMPLATE/🚀-feature-request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/🚀-feature-request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: "\U0001F680 Feature request"
|
||||
about: Submit a proposal for a new feature
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
41
.github/ISSUE_TEMPLATE/🪲bug-report.md
vendored
Normal file
41
.github/ISSUE_TEMPLATE/🪲bug-report.md
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: "\U0001FAB2Bug Report"
|
||||
about: Report a bug to help us improve.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Logs (if relevant)**
|
||||
Any log relevant log messages or snippets
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
161
.github/workflows/cd.yml
vendored
Normal file
161
.github/workflows/cd.yml
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
name: Fleetbase CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["deploy/*"]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PROJECT: ${{ secrets.PROJECT }}
|
||||
GITHUB_AUTH_KEY: ${{ secrets._GITHUB_AUTH_TOKEN }}
|
||||
|
||||
jobs:
|
||||
build_service:
|
||||
name: Build and Deploy the Service
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # This is required for requesting the JWT
|
||||
contents: read # This is required for actions/checkout
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Set Dynamic ENV Vars
|
||||
run: |
|
||||
SHORT_COMMIT=$(echo $GITHUB_SHA | cut -c -8)
|
||||
echo "VERSION=${SHORT_COMMIT}" >> $GITHUB_ENV
|
||||
echo "STACK=$(basename $GITHUB_REF)" >> $GITHUB_ENV
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_NUMBER }}:role/${{ env.PROJECT }}-${{ env.STACK }}-deployer
|
||||
role-session-name: github
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
|
||||
- name: Login to Amazon ECR
|
||||
id: login-ecr
|
||||
uses: aws-actions/amazon-ecr-login@v1
|
||||
|
||||
- name: Build and Release
|
||||
uses: docker/bake-action@v2
|
||||
env:
|
||||
REGISTRY: ${{ steps.login-ecr.outputs.registry }}/${{ env.PROJECT }}-${{ env.STACK }}
|
||||
VERSION: ${{ env.VERSION }}
|
||||
GITHUB_AUTH_KEY: ${{ env.GITHUB_AUTH_KEY }}
|
||||
CACHE: type=gha
|
||||
with:
|
||||
push: true
|
||||
files: |
|
||||
./docker-bake.hcl
|
||||
|
||||
- name: Download ecs-tool
|
||||
run: |
|
||||
wget -O ecs-tool.tar.gz https://github.com/springload/ecs-tool/releases/download/1.9.6/ecs-tool_1.9.6_linux_amd64.tar.gz && tar -xvf ecs-tool.tar.gz ecs-tool
|
||||
|
||||
- name: Deploy the images 🚀
|
||||
run: |-
|
||||
set -eu
|
||||
# run deploy.sh script before deployments
|
||||
env "ECS_RUN.SERVICE=app" "ECS_RUN.LAUNCH_TYPE=FARGATE" ./ecs-tool run -l "ecs-tool" --image_tag '{container_name}-${{ env.VERSION }}' --cluster ${{ env.PROJECT }}-${{ env.STACK }} --task_definition ${{ env.PROJECT }}-${{ env.STACK }}-app --container_name app ./deploy.sh
|
||||
./ecs-tool deploy --image_tag '{container_name}-${{ env.VERSION }}' --cluster ${{ env.PROJECT }}-${{ env.STACK }} -s app -s scheduler -s events
|
||||
|
||||
build_frontend:
|
||||
name: Build and Deploy the Console
|
||||
needs: [build_service]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # This is required for requesting the JWT
|
||||
contents: read # This is required for actions/checkout
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set Dynamic ENV Vars
|
||||
run: |
|
||||
SHORT_COMMIT=$(echo $GITHUB_SHA | cut -c -8)
|
||||
echo "VERSION=${SHORT_COMMIT}" >> $GITHUB_ENV
|
||||
echo "STACK=$(basename $GITHUB_REF)" >> $GITHUB_ENV
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v2
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_NUMBER }}:role/${{ env.PROJECT }}-${{ env.STACK }}-deployer
|
||||
role-session-name: github
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
|
||||
- name: Get infra-provided configuration
|
||||
run: |
|
||||
set -eu
|
||||
|
||||
wget -O- https://github.com/springload/ssm-parent/releases/download/1.8.0/ssm-parent_1.8.0_linux_amd64.tar.gz | tar xvzf - ssm-parent
|
||||
|
||||
./ssm-parent -n /actions/${{ env.PROJECT }}/${{ env.STACK }}/configuration dotenv /tmp/dotenv.file
|
||||
# remove double quotes and pipe into the env
|
||||
cat /tmp/dotenv.file | sed -e 's/"//g' >> $GITHUB_ENV
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm Store Directory
|
||||
id: pnpm-cache
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v3
|
||||
name: Setup pnpm Cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Check for _GITHUB_AUTH_TOKEN and create .npmrc
|
||||
run: |
|
||||
if [[ -n "${{ secrets._GITHUB_AUTH_TOKEN }}" ]]; then
|
||||
echo "//npm.pkg.github.com/:_authToken=${{ secrets._GITHUB_AUTH_TOKEN }}" > .npmrc
|
||||
fi
|
||||
working-directory: ./console
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
working-directory: ./console
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
set -eu
|
||||
|
||||
pnpm build --environment production
|
||||
working-directory: ./console
|
||||
|
||||
- name: Deploy Console 🚀
|
||||
run: |
|
||||
set -u
|
||||
|
||||
DEPLOY_BUCKET=${STATIC_DEPLOY_BUCKET:-${{ env.PROJECT }}-${{ env.STACK }}}
|
||||
# this value will come from the dotenv above
|
||||
echo "Deploying to $DEPLOY_BUCKET"
|
||||
wget -O- https://github.com/bep/s3deploy/releases/download/v2.11.0/s3deploy_2.11.0_linux-amd64.tar.gz | tar xzv -f - s3deploy
|
||||
./s3deploy -region ${AWS_REGION} -source console/dist -bucket ${DEPLOY_BUCKET}
|
||||
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@@ -15,16 +15,23 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Docker Compose
|
||||
run: |
|
||||
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
docker-compose --version
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Build and Start Docker Services
|
||||
- name: Prepare Composer Auth Secret
|
||||
run: |
|
||||
docker-compose up -d --build
|
||||
if [[ -n "${{ secrets._GITHUB_AUTH_TOKEN }}" ]]; then
|
||||
echo '{"github-oauth": {"github.com": "'${{ secrets._GITHUB_AUTH_TOKEN }}'"}}' > composer-auth.json
|
||||
else
|
||||
echo '{}' > composer-auth.json
|
||||
fi
|
||||
|
||||
- name: Build and Release
|
||||
uses: docker/bake-action@v2
|
||||
with:
|
||||
files: |
|
||||
./docker-bake.hcl
|
||||
|
||||
18
.github/workflows/cloud.yml
vendored
Normal file
18
.github/workflows/cloud.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: "Close Invalid PRs"
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- cloud
|
||||
|
||||
jobs:
|
||||
close_pr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Close invalid PRs
|
||||
run: |
|
||||
if [[ "${{ github.event.pull_request.head.ref }}" != "main" ]] && [[ "${{ github.event.pull_request.head.ref }}" != "dev-cloud" ]]; then
|
||||
echo "This PR is from '${{ github.event.pull_request.head.ref }}', but only PRs from 'main' or 'dev-cloud' are allowed. Closing PR."
|
||||
gh pr close ${{ github.event.pull_request.number }} --repo ${{ github.repository }}
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
170
.github/workflows/eks-cd.yml
vendored
Normal file
170
.github/workflows/eks-cd.yml
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
name: Fleetbase EKS CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["eksdeploy/*"]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PROJECT: ${{ secrets.PROJECT }}
|
||||
GITHUB_AUTH_KEY: ${{ secrets._GITHUB_AUTH_TOKEN }}
|
||||
|
||||
jobs:
|
||||
build_service:
|
||||
name: Build and Deploy the Service
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # This is required for requesting the JWT
|
||||
contents: read # This is required for actions/checkout
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Set Dynamic ENV Vars
|
||||
run: |
|
||||
SHORT_COMMIT=$(echo $GITHUB_SHA | cut -c -8)
|
||||
echo "VERSION=${SHORT_COMMIT}" >> $GITHUB_ENV
|
||||
echo "STACK=$(basename $GITHUB_REF)" >> $GITHUB_ENV
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: ${{ secrets.EKS_DEPLOYER_ROLE }}
|
||||
role-session-name: github
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
|
||||
- name: Login to Amazon ECR
|
||||
id: login-ecr
|
||||
uses: aws-actions/amazon-ecr-login@v1
|
||||
|
||||
- name: Build and Release
|
||||
uses: docker/bake-action@v2
|
||||
env:
|
||||
REGISTRY: ${{ steps.login-ecr.outputs.registry }}/${{ env.PROJECT }}-${{ env.STACK }}
|
||||
VERSION: ${{ env.VERSION }}
|
||||
GITHUB_AUTH_KEY: ${{ env.GITHUB_AUTH_KEY }}
|
||||
CACHE: type=gha
|
||||
with:
|
||||
push: true
|
||||
files: |
|
||||
./docker-bake.hcl
|
||||
|
||||
- name: Update kube config
|
||||
run: aws eks update-kubeconfig --name ${{ secrets.EKS_CLUSTER_NAME }} --region ${{ secrets.AWS_REGION }}
|
||||
|
||||
- name: Deploy the images 🚀
|
||||
env:
|
||||
REGISTRY: ${{ steps.login-ecr.outputs.registry }}/${{ env.PROJECT }}-${{ env.STACK }}
|
||||
run: |-
|
||||
set -eu
|
||||
# run deploy.sh script before deployments
|
||||
helm upgrade -i ${{ env.PROJECT }} infra/helm -n ${{ env.PROJECT}}-${{ env.STACK }} --set image.repository=${{ env.REGISTRY }} \
|
||||
--set image.tag=${{ env.VERSION }} --set 'api_host=${{ secrets.API_HOST }}' --set 'socketcluster_host=${{ secrets.SOCKETCLUSTER_HOST }}' \
|
||||
--set gcp=false --set 'ingress.annotations.kubernetes\.io/ingress\.class=null' --set 'ingress.annotations.alb\.ingress\.kubernetes\.io/scheme=internet-facing' \
|
||||
--set serviceAccount.name=default --set serviceAccount.create=false --set ingress.className=alb \
|
||||
--set 'ingress.annotations.alb\.ingress\.kubernetes\.io/listen-ports=[{"HTTPS":443}]' \
|
||||
--set service.type=NodePort
|
||||
|
||||
build_frontend:
|
||||
name: Build and Deploy the Console
|
||||
needs: [build_service]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # This is required for requesting the JWT
|
||||
contents: read # This is required for actions/checkout
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set Dynamic ENV Vars
|
||||
run: |
|
||||
SHORT_COMMIT=$(echo $GITHUB_SHA | cut -c -8)
|
||||
echo "VERSION=${SHORT_COMMIT}" >> $GITHUB_ENV
|
||||
echo "STACK=$(basename $GITHUB_REF)" >> $GITHUB_ENV
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v2
|
||||
with:
|
||||
role-to-assume: ${{ secrets.EKS_DEPLOYER_ROLE }}
|
||||
role-session-name: github
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
|
||||
- name: Get infra-provided configuration
|
||||
run: |
|
||||
set -eu
|
||||
|
||||
wget -O- https://github.com/springload/ssm-parent/releases/download/1.8.0/ssm-parent_1.8.0_linux_amd64.tar.gz | tar xvzf - ssm-parent
|
||||
|
||||
./ssm-parent -n /actions/${{ env.PROJECT }}/${{ env.STACK }}/configuration dotenv /tmp/dotenv.file
|
||||
# remove double quotes and pipe into the env
|
||||
cat /tmp/dotenv.file | sed -e 's/"//g' >> $GITHUB_ENV
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm Store Directory
|
||||
id: pnpm-cache
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v3
|
||||
name: Setup pnpm Cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Check for _GITHUB_AUTH_TOKEN and create .npmrc
|
||||
run: |
|
||||
if [[ -n "${{ secrets._GITHUB_AUTH_TOKEN }}" ]]; then
|
||||
echo "//npm.pkg.github.com/:_authToken=${{ secrets._GITHUB_AUTH_TOKEN }}" > .npmrc
|
||||
fi
|
||||
working-directory: ./console
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
working-directory: ./console
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
API_HOST: ${{ secrets.API_HOST }}
|
||||
SOCKETCLUSTER_HOST: ${{ secrets.SOCKETCLUSTER_HOST }}
|
||||
SOCKETCLUSTER_PORT: "443" # it uses common ingress so port 443
|
||||
run: |
|
||||
set -eu
|
||||
|
||||
pnpm build --environment production
|
||||
working-directory: ./console
|
||||
|
||||
- name: Deploy Console 🚀
|
||||
run: |
|
||||
set -u
|
||||
|
||||
DEPLOY_BUCKET=${STATIC_DEPLOY_BUCKET:-${{ env.PROJECT }}-${{ env.STACK }}}
|
||||
# this value will come from the dotenv above
|
||||
echo "Deploying to $DEPLOY_BUCKET"
|
||||
wget -O- https://github.com/bep/s3deploy/releases/download/v2.11.0/s3deploy_2.11.0_linux-amd64.tar.gz | tar xzv -f - s3deploy
|
||||
./s3deploy -region ${AWS_REGION} -source console/dist -bucket ${DEPLOY_BUCKET}
|
||||
185
.github/workflows/gcp-cd.yml
vendored
Normal file
185
.github/workflows/gcp-cd.yml
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
name: Fleetbase CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "gcpdeploy/*" ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PROJECT: ${{ vars.PROJECT }}
|
||||
REGISTRY: ${{ vars.REGISTRY }}
|
||||
SOCKETCLUSTER_HOST: ${{ vars.SOCKETCLUSTER_HOST }}
|
||||
API_HOST: ${{ vars.API_HOST }}
|
||||
K8S_CLUSTER_NAME: ${{ vars.K8S_CLUSTER_NAME }}
|
||||
K8S_CLUSTER_LOCATION: ${{ vars.K8S_CLUSTER_LOCATION }}
|
||||
GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }}
|
||||
GCP_SERVICE_ACCOUNT: ${{ vars.GCP_SERVICE_ACCOUNT }}
|
||||
GCP: "True" # switches docker builds to GCP-style registry
|
||||
|
||||
jobs:
|
||||
build_service:
|
||||
name: Build and Deploy the Service
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # This is required for requesting the JWT
|
||||
contents: read # This is required for actions/checkout
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Set Dynamic ENV Vars
|
||||
run: |
|
||||
SHORT_COMMIT=$(echo $GITHUB_SHA | cut -c -8)
|
||||
echo "VERSION=${SHORT_COMMIT}" >> $GITHUB_ENV
|
||||
echo "STACK=$(basename $GITHUB_REF)" >> $GITHUB_ENV
|
||||
echo "REGISTRY_HOST=$(dirname $(dirname $REGISTRY))" >> $GITHUB_ENV
|
||||
|
||||
- id: 'auth'
|
||||
name: 'Authenticate to Google Cloud'
|
||||
uses: 'google-github-actions/auth@v1'
|
||||
with:
|
||||
token_format: "access_token"
|
||||
create_credentials_file: true
|
||||
workload_identity_provider: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER }}
|
||||
service_account: ${{ env.GCP_SERVICE_ACCOUNT }}
|
||||
|
||||
- name: 'Set up Cloud SDK'
|
||||
uses: 'google-github-actions/setup-gcloud@v1'
|
||||
|
||||
- id: 'get-credentials'
|
||||
uses: 'google-github-actions/get-gke-credentials@v1'
|
||||
with:
|
||||
cluster_name: ${{ env.K8S_CLUSTER_NAME }}
|
||||
location: ${{ env.K8S_CLUSTER_LOCATION }}
|
||||
|
||||
- uses: 'docker/login-action@v3'
|
||||
with:
|
||||
registry: ${{ env.REGISTRY_HOST }}
|
||||
username: 'oauth2accesstoken'
|
||||
password: '${{ steps.auth.outputs.access_token }}'
|
||||
|
||||
- name: Prepare Composer Auth Secret
|
||||
run: |
|
||||
if [[ -n "${{ secrets._GITHUB_AUTH_TOKEN }}" ]]; then
|
||||
echo '{"github-oauth": {"github.com": "'${{ secrets._GITHUB_AUTH_TOKEN }}'"}}' > composer-auth.json
|
||||
else
|
||||
echo '{}' > composer-auth.json
|
||||
fi
|
||||
|
||||
- name: nullify ssm-parent config
|
||||
run: |
|
||||
# this is needed to disable ssm-parent, which is used on AWS
|
||||
echo > api/.ssm-parent.yaml
|
||||
|
||||
- name: Build and Release
|
||||
uses: docker/bake-action@v2
|
||||
env:
|
||||
REGISTRY: ${{ env.REGISTRY }}
|
||||
VERSION: ${{ env.VERSION }}
|
||||
CACHE: type=gha
|
||||
with:
|
||||
push: true
|
||||
files: |
|
||||
./docker-bake.hcl
|
||||
|
||||
- name: deploy with helm
|
||||
run: |
|
||||
helm upgrade -i fleetbase infra/helm -n ${{ env.PROJECT }}-${{ env.STACK }} --set image.repository=${{ env.REGISTRY }} --set image.tag=${{ env.VERSION }} --set 'api_host=${{ env.API_HOST }}' --set 'socketcluster_host=${{ env.SOCKETCLUSTER_HOST }}' --set 'ingress.annotations.kubernetes\.io/ingress\.global-static-ip-name=${{ env.PROJECT }}-${{ env.STACK }}'
|
||||
|
||||
build_frontend:
|
||||
name: Build and Deploy the Console
|
||||
needs: [build_service]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # This is required for requesting the JWT
|
||||
contents: read # This is required for actions/checkout
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set Dynamic ENV Vars
|
||||
run: |
|
||||
SHORT_COMMIT=$(echo $GITHUB_SHA | cut -c -8)
|
||||
echo "VERSION=${SHORT_COMMIT}" >> $GITHUB_ENV
|
||||
echo "STACK=$(basename $GITHUB_REF)" >> $GITHUB_ENV
|
||||
|
||||
- id: 'auth'
|
||||
name: 'Authenticate to Google Cloud'
|
||||
uses: 'google-github-actions/auth@v1'
|
||||
with:
|
||||
token_format: "access_token"
|
||||
create_credentials_file: true
|
||||
workload_identity_provider: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER }}
|
||||
service_account: ${{ env.GCP_SERVICE_ACCOUNT }}
|
||||
|
||||
- name: 'Set up Cloud SDK'
|
||||
uses: 'google-github-actions/setup-gcloud@v1'
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm Store Directory
|
||||
id: pnpm-cache
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v3
|
||||
name: Setup pnpm Cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Check for _GITHUB_AUTH_TOKEN and create .npmrc
|
||||
run: |
|
||||
if [[ -n "${{ secrets._GITHUB_AUTH_TOKEN }}" ]]; then
|
||||
echo "//npm.pkg.github.com/:_authToken=${{ secrets._GITHUB_AUTH_TOKEN }}" > .npmrc
|
||||
fi
|
||||
working-directory: ./console
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
working-directory: ./console
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
SOCKETCLUSTER_HOST: ${{ env.SOCKETCLUSTER_HOST }}
|
||||
SOCKETCLUSTER_SECURE: "true"
|
||||
SOCKETCLUSTER_PORT: "443"
|
||||
API_HOST: ${{ env.API_HOST }}
|
||||
run: |
|
||||
set -eu
|
||||
|
||||
pnpm build --environment production
|
||||
working-directory: ./console
|
||||
|
||||
- name: Deploy Console 🚀
|
||||
run: |
|
||||
set -eu
|
||||
|
||||
gcloud app deploy --appyaml console/app.yaml console/dist
|
||||
# leave 2 versions
|
||||
gcloud app versions list --filter="traffic_split=0" --sort-by '~version' --format 'value(version.id)' | sed '1d' | xargs -r gcloud app versions delete
|
||||
23
.gitignore
vendored
23
.gitignore
vendored
@@ -12,4 +12,25 @@ api/storage/*.key
|
||||
api/storage/uploads
|
||||
api/storage/public
|
||||
api/vendor
|
||||
act.sh
|
||||
api/composer.dev.json
|
||||
api/composer-install-dev.sh
|
||||
api/auth.json
|
||||
api/crontab
|
||||
act.sh
|
||||
composer-auth.json
|
||||
docker/database/*
|
||||
docker/database/mysql/*
|
||||
.talismanrc
|
||||
verdaccio/minio
|
||||
verdaccio/config/htpasswd
|
||||
verdaccio/storage
|
||||
# private packages
|
||||
packages/billing
|
||||
packages/flespi
|
||||
packages/loconav
|
||||
packages/internals
|
||||
packages/projectargus-engine
|
||||
# wip
|
||||
packages/solid
|
||||
solid
|
||||
verdaccio
|
||||
51
.gitmodules
vendored
51
.gitmodules
vendored
@@ -1,55 +1,38 @@
|
||||
[submodule "console"]
|
||||
path = console
|
||||
url = git@github.com:fleetbase/console.git
|
||||
[submodule "packages/fleetops"]
|
||||
path = packages/fleetops
|
||||
url = git@github.com:fleetbase/fleetops.git
|
||||
[submodule "packages/storefront"]
|
||||
path = packages/storefront
|
||||
url = git@github.com:fleetbase/storefront.git
|
||||
[submodule "packages/pallet"]
|
||||
path = packages/pallet
|
||||
url = git@github.com:fleetbase/pallet.git
|
||||
[submodule "packages/core-api"]
|
||||
path = packages/core-api
|
||||
url = git@github.com:fleetbase/core-api.git
|
||||
branch = dev-main
|
||||
[submodule "packages/fleetops-api"]
|
||||
path = packages/fleetops-api
|
||||
url = git@github.com:fleetbase/fleetops-api.git
|
||||
branch = dev-main
|
||||
branch = main
|
||||
[submodule "packages/ember-core"]
|
||||
path = packages/ember-core
|
||||
url = git@github.com:fleetbase/ember-core.git
|
||||
branch = dev-main
|
||||
branch = main
|
||||
[submodule "packages/ember-ui"]
|
||||
path = packages/ember-ui
|
||||
url = git@github.com:fleetbase/ember-ui.git
|
||||
branch = dev-main
|
||||
[submodule "packages/storefront-engine"]
|
||||
path = packages/storefront-engine
|
||||
url = git@github.com:fleetbase/storefront-engine.git
|
||||
branch = dev-main
|
||||
branch = main
|
||||
[submodule "packages/iam-engine"]
|
||||
path = packages/iam-engine
|
||||
url = git@github.com:fleetbase/iam-engine.git
|
||||
[submodule "packages/dev-engine"]
|
||||
path = packages/dev-engine
|
||||
url = git@github.com:fleetbase/dev-engine.git
|
||||
branch = dev-main
|
||||
branch = main
|
||||
[submodule "packages/fleetbase-extensions-indexer"]
|
||||
path = packages/fleetbase-extensions-indexer
|
||||
url = git@github.com:fleetbase/fleetbase-extensions-indexer.git
|
||||
[submodule "packages/fleetops-engine"]
|
||||
path = packages/fleetops-engine
|
||||
url = git@github.com:fleetbase/fleetops-engine.git
|
||||
branch = dev-main
|
||||
[submodule "docs/guides"]
|
||||
path = docs/guides
|
||||
url = git@github.com:fleetbase/guides.git
|
||||
[submodule "docs/api-reference"]
|
||||
path = docs/api-reference
|
||||
url = git@github.com:fleetbase/api-reference.git
|
||||
[submodule "packages/storefront-api"]
|
||||
path = packages/storefront-api
|
||||
url = git@github.com:fleetbase/storefront-api.git
|
||||
branch = dev-main
|
||||
[submodule "packages/fleetops-data"]
|
||||
path = packages/fleetops-data
|
||||
url = git@github.com:fleetbase/fleetops-data.git
|
||||
branch = dev-main
|
||||
[submodule "socket"]
|
||||
path = socket
|
||||
url = git@github.com:fleetbase/socket.git
|
||||
branch = dev-main
|
||||
branch = main
|
||||
[submodule "docs"]
|
||||
path = docs
|
||||
url = git@github.com:fleetbase/docs.git
|
||||
|
||||
17
.s3deploy.yml
Normal file
17
.s3deploy.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
routes:
|
||||
- route: "^.+\\.(js|css|svg|ttf)$"
|
||||
headers:
|
||||
Cache-Control: "max-age=600, no-transform, public"
|
||||
gzip: true
|
||||
- route: "^.+\\.(png|jpg)$"
|
||||
headers:
|
||||
Cache-Control: "max-age=600, no-transform, public"
|
||||
gzip: false
|
||||
- route: "^.+\\.(xml|json)$"
|
||||
headers:
|
||||
Cache-Control: "max-age=600, no-transform, public"
|
||||
gzip: true
|
||||
- route: "^.+\\.(html)$"
|
||||
headers:
|
||||
Cache-Control: "public, max-age=0, must-revalidate"
|
||||
gzip: true
|
||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
hello@fleetbase.io.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
11
COMMERCIAL_LICENSE.md
Normal file
11
COMMERCIAL_LICENSE.md
Normal file
@@ -0,0 +1,11 @@
|
||||
FLEETBASE DUAL LICENSE
|
||||
|
||||
COPYRIGHT (C) 2024 FLEETBASE PTE LTD.
|
||||
|
||||
PERMISSION IS HEREBY GRANTED, FREE OF CHARGE, TO ANY PERSON OBTAINING A COPY OF THIS SOFTWARE AND ASSOCIATED DOCUMENTATION FILES (THE "SOFTWARE"), TO USE THE SOFTWARE FOR NON-COMMERCIAL PURPOSES ONLY. NON-COMMERCIAL PURPOSES INCLUDE INTERNAL OPERATIONS, ACADEMIC RESEARCH, PERSONAL PROJECTS, OR ANY OTHER USE THAT IS NOT INTENDED FOR COMMERCIAL GAIN.
|
||||
|
||||
FOR VERSIONS 0.4.10 ONWARDS, YOU ARE PERMITTED TO USE THE SOFTWARE FOR NON-COMMERCIAL PURPOSES FREE OF CHARGE. HOWEVER, COMMERCIAL USE OF THIS SOFTWARE, INCLUDING BUT NOT LIMITED TO BUILDING SAAS PLATFORMS, OFFERING SERVICES TO THIRD PARTIES, OR INTEGRATING WITH COMMERCIAL PRODUCTS, REQUIRES THE PURCHASE OF A COMMERCIAL LICENSE FROM FLEETBASE PTE LTD.
|
||||
|
||||
FOR INQUIRIES REGARDING COMMERCIAL LICENSING OR ANY OTHER QUESTIONS RELATED TO FLEETBASE, PLEASE CONTACT HELLO@FLEETBASE.IO.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
47
CONTRIBUTING.md
Normal file
47
CONTRIBUTING.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Contributing to Fleetbase
|
||||
We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
|
||||
|
||||
- Reporting a bug
|
||||
- Discussing the current state of the code
|
||||
- Submitting a fix
|
||||
- Proposing new features
|
||||
- Becoming a maintainer
|
||||
|
||||
## We Develop with Github
|
||||
We use github to host code, to track issues and feature requests, as well as accept pull requests.
|
||||
|
||||
## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests
|
||||
Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests:
|
||||
|
||||
1. Fork the repo and create your branch from `master`.
|
||||
2. If you've added code that should be tested, add tests.
|
||||
3. If you've changed APIs, update the documentation.
|
||||
4. Ensure the test suite passes.
|
||||
5. Make sure your code lints.
|
||||
6. Issue that pull request!
|
||||
|
||||
## Any contributions you make will be under the MIT Software License
|
||||
In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern.
|
||||
|
||||
## Report bugs using Github's [issues](https://github.com/fleetbase/fleetbase/issues)
|
||||
We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/fleetbase/fleetbase/issues), it's that easy!
|
||||
|
||||
## Write bug reports with detail, background, and sample code
|
||||
|
||||
**Great Bug Reports** tend to have:
|
||||
|
||||
- A quick summary and/or background
|
||||
- Steps to reproduce
|
||||
- Be specific!
|
||||
- Give sample code if you can.
|
||||
- What you expected would happen
|
||||
- What actually happens
|
||||
- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
|
||||
|
||||
People *love* thorough bug reports. I'm not even kidding.
|
||||
|
||||
## License
|
||||
By contributing, you agree that your contributions will be licensed under its MIT License.
|
||||
|
||||
## References
|
||||
This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md)
|
||||
12
Caddyfile
Normal file
12
Caddyfile
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
frankenphp
|
||||
order php_server before file_server
|
||||
}
|
||||
|
||||
http://:8000 {
|
||||
root * /fleetbase/api/public
|
||||
encode zstd gzip
|
||||
php_server {
|
||||
resolve_root_symlink
|
||||
}
|
||||
}
|
||||
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT LICENSE
|
||||
|
||||
COPYRIGHT (C) 2023 FLEETBASE PTE LTD
|
||||
|
||||
PERMISSION IS HEREBY GRANTED, FREE OF CHARGE, TO ANY PERSON OBTAINING A COPY
|
||||
OF THIS SOFTWARE AND ASSOCIATED DOCUMENTATION FILES (THE "SOFTWARE"), TO DEAL
|
||||
IN THE SOFTWARE WITHOUT RESTRICTION, INCLUDING WITHOUT LIMITATION THE RIGHTS
|
||||
TO USE, COPY, MODIFY, MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL
|
||||
COPIES OF THE SOFTWARE, AND TO PERMIT PERSONS TO WHOM THE SOFTWARE IS
|
||||
FURNISHED TO DO SO, SUBJECT TO THE FOLLOWING CONDITIONS:
|
||||
|
||||
THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN ALL
|
||||
COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
12
PULL_REQUEST_TEMPLATE.md
Normal file
12
PULL_REQUEST_TEMPLATE.md
Normal file
@@ -0,0 +1,12 @@
|
||||
### What change does this PR introduce?
|
||||
|
||||
<!-- Explain here the changes your PR introduces and text to help us understand the context of this change. -->
|
||||
|
||||
### Why was this change needed?
|
||||
|
||||
<!-- If your PR fixes an open issue, use `Closes #999` to link your PR with the issue. #999 stands for the issue number you are fixing, Example: Closes #31 -->
|
||||
|
||||
### Other information (Screenshots)
|
||||
|
||||
<!-- Add notes or any other information here -->
|
||||
<!-- Also add all the screenshots which support your changes -->
|
||||
298
README.md
298
README.md
@@ -1,46 +1,49 @@
|
||||
<div id="hero">
|
||||
<p align="center" dir="auto">
|
||||
<a href="https://fleetbase.io" rel="nofollow">
|
||||
<img src="https://user-images.githubusercontent.com/58805033/191936702-fed04b0f-7966-4041-96d0-95e27bf98248.png" alt="Fleetbase logo" width="600" height="140" style="max-width: 100%;">
|
||||
<img src="https://user-images.githubusercontent.com/58805033/191936702-fed04b0f-7966-4041-96d0-95e27bf98248.png" alt="Fleetbase logo" width="500" height="120" style="max-width: 100%;">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center" dir="auto">
|
||||
Open Source Logistics & Supply Chain Platform and Infrastructure
|
||||
Modular logistics and supply chain operating system
|
||||
<br>
|
||||
<a href="https://docs.fleetbase.dev/api" rel="nofollow">Fleetbase Documentation →</a>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://github.com/fleetbase/fleetbase/issues">Report an Issue</a>
|
||||
<a href="https://docs.fleetbase.io/" rel="nofollow">Documentation</a>
|
||||
·
|
||||
<a href="https://fleetbase.dev/api">API Reference</a>
|
||||
<a href="https://console.fleetbase.io" rel="nofollow">Cloud Version</a>
|
||||
·
|
||||
<a href="https://github.com/fleetbase/guides">Guides</a>
|
||||
<a href="https://fleetbase.apichecker.com" target="_api_status" rel="nofollow">API Status</a>
|
||||
·
|
||||
<a href="https://github.com/fleetbase/fleetbase/issues">Request a Feature</a>
|
||||
<a href="https://meetings.hubspot.com/shiv-thakker" rel="nofollow">Book a Demo</a>
|
||||
·
|
||||
<a href="https://www.fleetbase.io/blog-2" rel="nofollow">Blog</a>
|
||||
<a href="https://discord.gg/V7RVWRQ2Wm" target="discord" rel="nofollow">Discord</a>
|
||||
</p>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
# ⭐️ Overview
|
||||
## What is Fleetbase?
|
||||
|
||||
Fleetbase is a comprehensive, open-source platform for the management and orchestration of logistics and supply chain operations. This dual-natured platform serves not only as an efficient operational tool for companies within these industries but also as a robust foundational layer for developers. Its versatility emanates from its core, which is skillfully constructed around a series of "extensions," creating a flexible and customizable framework that accommodates a wide spectrum of supply chain and logistics requirements.
|
||||
|
||||
Every individual extension within Fleetbase is purposefully engineered to fulfill specific roles within the expansive ecosystem of supply chain and logistics. With inherent extensibility users have the freedom to craft their own extensions, thereby expanding the ecosystem and ensuring the platform's adaptability across a multitude of use cases. This extensible nature ensures that Fleetbase remains at the forefront of meeting diverse logistical and supply chain needs now and into the future.
|
||||
Fleetbase is a modular logistics and supply chain operating system designed to streamline management, planning, optimization, and operational control across various sectors of the supply chain industry.
|
||||
|
||||
<p align="center" dir="auto">
|
||||
<img src="https://flb-assets.s3.ap-southeast-1.amazonaws.com/github/dark-light-layered-screenshots.png" alt="Fleetbase Console" width="600" style="max-width: 100%;" />
|
||||
</p>
|
||||
<img src="https://github.com/fleetbase/fleetbase/assets/816371/125348c9-c88a-49fe-b098-9abec9d7dff8" alt="Fleetbase Console" width="1200" style="max-width: 100%;" />
|
||||
</p>
|
||||
|
||||
# 📖 Table of contents
|
||||
**Quickstart**
|
||||
|
||||
- [What's Included](#-whats-included)
|
||||
- [Getting Started](#-getting-started)
|
||||
- [Use Cases](#-use-cases)
|
||||
- [Installation](#-installation)
|
||||
- [Extensions](#-extensions)
|
||||
```bash
|
||||
git clone git@github.com:fleetbase/fleetbase.git
|
||||
cd fleetbase
|
||||
docker-compose up -d
|
||||
docker exec -ti fleetbase-application-1 bash
|
||||
sh deploy.sh
|
||||
```
|
||||
|
||||
## 📖 Table of contents
|
||||
|
||||
- [Features](#-features)
|
||||
- [Install](#-install)
|
||||
- [Apps](#-apps)
|
||||
- [Roadmap](#-roadmap)
|
||||
- [Bugs and Feature Requests](#-bugs-and--feature-requests)
|
||||
- [Documentation](#-documentation)
|
||||
- [Contributing](#-contributing)
|
||||
@@ -48,177 +51,66 @@ Every individual extension within Fleetbase is purposefully engineered to fulfil
|
||||
- [Creators](#-creators)
|
||||
- [License & Copyright](#-license-and-copyright)
|
||||
|
||||
# 📦 What's Included
|
||||
## 📦 Features
|
||||
- **Extensible:** Build installable extensions and additional functionality directly into the OS via modular architecture.
|
||||
- **Developer Friendly:** RESTful API, socket, and webhooks to seamlessly integrate with external systems or develop custom applications.
|
||||
- **Native Apps:** Collection of open-source and native apps designed for operations and customer facing.
|
||||
- **Collaboration:** Dedicated chat and comments system for collaboration across your organization.
|
||||
- **Security:** Secure data encryption, adherence to industry-standard security practices, and a comprehensive dynamic Identity and Access Management (IAM) system.
|
||||
- **Telematics:** Integrate and connect to hardware devices and sensors to provide more feedback and visibility into operations.
|
||||
- **Internationalized:** Translate into multiple languages to accommodate diverse user bases and global operations.
|
||||
- **Framework:** PHP core built around logistics and supply chain abstractions to streamline extension development.
|
||||
- **Dynamic:** Configurable rules, flows and logic to enable automation and customization.
|
||||
- **UI/UX:** Clean, responsive user-friendly interface for efficient management and operations from desktop or mobile.
|
||||
- **Dashboards:** Create custom dashboards and widgets to get full visibility into operations.
|
||||
- **Scalability:** Uninterrupted growth with scalable infrastructure and design, capable of handling increasing data volume and user demand as your business expands.
|
||||
- **Continuous Improvements:** Commitment to continuous improvement, providing regular updates that seamlessly introduce optimizations, new features, and overall enhancements to the OS.
|
||||
- **Open Source:** Deploy it either on-premise or in the cloud according to your organization's needs and preferences.
|
||||
|
||||
Fleetbase is more than just a platform; it's a versatile ecosystem carefully architected to empower developers and businesses alike. Fleetbase comes pre-installed with a few extensions that provide base functionality to get users and businesses started:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Console</strong>: Fleetbase's frontend console is constructed using Ember.js and Ember Engines, providing a highly modular and extensible design. This not only enables the system to adapt and scale to your growing requirements but also simplifies the process of integrating new extensions. With the console's design, extensions can be easily installed using their respective package managers, thereby reducing complexity and time spent on integration.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Fleetbase API</strong>: Fleetbase's backend API and framework, developed using the reliable Laravel framework, is thoughtfully designed with extension development and integration in mind. This robust and flexible infrastructure not only manages intricate data structures and transactions with grace but also facilitates the easy incorporation of new extensions via package managers. We provide additional packages that allow developers to craft their unique extensions, further enhancing the flexibility and extensibility of the Fleetbase ecosystem.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Extensions</strong>: Fleetbase is designed to provide immediate utility out-of-the-box. It comes pre-installed with several key extensions
|
||||
<ul>
|
||||
<li><strong>FleetOps</strong>: FleetOps, our comprehensive fleet management extension, caters to all aspects of last-mile operations. Some of it's features include:
|
||||
<ul>
|
||||
<li>
|
||||
Real-time tracking for vehicles and assets, ensuring optimal operational efficiency.
|
||||
</li>
|
||||
<li>
|
||||
Order creation and management, facilitating seamless transaction processing.
|
||||
Service rates management, helping maintain financial transparency and profitability.
|
||||
</li>
|
||||
<li>
|
||||
Fleet management, providing a holistic view and control of your fleet.
|
||||
</li>
|
||||
<li>
|
||||
Third-party vendor integrations, allowing you to consolidate your resources.
|
||||
</li>
|
||||
<li>
|
||||
API & Webhooks that not only offer increased interconnectivity but also serve to facilitate integrations with other services and applications, making FleetOps a truly versatile solution.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Storefront</strong>: Storefront is an extension that delivers headless commerce functionality, ideal for businesses aspiring to develop on-demand stores or marketplaces. It aims to facilitate seamless transactions while focusing on providing an excellent user experience.
|
||||
</li>
|
||||
<li><strong>Dev Console</strong>: The Dev Console extension is a developer's toolbox, providing resources such as:
|
||||
<ul>
|
||||
<li>
|
||||
API keys management, ensuring secure interactions with the application programming interface.
|
||||
</li>
|
||||
<li>
|
||||
Webhooks management, enabling real-time data exchanges.
|
||||
</li>
|
||||
<li>
|
||||
Sockets management, facilitating bi-directional client-server communication.
|
||||
</li>
|
||||
<li>
|
||||
Logs management, crucial for system monitoring and troubleshooting.
|
||||
</li>
|
||||
<li>
|
||||
API events management, keeping a pulse on system communications.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
# 🏁 Getting Started
|
||||
|
||||
Before you can get started with Fleetbase, you'll need to make sure you have the following prerequisites:
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
A computer running either Linux, Mac OS, or Windows
|
||||
</li>
|
||||
<li>Docker installed</li>
|
||||
<li>Git installed</li>
|
||||
<li>If you want to try now, the <a href="https://console.fleetbase.io/" target="_fleetbase" alt="Fleetbase">cloud hosted version of Fleetbase available here.</a></li>
|
||||
</ol>
|
||||
|
||||
# 🚦 Use Cases
|
||||
|
||||
Fleetbase's comprehensive suite of features and the modular design make it highly versatile, catering to a broad array of applications across different industries. Here are a few use cases:
|
||||
<ul>
|
||||
<li><strong>Logistics and Supply Chain Management</strong>: Fleetbase could be employed by a logistics company to streamline its operations. Real-time tracking provided by FleetOps would help maintain visibility of fleet vehicles and assets at all times. This would ensure timely delivery, reduce operational inefficiencies, and enable proactive management of any logistical issues. Additionally, the order creation and management feature could be used to manage deliveries, pickups, and routing.</li>
|
||||
## 💾 Install
|
||||
Getting up and running with Fleetbase via Docker is the quickest and most straightforward way. If you’d like to use Fleetbase without docker read the [full install guide in the Fleetbase documentation](https://docs.fleetbase.io/getting-started/install).
|
||||
|
||||
<li><strong>On-demand Delivery Services</strong>: On-demand services like food delivery or courier companies could utilize Fleetbase to manage their fleet of delivery agents. The real-time tracking functionality would help to optimize routes and ensure prompt deliveries, while the order creation and management system would efficiently handle incoming orders.</li>
|
||||
Make sure you have both the latest versions of docker and docker-compose installed on your system.
|
||||
|
||||
```bash
|
||||
git clone git@github.com:fleetbase/fleetbase.git
|
||||
cd fleetbase
|
||||
docker-compose up -d
|
||||
docker exec -ti fleetbase-application-1 bash
|
||||
sh deploy.sh
|
||||
```
|
||||
|
||||
### Accessing Fleetbase
|
||||
Once successfully installed and running you can then access the Fleetbase console on port 4200 and the API will be accessible from port 8000.
|
||||
|
||||
<li><strong>E-Commerce Platforms</strong>: E-commerce businesses could leverage Fleetbase to manage their backend logistics. The Storefront extension would enable seamless online transactions, while FleetOps could manage all aspects of the delivery process, ensuring a smooth shopping experience for the customers.</li>
|
||||
Fleetbase Console: http://localhost:4200
|
||||
Fleetbase API: http://localhost:8000
|
||||
|
||||
### Additional Configurations
|
||||
|
||||
**CORS:** If you’re installing directly on a server you may need to add your IP address or domain to the `api/config/cors.php` file in the `allowed_hosts` array.
|
||||
|
||||
<li><strong>Ride-Hailing Services</strong>: Fleetbase could be a perfect fit for ride-hailing or car rental services. FleetOps would manage real-time tracking of vehicles, maintaining optimal vehicle utilization, while the API and Webhooks would facilitate integration with mobile apps to provide real-time updates to customers.</li>
|
||||
**Routing:** Fleetbase ships with its own OSRM server hosted at `[bundle.routing.fleetbase.io](https://bundle.routing.fleetbase.io)` but you’re able to use your own or any other OSRM compatible server. You can modify this in the `console/environments` directory by modifying the env file of the environment you’re deploying and setting the `OSRM_HOST` to the OSRM server for Fleetbase to use.
|
||||
|
||||
<li><strong>Third-party Logistics (3PL) Provider</strong>: A 3PL provider could use Fleetbase for comprehensive management of its services. From real-time tracking of cargo to managing service rates and integration with other vendors in the supply chain, Fleetbase could provide an all-in-one solution.</li>
|
||||
|
||||
<li><strong>Developer Resource Management</strong>: Developers building complex, resource-intensive applications could benefit from Fleetbase's Dev Console. API keys and webhook management could streamline the secure interaction between different software components. At the same time, sockets, logs, and API events management tools would assist in maintaining, troubleshooting, and improving the system.</li>
|
||||
**Services:** There are a few environment variables which need to be set for Fleetbase to function with full features. If you’re deploying with docker then it’s easiest to just create a `docker-compose.override.yml` and supply the environment variables in this file.
|
||||
|
||||
<li><strong>Public Transportation Systems</strong>: City transportation services could use Fleetbase to optimize their bus or subway operations. With FleetOps, they could have real-time tracking of their vehicles, ensuring that schedules are met promptly and delays are handled effectively. Moreover, service rates management could assist in setting and adjusting fares, while the API and Webhooks functionality could integrate with public apps to provide real-time updates to commuters about arrivals, delays, and route changes.</li>
|
||||
```yaml
|
||||
version: “3.8”
|
||||
services:
|
||||
application:
|
||||
environment:
|
||||
MAIL_MAILER: (ses, smtp, mailgun, postmark, sendgrid)
|
||||
OSRM_HOST: https://bundle.routing.fleetbase.io
|
||||
IPINFO_API_KEY:
|
||||
GOOGLE_MAPS_API_KEY:
|
||||
GOOGLE_MAPS_LOCALE: us
|
||||
TWILIO_SID:
|
||||
TWILIO_TOKEN:
|
||||
TWILIO_FROM:
|
||||
CONSOLE_HOST: http://localhost:4200
|
||||
```
|
||||
|
||||
<li><strong>Fleet Leasing Companies</strong>: Fleet leasing companies could employ Fleetbase to manage their vehicle assets and track their status in real time. From managing service rates to ensuring the best utilization of assets, FleetOps could provide a holistic solution. Moreover, the Storefront extension could be used to list available vehicles and manage online reservations seamlessly.</li>
|
||||
|
||||
<li><strong>Emergency Services</strong>: Emergency services like ambulance or firefighting departments could use Fleetbase to manage their operations. FleetOps would provide real-time tracking, ensuring that emergency vehicles are dispatched quickly and the fastest routes are chosen. In addition, the API and Webhooks functionality could allow integration with emergency call centers, ensuring a seamless flow of information and a swift response to emergencies.</li>
|
||||
</ul>
|
||||
|
||||
Remember, these are just a few examples. Given the modular and extensible nature of Fleetbase, it can be customized and scaled to fit many other use cases across different industries.
|
||||
|
||||
# 💾 Installation
|
||||
|
||||
Getting Fleetbase up and running on your system using Docker and Docker-compose is straightforward. Please follow the steps below:
|
||||
|
||||
### Prerequisites
|
||||
|
||||
<ul>
|
||||
<li>Ensure that you have Docker and Docker-compose installed on your system. If not, you can download and install them from their respective official websites:
|
||||
<ul>
|
||||
<li><a href="https://docs.docker.com/get-docker/" target="_docker">Docker</a></li>
|
||||
<li><a href="https://docs.docker.com/compose/install/" target="_docker_compose">Docker Compose</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
Clone the Fleetbase repository to your local machine:
|
||||
<pre>bash git clone https://github.com/fleetbase/fleetbase.git</pre>
|
||||
</li>
|
||||
<li>
|
||||
Navigate to the cloned repository:
|
||||
<pre>cd fleetbase</pre>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Build and Run Fleetbase
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<strong>Start the Docker daemon:</strong>
|
||||
Ensure the Docker daemon is running on your machine. You can either start Docker Desktop or either executed by running:
|
||||
<pre>sudo systemctl start docker</pre>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Build the Docker containers:</strong>
|
||||
Use Docker Compose to build and run the necessary containers. In the root directory of the Fleetbase repository, run:
|
||||
<pre>docker-compose up -d</pre>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
### Access Fleetbase
|
||||
|
||||
Now that Fleetbase is up and running via Docker you can find the console and the API accessible:
|
||||
|
||||
<ul>
|
||||
<li>Fleetbase Console: <code>http://localhost:4200</code></li>
|
||||
<li>Fleetbase API: <code>http://localhost:8000</code></li>
|
||||
</ul>
|
||||
|
||||
# 🧩 Extensions
|
||||
|
||||
Fleetbase extensions provide a powerful way to enhance and customize the functionality of Fleetbase to suit your specific needs. They are standalone modules that seamlessly integrate with Fleetbase's frontend and backend, allowing you to extend its capabilities.
|
||||
|
||||
### What are Fleetbase Extensions?
|
||||
Fleetbase Extensions are built using both a backend PHP package and a frontend Ember Engine Addon. They are designed to blend seamlessly into the Fleetbase ecosystem, utilizing shared services, utilities, stylesheets, components, and template helpers.
|
||||
|
||||
### How do Extensions Work?
|
||||
<ul>
|
||||
<li><strong>Backend</strong>: The backend of an extension is developed as a PHP package. This package should utilize the composer package `fleetbase/core-api`, which provides core API functionalities, making it easier to integrate your extension with Fleetbase's backend.</li>
|
||||
|
||||
<li><strong>Engine</strong>: The frontend of an extension is built as an Ember Engine Addon. The Addon must require the packages `@fleetbase/ember-core` and `@fleetbase/ember-ui`. The `@fleetbase/ember-core` package provides core services and utilities that help to align your extension with Fleetbase's frontend. The `@fleetbase/ember-ui` package, on the other hand, supplies all the stylesheets, components, and template helpers needed to design a Fleetbase extension that seamlessly matches the look and feel of the Fleetbase UI.</li>
|
||||
</ul>
|
||||
|
||||
### Building a Fleetbase Extension
|
||||
To create a Fleetbase extension, follow these steps:
|
||||
|
||||
<ul>
|
||||
<li><strong>Backend PHP Package Creation</strong>: Begin by creating a backend PHP package. Make sure to use the composer package `fleetbase/core-api` to ensure smooth integration with Fleetbase's backend.</li>
|
||||
|
||||
<li><strong>Frontend Ember Engine Addon</strong>: Next, you need to create the frontend of the extension using Ember Engine. Be sure to include the `@fleetbase/ember-core` and `@fleetbase/ember-ui` packages. These packages provide necessary services, utilities, and design components for aligning your extension with Fleetbase's UI.</li>
|
||||
|
||||
<li><strong>Integrate Your Extension</strong>: Once you have the backend and frontend ready, you can integrate your extension into Fleetbase by installing it via the respective package managers. In the future you will be able to publish your extension to the Fleetbase extensions repository making it available to all instances of Fleetbase with the ability to even sell your extension.</li>
|
||||
</ul>
|
||||
|
||||
With Fleetbase's modular architecture, you can develop your extensions to solve unique problems, enhance existing functionality, or add entirely new capabilities to your Fleetbase instance. This extensibility allows Fleetbase to adapt and evolve with your growing business needs.
|
||||
You can learn more about full installation, and configuration in the [official documentation](https://docs.fleetbase.io/getting-started/install).
|
||||
|
||||
# 📱 Apps
|
||||
|
||||
@@ -229,40 +121,36 @@ Fleetbase offers a few open sourced apps which are built on Fleetbase which can
|
||||
<li><a href="https://github.com/fleetbase/navigator-app">Navigator App</a>: Fleetbase based driver app which can be used for drivers to manage and update order, additionally provides real time driver location which can be viewed in the Fleetbase Console.</li>
|
||||
</ul>
|
||||
|
||||
# 🛣 Roadmap
|
||||
## 🛣️ Roadmap
|
||||
1. **Extensions Registry and Marketplace** ~ Allows users to publish and sell installable extensions on Fleetbase instances.
|
||||
2. **Inventory and Warehouse Management** ~ Pallet will be Fleetbase’s first official extension for WMS & Inventory.
|
||||
3. **Customer Facing Views** ~ Extensions will be able to create public/customer facing views tracking and management from outside of the console UI.
|
||||
4. **Binary Builds** ~ Run Fleetbase from a single binary.
|
||||
5. **Fleetbase CLI** ~ Official CLI for publishing and managing extensions, as well as scaffolding extensions.
|
||||
6. **Fleetbase for Desktop** ~ Desktop builds for OSX and Windows.
|
||||
7. **Custom Maps and Routing Engines** ~ Feature to enable easy integrations with custom maps and routing engines like Google Maps or Mapbox etc…
|
||||
|
||||
<ol>
|
||||
<li>Install Flow & System Configuration from Console</li>
|
||||
<li>Open Source Extension Repository</li>
|
||||
<li>Inventory and Warehouse Management Extension</li>
|
||||
<li>Freight Forwarder Quote Parser/ System Extension</li>
|
||||
</ol>
|
||||
|
||||
# 🪲 Bugs and 💡 Feature Requests
|
||||
## 🪲 Bugs and 💡 Feature Requests
|
||||
|
||||
Have a bug or a feature request? Please check the <a href="https://github.com/fleetbase/fleetbase/issues">issue tracker</a> and search for existing and closed issues. If your problem or idea is not addressed yet, please <a href="https://github.com/fleetbase/fleetbase/issues/new">open a new issue</a>.
|
||||
|
||||
# 📚 Documentation
|
||||
## 👨💻 Contributing
|
||||
|
||||
View and contribute to our <a href="https://github.com/fleetbase/guides">Fleetbase Guide's</a> and <a href="https://github.com/fleetbase/api-reference">API Reference</a>
|
||||
Please read through our <a href="https://github.com/fleetbase/fleetbase/blob/main/CONTRIBUTING.md">contributing guidelines</a>. Included are directions for opening issues, coding standards, and notes on development.
|
||||
|
||||
# 👨💻 Contributing
|
||||
## 👥 Community
|
||||
|
||||
Please read through our <a href="https://github.com/fleetbase/fleetbase/blob/main/.github/CONTRIBUTING.md">contributing guidelines</a>. Included are directions for opening issues, coding standards, and notes on development.
|
||||
|
||||
# 👥 Community
|
||||
|
||||
Get updates on Fleetbase's development and chat with the project maintainers and community members.
|
||||
Get updates on Fleetbase's development and chat with the project maintainers and community members by joining our <a href="https://discord.gg/V7RVWRQ2Wm">Discord</a>.
|
||||
|
||||
<ul>
|
||||
<li>Follow <a href="https://twitter.com/fleetbase_io">@fleetbase_io on Twitter</a>.</li>
|
||||
<li>Follow <a href="https://x.com/fleetbase_io">@fleetbase_io on X</a>.</li>
|
||||
<li>Read and subscribe to <a href="https://www.fleetbase.io/blog-2">The Official Fleetbase Blog</a>.</li>
|
||||
<li>Ask and explore <a href="https://github.com/orgs/fleetbase/discussions">our GitHub Discussions</a>.</li>
|
||||
</ul>
|
||||
<p dir="auto">See the <a href="https://github.com/fleetbase/fleetbase/releases">Releases</a> section of our GitHub project for changelogs for each release version of Fleetbase.</p>
|
||||
<p>Release announcement posts on <a href="https://www.fleetbase.io/blog-2" rel="nofollow">the official Fleetbase blog</a> contain summaries of the most noteworthy changes made in each release.</p>
|
||||
|
||||
# Creators
|
||||
## Creators
|
||||
|
||||
<p dir="auto"><strong>Ronald A. Richardson</strong>- Co-founder & CTO</p>
|
||||
<img src="https://user-images.githubusercontent.com/58805033/230263021-212f2553-1269-473d-be94-313cb3eecfa5.png" alt="Ron Image" width="75" height="75" style="max-width: 100%;">
|
||||
|
||||
39
SECURITY.md
Normal file
39
SECURITY.md
Normal file
@@ -0,0 +1,39 @@
|
||||
Contact: hello@fleetbase.io
|
||||
|
||||
Based on: https://supabase.com/.well-known/security.txt
|
||||
|
||||
|
||||
At Fleetbase, we consider the security of our systems a top priority. But no matter how much effort we put into system security, there can still be vulnerabilities present.
|
||||
|
||||
If you discover a vulnerability, we would like to know about it so we can take steps to address it as quickly as possible. We would like to ask you to help us better protect our clients and our systems.
|
||||
|
||||
Out of scope vulnerabilities:
|
||||
|
||||
- Clickjacking on pages with no sensitive actions.
|
||||
- Unauthenticated/logout/login CSRF.
|
||||
- Attacks requiring MITM or physical access to a user's device.
|
||||
- Any activity that could lead to the disruption of our service (DoS).
|
||||
- Content spoofing and text injection issues without showing an attack vector/without being able to modify HTML/CSS.
|
||||
- Email spoofing
|
||||
- Missing DNSSEC, CAA, CSP headers
|
||||
- Lack of Secure or HTTP only flag on non-sensitive cookies
|
||||
- Deadlinks
|
||||
|
||||
Please do the following:
|
||||
|
||||
- E-mail your findings to hello@fleetbase.io.
|
||||
- Do not run automated scanners on our infrastructure or dashboard. If you wish to do this, contact us and we will set up a sandbox for you.
|
||||
- Do not take advantage of the vulnerability or problem you have discovered, for example by downloading more data than necessary to demonstrate the vulnerability or deleting or modifying other people's data,
|
||||
- Do not reveal the problem to others until it has been resolved,
|
||||
- Do not use attacks on physical security, social engineering, distributed denial of service, spam or applications of third parties, and
|
||||
- Do provide sufficient information to reproduce the problem, so we will be able to resolve it as quickly as possible. Usually, the IP address or the URL of the affected system and a description of the vulnerability will be sufficient, but complex vulnerabilities may require further explanation.
|
||||
|
||||
What we promise:
|
||||
|
||||
- We will respond to your report within 3 business days with our evaluation of the report and an expected resolution date,
|
||||
- If you have followed the instructions above, we will not take any legal action against you in regard to the report,
|
||||
- We will handle your report with strict confidentiality, and not pass on your personal details to third parties without your permission,
|
||||
- We will keep you informed of the progress towards resolving the problem,
|
||||
- In the public information concerning the problem reported, we will give your name as the discoverer of the problem (unless you desire otherwise), and
|
||||
|
||||
We strive to resolve all problems as quickly as possible, and we would like to play an active role in the ultimate publication on the problem after it is resolved.
|
||||
4
api/.gitignore
vendored
4
api/.gitignore
vendored
@@ -13,3 +13,7 @@ npm-debug.log
|
||||
yarn-error.log
|
||||
/.idea
|
||||
/.vscode
|
||||
.composer.dev.json
|
||||
/caddy
|
||||
frankenphp
|
||||
frankenphp-worker.php
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400"></a></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/laravel/framework"><img src="https://travis-ci.org/laravel/framework.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## About Laravel
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
|
||||
## Learning Laravel
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 1500 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
## Laravel Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell).
|
||||
|
||||
### Premium Partners
|
||||
|
||||
- **[Vehikl](https://vehikl.com/)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Cubet Techno Labs](https://cubettech.com)**
|
||||
- **[Cyber-Duck](https://cyber-duck.co.uk)**
|
||||
- **[Many](https://www.many.co.uk)**
|
||||
- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
|
||||
- **[DevSquad](https://devsquad.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
|
||||
- **[OP.GG](https://op.gg)**
|
||||
- **[WebReinvent](https://webreinvent.com/?utm_source=laravel&utm_medium=github&utm_campaign=patreon-sponsors)**
|
||||
- **[Lendio](https://lendio.com)**
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
@@ -2,20 +2,10 @@
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Throwable;
|
||||
use Fleetbase\Exceptions\Handler as ExceptionHandler;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* A list of the exception types that are not reported.
|
||||
*
|
||||
* @var array<int, class-string<Throwable>>
|
||||
*/
|
||||
protected $dontReport = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of the inputs that are never flashed for validation exceptions.
|
||||
*
|
||||
@@ -26,16 +16,4 @@ class Handler extends ExceptionHandler
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the exception handling callbacks for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->reportable(function (Throwable $e) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class Kernel extends HttpKernel
|
||||
protected $middleware = [
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Fruitcake\Cors\HandleCors::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
|
||||
@@ -14,8 +14,6 @@ class Authenticate extends Middleware
|
||||
*/
|
||||
protected function redirectTo($request)
|
||||
{
|
||||
if (! $request->expectsJson()) {
|
||||
return route('login');
|
||||
}
|
||||
return response()->error('Unauthenticated.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f composer.lock ]; then
|
||||
rm composer.lock
|
||||
fi
|
||||
|
||||
mv composer.json composer.prod.json && mv composer.dev.json composer.json && composer install && mv composer.json composer.dev.json && mv composer.prod.json composer.json
|
||||
@@ -1,85 +0,0 @@
|
||||
{
|
||||
"name": "laravel/laravel",
|
||||
"type": "project",
|
||||
"description": "The Laravel Framework.",
|
||||
"keywords": ["framework", "laravel"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
"fleetbase/core-api": "^1.1.1-alpha",
|
||||
"fleetbase/fleetops-api": "^1.0.5-alpha",
|
||||
"fleetbase/storefront-api": "^1.0.4-alpha",
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"laravel/framework": "^8.75",
|
||||
"laravel/sanctum": "^2.11",
|
||||
"laravel/tinker": "^2.5",
|
||||
"league/flysystem-aws-s3-v3": "^1.0",
|
||||
"maatwebsite/excel": "^3.1",
|
||||
"phpoffice/phpspreadsheet": "^1.28",
|
||||
"predis/predis": "^2.1",
|
||||
"psr/http-factory-implementation": "*",
|
||||
"textalk/websocket": "^1.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"facade/ignition": "^2.5",
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"kitloong/laravel-migrations-generator": "^6.10",
|
||||
"laravel/sail": "^1.0.1",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"nunomaduro/collision": "^5.10",
|
||||
"phpunit/phpunit": "^9.5.10"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "path",
|
||||
"url": "../packages/core-api"
|
||||
},
|
||||
{
|
||||
"type": "path",
|
||||
"url": "../packages/fleetops-api"
|
||||
},
|
||||
{
|
||||
"type": "path",
|
||||
"url": "../packages/storefront-api"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": ["Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "@php artisan package:discover --ansi"],
|
||||
"post-update-cmd": ["@php artisan vendor:publish --tag=laravel-assets --ansi --force"],
|
||||
"post-root-package-install": ["@php -r \"file_exists('.env') || copy('.env.example', '.env');\""],
|
||||
"post-create-project-cmd": ["@php artisan key:generate --ansi"],
|
||||
"clear-logs": ["rm -rf storage/logs/*.log", "composer dump-autoload", "chmod -R 777 storage", "chmod -R 777 bootstrap/cache", "chmod -R 777 resources"],
|
||||
"clean-logs": ["composer run-script clear-logs"],
|
||||
"dock": ["docker exec -it fleetbase_os_application_1 /usr/bin/tmux -u new"],
|
||||
"dock-server": ["docker exec -it fleetbase_os_httpd_1 /bin/sh"],
|
||||
"tunnel": ["ngrok http --region=ap --hostname=fleetbase.ap.ngrok.io 8000"]
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"dont-discover": []
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"php-http/discovery": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
@@ -1,38 +1,43 @@
|
||||
{
|
||||
"name": "laravel/laravel",
|
||||
"name": "fleetbase/fleetbase",
|
||||
"type": "project",
|
||||
"description": "The Laravel Framework.",
|
||||
"description": "Modular logistics and supply chain operating system (LSOS)",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"laravel"
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
"fleetbase/core-api": "^1.1.1-alpha",
|
||||
"fleetbase/fleetops-api": "^1.0.5-alpha",
|
||||
"fleetbase/storefront-api": "^1.0.4-alpha",
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"php": "^8.0",
|
||||
"fleetbase/core-api": "^1.4.26",
|
||||
"fleetbase/fleetops-api": "^0.5.1",
|
||||
"fleetbase/storefront-api": "^0.3.10",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"laravel/framework": "^8.75",
|
||||
"laravel/sanctum": "^2.11",
|
||||
"laravel/tinker": "^2.5",
|
||||
"league/flysystem-aws-s3-v3": "^1.0",
|
||||
"laravel/framework": "^10.0",
|
||||
"laravel/octane": "^2.3",
|
||||
"laravel/tinker": "^2.9",
|
||||
"league/flysystem-aws-s3-v3": "^3.0",
|
||||
"maatwebsite/excel": "^3.1",
|
||||
"phpoffice/phpspreadsheet": "^1.28",
|
||||
"predis/predis": "^2.1",
|
||||
"psr/http-factory-implementation": "*",
|
||||
"textalk/websocket": "^1.6"
|
||||
"s-ichikawa/laravel-sendgrid-driver": "^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"facade/ignition": "^2.5",
|
||||
"spatie/laravel-ignition": "^2.0",
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"kitloong/laravel-migrations-generator": "^6.10",
|
||||
"laravel/sail": "^1.0.1",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"nunomaduro/collision": "^5.10",
|
||||
"phpunit/phpunit": "^9.5.10"
|
||||
"nunomaduro/collision": "^7.0",
|
||||
"phpunit/phpunit": "^10.0"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/fleetbase/laravel-model-caching"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
@@ -85,6 +90,7 @@
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"secure-http": false,
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
|
||||
9923
api/composer.lock
generated
9923
api/composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'Laravel'),
|
||||
'name' => env('APP_NAME', 'Fleetbase'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -52,7 +52,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
'url' => env('APP_URL', 'http://localhost:8000'),
|
||||
|
||||
'asset_url' => env('ASSET_URL', null),
|
||||
|
||||
@@ -165,7 +165,7 @@ return [
|
||||
/*
|
||||
* Package Service Providers...
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Application Service Providers...
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('BROADCAST_DRIVER', 'null'),
|
||||
'default' => env('BROADCAST_DRIVER', 'socketcluster'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -15,7 +15,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('CACHE_DRIVER', 'file'),
|
||||
'default' => env('CACHE_DRIVER', 'redis'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Fleetbase\Support\Utils;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
@@ -19,13 +21,13 @@ return [
|
||||
|
||||
'allowed_methods' => ['*'],
|
||||
|
||||
'allowed_origins' => ['http://localhost:4200'],
|
||||
'allowed_origins' => array_filter(['http://localhost:4200', env('CONSOLE_HOST'), Utils::addWwwToUrl(env('CONSOLE_HOST'))]),
|
||||
|
||||
'allowed_origins_patterns' => [],
|
||||
|
||||
'allowed_headers' => ['*'],
|
||||
|
||||
'exposed_headers' => [],
|
||||
'exposed_headers' => ['x-compressed-json', 'access-console-sandbox', 'access-console-sandbox-key'],
|
||||
|
||||
'max_age' => 0,
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('FILESYSTEM_DRIVER', 'local'),
|
||||
'default' => env('FILESYSTEM_DRIVER', 'public'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -32,34 +32,34 @@ return [
|
||||
|
||||
'local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app'),
|
||||
'root' => storage_path('app')
|
||||
],
|
||||
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'url' => env('APP_URL').'/storage',
|
||||
'visibility' => 'public',
|
||||
],
|
||||
|
||||
'uploads' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/uploads'),
|
||||
'url' => env('APP_URL').'/uploads',
|
||||
'visibility' => 'public',
|
||||
'url' => env('APP_URL') . '/storage',
|
||||
],
|
||||
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'ap-southeast-1'),
|
||||
'bucket' => env('AWS_BUCKET'),
|
||||
'url' => env('AWS_URL'),
|
||||
'endpoint' => env('AWS_ENDPOINT'),
|
||||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
||||
],
|
||||
|
||||
'gcs' => [
|
||||
'driver' => 'gcs',
|
||||
'project_id' => env('GOOGLE_CLOUD_PROJECT_ID', 'your-project-id'),
|
||||
'key_file' => env('GOOGLE_CLOUD_KEY_FILE', null),
|
||||
'bucket' => env('GOOGLE_CLOUD_STORAGE_BUCKET', env('AWS_BUCKET')),
|
||||
'path_prefix' => env('GOOGLE_CLOUD_STORAGE_PATH_PREFIX', null),
|
||||
'storage_api_uri' => env('GOOGLE_CLOUD_STORAGE_API_URI', env('AWS_URL')),
|
||||
'visibility_handler' => \League\Flysystem\GoogleCloudStorage\UniformBucketLevelAccessVisibility::class,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
@@ -74,8 +74,7 @@ return [
|
||||
*/
|
||||
|
||||
'links' => [
|
||||
public_path('storage') => storage_path('app/public'),
|
||||
public_path('uploads') => storage_path('app/uploads'),
|
||||
public_path('storage') => storage_path('app/public')
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Monolog\Formatter\JsonFormatter;
|
||||
use Monolog\Handler\NullHandler;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\SyslogUdpHandler;
|
||||
@@ -89,12 +90,21 @@ return [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => StreamHandler::class,
|
||||
'formatter' => env('LOG_STDERR_FORMATTER'),
|
||||
'formatter' => JsonFormatter::class,
|
||||
'with' => [
|
||||
'stream' => 'php://stderr',
|
||||
],
|
||||
],
|
||||
|
||||
'stdout' => [
|
||||
'driver' => 'monolog',
|
||||
'handler' => StreamHandler::class,
|
||||
'formatter' => JsonFormatter::class,
|
||||
'with' => [
|
||||
'stream' => 'php://stdout',
|
||||
],
|
||||
],
|
||||
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
|
||||
@@ -50,7 +50,6 @@ return [
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION'),
|
||||
'version' => '2010-12-01'
|
||||
],
|
||||
|
||||
'mailgun' => [
|
||||
@@ -61,6 +60,10 @@ return [
|
||||
'transport' => 'postmark',
|
||||
],
|
||||
|
||||
'sendgrid' => [
|
||||
'transport' => 'sendgrid',
|
||||
],
|
||||
|
||||
'sendmail' => [
|
||||
'transport' => 'sendmail',
|
||||
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -t -i'),
|
||||
@@ -96,8 +99,8 @@ return [
|
||||
*/
|
||||
|
||||
'from' => [
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
||||
'name' => env('MAIL_FROM_NAME', 'Example'),
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@fleetbase.io'),
|
||||
'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Fleetbase')),
|
||||
],
|
||||
|
||||
/*
|
||||
|
||||
223
api/config/octane.php
Normal file
223
api/config/octane.php
Normal file
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
use Laravel\Octane\Contracts\OperationTerminated;
|
||||
use Laravel\Octane\Events\RequestHandled;
|
||||
use Laravel\Octane\Events\RequestReceived;
|
||||
use Laravel\Octane\Events\RequestTerminated;
|
||||
use Laravel\Octane\Events\TaskReceived;
|
||||
use Laravel\Octane\Events\TaskTerminated;
|
||||
use Laravel\Octane\Events\TickReceived;
|
||||
use Laravel\Octane\Events\TickTerminated;
|
||||
use Laravel\Octane\Events\WorkerErrorOccurred;
|
||||
use Laravel\Octane\Events\WorkerStarting;
|
||||
use Laravel\Octane\Events\WorkerStopping;
|
||||
use Laravel\Octane\Listeners\CollectGarbage;
|
||||
use Laravel\Octane\Listeners\DisconnectFromDatabases;
|
||||
use Laravel\Octane\Listeners\EnsureUploadedFilesAreValid;
|
||||
use Laravel\Octane\Listeners\EnsureUploadedFilesCanBeMoved;
|
||||
use Laravel\Octane\Listeners\FlushOnce;
|
||||
use Laravel\Octane\Listeners\FlushTemporaryContainerInstances;
|
||||
use Laravel\Octane\Listeners\FlushUploadedFiles;
|
||||
use Laravel\Octane\Listeners\ReportException;
|
||||
use Laravel\Octane\Listeners\StopWorkerIfNecessary;
|
||||
use Laravel\Octane\Octane;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Octane Server
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the default "server" that will be used by Octane
|
||||
| when starting, restarting, or stopping your server via the CLI. You
|
||||
| are free to change this to the supported server of your choosing.
|
||||
|
|
||||
| Supported: "roadrunner", "swoole", "frankenphp"
|
||||
|
|
||||
*/
|
||||
|
||||
'server' => env('OCTANE_SERVER', 'frankenphp'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Force HTTPS
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When this configuration value is set to "true", Octane will inform the
|
||||
| framework that all absolute links must be generated using the HTTPS
|
||||
| protocol. Otherwise your links may be generated using plain HTTP.
|
||||
|
|
||||
*/
|
||||
|
||||
'https' => env('OCTANE_HTTPS', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Octane Listeners
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| All of the event listeners for Octane's events are defined below. These
|
||||
| listeners are responsible for resetting your application's state for
|
||||
| the next request. You may even add your own listeners to the list.
|
||||
|
|
||||
*/
|
||||
|
||||
'listeners' => [
|
||||
WorkerStarting::class => [
|
||||
EnsureUploadedFilesAreValid::class,
|
||||
EnsureUploadedFilesCanBeMoved::class,
|
||||
],
|
||||
|
||||
RequestReceived::class => [
|
||||
...Octane::prepareApplicationForNextOperation(),
|
||||
...Octane::prepareApplicationForNextRequest(),
|
||||
//
|
||||
],
|
||||
|
||||
RequestHandled::class => [
|
||||
//
|
||||
],
|
||||
|
||||
RequestTerminated::class => [
|
||||
// FlushUploadedFiles::class,
|
||||
],
|
||||
|
||||
TaskReceived::class => [
|
||||
...Octane::prepareApplicationForNextOperation(),
|
||||
//
|
||||
],
|
||||
|
||||
TaskTerminated::class => [
|
||||
//
|
||||
],
|
||||
|
||||
TickReceived::class => [
|
||||
...Octane::prepareApplicationForNextOperation(),
|
||||
//
|
||||
],
|
||||
|
||||
TickTerminated::class => [
|
||||
//
|
||||
],
|
||||
|
||||
OperationTerminated::class => [
|
||||
FlushOnce::class,
|
||||
FlushTemporaryContainerInstances::class,
|
||||
// DisconnectFromDatabases::class,
|
||||
// CollectGarbage::class,
|
||||
],
|
||||
|
||||
WorkerErrorOccurred::class => [
|
||||
ReportException::class,
|
||||
StopWorkerIfNecessary::class,
|
||||
],
|
||||
|
||||
WorkerStopping::class => [
|
||||
//
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Warm / Flush Bindings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The bindings listed below will either be pre-warmed when a worker boots
|
||||
| or they will be flushed before every new request. Flushing a binding
|
||||
| will force the container to resolve that binding again when asked.
|
||||
|
|
||||
*/
|
||||
|
||||
'warm' => [
|
||||
...Octane::defaultServicesToWarm(),
|
||||
],
|
||||
|
||||
'flush' => [
|
||||
//
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Octane Swoole Tables
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| While using Swoole, you may define additional tables as required by the
|
||||
| application. These tables can be used to store data that needs to be
|
||||
| quickly accessed by other workers on the particular Swoole server.
|
||||
|
|
||||
*/
|
||||
|
||||
'tables' => [
|
||||
'example:1000' => [
|
||||
'name' => 'string:1000',
|
||||
'votes' => 'int',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Octane Swoole Cache Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| While using Swoole, you may leverage the Octane cache, which is powered
|
||||
| by a Swoole table. You may set the maximum number of rows as well as
|
||||
| the number of bytes per row using the configuration options below.
|
||||
|
|
||||
*/
|
||||
|
||||
'cache' => [
|
||||
'rows' => 1000,
|
||||
'bytes' => 10000,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| File Watching
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following list of files and directories will be watched when using
|
||||
| the --watch option offered by Octane. If any of the directories and
|
||||
| files are changed, Octane will automatically reload your workers.
|
||||
|
|
||||
*/
|
||||
|
||||
'watch' => [
|
||||
'app',
|
||||
'bootstrap',
|
||||
'config',
|
||||
'database',
|
||||
'public/**/*.php',
|
||||
'resources/**/*.php',
|
||||
'routes',
|
||||
'composer.lock',
|
||||
'.env',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Garbage Collection Threshold
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When executing long-lived PHP scripts such as Octane, memory can build
|
||||
| up before being cleared by PHP. You can force Octane to run garbage
|
||||
| collection if your application consumes this amount of megabytes.
|
||||
|
|
||||
*/
|
||||
|
||||
'garbage' => 50,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maximum Execution Time
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following setting configures the maximum execution time for requests
|
||||
| being handled by Octane. You may set this value to 0 to indicate that
|
||||
| there isn't a specific time limit on Octane request execution time.
|
||||
|
|
||||
*/
|
||||
|
||||
'max_execution_time' => 30,
|
||||
|
||||
];
|
||||
@@ -13,7 +13,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('QUEUE_CONNECTION', 'sync'),
|
||||
'default' => env('QUEUE_CONNECTION', 'redis'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -30,4 +30,8 @@ return [
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
],
|
||||
|
||||
'sendgrid' => [
|
||||
'api_key' => env('SENDGRID_API_KEY'),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
25
api/deploy.sh
Executable file
25
api/deploy.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Exit the script as soon as a command fails
|
||||
set -e
|
||||
|
||||
# Create mysql databases if none exists
|
||||
php artisan mysql:createdb
|
||||
|
||||
# Run migrations
|
||||
php artisan migrate --force
|
||||
|
||||
# Run migrations for sandbox too
|
||||
php artisan sandbox:migrate --force
|
||||
|
||||
# Seed database
|
||||
php artisan fleetbase:seed
|
||||
|
||||
# Restart queue
|
||||
php artisan queue:restart
|
||||
|
||||
# Sync scheduler
|
||||
php artisan schedule-monitor:sync
|
||||
|
||||
# Clear cache
|
||||
php artisan cache:clear
|
||||
5622
api/pnpm-lock.yaml
generated
Normal file
5622
api/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
/var/www/html/api/storage/app/uploads
|
||||
1
console
1
console
Submodule console deleted from 008abd9a25
25
console/.dockerignore
Normal file
25
console/.dockerignore
Normal file
@@ -0,0 +1,25 @@
|
||||
# Ignore files not needed for docker to build microservice image
|
||||
node_modules/*
|
||||
postgres/*
|
||||
typings/*
|
||||
*/.git*
|
||||
*.git*
|
||||
*.git
|
||||
*.md
|
||||
!gulpfile.js
|
||||
ci/
|
||||
.circleci/
|
||||
Vagrantfile
|
||||
concourse/
|
||||
infra/*
|
||||
vagrant/*
|
||||
docker/*
|
||||
deploy/*
|
||||
media/*
|
||||
data/*
|
||||
appspec.yml
|
||||
compose.yml
|
||||
docker-compose-prod.yml
|
||||
docker-compose.yml
|
||||
$virtualenv.tar.gz
|
||||
$node_modules.tar.gz
|
||||
19
console/.editorconfig
Normal file
19
console/.editorconfig
Normal file
@@ -0,0 +1,19 @@
|
||||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.hbs]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.{diff,md}]
|
||||
trim_trailing_whitespace = false
|
||||
7
console/.ember-cli
Normal file
7
console/.ember-cli
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
/**
|
||||
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
|
||||
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
|
||||
*/
|
||||
"isTypeScriptProject": false
|
||||
}
|
||||
13
console/.eslintignore
Normal file
13
console/.eslintignore
Normal file
@@ -0,0 +1,13 @@
|
||||
# unconventional js
|
||||
/blueprints/*/files/
|
||||
|
||||
# compiled output
|
||||
/dist/
|
||||
|
||||
# misc
|
||||
/coverage/
|
||||
!.*
|
||||
.*/
|
||||
|
||||
# ember-try
|
||||
/.node_modules.ember-try/
|
||||
77
console/.eslintrc.js
Normal file
77
console/.eslintrc.js
Normal file
@@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@babel/eslint-parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
requireConfigFile: false,
|
||||
babelOptions: {
|
||||
plugins: [['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }]],
|
||||
},
|
||||
},
|
||||
plugins: ['ember'],
|
||||
extends: ['eslint:recommended', 'plugin:ember/recommended', 'plugin:prettier/recommended'],
|
||||
ignorePatterns: ['prebuild.js'],
|
||||
env: {
|
||||
browser: true,
|
||||
},
|
||||
globals: {
|
||||
socketClusterClient: 'readonly',
|
||||
L: 'readonly',
|
||||
module: 'readonly',
|
||||
require: 'readonly',
|
||||
},
|
||||
rules: {
|
||||
'ember/no-array-prototype-extensions': 'off',
|
||||
'ember/no-computed-properties-in-native-classes': 'off',
|
||||
'ember/no-controller-access-in-routes': 'off',
|
||||
'ember/no-empty-glimmer-component-classes': 'off',
|
||||
'ember/no-get': 'off',
|
||||
'ember/classic-decorator-no-classic-methods': 'off',
|
||||
'n/no-unpublished-require': [
|
||||
'error',
|
||||
{
|
||||
allowModules: [
|
||||
'ember-cli',
|
||||
'tailwindcss',
|
||||
'@tailwindcss/forms',
|
||||
'postcss-import',
|
||||
'postcss-preset-env',
|
||||
'postcss-each',
|
||||
'postcss-mixins',
|
||||
'postcss-conditionals-renewed',
|
||||
'autoprefixer',
|
||||
'broccoli-funnel',
|
||||
'ember-auto-import',
|
||||
],
|
||||
},
|
||||
],
|
||||
'no-prototype-builtins': 'off',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'./.eslintrc.js',
|
||||
'./.prettierrc.js',
|
||||
'./.stylelintrc.js',
|
||||
'./.template-lintrc.js',
|
||||
'./ember-cli-build.js',
|
||||
'./testem.js',
|
||||
'./blueprints/*/index.js',
|
||||
'./config/**/*.js',
|
||||
'./lib/*/index.js',
|
||||
'./server/**/*.js',
|
||||
],
|
||||
parserOptions: {
|
||||
sourceType: 'script',
|
||||
},
|
||||
env: {
|
||||
browser: false,
|
||||
node: true,
|
||||
},
|
||||
extends: ['plugin:n/recommended'],
|
||||
},
|
||||
],
|
||||
};
|
||||
60
console/.github/workflows/ci.yml
vendored
Normal file
60
console/.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Ember CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x] # Build on Node.js 18
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2.0.1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Get pnpm Store Directory
|
||||
id: pnpm-cache
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v3
|
||||
name: Setup pnpm Cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Create .npmrc
|
||||
run: |
|
||||
if [[ -n "${{ secrets._GITHUB_AUTH_TOKEN }}" ]]; then
|
||||
echo "@fleetbase:registry=https://npm.pkg.github.com/" > .npmrc
|
||||
echo "//npm.pkg.github.com/:_authToken=${{ secrets._GITHUB_AUTH_TOKEN }}" >> .npmrc
|
||||
fi
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
|
||||
- name: Build
|
||||
run: npx ember build --environment production
|
||||
28
console/.gitignore
vendored
Normal file
28
console/.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# compiled output
|
||||
/dist/
|
||||
/declarations/
|
||||
|
||||
# dependencies
|
||||
/node_modules/
|
||||
/scripts/node_modules/
|
||||
|
||||
# misc
|
||||
/.env*
|
||||
/environments/.env*
|
||||
/.pnp*
|
||||
/.eslintcache
|
||||
/coverage/
|
||||
/npm-debug.log*
|
||||
/testem.log
|
||||
/yarn-error.log
|
||||
/.npmrc
|
||||
|
||||
# ember-try
|
||||
/.node_modules.ember-try/
|
||||
/npm-shrinkwrap.json.ember-try
|
||||
/package.json.ember-try
|
||||
/package-lock.json.ember-try
|
||||
/yarn.lock.ember-try
|
||||
|
||||
# broccoli-debug
|
||||
/DEBUG/
|
||||
13
console/.prettierignore
Normal file
13
console/.prettierignore
Normal file
@@ -0,0 +1,13 @@
|
||||
# unconventional js
|
||||
/blueprints/*/files/
|
||||
|
||||
# compiled output
|
||||
/dist/
|
||||
|
||||
# misc
|
||||
/coverage/
|
||||
!.*
|
||||
.*/
|
||||
|
||||
# ember-try
|
||||
/.node_modules.ember-try/
|
||||
17
console/.prettierrc.js
Normal file
17
console/.prettierrc.js
Normal file
@@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
trailingComma: 'es5',
|
||||
tabWidth: 4,
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
printWidth: 190,
|
||||
overrides: [
|
||||
{
|
||||
files: '*.{hbs,js,ts}',
|
||||
options: {
|
||||
singleQuote: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
8
console/.stylelintignore
Normal file
8
console/.stylelintignore
Normal file
@@ -0,0 +1,8 @@
|
||||
# unconventional files
|
||||
/blueprints/*/files/
|
||||
|
||||
# compiled output
|
||||
/dist/
|
||||
|
||||
# addons
|
||||
/.node_modules.ember-try/
|
||||
8
console/.stylelintrc.js
Normal file
8
console/.stylelintrc.js
Normal file
@@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
extends: ['stylelint-config-standard', 'stylelint-prettier/recommended'],
|
||||
rules: {
|
||||
'import-notation': null,
|
||||
},
|
||||
};
|
||||
13
console/.template-lintrc.js
Normal file
13
console/.template-lintrc.js
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
extends: 'recommended',
|
||||
rules: {
|
||||
'no-bare-strings': 'off',
|
||||
'no-invalid-interactive': 'off',
|
||||
'no-yield-only': 'off',
|
||||
'table-groups': 'off',
|
||||
'link-href-attributes': 'off',
|
||||
'require-input-label': 'off',
|
||||
},
|
||||
};
|
||||
3
console/.watchmanconfig
Normal file
3
console/.watchmanconfig
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ignore_dirs": ["dist"]
|
||||
}
|
||||
54
console/Dockerfile
Normal file
54
console/Dockerfile
Normal file
@@ -0,0 +1,54 @@
|
||||
# ---- Build Stage ----
|
||||
FROM node:18.15.0-alpine AS builder
|
||||
|
||||
# Set the working directory in the container to /app
|
||||
WORKDIR /app
|
||||
|
||||
# Create the pnpm directory and set the PNPM_HOME environment variable
|
||||
RUN mkdir -p ~/.pnpm
|
||||
ENV PNPM_HOME /root/.pnpm
|
||||
|
||||
# Set environment
|
||||
ARG ENVIRONMENT=production
|
||||
|
||||
# Add the pnpm global bin to the PATH
|
||||
ENV PATH /root/.pnpm/bin:$PATH
|
||||
|
||||
# Copy pnpm-lock.yaml (or package.json) into the directory /app in the container
|
||||
COPY console/package.json console/pnpm-lock.yaml ./
|
||||
|
||||
# Copy over .npmrc if applicable
|
||||
COPY console/.npmr[c] ./
|
||||
|
||||
# Install global dependencies
|
||||
RUN npm install -g ember-cli pnpm
|
||||
|
||||
# Install git
|
||||
RUN apk update && apk add git openssh-client
|
||||
|
||||
# Trust GitHub's RSA host key
|
||||
RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
|
||||
|
||||
# Install app dependencies
|
||||
RUN pnpm install
|
||||
|
||||
# Copy the console directory contents into the container at /app
|
||||
COPY console .
|
||||
|
||||
# Build the application
|
||||
RUN pnpm build --environment $ENVIRONMENT
|
||||
|
||||
# ---- Serve Stage ----
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copy the built app to our served directory
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# Expose the port nginx is bound to
|
||||
EXPOSE 4200
|
||||
|
||||
# Use custom nginx.conf
|
||||
COPY console/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Start Nginx server
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
22
console/README.md
Normal file
22
console/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
<div id="hero">
|
||||
<p align="center" dir="auto">
|
||||
<a href="https://fleetbase.io" rel="nofollow">
|
||||
<img src="https://user-images.githubusercontent.com/58805033/191936702-fed04b0f-7966-4041-96d0-95e27bf98248.png" alt="Fleetbase logo" width="600" height="140" style="max-width: 100%;">
|
||||
</a>
|
||||
</p>
|
||||
<h2 align="center" dir="auto">
|
||||
Fleetbase Console
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p align="center" dir="auto">
|
||||
<img src="https://user-images.githubusercontent.com/816371/246987420-deef79fa-e30c-4ce9-8a04-0dee990ffd9d.png" alt="Fleetbase Console" width="600" style="max-width: 100%;" />
|
||||
</p>
|
||||
|
||||
# ⭐️ Overview
|
||||
|
||||
<p>The Fleetbase Console, a vital component of the Fleetbase Platform, serves as the graphical user interface that facilitates interaction with all Fleetbase extensions. It's architecturally designed to be lightweight, primarily employing Ember Engines for its operational foundation. Ember Engines, a scalable development paradigm for ambitious web applications, allow the Console to dynamically load and manage extensions, ensuring a streamlined and efficient user experience.</p>
|
||||
|
||||
<p>The responsibilities of the Console are kept minimalistic to maintain its performance and agility. The Console's key responsibilities encompass administration management, configuration of organization and user settings, and establishing a robust connection to the Fleetbase extensions repository. In its administrative capacity, it provides an intuitive interface for configuring the Fleetbas instance.</p>
|
||||
|
||||
<p>Lastly, the Console plays a pivotal role in connecting to the Fleetbase extensions repository. This provides the flexibility to add, update, or remove extensions as per the organizational needs and preferences, all while ensuring the seamless operation and integrity of the Fleetbase Platform.</p>
|
||||
12
console/app.yaml
Normal file
12
console/app.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
runtime: python312
|
||||
|
||||
handlers:
|
||||
- url: /(.*\..+)$
|
||||
static_files: \1
|
||||
upload: (.+)
|
||||
secure: always
|
||||
expiration: 1h
|
||||
- url: /.*
|
||||
static_files: index.html
|
||||
upload: index.html
|
||||
secure: always
|
||||
1
console/app/adapters/application.js
Normal file
1
console/app/adapters/application.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '@fleetbase/ember-core/adapters/application';
|
||||
15
console/app/adapters/brand.js
Normal file
15
console/app/adapters/brand.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import ApplicationAdapter from './application';
|
||||
|
||||
export default class BrandAdapter extends ApplicationAdapter {
|
||||
urlForFindRecord() {
|
||||
return `${this.host}/${this.namespace}/settings/branding`;
|
||||
}
|
||||
|
||||
urlForUpdateRecord() {
|
||||
return `${this.host}/${this.namespace}/settings/branding`;
|
||||
}
|
||||
|
||||
urlForCreateRecord() {
|
||||
return `${this.host}/${this.namespace}/settings/branding`;
|
||||
}
|
||||
}
|
||||
23
console/app/app.js
Normal file
23
console/app/app.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import Application from '@ember/application';
|
||||
import Resolver from 'ember-resolver';
|
||||
import loadInitializers from 'ember-load-initializers';
|
||||
import config from '@fleetbase/console/config/environment';
|
||||
import loadExtensions from '@fleetbase/ember-core/utils/load-extensions';
|
||||
import mapEngines from '@fleetbase/ember-core/utils/map-engines';
|
||||
|
||||
export default class App extends Application {
|
||||
modulePrefix = config.modulePrefix;
|
||||
podModulePrefix = config.podModulePrefix;
|
||||
Resolver = Resolver;
|
||||
extensions = [];
|
||||
engines = {};
|
||||
|
||||
async ready() {
|
||||
const extensions = await loadExtensions();
|
||||
|
||||
this.extensions = extensions;
|
||||
this.engines = mapEngines(extensions);
|
||||
}
|
||||
}
|
||||
|
||||
loadInitializers(App, config.modulePrefix);
|
||||
0
console/app/components/.gitkeep
Normal file
0
console/app/components/.gitkeep
Normal file
1
console/app/components/configure/2fa.hbs
Normal file
1
console/app/components/configure/2fa.hbs
Normal file
@@ -0,0 +1 @@
|
||||
{{yield}}
|
||||
3
console/app/components/configure/2fa.js
Normal file
3
console/app/components/configure/2fa.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
export default class Configure2faComponent extends Component {}
|
||||
46
console/app/components/configure/filesystem.hbs
Normal file
46
console/app/components/configure/filesystem.hbs
Normal file
@@ -0,0 +1,46 @@
|
||||
<ContentPanel @title="Filesystem" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name="Driver" @helpText="Select the default filesystem driver for Fleetbase to use.">
|
||||
<Select @options={{this.disks}} @value={{this.driver}} @onSelect={{this.setDriver}} @placeholder="Select filesystem driver" class="w-full" disabled={{this.isLoading}} />
|
||||
</InputGroup>
|
||||
{{#if (eq this.driver "s3")}}
|
||||
<InputGroup @name="S3 Bucket" @value={{this.s3Bucket}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="S3 URL" @value={{this.s3Url}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="S3 Endpoint" @value={{this.s3Endpoint}} disabled={{this.isLoading}} />
|
||||
{{/if}}
|
||||
{{#if (eq this.driver "gcs")}}
|
||||
{{#if this.isGoogleCloudStorageEnvConfigued}}
|
||||
<div class="border border-yellow-900 shadow-sm rounded-lg bg-yellow-200 mb-4">
|
||||
<div class="px-3 py-2 text-sm text-yellow-900 flex items-center">
|
||||
<FaIcon @icon="triangle-exclamation" @size="md" class="mr-1.5" />
|
||||
Warning! GCS is already configured in the server environment. Changing values below may break this.
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<InputGroup @name="GCS Bucket" @value={{this.gcsBucket}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="GCS Service Account Key File" @wrapperClass="">
|
||||
<div class="flex flex-row items-center mb-0i">
|
||||
<UploadButton @name="firebase-service-account" @accept="text/plain,text/javascript,application/json" @onFileAdded={{this.uploadGcsCredentialsFile}} @buttonText="Upload Service Account JSON" @icon="upload" class="w-auto m-0i mt-0i" />
|
||||
{{#if this.gcsCredentialsFile}}
|
||||
<div class="ml-2.5 text-sm dark:text-white text-black flex flex-row items-center border border-blue-500 rounded-lg px-2 py-0.5 -mt-1">
|
||||
<FaIcon @icon="file-text" @size="sm" class="mr-2 dark:text-white text-black" />
|
||||
<span>{{this.gcsCredentialsFile.original_filename}}</span>
|
||||
<a href="javascript:;" class="text-red-500 ml-2" {{on "click" this.removeGcsCredentialsFile}}>
|
||||
<FaIcon @icon="times" class="text-red-500" />
|
||||
</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</InputGroup>
|
||||
{{/if}}
|
||||
{{#if this.testResponse}}
|
||||
<div class="animate-pulse flex flex-row items-center rounded-lg border {{if (eq this.testResponse.status 'error') 'border-red-900 bg-red-800 text-red-100' 'border-green-900 bg-green-800 text-green-100'}} shadow-sm my-2 px-4 py-2">
|
||||
<FaIcon @icon={{if (eq this.testResponse.status 'error') 'triangle-exclamation' 'circle-check'}} class="mr-1.5 {{if (eq this.testResponse.status 'error') 'text-red-200' 'text-green-200'}}" />
|
||||
<span class="text-xs">{{this.this.testResponse.message}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<Button @wrapperClass="mt-3" @icon="plug" @text="Test Config" @onClick={{this.test}} @isLoading={{this.isLoading}} />
|
||||
</ContentPanel>
|
||||
|
||||
<EmberWormhole @to="next-view-section-subheader-actions">
|
||||
<Button @type="primary" @size="sm" @icon="save" @text="Save Changes" @onClick={{this.save}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
</EmberWormhole>
|
||||
119
console/app/components/configure/filesystem.js
Normal file
119
console/app/components/configure/filesystem.js
Normal file
@@ -0,0 +1,119 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class ConfigureFilesystemComponent extends Component {
|
||||
@service fetch;
|
||||
@service notifications;
|
||||
@service currentUser;
|
||||
@tracked isLoading = false;
|
||||
@tracked testResponse;
|
||||
@tracked disks = [];
|
||||
@tracked driver = 'local';
|
||||
@tracked s3Bucket = null;
|
||||
@tracked s3Url = null;
|
||||
@tracked s3Endpoint = null;
|
||||
@tracked gcsBucket = null;
|
||||
@tracked gcsCredentialsFileId = null;
|
||||
@tracked gcsCredentialsFile = null;
|
||||
@tracked isGoogleCloudStorageEnvConfigued = false;
|
||||
|
||||
/**
|
||||
* Creates an instance of ConfigureFilesystemComponent.
|
||||
* @memberof ConfigureFilesystemComponent
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadConfigValues();
|
||||
}
|
||||
|
||||
@action setConfigValues(config) {
|
||||
for (const key in config) {
|
||||
if (this[key] !== undefined) {
|
||||
this[key] = config[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@action setDriver(driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
@action loadConfigValues() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.get('settings/filesystem-config')
|
||||
.then((response) => {
|
||||
this.setConfigValues(response);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action save() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/filesystem-config', {
|
||||
driver: this.driver,
|
||||
s3: {
|
||||
bucket: this.s3Bucket,
|
||||
url: this.s3Url,
|
||||
endpoint: this.s3Endpoint,
|
||||
},
|
||||
gcsCredentialsFileId: this.gcsCredentialsFileId,
|
||||
gcsBucket: this.gcsBucket,
|
||||
})
|
||||
.then(() => {
|
||||
this.notifications.success('Filesystem configuration saved.');
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action test() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/test-filesystem-config', {
|
||||
disk: this.driver,
|
||||
})
|
||||
.then((response) => {
|
||||
this.testResponse = response;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action removeGcsCredentialsFile() {
|
||||
this.gcsCredentialsFileId = undefined;
|
||||
this.gcsCredentialsFile = undefined;
|
||||
}
|
||||
|
||||
@action uploadGcsCredentialsFile(file) {
|
||||
try {
|
||||
this.fetch.uploadFile.perform(
|
||||
file,
|
||||
{
|
||||
path: 'gcs',
|
||||
subject_uuid: this.currentUser.companyId,
|
||||
subject_type: 'company',
|
||||
type: 'gcs_credentials',
|
||||
},
|
||||
(uploadedFile) => {
|
||||
console.log('uploadedFile', uploadedFile);
|
||||
this.gcsCredentialsFileId = uploadedFile.id;
|
||||
this.gcsCredentialsFile = uploadedFile;
|
||||
console.log('this.gcsCredentialsFile', this.gcsCredentialsFile);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
console/app/components/configure/mail.hbs
Normal file
27
console/app/components/configure/mail.hbs
Normal file
@@ -0,0 +1,27 @@
|
||||
<ContentPanel @title="Mail" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name="Mailer" @helpText="Select the default mailer for Fleetbase to use.">
|
||||
<Select @options={{this.mailers}} @value={{this.mailer}} @onSelect={{this.setMailer}} @placeholder="Select mailer" class="w-full" />
|
||||
</InputGroup>
|
||||
{{#if (eq this.mailer "smtp")}}
|
||||
<InputGroup @name="SMTP Host" @value={{this.smtpHost}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="SMTP Port" @type="number" @value={{this.smtpPort}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="SMTP Encryption" @value={{this.smtpEncryption}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="SMTP Username" @value={{this.smtpUsername}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="SMTP Password" @type="password" @value={{this.smtpPassword}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="SMTP Timeout" @value={{this.smtpTimeout}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="SMTP Auth Mode" @value={{this.smtpAuth_mode}} disabled={{this.isLoading}} />
|
||||
{{/if}}
|
||||
<InputGroup @name="From Address" @helpText="Input the email address for Fleetbase to send emails from." @value={{this.fromAddress}} @placeholder="From Address" disabled={{this.isLoading}} />
|
||||
<InputGroup @name="From Name" @helpText="Input the name for Fleetbase to send emails from." @value={{this.fromName}} @placeholder="From Name" disabled={{this.isLoading}} />
|
||||
{{#if this.testResponse}}
|
||||
<div class="animate-pulse flex flex-row items-center rounded-lg border {{if (eq this.testResponse.status 'error') 'border-red-900 bg-red-800 text-red-100' 'border-green-900 bg-green-800 text-green-100'}} shadow-sm my-2 px-4 py-2">
|
||||
<FaIcon @icon={{if (eq this.testResponse.status 'error') 'triangle-exclamation' 'circle-check'}} class="mr-1.5 {{if (eq this.testResponse.status 'error') 'text-red-200' 'text-green-200'}}" />
|
||||
<span class="text-xs">{{this.this.testResponse.message}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<Button @wrapperClass="mt-3" @icon="plug" @text="Test Config" @onClick={{this.test}} @isLoading={{this.isLoading}} />
|
||||
</ContentPanel>
|
||||
|
||||
<EmberWormhole @to="next-view-section-subheader-actions">
|
||||
<Button @type="primary" @size="sm" @icon="save" @text="Save Changes" @onClick={{this.save}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
</EmberWormhole>
|
||||
108
console/app/components/configure/mail.js
Normal file
108
console/app/components/configure/mail.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class ConfigureMailComponent extends Component {
|
||||
@service fetch;
|
||||
@service notifications;
|
||||
@tracked isLoading = false;
|
||||
@tracked testResponse;
|
||||
@tracked mailers = [];
|
||||
@tracked mailer = 'smtp';
|
||||
@tracked fromAddress = null;
|
||||
@tracked fromName = null;
|
||||
@tracked smtpHost = 'smtp.mailgun.org';
|
||||
@tracked smtpPort = 587;
|
||||
@tracked smtpEncryption = 'tls';
|
||||
@tracked smtpUsername = null;
|
||||
@tracked smtpPassword = null;
|
||||
@tracked smtpTimeout = null;
|
||||
@tracked smtpAuth_mode = null;
|
||||
|
||||
/**
|
||||
* Creates an instance of ConfigureFilesystemComponent.
|
||||
* @memberof ConfigureFilesystemComponent
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadConfigValues();
|
||||
}
|
||||
|
||||
@action setConfigValues(config) {
|
||||
for (const key in config) {
|
||||
if (this[key] !== undefined) {
|
||||
this[key] = config[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@action setMailer(mailer) {
|
||||
this.mailer = mailer;
|
||||
}
|
||||
|
||||
@action serializeSmtpConfig() {
|
||||
return {
|
||||
host: this.smtpHost,
|
||||
port: this.smtpPort,
|
||||
encryption: this.smtpEncryption,
|
||||
username: this.smtpUsername,
|
||||
password: this.smtpPassword,
|
||||
timeout: this.smtpTimeout,
|
||||
auth_mode: this.smtpAuth_mode,
|
||||
};
|
||||
}
|
||||
|
||||
@action loadConfigValues() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.get('settings/mail-config')
|
||||
.then((response) => {
|
||||
this.setConfigValues(response);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action test() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/test-mail-config', {
|
||||
mailer: this.mailer,
|
||||
from: {
|
||||
address: this.fromAddress,
|
||||
name: this.fromName,
|
||||
},
|
||||
smtp: this.serializeSmtpConfig(),
|
||||
})
|
||||
.then((response) => {
|
||||
this.testResponse = response;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action save() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/mail-config', {
|
||||
mailer: this.mailer,
|
||||
from: {
|
||||
address: this.fromAddress,
|
||||
name: this.fromName,
|
||||
},
|
||||
smtp: this.serializeSmtpConfig(),
|
||||
})
|
||||
.then(() => {
|
||||
this.notifications.success('Mail configuration saved.');
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
70
console/app/components/configure/notification-channels.hbs
Normal file
70
console/app/components/configure/notification-channels.hbs
Normal file
@@ -0,0 +1,70 @@
|
||||
<ContentPanel @title="APN Configutation" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name="APN Key ID" @value={{this.apn.key_id}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="APN Team ID" @value={{this.apn.team_id}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="APN App Bundle ID" @value={{this.apn.app_bundle_id}} disabled={{this.isLoading}} />
|
||||
{{!-- <InputGroup @name="APN Private Key" disabled={{this.isLoading}}>
|
||||
<Textarea class="form-input w-full" @value={{this.apn.private_key_content}} placeholder="APN Private Key" rows="10" disabled={{this.isLoading}} />
|
||||
</InputGroup> --}}
|
||||
<InputGroup @wrapperClass="flex flex-row items-center">
|
||||
<UploadButton @name="apn-key" @accept="text/plain,application/x-pem-file,application/x-pkcs12,application/x-x509-ca-cert,.p12,.pem,.p8" @onFileAdded={{this.uploadApnKey}} @buttonText="Upload P8 Key File" @icon="upload" class="w-auto m-0i mt-0i" />
|
||||
{{#if this.apn.private_key_file}}
|
||||
<div class="ml-2.5 text-sm dark:text-white text-black flex flex-row items-center border border-blue-500 rounded-lg px-2 py-0.5 -mt-1">
|
||||
<FaIcon @icon="file-text" @size="sm" class="mr-2 dark:text-white text-black" />
|
||||
<span>{{this.apn.private_key_file.original_filename}}</span>
|
||||
<a href="javascript:;" class="text-red-500 ml-2" {{on "click" this.removeApnFile}}><FaIcon @icon="times" class="text-red-500" /></a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</InputGroup>
|
||||
<InputGroup @wrapperClass="mb-0i">
|
||||
<Checkbox @label="APN Production" @value={{this.apn.production}} @onToggle={{fn (mut this.apn.production)}} @disabled={{this.isLoading}} />
|
||||
</InputGroup>
|
||||
</ContentPanel>
|
||||
|
||||
<ContentPanel @title="Firebase Configutation" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @wrapperClass="flex flex-row items-center mb-0i">
|
||||
<UploadButton @name="firebase-service-account" @accept="text/plain,text/javascript,application/json" @onFileAdded={{this.uploadFirebaseCredentials}} @buttonText="Upload Service Account JSON" @icon="upload" class="w-auto m-0i mt-0i" />
|
||||
{{#if this.firebase.credentials_file}}
|
||||
<div class="ml-2.5 text-sm dark:text-white text-black flex flex-row items-center border border-blue-500 rounded-lg px-2 py-0.5 -mt-1">
|
||||
<FaIcon @icon="file-text" @size="sm" class="mr-2 dark:text-white text-black" />
|
||||
<span>{{this.firebase.credentials_file.original_filename}}</span>
|
||||
<a href="javascript:;" class="text-red-500 ml-2" {{on "click" this.removeFirebaseCredentialsFile}}><FaIcon @icon="times" class="text-red-500" /></a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</InputGroup>
|
||||
</ContentPanel>
|
||||
|
||||
<ContentPanel @title="Test Push Notification" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-900">
|
||||
{{#if this.testResponse}}
|
||||
<div class="animate-pulse flex flex-row items-center rounded-lg border {{if (eq this.testResponse.status 'error') 'border-red-900 bg-red-800 text-red-100' 'border-green-900 bg-green-800 text-green-100'}} shadow-sm my-2 px-4 py-2">
|
||||
<FaIcon @icon={{if (eq this.testResponse.status 'error') 'triangle-exclamation' 'circle-check'}} class="mr-1.5 {{if (eq this.testResponse.status 'error') 'text-red-200' 'text-green-200'}}" />
|
||||
<span class="text-xs">{{this.this.testResponse.message}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<div class="flex flex-row items-center">
|
||||
<div class="text-sm w-40">Title:</div>
|
||||
<Input @value={{this.testTitle}} placeholder="Push Notification Title" class="form-input flex-1" />
|
||||
</div>
|
||||
<div class="flex flex-row items-center">
|
||||
<div class="text-sm w-40">Message:</div>
|
||||
<Input @value={{this.testMessage}} placeholder="Push Notification Message" class="form-input flex-1" />
|
||||
</div>
|
||||
<div class="flex flex-row items-center">
|
||||
<div class="text-sm w-40">APN Device Token:</div>
|
||||
<Input @value={{this.apnToken}} placeholder="APN Device Token" class="form-input flex-1" />
|
||||
</div>
|
||||
<div class="flex flex-row items-center">
|
||||
<div class="text-sm w-40">FCM Device Token:</div>
|
||||
<Input @value={{this.fcmToken}} placeholder="FCM Device Token" class="form-input flex-1" />
|
||||
</div>
|
||||
</div>
|
||||
<Button @wrapperClass="mt-3" @icon="plug" @text="Test Config" @onClick={{this.test}} @isLoading={{this.isLoading}} />
|
||||
</div>
|
||||
</ContentPanel>
|
||||
|
||||
<Spacer @height="300px" />
|
||||
|
||||
<EmberWormhole @to="next-view-section-subheader-actions">
|
||||
<Button @type="primary" @size="sm" @icon="save" @text="Save Changes" @onClick={{this.save}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
</EmberWormhole>
|
||||
165
console/app/components/configure/notification-channels.js
Normal file
165
console/app/components/configure/notification-channels.js
Normal file
@@ -0,0 +1,165 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class ConfigureNotificationChannelsComponent extends Component {
|
||||
@service fetch;
|
||||
@service notifications;
|
||||
@service currentUser;
|
||||
@tracked isLoading = false;
|
||||
@tracked testResponse;
|
||||
@tracked testTitle = 'Hello World from Fleetbase 🚀';
|
||||
@tracked testMessage = 'This is a test push notification!';
|
||||
@tracked apnToken;
|
||||
@tracked fcmToken;
|
||||
@tracked apn = {
|
||||
key_id: '',
|
||||
team_id: '',
|
||||
app_bundle_id: '',
|
||||
private_key_path: '',
|
||||
_private_key_path: '',
|
||||
private_key_file_id: '',
|
||||
private_key_file: null,
|
||||
production: true,
|
||||
};
|
||||
@tracked firebase = {
|
||||
credentials: '',
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadConfigValues();
|
||||
}
|
||||
|
||||
@action removeApnFile() {
|
||||
const apnConfig = this.apn;
|
||||
apnConfig.private_key_file = null;
|
||||
apnConfig.private_key_file_id = '';
|
||||
apnConfig.private_key_path = '';
|
||||
apnConfig._private_key_path = '';
|
||||
|
||||
this.apn = apnConfig;
|
||||
}
|
||||
|
||||
@action removeFirebaseCredentialsFile() {
|
||||
const firebaseConfig = this.firebase;
|
||||
firebaseConfig.credentials_file = null;
|
||||
firebaseConfig.credentials_file_id = '';
|
||||
firebaseConfig.credentials = '';
|
||||
|
||||
this.firebase = firebaseConfig;
|
||||
}
|
||||
|
||||
@action uploadApnKey(file) {
|
||||
try {
|
||||
this.fetch.uploadFile.perform(
|
||||
file,
|
||||
{
|
||||
path: 'apn',
|
||||
subject_uuid: this.currentUser.companyId,
|
||||
subject_type: 'company',
|
||||
type: 'apn_key',
|
||||
},
|
||||
(uploadedFile) => {
|
||||
const apnConfig = this.apn;
|
||||
apnConfig.private_key_file = uploadedFile;
|
||||
apnConfig.private_key_file_id = uploadedFile.id;
|
||||
apnConfig.private_key_path = uploadedFile.path;
|
||||
apnConfig._private_key_path = uploadedFile.path;
|
||||
|
||||
this.apn = apnConfig;
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@action uploadFirebaseCredentials(file) {
|
||||
try {
|
||||
this.fetch.uploadFile.perform(
|
||||
file,
|
||||
{
|
||||
path: 'firebase',
|
||||
subject_uuid: this.currentUser.companyId,
|
||||
subject_type: 'company',
|
||||
type: 'firebase_credentials',
|
||||
},
|
||||
(uploadedFile) => {
|
||||
const firebaseConfig = this.firebase;
|
||||
firebaseConfig.credentials_file = uploadedFile;
|
||||
firebaseConfig.credentials_file_id = uploadedFile.id;
|
||||
firebaseConfig.credentials_file_path = uploadedFile.path;
|
||||
|
||||
this.firebase = firebaseConfig;
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@action setConfigValues(config) {
|
||||
for (const key in config) {
|
||||
if (this[key] !== undefined) {
|
||||
this[key] = config[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@action loadConfigValues() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.get('settings/notification-channels-config')
|
||||
.then((response) => {
|
||||
this.setConfigValues(response);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action save() {
|
||||
this.isLoading = true;
|
||||
|
||||
const apnConfig = this.apn;
|
||||
delete apnConfig.private_key_file;
|
||||
|
||||
const firebaseConfig = this.firebase;
|
||||
delete firebaseConfig.credentials_file;
|
||||
|
||||
this.fetch
|
||||
.post('settings/notification-channels-config', {
|
||||
apn: apnConfig,
|
||||
firebase: firebaseConfig,
|
||||
})
|
||||
.then(() => {
|
||||
this.notifications.success("Notification channel's configuration saved.");
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action test() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/test-notification-channels-config', {
|
||||
apn: this.apn,
|
||||
firebase: this.firebase,
|
||||
title: this.testTitle,
|
||||
message: this.testMessage,
|
||||
apnToken: this.apnToken,
|
||||
fcmToken: this.fcmToken,
|
||||
})
|
||||
.then((response) => {
|
||||
this.testResponse = response;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
25
console/app/components/configure/queue.hbs
Normal file
25
console/app/components/configure/queue.hbs
Normal file
@@ -0,0 +1,25 @@
|
||||
<ContentPanel @title="Queue" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name="Driver" @helpText="Select the default queue driver for Fleetbase to use.">
|
||||
<Select @options={{this.connections}} @value={{this.driver}} @onSelect={{this.setDriver}} @placeholder="Select queue driver" disabled={{this.isLoading}} class="w-full" />
|
||||
</InputGroup>
|
||||
{{#if (eq this.driver "beanstalkd")}}
|
||||
<InputGroup @name="Beanstalk Host" @value={{this.beanstalkdHost}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="Beanstalk Queue" @value={{this.beanstalkdQueue}} />
|
||||
{{/if}}
|
||||
{{#if (eq this.driver "sqs")}}
|
||||
<InputGroup @name="SQS Prefix" @value={{this.sqsPrefix}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="SQS Queue" @value={{this.sqsQueue}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="SQS Suffix" @value={{this.sqsSuffix}} disabled={{this.isLoading}} />
|
||||
{{/if}}
|
||||
{{#if this.testResponse}}
|
||||
<div class="animate-pulse flex flex-row items-center rounded-lg border {{if (eq this.testResponse.status 'error') 'border-red-900 bg-red-800 text-red-100' 'border-green-900 bg-green-800 text-green-100'}} shadow-sm my-2 px-4 py-2">
|
||||
<FaIcon @icon={{if (eq this.testResponse.status 'error') 'triangle-exclamation' 'circle-check'}} class="mr-1.5 {{if (eq this.testResponse.status 'error') 'text-red-200' 'text-green-200'}}" />
|
||||
<span class="text-xs">{{this.this.testResponse.message}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<Button @wrapperClass="mt-3" @icon="plug" @text="Test Config" @onClick={{this.test}} @isLoading={{this.isLoading}} />
|
||||
</ContentPanel>
|
||||
|
||||
<EmberWormhole @to="next-view-section-subheader-actions">
|
||||
<Button @type="primary" @size="sm" @icon="save" @text="Save Changes" @onClick={{this.save}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
</EmberWormhole>
|
||||
91
console/app/components/configure/queue.js
Normal file
91
console/app/components/configure/queue.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class ConfigureQueueComponent extends Component {
|
||||
@service fetch;
|
||||
@service notifications;
|
||||
@tracked isLoading = false;
|
||||
@tracked testResponse;
|
||||
@tracked connections = [];
|
||||
@tracked driver = 'sync';
|
||||
@tracked sqsPrefix = null;
|
||||
@tracked sqsQueue = null;
|
||||
@tracked sqsSuffix = null;
|
||||
@tracked beanstalkdHost = 'localhost';
|
||||
@tracked beanstalkdQueue = 'default';
|
||||
|
||||
/**
|
||||
* Creates an instance of ConfigureQueueComponent.
|
||||
* @memberof ConfigureQueueComponent
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadConfigValues();
|
||||
}
|
||||
|
||||
@action setConfigValues(config) {
|
||||
for (const key in config) {
|
||||
if (this[key] !== undefined) {
|
||||
this[key] = config[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@action setDriver(driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
@action loadConfigValues() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.get('settings/queue-config')
|
||||
.then((response) => {
|
||||
this.setConfigValues(response);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action save() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/queue-config', {
|
||||
driver: this.driver,
|
||||
sqs: {
|
||||
prefix: this.sqsPrefix,
|
||||
queue: this.sqsQueue,
|
||||
suffix: this.sqsSuffix,
|
||||
},
|
||||
beanstalkd: {
|
||||
host: this.beanstalkdHost,
|
||||
queue: this.beanstalkdQueue,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.notifications.success('Queue configuration saved.');
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action test() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/test-queue-config', {
|
||||
queue: this.driver,
|
||||
})
|
||||
.then((response) => {
|
||||
this.testResponse = response;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
45
console/app/components/configure/services.hbs
Normal file
45
console/app/components/configure/services.hbs
Normal file
@@ -0,0 +1,45 @@
|
||||
<ContentPanel @title="AWS" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name="AWS Access Key" @value={{this.awsKey}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="AWS Access Secret" @value={{this.awsSecret}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="AWS Region" @value={{this.awsRegion}} disabled={{this.isLoading}} />
|
||||
</ContentPanel>
|
||||
|
||||
<ContentPanel @title="Google Maps" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name="Google Maps API Key" @value={{this.googleMapsApiKey}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="Google Maps Locale" @value={{this.googleMapsLocale}} disabled={{this.isLoading}} />
|
||||
</ContentPanel>
|
||||
|
||||
<ContentPanel @title="Twilio" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name="Twilio SID" @value={{this.twilioSid}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="Twilio Token" @value={{this.twilioToken}} disabled={{this.isLoading}} />
|
||||
<InputGroup @name="Twilio From" @value={{this.twilioFrom}} disabled={{this.isLoading}} />
|
||||
{{#if this.twilioTestResponse}}
|
||||
<div class="animate-pulse flex flex-row items-center rounded-lg border {{if (eq this.twilioTestResponse.status 'error') 'border-red-900 bg-red-800 text-red-100' 'border-green-900 bg-green-800 text-green-100'}} shadow-sm my-2 px-4 py-2">
|
||||
<FaIcon @icon={{if (eq this.twilioTestResponse.status 'error') 'triangle-exclamation' 'circle-check'}} class="mr-1.5 {{if (eq this.twilioTestResponse.status 'error') 'text-red-200' 'text-green-200'}}" />
|
||||
<span class="text-xs">{{this.this.twilioTestResponse.message}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="flex flex-row items-center mt-3">
|
||||
<Input @value={{this.twilioTestPhone}} @type="tel" placeholder="Send Test SMS Here" class="form-input form-input-sm" />
|
||||
<Button @wrapperClass="ml-2" @icon="plug" @text="Test Twilio Config" @onClick={{this.testTwilio}} @isLoading={{this.isLoading}} @disabled={{not this.twilioTestPhone}} />
|
||||
</div>
|
||||
</ContentPanel>
|
||||
|
||||
<ContentPanel @title="Sentry" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name="Sentry DSN" @value={{this.sentryDsn}} disabled={{this.isLoading}} />
|
||||
{{#if this.sentryTestResponse}}
|
||||
<div class="animate-pulse flex flex-row items-center rounded-lg border {{if (eq this.sentryTestResponse.status 'error') 'border-red-900 bg-red-800 text-red-100' 'border-green-900 bg-green-800 text-green-100'}} shadow-sm my-2 px-4 py-2">
|
||||
<FaIcon @icon={{if (eq this.sentryTestResponse.status 'error') 'triangle-exclamation' 'circle-check'}} class="mr-1.5 {{if (eq this.sentryTestResponse.status 'error') 'text-red-200' 'text-green-200'}}" />
|
||||
<span class="text-xs">{{this.this.sentryTestResponse.message}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<Button @wrapperClass="mt-3" @icon="plug" @text="Test Sentry Config" @onClick={{this.testSentry}} @isLoading={{this.isLoading}} />
|
||||
</ContentPanel>
|
||||
|
||||
<ContentPanel @title="IP Info" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name="IP Info API Key" @value={{this.ipinfoApiKey}} disabled={{this.isLoading}} />
|
||||
</ContentPanel>
|
||||
|
||||
<EmberWormhole @to="next-view-section-subheader-actions">
|
||||
<Button @type="primary" @size="sm" @icon="save" @text="Save Changes" @onClick={{this.save}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
</EmberWormhole>
|
||||
130
console/app/components/configure/services.js
Normal file
130
console/app/components/configure/services.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class ConfigureServicesComponent extends Component {
|
||||
@service fetch;
|
||||
@service notifications;
|
||||
@tracked isLoading = false;
|
||||
|
||||
/** aws service */
|
||||
@tracked awsKey = null;
|
||||
@tracked awsSecret = null;
|
||||
@tracked awsRegion = null;
|
||||
|
||||
/** ipinfo service */
|
||||
@tracked ipinfoApiKey = null;
|
||||
|
||||
/** google maps service */
|
||||
@tracked googleMapsApiKey = null;
|
||||
@tracked googleMapsLocale = 'us';
|
||||
|
||||
/** twilio service */
|
||||
@tracked twilioSid = null;
|
||||
@tracked twilioToken = null;
|
||||
@tracked twilioFrom = null;
|
||||
@tracked twilioTestPhone = null;
|
||||
@tracked twilioTestResponse;
|
||||
|
||||
/** sentry service */
|
||||
@tracked sentryDsn = null;
|
||||
@tracked sentryTestResponse;
|
||||
|
||||
/**
|
||||
* Creates an instance of ConfigureServicesComponent.
|
||||
* @memberof ConfigureServicesComponent
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadConfigValues();
|
||||
}
|
||||
|
||||
@action setConfigValues(config) {
|
||||
for (const key in config) {
|
||||
if (this[key] !== undefined) {
|
||||
this[key] = config[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@action loadConfigValues() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.get('settings/services-config')
|
||||
.then((response) => {
|
||||
this.setConfigValues(response);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action save() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/services-config', {
|
||||
aws: {
|
||||
key: this.awsKey,
|
||||
secret: this.awsSecret,
|
||||
region: this.awsRegion,
|
||||
},
|
||||
ipinfo: {
|
||||
api_key: this.ipinfoApiKey,
|
||||
},
|
||||
googleMaps: {
|
||||
api_key: this.googleMapsApiKey,
|
||||
locale: this.googleMapsLocale,
|
||||
},
|
||||
twilio: {
|
||||
sid: this.twilioSid,
|
||||
token: this.twilioToken,
|
||||
from: this.twilioFrom,
|
||||
},
|
||||
sentry: {
|
||||
dsn: this.sentryDsn,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.notifications.success('Services configuration saved.');
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action testTwilio() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/test-twilio-config', {
|
||||
sid: this.twilioSid,
|
||||
token: this.twilioToken,
|
||||
from: this.twilioFrom,
|
||||
phone: this.twilioTestPhone,
|
||||
})
|
||||
.then((response) => {
|
||||
this.twilioTestResponse = response;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@action testSentry() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/test-sentry-config', {
|
||||
dsn: this.sentryDsn,
|
||||
})
|
||||
.then((response) => {
|
||||
this.sentryTestResponse = response;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
46
console/app/components/configure/socket.hbs
Normal file
46
console/app/components/configure/socket.hbs
Normal file
@@ -0,0 +1,46 @@
|
||||
<ContentPanel @title="SocketCluster Connection" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<p class="mb-4">The SocketCluster configuration cannot be changed at this time.</p>
|
||||
<div id="output" class="font-mono rounded-lg max-h-full px-6 py-4 overflow-y-scroll bg-black shadow-inner dark:shadow-none">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="block w-1/2 font-mono text-sm font-semibold text-green-300">
|
||||
Output
|
||||
</div>
|
||||
{{#if this.events.length}}
|
||||
<div class="flex items-center justify-end w-1/2 text-sm text-green-300">
|
||||
<Spinner @iconClass="green-loader" />
|
||||
|
||||
<span class="ml-2 text-sm font-mono">
|
||||
Awaiting events...
|
||||
</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{#each this.events as |event|}}
|
||||
<div class="mb-3 text-sm font-mono rounded-lg shadow-sm flex flex-row">
|
||||
<div class="text-{{event.color}}-300 uppercase text-sm font-mono pr-2 flex-shrink-0">
|
||||
[{{event.time}}]:
|
||||
</div>
|
||||
<div class="text-{{event.color}}-300 text-sm font-mono">
|
||||
{{html-safe event.content}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="flex items-center mt-6 text-green-300">
|
||||
<Spinner @iconClass="green-loader" />
|
||||
<span class="ml-2 text-sm font-mono">
|
||||
Awaiting events...
|
||||
</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{#if this.testResponse}}
|
||||
<div class="animate-pulse flex flex-row items-center rounded-lg border {{if (eq this.testResponse.status 'error') 'border-red-900 bg-red-800 text-red-100' 'border-green-900 bg-green-800 text-green-100'}} shadow-sm my-2 px-4 py-2">
|
||||
<FaIcon @icon={{if (eq this.testResponse.status 'error') 'triangle-exclamation' 'circle-check'}} class="mr-1.5 {{if (eq this.testResponse.status 'error') 'text-red-200' 'text-green-200'}}" />
|
||||
<span class="text-xs">{{this.this.testResponse.message}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<Button @wrapperClass="mt-3" @icon="plug" @text="Test Socket Connection" @onClick={{this.testSocketConnection}} @isLoading={{this.isLoading}} />
|
||||
</ContentPanel>
|
||||
165
console/app/components/configure/socket.js
Normal file
165
console/app/components/configure/socket.js
Normal file
@@ -0,0 +1,165 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
export default class ConfigureSocketComponent extends Component {
|
||||
/**
|
||||
* Inject the `router` service
|
||||
*
|
||||
* @var {Service}
|
||||
* @memberof ConfigureSocketComponent
|
||||
*/
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* Inject the `fetch` service
|
||||
*
|
||||
* @var {Service}
|
||||
* @memberof ConfigureSocketComponent
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Inject the `notifications` service
|
||||
*
|
||||
* @var {Service}
|
||||
* @memberof ConfigureSocketComponent
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* Inject the `socket` service
|
||||
*
|
||||
* @var {Service}
|
||||
* @memberof ConfigureSocketComponent
|
||||
*/
|
||||
@service socket;
|
||||
|
||||
/**
|
||||
* State of the test request.
|
||||
*
|
||||
* @memberof ConfigureSocketComponent
|
||||
*/
|
||||
@tracked isLoading = null;
|
||||
|
||||
/**
|
||||
* The response form testing the socket.
|
||||
*
|
||||
* @memberof ConfigureSocketComponent
|
||||
*/
|
||||
@tracked testResponse = null;
|
||||
|
||||
/**
|
||||
* Incoming events logged from test socket channel.
|
||||
*
|
||||
* @memberof ConfigureSocketComponent
|
||||
*/
|
||||
@tracked events = [];
|
||||
|
||||
/**
|
||||
* Date format to use for socket console events.
|
||||
*
|
||||
* @memberof ConfigureSocketComponent
|
||||
*/
|
||||
consoleDateFormat = 'MMM-dd HH:mm';
|
||||
|
||||
/**
|
||||
* Creates an instance of ConfigureSocketComponent.
|
||||
* @memberof ConfigureSocketComponent
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.listenToTestSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a request to test the socket connection.
|
||||
*
|
||||
* @memberof ConfigureSocketComponent
|
||||
*/
|
||||
@action testSocketConnection() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/test-socket', {
|
||||
channel: 'test',
|
||||
})
|
||||
.then((response) => {
|
||||
this.testResponse = response;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens socket and logs all incoming events.
|
||||
*
|
||||
* @memberof ConfigureSocketComponent
|
||||
*/
|
||||
@action async listenToTestSocket() {
|
||||
// Create SocketClusterClient
|
||||
const socket = this.socket.instance();
|
||||
|
||||
// Listen for socket connection errors
|
||||
(async () => {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for await (let event of socket.listener('error')) {
|
||||
// Push an event or notification for socket connection here
|
||||
this.events.pushObject({
|
||||
time: format(new Date(), this.consoleDateFormat),
|
||||
content: 'Socket connection error!',
|
||||
color: 'red',
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
// Listen for socket connection
|
||||
(async () => {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for await (let event of socket.listener('connect')) {
|
||||
// Push an event or notification for socket connection here
|
||||
this.events.pushObject({
|
||||
time: format(new Date(), this.consoleDateFormat),
|
||||
content: 'Socket is connected',
|
||||
color: 'green',
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
// Listed on company channel
|
||||
const channel = socket.subscribe('test');
|
||||
|
||||
// Listen for channel subscription
|
||||
(async () => {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for await (let event of channel.listener('subscribe')) {
|
||||
// Push an event or notification for channel subscription here
|
||||
this.events.pushObject({
|
||||
time: format(new Date(), this.consoleDateFormat),
|
||||
content: `Socket subscribed to test channel`,
|
||||
color: 'blue',
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
// Listen for channel subscription
|
||||
(async () => {
|
||||
for await (let data of channel) {
|
||||
this.events.pushObject({
|
||||
time: format(new Date(), this.consoleDateFormat),
|
||||
content: JSON.stringify(data, undefined, 2),
|
||||
color: 'green',
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
// disconnect when transitioning
|
||||
this.router.on('routeWillChange', () => {
|
||||
channel.close();
|
||||
this.events = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
1
console/app/components/console-wormhole.hbs
Normal file
1
console/app/components/console-wormhole.hbs
Normal file
@@ -0,0 +1 @@
|
||||
<div id="console-wormhole" />
|
||||
99
console/app/components/dashboard.hbs
Normal file
99
console/app/components/dashboard.hbs
Normal file
@@ -0,0 +1,99 @@
|
||||
<div class="fleetbase-dashboard-grid flex items-center justify-between mb-4 mt-6 px-14">
|
||||
<div class="left-section">
|
||||
<h1 class="text-lg font-bold">{{this.dashboard.currentDashboard.name}}</h1>
|
||||
</div>
|
||||
<div class="fleetbase-dashboard-actions right-section ml-4 flex items-center">
|
||||
<div class="fleetbase-model-select fleetbase-power-select ember-model-select h-10">
|
||||
|
||||
<DropdownButton
|
||||
class="h-10"
|
||||
@text={{if this.dashboard.currentDashboard.name this.dashboard.currentDashboard.name (t "component.dashboard.select-dashboard")}}
|
||||
@textClass="text-sm mr-2"
|
||||
@buttonClass="flex-row-reverse w-44 justify-between"
|
||||
@icon="caret-down"
|
||||
@iconClass="mr-0i"
|
||||
@size="sm"
|
||||
@iconPrefix="fas"
|
||||
@triggerClass="hidden md:flex"
|
||||
as |dd|
|
||||
>
|
||||
<div class="next-dd-menu mt-1 mx-0" aria-labelledby="user-menu">
|
||||
<div class="p-1">
|
||||
{{#each this.dashboard.dashboards as |dashboard|}}
|
||||
<a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.selectDashboard dashboard)}}>
|
||||
<div class="flex-1 flex flex-row items-center">
|
||||
<div class="w-6">
|
||||
<FaIcon @icon="desktop" />
|
||||
</div>
|
||||
<span>{{dashboard.name}}</span>
|
||||
</div>
|
||||
<div>
|
||||
{{#if (eq this.dashboard.currentDashboard.id dashboard.id)}}
|
||||
<FaIcon @icon="check" class="text-green-500" />
|
||||
{{/if}}
|
||||
</div>
|
||||
</a>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</DropdownButton>
|
||||
</div>
|
||||
|
||||
<div class="ml-2 relative h-10">
|
||||
<DropdownButton class="h-10" @icon="ellipsis-h" @size="sm" @iconPrefix="fas" @triggerClass="hidden md:flex" as |dd|>
|
||||
<div class="next-dd-menu mt-1 mx-0" aria-labelledby="user-menu">
|
||||
<div class="p-1">
|
||||
<a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.createDashboard)}}>
|
||||
<div class="w-6">
|
||||
<FaIcon @icon="add" />
|
||||
</div>
|
||||
<span>{{t "component.dashboard.create-new-dashboard"}}</span>
|
||||
</a>
|
||||
|
||||
{{#unless (eq this.dashboard.currentDashboard.user_uuid "system")}}
|
||||
<a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.onChangeEdit true)}}>
|
||||
<div class="w-6">
|
||||
<FaIcon @icon="edit" />
|
||||
</div>
|
||||
<span>{{t "component.dashboard.edit-layout"}}</span>
|
||||
</a>
|
||||
<a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.onAddingWidget true)}}>
|
||||
<div class="w-6">
|
||||
<FaIcon @icon="add" />
|
||||
</div>
|
||||
<span>{{t "component.dashboard.add-widgets"}}</span>
|
||||
</a>
|
||||
|
||||
<a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.deleteDashboard this.dashboard.currentDashboard)}}>
|
||||
<div class="w-6">
|
||||
<FaIcon @icon="trash" />
|
||||
</div>
|
||||
<span>{{t "component.dashboard.delete-dashboard"}}</span>
|
||||
</a>
|
||||
{{/unless}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</DropdownButton>
|
||||
</div>
|
||||
{{#if this.dashboard.isEditingDashboard}}
|
||||
<div class="ml-2 h-10">
|
||||
<Button @type="magic" @icon="save" @helpText={{t "component.dashboard.save-dashboard"}} @onClick={{fn this.onChangeEdit false}} class="h-10" />
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-10">
|
||||
<Dashboard::Create @isEdit={{this.dashboard.isEditingDashboard}} @isAddingWidget={{this.dashboard.isAddingWidget}} @dashboard={{this.dashboard.currentDashboard}} />
|
||||
{{#if this.dashboard.isAddingWidget}}
|
||||
<EmberWormhole @to="console-home-wormhole">
|
||||
<Dashboard::WidgetPanel
|
||||
@isOpen={{this.dashboard.isAddingWidget}}
|
||||
@onLoad={{this.setWidgetSelectorPanelContext}}
|
||||
@dashboard={{this.dashboard.currentDashboard}}
|
||||
@onClose={{fn this.onAddingWidget false}}
|
||||
/>
|
||||
</EmberWormhole>
|
||||
{{/if}}
|
||||
</div>
|
||||
140
console/app/components/dashboard.js
Normal file
140
console/app/components/dashboard.js
Normal file
@@ -0,0 +1,140 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
/**
|
||||
* DashboardComponent for managing dashboards in an Ember application.
|
||||
* This component handles actions such as selecting, creating, deleting dashboards,
|
||||
* and managing widget selectors and dashboard editing states.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
export default class DashboardComponent extends Component {
|
||||
/**
|
||||
* Ember Data store service.
|
||||
* @type {Service}
|
||||
*/
|
||||
@service store;
|
||||
|
||||
/**
|
||||
* Internationalization service for managing translations.
|
||||
* @type {Service}
|
||||
*/
|
||||
@service intl;
|
||||
|
||||
/**
|
||||
* Notifications service for displaying alerts or confirmations.
|
||||
* @type {Service}
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* Modals manager service for handling modal dialogs.
|
||||
* @type {Service}
|
||||
*/
|
||||
@service modalsManager;
|
||||
|
||||
/**
|
||||
* Fetch service for handling HTTP requests.
|
||||
* @type {Service}
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Dashboard service for business logic related to dashboards.
|
||||
* @type {Service}
|
||||
*/
|
||||
@service dashboard;
|
||||
|
||||
/**
|
||||
* Creates an instance of DashboardComponent.
|
||||
* @memberof DashboardComponent
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.dashboard.loadDashboards.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to select a dashboard.
|
||||
* @param {Object} dashboard - The dashboard to be selected.
|
||||
*/
|
||||
@action selectDashboard(dashboard) {
|
||||
this.dashboard.selectDashboard.perform(dashboard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context for the widget selector panel.
|
||||
* @param {Object} widgetSelectorContext - The context object for the widget selector.
|
||||
*/
|
||||
@action setWidgetSelectorPanelContext(widgetSelectorContext) {
|
||||
this.widgetSelectorContext = widgetSelectorContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dashboard.
|
||||
* @param {Object} dashboard - The dashboard to be created.
|
||||
* @param {Object} [options={}] - Optional parameters for dashboard creation.
|
||||
*/
|
||||
@action createDashboard(dashboard, options = {}) {
|
||||
this.modalsManager.show('modals/create-dashboard', {
|
||||
title: this.intl.t('component.dashboard.create-a-new-dashboard'),
|
||||
acceptButtonText: this.intl.t('component.dashboard.confirm-create-dashboard'),
|
||||
confirm: async (modal, done) => {
|
||||
modal.startLoading();
|
||||
|
||||
// Get the name from the modal options
|
||||
const { name } = modal.getOptions();
|
||||
|
||||
await this.dashboard.createDashboard.perform(name);
|
||||
done();
|
||||
},
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a dashboard.
|
||||
* @param {Object} dashboard - The dashboard to be deleted.
|
||||
* @param {Object} [options={}] - Optional parameters for dashboard deletion.
|
||||
*/
|
||||
@action deleteDashboard(dashboard, options = {}) {
|
||||
if (this.dashboard.dashboards?.length === 1) {
|
||||
return this.notifications.error(this.intl.t('component.dashboard.you-cannot-delete-this-dashboard'));
|
||||
}
|
||||
|
||||
this.modalsManager.confirm({
|
||||
title: this.intl.t('component.dashboard.are-you-sure-you-want-delete-dashboard', { dashboardName: dashboard.name }),
|
||||
confirm: async (modal, done) => {
|
||||
modal.startLoading();
|
||||
await this.dashboard.deleteDashboard.perform(dashboard);
|
||||
done();
|
||||
},
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to handle the addition of a widget.
|
||||
* @param {boolean} [state=true] - The state to set for adding a widget.
|
||||
*/
|
||||
@action onAddingWidget(state = true) {
|
||||
this.dashboard.onAddingWidget(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current dashboard.
|
||||
* @param {Object} dashboard - The dashboard to be set as current.
|
||||
*/
|
||||
@action setCurrentDashboard(dashboard) {
|
||||
this.dashboard.setCurrentDashboard.perform(dashboard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the editing state of the dashboard.
|
||||
* @param {boolean} [state=true] - The state to set for editing the dashboard.
|
||||
*/
|
||||
@action onChangeEdit(state = true) {
|
||||
this.dashboard.onChangeEdit(state);
|
||||
}
|
||||
}
|
||||
6
console/app/components/dashboard/count.hbs
Normal file
6
console/app/components/dashboard/count.hbs
Normal file
@@ -0,0 +1,6 @@
|
||||
<div class="dashboard-component-count lg:col-span-2 h-full {{@options.wrapperClass}}">
|
||||
<h3 class="text-sm dark:text-gray-100 text-black mb-4 {{@options.titleClass}}">{{this.title}}</h3>
|
||||
<h1 class="text-3xl font-bold dark:text-gray-100 text-black {{@options.valueClass}}">
|
||||
{{this.value}}
|
||||
</h1>
|
||||
</div>
|
||||
72
console/app/components/dashboard/count.js
Normal file
72
console/app/components/dashboard/count.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import formatCurrency from '@fleetbase/ember-ui/utils/format-currency';
|
||||
import formatMeters from '@fleetbase/ember-ui/utils/format-meters';
|
||||
import formatBytes from '@fleetbase/ember-ui/utils/format-bytes';
|
||||
import formatDuration from '@fleetbase/ember-ui/utils/format-duration';
|
||||
import formatDate from '@fleetbase/ember-ui/utils/format-date';
|
||||
|
||||
export default class DashboardCountComponent extends Component {
|
||||
/**
|
||||
* The title of the metric count.
|
||||
*
|
||||
* @memberof WidgetKeyMetricsCountComponent
|
||||
*/
|
||||
@tracked title;
|
||||
|
||||
/**
|
||||
* The value to render
|
||||
*
|
||||
* @memberof WidgetKeyMetricsCountComponent
|
||||
*/
|
||||
@tracked value;
|
||||
|
||||
/**
|
||||
* Creates an instance of WidgetKeyMetricsCountComponent.
|
||||
* @param {EngineInstance} owner
|
||||
* @param {Object} { options }
|
||||
* @memberof WidgetKeyMetricsCountComponent
|
||||
*/
|
||||
constructor(owner, { options, title }) {
|
||||
super(...arguments);
|
||||
this.title = title;
|
||||
this.createRenderValueFromOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the value to render using the options provided.
|
||||
*
|
||||
* @param {Object} [options={}]
|
||||
* @memberof WidgetKeyMetricsCountComponent
|
||||
*/
|
||||
createRenderValueFromOptions(options = {}) {
|
||||
let { format, currency, dateFormat, value } = options;
|
||||
|
||||
switch (format) {
|
||||
case 'money':
|
||||
value = formatCurrency([value, currency]);
|
||||
break;
|
||||
|
||||
case 'meters':
|
||||
value = formatMeters([value]);
|
||||
break;
|
||||
|
||||
case 'bytes':
|
||||
value = formatBytes([value]);
|
||||
break;
|
||||
|
||||
case 'duration':
|
||||
value = formatDuration([value]);
|
||||
break;
|
||||
|
||||
case 'date':
|
||||
value = formatDate([value, dateFormat]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
14
console/app/components/dashboard/create.hbs
Normal file
14
console/app/components/dashboard/create.hbs
Normal file
@@ -0,0 +1,14 @@
|
||||
<div class="fleetbase-dashboard-grid" ...attributes>
|
||||
<GridStack @options={{this.gridOptions}} @onChange={{this.onChangeGrid}}>
|
||||
{{#each @dashboard.widgets as |widget|}}
|
||||
<GridStackItem id={{widget.id}} @options={{spread-widget-options (hash id=widget.id options=widget.grid_options)}} class="relative">
|
||||
{{component widget.component options=widget.options}}
|
||||
{{#if @isEdit}}
|
||||
<div class="absolute top-2 right-2">
|
||||
<Button @type="default" @icon="trash" @helpText={{"Remove widget from the dashboard"}} @onClick={{fn this.removeWidget widget}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
</GridStackItem>
|
||||
{{/each}}
|
||||
</GridStack>
|
||||
</div>
|
||||
99
console/app/components/dashboard/create.js
Normal file
99
console/app/components/dashboard/create.js
Normal file
@@ -0,0 +1,99 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action, computed } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
/**
|
||||
* Component responsible for creating and managing the dashboard layout.
|
||||
* Provides functionalities such as toggling widget float, changing grid layout, and removing widgets.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
export default class DashboardCreateComponent extends Component {
|
||||
/**
|
||||
* Notifications service for displaying alerts or errors.
|
||||
* @type {Service}
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* Tracked array to keep track of widgets that have been updated.
|
||||
* @type {Array}
|
||||
*/
|
||||
@tracked updatedWidgets = [];
|
||||
|
||||
/**
|
||||
* Action to toggle the floating state of widgets on the grid.
|
||||
*/
|
||||
@action toggleFloat() {
|
||||
this.shouldFloat = !this.shouldFloat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes to the grid layout, such as repositioning or resizing widgets.
|
||||
* Iterates over each widget event detail and updates the corresponding widget's properties if necessary.
|
||||
*
|
||||
* @param {Event} event - Event containing details about the grid change.
|
||||
* @action
|
||||
*/
|
||||
@action onChangeGrid(event) {
|
||||
const { dashboard } = this.args;
|
||||
|
||||
event.detail.forEach((currentWidgetEvent) => {
|
||||
const alreadyUpdated = this.updatedWidgets.find((item) => item.id === currentWidgetEvent.id);
|
||||
if (alreadyUpdated || !this.dashboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
const changedWidget = dashboard.widgets.find((widget) => widget.id === currentWidgetEvent.id);
|
||||
if (!changedWidget) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { x, y, w, h } = currentWidgetEvent;
|
||||
const response = changedWidget.updateProperties({
|
||||
grid_options: { x, y, w, h },
|
||||
});
|
||||
if (response) {
|
||||
this.updatedWidgets.push(changedWidget);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a specified widget from the dashboard.
|
||||
* Performs a removal operation on the dashboard and handles any errors that occur during the process.
|
||||
*
|
||||
* @param {Object} widget - The widget object to be removed.
|
||||
* @action
|
||||
*/
|
||||
@action removeWidget(widget) {
|
||||
const { dashboard } = this.args;
|
||||
|
||||
if (dashboard) {
|
||||
dashboard.removeWidget(widget.id).catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed property that returns grid options based on the current edit state.
|
||||
* Configures grid behavior such as floating, animation, and drag and resize capabilities.
|
||||
*
|
||||
* @computed
|
||||
* @returns {Object} An object containing grid configuration options.
|
||||
*/
|
||||
@computed('args.isEdit') get gridOptions() {
|
||||
return {
|
||||
float: true,
|
||||
animate: true,
|
||||
acceptWidgets: true,
|
||||
alwaysShowResizeHandle: this.args.isEdit,
|
||||
disableDrag: !this.args.isEdit,
|
||||
disableResize: !this.args.isEdit,
|
||||
resizable: { handles: 'all' },
|
||||
cellHeight: 30,
|
||||
};
|
||||
}
|
||||
}
|
||||
18
console/app/components/dashboard/metric.hbs
Normal file
18
console/app/components/dashboard/metric.hbs
Normal file
@@ -0,0 +1,18 @@
|
||||
<div class="col-span-{{or @dashboard.size 12}}">
|
||||
<div class="dashboard-title flex flex-col lg:flex-row lg:items-center">
|
||||
<div class="flex flex-row items-center mb-2 lg:mb-0">
|
||||
{{#if this.isLoading}}
|
||||
<Spinner class="mr-2i" />
|
||||
{{/if}}
|
||||
<h2 class="text-sm font-bold dark:text-gray-100 text-black">{{this.dashboard.title}}</h2>
|
||||
</div>
|
||||
<div>
|
||||
<Dashboard::QueryParams @params={{this.dashboard.queryParams}} @onChange={{this.onQueryParamsChanged}} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 lg:grid-cols-12 gap-4">
|
||||
{{#each this.dashboard.widgets as |widget|}}
|
||||
{{component (concat "dashboard/" widget.component) options=widget.options}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
39
console/app/components/dashboard/metric.js
Normal file
39
console/app/components/dashboard/metric.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { isArray } from '@ember/array';
|
||||
import { task } from 'ember-concurrency-decorators';
|
||||
|
||||
export default class MetricComponent extends Component {
|
||||
@service fetch;
|
||||
@tracked isLoading = false;
|
||||
@tracked dashboard;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadDashboard.perform();
|
||||
}
|
||||
|
||||
@action onQueryParamsChanged(changedParams) {
|
||||
this.loadDashboard.perform(changedParams);
|
||||
}
|
||||
|
||||
@task *loadDashboard(params) {
|
||||
let dashboards = [];
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
try {
|
||||
dashboards = yield this.fetch.get(this.args.options.endpoint, params, { namespace: '' });
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isLoading = false;
|
||||
|
||||
if (isArray(dashboards)) {
|
||||
this.dashboard = dashboards.objectAt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
console/app/components/dashboard/query-params.hbs
Normal file
8
console/app/components/dashboard/query-params.hbs
Normal file
@@ -0,0 +1,8 @@
|
||||
<div class="flex flex-row items-center space-x-3" ...attributes>
|
||||
{{#each-in @params as |param options|}}
|
||||
<div class="flex flex-col lg:flex-row lg:items-center">
|
||||
<label class="hidden lg:flex mr-2 text-sm">{{humanize param}}:</label>
|
||||
{{component options.component onChange=(fn this.onChange param) placeholder=(humanize param) class="form-input-sm"}}
|
||||
</div>
|
||||
{{/each-in}}
|
||||
</div>
|
||||
21
console/app/components/dashboard/query-params.js
Normal file
21
console/app/components/dashboard/query-params.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class DashboardQueryParamsComponent extends Component {
|
||||
@tracked changedParams = {};
|
||||
|
||||
@action onChange(param, value) {
|
||||
const component = this.args.params[param].component;
|
||||
|
||||
if (component === 'date-picker') {
|
||||
value = value.formattedDate;
|
||||
}
|
||||
|
||||
this.changedParams = { ...this.changedParams, [param]: value };
|
||||
|
||||
if (typeof this.args.onChange === 'function') {
|
||||
this.args.onChange(this.changedParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
console/app/components/dashboard/widget-panel.hbs
Normal file
31
console/app/components/dashboard/widget-panel.hbs
Normal file
@@ -0,0 +1,31 @@
|
||||
<Overlay @isOpen={{@isOpen}} @onLoad={{this.setOverlayContext}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @width={{or this.width @width "400px"}}>
|
||||
<Overlay::Header @title={{t "component.dashboard-widget-panel.select-widgets"}} @hideStatusDot={{true}} @titleWrapperClass="leading-5">
|
||||
<div class="flex flex-1 justify-end">
|
||||
<Button @type="default" @icon="times" @helpText={{t "component.dashboard-widget-panel.close-and-save"}} @onClick={{this.onPressClose}} />
|
||||
</div>
|
||||
</Overlay::Header>
|
||||
|
||||
<Overlay::Body @wrapperClass="new-service-rate-overlay-body px-4 space-y-4 pt-4">
|
||||
<div class="grid grid-cols-1 gap-4 text-xs dark:text-gray-100">
|
||||
{{#each this.availableWidgets as |widget|}}
|
||||
<div
|
||||
class="rounded-lg border border-gray-300 bg-white dark:border-gray-700 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 transition-all duration-300 ease-in-out shadow-md px-4 py-2 cursor-pointer"
|
||||
{{on "click" (fn this.addWidgetToDashboard widget)}}
|
||||
>
|
||||
<div class="flex flex-row items-center leading-6 mb-2.5">
|
||||
<div class="w-8 flex items-center justify-start">
|
||||
<FaIcon @icon={{widget.icon}} class="text-lg text-gray-600 dark:text-gray-300" />
|
||||
</div>
|
||||
<p class="text-sm truncate font-semibold dark:text-gray-100 text-gray-800">
|
||||
{{t "component.dashboard-widget-panel.widget-name" widgetName=widget.name}}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs dark:text-gray-100 text-gray-800">{{widget.description}}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<Spacer @height="300px" />
|
||||
</Overlay::Body>
|
||||
</Overlay>
|
||||
60
console/app/components/dashboard/widget-panel.js
Normal file
60
console/app/components/dashboard/widget-panel.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class DashboardWidgetPanelComponent extends Component {
|
||||
@service universe;
|
||||
@tracked availableWidgets = [];
|
||||
@tracked dashboard;
|
||||
@tracked isOpen = true;
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* Constructs the component and applies initial state.
|
||||
*/
|
||||
constructor(owner, { dashboard }) {
|
||||
super(...arguments);
|
||||
|
||||
this.availableWidgets = this.universe.getDashboardWidgets();
|
||||
this.dashboard = dashboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the overlay context.
|
||||
*
|
||||
* @action
|
||||
* @param {OverlayContextObject} overlayContext
|
||||
*/
|
||||
@action setOverlayContext(overlayContext) {
|
||||
this.context = overlayContext;
|
||||
|
||||
if (typeof this.args.onLoad === 'function') {
|
||||
this.args.onLoad(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@action addWidgetToDashboard(widget) {
|
||||
// If widget is a component definition/class
|
||||
if (typeof widget.component === 'function') {
|
||||
widget.component = widget.component.name;
|
||||
}
|
||||
|
||||
this.args.dashboard.addWidget(widget).catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles cancel button press.
|
||||
*
|
||||
* @action
|
||||
*/
|
||||
@action onPressClose() {
|
||||
this.isOpen = false;
|
||||
|
||||
if (typeof this.args.onClose === 'function') {
|
||||
this.args.onClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
27
console/app/components/fleetbase-blog.hbs
Normal file
27
console/app/components/fleetbase-blog.hbs
Normal file
@@ -0,0 +1,27 @@
|
||||
<div class="fleetbase-blog" ...attributes>
|
||||
<div class="border dark:border-gray-700 border-gray-200 dark:bg-gray-800 bg-gray-50 rounded-lg shadow-sm flex flex-col">
|
||||
<div class="flex flex-row items-center justify-between rounded-t-lg px-4 py-2 border-b border-gray-200 dark:border-gray-700">
|
||||
<span class="text-base font-bold text-black dark:text-gray-100">Fleetbase Blog</span>
|
||||
<a href="https://www.fleetbase.io/blog" target="_blog" class="flex flex-row items-center text-sm">
|
||||
<FaIcon @icon="arrow-up-right-from-square" @size="xs" class="mr-2" />
|
||||
<span>Visit Blog</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="px-4 py-2.5">
|
||||
{{#if this.isLoading}}
|
||||
<Spinner />
|
||||
{{else}}
|
||||
<ul class="space-y-2">
|
||||
{{#each this.posts as |post|}}
|
||||
<li class="text-sm flex flex-col lg:flex-row">
|
||||
<a href={{post.link}} target="_blog" class="text-black truncate max-w-lg dark:text-gray-200">
|
||||
<span class="truncate">{{post.title}}</span>
|
||||
</a>
|
||||
<span class="ml-1 text-gray-900 dark:text-gray-400">- {{post.pubDate}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
28
console/app/components/fleetbase-blog.js
Normal file
28
console/app/components/fleetbase-blog.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class FleetbaseBlogComponent extends Component {
|
||||
@service fetch;
|
||||
@tracked posts = [];
|
||||
@tracked isLoading = false;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadBlogPosts();
|
||||
}
|
||||
|
||||
@action loadBlogPosts() {
|
||||
this.isLoading = true;
|
||||
|
||||
return this.fetch
|
||||
.get('lookup/fleetbase-blog')
|
||||
.then((response) => {
|
||||
this.posts = response;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
49
console/app/components/github-card.hbs
Normal file
49
console/app/components/github-card.hbs
Normal file
@@ -0,0 +1,49 @@
|
||||
<div class="fleetbase-github-card relative flex-1 w-full" ...attributes>
|
||||
<div class="border dark:border-gray-700 border-gray-200 dark:bg-gray-800 bg-gray-50 rounded-lg shadow-sm flex flex-col">
|
||||
{{#if this.isLoading}}
|
||||
<div class="p-4">
|
||||
<Spinner />
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="flex flex-row p-3 border-b dark:border-gray-700 border-gray-200">
|
||||
<div class="w-12 flex-shrink-0"><img src={{this.data.owner.avatar_url}} alt="fleetbase/fleetbase" class="rounded-full w-8 h-8" width="32" height="32" /></div>
|
||||
<div class="flex-1 -mt-2">
|
||||
<div class="flex flex-1 flex-row items-center justify-between mb-2">
|
||||
<a href={{this.data.html_url}} target="_github" class="dark:text-gray-100 text-black text-base font-semibold">{{this.data.full_name}}</a>
|
||||
<a href={{this.data.html_url}} target="_github" class="btn btn-xs btn-default">
|
||||
<FaIcon @icon="star" class="text-yellow-400" />
|
||||
<span class="hidden truncate lg:flex ml-2.5">Star on Github</span>
|
||||
</a>
|
||||
</div>
|
||||
<p class="dark:text-gray-100 text-black text-sm">{{this.data.description}}</p>
|
||||
<div class="flex-grow-0 flex mt-2">
|
||||
<a href={{this.releaseUrl}} class="rounded-lg px-2 py-1 flex flex-row items-center text-xs text-white font-bold font-mono">
|
||||
<FaIcon @icon="arrow-up-right-from-square" @size="xs" class="mr-2" />
|
||||
<span>Latest Release: {{this.latestRelease.name}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-b-lg px-4 py-2">
|
||||
<div class="grid grid-cols-12 gap-2">
|
||||
<div class="col-span-3 dark:text-gray-100 text-black text-xs flex flex-row">
|
||||
<span class="font-bold mr-1">{{this.data.stargazers_count}}</span>
|
||||
<span>Stars</span>
|
||||
</div>
|
||||
<div class="col-span-3 dark:text-gray-100 text-black text-xs flex flex-row">
|
||||
<span class="font-bold mr-1">{{this.data.subscribers_count}}</span>
|
||||
<span>Watchers</span>
|
||||
</div>
|
||||
<div class="col-span-3 dark:text-gray-100 text-black text-xs flex flex-row">
|
||||
<span class="font-bold mr-1">{{this.data.forks_count}}</span>
|
||||
<span>Forks</span>
|
||||
</div>
|
||||
<div class="col-span-3 dark:text-gray-100 text-black text-xs flex flex-row">
|
||||
<span class="font-bold mr-1">{{this.data.open_issues_count}}</span>
|
||||
<span class="truncate"><span class="hidden lg:inline-flex">Open</span> Issues</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
80
console/app/components/github-card.js
Normal file
80
console/app/components/github-card.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { computed } from '@ember/object';
|
||||
import { isArray } from '@ember/array';
|
||||
import { isBlank } from '@ember/utils';
|
||||
import { task } from 'ember-concurrency';
|
||||
import { storageFor } from 'ember-local-storage';
|
||||
import { add, isPast } from 'date-fns';
|
||||
import fetch from 'fetch';
|
||||
|
||||
export default class GithubCardComponent extends Component {
|
||||
@storageFor('local-cache') localCache;
|
||||
@tracked data;
|
||||
@tracked tags;
|
||||
@tracked isLoading = false;
|
||||
|
||||
@computed('tags.length') get latestRelease() {
|
||||
if (isArray(this.tags) && this.tags.length) {
|
||||
return this.tags[0];
|
||||
}
|
||||
|
||||
return { name: 'v0.0.1' };
|
||||
}
|
||||
|
||||
@computed('data.releases_url', 'latestRelease.name') get releaseUrl() {
|
||||
let url = 'https://github.com/fleetbase/fleetbase/releases';
|
||||
|
||||
if (!isBlank(this.latestRelease?.name)) {
|
||||
url += '/tag/' + this.latestRelease.name;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.getRepositoryData.perform();
|
||||
this.getRepositoryTags.perform();
|
||||
}
|
||||
|
||||
@task *getRepositoryData() {
|
||||
// Check if cached data and expiration are available
|
||||
const cachedData = this.localCache.get('fleetbase-github-data');
|
||||
const expiration = this.localCache.get('fleetbase-github-data-expiration');
|
||||
|
||||
// Check if the cached data is still valid
|
||||
if (cachedData && expiration && !isPast(new Date(expiration))) {
|
||||
// Use cached data
|
||||
this.data = cachedData;
|
||||
} else {
|
||||
// Fetch new data
|
||||
const response = yield fetch('https://api.github.com/repos/fleetbase/fleetbase');
|
||||
if (response.ok) {
|
||||
this.data = yield response.json();
|
||||
this.localCache.set('fleetbase-github-data', this.data);
|
||||
this.localCache.set('fleetbase-github-data-expiration', add(new Date(), { hours: 6 }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@task *getRepositoryTags() {
|
||||
// Check if cached tags and expiration are available
|
||||
const cachedTags = this.localCache.get('fleetbase-github-tags');
|
||||
const expiration = this.localCache.get('fleetbase-github-tags-expiration');
|
||||
|
||||
// Check if the cached tags are still valid
|
||||
if (cachedTags && expiration && !isPast(new Date(expiration))) {
|
||||
// Use cached tags
|
||||
this.tags = cachedTags;
|
||||
} else {
|
||||
// Fetch new tags
|
||||
const response = yield fetch('https://api.github.com/repos/fleetbase/fleetbase/tags');
|
||||
if (response.ok) {
|
||||
this.tags = yield response.json();
|
||||
this.localCache.set('fleetbase-github-tags', this.tags);
|
||||
this.localCache.set('fleetbase-github-tags-expiration', add(new Date(), { hours: 6 }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
console/app/components/modals/create-dashboard.hbs
Normal file
5
console/app/components/modals/create-dashboard.hbs
Normal file
@@ -0,0 +1,5 @@
|
||||
<Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
|
||||
<div class="modal-body-container">
|
||||
<InputGroup @name="Dashboard name" @value={{@options.name}} @helpText="Enter the name of your dashboard" />
|
||||
</div>
|
||||
</Modal::Default>
|
||||
13
console/app/components/modals/validate-password.hbs
Normal file
13
console/app/components/modals/validate-password.hbs
Normal file
@@ -0,0 +1,13 @@
|
||||
<Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
|
||||
<div class="px-5">
|
||||
{{#if @options.body}}
|
||||
<p class="dark:text-gray-400 text-gray-700 mb-4">{{@options.body}}</p>
|
||||
{{/if}}
|
||||
<InputGroup>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<InputGroup @name="Password" @type="password" @value={{this.password}} @wrapperClass="mb-0i" />
|
||||
<InputGroup @name="Confirm Password" @type="password" @value={{this.confirmPassword}} @wrapperClass="mb-0i" />
|
||||
</div>
|
||||
</InputGroup>
|
||||
</div>
|
||||
</Modal::Default>
|
||||
49
console/app/components/modals/validate-password.js
Normal file
49
console/app/components/modals/validate-password.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { task } from 'ember-concurrency';
|
||||
|
||||
export default class ModalsValidatePasswordComponent extends Component {
|
||||
@service fetch;
|
||||
@service notifications;
|
||||
@tracked options = {};
|
||||
@tracked password;
|
||||
@tracked confirmPassword;
|
||||
|
||||
constructor(owner, { options }) {
|
||||
super(...arguments);
|
||||
this.options = options;
|
||||
this.setupOptions();
|
||||
}
|
||||
|
||||
setupOptions() {
|
||||
this.options.title = 'Validate Current Password';
|
||||
this.options.acceptButtonText = 'Validate Password';
|
||||
this.options.declineButtonHidden = true;
|
||||
this.options.confirm = (modal) => {
|
||||
modal.startLoading();
|
||||
return this.validatePassword.perform();
|
||||
};
|
||||
}
|
||||
|
||||
@task *validatePassword() {
|
||||
let isPasswordValid = false;
|
||||
|
||||
try {
|
||||
yield this.fetch.post('users/validate-password', {
|
||||
password: this.password,
|
||||
password_confirmation: this.confirmPassword,
|
||||
});
|
||||
|
||||
isPasswordValid = true;
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error, 'Invalid current password.');
|
||||
}
|
||||
|
||||
if (typeof this.options.onValidated === 'function') {
|
||||
this.options.onValidated(isPasswordValid);
|
||||
}
|
||||
|
||||
return isPasswordValid;
|
||||
}
|
||||
}
|
||||
14
console/app/components/two-fa-enforcement-alert.hbs
Normal file
14
console/app/components/two-fa-enforcement-alert.hbs
Normal file
@@ -0,0 +1,14 @@
|
||||
{{#if this.shouldRender}}
|
||||
<InfoBlock @icon="triangle-exclamation" class="two-fa-enforcement-alert bg-yellow-100 border-2 border-yellow-600 dark:border-yellow-500 rounded-lg py-3.5 px-5">
|
||||
<div class="flex flex-row justify-between">
|
||||
<div class="flex-1 pr-2">
|
||||
<p class="text-sm dark:text-yellow-900 mb-2">
|
||||
{{t "component.two-fa-enforcement-alert.message"}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
<Button id="two-fa-setup-button" @text={{t "component.two-fa-enforcement-alert.button-text"}} @icon="shield-halved" @type="warning" @buttonType="button" @onClick={{this.transitionToTwoFactorSettings}} />
|
||||
</div>
|
||||
</div>
|
||||
</InfoBlock>
|
||||
{{/if}}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user