mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 08:42:13 +02:00
Compare commits
1054 Commits
ext-postgr
...
copilot-ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50c3bbb845 | ||
|
|
99daa9f25b | ||
|
|
39ff367afe | ||
|
|
012c9299ed | ||
|
|
0559997c01 | ||
|
|
b8fad24329 | ||
|
|
e907c235a9 | ||
|
|
258914731c | ||
|
|
d5af7a907d | ||
|
|
1822107c49 | ||
|
|
bd5808b2d4 | ||
|
|
4facc12e44 | ||
|
|
6a10bdff56 | ||
|
|
9be38d55c6 | ||
|
|
74d210bffd | ||
|
|
4b986346ae | ||
|
|
507b214cc1 | ||
|
|
7904d36650 | ||
|
|
a23039afb6 | ||
|
|
5be761dd7c | ||
|
|
6398fea83e | ||
|
|
4b0f71b4e4 | ||
|
|
4314920cd7 | ||
|
|
d2cb9a8eeb | ||
|
|
7512145a50 | ||
|
|
b3f4406c3c | ||
|
|
06ac018ec6 | ||
|
|
c76237f785 | ||
|
|
97e60faa22 | ||
|
|
b857555cd7 | ||
|
|
6541fc41f0 | ||
|
|
8bb2c3c865 | ||
|
|
6fb477383d | ||
|
|
2269681487 | ||
|
|
6e10739479 | ||
|
|
1f3cb96f0a | ||
|
|
e85f8c19df | ||
|
|
bc9a8c5151 | ||
|
|
9b83202e58 | ||
|
|
3ca4d5d481 | ||
|
|
5c4d4b2c63 | ||
|
|
fd0a95d436 | ||
|
|
23c65cecf0 | ||
|
|
5c3dcf7bc9 | ||
|
|
108940678f | ||
|
|
3df72091a2 | ||
|
|
b1de73a16a | ||
|
|
f715b14b74 | ||
|
|
17eef6408e | ||
|
|
bc288100c0 | ||
|
|
c4a01058b7 | ||
|
|
cb10647285 | ||
|
|
fb8b00d383 | ||
|
|
4563f42794 | ||
|
|
a6072634f2 | ||
|
|
646908e66b | ||
|
|
ed150c1482 | ||
|
|
fac59332ed | ||
|
|
64725b3973 | ||
|
|
3636b160fb | ||
|
|
fee3264350 | ||
|
|
5bcd451bc8 | ||
|
|
7c2d6dc9e5 | ||
|
|
185eb20d1b | ||
|
|
cc7696f481 | ||
|
|
a3856588bb | ||
|
|
1a9668a877 | ||
|
|
92e519c49e | ||
|
|
3c296f72d8 | ||
|
|
f0e701c5bb | ||
|
|
7fa461e652 | ||
|
|
66b4fbded1 | ||
|
|
e7fc9bb9a5 | ||
|
|
2f3dbebe9a | ||
|
|
a6a5f189c3 | ||
|
|
67f1ad9ef1 | ||
|
|
4e7d1d5c7a | ||
|
|
b9dd9eb8de | ||
|
|
3c86293864 | ||
|
|
b202b07353 | ||
|
|
9db1c59893 | ||
|
|
ac7d4f974b | ||
|
|
6251dfe482 | ||
|
|
dbdca789d4 | ||
|
|
12e766e9d4 | ||
|
|
ba2a480d4f | ||
|
|
3b141d7e90 | ||
|
|
61541ac7c9 | ||
|
|
abde6ee7cb | ||
|
|
9aae86cc78 | ||
|
|
222027f2e0 | ||
|
|
adfb3e1db4 | ||
|
|
fe8aa54f1c | ||
|
|
5237384d08 | ||
|
|
aca766a252 | ||
|
|
ee29277cb9 | ||
|
|
310ca572ff | ||
|
|
3d13620a28 | ||
|
|
1c069c60fc | ||
|
|
7146b341c5 | ||
|
|
8f8e40aa71 | ||
|
|
0e9c752aae | ||
|
|
d37f8a4049 | ||
|
|
26dd72eea2 | ||
|
|
462105c6fd | ||
|
|
5662d45826 | ||
|
|
32ebb26960 | ||
|
|
235fed6f17 | ||
|
|
a51c0c45e4 | ||
|
|
8ea54108c1 | ||
|
|
cba6e9e2a0 | ||
|
|
bd1e1792e4 | ||
|
|
e5c9b20c64 | ||
|
|
8145f07e51 | ||
|
|
56654085a4 | ||
|
|
a6ddba16ec | ||
|
|
b3cf198649 | ||
|
|
4dd7285c13 | ||
|
|
5347b5c109 | ||
|
|
24b956c55f | ||
|
|
32c9d6d2df | ||
|
|
c883ce6a34 | ||
|
|
3851c1f963 | ||
|
|
dd5cb900ff | ||
|
|
47a4fe1937 | ||
|
|
0d0e64f633 | ||
|
|
cc432dfa1c | ||
|
|
0fd161d251 | ||
|
|
a6cb80f640 | ||
|
|
5dbd8b9b35 | ||
|
|
3df855c52a | ||
|
|
183ff170ae | ||
|
|
946e53b5c5 | ||
|
|
c8f8b79cea | ||
|
|
cf71ba0c72 | ||
|
|
90ffb7233d | ||
|
|
7742318b74 | ||
|
|
5a1705cf12 | ||
|
|
fe55d39d14 | ||
|
|
a820b53d65 | ||
|
|
d2a9d9fed4 | ||
|
|
c9a69db7f2 | ||
|
|
69dd24128a | ||
|
|
7c2238eac7 | ||
|
|
2e850ed6ab | ||
|
|
7e54fc33ae | ||
|
|
bc15416103 | ||
|
|
b61fb75e36 | ||
|
|
a2d2ed038b | ||
|
|
f36ea78168 | ||
|
|
14cf7f047f | ||
|
|
c64c3d7012 | ||
|
|
8cb15df390 | ||
|
|
32da3f7c94 | ||
|
|
9355f185a9 | ||
|
|
c53584e9d5 | ||
|
|
01be73612d | ||
|
|
99a80ab9b6 | ||
|
|
95dca8e8c6 | ||
|
|
eddc5f1845 | ||
|
|
c965c18943 | ||
|
|
3c20f424e9 | ||
|
|
0ef7ca93f8 | ||
|
|
ca7c55b557 | ||
|
|
a72373994a | ||
|
|
fc0071162e | ||
|
|
3eef55a29d | ||
|
|
aa5dc27427 | ||
|
|
900e567748 | ||
|
|
a88b14747c | ||
|
|
1619b23996 | ||
|
|
6ed7e19538 | ||
|
|
41a3bc8a72 | ||
|
|
2621dc7cc2 | ||
|
|
c40ec85ce3 | ||
|
|
ff6369389c | ||
|
|
9211c91510 | ||
|
|
3e778fe503 | ||
|
|
77308cd9b7 | ||
|
|
60a33b5b0c | ||
|
|
679649e7ac | ||
|
|
719272e360 | ||
|
|
ca7907973a | ||
|
|
86e5a33bd9 | ||
|
|
3eb6f4da5f | ||
|
|
dea6c55365 | ||
|
|
ce9e449ba1 | ||
|
|
12530a1d0d | ||
|
|
4e81ed92f2 | ||
|
|
21047e77b1 | ||
|
|
67a326e6af | ||
|
|
23318f093f | ||
|
|
805a9b928f | ||
|
|
63202cc51f | ||
|
|
bb646bbcf1 | ||
|
|
000012cd03 | ||
|
|
1ed9dfc429 | ||
|
|
491b89cbb4 | ||
|
|
bfc8619095 | ||
|
|
8ec5e4bb8a | ||
|
|
c0c596346c | ||
|
|
e27d47e5bb | ||
|
|
524863dff3 | ||
|
|
5bc054504a | ||
|
|
6fd43622a3 | ||
|
|
725a58d5fb | ||
|
|
9bf3856c95 | ||
|
|
5fc95eb02e | ||
|
|
3f7cfa3110 | ||
|
|
bca44b5faf | ||
|
|
02f91d26ff | ||
|
|
5221c20ee2 | ||
|
|
ea0d1a479f | ||
|
|
ea3c357be1 | ||
|
|
b1c3a9e3c8 | ||
|
|
9ec136bf78 | ||
|
|
c4611848e3 | ||
|
|
a096591c52 | ||
|
|
b29fb6e833 | ||
|
|
2e2ade0b16 | ||
|
|
525ad8c664 | ||
|
|
434c55f88f | ||
|
|
c968156fa7 | ||
|
|
36a0dad41c | ||
|
|
52630b16d1 | ||
|
|
7b994675c8 | ||
|
|
d0692ebc8f | ||
|
|
63197d263f | ||
|
|
13b1998261 | ||
|
|
29764ae7c7 | ||
|
|
5c3175f9e7 | ||
|
|
8caac12041 | ||
|
|
d84cf5b8ec | ||
|
|
a49709992c | ||
|
|
21b078040c | ||
|
|
70371a32af | ||
|
|
f45985e0ce | ||
|
|
f1d7e0d13f | ||
|
|
3f8a5291f0 | ||
|
|
e60b06d014 | ||
|
|
c9e67311e7 | ||
|
|
0b10880f58 | ||
|
|
d49a6ce009 | ||
|
|
9b8a5c9c43 | ||
|
|
9dc31fe536 | ||
|
|
0d40be3db1 | ||
|
|
6754167e57 | ||
|
|
3bf74edae4 | ||
|
|
8a77ae977e | ||
|
|
6fbca4b6cb | ||
|
|
5cf1a7f675 | ||
|
|
7339f4873b | ||
|
|
0872b1e8a2 | ||
|
|
8307f4deca | ||
|
|
0ac88b3679 | ||
|
|
2c5bbbb6da | ||
|
|
88216cdebb | ||
|
|
a24822bd56 | ||
|
|
8f4538a75c | ||
|
|
14be322ef7 | ||
|
|
b50f05b290 | ||
|
|
e215b5b1ba | ||
|
|
c93f58227f | ||
|
|
538e40c4ae | ||
|
|
4a03abe3d5 | ||
|
|
c218a6f209 | ||
|
|
a8f04a8204 | ||
|
|
642b1616ca | ||
|
|
9afbac2cbc | ||
|
|
b6b49a1255 | ||
|
|
1cd89851bb | ||
|
|
229b5cd772 | ||
|
|
2b1ad303d9 | ||
|
|
54b0f355dc | ||
|
|
10ed38197e | ||
|
|
cc4dab2dcf | ||
|
|
8852e2ab61 | ||
|
|
50b6a746f4 | ||
|
|
df8d2f25ee | ||
|
|
ee3de663b5 | ||
|
|
a4d1ed7f01 | ||
|
|
05a26d0b3f | ||
|
|
0a5094db37 | ||
|
|
2f2c4891e3 | ||
|
|
83cb3b66e0 | ||
|
|
63575f3065 | ||
|
|
bf031f64fa | ||
|
|
04f1cfe414 | ||
|
|
f11863df0f | ||
|
|
599fc23c1a | ||
|
|
fa03d9296c | ||
|
|
df9bb5184f | ||
|
|
ca7b06d4bc | ||
|
|
59d6ef240f | ||
|
|
5ae034ffdf | ||
|
|
be75960aff | ||
|
|
708f104dfc | ||
|
|
23c34cc71a | ||
|
|
3bf76168f1 | ||
|
|
3499850c6d | ||
|
|
273a153311 | ||
|
|
b248dc324d | ||
|
|
7d7b4110a7 | ||
|
|
262436f906 | ||
|
|
2c7b845cb6 | ||
|
|
3c9284915a | ||
|
|
ba97a1e14b | ||
|
|
e552a8130f | ||
|
|
1ed2548be2 | ||
|
|
5c697105f3 | ||
|
|
b2bdd5d1af | ||
|
|
72e0f200f7 | ||
|
|
3f0d75d799 | ||
|
|
519f2ef312 | ||
|
|
cfbe92e40a | ||
|
|
8ef66650fb | ||
|
|
07973e85a3 | ||
|
|
34ca901007 | ||
|
|
09883b1129 | ||
|
|
b94b775e46 | ||
|
|
1c3b7d9982 | ||
|
|
ff062cb6a2 | ||
|
|
2cabc922a0 | ||
|
|
b6aaea8ae4 | ||
|
|
886aa086ec | ||
|
|
6c44201e1c | ||
|
|
3a72c5d04f | ||
|
|
ea162322d2 | ||
|
|
222c857354 | ||
|
|
d2d705783e | ||
|
|
75a7308647 | ||
|
|
d61ade1eb4 | ||
|
|
55e1f6b504 | ||
|
|
8b95f2887c | ||
|
|
2bd1693d1f | ||
|
|
b1eaded3fd | ||
|
|
7e486d34ae | ||
|
|
c930caa2bb | ||
|
|
e654b65e71 | ||
|
|
a145b346a4 | ||
|
|
f090654ab6 | ||
|
|
c2e14c378b | ||
|
|
77996b2066 | ||
|
|
5fce72fe59 | ||
|
|
9f8764d741 | ||
|
|
9ce44cc416 | ||
|
|
f5f542cae6 | ||
|
|
de4eea4d24 | ||
|
|
104535b723 | ||
|
|
6f5dfceb34 | ||
|
|
1ec61a473a | ||
|
|
abf4faade7 | ||
|
|
6a9ee5ba75 | ||
|
|
b696e52145 | ||
|
|
8af8eb34ad | ||
|
|
e600fac4fd | ||
|
|
1772953b34 | ||
|
|
44e3c99da8 | ||
|
|
5fa0f23689 | ||
|
|
5a9a53dfea | ||
|
|
9dae8e1764 | ||
|
|
2c8b58c293 | ||
|
|
57686c87e3 | ||
|
|
738fb77773 | ||
|
|
51a714f4bd | ||
|
|
358b785fe0 | ||
|
|
a464723729 | ||
|
|
5ab959cec5 | ||
|
|
97bb269ccf | ||
|
|
85c70bbc17 | ||
|
|
7effd05896 | ||
|
|
3123b5304d | ||
|
|
6ea6f94694 | ||
|
|
4b91e598ce | ||
|
|
07293006c3 | ||
|
|
8f86a4651e | ||
|
|
74857d58d8 | ||
|
|
e93b74665b | ||
|
|
3c0eeeb81d | ||
|
|
8de13e75f2 | ||
|
|
f57a487b13 | ||
|
|
2eb0f51636 | ||
|
|
4741d8f9e7 | ||
|
|
4ce5abc918 | ||
|
|
f041a6cbbb | ||
|
|
394e5460b5 | ||
|
|
7a8043f4db | ||
|
|
7fe26603fd | ||
|
|
a50cce6c6f | ||
|
|
3c839799da | ||
|
|
5a11df0153 | ||
|
|
bb8b4430cf | ||
|
|
c6961b9a0b | ||
|
|
754e6b165e | ||
|
|
5f2430ff06 | ||
|
|
80b9961bd8 | ||
|
|
886f753422 | ||
|
|
9ea0c6389c | ||
|
|
f29bcc6641 | ||
|
|
60aa7032c5 | ||
|
|
4d8ba7d3a3 | ||
|
|
c87744c51e | ||
|
|
9841ba52a9 | ||
|
|
b16df022ed | ||
|
|
c3d8acc61a | ||
|
|
04fbb15405 | ||
|
|
2cd61dbbac | ||
|
|
e6bc8e4b30 | ||
|
|
e9f0510d3c | ||
|
|
9c0cf6a3db | ||
|
|
bca58a58ae | ||
|
|
2c0fbfef19 | ||
|
|
395f9bcdb7 | ||
|
|
4587e3136b | ||
|
|
2a458ec7e6 | ||
|
|
2c02a62e63 | ||
|
|
1afd76b080 | ||
|
|
5fae51fa01 | ||
|
|
d8366efee8 | ||
|
|
5f3cfa3f0d | ||
|
|
84740444ec | ||
|
|
3881ad163c | ||
|
|
f7f43ed16c | ||
|
|
39b5982d67 | ||
|
|
0f0f2c1b81 | ||
|
|
7e05939977 | ||
|
|
c5062b5bc5 | ||
|
|
90355ae7ed | ||
|
|
da67dc6930 | ||
|
|
71151ea92e | ||
|
|
d97211d52c | ||
|
|
d0e31ad95e | ||
|
|
7c796e110b | ||
|
|
137cdb7716 | ||
|
|
0a02aba790 | ||
|
|
652e81e984 | ||
|
|
4dacd9b8b4 | ||
|
|
71fb83e698 | ||
|
|
d514059d5d | ||
|
|
3c29278cc1 | ||
|
|
5d1ad931fc | ||
|
|
4e0d56de54 | ||
|
|
a33b1f20f8 | ||
|
|
9fa61dac31 | ||
|
|
263c1ee613 | ||
|
|
c648a6bf79 | ||
|
|
ed954d79a4 | ||
|
|
113e95a059 | ||
|
|
4d31b85c4d | ||
|
|
58cf3abda9 | ||
|
|
9227324ac3 | ||
|
|
e5c608e20b | ||
|
|
8a41d48941 | ||
|
|
eb19465e56 | ||
|
|
93ee819457 | ||
|
|
7931ac475e | ||
|
|
6286c203fe | ||
|
|
9d43a2d98b | ||
|
|
5271bcd49f | ||
|
|
54db88dd96 | ||
|
|
dd336896d2 | ||
|
|
75ad6697cd | ||
|
|
2862d1e69b | ||
|
|
5e486e4ce6 | ||
|
|
6b17d6d6dc | ||
|
|
272a29aad1 | ||
|
|
d21e333c9e | ||
|
|
b5195c27c0 | ||
|
|
3f436861ad | ||
|
|
3982d49136 | ||
|
|
e007fb1b72 | ||
|
|
ccba9482de | ||
|
|
8dfa8c2e09 | ||
|
|
af0a5d1022 | ||
|
|
43a22d9973 | ||
|
|
d8c8a76c1d | ||
|
|
e40dc2e9ff | ||
|
|
e0769f1d2b | ||
|
|
e336d2ad38 | ||
|
|
6ca6daf38d | ||
|
|
ffa6f143ac | ||
|
|
04650f165f | ||
|
|
214c4e4839 | ||
|
|
fad530cf86 | ||
|
|
6b9e0c8b99 | ||
|
|
5a66e9a393 | ||
|
|
ddd5813266 | ||
|
|
e246e3fbcd | ||
|
|
e1f4cc22ac | ||
|
|
6911cf59f4 | ||
|
|
5172d7f349 | ||
|
|
55e043f3bd | ||
|
|
bdee2f0585 | ||
|
|
833ca82cb7 | ||
|
|
6751972d7f | ||
|
|
44ed919f95 | ||
|
|
c8ad2a1ac8 | ||
|
|
232b537969 | ||
|
|
d5c8e7836f | ||
|
|
e897955466 | ||
|
|
4f3ae06e59 | ||
|
|
e971bdaee9 | ||
|
|
d165ce86df | ||
|
|
45d327e55d | ||
|
|
ec704cbe34 | ||
|
|
658cb9fee3 | ||
|
|
3234112644 | ||
|
|
0c7db14e99 | ||
|
|
34118d5892 | ||
|
|
2ee80ee2f9 | ||
|
|
aaacaa654b | ||
|
|
d8c38a3564 | ||
|
|
add77694c3 | ||
|
|
66171e8035 | ||
|
|
932d218455 | ||
|
|
33a295c191 | ||
|
|
4c0f1b08c8 | ||
|
|
f58e935cb9 | ||
|
|
ad48ea5a3f | ||
|
|
3a4ae31872 | ||
|
|
48996b0810 | ||
|
|
d450c99560 | ||
|
|
51884a31cd | ||
|
|
5251b93fc9 | ||
|
|
ac3635511c | ||
|
|
0990ef0846 | ||
|
|
294b245d88 | ||
|
|
3eb3564c57 | ||
|
|
851f061018 | ||
|
|
e9a2c64d67 | ||
|
|
f9b72e0155 | ||
|
|
834fd23542 | ||
|
|
14b45b95a4 | ||
|
|
74d4a6545e | ||
|
|
646c0f37d6 | ||
|
|
1fdc36fea2 | ||
|
|
a34a6657f0 | ||
|
|
95f87567e8 | ||
|
|
9ad97b1fe0 | ||
|
|
9b9e6d666b | ||
|
|
24845b267f | ||
|
|
448326ee88 | ||
|
|
12dab7bdda | ||
|
|
3eba4da4a8 | ||
|
|
b947ed2ed1 | ||
|
|
6eef29e4a3 | ||
|
|
74c2de8adc | ||
|
|
f53b314b59 | ||
|
|
e4bf81fcc1 | ||
|
|
00b48b16ce | ||
|
|
5b00b5dac1 | ||
|
|
97306d47fa | ||
|
|
5df9f9fd69 | ||
|
|
d9ce813689 | ||
|
|
93f1b97e88 | ||
|
|
c617e49a63 | ||
|
|
de7d06e5d7 | ||
|
|
9b862162b6 | ||
|
|
0ecf3fa1e0 | ||
|
|
b5fa564cb3 | ||
|
|
b771e1486e | ||
|
|
3f1479d8f5 | ||
|
|
dcdf1e21ae | ||
|
|
cc3f003be5 | ||
|
|
0e53e26695 | ||
|
|
ad1e5fefc6 | ||
|
|
8ea77dc6d1 | ||
|
|
2222ca301f | ||
|
|
0f66c70205 | ||
|
|
88dc64f182 | ||
|
|
94c50b980a | ||
|
|
9866919873 | ||
|
|
e025f996e4 | ||
|
|
cd4c326f6e | ||
|
|
c668731389 | ||
|
|
e6a10edca6 | ||
|
|
847f7fa5c9 | ||
|
|
03cb9a67b4 | ||
|
|
9791aa1259 | ||
|
|
0df7209723 | ||
|
|
0de8e2d818 | ||
|
|
5f9a1091de | ||
|
|
6ef17b1720 | ||
|
|
9c41a20ebb | ||
|
|
eddb5a81c9 | ||
|
|
3861d09930 | ||
|
|
733901a870 | ||
|
|
131c3034d6 | ||
|
|
0c9175c728 | ||
|
|
e2eff652e3 | ||
|
|
43e9530fdb | ||
|
|
0000cbc53e | ||
|
|
8b26cac8f0 | ||
|
|
e8417b23d9 | ||
|
|
1bd5671d17 | ||
|
|
56d3e7e1f2 | ||
|
|
34888e9b7f | ||
|
|
7c09423766 | ||
|
|
9ba8251c58 | ||
|
|
7a5efd99ca | ||
|
|
1e595be586 | ||
|
|
1d63168606 | ||
|
|
82601a3e8f | ||
|
|
17bf63e83b | ||
|
|
885a6ca36e | ||
|
|
dec35bbc03 | ||
|
|
77dcb16e8b | ||
|
|
b10a4c7db8 | ||
|
|
8d82f1128b | ||
|
|
056c069c69 | ||
|
|
0466950c2d | ||
|
|
4442437ad7 | ||
|
|
1192c4b6f0 | ||
|
|
08f39af9ec | ||
|
|
7bf58d23bc | ||
|
|
afb0cbcc2a | ||
|
|
7393d9c2bc | ||
|
|
7b8efb2785 | ||
|
|
efea6f4c11 | ||
|
|
8dbf724f36 | ||
|
|
fa4c705023 | ||
|
|
106e41206e | ||
|
|
dc0c71b063 | ||
|
|
0cadb54117 | ||
|
|
0ba315342c | ||
|
|
d87b292691 | ||
|
|
ea3abf2ca0 | ||
|
|
3e507c0259 | ||
|
|
5385c8e65c | ||
|
|
771ad54110 | ||
|
|
6743193872 | ||
|
|
b94d862cae | ||
|
|
77d9b2f98f | ||
|
|
2565087dba | ||
|
|
5f330b5ea6 | ||
|
|
c5f6816d7b | ||
|
|
585b98c83d | ||
|
|
40635d5b07 | ||
|
|
fd96f7a1ec | ||
|
|
f585ffe756 | ||
|
|
9a86978c9d | ||
|
|
3bf87cf9eb | ||
|
|
b7b5288f9c | ||
|
|
2833dbb474 | ||
|
|
2a6dda5fe2 | ||
|
|
e9e5b5a329 | ||
|
|
c0e055343e | ||
|
|
c657397d3b | ||
|
|
91cf4995b1 | ||
|
|
7b7c1e5951 | ||
|
|
b67583f8d3 | ||
|
|
460b8459d4 | ||
|
|
8328ece011 | ||
|
|
6f4045ffe9 | ||
|
|
54ce2c71c0 | ||
|
|
0a00cd9581 | ||
|
|
162c5d9db9 | ||
|
|
8be8c23ed9 | ||
|
|
f2b6fffb4c | ||
|
|
626312d495 | ||
|
|
77287868c4 | ||
|
|
f7d221900a | ||
|
|
d51a420532 | ||
|
|
0567f92ddd | ||
|
|
e88e29f9e5 | ||
|
|
e50f6e14f0 | ||
|
|
933b3e96a1 | ||
|
|
00b67974ed | ||
|
|
a05a7d9122 | ||
|
|
fdf440f308 | ||
|
|
34c6a32c48 | ||
|
|
0b078226f3 | ||
|
|
e0a5927bd1 | ||
|
|
bed1f0dbb5 | ||
|
|
4fef8ccfb5 | ||
|
|
bb29b4be4b | ||
|
|
e649a6f25a | ||
|
|
078d89d8ae | ||
|
|
a13c172d6d | ||
|
|
6b4727bc9f | ||
|
|
95aaa68010 | ||
|
|
976686b5af | ||
|
|
f59064108e | ||
|
|
44d8254e7d | ||
|
|
d284a6ff54 | ||
|
|
1908f02447 | ||
|
|
f50e20d896 | ||
|
|
bd8ed04e1b | ||
|
|
27c54221cb | ||
|
|
cf589ba0c6 | ||
|
|
43eaff3a6d | ||
|
|
30e96de2d9 | ||
|
|
43c4f44f1f | ||
|
|
af6f5af11d | ||
|
|
79603c9bb4 | ||
|
|
e2074b010e | ||
|
|
2187edc158 | ||
|
|
a9778cf9d0 | ||
|
|
c450c3fa1b | ||
|
|
3aaed9c901 | ||
|
|
70a9944f6a | ||
|
|
ad48aae0ba | ||
|
|
14f9950c38 | ||
|
|
4acedfdd62 | ||
|
|
deff7c9464 | ||
|
|
b407286ef0 | ||
|
|
9348076df0 | ||
|
|
09a7e155b7 | ||
|
|
127cc6b9b1 | ||
|
|
ea5474527c | ||
|
|
9111b831a7 | ||
|
|
dcabdd0a55 | ||
|
|
058b585eda | ||
|
|
25da20a968 | ||
|
|
4f4d9946ff | ||
|
|
0987634e54 | ||
|
|
c099f3a3ef | ||
|
|
a3f5e268b5 | ||
|
|
2599841d37 | ||
|
|
6c8a0c8cdc | ||
|
|
0d24bb6351 | ||
|
|
a4c5354152 | ||
|
|
a906713f6e | ||
|
|
e85e351797 | ||
|
|
3f9c1fd46e | ||
|
|
6c3edbfcb0 | ||
|
|
d3f212c9ec | ||
|
|
1964db22f1 | ||
|
|
b6a70bbd6c | ||
|
|
f36609712b | ||
|
|
6737d97f30 | ||
|
|
d22b40e91a | ||
|
|
e2e4db5073 | ||
|
|
2415f97ec6 | ||
|
|
0910c9f6f6 | ||
|
|
1a02f5b73e | ||
|
|
b6cabf12d5 | ||
|
|
27ef73be20 | ||
|
|
00b35c4d9a | ||
|
|
369bdfc841 | ||
|
|
8150f44244 | ||
|
|
6a646c06b3 | ||
|
|
422656caf3 | ||
|
|
5223d1bd6d | ||
|
|
42f0b90291 | ||
|
|
3d4f28fc0f | ||
|
|
e74c6a7030 | ||
|
|
6be976102c | ||
|
|
261072b1fd | ||
|
|
e7659bdf08 | ||
|
|
2a8c45cea0 | ||
|
|
b197416247 | ||
|
|
fe0e745640 | ||
|
|
28ea4d9b83 | ||
|
|
3d7cb148d0 | ||
|
|
0d0760e450 | ||
|
|
a6f66a59bf | ||
|
|
f09419ef50 | ||
|
|
4c90dc66a0 | ||
|
|
99c2e34ab5 | ||
|
|
0fb0e9b047 | ||
|
|
7ae6a3c5ea | ||
|
|
59f0526936 | ||
|
|
bfcd5c1753 | ||
|
|
76df1a5889 | ||
|
|
422ee6c192 | ||
|
|
808b3512f3 | ||
|
|
742796fd67 | ||
|
|
9027369e10 | ||
|
|
a87f3ac896 | ||
|
|
db75cc1a5b | ||
|
|
2deeb32b78 | ||
|
|
bc8b8eb982 | ||
|
|
ef3fe66e72 | ||
|
|
17a0b65a4b | ||
|
|
01a1a0f69e | ||
|
|
1260e482cf | ||
|
|
6e5081a4e3 | ||
|
|
dd93998296 | ||
|
|
94956b045a | ||
|
|
b2f650a865 | ||
|
|
4390a37184 | ||
|
|
9b08d1a9e4 | ||
|
|
dbef1071e0 | ||
|
|
98fbbe301e | ||
|
|
6805083725 | ||
|
|
27213fa235 | ||
|
|
baa98e333e | ||
|
|
ccdcf2c679 | ||
|
|
85edd12c2d | ||
|
|
97cc28b182 | ||
|
|
b0041e6993 | ||
|
|
a66d78743d | ||
|
|
bc78491478 | ||
|
|
7751870ccf | ||
|
|
df6ffb15d4 | ||
|
|
df20f343e9 | ||
|
|
e4ade513ce | ||
|
|
3c2af1dc38 | ||
|
|
af5d714642 | ||
|
|
3f315be279 | ||
|
|
095493cec9 | ||
|
|
3bdf87f34f | ||
|
|
26bb6f1e74 | ||
|
|
20db81a5f6 | ||
|
|
f9b097a112 | ||
|
|
b70b70e27e | ||
|
|
93df459662 | ||
|
|
1aceb81c85 | ||
|
|
023aea94c2 | ||
|
|
d8dff468ab | ||
|
|
082d800c34 | ||
|
|
e7f4c962b8 | ||
|
|
f6074fe8f4 | ||
|
|
b45678f167 | ||
|
|
6d53678135 | ||
|
|
7b77dd4a53 | ||
|
|
51bb54e771 | ||
|
|
48b095f548 | ||
|
|
786ec6ce7a | ||
|
|
401fbb58e3 | ||
|
|
5243ae6b8d | ||
|
|
a06a0f1d16 | ||
|
|
e0bc906484 | ||
|
|
139ff2d638 | ||
|
|
7be603ef08 | ||
|
|
d23548f984 | ||
|
|
3573e59634 | ||
|
|
c4e4e7e488 | ||
|
|
be3e8447ab | ||
|
|
2615e39c19 | ||
|
|
49b6550581 | ||
|
|
e870efc3c4 | ||
|
|
38862adf5a | ||
|
|
06b92e2745 | ||
|
|
1a8e0682c7 | ||
|
|
e0189356d5 | ||
|
|
dad07b9f80 | ||
|
|
8d59710306 | ||
|
|
5e9227eb4f | ||
|
|
1e63173564 | ||
|
|
76fded39ee | ||
|
|
c82f3c1f71 | ||
|
|
24d9219a62 | ||
|
|
6ed588d7aa | ||
|
|
17c1862eac | ||
|
|
37b1846363 | ||
|
|
da3e6bc3af | ||
|
|
a35ea2ba66 | ||
|
|
b2fa2e40f4 | ||
|
|
babecb5170 | ||
|
|
6e0e643337 | ||
|
|
2a59faa6b4 | ||
|
|
9d7d44afda | ||
|
|
8f1e67da3a | ||
|
|
c09f863d58 | ||
|
|
3c8c2a3feb | ||
|
|
4c4169e245 | ||
|
|
34c5cb1e94 | ||
|
|
f5fca46e5b | ||
|
|
48e3c24c6e | ||
|
|
e9c94876c0 | ||
|
|
fcd6c8ea7d | ||
|
|
81726ce7b2 | ||
|
|
6349c60bdb | ||
|
|
74cf119444 | ||
|
|
ba6ac2e32e | ||
|
|
1498656a43 | ||
|
|
70a2a3993b | ||
|
|
5152d5de12 | ||
|
|
2999e539b3 | ||
|
|
8ce432256c | ||
|
|
ddbfbac802 | ||
|
|
1d6a7ee1fa | ||
|
|
0d97f0447a | ||
|
|
36a13b60fe | ||
|
|
64f4fcf829 | ||
|
|
1ff7c84d82 | ||
|
|
da623d4d34 | ||
|
|
2efb630640 | ||
|
|
affa492ce3 | ||
|
|
5ae3a5b5ee | ||
|
|
5716ab2445 | ||
|
|
a66a04456b | ||
|
|
e97a78eaeb | ||
|
|
57f41a908a | ||
|
|
f42291a428 | ||
|
|
4fa9adfc7d | ||
|
|
3e2e581e52 | ||
|
|
2a3c34af95 | ||
|
|
170b79e4cf | ||
|
|
c15b8bb951 | ||
|
|
17c47f7d89 | ||
|
|
a406287215 | ||
|
|
a73b050a4d | ||
|
|
224fb2e887 | ||
|
|
96d4131614 | ||
|
|
cda00d5238 | ||
|
|
dea385ad44 | ||
|
|
21973401fb | ||
|
|
d153ad9bf7 | ||
|
|
2ce8ba6295 | ||
|
|
572130d349 | ||
|
|
f0cb049266 | ||
|
|
cab5af6645 | ||
|
|
e77b923dc1 | ||
|
|
8325c06ca3 | ||
|
|
b66f56bc12 | ||
|
|
2de2926d79 | ||
|
|
fd13e91aac | ||
|
|
cf7d4c7720 | ||
|
|
b1a76e97ce | ||
|
|
7d8036e3eb | ||
|
|
c7344a7624 | ||
|
|
42253e4e50 | ||
|
|
64acf372d6 | ||
|
|
80f4238618 | ||
|
|
2fe54c46c1 | ||
|
|
266b046b9e | ||
|
|
f311841df2 | ||
|
|
26319ff3c8 | ||
|
|
e719bb3b70 | ||
|
|
d0255c1e33 | ||
|
|
f66241d12f | ||
|
|
52f4c74908 | ||
|
|
eeb6904c2d | ||
|
|
6fd37c62b8 | ||
|
|
16b761e498 | ||
|
|
9b1e702c64 | ||
|
|
f10fa9a48e | ||
|
|
de91346155 | ||
|
|
629a442ff7 | ||
|
|
be54d782e5 | ||
|
|
029c1b0704 | ||
|
|
36cfb7e20f | ||
|
|
02046a525e | ||
|
|
c9a998da0b | ||
|
|
7fc192e466 | ||
|
|
eae8b79b2e | ||
|
|
08c6cf31a0 | ||
|
|
dfb7f2320c | ||
|
|
c53b14f88f | ||
|
|
c5680f6c26 | ||
|
|
d2a1385123 | ||
|
|
feda1b0426 | ||
|
|
b97cc46a1e | ||
|
|
41a8101b54 | ||
|
|
676a2b18b3 | ||
|
|
df7477929b | ||
|
|
c1ebe14c50 | ||
|
|
2b478e7a13 | ||
|
|
3bb1d93f3e | ||
|
|
703c4b7685 | ||
|
|
d7e9776a3c | ||
|
|
3cb29b63fe | ||
|
|
e7f4a36ec9 | ||
|
|
be78862e49 | ||
|
|
64ae5eeb89 | ||
|
|
78bdc42534 | ||
|
|
68c81734e8 | ||
|
|
bf081d1ebe | ||
|
|
3de407842e | ||
|
|
a4a9e45fe0 | ||
|
|
f9c9480434 | ||
|
|
eb644ad2f2 | ||
|
|
5186e193a8 | ||
|
|
55d947fb39 | ||
|
|
77f1262ff5 | ||
|
|
47bf8f9c89 | ||
|
|
4ca4f28d1c | ||
|
|
b6565ce2bb | ||
|
|
0704e1f556 | ||
|
|
87c16d7bc3 | ||
|
|
cc66820e7b | ||
|
|
a478f60a39 | ||
|
|
5637f12d3a | ||
|
|
27c28b17af | ||
|
|
c55b169488 | ||
|
|
9b584d69ff | ||
|
|
05c090445a | ||
|
|
597aeb74f4 | ||
|
|
b7191a9c2e | ||
|
|
c686030014 | ||
|
|
eed1078f06 | ||
|
|
051a3c43b2 | ||
|
|
a152813535 | ||
|
|
decea5acfc | ||
|
|
4c2dfb0f92 | ||
|
|
a24bf077ce | ||
|
|
2d82f50789 | ||
|
|
3e13776252 | ||
|
|
463bb32872 | ||
|
|
99dcee80cd | ||
|
|
c418dc41dd | ||
|
|
c0678c410d | ||
|
|
081359f7e0 | ||
|
|
cc0cfe4f8c | ||
|
|
dc87905f05 | ||
|
|
9c31047d52 | ||
|
|
91d196ddea | ||
|
|
78db5cab39 | ||
|
|
689e72e5ec | ||
|
|
bc9e97f67c | ||
|
|
19550c23ed | ||
|
|
2d09df2d87 | ||
|
|
2dfebdd2e4 | ||
|
|
ebec143c9c | ||
|
|
aa68a6316a | ||
|
|
27a37581e4 | ||
|
|
091622f8cf | ||
|
|
87caae077c | ||
|
|
a146691773 | ||
|
|
9fce103b11 | ||
|
|
2aa0ae89fc | ||
|
|
0a16f6bf44 | ||
|
|
0f49e6e100 | ||
|
|
d954b4a5df | ||
|
|
e762778fc6 | ||
|
|
4cced50857 | ||
|
|
26c900d8e2 | ||
|
|
63461343ba | ||
|
|
931abc522b | ||
|
|
054592eed3 | ||
|
|
82b2307b51 | ||
|
|
b1dba73842 | ||
|
|
babbf5f8a6 | ||
|
|
39c7da79ab | ||
|
|
938bd32915 | ||
|
|
f8e1ace311 | ||
|
|
05e2e22e1d | ||
|
|
9054c49b3e | ||
|
|
5d5468603f | ||
|
|
4c6979cfa1 | ||
|
|
714a4be2b0 | ||
|
|
b935443f96 | ||
|
|
d3a3f01f20 | ||
|
|
56b0fea04a | ||
|
|
2605140166 | ||
|
|
8b9611e145 | ||
|
|
21057038d1 | ||
|
|
e587d4ba19 | ||
|
|
14da201c8d | ||
|
|
f5584a5037 | ||
|
|
157f8e95d7 | ||
|
|
fb83126f37 | ||
|
|
964def0c45 | ||
|
|
650d7cc939 | ||
|
|
bfb4c46bd0 | ||
|
|
149c8c763d | ||
|
|
fdbcace48c | ||
|
|
3cfe0517a8 | ||
|
|
9a32a47146 | ||
|
|
fd83a71a56 | ||
|
|
070190dd31 |
68
.bash_profile
Normal file
68
.bash_profile
Normal file
@@ -0,0 +1,68 @@
|
||||
# These are aliases that will make your life simple when you're building OneUptime
|
||||
|
||||
# Make directory and change directory at the same time.
|
||||
mkcdir ()
|
||||
{
|
||||
mkdir -p -- "$1" &&
|
||||
cd -P -- "$1"
|
||||
}
|
||||
|
||||
# Git aliases
|
||||
alias g="git"
|
||||
alias gs="git status"
|
||||
alias ga="git add"
|
||||
alias gc="git checkout"
|
||||
alias gb="git branch"
|
||||
alias gp="git pull"
|
||||
alias gpo="git push origin"
|
||||
alias gl="git log"
|
||||
alias gd="git diff"
|
||||
alias gm="git merge"
|
||||
|
||||
# Kubernetes aliases
|
||||
alias k="kubectl"
|
||||
alias kg="kubectl get"
|
||||
alias kd="kubectl describe"
|
||||
alias kc="kubectl create"
|
||||
alias kdel="kubectl delete"
|
||||
alias klo="kubectl logs"
|
||||
alias klof="kubectl logs -f"
|
||||
alias kex="kubectl exec"
|
||||
alias kexi="kubectl exec -it"
|
||||
|
||||
# Docker aliases
|
||||
alias d="docker"
|
||||
alias dc="docker compose"
|
||||
alias dcu="docker compose up"
|
||||
alias dcd="docker compose down"
|
||||
|
||||
# Node aliases
|
||||
alias n="npm"
|
||||
alias ni="npm install"
|
||||
alias nis="npm install --save"
|
||||
alias nid="npm install --save-dev"
|
||||
alias nr="npm run"
|
||||
alias nt="npm test"
|
||||
alias ns="npm start"
|
||||
alias nb="npm build"
|
||||
|
||||
# Rust aliases
|
||||
alias c="cargo"
|
||||
alias cb="cargo build"
|
||||
alias cr="cargo run"
|
||||
|
||||
# OneUptime Specific Aliases
|
||||
# --------------------------
|
||||
|
||||
alias nrd="npm run dev"
|
||||
alias nrl="npm run logs"
|
||||
alias nrb="npm run build"
|
||||
alias nrfb="npm run force-build"
|
||||
alias nrps="npm run ps-dev"
|
||||
|
||||
# OneUptime LLM Server
|
||||
alias nrfbl="npm run force-build-llm"
|
||||
alias nrdl="npm run dev-llm"
|
||||
alias nrll="npm run logs-llm"
|
||||
alias nrbl="npm run build-llm"
|
||||
|
||||
@@ -56,5 +56,4 @@ settings.json
|
||||
|
||||
GoSDK/tester/
|
||||
|
||||
Llama/Models/*
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
*/node_modules/*
|
||||
*/build/*
|
||||
*/coverage/*
|
||||
|
||||
*/dist/*
|
||||
|
||||
*/public/*
|
||||
*/views/*
|
||||
|
||||
*fonts*
|
||||
*logos*
|
||||
|
||||
.*
|
||||
*.png
|
||||
*.sh
|
||||
*.txt
|
||||
*.snap
|
||||
*.enc
|
||||
Dockerfile
|
||||
CHANGELOG
|
||||
LICENSE
|
||||
|
||||
marketing/*/*
|
||||
licenses/*
|
||||
certifications/*
|
||||
ApiReference/public/assets/*
|
||||
JavaScriptSDK/src/cli/server-monitor/out/scripts/prettify/*
|
||||
_test/*
|
||||
216
.eslintrc.json
216
.eslintrc.json
@@ -1,216 +0,0 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 8,
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true,
|
||||
"jsx": true,
|
||||
"tsx": true,
|
||||
"spread": true
|
||||
},
|
||||
"sourceType": "module",
|
||||
"project": [
|
||||
"./tsconfig.json"
|
||||
]
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"jquery": true,
|
||||
"es6": true,
|
||||
"jest": true,
|
||||
"jasmine": true
|
||||
},
|
||||
"plugins": [
|
||||
"react",
|
||||
"jsx-a11y",
|
||||
"progress",
|
||||
"@typescript-eslint",
|
||||
"unused-imports"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"globals": {
|
||||
"describe": true,
|
||||
"context": true,
|
||||
"before": true,
|
||||
"beforeEach": true,
|
||||
"after": true,
|
||||
"afterEach": true,
|
||||
"it": true,
|
||||
"expect": true,
|
||||
"workbox": true,
|
||||
"importScripts": true,
|
||||
"$TSFixMe": true,
|
||||
"NodeJS": true,
|
||||
"JSX": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"rules": {
|
||||
"no-fallthrough": "error",
|
||||
"no-unreachable": "error",
|
||||
"no-cond-assign": "error",
|
||||
"valid-typeof": "error",
|
||||
"no-func-assign": "error",
|
||||
"curly": "error",
|
||||
"no-extra-semi": "error",
|
||||
"no-else-return": "error",
|
||||
"no-div-regex": "error",
|
||||
"no-octal": "error",
|
||||
"no-extra-bind": "error",
|
||||
"unicode-bom": "error",
|
||||
"no-extra-boolean-cast": "error",
|
||||
"wrap-regex": "error",
|
||||
"wrap-iife": "error",
|
||||
"yield-star-spacing": "error",
|
||||
"no-implicit-coercion": "error",
|
||||
"no-extra-label": "error",
|
||||
"multiline-comment-style": "off",
|
||||
"no-lonely-if": "error",
|
||||
"no-floating-decimal": "error",
|
||||
"eqeqeq": "error",
|
||||
"dot-notation": "off", // Off because it messes up with typescript compiler.
|
||||
"@typescript-eslint/dot-notation": "off", //temp off.
|
||||
"progress/activate": 1,
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"@typescript-eslint/no-empty-interface": [
|
||||
"error",
|
||||
{
|
||||
"allowSingleExtends": true
|
||||
}
|
||||
],
|
||||
// https://www.npmjs.com/package/eslint-plugin-unused-imports
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-extra-non-null-assertion": "error",
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/await-thenable": "error",
|
||||
"@typescript-eslint/no-non-null-asserted-optional-chain": "error",
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"error"
|
||||
],
|
||||
"no-console": "error",
|
||||
"no-undef": "error",
|
||||
"no-empty": "error",
|
||||
"no-control-regex": "off",
|
||||
"prefer-arrow-callback": "error",
|
||||
"constructor-super": "error",
|
||||
"no-case-declarations": "error",
|
||||
"no-mixed-spaces-and-tabs": "error",
|
||||
"no-useless-escape": "error",
|
||||
"prettier/prettier": "error",
|
||||
"react/jsx-no-undef": "error",
|
||||
"react/jsx-no-bind": [
|
||||
"error",
|
||||
{
|
||||
"allowArrowFunctions": true,
|
||||
"allowBind": false,
|
||||
"ignoreRefs": false
|
||||
}
|
||||
],
|
||||
"react/no-children-prop": "error",
|
||||
"react/no-deprecated": "error",
|
||||
"react/boolean-prop-naming": "error",
|
||||
"react/no-is-mounted": "error",
|
||||
"react/no-find-dom-node": "error",
|
||||
"one-var-declaration-per-line": "error",
|
||||
"arrow-parens": "error",
|
||||
"arrow-body-style": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"@typescript-eslint/typedef": [
|
||||
"error",
|
||||
{
|
||||
"arrowParameter": true,
|
||||
"variableDeclaration": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/strict-boolean-expressions": "off", //Need to enable this very soon
|
||||
"@typescript-eslint/explicit-function-return-type": [
|
||||
"error",
|
||||
{
|
||||
"allowExpressions": true
|
||||
}
|
||||
],
|
||||
"react/no-did-update-set-state": "error",
|
||||
"react/no-unknown-property": "error",
|
||||
"react/no-unused-prop-types": "error",
|
||||
"react/jsx-no-duplicate-props": "error",
|
||||
"react/no-unused-state": "error",
|
||||
"react/jsx-uses-vars": "error",
|
||||
"react/prop-types": "error",
|
||||
"react/react-in-jsx-scope": "error",
|
||||
"react/no-string-refs": "error",
|
||||
"jsx-a11y/href-no-hash": [
|
||||
0
|
||||
],
|
||||
"react/no-unescaped-entities": "error",
|
||||
"react/display-name": "error",
|
||||
"react/jsx-pascal-case": "error",
|
||||
"array-callback-return": "error",
|
||||
"no-loop-func": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
"no-promise-executor-return": "error",
|
||||
"capitalized-comments": "off", // this is turned off because come commented code should not be capitalized.
|
||||
"for-direction": "error",
|
||||
"getter-return": "error",
|
||||
"jsx-a11y/anchor-is-valid": "error",
|
||||
"no-async-promise-executor": "error",
|
||||
"prefer-const": [
|
||||
"error",
|
||||
{
|
||||
"destructuring": "any",
|
||||
"ignoreReadBeforeAssign": false
|
||||
}
|
||||
],
|
||||
"no-var": "error",
|
||||
"object-curly-spacing": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"no-unneeded-ternary": "error",
|
||||
"@typescript-eslint/ban-types": [
|
||||
"error",
|
||||
{
|
||||
"types": {
|
||||
"String": true,
|
||||
"Boolean": true,
|
||||
"Number": true,
|
||||
"Symbol": false,
|
||||
"{}": true,
|
||||
"Object": true,
|
||||
"object": true,
|
||||
"Function": true
|
||||
},
|
||||
"extendDefaults": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "18.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: 'Bug: <Title of the issue>'
|
||||
title: '<Title of the issue>'
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: 'Enhancement: <Title of the issue>'
|
||||
title: '<Title of the issue>'
|
||||
labels: 'enhancement'
|
||||
assignees: ''
|
||||
|
||||
|
||||
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
@@ -72,6 +72,22 @@ jobs:
|
||||
- name: build docker image
|
||||
run: sudo docker build -f ./App/Dockerfile .
|
||||
|
||||
|
||||
docker-build-copilot:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
run: npm run prerun
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
run: sudo docker build -f ./Copilot/Dockerfile .
|
||||
|
||||
docker-build-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
language: [ 'javascript', 'typescript', 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
|
||||
84
.github/workflows/compile.yml
vendored
84
.github/workflows/compile.yml
vendored
@@ -20,9 +20,6 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
- run: cd Accounts && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-isolated-vm:
|
||||
@@ -35,35 +32,8 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd IsolatedVM && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-common-server:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-common-ui:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonUI && npm install --force && npm run compile && npm run dep-check
|
||||
|
||||
compile-common:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
@@ -73,8 +43,7 @@ jobs:
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install && npm run compile && npm run dep-check
|
||||
- run: cd Common && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-app:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -86,11 +55,21 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
|
||||
- run: cd App && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-copilot:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Copilot && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-nginx:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
@@ -101,9 +80,7 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
|
||||
- run: cd Nginx && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-infrastructure-agent:
|
||||
@@ -127,9 +104,7 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
|
||||
- run: cd AdminDashboard && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-dashboard:
|
||||
@@ -142,25 +117,10 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
|
||||
- run: cd Dashboard && npm install && npm run compile && npm run dep-check
|
||||
|
||||
|
||||
compile-model:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd Model && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
@@ -183,8 +143,6 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd Probe && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-ingestor:
|
||||
@@ -197,8 +155,6 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd Ingestor && npm install && npm run compile && npm run dep-check
|
||||
|
||||
|
||||
@@ -212,9 +168,7 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
|
||||
- run: cd StatusPage && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-test-server:
|
||||
@@ -227,6 +181,4 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd TestServer && npm install && npm run compile && npm run dep-check
|
||||
@@ -1,16 +0,0 @@
|
||||
name: Misc / Dependabot Automerge
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ahmadnassri/action-dependabot-auto-merge@v2
|
||||
with:
|
||||
target: minor
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
31
.github/workflows/playwright.yml.skip
vendored
31
.github/workflows/playwright.yml.skip
vendored
@@ -1,31 +0,0 @@
|
||||
name: Playwright Tests
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master ]
|
||||
pull_request:
|
||||
branches: [ main, master ]
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
BASE_URL: http://localhost
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Run Server in Docker
|
||||
run: npm run dev
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
- name: Run Playwright tests
|
||||
run: cd Playwright && npm install && npx playwright install && npx playwright test
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
341
.github/workflows/release.yml
vendored
341
.github/workflows/release.yml
vendored
@@ -18,35 +18,12 @@ jobs:
|
||||
token: ${{secrets.github_token}}
|
||||
- run: echo "Build number is ${{ steps.buildnumber.outputs.build_number }}"
|
||||
|
||||
github-release:
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/release'
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
- run: echo "${{needs.generate-build-number.outputs.build_number}}"
|
||||
- name: "Build Changelog"
|
||||
id: build_changelog
|
||||
uses: mikepenz/release-changelog-builder-action@v4.2.0
|
||||
with:
|
||||
configuration: "./Scripts/Release/ChangelogConfig.json"
|
||||
- run: echo "Changelog:"
|
||||
- run: echo "${{steps.build_changelog.outputs.changelog}}"
|
||||
- uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: "7.0.${{needs.generate-build-number.outputs.build_number}}"
|
||||
artifactErrorsFailBuild: true
|
||||
body: |
|
||||
${{steps.build_changelog.outputs.changelog}}
|
||||
|
||||
|
||||
|
||||
helm-chart-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
env:
|
||||
CI_COMMIT_AUTHOR: Continuous Integration
|
||||
steps:
|
||||
@@ -92,7 +69,7 @@ jobs:
|
||||
git push origin master
|
||||
|
||||
nginx-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -152,7 +129,7 @@ jobs:
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
e2e-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -212,7 +189,7 @@ jobs:
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
isolated-vm-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -272,7 +249,7 @@ jobs:
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
test-server-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -332,7 +309,7 @@ jobs:
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
otel-collector-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -394,7 +371,7 @@ jobs:
|
||||
|
||||
|
||||
status-page-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -454,7 +431,7 @@ jobs:
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
test-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -514,7 +491,7 @@ jobs:
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
ingestor-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -574,7 +551,7 @@ jobs:
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
probe-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -635,7 +612,7 @@ jobs:
|
||||
|
||||
|
||||
haraka-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -695,7 +672,7 @@ jobs:
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
admin-dashboard-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -756,7 +733,7 @@ jobs:
|
||||
|
||||
|
||||
dashboard-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -816,7 +793,7 @@ jobs:
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
app-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -875,8 +852,69 @@ jobs:
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
copilot-docker-image-deploy:
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
oneuptime/copilot
|
||||
ghcr.io/oneuptime/copilot
|
||||
tags: |
|
||||
type=raw,value=release,enable=true
|
||||
type=semver,value=7.0.${{needs.generate-build-number.outputs.build_number}},pattern={{version}},enable=true
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Generate Dockerfile from Dockerfile.tpl
|
||||
run: npm run prerun
|
||||
|
||||
# Build and deploy app.
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
file: ./Copilot/Dockerfile
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
accounts-docker-image-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
@@ -938,7 +976,7 @@ jobs:
|
||||
|
||||
publish-npm-packages:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [generate-build-number]
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
NPM_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
|
||||
@@ -950,9 +988,228 @@ jobs:
|
||||
run: npm run prerun
|
||||
- name: Publish Infrastructure Agent
|
||||
run: bash ./Scripts/NPM/PublishAllPackages.sh
|
||||
|
||||
|
||||
llm-docker-image-deploy:
|
||||
needs: generate-build-number
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
# Docker compose needs a lot of space to build images, so we need to free up some space first in the GitHub Actions runner
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
|
||||
- name: Docker Meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
oneuptime/llm
|
||||
ghcr.io/oneuptime/llm
|
||||
tags: |
|
||||
type=raw,value=release,enable=true
|
||||
type=semver,value=7.0.${{needs.generate-build-number.outputs.build_number}},pattern={{version}},enable=true
|
||||
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
|
||||
# - name: Setup Git LFS
|
||||
# run: git lfs install
|
||||
|
||||
# # Cannot do this, no space on the gitHub standard runner. We need to use the large runner which is selfhosted
|
||||
# - name: Download the Model from Hugging Face
|
||||
# run: mkdir -p ./LLM/Models && cd ./LLM/Models && git clone https://${{ secrets.HUGGING_FACE_USERNAME }}:${{ secrets.HUGGING_FACE_PASSWORD }}@huggingface.co/meta-llama/Meta-Llama-3-8B-Instruct
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Generate Dockerfile from Dockerfile.tpl
|
||||
run: npm run prerun
|
||||
|
||||
# Build and deploy nginx.
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
file: ./LLM/Dockerfile
|
||||
context: ./LLM
|
||||
# arm64 is not supported by the base image of the LLM
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
|
||||
test-e2e-release-saas:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [copilot-docker-image-deploy, accounts-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, dashboard-docker-image-deploy, haraka-docker-image-deploy, ingestor-docker-image-deploy, isolated-vm-docker-image-deploy, otel-collector-docker-image-deploy, probe-docker-image-deploy, status-page-docker-image-deploy, test-docker-image-deploy, test-server-docker-image-deploy, publish-npm-packages, e2e-docker-image-deploy, helm-chart-deploy, generate-build-number, nginx-docker-image-deploy]
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
# Docker compose needs a lot of space to build images, so we need to free up some space first in the GitHub Actions runner
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: npm run prerun && bash ./Tests/Scripts/enable-billing-env-var.sh
|
||||
- name: Start Server with release tag
|
||||
run: npm run start
|
||||
- name: Wait for server to start
|
||||
run: bash ./Tests/Scripts/status-check.sh http://localhost
|
||||
- name: Run E2E Tests. Run docker container e2e in docker compose file
|
||||
run: export $(grep -v '^#' config.env | xargs) && docker compose -f docker-compose.dev.yml up --exit-code-from e2e --abort-on-container-exit e2e || (docker compose -f docker-compose.dev.yml logs e2e && exit 1)
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v4
|
||||
# Run this on failure
|
||||
if: failure()
|
||||
with:
|
||||
# Name of the artifact to upload.
|
||||
# Optional. Default is 'artifact'
|
||||
name: test-results
|
||||
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
# Required.
|
||||
path: |
|
||||
./E2E
|
||||
|
||||
|
||||
# Duration after which artifact will expire in days. 0 means using default retention.
|
||||
# Minimum 1 day.
|
||||
# Maximum 90 days unless changed from the repository settings page.
|
||||
# Optional. Defaults to repository settings.
|
||||
retention-days: 7
|
||||
|
||||
|
||||
test-e2e-release-self-hosted:
|
||||
runs-on: ubuntu-latest
|
||||
# After all the jobs runs
|
||||
needs: [copilot-docker-image-deploy, llm-docker-image-deploy, accounts-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, dashboard-docker-image-deploy, haraka-docker-image-deploy, ingestor-docker-image-deploy, isolated-vm-docker-image-deploy, otel-collector-docker-image-deploy, probe-docker-image-deploy, status-page-docker-image-deploy, test-docker-image-deploy, test-server-docker-image-deploy, publish-npm-packages, e2e-docker-image-deploy, helm-chart-deploy, generate-build-number, nginx-docker-image-deploy]
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
# Docker compose needs a lot of space to build images, so we need to free up some space first in the GitHub Actions runner
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: npm run prerun
|
||||
- name: Start Server with release tag
|
||||
run: npm run start
|
||||
- name: Wait for server to start
|
||||
run: bash ./Tests/Scripts/status-check.sh http://localhost
|
||||
- name: Run E2E Tests. Run docker container e2e in docker compose file
|
||||
run: export $(grep -v '^#' config.env | xargs) && docker compose -f docker-compose.dev.yml up --exit-code-from e2e --abort-on-container-exit e2e || (docker compose -f docker-compose.dev.yml logs e2e && exit 1)
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v4
|
||||
# Run this on failure
|
||||
if: failure()
|
||||
with:
|
||||
# Name of the artifact to upload.
|
||||
# Optional. Default is 'artifact'
|
||||
name: test-results
|
||||
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
# Required.
|
||||
path: |
|
||||
./E2E
|
||||
|
||||
|
||||
# Duration after which artifact will expire in days. 0 means using default retention.
|
||||
# Minimum 1 day.
|
||||
# Maximum 90 days unless changed from the repository settings page.
|
||||
# Optional. Defaults to repository settings.
|
||||
retention-days: 7
|
||||
|
||||
github-release:
|
||||
needs: [test-e2e-release-saas, test-e2e-release-self-hosted, generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/release'
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
- run: echo "${{needs.generate-build-number.outputs.build_number}}"
|
||||
- name: "Build Changelog"
|
||||
id: build_changelog
|
||||
uses: mikepenz/release-changelog-builder-action@v4.2.0
|
||||
with:
|
||||
configuration: "./Scripts/Release/ChangelogConfig.json"
|
||||
- run: echo "Changelog:"
|
||||
- run: echo "${{steps.build_changelog.outputs.changelog}}"
|
||||
- uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: "7.0.${{needs.generate-build-number.outputs.build_number}}"
|
||||
artifactErrorsFailBuild: true
|
||||
body: |
|
||||
${{steps.build_changelog.outputs.changelog}}
|
||||
|
||||
|
||||
infrastructure-agent-deploy:
|
||||
needs: [generate-build-number, github-release]
|
||||
needs: [github-release, generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -983,3 +1240,5 @@ jobs:
|
||||
draft: false
|
||||
prerelease: false
|
||||
tag_name: 7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
|
||||
32
.github/workflows/reliability-copilot.yml
vendored
Normal file
32
.github/workflows/reliability-copilot.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
name: "OneUptime Reliability Copilot"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
# Run every day at midnight UTC
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze Code
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
# Run Reliability Copilot in Docker Container
|
||||
- name: Run Copilot
|
||||
run: |
|
||||
docker run --rm \
|
||||
-e ONEUPTIME_URL="https://test.oneuptime.com" \
|
||||
-e ONEUPTIME_REPOSITORY_SECRET_KEY="${{ secrets.COPILOT_ONEUPTIME_REPOSITORY_SECRET_KEY }}" \
|
||||
-e CODE_REPOSITORY_PASSWORD="${{ github.token }}" \
|
||||
-e CODE_REPOSITORY_USERNAME="simlarsen" \
|
||||
-e OPENAI_API_KEY="${{ secrets.OPENAI_API_KEY }}" \
|
||||
--net=host oneuptime/copilot:test
|
||||
259
.github/workflows/test-release.yaml
vendored
259
.github/workflows/test-release.yaml
vendored
@@ -6,6 +6,7 @@ on:
|
||||
- "master"
|
||||
|
||||
jobs:
|
||||
|
||||
generate-build-number:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
@@ -18,6 +19,92 @@ jobs:
|
||||
token: ${{secrets.github_token}}
|
||||
- run: echo "Build number is ${{ steps.buildnumber.outputs.build_number }}"
|
||||
|
||||
llm-docker-image-deploy:
|
||||
needs: generate-build-number
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
# Docker compose needs a lot of space to build images, so we need to free up some space first in the GitHub Actions runner
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
|
||||
- name: Docker Meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
oneuptime/llm
|
||||
ghcr.io/oneuptime/llm
|
||||
tags: |
|
||||
type=raw,value=test,enable=true
|
||||
type=semver,value=7.0.${{needs.generate-build-number.outputs.build_number}}-test,pattern={{version}},enable=true
|
||||
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
|
||||
# - name: Setup Git LFS
|
||||
# run: git lfs install
|
||||
|
||||
# # Cannot do this, no space on the gitHub standard runner. We need to use the large runner which is selfhosted
|
||||
# - name: Download the Model from Hugging Face
|
||||
# run: mkdir -p ./LLM/Models && cd ./LLM/Models && git clone https://${{ secrets.HUGGING_FACE_USERNAME }}:${{ secrets.HUGGING_FACE_PASSWORD }}@huggingface.co/meta-llama/Meta-Llama-3-8B-Instruct
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Generate Dockerfile from Dockerfile.tpl
|
||||
run: npm run prerun
|
||||
|
||||
# Build and deploy nginx.
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
file: ./LLM/Dockerfile
|
||||
context: ./LLM
|
||||
# arm64 is not supported by the base image of the LLM
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
nginx-docker-image-deploy:
|
||||
needs: generate-build-number
|
||||
@@ -876,10 +963,72 @@ jobs:
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
copilot-docker-image-deploy:
|
||||
needs: generate-build-number
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
oneuptime/copilot
|
||||
ghcr.io/oneuptime/copilot
|
||||
tags: |
|
||||
type=raw,value=test,enable=true
|
||||
type=semver,value=7.0.${{needs.generate-build-number.outputs.build_number}}-test,pattern={{version}},enable=true
|
||||
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Generate Dockerfile from Dockerfile.tpl
|
||||
run: npm run prerun
|
||||
|
||||
# Build and deploy accounts.
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
file: ./Copilot/Dockerfile
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
test-helm-chart:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [isolated-vm-docker-image-deploy, test-server-docker-image-deploy, test-docker-image-deploy, ingestor-docker-image-deploy, probe-docker-image-deploy, haraka-docker-image-deploy, dashboard-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, accounts-docker-image-deploy, otel-collector-docker-image-deploy, status-page-docker-image-deploy, nginx-docker-image-deploy, e2e-docker-image-deploy]
|
||||
needs: [llm-docker-image-deploy, copilot-docker-image-deploy, isolated-vm-docker-image-deploy, test-server-docker-image-deploy, test-docker-image-deploy, ingestor-docker-image-deploy, probe-docker-image-deploy, haraka-docker-image-deploy, dashboard-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, accounts-docker-image-deploy, otel-collector-docker-image-deploy, status-page-docker-image-deploy, nginx-docker-image-deploy, e2e-docker-image-deploy]
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
@@ -888,3 +1037,111 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd HelmChart && cd Tests && bash index.sh
|
||||
|
||||
test-e2e-test-saas:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test-helm-chart]
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
# Docker compose needs a lot of space to build images, so we need to free up some space first in the GitHub Actions runner
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: npm run prerun && bash ./Tests/Scripts/change-release-to-test-tag.sh
|
||||
- name: Start Server with release tag
|
||||
run: npm run start
|
||||
- name: Wait for server to start
|
||||
run: bash ./Tests/Scripts/status-check.sh http://localhost
|
||||
- name: Run E2E Tests. Run docker container e2e in docker compose file
|
||||
run: export $(grep -v '^#' config.env | xargs) && docker compose -f docker-compose.dev.yml up --exit-code-from e2e --abort-on-container-exit e2e || (docker compose -f docker-compose.dev.yml logs e2e && exit 1)
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v4
|
||||
# Run this on failure
|
||||
if: failure()
|
||||
with:
|
||||
# Name of the artifact to upload.
|
||||
# Optional. Default is 'artifact'
|
||||
name: test-results
|
||||
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
# Required.
|
||||
path: |
|
||||
./E2E
|
||||
|
||||
|
||||
# Duration after which artifact will expire in days. 0 means using default retention.
|
||||
# Minimum 1 day.
|
||||
# Maximum 90 days unless changed from the repository settings page.
|
||||
# Optional. Defaults to repository settings.
|
||||
retention-days: 7
|
||||
|
||||
|
||||
test-e2e-test-self-hosted:
|
||||
runs-on: ubuntu-latest
|
||||
# After all the jobs runs
|
||||
needs: [test-helm-chart]
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
# Docker compose needs a lot of space to build images, so we need to free up some space first in the GitHub Actions runner
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: npm run prerun && bash ./Tests/Scripts/change-release-to-test-tag.sh
|
||||
- name: Start Server with release tag
|
||||
run: npm run start
|
||||
- name: Wait for server to start
|
||||
run: bash ./Tests/Scripts/status-check.sh http://localhost
|
||||
- name: Run E2E Tests. Run docker container e2e in docker compose file
|
||||
run: export $(grep -v '^#' config.env | xargs) && docker compose -f docker-compose.dev.yml up --exit-code-from e2e --abort-on-container-exit e2e || (docker compose -f docker-compose.dev.yml logs e2e && exit 1)
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v4
|
||||
# Run this on failure
|
||||
if: failure()
|
||||
with:
|
||||
# Name of the artifact to upload.
|
||||
# Optional. Default is 'artifact'
|
||||
name: test-results
|
||||
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
# Required.
|
||||
path: |
|
||||
./E2E
|
||||
|
||||
|
||||
# Duration after which artifact will expire in days. 0 means using default retention.
|
||||
# Minimum 1 day.
|
||||
# Maximum 90 days unless changed from the repository settings page.
|
||||
# Optional. Defaults to repository settings.
|
||||
retention-days: 7
|
||||
|
||||
|
||||
|
||||
|
||||
24
.github/workflows/test.common-server.yaml
vendored
24
.github/workflows/test.common-server.yaml
vendored
@@ -1,24 +0,0 @@
|
||||
name: Common Server Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'hotfix-*' # excludes hotfix branches
|
||||
- 'release'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonServer && bash test-setup.sh
|
||||
- run: export $(grep -v '^#' config.env | xargs) && cd CommonServer && rm -rf build && npm run test
|
||||
7
.github/workflows/test.common.yaml
vendored
7
.github/workflows/test.common.yaml
vendored
@@ -12,11 +12,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
BILLING_PRIVATE_KEY: ${{secrets.TEST_BILLING_PRIVATE_KEY}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Model && npm install
|
||||
- run: cd Common && npm install && npm run test
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && bash test-setup.sh
|
||||
- run: cd Common && npm install && rm -rf build && npm run test
|
||||
|
||||
23
.github/workflows/test.commonui.yaml
vendored
23
.github/workflows/test.commonui.yaml
vendored
@@ -1,23 +0,0 @@
|
||||
name: CommonUI Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'hotfix-*' # excludes hotfix branches
|
||||
- 'release'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonUI && npm install --force && npm run test
|
||||
|
||||
12
.github/workflows/test.e2e.yaml
vendored
12
.github/workflows/test.e2e.yaml
vendored
@@ -33,9 +33,9 @@ jobs:
|
||||
- run: npm run prerun && bash ./Tests/Scripts/enable-billing-env-var.sh
|
||||
- run: npm run dev
|
||||
- name: Wait for server to start
|
||||
run: bash ./Tests/Scripts/status-check.sh http://localhost
|
||||
run: bash ./Tests/Scripts/status-check.sh http://localhost
|
||||
- name: Run E2E Tests. Run docker container e2e in docker compose file
|
||||
run: export $(grep -v '^#' config.env | xargs) && docker-compose -f docker-compose.dev.yml up --exit-code-from e2e --abort-on-container-exit e2e || (docker-compose -f docker-compose.dev.yml logs e2e && exit 1)
|
||||
run: export $(grep -v '^#' config.env | xargs) && docker compose -f docker-compose.dev.yml up --exit-code-from e2e --abort-on-container-exit e2e || (docker compose -f docker-compose.dev.yml logs e2e && exit 1)
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v4
|
||||
# Run this on failure
|
||||
@@ -48,15 +48,11 @@ jobs:
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
# Required.
|
||||
path: |
|
||||
./E2E/playwright-report
|
||||
./E2E/test-results
|
||||
./E2E
|
||||
|
||||
|
||||
# Duration after which artifact will expire in days. 0 means using default retention.
|
||||
# Minimum 1 day.
|
||||
# Maximum 90 days unless changed from the repository settings page.
|
||||
# Optional. Defaults to repository settings.
|
||||
retention-days: 7
|
||||
|
||||
|
||||
|
||||
retention-days: 7
|
||||
23
.github/workflows/test.model.yaml
vendored
23
.github/workflows/test.model.yaml
vendored
@@ -1,23 +0,0 @@
|
||||
name: Model Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'hotfix-*' # excludes hotfix branches
|
||||
- 'release'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd Model && npm install && npm run test
|
||||
|
||||
1
.github/workflows/test.probe.yaml
vendored
1
.github/workflows/test.probe.yaml
vendored
@@ -18,7 +18,6 @@ jobs:
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd Probe && npm install
|
||||
- run: cd Probe && npm run test
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -93,10 +93,9 @@ Haraka/dkim/keys/public_base64.txt
|
||||
|
||||
HelmChart/Values/*.values.yaml
|
||||
|
||||
Llama/Models/tokenizer*
|
||||
Llama/Models/llama*
|
||||
LLM/__pycache__/*
|
||||
|
||||
Llama/__pycache__/*
|
||||
LLM/Models/*
|
||||
|
||||
Examples/otel-dotnet/obj/*
|
||||
|
||||
|
||||
15
.oneuptime/README.md
Normal file
15
.oneuptime/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
## OneUptime Copilot
|
||||
|
||||
This folder contains the configuration files for the OneUptime Copilot. The Copilot is a tool that automatically improves your code. It can fix issues, improve code quality, and help you ship faster.
|
||||
|
||||
This folder has the following structure:
|
||||
|
||||
- `config.js`: The configuration file for the Copilot. You can customize the Copilot's behavior by changing this file.
|
||||
- `scripts`: A folder containing scripts that the Copilot runs. These are hooks that run at different stages of the Copilot's process.
|
||||
- `on-after-clone.sh`: A script that runs after the Copilot clones your repository.
|
||||
- `on-before-code-change.sh`: A script that runs before the Copilot makes changes to your code.
|
||||
- `on-after-code-change.sh`: A script that runs after the Copilot makes changes to your code.
|
||||
- `on-before-commit.sh`: A script that runs before the Copilot commits changes to your repository.
|
||||
- `on-after-commit.sh`: A script that runs after the Copilot commits changes to your repository.
|
||||
|
||||
|
||||
10
.oneuptime/config.js
Normal file
10
.oneuptime/config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// This is the configuration file for the oneuptime copilot.
|
||||
|
||||
const getCopilotConfig = () => {
|
||||
return {
|
||||
// The version of the schema for this configuration file.
|
||||
schemaVersion: '1.0',
|
||||
}
|
||||
}
|
||||
|
||||
export default getCopilotConfig;
|
||||
16
.oneuptime/scripts/on-after-clone.sh
Normal file
16
.oneuptime/scripts/on-after-clone.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
# Description: Copilot clones your repository and to improve your code.
|
||||
# This scirpt runs after the clone process is completed.
|
||||
# Some of the common tasks you can do here are:
|
||||
# 1. Install dependencies
|
||||
# 2. Run linting
|
||||
# 3. Run tests
|
||||
# 4. Run build
|
||||
# 5. Run any other command that you want to run after the clone process is completed.
|
||||
# If this script fails, copilot will not proceed with the next steps to improve your code.
|
||||
# This step is to ensure that the code is in a good state before we start improving it.
|
||||
# If you want to skip this script, you can keep this file empty.
|
||||
# It's highly recommended to run linting and tests in this script to ensure the code is in a good state.
|
||||
# This scirpt will run on ubuntu machine. So, make sure the commands you run are compatible with ubuntu.
|
||||
|
||||
npm install
|
||||
npm run lint
|
||||
13
.oneuptime/scripts/on-after-code-change.sh
Normal file
13
.oneuptime/scripts/on-after-code-change.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
# Description: Copilot will run this script after we make improvements to your code and write it to disk.
|
||||
# Some of the common tasks you can do here are:
|
||||
# 1. Run linting
|
||||
# 2. Run tests
|
||||
# 3. Run build
|
||||
# 4. Run any other command that you want to run after the code is changed.
|
||||
# If this script fails, copilot will not commit the changes to your repository.
|
||||
# This step is to ensure that the code is in a good state before we commit the changes.
|
||||
# If you want to skip this script, you can keep this file empty.
|
||||
# It's highly recommended to run linting and tests in this script to ensure the code is in a good state.
|
||||
# This scirpt will run on ubuntu machine. So, make sure the commands you run are compatible with ubuntu.
|
||||
|
||||
npm run fix
|
||||
1
.oneuptime/scripts/on-after-commit.sh
Normal file
1
.oneuptime/scripts/on-after-commit.sh
Normal file
@@ -0,0 +1 @@
|
||||
# Description: Copilot will run this script after the commit process is completed.
|
||||
9
.oneuptime/scripts/on-before-code-change.sh
Normal file
9
.oneuptime/scripts/on-before-code-change.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
# Description: Copilot will run this script before we make changes to your code.
|
||||
# Some of the common tasks you can do here are:
|
||||
# 1. Install dependencies
|
||||
# 2. Run any other command that you want to run before the code is changed.
|
||||
# If this script fails, copilot will not make any changes to the code.
|
||||
# This step is to ensure that the code is in a good state before we start making changes.
|
||||
# If you want to skip this script, you can keep this file empty.
|
||||
# It's highly recommended to run things like installing dependencies in this script.
|
||||
# This scirpt will run on ubuntu machine. So, make sure the commands you run are compatible with ubuntu.
|
||||
1
.oneuptime/scripts/on-before-commit.sh
Normal file
1
.oneuptime/scripts/on-before-commit.sh
Normal file
@@ -0,0 +1 @@
|
||||
# Description: Copilot will run this script before we commit the changes to your repository.
|
||||
@@ -51,6 +51,3 @@ licenses/*
|
||||
certifications/*
|
||||
ApiReference/public/assets/*
|
||||
JavaScriptSDK/src/cli/server-monitor/out/scripts/prettify/*
|
||||
|
||||
|
||||
CommonServer/Tests/TestingUtils/__mocks__/Stripe.mock.ts
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "avoid",
|
||||
"plugins": ["@trivago/prettier-plugin-sort-imports"],
|
||||
"importOrderSeparation": true,
|
||||
"importOrderSortSpecifiers": true,
|
||||
"importOrderParserPlugins": ["typescript", "decorators", "dynamicImport", "jsx"]
|
||||
}
|
||||
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@@ -19,6 +19,20 @@
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/TestServer",
|
||||
"name": "Copilot: Debug with Docker",
|
||||
"port": 9985,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"name": "Debug Infrastructure Agent",
|
||||
"type": "go",
|
||||
@@ -204,12 +218,12 @@
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"name": "CommonServer: Debug Tests",
|
||||
"name": "Common: Debug Tests",
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true,
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceRoot}/CommonServer",
|
||||
"cwd": "${workspaceRoot}/Common",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": [
|
||||
"run-script",
|
||||
|
||||
@@ -36,33 +36,17 @@ COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
# Set version in ./Model/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/Model/package.json
|
||||
RUN npm install
|
||||
COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
# Set version in ./CommonServer/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/CommonServer/package.json
|
||||
RUN npm install
|
||||
COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
|
||||
|
||||
# Install CommonUI
|
||||
|
||||
WORKDIR /usr/src/CommonUI
|
||||
COPY ./CommonUI/package*.json /usr/src/CommonUI/
|
||||
# Set version in ./CommonUI/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/CommonUI/package.json
|
||||
RUN npm install --force
|
||||
COPY ./CommonUI /usr/src/CommonUI
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
import { PromiseVoidFunction } from 'Common/Types/FunctionTypes';
|
||||
import Express, { ExpressApplication } from 'CommonServer/Utils/Express';
|
||||
import logger from 'CommonServer/Utils/Logger';
|
||||
import App from 'CommonServer/Utils/StartServer';
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import Express, { ExpressApplication } from "Common/Server/Utils/Express";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import App from "Common/Server/Utils/StartServer";
|
||||
|
||||
export const APP_NAME: string = 'accounts';
|
||||
export const APP_NAME: string = "accounts";
|
||||
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
|
||||
const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
try {
|
||||
// init the app
|
||||
await App.init({
|
||||
appName: APP_NAME,
|
||||
port: undefined,
|
||||
isFrontendApp: true,
|
||||
statusOptions: {
|
||||
liveCheck: async () => {},
|
||||
readyCheck: async () => {},
|
||||
},
|
||||
});
|
||||
// add default routes
|
||||
await App.addDefaultRoutes();
|
||||
} catch (err) {
|
||||
logger.error('App Init Failed:');
|
||||
logger.error(err);
|
||||
throw err;
|
||||
}
|
||||
try {
|
||||
// init the app
|
||||
await App.init({
|
||||
appName: APP_NAME,
|
||||
port: undefined,
|
||||
isFrontendApp: true,
|
||||
statusOptions: {
|
||||
liveCheck: async () => {},
|
||||
readyCheck: async () => {},
|
||||
},
|
||||
});
|
||||
// add default routes
|
||||
await App.addDefaultRoutes();
|
||||
} catch (err) {
|
||||
logger.error("App Init Failed:");
|
||||
logger.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
init().catch((err: Error) => {
|
||||
logger.error(err);
|
||||
logger.error('Exiting node process');
|
||||
process.exit(1);
|
||||
logger.error(err);
|
||||
logger.error("Exiting node process");
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
export default app;
|
||||
|
||||
8
Accounts/index.d.ts
vendored
8
Accounts/index.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
declare module '*.png';
|
||||
declare module '*.svg';
|
||||
declare module '*.jpg';
|
||||
declare module '*.gif';
|
||||
declare module "*.png";
|
||||
declare module "*.svg";
|
||||
declare module "*.jpg";
|
||||
declare module "*.gif";
|
||||
|
||||
130
Accounts/package-lock.json
generated
130
Accounts/package-lock.json
generated
@@ -9,13 +9,11 @@
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"CommonServer": "file:../CommonServer",
|
||||
"CommonUI": "file:../CommonUI",
|
||||
|
||||
"css-loader": "^6.11.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"Model": "file:../Model",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.23.1",
|
||||
@@ -39,25 +37,81 @@
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.24.6",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@nivo/core": "^0.87.0",
|
||||
"@nivo/line": "^0.87.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/context-zone": "^1.25.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.52.0",
|
||||
"@opentelemetry/instrumentation": "^0.52.0",
|
||||
"@opentelemetry/instrumentation-fetch": "^0.52.1",
|
||||
"@opentelemetry/instrumentation-xml-http-request": "^0.52.1",
|
||||
"@opentelemetry/resources": "^1.25.0",
|
||||
"@opentelemetry/sdk-trace-web": "^1.23.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.25.0",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"axios": "^1.6.8",
|
||||
"axios": "^1.7.2",
|
||||
"Common": "file:../Common",
|
||||
"crypto-js": "^4.1.1",
|
||||
"formik": "^2.4.6",
|
||||
"history": "^5.3.0",
|
||||
"json5": "^2.2.3",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"posthog-js": "^1.116.6",
|
||||
"posthog-js": "^1.139.6",
|
||||
"prop-types": "^15.8.1",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.3.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-big-calendar": "^1.13.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-dropzone": "^14.2.2",
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-highlight": "^0.15.0",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-router-dom": "^6.24.1",
|
||||
"react-select": "^5.4.0",
|
||||
"react-spinners": "^0.14.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactflow": "^11.11.4",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"slugify": "^1.6.5",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"tippy.js": "^6.3.7",
|
||||
"typeorm": "^0.3.20",
|
||||
"universal-cookie": "^4.0.4",
|
||||
"use-async-effect": "^2.2.6",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^17.0.22",
|
||||
"jest": "^27.5.1",
|
||||
"ts-jest": "^27.1.4"
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/jest": "^28.1.4",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "^17.0.45",
|
||||
"@types/react": "^18.2.38",
|
||||
"@types/react-beautiful-dnd": "^13.1.2",
|
||||
"@types/react-big-calendar": "^1.8.5",
|
||||
"@types/react-color": "^3.0.6",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"@types/react-toggle": "^4.0.3",
|
||||
"jest": "^28.1.1",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"react-test-renderer": "^18.2.0",
|
||||
"ts-jest": "^28.0.5"
|
||||
}
|
||||
},
|
||||
"../CommonServer": {
|
||||
@@ -78,6 +132,7 @@
|
||||
"@opentelemetry/sdk-metrics": "^1.21.0",
|
||||
"@opentelemetry/sdk-node": "^0.48.0",
|
||||
"@opentelemetry/sdk-trace-node": "^1.21.0",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"acme-client": "^5.3.0",
|
||||
"airtable": "^0.12.2",
|
||||
"bullmq": "^5.3.3",
|
||||
@@ -85,17 +140,19 @@
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"cron-parser": "^4.8.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dotenv": "^16.4.4",
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^4.19.2",
|
||||
"ioredis": "^5.3.2",
|
||||
"json2csv": "^5.0.7",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"markdown-it": "^13.0.1",
|
||||
"Model": "file:../Model",
|
||||
"marked": "^12.0.2",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^6.9.10",
|
||||
"otpauth": "^9.3.1",
|
||||
"pg": "^8.7.3",
|
||||
"redis-semaphore": "^5.5.1",
|
||||
"socket.io": "^4.7.4",
|
||||
"stripe": "^10.17.0",
|
||||
"twilio": "^4.22.0",
|
||||
@@ -111,7 +168,6 @@
|
||||
"@types/jest": "^27.4.1",
|
||||
"@types/json2csv": "^5.0.3",
|
||||
"@types/jsonwebtoken": "^8.5.9",
|
||||
"@types/markdown-it": "^12.2.3",
|
||||
"@types/node": "^17.0.22",
|
||||
"@types/node-cron": "^3.0.7",
|
||||
"@types/nodemailer": "^6.4.7",
|
||||
@@ -123,44 +179,49 @@
|
||||
"../CommonUI": {
|
||||
"name": "@oneuptime/common-ui",
|
||||
"version": "1.0.0",
|
||||
"extraneous": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.24.1",
|
||||
"@babel/runtime": "^7.24.6",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@nivo/core": "^0.85.1",
|
||||
"@nivo/line": "^0.85.1",
|
||||
"@opentelemetry/api": "^1.8.0",
|
||||
"@opentelemetry/context-zone": "^1.22.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.49.1",
|
||||
"@opentelemetry/instrumentation": "^0.49.1",
|
||||
"@opentelemetry/instrumentation-fetch": "^0.49.1",
|
||||
"@opentelemetry/instrumentation-xml-http-request": "^0.49.1",
|
||||
"@opentelemetry/resources": "^1.21.0",
|
||||
"@opentelemetry/sdk-trace-web": "^1.21.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.21.0",
|
||||
"@nivo/core": "^0.87.0",
|
||||
"@nivo/line": "^0.87.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/context-zone": "^1.25.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.52.0",
|
||||
"@opentelemetry/instrumentation": "^0.52.0",
|
||||
"@opentelemetry/instrumentation-fetch": "^0.52.0",
|
||||
"@opentelemetry/instrumentation-xml-http-request": "^0.52.1",
|
||||
"@opentelemetry/resources": "^1.25.0",
|
||||
"@opentelemetry/sdk-trace-web": "^1.23.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.25.0",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"Common": "file:../Common",
|
||||
"formik": "^2.2.9",
|
||||
"CommonProject": "file:../CommonProject",
|
||||
"formik": "^2.4.6",
|
||||
"history": "^5.3.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"Model": "file:../Model",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react": "^18.3.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-big-calendar": "^1.11.2",
|
||||
"react-big-calendar": "^1.13.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-dropzone": "^14.2.2",
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-highlight": "^0.15.0",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"react-select": "^5.4.0",
|
||||
"react-spinners": "^0.13.6",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactflow": "^11.10.4",
|
||||
"reactflow": "^11.11.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"tippy.js": "^6.3.7",
|
||||
@@ -190,6 +251,7 @@
|
||||
"../Model": {
|
||||
"name": "@oneuptime/model",
|
||||
"version": "1.0.0",
|
||||
"extraneous": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
@@ -898,10 +960,6 @@
|
||||
"resolved": "../CommonServer",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/CommonUI": {
|
||||
"resolved": "../CommonUI",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@@ -1742,10 +1800,6 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/Model": {
|
||||
"resolved": "../Model",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
||||
@@ -27,13 +27,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"CommonServer": "file:../CommonServer",
|
||||
"CommonUI": "file:../CommonUI",
|
||||
"css-loader": "^6.11.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"Model": "file:../Model",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.23.1",
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
import ForgotPasswordPage from './Pages/ForgotPassword';
|
||||
import LoginPage from './Pages/Login';
|
||||
import NotFound from './Pages/NotFound';
|
||||
import RegisterPage from './Pages/Register';
|
||||
import ResetPasswordPage from './Pages/ResetPassword';
|
||||
import VerifyEmail from './Pages/VerifyEmail';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import React, { ReactElement } from 'react';
|
||||
import ForgotPasswordPage from "./Pages/ForgotPassword";
|
||||
import LoginPage from "./Pages/Login";
|
||||
import LoginWithSSO from "./Pages/LoginWithSSO";
|
||||
import NotFound from "./Pages/NotFound";
|
||||
import RegisterPage from "./Pages/Register";
|
||||
import ResetPasswordPage from "./Pages/ResetPassword";
|
||||
import VerifyEmail from "./Pages/VerifyEmail";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import React, { ReactElement } from "react";
|
||||
import {
|
||||
Route,
|
||||
Routes,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useParams,
|
||||
} from 'react-router-dom';
|
||||
Route,
|
||||
Routes,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
|
||||
function App(): ReactElement {
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
Navigation.setLocation(useLocation());
|
||||
Navigation.setParams(useParams());
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
Navigation.setLocation(useLocation());
|
||||
Navigation.setParams(useParams());
|
||||
|
||||
return (
|
||||
<div className="m-auto h-screen">
|
||||
<Routes>
|
||||
<Route path="/accounts" element={<LoginPage />} />
|
||||
<Route path="/accounts/login" element={<LoginPage />} />
|
||||
<Route
|
||||
path="/accounts/forgot-password"
|
||||
element={<ForgotPasswordPage />}
|
||||
/>
|
||||
<Route
|
||||
path="/accounts/reset-password/:token"
|
||||
element={<ResetPasswordPage />}
|
||||
/>
|
||||
<Route path="/accounts/register" element={<RegisterPage />} />
|
||||
<Route
|
||||
path="/accounts/verify-email/:token"
|
||||
element={<VerifyEmail />}
|
||||
/>
|
||||
{/* 👇️ only match this when no other routes match */}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="m-auto h-screen">
|
||||
<Routes>
|
||||
<Route path="/accounts" element={<LoginPage />} />
|
||||
<Route path="/accounts/login" element={<LoginPage />} />
|
||||
|
||||
<Route path="/accounts/sso" element={<LoginWithSSO />} />
|
||||
<Route
|
||||
path="/accounts/forgot-password"
|
||||
element={<ForgotPasswordPage />}
|
||||
/>
|
||||
<Route
|
||||
path="/accounts/reset-password/:token"
|
||||
element={<ResetPasswordPage />}
|
||||
/>
|
||||
<Route path="/accounts/register" element={<RegisterPage />} />
|
||||
<Route path="/accounts/verify-email/:token" element={<VerifyEmail />} />
|
||||
{/* 👇️ only match this when no other routes match */}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
const Footer: () => JSX.Element = () => {
|
||||
return (
|
||||
<div className="footer">
|
||||
<p>
|
||||
<Link to="/">© OneUptime</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link to="/">Contact</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link to="/">Privacy & terms</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="footer">
|
||||
<p>
|
||||
<Link to="/">© OneUptime</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link to="/">Contact</Link>
|
||||
</p>
|
||||
<p>
|
||||
<Link to="/">Privacy & terms</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import App from './App';
|
||||
import Telemetry from 'CommonUI/src/Utils/Telemetry';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import App from "./App";
|
||||
import Telemetry from "Common/UI/Utils/Telemetry";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
Telemetry.init({
|
||||
serviceName: 'Accounts',
|
||||
serviceName: "accounts",
|
||||
});
|
||||
|
||||
const root: any = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
document.getElementById("root") as HTMLElement,
|
||||
);
|
||||
|
||||
root.render(
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>,
|
||||
);
|
||||
|
||||
@@ -1,99 +1,99 @@
|
||||
import { FORGOT_PASSWORD_API_URL } from '../Utils/ApiPaths';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import ModelForm, { FormType } from 'CommonUI/src/Components/Forms/ModelForm';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import Link from 'CommonUI/src/Components/Link/Link';
|
||||
import OneUptimeLogo from 'CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg';
|
||||
import User from 'Model/Models/User';
|
||||
import React, { useState } from 'react';
|
||||
import { FORGOT_PASSWORD_API_URL } from "../Utils/ApiPaths";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import ModelForm, { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import React, { useState } from "react";
|
||||
|
||||
const ForgotPassword: () => JSX.Element = () => {
|
||||
const apiUrl: URL = FORGOT_PASSWORD_API_URL;
|
||||
const apiUrl: URL = FORGOT_PASSWORD_API_URL;
|
||||
|
||||
const [isSuccess, setIsSuccess] = useState<boolean>(false);
|
||||
const [isSuccess, setIsSuccess] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="Your Company"
|
||||
/>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Forgot your password
|
||||
</h2>
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="Your Company"
|
||||
/>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Forgot your password
|
||||
</h2>
|
||||
|
||||
{!isSuccess && (
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Please enter your email and the password reset link will
|
||||
be sent to you.
|
||||
</p>
|
||||
)}
|
||||
{!isSuccess && (
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Please enter your email and the password reset link will be sent to
|
||||
you.
|
||||
</p>
|
||||
)}
|
||||
|
||||
{isSuccess && (
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
We have emailed you the password reset link. Please do
|
||||
not forget to check spam.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{isSuccess && (
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
We have emailed you the password reset link. Please do not forget to
|
||||
check spam.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
{!isSuccess && (
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<ModelForm<User>
|
||||
modelType={User}
|
||||
name="Forgot Password"
|
||||
id="login-form"
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: 'Email',
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
onSuccess={() => {
|
||||
setIsSuccess(true);
|
||||
}}
|
||||
submitButtonText={'Send Password Reset Link'}
|
||||
formType={FormType.Create}
|
||||
maxPrimaryButtonWidth={true}
|
||||
footer={
|
||||
<div className="actions pointer text-center mt-4 hover:underline fw-semibold">
|
||||
<p>
|
||||
<Link
|
||||
to={new Route('/accounts/login')}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm"
|
||||
>
|
||||
Return to Sign in.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-5 text-center">
|
||||
<p className="text-muted mb-0 text-gray-500">
|
||||
Remember your password?{' '}
|
||||
<Link
|
||||
to={new Route('/accounts/login')}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Login.
|
||||
</Link>
|
||||
</p>
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
{!isSuccess && (
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<ModelForm<User>
|
||||
modelType={User}
|
||||
name="Forgot Password"
|
||||
id="login-form"
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: "Email",
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
onSuccess={() => {
|
||||
setIsSuccess(true);
|
||||
}}
|
||||
submitButtonText={"Send Password Reset Link"}
|
||||
formType={FormType.Create}
|
||||
maxPrimaryButtonWidth={true}
|
||||
footer={
|
||||
<div className="actions pointer text-center mt-4 hover:underline fw-semibold">
|
||||
<p>
|
||||
<Link
|
||||
to={new Route("/accounts/login")}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm"
|
||||
>
|
||||
Return to Sign in.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-5 text-center">
|
||||
<p className="text-muted mb-0 text-gray-500">
|
||||
Remember your password?{" "}
|
||||
<Link
|
||||
to={new Route("/accounts/login")}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Login.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForgotPassword;
|
||||
|
||||
@@ -1,167 +1,293 @@
|
||||
import { LOGIN_API_URL } from '../Utils/ApiPaths';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import Alert, { AlertType } from 'CommonUI/src/Components/Alerts/Alert';
|
||||
import ModelForm, { FormType } from 'CommonUI/src/Components/Forms/ModelForm';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import Link from 'CommonUI/src/Components/Link/Link';
|
||||
import { DASHBOARD_URL } from 'CommonUI/src/Config';
|
||||
import OneUptimeLogo from 'CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg';
|
||||
import UiAnalytics from 'CommonUI/src/Utils/Analytics';
|
||||
import LoginUtil from 'CommonUI/src/Utils/Login';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import UserUtil from 'CommonUI/src/Utils/User';
|
||||
import User from 'Model/Models/User';
|
||||
import React, { useState } from 'react';
|
||||
import useAsyncEffect from 'use-async-effect';
|
||||
import {
|
||||
LOGIN_API_URL,
|
||||
VERIFY_TWO_FACTOR_AUTH_API_URL,
|
||||
} from "../Utils/ApiPaths";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { JSONArray, JSONObject } from "Common/Types/JSON";
|
||||
import ModelForm, { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import { DASHBOARD_URL } from "Common/UI/Config";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import UiAnalytics from "Common/UI/Utils/Analytics";
|
||||
import LoginUtil from "Common/UI/Utils/Login";
|
||||
import UserTwoFactorAuth from "Common/Models/DatabaseModels/UserTwoFactorAuth";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import UserUtil from "Common/UI/Utils/User";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import React from "react";
|
||||
import useAsyncEffect from "use-async-effect";
|
||||
import StaticModelList from "Common/UI/Components/ModelList/StaticModelList";
|
||||
import BasicForm from "Common/UI/Components/Forms/BasicForm";
|
||||
import API from "Common/UI/Utils/API/API";
|
||||
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
||||
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
|
||||
const LoginPage: () => JSX.Element = () => {
|
||||
const apiUrl: URL = LOGIN_API_URL;
|
||||
const apiUrl: URL = LOGIN_API_URL;
|
||||
|
||||
if (UserUtil.isLoggedIn()) {
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
if (UserUtil.isLoggedIn()) {
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
}
|
||||
|
||||
const [initialValues, setInitialValues] = React.useState<JSONObject>({});
|
||||
|
||||
const [showTwoFactorAuth, setShowTwoFactorAuth] =
|
||||
React.useState<boolean>(false);
|
||||
|
||||
const [twoFactorAuthList, setTwoFactorAuthList] = React.useState<
|
||||
UserTwoFactorAuth[]
|
||||
>([]);
|
||||
|
||||
const [selectedTwoFactorAuth, setSelectedTwoFactorAuth] = React.useState<
|
||||
UserTwoFactorAuth | undefined
|
||||
>(undefined);
|
||||
|
||||
const [isTwoFactorAuthLoading, setIsTwoFactorAuthLoading] =
|
||||
React.useState<boolean>(false);
|
||||
const [twofactorAuthError, setTwoFactorAuthError] =
|
||||
React.useState<string>("");
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
if (Navigation.getQueryStringByName("email")) {
|
||||
setInitialValues({
|
||||
email: Navigation.getQueryStringByName("email"),
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
type LoginFunction = (user: User, miscData: JSONObject) => void;
|
||||
|
||||
const login: LoginFunction = (user: User, miscData: JSONObject): void => {
|
||||
if (user instanceof User && user && user.email) {
|
||||
UiAnalytics.userAuth(user.email);
|
||||
UiAnalytics.capture("accounts/login");
|
||||
}
|
||||
|
||||
const showSsoMessage: boolean = Boolean(
|
||||
Navigation.getQueryStringByName('sso')
|
||||
);
|
||||
LoginUtil.login({
|
||||
user: user,
|
||||
token: miscData ? miscData["token"] : undefined,
|
||||
});
|
||||
};
|
||||
|
||||
const [showSsoTip, setShowSSOTip] = useState<boolean>(false);
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="OneUptime"
|
||||
/>
|
||||
{!showTwoFactorAuth && (
|
||||
<>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Sign in to your account
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Join thousands of business that use OneUptime to help them stay
|
||||
online all the time.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
const [initialValues, setInitialValues] = React.useState<JSONObject>({});
|
||||
{showTwoFactorAuth && (
|
||||
<>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Two Factor Authentication
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Select two factor authentication method. You will be asked to
|
||||
enter a code from the selected method.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
if (Navigation.getQueryStringByName('email')) {
|
||||
setInitialValues({
|
||||
email: Navigation.getQueryStringByName('email'),
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
{!showTwoFactorAuth && (
|
||||
<ModelForm<User>
|
||||
modelType={User}
|
||||
id="login-form"
|
||||
name="Login"
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
placeholder: "jeff@example.com",
|
||||
required: true,
|
||||
disabled: Boolean(initialValues && initialValues["email"]),
|
||||
title: "Email",
|
||||
dataTestId: "email",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
password: true,
|
||||
},
|
||||
title: "Password",
|
||||
required: true,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
sideLink: {
|
||||
text: "Forgot password?",
|
||||
url: new Route("/accounts/forgot-password"),
|
||||
openLinkInNewTab: false,
|
||||
},
|
||||
dataTestId: "password",
|
||||
},
|
||||
]}
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
formType={FormType.Create}
|
||||
submitButtonText={"Login"}
|
||||
onBeforeCreate={(data: User) => {
|
||||
setInitialValues(User.toJSON(data, User));
|
||||
return Promise.resolve(data);
|
||||
}}
|
||||
onSuccess={(
|
||||
value: User | JSONObject,
|
||||
miscData: JSONObject | undefined,
|
||||
) => {
|
||||
if (
|
||||
miscData &&
|
||||
(miscData as JSONObject)["twoFactorAuth"] === true
|
||||
) {
|
||||
const twoFactorAuthList: Array<UserTwoFactorAuth> =
|
||||
UserTwoFactorAuth.fromJSONArray(
|
||||
(miscData as JSONObject)[
|
||||
"twoFactorAuthList"
|
||||
] as JSONArray,
|
||||
UserTwoFactorAuth,
|
||||
);
|
||||
setTwoFactorAuthList(twoFactorAuthList);
|
||||
setShowTwoFactorAuth(true);
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="OneUptime"
|
||||
/>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Sign in to your account
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Join thousands of business that use OneUptime to help them
|
||||
stay online all the time.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{showSsoMessage && (
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md mt-8">
|
||||
{' '}
|
||||
<Alert
|
||||
type={AlertType.DANGER}
|
||||
title="You must be logged into OneUptime account to use single sign-on (SSO) for your project. Logging in to OneUptime account and single sign on (SSO) for your project are two separate steps. Please use the form below to log in to your OneUptime account before you use SSO."
|
||||
/>{' '}
|
||||
login(value as User, miscData as JSONObject);
|
||||
}}
|
||||
maxPrimaryButtonWidth={true}
|
||||
footer={
|
||||
<div className="actions text-center mt-4 hover:underline fw-semibold">
|
||||
<div>
|
||||
<Link to={new Route("/accounts/sso")}>
|
||||
<div className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm">
|
||||
Use single sign-on (SSO) instead
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<ModelForm<User>
|
||||
modelType={User}
|
||||
id="login-form"
|
||||
name="Login"
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
placeholder: 'jeff@example.com',
|
||||
required: true,
|
||||
disabled: Boolean(
|
||||
initialValues && initialValues['email']
|
||||
),
|
||||
title: 'Email',
|
||||
dataTestId: 'email',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
password: true,
|
||||
},
|
||||
title: 'Password',
|
||||
required: true,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
sideLink: {
|
||||
text: 'Forgot password?',
|
||||
url: new Route('/accounts/forgot-password'),
|
||||
openLinkInNewTab: false,
|
||||
},
|
||||
dataTestId: 'password',
|
||||
},
|
||||
]}
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
formType={FormType.Create}
|
||||
submitButtonText={'Login'}
|
||||
onSuccess={(
|
||||
value: User,
|
||||
miscData: JSONObject | undefined
|
||||
) => {
|
||||
if (value && value.email) {
|
||||
UiAnalytics.userAuth(value.email);
|
||||
UiAnalytics.capture('accounts/login');
|
||||
}
|
||||
{showTwoFactorAuth && !selectedTwoFactorAuth && (
|
||||
<StaticModelList<UserTwoFactorAuth>
|
||||
titleField="name"
|
||||
descriptionField=""
|
||||
selectedItems={[]}
|
||||
list={twoFactorAuthList}
|
||||
onClick={(item: UserTwoFactorAuth) => {
|
||||
setSelectedTwoFactorAuth(item);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
LoginUtil.login({
|
||||
user: value,
|
||||
token: miscData ? miscData['token'] : undefined,
|
||||
});
|
||||
}}
|
||||
maxPrimaryButtonWidth={true}
|
||||
footer={
|
||||
<div className="actions text-center mt-4 hover:underline fw-semibold">
|
||||
<div>
|
||||
{!showSsoTip && (
|
||||
<div
|
||||
onClick={() => {
|
||||
setShowSSOTip(true);
|
||||
}}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm"
|
||||
>
|
||||
Use single sign-on (SSO) instead
|
||||
</div>
|
||||
)}
|
||||
{showTwoFactorAuth && selectedTwoFactorAuth && (
|
||||
<BasicForm
|
||||
id="two-factor-auth-form"
|
||||
name="Two Factor Auth"
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
code: true,
|
||||
},
|
||||
title: "Code",
|
||||
description: "Enter the code from your authenticator app",
|
||||
required: true,
|
||||
dataTestId: "code",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
},
|
||||
]}
|
||||
submitButtonText={"Login"}
|
||||
maxPrimaryButtonWidth={true}
|
||||
isLoading={isTwoFactorAuthLoading}
|
||||
error={twofactorAuthError}
|
||||
onSubmit={async (data: JSONObject) => {
|
||||
setIsTwoFactorAuthLoading(true);
|
||||
|
||||
{showSsoTip && (
|
||||
<div className="text-gray-500 text-sm">
|
||||
Please sign in with your SSO
|
||||
provider like Okta, Auth0, Entra ID
|
||||
or any other SAML 2.0 provider.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-10 text-center">
|
||||
<div className="text-muted mb-0 text-gray-500">
|
||||
Don't have an account?{' '}
|
||||
<Link
|
||||
to={new Route('/accounts/register')}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Register.
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
try {
|
||||
const code: string = data["code"] as string;
|
||||
const twoFactorAuthId: string =
|
||||
selectedTwoFactorAuth.id?.toString() as string;
|
||||
|
||||
const result: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post(VERIFY_TWO_FACTOR_AUTH_API_URL, {
|
||||
data: {
|
||||
...initialValues,
|
||||
code: code,
|
||||
twoFactorAuthId: twoFactorAuthId,
|
||||
},
|
||||
});
|
||||
|
||||
if (result instanceof HTTPErrorResponse) {
|
||||
throw result;
|
||||
}
|
||||
|
||||
const user: User = User.fromJSON(
|
||||
result["data"] as JSONObject,
|
||||
User,
|
||||
) as User;
|
||||
const miscData: JSONObject = (result["data"] as JSONObject)[
|
||||
"miscData"
|
||||
] as JSONObject;
|
||||
|
||||
login(user as User, miscData as JSONObject);
|
||||
} catch (error) {
|
||||
setTwoFactorAuthError(
|
||||
API.getFriendlyErrorMessage(error as Error),
|
||||
);
|
||||
}
|
||||
|
||||
setIsTwoFactorAuthLoading(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
<div className="mt-10 text-center">
|
||||
{!selectedTwoFactorAuth && (
|
||||
<div className="text-muted mb-0 text-gray-500">
|
||||
Don't have an account?{" "}
|
||||
<Link
|
||||
to={new Route("/accounts/register")}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Register.
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
{selectedTwoFactorAuth ? (
|
||||
<div className="text-muted mb-0 text-gray-500">
|
||||
<Link
|
||||
onClick={() => {
|
||||
setSelectedTwoFactorAuth(undefined);
|
||||
}}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Select a different two factor authentication method
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
|
||||
235
Accounts/src/Pages/LoginWithSSO.tsx
Normal file
235
Accounts/src/Pages/LoginWithSSO.tsx
Normal file
@@ -0,0 +1,235 @@
|
||||
import { SERVICE_PROVIDER_LOGIN_URL } from "../Utils/ApiPaths";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { JSONArray, JSONObject } from "Common/Types/JSON";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import { DASHBOARD_URL, IDENTITY_URL } from "Common/UI/Config";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import UserUtil from "Common/UI/Utils/User";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import React, { ReactElement, useState } from "react";
|
||||
import ProjectSSO from "Common/Models/DatabaseModels/ProjectSso";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import API from "Common/UI/Utils/API/API";
|
||||
import BasicForm from "Common/UI/Components/Forms/BasicForm";
|
||||
import Email from "Common/Types/Email";
|
||||
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
||||
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
import StaticModelList from "Common/UI/Components/ModelList/StaticModelList";
|
||||
|
||||
const LoginPage: () => JSX.Element = () => {
|
||||
const apiUrl: URL = SERVICE_PROVIDER_LOGIN_URL;
|
||||
|
||||
if (UserUtil.isLoggedIn()) {
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
}
|
||||
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [projectSsoConfigList, setProjectSsoConfigList] = useState<
|
||||
Array<ProjectSSO>
|
||||
>([]);
|
||||
|
||||
type FetchSSOConfigsFunction = (email: Email) => Promise<void>;
|
||||
|
||||
const fetchSsoConfigs: FetchSSOConfigsFunction = async (
|
||||
email: Email,
|
||||
): Promise<void> => {
|
||||
if (email) {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// get sso config by email.
|
||||
const listResult: HTTPErrorResponse | HTTPResponse<JSONArray> =
|
||||
await API.get(
|
||||
URL.fromString(apiUrl.toString()).addQueryParam(
|
||||
"email",
|
||||
email.toString(),
|
||||
),
|
||||
);
|
||||
|
||||
if (listResult instanceof HTTPErrorResponse) {
|
||||
throw listResult;
|
||||
}
|
||||
|
||||
if (!listResult.data || (listResult.data as JSONArray).length === 0) {
|
||||
setError(
|
||||
"No SSO configuration found for the email: " + email.toString(),
|
||||
);
|
||||
} else {
|
||||
setProjectSsoConfigList(
|
||||
ProjectSSO.fromJSONArray(listResult["data"], ProjectSSO),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
setError(API.getFriendlyErrorMessage(error as Error));
|
||||
}
|
||||
} else {
|
||||
setError("Email is required to perform this action");
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
type GetSsoConfigModelListFunction = (
|
||||
configs: Array<ProjectSSO>,
|
||||
) => ReactElement;
|
||||
|
||||
const getSsoConfigModelList: GetSsoConfigModelListFunction = (
|
||||
configs: Array<ProjectSSO>,
|
||||
): ReactElement => {
|
||||
return (
|
||||
<StaticModelList<ProjectSSO>
|
||||
list={configs}
|
||||
titleField="name"
|
||||
selectedItems={[]}
|
||||
descriptionField="description"
|
||||
onClick={(item: ProjectSSO) => {
|
||||
setIsLoading(true);
|
||||
Navigation.navigate(
|
||||
URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route(
|
||||
`/sso/${item.projectId?.toString()}/${item.id?.toString()}`,
|
||||
),
|
||||
),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
type GetProjectNameFunction = (projectId: string) => string;
|
||||
|
||||
const getProjectName: GetProjectNameFunction = (
|
||||
projectId: string,
|
||||
): string => {
|
||||
const projectNames: Array<string | undefined> = projectSsoConfigList
|
||||
.filter((config: ProjectSSO) => {
|
||||
return config.projectId?.toString() === projectId.toString();
|
||||
})
|
||||
.map((config: ProjectSSO) => {
|
||||
return config.project?.name;
|
||||
});
|
||||
return projectNames[0] || "Project";
|
||||
};
|
||||
|
||||
if (projectSsoConfigList.length > 0 && !error && !isLoading) {
|
||||
const projectIds: Array<string> = projectSsoConfigList.map(
|
||||
(config: ProjectSSO) => {
|
||||
return config.projectId?.toString() as string;
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="OneUptime"
|
||||
/>
|
||||
<h2 className="mt-10 text-center text-xl tracking-tight text-gray-900">
|
||||
Select Project
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Select the project you want to login to.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{projectIds.map((projectId: string) => {
|
||||
return (
|
||||
<div key={projectId}>
|
||||
<h3 className="mt-6 font-medium tracking-tight">
|
||||
{getProjectName(projectId)}
|
||||
</h3>
|
||||
{getSsoConfigModelList(
|
||||
projectSsoConfigList.filter((config: ProjectSSO) => {
|
||||
return (
|
||||
config.projectId?.toString() === projectId.toString()
|
||||
);
|
||||
}),
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="OneUptime"
|
||||
/>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Login with SSO
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Login with your SSO provider to access your account.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<BasicForm
|
||||
modelType={User}
|
||||
id="login-form"
|
||||
error={error}
|
||||
name="Login"
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
placeholder: "jeff@example.com",
|
||||
required: true,
|
||||
title: "Email",
|
||||
dataTestId: "email",
|
||||
},
|
||||
]}
|
||||
maxPrimaryButtonWidth={true}
|
||||
submitButtonText="Login with SSO"
|
||||
onSubmit={async (data: JSONObject) => {
|
||||
await fetchSsoConfigs(data["email"] as Email);
|
||||
}}
|
||||
footer={
|
||||
<div className="actions text-center mt-4 hover:underline fw-semibold">
|
||||
<div>
|
||||
<Link to={new Route("/accounts/login")}>
|
||||
<div className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm">
|
||||
Use username and password insead.
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-10 text-center">
|
||||
<div className="text-muted mb-0 text-gray-500">
|
||||
Don't have an account?{" "}
|
||||
<Link
|
||||
to={new Route("/accounts/register")}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Register.
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
@@ -1,18 +1,18 @@
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
const LoginPage: () => JSX.Element = () => {
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Page not found
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Page you are looking for does not exist.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Page not found
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Page you are looking for does not exist.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
|
||||
@@ -1,276 +1,267 @@
|
||||
import { SIGNUP_API_URL } from '../Utils/ApiPaths';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import ErrorMessage from 'CommonUI/src/Components/ErrorMessage/ErrorMessage';
|
||||
import ModelForm, { FormType } from 'CommonUI/src/Components/Forms/ModelForm';
|
||||
import Fields from 'CommonUI/src/Components/Forms/Types/Fields';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import Link from 'CommonUI/src/Components/Link/Link';
|
||||
import PageLoader from 'CommonUI/src/Components/Loader/PageLoader';
|
||||
import { BILLING_ENABLED, DASHBOARD_URL } from 'CommonUI/src/Config';
|
||||
import OneUptimeLogo from 'CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg';
|
||||
import BaseAPI from 'CommonUI/src/Utils/API/API';
|
||||
import UiAnalytics from 'CommonUI/src/Utils/Analytics';
|
||||
import LocalStorage from 'CommonUI/src/Utils/LocalStorage';
|
||||
import LoginUtil from 'CommonUI/src/Utils/Login';
|
||||
import ModelAPI, { ListResult } from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import UserUtil from 'CommonUI/src/Utils/User';
|
||||
import Reseller from 'Model/Models/Reseller';
|
||||
import User from 'Model/Models/User';
|
||||
import React, { useState } from 'react';
|
||||
import useAsyncEffect from 'use-async-effect';
|
||||
import { SIGNUP_API_URL } from "../Utils/ApiPaths";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import ModelForm, { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import Fields from "Common/UI/Components/Forms/Types/Fields";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import { BILLING_ENABLED, DASHBOARD_URL } from "Common/UI/Config";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import BaseAPI from "Common/UI/Utils/API/API";
|
||||
import UiAnalytics from "Common/UI/Utils/Analytics";
|
||||
import LocalStorage from "Common/UI/Utils/LocalStorage";
|
||||
import LoginUtil from "Common/UI/Utils/Login";
|
||||
import ModelAPI, { ListResult } from "Common/UI/Utils/ModelAPI/ModelAPI";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import UserUtil from "Common/UI/Utils/User";
|
||||
import Reseller from "Common/Models/DatabaseModels/Reseller";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import React, { useState } from "react";
|
||||
import useAsyncEffect from "use-async-effect";
|
||||
|
||||
const RegisterPage: () => JSX.Element = () => {
|
||||
const apiUrl: URL = SIGNUP_API_URL;
|
||||
const apiUrl: URL = SIGNUP_API_URL;
|
||||
|
||||
const [initialValues, setInitialValues] = React.useState<JSONObject>({});
|
||||
const [initialValues, setInitialValues] = React.useState<JSONObject>({});
|
||||
|
||||
const [error, setError] = useState<string>('');
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
||||
|
||||
const [reseller, setResller] = React.useState<Reseller | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [reseller, setResller] = React.useState<Reseller | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
if (UserUtil.isLoggedIn()) {
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
if (UserUtil.isLoggedIn()) {
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
}
|
||||
|
||||
type FetchResellerFunction = (resellerId: string) => Promise<void>;
|
||||
|
||||
const fetchReseller: FetchResellerFunction = async (
|
||||
resellerId: string,
|
||||
): Promise<void> => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const reseller: ListResult<Reseller> = await ModelAPI.getList<Reseller>({
|
||||
modelType: Reseller,
|
||||
query: {
|
||||
resellerId: resellerId,
|
||||
},
|
||||
limit: 1,
|
||||
skip: 0,
|
||||
select: {
|
||||
hidePhoneNumberOnSignup: true,
|
||||
},
|
||||
sort: {},
|
||||
requestOptions: {},
|
||||
});
|
||||
|
||||
if (reseller.data.length > 0) {
|
||||
setResller(reseller.data[0]);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(BaseAPI.getFriendlyMessage(err));
|
||||
}
|
||||
|
||||
type FetchResellerFunction = (resellerId: string) => Promise<void>;
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const fetchReseller: FetchResellerFunction = async (
|
||||
resellerId: string
|
||||
): Promise<void> => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const reseller: ListResult<Reseller> =
|
||||
await ModelAPI.getList<Reseller>({
|
||||
modelType: Reseller,
|
||||
query: {
|
||||
resellerId: resellerId,
|
||||
},
|
||||
limit: 1,
|
||||
skip: 0,
|
||||
select: {
|
||||
hidePhoneNumberOnSignup: true,
|
||||
},
|
||||
sort: {},
|
||||
requestOptions: {},
|
||||
});
|
||||
|
||||
if (reseller.data.length > 0) {
|
||||
setResller(reseller.data[0]);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(BaseAPI.getFriendlyMessage(err));
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
// if promo code is found, please save it in localstorage.
|
||||
if (Navigation.getQueryStringByName('promoCode')) {
|
||||
LocalStorage.setItem(
|
||||
'promoCode',
|
||||
Navigation.getQueryStringByName('promoCode')
|
||||
);
|
||||
}
|
||||
|
||||
if (Navigation.getQueryStringByName('email')) {
|
||||
setInitialValues({
|
||||
email: Navigation.getQueryStringByName('email'),
|
||||
});
|
||||
}
|
||||
|
||||
// if promo code is found, please save it in localstorage.
|
||||
if (Navigation.getQueryStringByName('partnerId')) {
|
||||
await fetchReseller(Navigation.getQueryStringByName('partnerId')!);
|
||||
}
|
||||
}, []);
|
||||
|
||||
let formFields: Fields<User> = [
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
placeholder: 'jeff@example.com',
|
||||
required: true,
|
||||
disabled: Boolean(initialValues && initialValues['email']),
|
||||
title: 'Email',
|
||||
dataTestId: 'email',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: 'Jeff Smith',
|
||||
required: true,
|
||||
title: 'Full Name',
|
||||
dataTestId: 'name',
|
||||
},
|
||||
];
|
||||
|
||||
if (BILLING_ENABLED) {
|
||||
formFields = formFields.concat([
|
||||
{
|
||||
field: {
|
||||
companyName: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: 'Acme, Inc.',
|
||||
required: true,
|
||||
title: 'Company Name',
|
||||
dataTestId: 'companyName',
|
||||
},
|
||||
]);
|
||||
|
||||
// If reseller wants to hide phone number on sign up, we hide it.
|
||||
if (!reseller || !reseller.hidePhoneNumberOnSignup) {
|
||||
formFields.push({
|
||||
field: {
|
||||
companyPhoneNumber: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Phone,
|
||||
required: true,
|
||||
placeholder: '+11234567890',
|
||||
title: 'Phone Number',
|
||||
dataTestId: 'companyPhoneNumber',
|
||||
});
|
||||
}
|
||||
useAsyncEffect(async () => {
|
||||
// if promo code is found, please save it in localstorage.
|
||||
if (Navigation.getQueryStringByName("promoCode")) {
|
||||
LocalStorage.setItem(
|
||||
"promoCode",
|
||||
Navigation.getQueryStringByName("promoCode"),
|
||||
);
|
||||
}
|
||||
|
||||
if (Navigation.getQueryStringByName("email")) {
|
||||
setInitialValues({
|
||||
email: Navigation.getQueryStringByName("email"),
|
||||
});
|
||||
}
|
||||
|
||||
// if promo code is found, please save it in localstorage.
|
||||
if (Navigation.getQueryStringByName("partnerId")) {
|
||||
await fetchReseller(Navigation.getQueryStringByName("partnerId")!);
|
||||
}
|
||||
}, []);
|
||||
|
||||
let formFields: Fields<User> = [
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
placeholder: "jeff@example.com",
|
||||
required: true,
|
||||
disabled: Boolean(initialValues && initialValues["email"]),
|
||||
title: "Email",
|
||||
dataTestId: "email",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: "Jeff Smith",
|
||||
required: true,
|
||||
title: "Full Name",
|
||||
dataTestId: "name",
|
||||
},
|
||||
];
|
||||
|
||||
if (BILLING_ENABLED) {
|
||||
formFields = formFields.concat([
|
||||
{
|
||||
field: {
|
||||
password: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
placeholder: 'Password',
|
||||
title: 'Password',
|
||||
required: true,
|
||||
dataTestId: 'password',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
confirmPassword: true,
|
||||
} as any,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
toMatchField: 'password',
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
placeholder: 'Confirm Password',
|
||||
title: 'Confirm Password',
|
||||
overrideFieldKey: 'confirmPassword',
|
||||
required: true,
|
||||
showEvenIfPermissionDoesNotExist: true,
|
||||
dataTestId: 'confirmPassword',
|
||||
{
|
||||
field: {
|
||||
companyName: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: "Acme, Inc.",
|
||||
required: true,
|
||||
title: "Company Name",
|
||||
dataTestId: "companyName",
|
||||
},
|
||||
]);
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage error={error} />;
|
||||
// If reseller wants to hide phone number on sign up, we hide it.
|
||||
if (!reseller || !reseller.hidePhoneNumberOnSignup) {
|
||||
formFields.push({
|
||||
field: {
|
||||
companyPhoneNumber: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Phone,
|
||||
required: true,
|
||||
placeholder: "+11234567890",
|
||||
title: "Phone Number",
|
||||
dataTestId: "companyPhoneNumber",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
formFields = formFields.concat([
|
||||
{
|
||||
field: {
|
||||
password: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
placeholder: "Password",
|
||||
title: "Password",
|
||||
required: true,
|
||||
dataTestId: "password",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
confirmPassword: true,
|
||||
} as any,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
toMatchField: "password",
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
placeholder: "Confirm Password",
|
||||
title: "Confirm Password",
|
||||
overrideFieldKey: "confirmPassword",
|
||||
required: true,
|
||||
showEvenIfPermissionDoesNotExist: true,
|
||||
dataTestId: "confirmPassword",
|
||||
},
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="OneUptime"
|
||||
/>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Create your OneUptime account
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Join thousands of business that use OneUptime to help them
|
||||
stay online all the time.
|
||||
</p>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
No credit card required.
|
||||
</p>
|
||||
</div>
|
||||
if (error) {
|
||||
return <ErrorMessage error={error} />;
|
||||
}
|
||||
|
||||
<div className="mt-8 lg:mx-auto lg:w-full lg:max-w-2xl">
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<ModelForm<User>
|
||||
modelType={User}
|
||||
id="register-form"
|
||||
showAsColumns={reseller ? 1 : 2}
|
||||
name="Register"
|
||||
initialValues={initialValues}
|
||||
maxPrimaryButtonWidth={true}
|
||||
fields={formFields}
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
onBeforeCreate={(item: User): Promise<User> => {
|
||||
const utmParams: Dictionary<string> =
|
||||
UserUtil.getUtmParams();
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
if (
|
||||
utmParams &&
|
||||
Object.keys(utmParams).length > 0
|
||||
) {
|
||||
item.utmSource = utmParams['utmSource'] || '';
|
||||
item.utmMedium = utmParams['utmMedium'] || '';
|
||||
item.utmCampaign =
|
||||
utmParams['utmCampaign'] || '';
|
||||
item.utmTerm = utmParams['utmTerm'] || '';
|
||||
item.utmContent = utmParams['utmContent'] || '';
|
||||
item.utmUrl = utmParams['utmUrl'] || '';
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="OneUptime"
|
||||
/>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Create your OneUptime account
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Join thousands of business that use OneUptime to help them stay online
|
||||
all the time.
|
||||
</p>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
No credit card required.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
UiAnalytics.capture('utm_event', utmParams);
|
||||
}
|
||||
<div className="mt-8 lg:mx-auto lg:w-full lg:max-w-2xl">
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<ModelForm<User>
|
||||
modelType={User}
|
||||
id="register-form"
|
||||
showAsColumns={reseller ? 1 : 2}
|
||||
name="Register"
|
||||
initialValues={initialValues}
|
||||
maxPrimaryButtonWidth={true}
|
||||
fields={formFields}
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
onBeforeCreate={(item: User): Promise<User> => {
|
||||
const utmParams: Dictionary<string> = UserUtil.getUtmParams();
|
||||
|
||||
return Promise.resolve(item);
|
||||
}}
|
||||
formType={FormType.Create}
|
||||
submitButtonText={'Sign Up'}
|
||||
onSuccess={(
|
||||
value: User,
|
||||
miscData: JSONObject | undefined
|
||||
) => {
|
||||
if (value && value.email) {
|
||||
UiAnalytics.userAuth(value.email);
|
||||
UiAnalytics.capture('accounts/register');
|
||||
}
|
||||
if (utmParams && Object.keys(utmParams).length > 0) {
|
||||
item.utmSource = utmParams["utmSource"] || "";
|
||||
item.utmMedium = utmParams["utmMedium"] || "";
|
||||
item.utmCampaign = utmParams["utmCampaign"] || "";
|
||||
item.utmTerm = utmParams["utmTerm"] || "";
|
||||
item.utmContent = utmParams["utmContent"] || "";
|
||||
item.utmUrl = utmParams["utmUrl"] || "";
|
||||
|
||||
LoginUtil.login({
|
||||
user: value,
|
||||
token: miscData ? miscData['token'] : undefined,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-5 text-center text-gray-500">
|
||||
<p className="text-muted mb-0">
|
||||
Already have an account?{' '}
|
||||
<Link
|
||||
to={new Route('/accounts/login')}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Log in.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
UiAnalytics.capture("utm_event", utmParams);
|
||||
}
|
||||
|
||||
return Promise.resolve(item);
|
||||
}}
|
||||
formType={FormType.Create}
|
||||
submitButtonText={"Sign Up"}
|
||||
onSuccess={(value: User, miscData: JSONObject | undefined) => {
|
||||
if (value && value.email) {
|
||||
UiAnalytics.userAuth(value.email);
|
||||
UiAnalytics.capture("accounts/register");
|
||||
}
|
||||
|
||||
LoginUtil.login({
|
||||
user: value,
|
||||
token: miscData ? miscData["token"] : undefined,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
<div className="mt-5 text-center text-gray-500">
|
||||
<p className="text-muted mb-0">
|
||||
Already have an account?{" "}
|
||||
<Link
|
||||
to={new Route("/accounts/login")}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Log in.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegisterPage;
|
||||
|
||||
@@ -1,113 +1,114 @@
|
||||
import { RESET_PASSWORD_API_URL } from '../Utils/ApiPaths';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import ModelForm, { FormType } from 'CommonUI/src/Components/Forms/ModelForm';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import Link from 'CommonUI/src/Components/Link/Link';
|
||||
import OneUptimeLogo from 'CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import User from 'Model/Models/User';
|
||||
import React, { useState } from 'react';
|
||||
import { RESET_PASSWORD_API_URL } from "../Utils/ApiPaths";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import ModelForm, { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import React, { useState } from "react";
|
||||
|
||||
const RegisterPage: () => JSX.Element = () => {
|
||||
const apiUrl: URL = RESET_PASSWORD_API_URL;
|
||||
const [isSuccess, setIsSuccess] = useState<boolean>(false);
|
||||
const apiUrl: URL = RESET_PASSWORD_API_URL;
|
||||
const [isSuccess, setIsSuccess] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="Your Company"
|
||||
/>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Reset your password
|
||||
</h2>
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="Your Company"
|
||||
/>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Reset your password
|
||||
</h2>
|
||||
|
||||
{!isSuccess && (
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Please enter your new password and we will have it
|
||||
updated.{' '}
|
||||
</p>
|
||||
)}
|
||||
{!isSuccess && (
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Please enter your new password and we will have it updated.{" "}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{isSuccess && (
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Your password has been updated. Please log in.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{isSuccess && (
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Your password has been updated. Please log in.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
{!isSuccess && (
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<ModelForm<User>
|
||||
modelType={User}
|
||||
id="register-form"
|
||||
name="Reset Password"
|
||||
onBeforeCreate={(item: User): Promise<User> => {
|
||||
item.resetPasswordToken =
|
||||
Navigation.getLastParam()
|
||||
?.toString()
|
||||
.replace('/', '')
|
||||
.toString() || '';
|
||||
return Promise.resolve(item);
|
||||
}}
|
||||
showAsColumns={1}
|
||||
maxPrimaryButtonWidth={true}
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
password: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
placeholder: 'New Password',
|
||||
title: 'New Password',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
password: true,
|
||||
},
|
||||
validation: {
|
||||
minLength: 6,
|
||||
toMatchField: 'password',
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
placeholder: 'Confirm Password',
|
||||
title: 'Confirm Password',
|
||||
overrideFieldKey: 'confirmPassword',
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
formType={FormType.Create}
|
||||
submitButtonText={'Reset Password'}
|
||||
onSuccess={() => {
|
||||
setIsSuccess(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
{!isSuccess && (
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<ModelForm<User>
|
||||
modelType={User}
|
||||
id="register-form"
|
||||
name="Reset Password"
|
||||
onBeforeCreate={(item: User): Promise<User> => {
|
||||
item.resetPasswordToken =
|
||||
Navigation.getLastParam()
|
||||
?.toString()
|
||||
.replace("/", "")
|
||||
.toString() || "";
|
||||
return Promise.resolve(item);
|
||||
}}
|
||||
showAsColumns={1}
|
||||
maxPrimaryButtonWidth={true}
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
password: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
placeholder: "New Password",
|
||||
title: "New Password",
|
||||
required: true,
|
||||
showEvenIfPermissionDoesNotExist: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
confirmPassword: true,
|
||||
} as any,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
toMatchField: "password",
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
placeholder: "Confirm Password",
|
||||
title: "Confirm Password",
|
||||
overrideFieldKey: "confirmPassword",
|
||||
required: true,
|
||||
showEvenIfPermissionDoesNotExist: true,
|
||||
},
|
||||
]}
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
formType={FormType.Create}
|
||||
submitButtonText={"Reset Password"}
|
||||
onSuccess={() => {
|
||||
setIsSuccess(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-5 text-center">
|
||||
<p className="text-muted mb-0 text-gray-500">
|
||||
Know your password?{' '}
|
||||
<Link
|
||||
to={new Route('/accounts/login')}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Log in.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 text-center">
|
||||
<p className="text-muted mb-0 text-gray-500">
|
||||
Know your password?{" "}
|
||||
<Link
|
||||
to={new Route("/accounts/login")}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Log in.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegisterPage;
|
||||
|
||||
@@ -1,132 +1,121 @@
|
||||
import { VERIFY_EMAIL_API_URL } from '../Utils/ApiPaths';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { PromiseVoidFunction } from 'Common/Types/FunctionTypes';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import { FormType } from 'CommonUI/src/Components/Forms/ModelForm';
|
||||
import Link from 'CommonUI/src/Components/Link/Link';
|
||||
import PageLoader from 'CommonUI/src/Components/Loader/PageLoader';
|
||||
import OneUptimeLogo from 'CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg';
|
||||
import API from 'CommonUI/src/Utils/API/API';
|
||||
import ModelAPI from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import EmailVerificationToken from 'Model/Models/EmailVerificationToken';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { VERIFY_EMAIL_API_URL } from "../Utils/ApiPaths";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import API from "Common/UI/Utils/API/API";
|
||||
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import EmailVerificationToken from "Common/Models/DatabaseModels/EmailVerificationToken";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
const VerifyEmail: () => JSX.Element = () => {
|
||||
const apiUrl: URL = VERIFY_EMAIL_API_URL;
|
||||
const [error, setError] = useState<string>('');
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const apiUrl: URL = VERIFY_EMAIL_API_URL;
|
||||
const [error, setError] = useState<string>("");
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
// Ping an API here.
|
||||
setError('');
|
||||
setIsLoading(true);
|
||||
const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
// Ping an API here.
|
||||
setError("");
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
// strip data.
|
||||
const emailverificationToken: EmailVerificationToken =
|
||||
new EmailVerificationToken();
|
||||
emailverificationToken.token = new ObjectID(
|
||||
Navigation.getLastParam()?.toString().replace('/', '') || ''
|
||||
);
|
||||
try {
|
||||
// strip data.
|
||||
const emailverificationToken: EmailVerificationToken =
|
||||
new EmailVerificationToken();
|
||||
emailverificationToken.token = new ObjectID(
|
||||
Navigation.getLastParam()?.toString().replace("/", "") || "",
|
||||
);
|
||||
|
||||
await ModelAPI.createOrUpdate<EmailVerificationToken>({
|
||||
model: emailverificationToken,
|
||||
modelType: EmailVerificationToken,
|
||||
formType: FormType.Create,
|
||||
miscDataProps: {},
|
||||
requestOptions: {
|
||||
overrideRequestUrl: apiUrl,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
setError(API.getFriendlyMessage(err));
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
init().catch((err: Error) => {
|
||||
setError(err.toString());
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
await ModelAPI.createOrUpdate<EmailVerificationToken>({
|
||||
model: emailverificationToken,
|
||||
modelType: EmailVerificationToken,
|
||||
formType: FormType.Create,
|
||||
miscDataProps: {},
|
||||
requestOptions: {
|
||||
overrideRequestUrl: apiUrl,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
setError(API.getFriendlyMessage(err));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="auth-page">
|
||||
<div className="container-fluid p-0">
|
||||
<div className="row g-0">
|
||||
<div className="col-xxl-4 col-lg-4 col-md-3"></div>
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
<div className="col-xxl-4 col-lg-4 col-md-6">
|
||||
<div className="auth-full-page-content d-flex p-sm-5 p-4">
|
||||
<div className="w-100">
|
||||
<div className="d-flex flex-column h-100">
|
||||
<div className="auth-content my-auto">
|
||||
<div
|
||||
className="mt-4 text-center flex justify-center"
|
||||
style={{ marginBottom: '40px' }}
|
||||
>
|
||||
<img
|
||||
style={{ height: '50px' }}
|
||||
src={`${OneUptimeLogo}`}
|
||||
/>
|
||||
</div>
|
||||
{!error && (
|
||||
<div className="text-center">
|
||||
<h5 className="mb-0">
|
||||
Your email is verified.
|
||||
</h5>
|
||||
<p className="text-muted mt-2 mb-0">
|
||||
Thank you for verifying your
|
||||
email. You can now log in to
|
||||
OneUptime.{' '}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
useEffect(() => {
|
||||
init().catch((err: Error) => {
|
||||
setError(err.toString());
|
||||
});
|
||||
}, []);
|
||||
|
||||
{error && (
|
||||
<div className="text-center">
|
||||
<h5 className="mb-0">
|
||||
Sorry, something went wrong!
|
||||
</h5>
|
||||
<p className="text-muted mt-2 mb-0">
|
||||
{error}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
<div className="mt-5 text-center">
|
||||
<p className="text-muted mb-0">
|
||||
Return to sign in?{' '}
|
||||
<Link
|
||||
to={
|
||||
new Route(
|
||||
'/accounts/login'
|
||||
)
|
||||
}
|
||||
className="hover:underline text-primary fw-semibold"
|
||||
>
|
||||
Login.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<div className="auth-page">
|
||||
<div className="container-fluid p-0">
|
||||
<div className="row g-0">
|
||||
<div className="col-xxl-4 col-lg-4 col-md-3"></div>
|
||||
|
||||
<div className="col-xxl-4 col-lg-4 col-md-6">
|
||||
<div className="auth-full-page-content d-flex p-sm-5 p-4">
|
||||
<div className="w-100">
|
||||
<div className="d-flex flex-column h-100">
|
||||
<div className="auth-content my-auto">
|
||||
<div
|
||||
className="mt-4 text-center flex justify-center"
|
||||
style={{ marginBottom: "40px" }}
|
||||
>
|
||||
<img
|
||||
style={{ height: "50px" }}
|
||||
src={`${OneUptimeLogo}`}
|
||||
/>
|
||||
</div>
|
||||
{!error && (
|
||||
<div className="text-center">
|
||||
<h5 className="mb-0">Your email is verified.</h5>
|
||||
<p className="text-muted mt-2 mb-0">
|
||||
Thank you for verifying your email. You can now log in
|
||||
to OneUptime.{" "}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="col-xxl-4 col-lg-4 col-md-3"></div>
|
||||
{error && (
|
||||
<div className="text-center">
|
||||
<h5 className="mb-0">Sorry, something went wrong!</h5>
|
||||
<p className="text-muted mt-2 mb-0">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-5 text-center">
|
||||
<p className="text-muted mb-0">
|
||||
Return to sign in?{" "}
|
||||
<Link
|
||||
to={new Route("/accounts/login")}
|
||||
className="hover:underline text-primary fw-semibold"
|
||||
>
|
||||
Login.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-xxl-4 col-lg-4 col-md-3"></div>
|
||||
</div>
|
||||
);
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default VerifyEmail;
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { IDENTITY_URL } from 'CommonUI/src/Config';
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { IDENTITY_URL } from "Common/UI/Config";
|
||||
|
||||
export const SIGNUP_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route('/signup')
|
||||
new Route("/signup"),
|
||||
);
|
||||
export const LOGIN_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route('/login')
|
||||
new Route("/login"),
|
||||
);
|
||||
|
||||
export const VERIFY_TWO_FACTOR_AUTH_API_URL: URL = URL.fromURL(
|
||||
IDENTITY_URL,
|
||||
).addRoute(new Route("/verify-two-factor-auth"));
|
||||
|
||||
export const SERVICE_PROVIDER_LOGIN_URL: URL = URL.fromURL(
|
||||
IDENTITY_URL,
|
||||
).addRoute(new Route("/service-provider-login"));
|
||||
|
||||
export const FORGOT_PASSWORD_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route('/forgot-password')
|
||||
new Route("/forgot-password"),
|
||||
);
|
||||
|
||||
export const VERIFY_EMAIL_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route('/verify-email')
|
||||
new Route("/verify-email"),
|
||||
);
|
||||
|
||||
export const RESET_PASSWORD_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route('/reset-password')
|
||||
new Route("/reset-password"),
|
||||
);
|
||||
|
||||
@@ -1,83 +1,84 @@
|
||||
require('ts-loader');
|
||||
require('file-loader');
|
||||
require('style-loader');
|
||||
require('css-loader');
|
||||
require('sass-loader');
|
||||
require("ts-loader");
|
||||
require("file-loader");
|
||||
require("style-loader");
|
||||
require("css-loader");
|
||||
require("sass-loader");
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const dotenv = require('dotenv');
|
||||
const express = require('express');
|
||||
const dotenv = require("dotenv");
|
||||
const express = require("express");
|
||||
|
||||
const readEnvFile = (pathToFile) => {
|
||||
const parsed = dotenv.config({ path: pathToFile }).parsed;
|
||||
|
||||
const parsed = dotenv.config({ path: pathToFile }).parsed;
|
||||
const env = {};
|
||||
|
||||
const env = {
|
||||
};
|
||||
for (const key in parsed) {
|
||||
env[key] = JSON.stringify(parsed[key]);
|
||||
}
|
||||
|
||||
for (const key in parsed) {
|
||||
env[key] = JSON.stringify(parsed[key]);
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
return env;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
entry: "./src/Index.tsx",
|
||||
mode: "development",
|
||||
output: {
|
||||
filename: "bundle.js",
|
||||
path: path.resolve(__dirname, "public", "dist"),
|
||||
publicPath: "/accounts/dist/",
|
||||
entry: "./src/Index.tsx",
|
||||
mode: "development",
|
||||
output: {
|
||||
filename: "bundle.js",
|
||||
path: path.resolve(__dirname, "public", "dist"),
|
||||
publicPath: "/accounts/dist/",
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".ts", ".tsx", ".js", ".jsx", ".json", ".css", ".scss"],
|
||||
alias: {
|
||||
react: path.resolve("./node_modules/react"),
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json', '.css', '.scss'],
|
||||
alias: {
|
||||
react: path.resolve('./node_modules/react'),
|
||||
}
|
||||
},
|
||||
externals: {
|
||||
'react-native-sqlite-storage': 'react-native-sqlite-storage'
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process': {
|
||||
'env': {
|
||||
...readEnvFile('/usr/src/app/dev-env/.env')
|
||||
}
|
||||
}
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
use: 'ts-loader'
|
||||
},
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
use: ['style-loader', 'css-loader', "sass-loader"]
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||
loader: 'file-loader'
|
||||
}
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
devMiddleware: {
|
||||
writeToDisk: true,
|
||||
},
|
||||
externals: {
|
||||
"react-native-sqlite-storage": "react-native-sqlite-storage",
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
process: {
|
||||
env: {
|
||||
...readEnvFile("/usr/src/app/dev-env/.env"),
|
||||
},
|
||||
allowedHosts: "all",
|
||||
setupMiddlewares: (middlewares, devServer) => {
|
||||
devServer.app.use('/accounts/assets', express.static(path.resolve(__dirname, 'public', 'assets')));
|
||||
return middlewares;
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
use: "ts-loader",
|
||||
},
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
use: ["style-loader", "css-loader", "sass-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ["style-loader", "css-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||
loader: "file-loader",
|
||||
},
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
devMiddleware: {
|
||||
writeToDisk: true,
|
||||
},
|
||||
devtool: 'eval-source-map',
|
||||
}
|
||||
allowedHosts: "all",
|
||||
setupMiddlewares: (middlewares, devServer) => {
|
||||
devServer.app.use(
|
||||
"/accounts/assets",
|
||||
express.static(path.resolve(__dirname, "public", "assets")),
|
||||
);
|
||||
return middlewares;
|
||||
},
|
||||
},
|
||||
devtool: "eval-source-map",
|
||||
};
|
||||
|
||||
@@ -35,33 +35,17 @@ RUN npm install
|
||||
COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
# Set version in ./Model/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/Model/package.json
|
||||
RUN npm install
|
||||
COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
# Set version in ./CommonServer/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/CommonServer/package.json
|
||||
RUN npm install
|
||||
COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
|
||||
|
||||
# Install CommonUI
|
||||
|
||||
WORKDIR /usr/src/CommonUI
|
||||
COPY ./CommonUI/package*.json /usr/src/CommonUI/
|
||||
# Set version in ./CommonUI/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/CommonUI/package.json
|
||||
RUN npm install --force
|
||||
COPY ./CommonUI /usr/src/CommonUI
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
import { PromiseVoidFunction } from 'Common/Types/FunctionTypes';
|
||||
import Express, { ExpressApplication } from 'CommonServer/Utils/Express';
|
||||
import logger from 'CommonServer/Utils/Logger';
|
||||
import App from 'CommonServer/Utils/StartServer';
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import Express, { ExpressApplication } from "Common/Server/Utils/Express";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import App from "Common/Server/Utils/StartServer";
|
||||
|
||||
export const APP_NAME: string = 'admin';
|
||||
export const APP_NAME: string = "admin";
|
||||
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
|
||||
const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
try {
|
||||
// init the app
|
||||
await App.init({
|
||||
appName: APP_NAME,
|
||||
port: undefined,
|
||||
isFrontendApp: true,
|
||||
statusOptions: {
|
||||
liveCheck: async () => {},
|
||||
readyCheck: async () => {},
|
||||
},
|
||||
});
|
||||
try {
|
||||
// init the app
|
||||
await App.init({
|
||||
appName: APP_NAME,
|
||||
port: undefined,
|
||||
isFrontendApp: true,
|
||||
statusOptions: {
|
||||
liveCheck: async () => {},
|
||||
readyCheck: async () => {},
|
||||
},
|
||||
});
|
||||
|
||||
// add default routes
|
||||
await App.addDefaultRoutes();
|
||||
} catch (err) {
|
||||
logger.error('App Init Failed:');
|
||||
logger.error(err);
|
||||
throw err;
|
||||
}
|
||||
// add default routes
|
||||
await App.addDefaultRoutes();
|
||||
} catch (err) {
|
||||
logger.error("App Init Failed:");
|
||||
logger.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
init().catch((err: Error) => {
|
||||
logger.error(err);
|
||||
logger.error('Exiting node process');
|
||||
process.exit(1);
|
||||
logger.error(err);
|
||||
logger.error("Exiting node process");
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
export default app;
|
||||
|
||||
8
AdminDashboard/index.d.ts
vendored
8
AdminDashboard/index.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
declare module '*.png';
|
||||
declare module '*.svg';
|
||||
declare module '*.jpg';
|
||||
declare module '*.gif';
|
||||
declare module "*.png";
|
||||
declare module "*.svg";
|
||||
declare module "*.jpg";
|
||||
declare module "*.gif";
|
||||
|
||||
116
AdminDashboard/package-lock.json
generated
116
AdminDashboard/package-lock.json
generated
@@ -9,11 +9,9 @@
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"CommonServer": "file:../CommonServer",
|
||||
"CommonUI": "file:../CommonUI",
|
||||
|
||||
"dotenv": "^16.4.5",
|
||||
"file-loader": "^6.2.0",
|
||||
"Model": "file:../Model",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.23.1",
|
||||
@@ -39,25 +37,81 @@
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.24.6",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@nivo/core": "^0.87.0",
|
||||
"@nivo/line": "^0.87.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/context-zone": "^1.25.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.52.0",
|
||||
"@opentelemetry/instrumentation": "^0.52.0",
|
||||
"@opentelemetry/instrumentation-fetch": "^0.52.1",
|
||||
"@opentelemetry/instrumentation-xml-http-request": "^0.52.1",
|
||||
"@opentelemetry/resources": "^1.25.0",
|
||||
"@opentelemetry/sdk-trace-web": "^1.23.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.25.0",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"axios": "^1.6.8",
|
||||
"axios": "^1.7.2",
|
||||
"Common": "file:../Common",
|
||||
"crypto-js": "^4.1.1",
|
||||
"formik": "^2.4.6",
|
||||
"history": "^5.3.0",
|
||||
"json5": "^2.2.3",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"posthog-js": "^1.130.1",
|
||||
"posthog-js": "^1.139.6",
|
||||
"prop-types": "^15.8.1",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.3.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-big-calendar": "^1.13.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-dropzone": "^14.2.2",
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-highlight": "^0.15.0",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-router-dom": "^6.24.1",
|
||||
"react-select": "^5.4.0",
|
||||
"react-spinners": "^0.14.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactflow": "^11.11.4",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"slugify": "^1.6.5",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"tippy.js": "^6.3.7",
|
||||
"typeorm": "^0.3.20",
|
||||
"universal-cookie": "^4.0.4",
|
||||
"use-async-effect": "^2.2.6",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^17.0.22",
|
||||
"jest": "^27.5.1",
|
||||
"ts-jest": "^27.1.4"
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/jest": "^28.1.4",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "^17.0.45",
|
||||
"@types/react": "^18.2.38",
|
||||
"@types/react-beautiful-dnd": "^13.1.2",
|
||||
"@types/react-big-calendar": "^1.8.5",
|
||||
"@types/react-color": "^3.0.6",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"@types/react-toggle": "^4.0.3",
|
||||
"jest": "^28.1.1",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"react-test-renderer": "^18.2.0",
|
||||
"ts-jest": "^28.0.5"
|
||||
}
|
||||
},
|
||||
"../CommonServer": {
|
||||
@@ -78,6 +132,7 @@
|
||||
"@opentelemetry/sdk-metrics": "^1.21.0",
|
||||
"@opentelemetry/sdk-node": "^0.48.0",
|
||||
"@opentelemetry/sdk-trace-node": "^1.21.0",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"acme-client": "^5.3.0",
|
||||
"airtable": "^0.12.2",
|
||||
"bullmq": "^5.3.3",
|
||||
@@ -85,6 +140,7 @@
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"cron-parser": "^4.8.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dotenv": "^16.4.4",
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^4.19.2",
|
||||
@@ -92,9 +148,9 @@
|
||||
"json2csv": "^5.0.7",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"marked": "^12.0.2",
|
||||
"Model": "file:../Model",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^6.9.10",
|
||||
"otpauth": "^9.3.1",
|
||||
"pg": "^8.7.3",
|
||||
"redis-semaphore": "^5.5.1",
|
||||
"socket.io": "^4.7.4",
|
||||
@@ -123,42 +179,47 @@
|
||||
"../CommonUI": {
|
||||
"name": "@oneuptime/common-ui",
|
||||
"version": "1.0.0",
|
||||
"extraneous": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.24.1",
|
||||
"@babel/runtime": "^7.24.6",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@nivo/core": "^0.86.0",
|
||||
"@nivo/line": "^0.86.0",
|
||||
"@opentelemetry/api": "^1.8.0",
|
||||
"@opentelemetry/context-zone": "^1.23.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.51.0",
|
||||
"@opentelemetry/instrumentation": "^0.51.0",
|
||||
"@opentelemetry/instrumentation-fetch": "^0.51.0",
|
||||
"@opentelemetry/instrumentation-xml-http-request": "^0.51.0",
|
||||
"@opentelemetry/resources": "^1.23.0",
|
||||
"@nivo/core": "^0.87.0",
|
||||
"@nivo/line": "^0.87.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/context-zone": "^1.25.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.52.0",
|
||||
"@opentelemetry/instrumentation": "^0.52.0",
|
||||
"@opentelemetry/instrumentation-fetch": "^0.52.0",
|
||||
"@opentelemetry/instrumentation-xml-http-request": "^0.52.1",
|
||||
"@opentelemetry/resources": "^1.25.0",
|
||||
"@opentelemetry/sdk-trace-web": "^1.23.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.23.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.25.0",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"Common": "file:../Common",
|
||||
"CommonProject": "file:../CommonProject",
|
||||
"formik": "^2.4.6",
|
||||
"history": "^5.3.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"Model": "file:../Model",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.3.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-big-calendar": "^1.11.2",
|
||||
"react-big-calendar": "^1.13.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-dropzone": "^14.2.2",
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-highlight": "^0.15.0",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"react-select": "^5.4.0",
|
||||
"react-spinners": "^0.13.6",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactflow": "^11.11.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
@@ -190,6 +251,7 @@
|
||||
"../Model": {
|
||||
"name": "@oneuptime/model",
|
||||
"version": "1.0.0",
|
||||
"extraneous": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
@@ -6874,10 +6936,6 @@
|
||||
"resolved": "../CommonServer",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/CommonUI": {
|
||||
"resolved": "../CommonUI",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/compressible": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
||||
@@ -14250,10 +14308,6 @@
|
||||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/Model": {
|
||||
"resolved": "../Model",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
"private": false,
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"CommonServer": "file:../CommonServer",
|
||||
"CommonUI": "file:../CommonUI",
|
||||
|
||||
"dotenv": "^16.4.5",
|
||||
"file-loader": "^6.2.0",
|
||||
"Model": "file:../Model",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.23.1",
|
||||
|
||||
@@ -1,112 +1,127 @@
|
||||
import MasterPage from './Components/MasterPage/MasterPage';
|
||||
import Init from './Pages/Init/Init';
|
||||
import Logout from './Pages/Logout/Logout';
|
||||
import Projects from './Pages/Projects/Index';
|
||||
import SettingsAPIKey from './Pages/Settings/APIKey/Index';
|
||||
import SettingsAuthentication from './Pages/Settings/Authentication/Index';
|
||||
import SettingsCallSMS from './Pages/Settings/CallSMS/Index';
|
||||
import MasterPage from "./Components/MasterPage/MasterPage";
|
||||
import Init from "./Pages/Init/Init";
|
||||
import Logout from "./Pages/Logout/Logout";
|
||||
import Projects from "./Pages/Projects/Index";
|
||||
import SettingsAPIKey from "./Pages/Settings/APIKey/Index";
|
||||
import SettingsAuthentication from "./Pages/Settings/Authentication/Index";
|
||||
import SettingsCallSMS from "./Pages/Settings/CallSMS/Index";
|
||||
// Settings Pages.
|
||||
import SettingsEmail from './Pages/Settings/Email/Index';
|
||||
import SettingsProbes from './Pages/Settings/Probes/Index';
|
||||
import Users from './Pages/Users/Index';
|
||||
import PageMap from './Utils/PageMap';
|
||||
import RouteMap from './Utils/RouteMap';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { ACCOUNTS_URL, DASHBOARD_URL } from 'CommonUI/src/Config';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import User from 'CommonUI/src/Utils/User';
|
||||
import React from 'react';
|
||||
import SettingsEmail from "./Pages/Settings/Email/Index";
|
||||
import SettingsProbes from "./Pages/Settings/Probes/Index";
|
||||
import Users from "./Pages/Users/Index";
|
||||
import PageMap from "./Utils/PageMap";
|
||||
import RouteMap from "./Utils/RouteMap";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { ACCOUNTS_URL, DASHBOARD_URL } from "Common/UI/Config";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import User from "Common/UI/Utils/User";
|
||||
import React from "react";
|
||||
import {
|
||||
Route as PageRoute,
|
||||
Routes,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useParams,
|
||||
} from 'react-router-dom';
|
||||
Route as PageRoute,
|
||||
Routes,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
import UserView from "./Pages/Users/View/Index";
|
||||
import UserDelete from "./Pages/Users/View/Delete";
|
||||
import ProjectView from "./Pages/Projects/View/Index";
|
||||
import ProjectDelete from "./Pages/Projects/View/Delete";
|
||||
|
||||
const App: () => JSX.Element = () => {
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
Navigation.setLocation(useLocation());
|
||||
Navigation.setParams(useParams());
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
Navigation.setLocation(useLocation());
|
||||
Navigation.setParams(useParams());
|
||||
|
||||
if (!User.isLoggedIn()) {
|
||||
if (Navigation.getQueryStringByName('sso_token')) {
|
||||
Navigation.navigate(
|
||||
URL.fromString(ACCOUNTS_URL.toString()).addQueryParam(
|
||||
'sso',
|
||||
'true'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
Navigation.navigate(URL.fromString(ACCOUNTS_URL.toString()));
|
||||
}
|
||||
if (!User.isLoggedIn()) {
|
||||
if (Navigation.getQueryStringByName("sso_token")) {
|
||||
Navigation.navigate(
|
||||
URL.fromString(ACCOUNTS_URL.toString()).addQueryParam("sso", "true"),
|
||||
);
|
||||
} else {
|
||||
Navigation.navigate(URL.fromString(ACCOUNTS_URL.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!User.isMasterAdmin()) {
|
||||
Navigation.navigate(URL.fromString(DASHBOARD_URL.toString()));
|
||||
}
|
||||
if (!User.isMasterAdmin()) {
|
||||
Navigation.navigate(URL.fromString(DASHBOARD_URL.toString()));
|
||||
}
|
||||
|
||||
return (
|
||||
<MasterPage>
|
||||
<Routes>
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.INIT]?.toString() || ''}
|
||||
element={<Init />}
|
||||
/>
|
||||
return (
|
||||
<MasterPage>
|
||||
<Routes>
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.INIT]?.toString() || ""}
|
||||
element={<Init />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.PROJECTS]?.toString() || ''}
|
||||
element={<Projects />}
|
||||
/>
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.PROJECTS]?.toString() || ""}
|
||||
element={<Projects />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.USERS]?.toString() || ''}
|
||||
element={<Users />}
|
||||
/>
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.USERS]?.toString() || ""}
|
||||
element={<Users />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.LOGOUT]?.toString() || ''}
|
||||
element={<Logout />}
|
||||
/>
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.USER_VIEW]?.toString() || ""}
|
||||
element={<UserView />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS]?.toString() || ''}
|
||||
element={<SettingsAuthentication />}
|
||||
/>
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.USER_DELETE]?.toString() || ""}
|
||||
element={<UserDelete />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_SMTP]?.toString() || ''}
|
||||
element={<SettingsEmail />}
|
||||
/>
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.PROJECT_VIEW]?.toString() || ""}
|
||||
element={<ProjectView />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[PageMap.SETTINGS_CALL_AND_SMS]?.toString() ||
|
||||
''
|
||||
}
|
||||
element={<SettingsCallSMS />}
|
||||
/>
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.PROJECT_DELETE]?.toString() || ""}
|
||||
element={<ProjectDelete />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_PROBES]?.toString() || ''}
|
||||
element={<SettingsProbes />}
|
||||
/>
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.LOGOUT]?.toString() || ""}
|
||||
element={<Logout />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[PageMap.SETTINGS_AUTHENTICATION]?.toString() ||
|
||||
''
|
||||
}
|
||||
element={<SettingsAuthentication />}
|
||||
/>
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS]?.toString() || ""}
|
||||
element={<SettingsAuthentication />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_API_KEY]?.toString() || ''}
|
||||
element={<SettingsAPIKey />}
|
||||
/>
|
||||
</Routes>
|
||||
</MasterPage>
|
||||
);
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_SMTP]?.toString() || ""}
|
||||
element={<SettingsEmail />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_CALL_AND_SMS]?.toString() || ""}
|
||||
element={<SettingsCallSMS />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_PROBES]?.toString() || ""}
|
||||
element={<SettingsProbes />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_AUTHENTICATION]?.toString() || ""}
|
||||
element={<SettingsAuthentication />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_API_KEY]?.toString() || ""}
|
||||
element={<SettingsAPIKey />}
|
||||
/>
|
||||
</Routes>
|
||||
</MasterPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1,122 +1,114 @@
|
||||
import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import { PromiseVoidFunction } from 'Common/Types/FunctionTypes';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import API from 'Common/Utils/API';
|
||||
import Footer from 'CommonUI/src/Components/Footer/Footer';
|
||||
import ConfirmModal from 'CommonUI/src/Components/Modal/ConfirmModal';
|
||||
import { HOST, HTTP_PROTOCOL } from 'CommonUI/src/Config';
|
||||
import React from 'react';
|
||||
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import BadDataException from "Common/Types/Exception/BadDataException";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import API from "Common/Utils/API";
|
||||
import Footer from "Common/UI/Components/Footer/Footer";
|
||||
import ConfirmModal from "Common/UI/Components/Modal/ConfirmModal";
|
||||
import { HOST, HTTP_PROTOCOL } from "Common/UI/Config";
|
||||
import React from "react";
|
||||
|
||||
const DashboardFooter: () => JSX.Element = () => {
|
||||
const [showAboutModal, setShowAboutModal] = React.useState<boolean>(false);
|
||||
const [isAboutModalLoading, setIsAboutModalLoading] =
|
||||
React.useState<boolean>(false);
|
||||
const [versionText, setVersionText] = React.useState<Dictionary<string>>(
|
||||
{}
|
||||
const [showAboutModal, setShowAboutModal] = React.useState<boolean>(false);
|
||||
const [isAboutModalLoading, setIsAboutModalLoading] =
|
||||
React.useState<boolean>(false);
|
||||
const [versionText, setVersionText] = React.useState<Dictionary<string>>({});
|
||||
|
||||
const fetchVersions: PromiseVoidFunction = async (): Promise<void> => {
|
||||
setIsAboutModalLoading(true);
|
||||
|
||||
try {
|
||||
const verText: Dictionary<string> = {};
|
||||
const apps: Array<{
|
||||
name: string;
|
||||
path: string;
|
||||
}> = [
|
||||
{
|
||||
name: "API",
|
||||
path: "/api",
|
||||
},
|
||||
{
|
||||
name: "Dashboard",
|
||||
path: "/dashboard",
|
||||
},
|
||||
];
|
||||
|
||||
for (const app of apps) {
|
||||
const version: JSONObject = await fetchAppVersion(app.path);
|
||||
verText[app.name] =
|
||||
`${app.name}: ${version["version"]} (${version["commit"]})`;
|
||||
}
|
||||
|
||||
setVersionText(verText);
|
||||
} catch (err) {
|
||||
setVersionText({
|
||||
error: "Version data is not available: " + (err as Error).message,
|
||||
});
|
||||
}
|
||||
|
||||
setIsAboutModalLoading(false);
|
||||
};
|
||||
|
||||
const fetchAppVersion: (appName: string) => Promise<JSONObject> = async (
|
||||
appName: string,
|
||||
): Promise<JSONObject> => {
|
||||
const response: HTTPResponse<JSONObject> = await API.get<JSONObject>(
|
||||
URL.fromString(`${HTTP_PROTOCOL}/${HOST}${appName}/version`),
|
||||
);
|
||||
|
||||
const fetchVersions: PromiseVoidFunction = async (): Promise<void> => {
|
||||
setIsAboutModalLoading(true);
|
||||
if (response.data) {
|
||||
return response.data as JSONObject;
|
||||
}
|
||||
throw new BadDataException("Version data is not available");
|
||||
};
|
||||
|
||||
try {
|
||||
const verText: Dictionary<string> = {};
|
||||
const apps: Array<{
|
||||
name: string;
|
||||
path: string;
|
||||
}> = [
|
||||
{
|
||||
name: 'API',
|
||||
path: '/api',
|
||||
},
|
||||
{
|
||||
name: 'Dashboard',
|
||||
path: '/dashboard',
|
||||
},
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<Footer
|
||||
className="bg-white h-16 inset-x-0 bottom-0 px-8"
|
||||
copyright="HackerBay, Inc."
|
||||
links={[
|
||||
{
|
||||
title: "Help and Support",
|
||||
to: URL.fromString("https://oneuptime.com/support"),
|
||||
},
|
||||
{
|
||||
title: "Legal",
|
||||
to: URL.fromString("https://oneuptime.com/legal"),
|
||||
},
|
||||
{
|
||||
title: "Version",
|
||||
onClick: async () => {
|
||||
setShowAboutModal(true);
|
||||
await fetchVersions();
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
for (const app of apps) {
|
||||
const version: JSONObject = await fetchAppVersion(app.path);
|
||||
verText[
|
||||
app.name
|
||||
] = `${app.name}: ${version['version']} (${version['commit']})`;
|
||||
}
|
||||
|
||||
setVersionText(verText);
|
||||
} catch (err) {
|
||||
setVersionText({
|
||||
error:
|
||||
'Version data is not available: ' + (err as Error).message,
|
||||
});
|
||||
}
|
||||
|
||||
setIsAboutModalLoading(false);
|
||||
};
|
||||
|
||||
const fetchAppVersion: (appName: string) => Promise<JSONObject> = async (
|
||||
appName: string
|
||||
): Promise<JSONObject> => {
|
||||
const response: HTTPResponse<JSONObject> = await API.get<JSONObject>(
|
||||
URL.fromString(`${HTTP_PROTOCOL}/${HOST}${appName}/version`)
|
||||
);
|
||||
|
||||
if (response.data) {
|
||||
return response.data as JSONObject;
|
||||
}
|
||||
throw new BadDataException('Version data is not available');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Footer
|
||||
className="bg-white h-16 inset-x-0 bottom-0 px-8"
|
||||
copyright="HackerBay, Inc."
|
||||
links={[
|
||||
{
|
||||
title: 'Help and Support',
|
||||
to: URL.fromString('https://oneuptime.com/support'),
|
||||
},
|
||||
{
|
||||
title: 'Legal',
|
||||
to: URL.fromString('https://oneuptime.com/legal'),
|
||||
},
|
||||
{
|
||||
title: 'Version',
|
||||
onClick: async () => {
|
||||
setShowAboutModal(true);
|
||||
await fetchVersions();
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{showAboutModal ? (
|
||||
<ConfirmModal
|
||||
title={`OneUptime Version`}
|
||||
description={
|
||||
<div>
|
||||
{Object.keys(versionText).map(
|
||||
(key: string, i: number) => {
|
||||
return (
|
||||
<div key={i}>{versionText[key]}</div>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
isLoading={isAboutModalLoading}
|
||||
submitButtonText={'Close'}
|
||||
onSubmit={() => {
|
||||
return setShowAboutModal(false);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
{showAboutModal ? (
|
||||
<ConfirmModal
|
||||
title={`OneUptime Version`}
|
||||
description={
|
||||
<div>
|
||||
{Object.keys(versionText).map((key: string, i: number) => {
|
||||
return <div key={i}>{versionText[key]}</div>;
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
isLoading={isAboutModalLoading}
|
||||
submitButtonText={"Close"}
|
||||
onSubmit={() => {
|
||||
return setShowAboutModal(false);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardFooter;
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
import Help from './Help';
|
||||
import Logo from './Logo';
|
||||
import UserProfile from './UserProfile';
|
||||
import Button, { ButtonStyleType } from 'CommonUI/src/Components/Button/Button';
|
||||
import Header from 'CommonUI/src/Components/Header/Header';
|
||||
import { DASHBOARD_URL } from 'CommonUI/src/Config';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import Help from "./Help";
|
||||
import Logo from "./Logo";
|
||||
import UserProfile from "./UserProfile";
|
||||
import Button, { ButtonStyleType } from "Common/UI/Components/Button/Button";
|
||||
import Header from "Common/UI/Components/Header/Header";
|
||||
import { DASHBOARD_URL } from "Common/UI/Config";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const DashboardHeader: FunctionComponent = (): ReactElement => {
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
leftComponents={
|
||||
<>
|
||||
<Logo onClick={() => {}} />
|
||||
</>
|
||||
}
|
||||
centerComponents={
|
||||
<>
|
||||
{/* <SearchBox
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
leftComponents={
|
||||
<>
|
||||
<Logo onClick={() => {}} />
|
||||
</>
|
||||
}
|
||||
centerComponents={
|
||||
<>
|
||||
{/* <SearchBox
|
||||
key={2}
|
||||
selectedProject={props.selectedProject}
|
||||
onChange={(_value: string) => { }}
|
||||
/>{' '} */}
|
||||
</>
|
||||
}
|
||||
rightComponents={
|
||||
<>
|
||||
<Button
|
||||
title="Exit Admin"
|
||||
buttonStyle={ButtonStyleType.NORMAL}
|
||||
onClick={() => {
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
}}
|
||||
/>
|
||||
<Help />
|
||||
<UserProfile />
|
||||
</>
|
||||
}
|
||||
</>
|
||||
}
|
||||
rightComponents={
|
||||
<>
|
||||
<Button
|
||||
title="Exit Admin"
|
||||
buttonStyle={ButtonStyleType.NORMAL}
|
||||
onClick={() => {
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
<Help />
|
||||
<UserProfile />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardHeader;
|
||||
|
||||
@@ -1,60 +1,58 @@
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import HeaderIconDropdownButton from 'CommonUI/src/Components/Header/HeaderIconDropdownButton';
|
||||
import IconDropdownItem from 'CommonUI/src/Components/Header/IconDropdown/IconDropdownItem';
|
||||
import IconDropdownMenu from 'CommonUI/src/Components/Header/IconDropdown/IconDropdownMenu';
|
||||
import IconDropdownRow from 'CommonUI/src/Components/Header/IconDropdown/IconDropdownRow';
|
||||
import React, { ReactElement, useState } from 'react';
|
||||
import URL from "Common/Types/API/URL";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import HeaderIconDropdownButton from "Common/UI/Components/Header/HeaderIconDropdownButton";
|
||||
import IconDropdownItem from "Common/UI/Components/Header/IconDropdown/IconDropdownItem";
|
||||
import IconDropdownMenu from "Common/UI/Components/Header/IconDropdown/IconDropdownMenu";
|
||||
import IconDropdownRow from "Common/UI/Components/Header/IconDropdown/IconDropdownRow";
|
||||
import React, { ReactElement, useState } from "react";
|
||||
|
||||
const Help: () => JSX.Element = (): ReactElement => {
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState<boolean>(false);
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<HeaderIconDropdownButton
|
||||
icon={IconProp.Help}
|
||||
name="Help"
|
||||
showDropdown={isDropdownVisible}
|
||||
return (
|
||||
<HeaderIconDropdownButton
|
||||
icon={IconProp.Help}
|
||||
name="Help"
|
||||
showDropdown={isDropdownVisible}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(true);
|
||||
}}
|
||||
>
|
||||
<IconDropdownMenu>
|
||||
<IconDropdownRow>
|
||||
<IconDropdownItem
|
||||
title="Support Email"
|
||||
icon={IconProp.Email}
|
||||
openInNewTab={true}
|
||||
url={URL.fromString("mailto:support@oneuptime.com")}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(true);
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
>
|
||||
<IconDropdownMenu>
|
||||
<IconDropdownRow>
|
||||
<IconDropdownItem
|
||||
title="Support Email"
|
||||
icon={IconProp.Email}
|
||||
openInNewTab={true}
|
||||
url={URL.fromString('mailto:support@oneuptime.com')}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
/>
|
||||
<IconDropdownItem
|
||||
title="Chat on Slack"
|
||||
icon={IconProp.Slack}
|
||||
openInNewTab={true}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
url={URL.fromString(
|
||||
'https://join.slack.com/t/oneuptimesupport/shared_invite/zt-1kavkds2f-gegm_wePorvwvM3M_SaoCQ'
|
||||
)}
|
||||
/>
|
||||
<IconDropdownItem
|
||||
title="Request Demo"
|
||||
icon={IconProp.Window}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
openInNewTab={true}
|
||||
url={URL.fromString(
|
||||
'https://oneuptime.com/enterprise/demo'
|
||||
)}
|
||||
/>
|
||||
</IconDropdownRow>
|
||||
</IconDropdownMenu>
|
||||
</HeaderIconDropdownButton>
|
||||
);
|
||||
/>
|
||||
<IconDropdownItem
|
||||
title="Chat on Slack"
|
||||
icon={IconProp.Slack}
|
||||
openInNewTab={true}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
url={URL.fromString(
|
||||
"https://join.slack.com/t/oneuptimesupport/shared_invite/zt-1kavkds2f-gegm_wePorvwvM3M_SaoCQ",
|
||||
)}
|
||||
/>
|
||||
<IconDropdownItem
|
||||
title="Request Demo"
|
||||
icon={IconProp.Window}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
openInNewTab={true}
|
||||
url={URL.fromString("https://oneuptime.com/enterprise/demo")}
|
||||
/>
|
||||
</IconDropdownRow>
|
||||
</IconDropdownMenu>
|
||||
</HeaderIconDropdownButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default Help;
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
// Tailwind
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import Image from 'CommonUI/src/Components/Image/Image';
|
||||
import OneUptimeLogo from 'CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import Route from "Common/Types/API/Route";
|
||||
import Image from "Common/UI/Components/Image/Image";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
export interface ComponentProps {
|
||||
onClick: () => void;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const Logo: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
return (
|
||||
<div className="relative z-10 flex px-2 lg:px-0">
|
||||
<div className="flex flex-shrink-0 items-center">
|
||||
<Image
|
||||
className="block h-8 w-auto"
|
||||
onClick={() => {
|
||||
props.onClick && props.onClick();
|
||||
}}
|
||||
imageUrl={Route.fromString(`${OneUptimeLogo}`)}
|
||||
alt={'OneUptime'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="relative z-10 flex px-2 lg:px-0">
|
||||
<div className="flex flex-shrink-0 items-center">
|
||||
<Image
|
||||
className="block h-8 w-auto"
|
||||
onClick={() => {
|
||||
props.onClick && props.onClick();
|
||||
}}
|
||||
imageUrl={Route.fromString(`${OneUptimeLogo}`)}
|
||||
alt={"OneUptime"}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Logo;
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import HeaderIconDropdownButton from 'CommonUI/src/Components/Header/HeaderIconDropdownButton';
|
||||
import NotificationItem from 'CommonUI/src/Components/Header/Notifications/NotificationItem';
|
||||
import Notifications from 'CommonUI/src/Components/Header/Notifications/Notifications';
|
||||
import React, { ReactElement, useState } from 'react';
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import HeaderIconDropdownButton from "Common/UI/Components/Header/HeaderIconDropdownButton";
|
||||
import NotificationItem from "Common/UI/Components/Header/Notifications/NotificationItem";
|
||||
import Notifications from "Common/UI/Components/Header/Notifications/Notifications";
|
||||
import React, { ReactElement, useState } from "react";
|
||||
|
||||
const DashboardHeader: () => JSX.Element = (): ReactElement => {
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState<boolean>(false);
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<HeaderIconDropdownButton
|
||||
name="Notifications"
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(true);
|
||||
}}
|
||||
showDropdown={isDropdownVisible}
|
||||
icon={IconProp.Notification}
|
||||
badge={4}
|
||||
>
|
||||
<Notifications>
|
||||
<NotificationItem
|
||||
title="Sample Title"
|
||||
description="Sample Description"
|
||||
createdAt={new Date()}
|
||||
icon={IconProp.Home}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
/>
|
||||
</Notifications>
|
||||
</HeaderIconDropdownButton>
|
||||
);
|
||||
return (
|
||||
<HeaderIconDropdownButton
|
||||
name="Notifications"
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(true);
|
||||
}}
|
||||
showDropdown={isDropdownVisible}
|
||||
icon={IconProp.Notification}
|
||||
badge={4}
|
||||
>
|
||||
<Notifications>
|
||||
<NotificationItem
|
||||
title="Sample Title"
|
||||
description="Sample Description"
|
||||
createdAt={new Date()}
|
||||
icon={IconProp.Home}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
/>
|
||||
</Notifications>
|
||||
</HeaderIconDropdownButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardHeader;
|
||||
|
||||
@@ -1,286 +1,273 @@
|
||||
import SubscriptionPlan from 'Common/Types/Billing/SubscriptionPlan';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import { FormType } from 'CommonUI/src/Components/Forms/ModelForm';
|
||||
import Field from 'CommonUI/src/Components/Forms/Types/Field';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import ProjectPicker from 'CommonUI/src/Components/Header/ProjectPicker/ProjectPicker';
|
||||
import ModelFormModal from 'CommonUI/src/Components/ModelFormModal/ModelFormModal';
|
||||
import { RadioButton } from 'CommonUI/src/Components/RadioButtons/GroupRadioButtons';
|
||||
import Toggle from 'CommonUI/src/Components/Toggle/Toggle';
|
||||
import { BILLING_ENABLED, getAllEnvVars } from 'CommonUI/src/Config';
|
||||
import { GetReactElementFunction } from 'CommonUI/src/Types/FunctionTypes';
|
||||
import ProjectUtil from 'CommonUI/src/Utils/Project';
|
||||
import Project from 'Model/Models/Project';
|
||||
import SubscriptionPlan from "Common/Types/Billing/SubscriptionPlan";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import Field from "Common/UI/Components/Forms/Types/Field";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ProjectPicker from "Common/UI/Components/Header/ProjectPicker/ProjectPicker";
|
||||
import ModelFormModal from "Common/UI/Components/ModelFormModal/ModelFormModal";
|
||||
import { RadioButton } from "Common/UI/Components/RadioButtons/GroupRadioButtons";
|
||||
import Toggle from "Common/UI/Components/Toggle/Toggle";
|
||||
import { BILLING_ENABLED, getAllEnvVars } from "Common/UI/Config";
|
||||
import { GetReactElementFunction } from "Common/UI/Types/FunctionTypes";
|
||||
import ProjectUtil from "Common/UI/Utils/Project";
|
||||
import Project from "Common/Models/DatabaseModels/Project";
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
|
||||
export interface ComponentProps {
|
||||
projects: Array<Project>;
|
||||
onProjectSelected: (project: Project) => void;
|
||||
showProjectModal: boolean;
|
||||
onProjectModalClose: () => void;
|
||||
projects: Array<Project>;
|
||||
onProjectSelected: (project: Project) => void;
|
||||
showProjectModal: boolean;
|
||||
onProjectModalClose: () => void;
|
||||
}
|
||||
|
||||
const DashboardProjectPicker: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
const [showModal, setShowModal] = useState<boolean>(false);
|
||||
const [selectedProject, setSelectedProject] = useState<Project | null>(
|
||||
null
|
||||
);
|
||||
const [showModal, setShowModal] = useState<boolean>(false);
|
||||
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
|
||||
|
||||
const getFooter: GetReactElementFunction = (): ReactElement => {
|
||||
if (!BILLING_ENABLED) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Toggle
|
||||
title="Yearly Plan"
|
||||
value={isSubscriptionPlanYearly}
|
||||
description="(Save 20%)"
|
||||
onChange={(value: boolean) => {
|
||||
setIsSubscriptionPlanYearly(value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const [isSubscriptionPlanYearly, setIsSubscriptionPlanYearly] =
|
||||
useState<boolean>(true);
|
||||
|
||||
const [fields, setFields] = useState<Array<Field<Project>>>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.showProjectModal) {
|
||||
setShowModal(true);
|
||||
}
|
||||
}, [props.showProjectModal]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentProject: Project | null = ProjectUtil.getCurrentProject();
|
||||
setSelectedProject(currentProject);
|
||||
if (currentProject && props.onProjectSelected) {
|
||||
props.onProjectSelected(currentProject);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProject) {
|
||||
ProjectUtil.setCurrentProject(selectedProject);
|
||||
if (props.onProjectSelected) {
|
||||
props.onProjectSelected(selectedProject);
|
||||
}
|
||||
}
|
||||
}, [selectedProject]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
props.projects &&
|
||||
props.projects.length > 0 &&
|
||||
!selectedProject &&
|
||||
props.projects[0]
|
||||
) {
|
||||
const currentProject: Project | null =
|
||||
ProjectUtil.getCurrentProject();
|
||||
|
||||
if (!currentProject) {
|
||||
setSelectedProject(props.projects[0]);
|
||||
} else if (
|
||||
props.projects.filter((project: Project) => {
|
||||
return project._id === currentProject._id;
|
||||
}).length > 0
|
||||
) {
|
||||
setSelectedProject(
|
||||
props.projects.filter((project: Project) => {
|
||||
return project._id === currentProject._id;
|
||||
})[0] as Project
|
||||
);
|
||||
} else {
|
||||
setSelectedProject(props.projects[0]);
|
||||
}
|
||||
}
|
||||
}, [props.projects]);
|
||||
|
||||
useEffect(() => {
|
||||
refreshFields();
|
||||
}, [isSubscriptionPlanYearly]);
|
||||
|
||||
const refreshFields: VoidFunction = (): void => {
|
||||
let formFields: Array<Field<Project>> = [
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
validation: {
|
||||
minLength: 4,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: 'My Project',
|
||||
description: 'Pick a friendly name.',
|
||||
title: 'Project Name',
|
||||
required: true,
|
||||
stepId: BILLING_ENABLED ? 'basic' : undefined,
|
||||
},
|
||||
];
|
||||
|
||||
if (BILLING_ENABLED) {
|
||||
formFields = [
|
||||
...formFields,
|
||||
{
|
||||
field: {
|
||||
paymentProviderPlanId: true,
|
||||
},
|
||||
stepId: 'plan',
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
footerElement: getFooter(),
|
||||
fieldType: FormFieldSchemaType.RadioButton,
|
||||
radioButtonOptions: SubscriptionPlan.getSubscriptionPlans(
|
||||
getAllEnvVars()
|
||||
).map((plan: SubscriptionPlan): RadioButton => {
|
||||
let description: string = plan.isCustomPricing()
|
||||
? `Our sales team will contact you soon.`
|
||||
: `Billed ${
|
||||
isSubscriptionPlanYearly
|
||||
? 'yearly'
|
||||
: 'monthly'
|
||||
}. ${
|
||||
plan.getTrialPeriod() > 0
|
||||
? `Free ${plan.getTrialPeriod()} days trial.`
|
||||
: ''
|
||||
}`;
|
||||
|
||||
if (
|
||||
isSubscriptionPlanYearly &&
|
||||
plan.getYearlySubscriptionAmountInUSD() === 0
|
||||
) {
|
||||
description = 'This plan is free, forever. ';
|
||||
}
|
||||
|
||||
if (
|
||||
!isSubscriptionPlanYearly &&
|
||||
plan.getMonthlySubscriptionAmountInUSD() === 0
|
||||
) {
|
||||
description = 'This plan is free, forever. ';
|
||||
}
|
||||
|
||||
return {
|
||||
value: isSubscriptionPlanYearly
|
||||
? plan.getYearlyPlanId()
|
||||
: plan.getMonthlyPlanId(),
|
||||
title: plan.getName(),
|
||||
description: description,
|
||||
sideTitle: plan.isCustomPricing()
|
||||
? 'Custom Price'
|
||||
: isSubscriptionPlanYearly
|
||||
? '$' +
|
||||
plan
|
||||
.getYearlySubscriptionAmountInUSD()
|
||||
.toString() +
|
||||
'/mo billed yearly'
|
||||
: '$' +
|
||||
plan
|
||||
.getMonthlySubscriptionAmountInUSD()
|
||||
.toString(),
|
||||
sideDescription: plan.isCustomPricing()
|
||||
? ''
|
||||
: isSubscriptionPlanYearly
|
||||
? `~ $${
|
||||
plan.getYearlySubscriptionAmountInUSD() *
|
||||
12
|
||||
} per user / year`
|
||||
: `/month per user`,
|
||||
};
|
||||
}),
|
||||
title: 'Please select a plan.',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
paymentProviderPromoCode: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: 'Promo Code (Optional)',
|
||||
description: 'If you have a coupon code, enter it here.',
|
||||
title: 'Promo Code',
|
||||
required: false,
|
||||
stepId: 'plan',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
setFields(formFields);
|
||||
};
|
||||
const getFooter: GetReactElementFunction = (): ReactElement => {
|
||||
if (!BILLING_ENABLED) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.projects.length !== 0 && (
|
||||
<ProjectPicker
|
||||
selectedProjectName={selectedProject?.name || ''}
|
||||
selectedProjectIcon={IconProp.Folder}
|
||||
projects={props.projects}
|
||||
onCreateProjectButtonClicked={() => {
|
||||
setShowModal(true);
|
||||
props.onProjectModalClose();
|
||||
}}
|
||||
onProjectSelected={(project: Project) => {
|
||||
setSelectedProject(project);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showModal ? (
|
||||
<ModelFormModal<Project>
|
||||
modelType={Project}
|
||||
name="Create New Project"
|
||||
title="Create New Project"
|
||||
description="Please create a new OneUptime project to get started."
|
||||
onClose={() => {
|
||||
setShowModal(false);
|
||||
props.onProjectModalClose();
|
||||
}}
|
||||
submitButtonText="Create Project"
|
||||
onSuccess={(project: Project | null) => {
|
||||
setSelectedProject(project);
|
||||
if (project && props.onProjectSelected) {
|
||||
props.onProjectSelected(project);
|
||||
}
|
||||
setShowModal(false);
|
||||
props.onProjectModalClose();
|
||||
}}
|
||||
formProps={{
|
||||
name: 'Create New Project',
|
||||
steps: BILLING_ENABLED
|
||||
? [
|
||||
{
|
||||
title: 'Basic',
|
||||
id: 'basic',
|
||||
},
|
||||
{
|
||||
title: 'Select Plan',
|
||||
id: 'plan',
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
saveRequestOptions: {
|
||||
isMultiTenantRequest: true, // because this is a tenant request, we do not have to include the header in the request
|
||||
},
|
||||
modelType: Project,
|
||||
id: 'create-project-from',
|
||||
fields: [...fields],
|
||||
formType: FormType.Create,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
<Toggle
|
||||
title="Yearly Plan"
|
||||
value={isSubscriptionPlanYearly}
|
||||
description="(Save 20%)"
|
||||
onChange={(value: boolean) => {
|
||||
setIsSubscriptionPlanYearly(value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const [isSubscriptionPlanYearly, setIsSubscriptionPlanYearly] =
|
||||
useState<boolean>(true);
|
||||
|
||||
const [fields, setFields] = useState<Array<Field<Project>>>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.showProjectModal) {
|
||||
setShowModal(true);
|
||||
}
|
||||
}, [props.showProjectModal]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentProject: Project | null = ProjectUtil.getCurrentProject();
|
||||
setSelectedProject(currentProject);
|
||||
if (currentProject && props.onProjectSelected) {
|
||||
props.onProjectSelected(currentProject);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProject) {
|
||||
ProjectUtil.setCurrentProject(selectedProject);
|
||||
if (props.onProjectSelected) {
|
||||
props.onProjectSelected(selectedProject);
|
||||
}
|
||||
}
|
||||
}, [selectedProject]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
props.projects &&
|
||||
props.projects.length > 0 &&
|
||||
!selectedProject &&
|
||||
props.projects[0]
|
||||
) {
|
||||
const currentProject: Project | null = ProjectUtil.getCurrentProject();
|
||||
|
||||
if (!currentProject) {
|
||||
setSelectedProject(props.projects[0]);
|
||||
} else if (
|
||||
props.projects.filter((project: Project) => {
|
||||
return project._id === currentProject._id;
|
||||
}).length > 0
|
||||
) {
|
||||
setSelectedProject(
|
||||
props.projects.filter((project: Project) => {
|
||||
return project._id === currentProject._id;
|
||||
})[0] as Project,
|
||||
);
|
||||
} else {
|
||||
setSelectedProject(props.projects[0]);
|
||||
}
|
||||
}
|
||||
}, [props.projects]);
|
||||
|
||||
useEffect(() => {
|
||||
refreshFields();
|
||||
}, [isSubscriptionPlanYearly]);
|
||||
|
||||
const refreshFields: VoidFunction = (): void => {
|
||||
let formFields: Array<Field<Project>> = [
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
validation: {
|
||||
minLength: 4,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: "My Project",
|
||||
description: "Pick a friendly name.",
|
||||
title: "Project Name",
|
||||
required: true,
|
||||
stepId: BILLING_ENABLED ? "basic" : undefined,
|
||||
},
|
||||
];
|
||||
|
||||
if (BILLING_ENABLED) {
|
||||
formFields = [
|
||||
...formFields,
|
||||
{
|
||||
field: {
|
||||
paymentProviderPlanId: true,
|
||||
},
|
||||
stepId: "plan",
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
footerElement: getFooter(),
|
||||
fieldType: FormFieldSchemaType.RadioButton,
|
||||
radioButtonOptions: SubscriptionPlan.getSubscriptionPlans(
|
||||
getAllEnvVars(),
|
||||
).map((plan: SubscriptionPlan): RadioButton => {
|
||||
let description: string = plan.isCustomPricing()
|
||||
? `Our sales team will contact you soon.`
|
||||
: `Billed ${isSubscriptionPlanYearly ? "yearly" : "monthly"}. ${
|
||||
plan.getTrialPeriod() > 0
|
||||
? `Free ${plan.getTrialPeriod()} days trial.`
|
||||
: ""
|
||||
}`;
|
||||
|
||||
if (
|
||||
isSubscriptionPlanYearly &&
|
||||
plan.getYearlySubscriptionAmountInUSD() === 0
|
||||
) {
|
||||
description = "This plan is free, forever. ";
|
||||
}
|
||||
|
||||
if (
|
||||
!isSubscriptionPlanYearly &&
|
||||
plan.getMonthlySubscriptionAmountInUSD() === 0
|
||||
) {
|
||||
description = "This plan is free, forever. ";
|
||||
}
|
||||
|
||||
return {
|
||||
value: isSubscriptionPlanYearly
|
||||
? plan.getYearlyPlanId()
|
||||
: plan.getMonthlyPlanId(),
|
||||
title: plan.getName(),
|
||||
description: description,
|
||||
sideTitle: plan.isCustomPricing()
|
||||
? "Custom Price"
|
||||
: isSubscriptionPlanYearly
|
||||
? "$" +
|
||||
plan.getYearlySubscriptionAmountInUSD().toString() +
|
||||
"/mo billed yearly"
|
||||
: "$" + plan.getMonthlySubscriptionAmountInUSD().toString(),
|
||||
sideDescription: plan.isCustomPricing()
|
||||
? ""
|
||||
: isSubscriptionPlanYearly
|
||||
? `~ $${
|
||||
plan.getYearlySubscriptionAmountInUSD() * 12
|
||||
} per user / year`
|
||||
: `/month per user`,
|
||||
};
|
||||
}),
|
||||
title: "Please select a plan.",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
paymentProviderPromoCode: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: "Promo Code (Optional)",
|
||||
description: "If you have a coupon code, enter it here.",
|
||||
title: "Promo Code",
|
||||
required: false,
|
||||
stepId: "plan",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
setFields(formFields);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.projects.length !== 0 && (
|
||||
<ProjectPicker
|
||||
selectedProjectName={selectedProject?.name || ""}
|
||||
selectedProjectIcon={IconProp.Folder}
|
||||
projects={props.projects}
|
||||
onCreateProjectButtonClicked={() => {
|
||||
setShowModal(true);
|
||||
props.onProjectModalClose();
|
||||
}}
|
||||
onProjectSelected={(project: Project) => {
|
||||
setSelectedProject(project);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showModal ? (
|
||||
<ModelFormModal<Project>
|
||||
modelType={Project}
|
||||
name="Create New Project"
|
||||
title="Create New Project"
|
||||
description="Please create a new OneUptime project to get started."
|
||||
onClose={() => {
|
||||
setShowModal(false);
|
||||
props.onProjectModalClose();
|
||||
}}
|
||||
submitButtonText="Create Project"
|
||||
onSuccess={(project: Project | null) => {
|
||||
setSelectedProject(project);
|
||||
if (project && props.onProjectSelected) {
|
||||
props.onProjectSelected(project);
|
||||
}
|
||||
setShowModal(false);
|
||||
props.onProjectModalClose();
|
||||
}}
|
||||
formProps={{
|
||||
name: "Create New Project",
|
||||
steps: BILLING_ENABLED
|
||||
? [
|
||||
{
|
||||
title: "Basic",
|
||||
id: "basic",
|
||||
},
|
||||
{
|
||||
title: "Select Plan",
|
||||
id: "plan",
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
saveRequestOptions: {
|
||||
isMultiTenantRequest: true, // because this is a tenant request, we do not have to include the header in the request
|
||||
},
|
||||
modelType: Project,
|
||||
id: "create-project-from",
|
||||
fields: [...fields],
|
||||
formType: FormType.Create,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardProjectPicker;
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import SearchBox from 'CommonUI/src/Components/Header/SearchBox';
|
||||
import Project from 'Model/Models/Project';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import SearchBox from "Common/UI/Components/Header/SearchBox";
|
||||
import Project from "Common/Models/DatabaseModels/Project";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
export interface ComponentProps {
|
||||
onChange: (search: string) => void;
|
||||
selectedProject: Project | null;
|
||||
onChange: (search: string) => void;
|
||||
selectedProject: Project | null;
|
||||
}
|
||||
|
||||
const Search: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
if (!props.selectedProject) {
|
||||
return <></>;
|
||||
}
|
||||
if (!props.selectedProject) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return <SearchBox key={2} onChange={props.onChange} />;
|
||||
return <SearchBox key={2} onChange={props.onChange} />;
|
||||
};
|
||||
|
||||
export default Search;
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import HeaderIconDropdownButton from 'CommonUI/src/Components/Header/HeaderIconDropdownButton';
|
||||
import IconDropdownItem from 'CommonUI/src/Components/Header/IconDropdown/IconDropdownItem';
|
||||
import IconDropdownMenu from 'CommonUI/src/Components/Header/IconDropdown/IconDropdownMenu';
|
||||
import { DASHBOARD_URL } from 'CommonUI/src/Config';
|
||||
import BlankProfilePic from 'CommonUI/src/Images/users/blank-profile.svg';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import User from 'CommonUI/src/Utils/User';
|
||||
import React, { FunctionComponent, ReactElement, useState } from 'react';
|
||||
import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import HeaderIconDropdownButton from "Common/UI/Components/Header/HeaderIconDropdownButton";
|
||||
import IconDropdownItem from "Common/UI/Components/Header/IconDropdown/IconDropdownItem";
|
||||
import IconDropdownMenu from "Common/UI/Components/Header/IconDropdown/IconDropdownMenu";
|
||||
import { DASHBOARD_URL } from "Common/UI/Config";
|
||||
import BlankProfilePic from "Common/UI/Images/users/blank-profile.svg";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import User from "Common/UI/Utils/User";
|
||||
import React, { FunctionComponent, ReactElement, useState } from "react";
|
||||
|
||||
const DashboardUserProfile: FunctionComponent = (): ReactElement => {
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState<boolean>(false);
|
||||
const [isDropdownVisible, setIsDropdownVisible] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderIconDropdownButton
|
||||
iconImageUrl={BlankProfilePic}
|
||||
name="User Profile"
|
||||
showDropdown={isDropdownVisible}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(true);
|
||||
}}
|
||||
>
|
||||
<IconDropdownMenu>
|
||||
{User.isMasterAdmin() ? (
|
||||
<IconDropdownItem
|
||||
title="Exit Admin"
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
}}
|
||||
icon={IconProp.ExternalLink}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
return (
|
||||
<>
|
||||
<HeaderIconDropdownButton
|
||||
iconImageUrl={BlankProfilePic}
|
||||
name="User Profile"
|
||||
showDropdown={isDropdownVisible}
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(true);
|
||||
}}
|
||||
>
|
||||
<IconDropdownMenu>
|
||||
{User.isMasterAdmin() ? (
|
||||
<IconDropdownItem
|
||||
title="Exit Admin"
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
}}
|
||||
icon={IconProp.ExternalLink}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<IconDropdownItem
|
||||
title="Log out"
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
url={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.LOGOUT] as Route
|
||||
)}
|
||||
icon={IconProp.Logout}
|
||||
/>
|
||||
</IconDropdownMenu>
|
||||
</HeaderIconDropdownButton>
|
||||
</>
|
||||
);
|
||||
<IconDropdownItem
|
||||
title="Log out"
|
||||
onClick={() => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
url={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.LOGOUT] as Route,
|
||||
)}
|
||||
icon={IconProp.Logout}
|
||||
/>
|
||||
</IconDropdownMenu>
|
||||
</HeaderIconDropdownButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardUserProfile;
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
import Footer from '../Footer/Footer';
|
||||
import Header from '../Header/Header';
|
||||
import NavBar from '../NavBar/NavBar';
|
||||
import MasterPage from 'CommonUI/src/Components/MasterPage/MasterPage';
|
||||
import TopAlert from 'CommonUI/src/Components/TopAlert/TopAlert';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import Footer from "../Footer/Footer";
|
||||
import Header from "../Header/Header";
|
||||
import NavBar from "../NavBar/NavBar";
|
||||
import MasterPage from "Common/UI/Components/MasterPage/MasterPage";
|
||||
import TopAlert from "Common/UI/Components/TopAlert/TopAlert";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
export interface ComponentProps {
|
||||
children: ReactElement | Array<ReactElement>;
|
||||
children: ReactElement | Array<ReactElement>;
|
||||
}
|
||||
|
||||
const DashboardMasterPage: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
return (
|
||||
<div>
|
||||
<TopAlert
|
||||
title="OneUptime Admin Dashboard"
|
||||
description="You can perform your OneUptime server related tasks on this dashboard."
|
||||
/>
|
||||
<MasterPage
|
||||
footer={<Footer />}
|
||||
header={<Header />}
|
||||
navBar={<NavBar />}
|
||||
isLoading={false}
|
||||
error={''}
|
||||
className="flex flex-col h-screen justify-between"
|
||||
>
|
||||
{props.children}
|
||||
</MasterPage>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<TopAlert
|
||||
title="OneUptime Admin Dashboard"
|
||||
description="You can perform your OneUptime server related tasks on this dashboard."
|
||||
/>
|
||||
<MasterPage
|
||||
footer={<Footer />}
|
||||
header={<Header />}
|
||||
navBar={<NavBar />}
|
||||
isLoading={false}
|
||||
error={""}
|
||||
className="flex flex-col h-screen justify-between"
|
||||
>
|
||||
{props.children}
|
||||
</MasterPage>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardMasterPage;
|
||||
|
||||
@@ -1,39 +1,37 @@
|
||||
import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import NavBar from 'CommonUI/src/Components/Navbar/NavBar';
|
||||
import NavBarItem from 'CommonUI/src/Components/Navbar/NavBarItem';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import NavBar from "Common/UI/Components/Navbar/NavBar";
|
||||
import NavBarItem from "Common/UI/Components/Navbar/NavBarItem";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const DashboardNavbar: FunctionComponent = (): ReactElement => {
|
||||
return (
|
||||
<NavBar>
|
||||
<NavBarItem
|
||||
title="Users"
|
||||
icon={IconProp.User}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.USERS] as Route
|
||||
)}
|
||||
></NavBarItem>
|
||||
return (
|
||||
<NavBar>
|
||||
<NavBarItem
|
||||
title="Users"
|
||||
icon={IconProp.User}
|
||||
route={RouteUtil.populateRouteParams(RouteMap[PageMap.USERS] as Route)}
|
||||
></NavBarItem>
|
||||
|
||||
<NavBarItem
|
||||
title="Projects"
|
||||
icon={IconProp.Folder}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECTS] as Route
|
||||
)}
|
||||
></NavBarItem>
|
||||
<NavBarItem
|
||||
title="Projects"
|
||||
icon={IconProp.Folder}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECTS] as Route,
|
||||
)}
|
||||
></NavBarItem>
|
||||
|
||||
<NavBarItem
|
||||
title="Settings"
|
||||
icon={IconProp.Settings}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route
|
||||
)}
|
||||
></NavBarItem>
|
||||
</NavBar>
|
||||
);
|
||||
<NavBarItem
|
||||
title="Settings"
|
||||
icon={IconProp.Settings}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route,
|
||||
)}
|
||||
></NavBarItem>
|
||||
</NavBar>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardNavbar;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import App from './App';
|
||||
import Telemetry from 'CommonUI/src/Utils/Telemetry';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import App from "./App";
|
||||
import Telemetry from "Common/UI/Utils/Telemetry";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
Telemetry.init({
|
||||
serviceName: 'AdminDashboard',
|
||||
serviceName: "admin-dashboard",
|
||||
});
|
||||
|
||||
const root: any = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
document.getElementById("root") as HTMLElement,
|
||||
);
|
||||
|
||||
root.render(
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>,
|
||||
);
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap from '../../Utils/RouteMap';
|
||||
import PageLoader from 'CommonUI/src/Components/Loader/PageLoader';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from 'react';
|
||||
import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap from "../../Utils/RouteMap";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
||||
|
||||
const Init: FunctionComponent = (): ReactElement => {
|
||||
useEffect(() => {
|
||||
Navigation.navigate(RouteMap[PageMap.USERS]!, {
|
||||
forceNavigate: true,
|
||||
});
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
Navigation.navigate(RouteMap[PageMap.USERS]!, {
|
||||
forceNavigate: true,
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Page title={''} breadcrumbLinks={[]}>
|
||||
<PageLoader isVisible={true} />
|
||||
</Page>
|
||||
);
|
||||
return (
|
||||
<Page title={""} breadcrumbLinks={[]}>
|
||||
<PageLoader isVisible={true} />
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Init;
|
||||
|
||||
@@ -1,53 +1,49 @@
|
||||
import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import { PromiseVoidFunction } from 'Common/Types/FunctionTypes';
|
||||
import ErrorMessage from 'CommonUI/src/Components/ErrorMessage/ErrorMessage';
|
||||
import PageLoader from 'CommonUI/src/Components/Loader/PageLoader';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import { ACCOUNTS_URL } from 'CommonUI/src/Config';
|
||||
import UiAnalytics from 'CommonUI/src/Utils/Analytics';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import UserUtil from 'CommonUI/src/Utils/User';
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from 'react';
|
||||
import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import { ACCOUNTS_URL } from "Common/UI/Config";
|
||||
import UiAnalytics from "Common/UI/Utils/Analytics";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import UserUtil from "Common/UI/Utils/User";
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
||||
|
||||
const Logout: FunctionComponent = (): ReactElement => {
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
||||
const logout: PromiseVoidFunction = async (): Promise<void> => {
|
||||
UiAnalytics.logout();
|
||||
await UserUtil.logout();
|
||||
Navigation.navigate(ACCOUNTS_URL);
|
||||
};
|
||||
const logout: PromiseVoidFunction = async (): Promise<void> => {
|
||||
UiAnalytics.logout();
|
||||
await UserUtil.logout();
|
||||
Navigation.navigate(ACCOUNTS_URL);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
logout().catch((error: Error) => {
|
||||
setError(error.message || error.toString());
|
||||
});
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
logout().catch((error: Error) => {
|
||||
setError(error.message || error.toString());
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Page
|
||||
title={'Logout'}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Admin Dashboard',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INIT] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Logout',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.LOGOUT] as Route
|
||||
),
|
||||
},
|
||||
]}
|
||||
>
|
||||
{!error ? <PageLoader isVisible={true} /> : <></>}
|
||||
{error ? <ErrorMessage error={error} /> : <></>}
|
||||
</Page>
|
||||
);
|
||||
return (
|
||||
<Page
|
||||
title={"Logout"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.INIT] as Route),
|
||||
},
|
||||
{
|
||||
title: "Logout",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.LOGOUT] as Route),
|
||||
},
|
||||
]}
|
||||
>
|
||||
{!error ? <PageLoader isVisible={true} /> : <></>}
|
||||
{error ? <ErrorMessage error={error} /> : <></>}
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Logout;
|
||||
|
||||
@@ -1,263 +1,251 @@
|
||||
import AdminModelAPI from '../../Utils/ModelAPI';
|
||||
import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import SubscriptionPlan from 'Common/Types/Billing/SubscriptionPlan';
|
||||
import Field from 'CommonUI/src/Components/Forms/Types/Field';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import { RadioButton } from 'CommonUI/src/Components/RadioButtons/GroupRadioButtons';
|
||||
import Toggle from 'CommonUI/src/Components/Toggle/Toggle';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import { BILLING_ENABLED, getAllEnvVars } from 'CommonUI/src/Config';
|
||||
import { GetReactElementFunction } from 'CommonUI/src/Types/FunctionTypes';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import Project from 'Model/Models/Project';
|
||||
import User from 'Model/Models/User';
|
||||
import AdminModelAPI from "../../Utils/ModelAPI";
|
||||
import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import SubscriptionPlan from "Common/Types/Billing/SubscriptionPlan";
|
||||
import Field from "Common/UI/Components/Forms/Types/Field";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ModelTable from "Common/UI/Components/ModelTable/ModelTable";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import { RadioButton } from "Common/UI/Components/RadioButtons/GroupRadioButtons";
|
||||
import Toggle from "Common/UI/Components/Toggle/Toggle";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import { BILLING_ENABLED, getAllEnvVars } from "Common/UI/Config";
|
||||
import { GetReactElementFunction } from "Common/UI/Types/FunctionTypes";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import Project from "Common/Models/DatabaseModels/Project";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
|
||||
const Projects: FunctionComponent = (): ReactElement => {
|
||||
const [isSubscriptionPlanYearly, setIsSubscriptionPlanYearly] =
|
||||
useState<boolean>(true);
|
||||
const [isSubscriptionPlanYearly, setIsSubscriptionPlanYearly] =
|
||||
useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
refreshFields();
|
||||
}, [isSubscriptionPlanYearly]);
|
||||
useEffect(() => {
|
||||
refreshFields();
|
||||
}, [isSubscriptionPlanYearly]);
|
||||
|
||||
const refreshFields: VoidFunction = (): void => {
|
||||
let formFields: Array<Field<Project>> = [
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
validation: {
|
||||
minLength: 4,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: 'My Project',
|
||||
description: 'Pick a friendly name.',
|
||||
title: 'Project Name',
|
||||
required: true,
|
||||
stepId: BILLING_ENABLED ? 'basic' : undefined,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
createdByUser: true,
|
||||
},
|
||||
title: 'Owner',
|
||||
description:
|
||||
'Who would you like the owner of this project to be? If you leave this blank - you will be the owner of the project',
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
stepId: BILLING_ENABLED ? 'basic' : undefined,
|
||||
dropdownModal: {
|
||||
type: User,
|
||||
labelField: 'email',
|
||||
valueField: '_id',
|
||||
},
|
||||
},
|
||||
];
|
||||
const refreshFields: VoidFunction = (): void => {
|
||||
let formFields: Array<Field<Project>> = [
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
validation: {
|
||||
minLength: 4,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: "My Project",
|
||||
description: "Pick a friendly name.",
|
||||
title: "Project Name",
|
||||
required: true,
|
||||
stepId: BILLING_ENABLED ? "basic" : undefined,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
createdByUser: true,
|
||||
},
|
||||
title: "Owner",
|
||||
description:
|
||||
"Who would you like the owner of this project to be? If you leave this blank - you will be the owner of the project",
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
stepId: BILLING_ENABLED ? "basic" : undefined,
|
||||
dropdownModal: {
|
||||
type: User,
|
||||
labelField: "email",
|
||||
valueField: "_id",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (BILLING_ENABLED) {
|
||||
formFields = [
|
||||
...formFields,
|
||||
{
|
||||
field: {
|
||||
paymentProviderPlanId: true,
|
||||
},
|
||||
stepId: 'plan',
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
footerElement: getFooter(),
|
||||
fieldType: FormFieldSchemaType.RadioButton,
|
||||
radioButtonOptions: SubscriptionPlan.getSubscriptionPlans(
|
||||
getAllEnvVars()
|
||||
).map((plan: SubscriptionPlan): RadioButton => {
|
||||
let description: string = plan.isCustomPricing()
|
||||
? `Our sales team will contact you soon.`
|
||||
: `Billed ${
|
||||
isSubscriptionPlanYearly
|
||||
? 'yearly'
|
||||
: 'monthly'
|
||||
}. ${
|
||||
plan.getTrialPeriod() > 0
|
||||
? `Free ${plan.getTrialPeriod()} days trial.`
|
||||
: ''
|
||||
}`;
|
||||
if (BILLING_ENABLED) {
|
||||
formFields = [
|
||||
...formFields,
|
||||
{
|
||||
field: {
|
||||
paymentProviderPlanId: true,
|
||||
},
|
||||
stepId: "plan",
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
footerElement: getFooter(),
|
||||
fieldType: FormFieldSchemaType.RadioButton,
|
||||
radioButtonOptions: SubscriptionPlan.getSubscriptionPlans(
|
||||
getAllEnvVars(),
|
||||
).map((plan: SubscriptionPlan): RadioButton => {
|
||||
let description: string = plan.isCustomPricing()
|
||||
? `Our sales team will contact you soon.`
|
||||
: `Billed ${isSubscriptionPlanYearly ? "yearly" : "monthly"}. ${
|
||||
plan.getTrialPeriod() > 0
|
||||
? `Free ${plan.getTrialPeriod()} days trial.`
|
||||
: ""
|
||||
}`;
|
||||
|
||||
if (
|
||||
isSubscriptionPlanYearly &&
|
||||
plan.getYearlySubscriptionAmountInUSD() === 0
|
||||
) {
|
||||
description = 'This plan is free, forever. ';
|
||||
}
|
||||
if (
|
||||
isSubscriptionPlanYearly &&
|
||||
plan.getYearlySubscriptionAmountInUSD() === 0
|
||||
) {
|
||||
description = "This plan is free, forever. ";
|
||||
}
|
||||
|
||||
if (
|
||||
!isSubscriptionPlanYearly &&
|
||||
plan.getMonthlySubscriptionAmountInUSD() === 0
|
||||
) {
|
||||
description = 'This plan is free, forever. ';
|
||||
}
|
||||
if (
|
||||
!isSubscriptionPlanYearly &&
|
||||
plan.getMonthlySubscriptionAmountInUSD() === 0
|
||||
) {
|
||||
description = "This plan is free, forever. ";
|
||||
}
|
||||
|
||||
return {
|
||||
value: isSubscriptionPlanYearly
|
||||
? plan.getYearlyPlanId()
|
||||
: plan.getMonthlyPlanId(),
|
||||
title: plan.getName(),
|
||||
description: description,
|
||||
sideTitle: plan.isCustomPricing()
|
||||
? 'Custom Price'
|
||||
: isSubscriptionPlanYearly
|
||||
? '$' +
|
||||
plan
|
||||
.getYearlySubscriptionAmountInUSD()
|
||||
.toString() +
|
||||
'/mo billed yearly'
|
||||
: '$' +
|
||||
plan
|
||||
.getMonthlySubscriptionAmountInUSD()
|
||||
.toString(),
|
||||
sideDescription: plan.isCustomPricing()
|
||||
? ''
|
||||
: isSubscriptionPlanYearly
|
||||
? `~ $${
|
||||
plan.getYearlySubscriptionAmountInUSD() *
|
||||
12
|
||||
} per user / year`
|
||||
: `/month per user`,
|
||||
};
|
||||
}),
|
||||
title: 'Please select a plan.',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
paymentProviderPromoCode: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: 'Promo Code (Optional)',
|
||||
description: 'If you have a coupon code, enter it here.',
|
||||
title: 'Promo Code',
|
||||
required: false,
|
||||
stepId: 'plan',
|
||||
disabled: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
return {
|
||||
value: isSubscriptionPlanYearly
|
||||
? plan.getYearlyPlanId()
|
||||
: plan.getMonthlyPlanId(),
|
||||
title: plan.getName(),
|
||||
description: description,
|
||||
sideTitle: plan.isCustomPricing()
|
||||
? "Custom Price"
|
||||
: isSubscriptionPlanYearly
|
||||
? "$" +
|
||||
plan.getYearlySubscriptionAmountInUSD().toString() +
|
||||
"/mo billed yearly"
|
||||
: "$" + plan.getMonthlySubscriptionAmountInUSD().toString(),
|
||||
sideDescription: plan.isCustomPricing()
|
||||
? ""
|
||||
: isSubscriptionPlanYearly
|
||||
? `~ $${
|
||||
plan.getYearlySubscriptionAmountInUSD() * 12
|
||||
} per user / year`
|
||||
: `/month per user`,
|
||||
};
|
||||
}),
|
||||
title: "Please select a plan.",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
paymentProviderPromoCode: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: "Promo Code (Optional)",
|
||||
description: "If you have a coupon code, enter it here.",
|
||||
title: "Promo Code",
|
||||
required: false,
|
||||
stepId: "plan",
|
||||
disabled: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
setFields(formFields);
|
||||
};
|
||||
setFields(formFields);
|
||||
};
|
||||
|
||||
const [fields, setFields] = useState<Array<Field<Project>>>([]);
|
||||
const [fields, setFields] = useState<Array<Field<Project>>>([]);
|
||||
|
||||
const getFooter: GetReactElementFunction = (): ReactElement => {
|
||||
if (!BILLING_ENABLED) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Toggle
|
||||
title="Yearly Plan"
|
||||
value={isSubscriptionPlanYearly}
|
||||
description="(Save 20%)"
|
||||
onChange={(value: boolean) => {
|
||||
setIsSubscriptionPlanYearly(value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const getFooter: GetReactElementFunction = (): ReactElement => {
|
||||
if (!BILLING_ENABLED) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Page
|
||||
title={'Projects'}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Admin Dashboard',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Projects',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECTS] as Route
|
||||
),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<ModelTable<Project>
|
||||
modelType={Project}
|
||||
modelAPI={AdminModelAPI}
|
||||
id="projects-table"
|
||||
isDeleteable={false}
|
||||
isEditable={false}
|
||||
isCreateable={true}
|
||||
name="Projects"
|
||||
isViewable={false}
|
||||
cardProps={{
|
||||
title: 'Projects',
|
||||
description: 'Here is a list of proejcts in OneUptime.',
|
||||
}}
|
||||
showViewIdButton={true}
|
||||
formSteps={
|
||||
BILLING_ENABLED
|
||||
? [
|
||||
{
|
||||
title: 'Basic',
|
||||
id: 'basic',
|
||||
},
|
||||
{
|
||||
title: 'Select Plan',
|
||||
id: 'plan',
|
||||
},
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
noItemsMessage={'No projects found.'}
|
||||
formFields={fields}
|
||||
showRefreshButton={true}
|
||||
viewPageRoute={Navigation.getCurrentRoute()}
|
||||
filters={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: 'Name',
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
createdAt: true,
|
||||
},
|
||||
title: 'Created At',
|
||||
type: FieldType.DateTime,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: 'Name',
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
createdAt: true,
|
||||
},
|
||||
title: 'Created At',
|
||||
type: FieldType.DateTime,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Page>
|
||||
<Toggle
|
||||
title="Yearly Plan"
|
||||
value={isSubscriptionPlanYearly}
|
||||
description="(Save 20%)"
|
||||
onChange={(value: boolean) => {
|
||||
setIsSubscriptionPlanYearly(value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Page
|
||||
title={"Projects"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Projects",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECTS] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<ModelTable<Project>
|
||||
modelType={Project}
|
||||
modelAPI={AdminModelAPI}
|
||||
id="projects-table"
|
||||
isDeleteable={false}
|
||||
isEditable={false}
|
||||
isCreateable={true}
|
||||
name="Projects"
|
||||
isViewable={true}
|
||||
cardProps={{
|
||||
title: "Projects",
|
||||
description: "Here is a list of proejcts in OneUptime.",
|
||||
}}
|
||||
showViewIdButton={true}
|
||||
formSteps={
|
||||
BILLING_ENABLED
|
||||
? [
|
||||
{
|
||||
title: "Basic",
|
||||
id: "basic",
|
||||
},
|
||||
{
|
||||
title: "Select Plan",
|
||||
id: "plan",
|
||||
},
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
noItemsMessage={"No projects found."}
|
||||
formFields={fields}
|
||||
showRefreshButton={true}
|
||||
viewPageRoute={Navigation.getCurrentRoute()}
|
||||
filters={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
createdAt: true,
|
||||
},
|
||||
title: "Created At",
|
||||
type: FieldType.DateTime,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
createdAt: true,
|
||||
},
|
||||
title: "Created At",
|
||||
type: FieldType.DateTime,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Projects;
|
||||
|
||||
52
AdminDashboard/src/Pages/Projects/View/Delete.tsx
Normal file
52
AdminDashboard/src/Pages/Projects/View/Delete.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import ModelDelete from "Common/UI/Components/ModelDelete/ModelDelete";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import SideMenuComponent from "./SideMenu";
|
||||
import Project from "Common/Models/DatabaseModels/Project";
|
||||
import ModelPage from "Common/UI/Components/Page/ModelPage";
|
||||
|
||||
const DeletePage: FunctionComponent = (): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return (
|
||||
<ModelPage<Project>
|
||||
modelId={modelId}
|
||||
modelNameField="name"
|
||||
modelType={Project}
|
||||
title={"Project"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Projects",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECTS] as Route,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Project",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECT_VIEW] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenuComponent modelId={modelId} />}
|
||||
>
|
||||
<ModelDelete
|
||||
modelType={Project}
|
||||
modelId={modelId}
|
||||
onDeleteSuccess={() => {
|
||||
Navigation.navigate(RouteMap[PageMap.PROJECTS] as Route);
|
||||
}}
|
||||
/>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeletePage;
|
||||
90
AdminDashboard/src/Pages/Projects/View/Index.tsx
Normal file
90
AdminDashboard/src/Pages/Projects/View/Index.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import Project from "Common/Models/DatabaseModels/Project";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import ModelPage from "Common/UI/Components/Page/ModelPage";
|
||||
import SideMenuComponent from "./SideMenu";
|
||||
|
||||
const Projects: FunctionComponent = (): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID();
|
||||
|
||||
return (
|
||||
<ModelPage
|
||||
modelId={modelId}
|
||||
modelNameField="name"
|
||||
modelType={Project}
|
||||
title={"Project"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Projects",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECTS] as Route,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Project",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECT_VIEW] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenuComponent modelId={modelId} />}
|
||||
>
|
||||
<div>
|
||||
<CardModelDetail<Project>
|
||||
name="Project"
|
||||
cardProps={{
|
||||
title: "Project",
|
||||
description: "Project details",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Project"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: Project,
|
||||
id: "model-detail-user",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
_id: true,
|
||||
},
|
||||
title: "Project ID",
|
||||
fieldType: FieldType.Text,
|
||||
placeholder: "-",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
fieldType: FieldType.Text,
|
||||
},
|
||||
],
|
||||
modelId: modelId,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Projects;
|
||||
53
AdminDashboard/src/Pages/Projects/View/SideMenu.tsx
Normal file
53
AdminDashboard/src/Pages/Projects/View/SideMenu.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import SideMenu from "Common/UI/Components/SideMenu/SideMenu";
|
||||
import SideMenuItem from "Common/UI/Components/SideMenu/SideMenuItem";
|
||||
import SideMenuSection from "Common/UI/Components/SideMenu/SideMenuSection";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
export interface SideMenuProps {
|
||||
modelId: ObjectID;
|
||||
}
|
||||
|
||||
const SideMenuComponent: FunctionComponent<SideMenuProps> = (
|
||||
props: SideMenuProps,
|
||||
): ReactElement => {
|
||||
return (
|
||||
<SideMenu>
|
||||
<SideMenuSection title="Basic">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Overview",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECT_VIEW] as Route,
|
||||
{
|
||||
modelId: props.modelId,
|
||||
},
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Info}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="Advanced">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Delete",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECT_DELETE] as Route,
|
||||
{
|
||||
modelId: props.modelId,
|
||||
},
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Trash}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
</SideMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default SideMenuComponent;
|
||||
@@ -1,102 +1,100 @@
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import DashboardSideMenu from '../SideMenu';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import CardModelDetail from 'CommonUI/src/Components/ModelDetail/CardModelDetail';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import GlobalConfig from 'Model/Models/GlobalConfig';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import DashboardSideMenu from "../SideMenu";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import GlobalConfig from "Common/Models/DatabaseModels/GlobalConfig";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
return (
|
||||
<Page
|
||||
title={'Admin Settings'}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Admin Dashboard',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'API Key',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_HOST] as Route
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
<CardModelDetail
|
||||
name="API Key Settings"
|
||||
cardProps={{
|
||||
title: 'Master API Key Settings',
|
||||
description:
|
||||
'This API key has root access to all the resources in all the projects on OneUptime.',
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit API Key Settings"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
masterApiKey: true,
|
||||
},
|
||||
title: 'Master API Key',
|
||||
fieldType: FormFieldSchemaType.ObjectID,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isMasterApiKeyEnabled: true,
|
||||
},
|
||||
title: 'Enabled',
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: 'model-detail-global-config',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
masterApiKey: true,
|
||||
},
|
||||
title: 'Master API Key',
|
||||
description:
|
||||
'This API key has root access to all the resources in all the projects on OneUptime.',
|
||||
fieldType: FieldType.HiddenText,
|
||||
opts: {
|
||||
isCopyable: true,
|
||||
},
|
||||
placeholder: 'API Key not generated yet.',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isMasterApiKeyEnabled: true,
|
||||
},
|
||||
title: 'Enabled',
|
||||
description:
|
||||
'Enable or disable the master API key. If disabled, all requests using this key will fail.',
|
||||
fieldType: FieldType.Boolean,
|
||||
placeholder: 'Not Enabled',
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
return (
|
||||
<Page
|
||||
title={"Admin Settings"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "API Key",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_HOST] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
<CardModelDetail
|
||||
name="API Key Settings"
|
||||
cardProps={{
|
||||
title: "Master API Key Settings",
|
||||
description:
|
||||
"This API key has root access to all the resources in all the projects on OneUptime.",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit API Key Settings"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
masterApiKey: true,
|
||||
},
|
||||
title: "Master API Key",
|
||||
fieldType: FormFieldSchemaType.ObjectID,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isMasterApiKeyEnabled: true,
|
||||
},
|
||||
title: "Enabled",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: "model-detail-global-config",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
masterApiKey: true,
|
||||
},
|
||||
title: "Master API Key",
|
||||
description:
|
||||
"This API key has root access to all the resources in all the projects on OneUptime.",
|
||||
fieldType: FieldType.HiddenText,
|
||||
opts: {
|
||||
isCopyable: true,
|
||||
},
|
||||
placeholder: "API Key not generated yet.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isMasterApiKeyEnabled: true,
|
||||
},
|
||||
title: "Enabled",
|
||||
description:
|
||||
"Enable or disable the master API key. If disabled, all requests using this key will fail.",
|
||||
fieldType: FieldType.Boolean,
|
||||
placeholder: "Not Enabled",
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
||||
@@ -1,83 +1,80 @@
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import DashboardSideMenu from '../SideMenu';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import CardModelDetail from 'CommonUI/src/Components/ModelDetail/CardModelDetail';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import GlobalConfig from 'Model/Models/GlobalConfig';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import DashboardSideMenu from "../SideMenu";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import GlobalConfig from "Common/Models/DatabaseModels/GlobalConfig";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
return (
|
||||
<Page
|
||||
title={'Admin Settings'}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Admin Dashboard',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Authentication',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_AUTHENTICATION] as Route
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
<CardModelDetail
|
||||
name="Authentication Settings"
|
||||
cardProps={{
|
||||
title: 'Authentication Settings',
|
||||
description:
|
||||
'Authentication Settings for this OneUptime Server instance.',
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Settings"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
disableSignup: true,
|
||||
},
|
||||
title: 'Disable Sign Up',
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
description:
|
||||
'Should we disable sign up of new users to OneUptime?',
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: 'model-detail-global-config',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
disableSignup: true,
|
||||
},
|
||||
fieldType: FieldType.Boolean,
|
||||
title: 'Disable Sign Up',
|
||||
placeholder: 'No',
|
||||
description:
|
||||
'Should we disable sign up of new users to OneUptime?',
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
return (
|
||||
<Page
|
||||
title={"Admin Settings"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Authentication",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_AUTHENTICATION] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
<CardModelDetail
|
||||
name="Authentication Settings"
|
||||
cardProps={{
|
||||
title: "Authentication Settings",
|
||||
description:
|
||||
"Authentication Settings for this OneUptime Server instance.",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Settings"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
disableSignup: true,
|
||||
},
|
||||
title: "Disable Sign Up",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
description: "Should we disable sign up of new users to OneUptime?",
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: "model-detail-global-config",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
disableSignup: true,
|
||||
},
|
||||
fieldType: FieldType.Boolean,
|
||||
title: "Disable Sign Up",
|
||||
placeholder: "No",
|
||||
description:
|
||||
"Should we disable sign up of new users to OneUptime?",
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
||||
@@ -1,119 +1,114 @@
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import DashboardSideMenu from '../SideMenu';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import CardModelDetail from 'CommonUI/src/Components/ModelDetail/CardModelDetail';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import GlobalConfig from 'Model/Models/GlobalConfig';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import DashboardSideMenu from "../SideMenu";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import GlobalConfig from "Common/Models/DatabaseModels/GlobalConfig";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
return (
|
||||
<Page
|
||||
title={'Admin Settings'}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Admin Dashboard',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Calls and SMS',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_CALL_AND_SMS] as Route
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
<CardModelDetail
|
||||
name="Call and SMS Settings"
|
||||
cardProps={{
|
||||
title: 'Twilio Config',
|
||||
description: 'This will be used to make Call and send SMS.',
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Twilio Config"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
twilioAccountSID: true,
|
||||
},
|
||||
title: 'Twilio Account SID',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
description:
|
||||
'You can find this in your Twilio console.',
|
||||
placeholder: '',
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
twilioAuthToken: true,
|
||||
},
|
||||
title: 'Twilio Auth Token',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
description:
|
||||
'You can find this in your Twilio console.',
|
||||
placeholder: '',
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
twilioPhoneNumber: true,
|
||||
},
|
||||
title: 'Twilio Phone Number',
|
||||
fieldType: FormFieldSchemaType.Phone,
|
||||
required: true,
|
||||
description:
|
||||
'You can find this in your Twilio console.',
|
||||
placeholder: '',
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: 'model-detail-global-config',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
twilioAccountSID: true,
|
||||
},
|
||||
title: 'Twilio Account SID',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
twilioPhoneNumber: true,
|
||||
},
|
||||
title: 'Twilio Phone Number',
|
||||
fieldType: FieldType.Phone,
|
||||
placeholder: 'None',
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
return (
|
||||
<Page
|
||||
title={"Admin Settings"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Calls and SMS",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_CALL_AND_SMS] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
<CardModelDetail
|
||||
name="Call and SMS Settings"
|
||||
cardProps={{
|
||||
title: "Twilio Config",
|
||||
description: "This will be used to make Call and send SMS.",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Twilio Config"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
twilioAccountSID: true,
|
||||
},
|
||||
title: "Twilio Account SID",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
description: "You can find this in your Twilio console.",
|
||||
placeholder: "",
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
twilioAuthToken: true,
|
||||
},
|
||||
title: "Twilio Auth Token",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
description: "You can find this in your Twilio console.",
|
||||
placeholder: "",
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
twilioPhoneNumber: true,
|
||||
},
|
||||
title: "Twilio Phone Number",
|
||||
fieldType: FormFieldSchemaType.Phone,
|
||||
required: true,
|
||||
description: "You can find this in your Twilio console.",
|
||||
placeholder: "",
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: "model-detail-global-config",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
twilioAccountSID: true,
|
||||
},
|
||||
title: "Twilio Account SID",
|
||||
placeholder: "None",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
twilioPhoneNumber: true,
|
||||
},
|
||||
title: "Twilio Phone Number",
|
||||
fieldType: FieldType.Phone,
|
||||
placeholder: "None",
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
||||
@@ -1,427 +1,421 @@
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import DashboardSideMenu from '../SideMenu';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import { Green, Red } from 'Common/Types/BrandColors';
|
||||
import { PromiseVoidFunction } from 'Common/Types/FunctionTypes';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import ErrorMessage from 'CommonUI/src/Components/ErrorMessage/ErrorMessage';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import PageLoader from 'CommonUI/src/Components/Loader/PageLoader';
|
||||
import CardModelDetail from 'CommonUI/src/Components/ModelDetail/CardModelDetail';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import Pill from 'CommonUI/src/Components/Pill/Pill';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import DropdownUtil from 'CommonUI/src/Utils/Dropdown';
|
||||
import ModelAPI from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
import GlobalConfig, { EmailServerType } from 'Model/Models/GlobalConfig';
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from 'react';
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import DashboardSideMenu from "../SideMenu";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import { Green, Red } from "Common/Types/BrandColors";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import Pill from "Common/UI/Components/Pill/Pill";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import DropdownUtil from "Common/UI/Utils/Dropdown";
|
||||
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
|
||||
import GlobalConfig, {
|
||||
EmailServerType,
|
||||
} from "Common/Models/DatabaseModels/GlobalConfig";
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
const [emailServerType, setemailServerType] =
|
||||
React.useState<EmailServerType>(EmailServerType.Internal);
|
||||
const [emailServerType, setemailServerType] = React.useState<EmailServerType>(
|
||||
EmailServerType.Internal,
|
||||
);
|
||||
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(true);
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(true);
|
||||
|
||||
const [error, setError] = React.useState<string>('');
|
||||
const [error, setError] = React.useState<string>("");
|
||||
|
||||
const fetchItem: PromiseVoidFunction = async (): Promise<void> => {
|
||||
setIsLoading(true);
|
||||
const fetchItem: PromiseVoidFunction = async (): Promise<void> => {
|
||||
setIsLoading(true);
|
||||
|
||||
const globalConfig: GlobalConfig | null =
|
||||
await ModelAPI.getItem<GlobalConfig>({
|
||||
modelType: GlobalConfig,
|
||||
id: ObjectID.getZeroObjectID(),
|
||||
select: {
|
||||
_id: true,
|
||||
emailServerType: true,
|
||||
},
|
||||
});
|
||||
const globalConfig: GlobalConfig | null =
|
||||
await ModelAPI.getItem<GlobalConfig>({
|
||||
modelType: GlobalConfig,
|
||||
id: ObjectID.getZeroObjectID(),
|
||||
select: {
|
||||
_id: true,
|
||||
emailServerType: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (globalConfig) {
|
||||
setemailServerType(
|
||||
globalConfig.emailServerType || EmailServerType.Internal
|
||||
);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchItem().catch((err: Error) => {
|
||||
setError(err.message);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
if (globalConfig) {
|
||||
setemailServerType(
|
||||
globalConfig.emailServerType || EmailServerType.Internal,
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage error={error} />;
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Page
|
||||
title={'Admin Settings'}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Admin Dashboard',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route
|
||||
),
|
||||
useEffect(() => {
|
||||
fetchItem().catch((err: Error) => {
|
||||
setError(err.message);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage error={error} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Page
|
||||
title={"Admin Settings"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Email Settings",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_SMTP] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
|
||||
<CardModelDetail
|
||||
name="Admin Notification Email"
|
||||
cardProps={{
|
||||
title: "Admin Notification Email",
|
||||
description:
|
||||
"Enter the email address where you would like to receive admin-level notifications.",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Email"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
adminNotificationEmail: true,
|
||||
},
|
||||
title: "Admin Notification Email",
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: "model-detail-global-config",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
adminNotificationEmail: true,
|
||||
},
|
||||
title: "Admin Notification Email",
|
||||
fieldType: FieldType.Email,
|
||||
placeholder: "None",
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
|
||||
<CardModelDetail
|
||||
name="Internal SMTP Settings"
|
||||
cardProps={{
|
||||
title: "Email Server Settings",
|
||||
description:
|
||||
"Pick which email server you would like to use to send emails.",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Server"
|
||||
onSaveSuccess={() => {
|
||||
window.location.reload();
|
||||
}}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
emailServerType: true,
|
||||
},
|
||||
title: "Email Server Type",
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
dropdownOptions:
|
||||
DropdownUtil.getDropdownOptionsFromEnum(EmailServerType),
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: "model-detail-global-config",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
emailServerType: true,
|
||||
},
|
||||
title: "Email Server Type",
|
||||
fieldType: FieldType.Text,
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
|
||||
{emailServerType === EmailServerType.CustomSMTP ? (
|
||||
<CardModelDetail
|
||||
name="Host Settings"
|
||||
cardProps={{
|
||||
title: "Custom Email and SMTP Settings",
|
||||
description:
|
||||
"If you have not enabled Internal SMTP server to send emails. Please configure your SMTP server here.",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit SMTP Config"
|
||||
formSteps={[
|
||||
{
|
||||
title: "SMTP Server",
|
||||
id: "server-info",
|
||||
},
|
||||
{
|
||||
title: "Authentication",
|
||||
id: "authentication",
|
||||
},
|
||||
{
|
||||
title: "Email",
|
||||
id: "email-info",
|
||||
},
|
||||
]}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
smtpHost: true,
|
||||
},
|
||||
title: "Hostname",
|
||||
stepId: "server-info",
|
||||
fieldType: FormFieldSchemaType.Hostname,
|
||||
required: true,
|
||||
placeholder: "smtp.server.com",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPort: true,
|
||||
},
|
||||
title: "Port",
|
||||
stepId: "server-info",
|
||||
fieldType: FormFieldSchemaType.Port,
|
||||
required: true,
|
||||
placeholder: "587",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isSMTPSecure: true,
|
||||
},
|
||||
title: "Use SSL / TLS",
|
||||
stepId: "server-info",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
description:
|
||||
"If you use port 465, please enable this. Do not enable this if you use port 587.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpUsername: true,
|
||||
},
|
||||
title: "Username",
|
||||
stepId: "authentication",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: "emailuser",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPassword: true,
|
||||
},
|
||||
title: "Password",
|
||||
stepId: "authentication",
|
||||
fieldType: FormFieldSchemaType.EncryptedText,
|
||||
required: false,
|
||||
placeholder: "Password",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromEmail: true,
|
||||
},
|
||||
title: "Email From",
|
||||
stepId: "email-info",
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
description:
|
||||
"Email used to log in to this SMTP Server. This is also the email your customers will see. ",
|
||||
placeholder: "email@company.com",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromName: true,
|
||||
},
|
||||
title: "From Name",
|
||||
stepId: "email-info",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
description:
|
||||
"This is the display name your team and customers see, when they receive emails from OneUptime.",
|
||||
placeholder: "Company, Inc.",
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: "model-detail-global-config",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
smtpHost: true,
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route
|
||||
),
|
||||
title: "SMTP Host",
|
||||
placeholder: "None",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPort: true,
|
||||
},
|
||||
{
|
||||
title: 'Email Settings',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_SMTP] as Route
|
||||
),
|
||||
title: "SMTP Port",
|
||||
placeholder: "None",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpUsername: true,
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
title: "SMTP Username",
|
||||
placeholder: "None",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromEmail: true,
|
||||
},
|
||||
title: "SMTP Email",
|
||||
placeholder: "None",
|
||||
fieldType: FieldType.Email,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromName: true,
|
||||
},
|
||||
title: "SMTP From Name",
|
||||
placeholder: "None",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isSMTPSecure: true,
|
||||
},
|
||||
title: "Use SSL/TLS",
|
||||
placeholder: "No",
|
||||
fieldType: FieldType.Boolean,
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<CardModelDetail
|
||||
name="Admin Notification Email"
|
||||
cardProps={{
|
||||
title: 'Admin Notification Email',
|
||||
description:
|
||||
'Enter the email address where you would like to receive admin-level notifications.',
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Email"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
adminNotificationEmail: true,
|
||||
},
|
||||
title: 'Admin Notification Email',
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: 'model-detail-global-config',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
adminNotificationEmail: true,
|
||||
},
|
||||
title: 'Admin Notification Email',
|
||||
fieldType: FieldType.Email,
|
||||
placeholder: 'None',
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
{emailServerType === EmailServerType.Sendgrid ? (
|
||||
<CardModelDetail<GlobalConfig>
|
||||
name="Sendgrid Settings"
|
||||
cardProps={{
|
||||
title: "Sendgrid Settings",
|
||||
description:
|
||||
"Enter your Sendgrid API key to send emails through Sendgrid.",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit API Key"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
sendgridApiKey: true,
|
||||
},
|
||||
title: "Sendgrid API Key",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: "Sendgrid API Key",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
sendgridFromEmail: true,
|
||||
},
|
||||
title: "From Email",
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
placeholder: "email@yourcompany.com",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
sendgridFromName: true,
|
||||
},
|
||||
title: "From Name",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: "Acme, Inc.",
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: "model-detail-global-config",
|
||||
selectMoreFields: {
|
||||
sendgridFromEmail: true,
|
||||
sendgridFromName: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
sendgridApiKey: true,
|
||||
},
|
||||
title: "",
|
||||
placeholder: "None",
|
||||
getElement: (item: GlobalConfig) => {
|
||||
if (
|
||||
item["sendgridApiKey"] &&
|
||||
item["sendgridFromEmail"] &&
|
||||
item["sendgridFromName"]
|
||||
) {
|
||||
return <Pill text="Enabled" color={Green} />;
|
||||
} else if (!item["sendgridApiKey"]) {
|
||||
return (
|
||||
<Pill
|
||||
text="Not Enabled. Please add the API key."
|
||||
color={Red}
|
||||
/>
|
||||
);
|
||||
} else if (!item["sendgridFromEmail"]) {
|
||||
return (
|
||||
<Pill
|
||||
text="Not Enabled. Please add the From Email."
|
||||
color={Red}
|
||||
/>
|
||||
);
|
||||
} else if (!item["sendgridFromName"]) {
|
||||
return (
|
||||
<Pill
|
||||
text="Not Enabled. Please add the From Name."
|
||||
color={Red}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
<CardModelDetail
|
||||
name="Internal SMTP Settings"
|
||||
cardProps={{
|
||||
title: 'Email Server Settings',
|
||||
description:
|
||||
'Pick which email server you would like to use to send emails.',
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Server"
|
||||
onSaveSuccess={() => {
|
||||
window.location.reload();
|
||||
}}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
emailServerType: true,
|
||||
},
|
||||
title: 'Email Server Type',
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
dropdownOptions:
|
||||
DropdownUtil.getDropdownOptionsFromEnum(
|
||||
EmailServerType
|
||||
),
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: 'model-detail-global-config',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
emailServerType: true,
|
||||
},
|
||||
title: 'Email Server Type',
|
||||
fieldType: FieldType.Text,
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
|
||||
{emailServerType === EmailServerType.CustomSMTP ? (
|
||||
<CardModelDetail
|
||||
name="Host Settings"
|
||||
cardProps={{
|
||||
title: 'Custom Email and SMTP Settings',
|
||||
description:
|
||||
'If you have not enabled Internal SMTP server to send emails. Please configure your SMTP server here.',
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit SMTP Config"
|
||||
formSteps={[
|
||||
{
|
||||
title: 'SMTP Server',
|
||||
id: 'server-info',
|
||||
},
|
||||
{
|
||||
title: 'Authentication',
|
||||
id: 'authentication',
|
||||
},
|
||||
{
|
||||
title: 'Email',
|
||||
id: 'email-info',
|
||||
},
|
||||
]}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
smtpHost: true,
|
||||
},
|
||||
title: 'Hostname',
|
||||
stepId: 'server-info',
|
||||
fieldType: FormFieldSchemaType.Hostname,
|
||||
required: true,
|
||||
placeholder: 'smtp.server.com',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPort: true,
|
||||
},
|
||||
title: 'Port',
|
||||
stepId: 'server-info',
|
||||
fieldType: FormFieldSchemaType.Port,
|
||||
required: true,
|
||||
placeholder: '587',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isSMTPSecure: true,
|
||||
},
|
||||
title: 'Use SSL / TLS',
|
||||
stepId: 'server-info',
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
description:
|
||||
'If you use port 465, please enable this. Do not enable this if you use port 587.',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpUsername: true,
|
||||
},
|
||||
title: 'Username',
|
||||
stepId: 'authentication',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: 'emailuser',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPassword: true,
|
||||
},
|
||||
title: 'Password',
|
||||
stepId: 'authentication',
|
||||
fieldType: FormFieldSchemaType.EncryptedText,
|
||||
required: false,
|
||||
placeholder: 'Password',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromEmail: true,
|
||||
},
|
||||
title: 'Email From',
|
||||
stepId: 'email-info',
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
description:
|
||||
'This is the display email your team and customers see, when they receive emails from OneUptime.',
|
||||
placeholder: 'email@company.com',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromName: true,
|
||||
},
|
||||
title: 'From Name',
|
||||
stepId: 'email-info',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
description:
|
||||
'This is the display name your team and customers see, when they receive emails from OneUptime.',
|
||||
placeholder: 'Company, Inc.',
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: 'model-detail-global-config',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
smtpHost: true,
|
||||
},
|
||||
title: 'SMTP Host',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPort: true,
|
||||
},
|
||||
title: 'SMTP Port',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpUsername: true,
|
||||
},
|
||||
title: 'SMTP Username',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromEmail: true,
|
||||
},
|
||||
title: 'SMTP Email',
|
||||
placeholder: 'None',
|
||||
fieldType: FieldType.Email,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromName: true,
|
||||
},
|
||||
title: 'SMTP From Name',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isSMTPSecure: true,
|
||||
},
|
||||
title: 'Use SSL/TLS',
|
||||
placeholder: 'No',
|
||||
fieldType: FieldType.Boolean,
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{emailServerType === EmailServerType.Sendgrid ? (
|
||||
<CardModelDetail<GlobalConfig>
|
||||
name="Sendgrid Settings"
|
||||
cardProps={{
|
||||
title: 'Sendgrid Settings',
|
||||
description:
|
||||
'Enter your Sendgrid API key to send emails through Sendgrid.',
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit API Key"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
sendgridApiKey: true,
|
||||
},
|
||||
title: 'Sendgrid API Key',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: 'Sendgrid API Key',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
sendgridFromEmail: true,
|
||||
},
|
||||
title: 'From Email',
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
placeholder: 'email@yourcompany.com',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
sendgridFromName: true,
|
||||
},
|
||||
title: 'From Name',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: 'Acme, Inc.',
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: 'model-detail-global-config',
|
||||
selectMoreFields: {
|
||||
sendgridFromEmail: true,
|
||||
sendgridFromName: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
sendgridApiKey: true,
|
||||
},
|
||||
title: '',
|
||||
placeholder: 'None',
|
||||
getElement: (item: GlobalConfig) => {
|
||||
if (
|
||||
item['sendgridApiKey'] &&
|
||||
item['sendgridFromEmail'] &&
|
||||
item['sendgridFromName']
|
||||
) {
|
||||
return (
|
||||
<Pill
|
||||
text="Enabled"
|
||||
color={Green}
|
||||
/>
|
||||
);
|
||||
} else if (!item['sendgridApiKey']) {
|
||||
return (
|
||||
<Pill
|
||||
text="Not Enabled. Please add the API key."
|
||||
color={Red}
|
||||
/>
|
||||
);
|
||||
} else if (!item['sendgridFromEmail']) {
|
||||
return (
|
||||
<Pill
|
||||
text="Not Enabled. Please add the From Email."
|
||||
color={Red}
|
||||
/>
|
||||
);
|
||||
} else if (!item['sendgridFromName']) {
|
||||
return (
|
||||
<Pill
|
||||
text="Not Enabled. Please add the From Name."
|
||||
color={Red}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <></>;
|
||||
},
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
return <></>;
|
||||
},
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
||||
@@ -1,251 +1,243 @@
|
||||
import AdminModelAPI from '../../../Utils/ModelAPI';
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import DashboardSideMenu from '../SideMenu';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import IsNull from 'Common/Types/BaseDatabase/IsNull';
|
||||
import { Green, Red } from 'Common/Types/BrandColors';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import { ErrorFunction, VoidFunction } from 'Common/Types/FunctionTypes';
|
||||
import Banner from 'CommonUI/src/Components/Banner/Banner';
|
||||
import { ButtonStyleType } from 'CommonUI/src/Components/Button/Button';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import ConfirmModal from 'CommonUI/src/Components/Modal/ConfirmModal';
|
||||
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import ProbeElement from 'CommonUI/src/Components/Probe/Probe';
|
||||
import Statusbubble from 'CommonUI/src/Components/StatusBubble/StatusBubble';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import Probe from 'Model/Models/Probe';
|
||||
import React, { FunctionComponent, ReactElement, useState } from 'react';
|
||||
import AdminModelAPI from "../../../Utils/ModelAPI";
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import DashboardSideMenu from "../SideMenu";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import IsNull from "Common/Types/BaseDatabase/IsNull";
|
||||
import { Green, Red } from "Common/Types/BrandColors";
|
||||
import OneUptimeDate from "Common/Types/Date";
|
||||
import { ErrorFunction, VoidFunction } from "Common/Types/FunctionTypes";
|
||||
import Banner from "Common/UI/Components/Banner/Banner";
|
||||
import { ButtonStyleType } from "Common/UI/Components/Button/Button";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ConfirmModal from "Common/UI/Components/Modal/ConfirmModal";
|
||||
import ModelTable from "Common/UI/Components/ModelTable/ModelTable";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import ProbeElement from "Common/UI/Components/Probe/Probe";
|
||||
import Statusbubble from "Common/UI/Components/StatusBubble/StatusBubble";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import Probe from "Common/Models/DatabaseModels/Probe";
|
||||
import React, { FunctionComponent, ReactElement, useState } from "react";
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
const [showKeyModal, setShowKeyModal] = useState<boolean>(false);
|
||||
const [showKeyModal, setShowKeyModal] = useState<boolean>(false);
|
||||
|
||||
const [currentProbe, setCurrentProbe] = useState<Probe | null>(null);
|
||||
const [currentProbe, setCurrentProbe] = useState<Probe | null>(null);
|
||||
|
||||
return (
|
||||
<Page
|
||||
title={'Admin Settings'}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Admin Dashboard',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Global Probes',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_PROBES] as Route
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
return (
|
||||
<Page
|
||||
title={"Admin Settings"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Global Probes",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_PROBES] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
|
||||
<Banner
|
||||
openInNewTab={true}
|
||||
title="Need help with setting up Global Probes?"
|
||||
description="Here is a guide which will help you get set up"
|
||||
link={Route.fromString('/docs/probe/custom-probe')}
|
||||
/>
|
||||
<Banner
|
||||
openInNewTab={true}
|
||||
title="Need help with setting up Global Probes?"
|
||||
description="Here is a guide which will help you get set up"
|
||||
link={Route.fromString("/docs/probe/custom-probe")}
|
||||
/>
|
||||
|
||||
<ModelTable<Probe>
|
||||
modelType={Probe}
|
||||
id="probes-table"
|
||||
name="Settings > Global Probes"
|
||||
isDeleteable={true}
|
||||
isEditable={true}
|
||||
isCreateable={true}
|
||||
cardProps={{
|
||||
title: 'Global Probes',
|
||||
description:
|
||||
'Global Probes help you monitor external resources from different locations around the world.',
|
||||
}}
|
||||
query={{
|
||||
projectId: new IsNull(),
|
||||
isGlobalProbe: true,
|
||||
}}
|
||||
modelAPI={AdminModelAPI}
|
||||
noItemsMessage={'No probes found.'}
|
||||
showRefreshButton={true}
|
||||
onBeforeCreate={(item: Probe) => {
|
||||
item.isGlobalProbe = true;
|
||||
return Promise.resolve(item);
|
||||
}}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: 'Name',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: 'internal-probe',
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
<ModelTable<Probe>
|
||||
modelType={Probe}
|
||||
id="probes-table"
|
||||
name="Settings > Global Probes"
|
||||
isDeleteable={true}
|
||||
isEditable={true}
|
||||
isCreateable={true}
|
||||
cardProps={{
|
||||
title: "Global Probes",
|
||||
description:
|
||||
"Global Probes help you monitor external resources from different locations around the world.",
|
||||
}}
|
||||
query={{
|
||||
projectId: new IsNull(),
|
||||
isGlobalProbe: true,
|
||||
}}
|
||||
modelAPI={AdminModelAPI}
|
||||
noItemsMessage={"No probes found."}
|
||||
showRefreshButton={true}
|
||||
onBeforeCreate={(item: Probe) => {
|
||||
item.isGlobalProbe = true;
|
||||
return Promise.resolve(item);
|
||||
}}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: "internal-probe",
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: 'Description',
|
||||
fieldType: FormFieldSchemaType.LongText,
|
||||
required: true,
|
||||
placeholder:
|
||||
'This probe is to monitor all the internal services.',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: "Description",
|
||||
fieldType: FormFieldSchemaType.LongText,
|
||||
required: true,
|
||||
placeholder: "This probe is to monitor all the internal services.",
|
||||
},
|
||||
|
||||
{
|
||||
field: {
|
||||
iconFile: true,
|
||||
},
|
||||
title: 'Probe Logo',
|
||||
fieldType: FormFieldSchemaType.ImageFile,
|
||||
required: false,
|
||||
placeholder: 'Upload logo',
|
||||
},
|
||||
]}
|
||||
selectMoreFields={{
|
||||
key: true,
|
||||
iconFileId: true,
|
||||
}}
|
||||
actionButtons={[
|
||||
{
|
||||
title: 'Show ID and Key',
|
||||
buttonStyleType: ButtonStyleType.NORMAL,
|
||||
onClick: async (
|
||||
item: Probe,
|
||||
onCompleteAction: VoidFunction,
|
||||
onError: ErrorFunction
|
||||
) => {
|
||||
try {
|
||||
setCurrentProbe(item);
|
||||
setShowKeyModal(true);
|
||||
{
|
||||
field: {
|
||||
iconFile: true,
|
||||
},
|
||||
title: "Probe Logo",
|
||||
fieldType: FormFieldSchemaType.ImageFile,
|
||||
required: false,
|
||||
placeholder: "Upload logo",
|
||||
},
|
||||
]}
|
||||
selectMoreFields={{
|
||||
key: true,
|
||||
iconFileId: true,
|
||||
}}
|
||||
actionButtons={[
|
||||
{
|
||||
title: "Show ID and Key",
|
||||
buttonStyleType: ButtonStyleType.NORMAL,
|
||||
onClick: async (
|
||||
item: Probe,
|
||||
onCompleteAction: VoidFunction,
|
||||
onError: ErrorFunction,
|
||||
) => {
|
||||
try {
|
||||
setCurrentProbe(item);
|
||||
setShowKeyModal(true);
|
||||
|
||||
onCompleteAction();
|
||||
} catch (err) {
|
||||
onCompleteAction();
|
||||
onError(err as Error);
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
filters={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: 'Name',
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: 'Description',
|
||||
type: FieldType.Text,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: 'Name',
|
||||
type: FieldType.Text,
|
||||
onCompleteAction();
|
||||
} catch (err) {
|
||||
onCompleteAction();
|
||||
onError(err as Error);
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
filters={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: "Description",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
type: FieldType.Text,
|
||||
|
||||
getElement: (item: Probe): ReactElement => {
|
||||
return <ProbeElement probe={item} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: 'Description',
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
lastAlive: true,
|
||||
},
|
||||
title: 'Status',
|
||||
type: FieldType.Text,
|
||||
getElement: (item: Probe): ReactElement => {
|
||||
if (
|
||||
item &&
|
||||
item['lastAlive'] &&
|
||||
OneUptimeDate.getNumberOfMinutesBetweenDates(
|
||||
OneUptimeDate.fromString(item['lastAlive']),
|
||||
OneUptimeDate.getCurrentDate()
|
||||
) < 5
|
||||
) {
|
||||
return (
|
||||
<Statusbubble
|
||||
text={'Connected'}
|
||||
color={Green}
|
||||
shouldAnimate={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
getElement: (item: Probe): ReactElement => {
|
||||
return <ProbeElement probe={item} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: "Description",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
lastAlive: true,
|
||||
},
|
||||
title: "Status",
|
||||
type: FieldType.Text,
|
||||
getElement: (item: Probe): ReactElement => {
|
||||
if (
|
||||
item &&
|
||||
item["lastAlive"] &&
|
||||
OneUptimeDate.getNumberOfMinutesBetweenDates(
|
||||
OneUptimeDate.fromString(item["lastAlive"]),
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
) < 5
|
||||
) {
|
||||
return (
|
||||
<Statusbubble
|
||||
text={"Connected"}
|
||||
color={Green}
|
||||
shouldAnimate={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Statusbubble
|
||||
text={'Disconnected'}
|
||||
color={Red}
|
||||
shouldAnimate={false}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{showKeyModal && currentProbe ? (
|
||||
<ConfirmModal
|
||||
title={`Probe Key`}
|
||||
description={
|
||||
<div>
|
||||
<span>
|
||||
Here is your probe key. Please keep this a
|
||||
secret.
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
<span>
|
||||
<b>Probe ID: </b>{' '}
|
||||
{currentProbe['_id']?.toString()}
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
<span>
|
||||
<b>Probe Key: </b>{' '}
|
||||
{currentProbe['key']?.toString()}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
submitButtonText={'Close'}
|
||||
submitButtonType={ButtonStyleType.NORMAL}
|
||||
onSubmit={async () => {
|
||||
setShowKeyModal(false);
|
||||
}}
|
||||
return (
|
||||
<Statusbubble
|
||||
text={"Disconnected"}
|
||||
color={Red}
|
||||
shouldAnimate={false}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{showKeyModal && currentProbe ? (
|
||||
<ConfirmModal
|
||||
title={`Probe Key`}
|
||||
description={
|
||||
<div>
|
||||
<span>Here is your probe key. Please keep this a secret.</span>
|
||||
<br />
|
||||
<br />
|
||||
<span>
|
||||
<b>Probe ID: </b> {currentProbe["_id"]?.toString()}
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
<span>
|
||||
<b>Probe Key: </b> {currentProbe["key"]?.toString()}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
submitButtonText={"Close"}
|
||||
submitButtonType={ButtonStyleType.NORMAL}
|
||||
onSubmit={async () => {
|
||||
setShowKeyModal(false);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import SideMenu from 'CommonUI/src/Components/SideMenu/SideMenu';
|
||||
import SideMenuItem from 'CommonUI/src/Components/SideMenu/SideMenuItem';
|
||||
import SideMenuSection from 'CommonUI/src/Components/SideMenu/SideMenuSection';
|
||||
import React, { ReactElement } from 'react';
|
||||
import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import SideMenu from "Common/UI/Components/SideMenu/SideMenu";
|
||||
import SideMenuItem from "Common/UI/Components/SideMenu/SideMenuItem";
|
||||
import SideMenuSection from "Common/UI/Components/SideMenu/SideMenuSection";
|
||||
import React, { ReactElement } from "react";
|
||||
|
||||
const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
|
||||
return (
|
||||
<SideMenu>
|
||||
<SideMenuSection title="Basic">
|
||||
{/* <SideMenuItem
|
||||
return (
|
||||
<SideMenu>
|
||||
<SideMenuSection title="Basic">
|
||||
{/* <SideMenuItem
|
||||
link={{
|
||||
title: 'Host',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
@@ -20,62 +20,62 @@ const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
|
||||
}}
|
||||
icon={IconProp.Globe}
|
||||
/> */}
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'Authentication',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_AUTHENTICATION] as Route
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Lock}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Authentication",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_AUTHENTICATION] as Route,
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Lock}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="Notifications">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'Emails',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_SMTP] as Route
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Email}
|
||||
/>
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'Call and SMS',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_CALL_AND_SMS] as Route
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Call}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
<SideMenuSection title="Notifications">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Emails",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_SMTP] as Route,
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Email}
|
||||
/>
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Call and SMS",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_CALL_AND_SMS] as Route,
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Call}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="Monitoring">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'Global Probes',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_PROBES] as Route
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Signal}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
<SideMenuSection title="API and Integrations">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'API Key',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_API_KEY] as Route
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Code}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
</SideMenu>
|
||||
);
|
||||
<SideMenuSection title="Monitoring">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Global Probes",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_PROBES] as Route,
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Signal}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
<SideMenuSection title="API and Integrations">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "API Key",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_API_KEY] as Route,
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Code}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
</SideMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardSideMenu;
|
||||
|
||||
@@ -1,222 +1,130 @@
|
||||
import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import { ErrorFunction } from 'Common/Types/FunctionTypes';
|
||||
import { ButtonStyleType } from 'CommonUI/src/Components/Button/Button';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import ConfirmModal from 'CommonUI/src/Components/Modal/ConfirmModal';
|
||||
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import API from 'CommonUI/src/Utils/API/API';
|
||||
import ModelAPI from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import User from 'Model/Models/User';
|
||||
import React, { FunctionComponent, ReactElement, useState } from 'react';
|
||||
import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ModelTable from "Common/UI/Components/ModelTable/ModelTable";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const Users: FunctionComponent = (): ReactElement => {
|
||||
const [showConfirmVerifyEmailModal, setShowConfirmVerifyEmailModal] =
|
||||
useState<boolean>(false);
|
||||
const [selectedUser, setSelectedUser] = useState<User | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [isConfimModalLoading, setIsConfirmModalLoading] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [refreshItemsTrigger, setRefreshItemsTrigger] =
|
||||
useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<Page
|
||||
title={'Users'}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Admin Dashboard',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Users',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.USERS] as Route
|
||||
),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<ModelTable<User>
|
||||
modelType={User}
|
||||
id="users-table"
|
||||
isDeleteable={false}
|
||||
isEditable={false}
|
||||
showViewIdButton={true}
|
||||
refreshToggle={refreshItemsTrigger}
|
||||
isCreateable={true}
|
||||
name="Users"
|
||||
isViewable={false}
|
||||
cardProps={{
|
||||
title: 'Users',
|
||||
description: 'Here is a list of users in OneUptime.',
|
||||
}}
|
||||
actionButtons={[
|
||||
{
|
||||
title: 'Verify Email',
|
||||
buttonStyleType: ButtonStyleType.NORMAL,
|
||||
isVisible: (item: User) => {
|
||||
return !item.isEmailVerified;
|
||||
},
|
||||
onClick: async (
|
||||
item: User,
|
||||
onCompleteAction: VoidFunction,
|
||||
onError: ErrorFunction
|
||||
) => {
|
||||
try {
|
||||
setSelectedUser(item);
|
||||
setShowConfirmVerifyEmailModal(true);
|
||||
|
||||
onCompleteAction();
|
||||
} catch (err) {
|
||||
onCompleteAction();
|
||||
onError(err as Error);
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
noItemsMessage={'No users found.'}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: 'Email',
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
placeholder: 'email@company.com',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
password: true,
|
||||
},
|
||||
title: 'Password',
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
required: true,
|
||||
placeholder: 'Password',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: 'Full Name',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: 'John Smith',
|
||||
},
|
||||
]}
|
||||
showRefreshButton={true}
|
||||
viewPageRoute={Navigation.getCurrentRoute()}
|
||||
filters={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: 'Full Name',
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: 'Email',
|
||||
type: FieldType.Email,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
createdAt: true,
|
||||
},
|
||||
title: 'Created At',
|
||||
type: FieldType.DateTime,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: 'Full Name',
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: 'Email',
|
||||
type: FieldType.Email,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isEmailVerified: true,
|
||||
},
|
||||
title: 'Email Verified',
|
||||
type: FieldType.Boolean,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
createdAt: true,
|
||||
},
|
||||
title: 'Created At',
|
||||
type: FieldType.DateTime,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{error ? (
|
||||
<ConfirmModal
|
||||
title={`Error`}
|
||||
description={error}
|
||||
submitButtonText={'Close'}
|
||||
onSubmit={async () => {
|
||||
setError(null);
|
||||
}}
|
||||
submitButtonType={ButtonStyleType.NORMAL}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{showConfirmVerifyEmailModal && selectedUser ? (
|
||||
<ConfirmModal
|
||||
title={`Verify Email`}
|
||||
description={`Are you sure you want to verify the email - ${selectedUser.email}?`}
|
||||
isLoading={isConfimModalLoading}
|
||||
submitButtonText={'Verify Email'}
|
||||
onClose={async () => {
|
||||
setShowConfirmVerifyEmailModal(false);
|
||||
setSelectedUser(null);
|
||||
}}
|
||||
onSubmit={async () => {
|
||||
try {
|
||||
setIsConfirmModalLoading(true);
|
||||
await ModelAPI.updateById<User>({
|
||||
modelType: User,
|
||||
id: selectedUser.id!,
|
||||
data: {
|
||||
isEmailVerified: true,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
setError(API.getFriendlyMessage(err as Error));
|
||||
}
|
||||
|
||||
setRefreshItemsTrigger(!refreshItemsTrigger);
|
||||
setIsConfirmModalLoading(false);
|
||||
setShowConfirmVerifyEmailModal(false);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
return (
|
||||
<Page
|
||||
title={"Users"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Users",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.USERS] as Route),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<ModelTable<User>
|
||||
modelType={User}
|
||||
id="users-table"
|
||||
isDeleteable={false}
|
||||
isEditable={false}
|
||||
showViewIdButton={true}
|
||||
isCreateable={true}
|
||||
name="Users"
|
||||
isViewable={true}
|
||||
cardProps={{
|
||||
title: "Users",
|
||||
description: "Here is a list of users in OneUptime.",
|
||||
}}
|
||||
noItemsMessage={"No users found."}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: "Email",
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
placeholder: "email@company.com",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
password: true,
|
||||
},
|
||||
title: "Password",
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
required: true,
|
||||
placeholder: "Password",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Full Name",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: "John Smith",
|
||||
},
|
||||
]}
|
||||
showRefreshButton={true}
|
||||
viewPageRoute={Navigation.getCurrentRoute()}
|
||||
filters={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Full Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: "Email",
|
||||
type: FieldType.Email,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
createdAt: true,
|
||||
},
|
||||
title: "Created At",
|
||||
type: FieldType.DateTime,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Full Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: "Email",
|
||||
type: FieldType.Email,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isEmailVerified: true,
|
||||
},
|
||||
title: "Email Verified",
|
||||
type: FieldType.Boolean,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
createdAt: true,
|
||||
},
|
||||
title: "Created At",
|
||||
type: FieldType.DateTime,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Users;
|
||||
|
||||
50
AdminDashboard/src/Pages/Users/View/Delete.tsx
Normal file
50
AdminDashboard/src/Pages/Users/View/Delete.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import ModelDelete from "Common/UI/Components/ModelDelete/ModelDelete";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import SideMenuComponent from "./SideMenu";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import ModelPage from "Common/UI/Components/Page/ModelPage";
|
||||
|
||||
const DeletePage: FunctionComponent = (): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return (
|
||||
<ModelPage
|
||||
modelId={modelId}
|
||||
modelNameField="email"
|
||||
modelType={User}
|
||||
title={"User"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Users",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.USERS] as Route),
|
||||
},
|
||||
{
|
||||
title: "User",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.USER_VIEW] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenuComponent modelId={modelId} />}
|
||||
>
|
||||
<ModelDelete
|
||||
modelType={User}
|
||||
modelId={modelId}
|
||||
onDeleteSuccess={() => {
|
||||
Navigation.navigate(RouteMap[PageMap.USERS] as Route);
|
||||
}}
|
||||
/>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeletePage;
|
||||
136
AdminDashboard/src/Pages/Users/View/Index.tsx
Normal file
136
AdminDashboard/src/Pages/Users/View/Index.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import ModelPage from "Common/UI/Components/Page/ModelPage";
|
||||
import SideMenuComponent from "./SideMenu";
|
||||
|
||||
const Users: FunctionComponent = (): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID();
|
||||
|
||||
return (
|
||||
<ModelPage
|
||||
modelId={modelId}
|
||||
modelNameField="email"
|
||||
modelType={User}
|
||||
title={"User"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Users",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.USERS] as Route),
|
||||
},
|
||||
{
|
||||
title: "User",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.USER_VIEW] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenuComponent modelId={modelId} />}
|
||||
>
|
||||
<div>
|
||||
<CardModelDetail<User>
|
||||
name="User"
|
||||
cardProps={{
|
||||
title: "User",
|
||||
description: "User details",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit User"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: "Email",
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isEmailVerified: true,
|
||||
},
|
||||
title: "Email Verified",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
enableTwoFactorAuth: true,
|
||||
},
|
||||
title: "Two Factor Auth Enabled",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: User,
|
||||
id: "model-detail-user",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
_id: true,
|
||||
},
|
||||
title: "User ID",
|
||||
fieldType: FieldType.Text,
|
||||
placeholder: "-",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
fieldType: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: "Email",
|
||||
fieldType: FieldType.Email,
|
||||
placeholder: "-",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isEmailVerified: true,
|
||||
},
|
||||
title: "Email Verified",
|
||||
fieldType: FieldType.Boolean,
|
||||
placeholder: "No",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
enableTwoFactorAuth: true,
|
||||
},
|
||||
title: "Two Factor Auth Enabled",
|
||||
fieldType: FieldType.Boolean,
|
||||
placeholder: "No",
|
||||
},
|
||||
],
|
||||
modelId: modelId,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Users;
|
||||
53
AdminDashboard/src/Pages/Users/View/SideMenu.tsx
Normal file
53
AdminDashboard/src/Pages/Users/View/SideMenu.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import SideMenu from "Common/UI/Components/SideMenu/SideMenu";
|
||||
import SideMenuItem from "Common/UI/Components/SideMenu/SideMenuItem";
|
||||
import SideMenuSection from "Common/UI/Components/SideMenu/SideMenuSection";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
export interface SideMenuProps {
|
||||
modelId: ObjectID;
|
||||
}
|
||||
|
||||
const SideMenuComponent: FunctionComponent<SideMenuProps> = (
|
||||
props: SideMenuProps,
|
||||
): ReactElement => {
|
||||
return (
|
||||
<SideMenu>
|
||||
<SideMenuSection title="Basic">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Overview",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.USER_VIEW] as Route,
|
||||
{
|
||||
modelId: props.modelId,
|
||||
},
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Info}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="Advanced">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Delete",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.USER_DELETE] as Route,
|
||||
{
|
||||
modelId: props.modelId,
|
||||
},
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Trash}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
</SideMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default SideMenuComponent;
|
||||
@@ -1,8 +1,8 @@
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import ModelAPI from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
|
||||
|
||||
export default class AdminModelAPI extends ModelAPI {
|
||||
public static override getCommonHeaders(): Dictionary<string> {
|
||||
return {};
|
||||
}
|
||||
public static override getCommonHeaders(): Dictionary<string> {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
enum PageMap {
|
||||
INIT = 'INIT',
|
||||
HOME = 'HOME',
|
||||
LOGOUT = 'LOGOUT',
|
||||
SETTINGS = 'SETTINGS',
|
||||
USERS = 'USERS',
|
||||
PROJECTS = 'PROJECTS',
|
||||
INIT = "INIT",
|
||||
HOME = "HOME",
|
||||
LOGOUT = "LOGOUT",
|
||||
SETTINGS = "SETTINGS",
|
||||
|
||||
SETTINGS_HOST = 'SETTINGS_HOST',
|
||||
SETTINGS_SMTP = 'SETTINGS_SMTP',
|
||||
SETTINGS_CALL_AND_SMS = 'SETTINGS_CALL_AND_SMS',
|
||||
SETTINGS_PROBES = 'SETTINGS_PROBES',
|
||||
SETTINGS_AUTHENTICATION = 'SETTINGS_AUTHENTICATION',
|
||||
SETTINGS_API_KEY = 'SETTINGS_API_KEY',
|
||||
USERS = "USERS",
|
||||
USER_VIEW = "USER_VIEW",
|
||||
USER_DELETE = "USER_DELETE",
|
||||
|
||||
PROJECTS = "PROJECTS",
|
||||
PROJECT_VIEW = "PROJECT_VIEW",
|
||||
PROJECT_DELETE = "PROJECT_DELETE",
|
||||
|
||||
SETTINGS_HOST = "SETTINGS_HOST",
|
||||
SETTINGS_SMTP = "SETTINGS_SMTP",
|
||||
SETTINGS_CALL_AND_SMS = "SETTINGS_CALL_AND_SMS",
|
||||
SETTINGS_PROBES = "SETTINGS_PROBES",
|
||||
SETTINGS_AUTHENTICATION = "SETTINGS_AUTHENTICATION",
|
||||
SETTINGS_API_KEY = "SETTINGS_API_KEY",
|
||||
}
|
||||
|
||||
export default PageMap;
|
||||
|
||||
@@ -1,54 +1,65 @@
|
||||
import PageMap from './PageMap';
|
||||
import RouteParams from './RouteParams';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import PageMap from "./PageMap";
|
||||
import RouteParams from "./RouteParams";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
|
||||
const RouteMap: Dictionary<Route> = {
|
||||
[PageMap.INIT]: new Route(`/admin`),
|
||||
[PageMap.HOME]: new Route(`/admin`),
|
||||
[PageMap.LOGOUT]: new Route(`/admin/logout`),
|
||||
[PageMap.SETTINGS]: new Route(`/admin/settings/host`),
|
||||
[PageMap.PROJECTS]: new Route(`/admin/projects`),
|
||||
[PageMap.USERS]: new Route(`/admin/users`),
|
||||
[PageMap.SETTINGS_HOST]: new Route(`/admin/settings/host`),
|
||||
[PageMap.SETTINGS_SMTP]: new Route(`/admin/settings/smtp`),
|
||||
[PageMap.SETTINGS_CALL_AND_SMS]: new Route(`/admin/settings/call-and-sms`),
|
||||
[PageMap.SETTINGS_PROBES]: new Route(`/admin/settings/probes`),
|
||||
[PageMap.SETTINGS_AUTHENTICATION]: new Route(
|
||||
`/admin/settings/authentication`
|
||||
),
|
||||
[PageMap.SETTINGS_API_KEY]: new Route(`/admin/settings/api-key`),
|
||||
[PageMap.INIT]: new Route(`/admin`),
|
||||
[PageMap.HOME]: new Route(`/admin`),
|
||||
[PageMap.LOGOUT]: new Route(`/admin/logout`),
|
||||
[PageMap.SETTINGS]: new Route(`/admin/settings/host`),
|
||||
|
||||
[PageMap.PROJECTS]: new Route(`/admin/projects`),
|
||||
[PageMap.PROJECT_VIEW]: new Route(`/admin/projects/${RouteParams.ModelID}`),
|
||||
[PageMap.PROJECT_DELETE]: new Route(
|
||||
`/admin/projects/${RouteParams.ModelID}/delete`,
|
||||
),
|
||||
|
||||
[PageMap.USERS]: new Route(`/admin/users`),
|
||||
[PageMap.USER_VIEW]: new Route(`/admin/users/${RouteParams.ModelID}`),
|
||||
[PageMap.USER_DELETE]: new Route(
|
||||
`/admin/users/${RouteParams.ModelID}/delete`,
|
||||
),
|
||||
|
||||
[PageMap.SETTINGS_HOST]: new Route(`/admin/settings/host`),
|
||||
[PageMap.SETTINGS_SMTP]: new Route(`/admin/settings/smtp`),
|
||||
[PageMap.SETTINGS_CALL_AND_SMS]: new Route(`/admin/settings/call-and-sms`),
|
||||
[PageMap.SETTINGS_PROBES]: new Route(`/admin/settings/probes`),
|
||||
[PageMap.SETTINGS_AUTHENTICATION]: new Route(
|
||||
`/admin/settings/authentication`,
|
||||
),
|
||||
[PageMap.SETTINGS_API_KEY]: new Route(`/admin/settings/api-key`),
|
||||
};
|
||||
|
||||
export class RouteUtil {
|
||||
public static populateRouteParams(
|
||||
route: Route,
|
||||
props?: {
|
||||
modelId?: ObjectID;
|
||||
subModelId?: ObjectID;
|
||||
}
|
||||
): Route {
|
||||
// populate projectid
|
||||
public static populateRouteParams(
|
||||
route: Route,
|
||||
props?: {
|
||||
modelId?: ObjectID;
|
||||
subModelId?: ObjectID;
|
||||
},
|
||||
): Route {
|
||||
// populate projectid
|
||||
|
||||
const tempRoute: Route = new Route(route.toString());
|
||||
const tempRoute: Route = new Route(route.toString());
|
||||
|
||||
if (props && props.modelId) {
|
||||
route = tempRoute.addRouteParam(
|
||||
RouteParams.ModelID,
|
||||
props.modelId.toString()
|
||||
);
|
||||
}
|
||||
|
||||
if (props && props.subModelId) {
|
||||
route = tempRoute.addRouteParam(
|
||||
RouteParams.SubModelID,
|
||||
props.subModelId.toString()
|
||||
);
|
||||
}
|
||||
|
||||
return tempRoute;
|
||||
if (props && props.modelId) {
|
||||
route = tempRoute.addRouteParam(
|
||||
RouteParams.ModelID,
|
||||
props.modelId.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
if (props && props.subModelId) {
|
||||
route = tempRoute.addRouteParam(
|
||||
RouteParams.SubModelID,
|
||||
props.subModelId.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return tempRoute;
|
||||
}
|
||||
}
|
||||
|
||||
export default RouteMap;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
enum RouteParams {
|
||||
ModelID = ':id',
|
||||
SubModelID = ':subModelId',
|
||||
ModelID = ":id",
|
||||
SubModelID = ":subModelId",
|
||||
}
|
||||
|
||||
export default RouteParams;
|
||||
|
||||
@@ -1,82 +1,84 @@
|
||||
require('ts-loader');
|
||||
require('file-loader');
|
||||
require('style-loader');
|
||||
require('css-loader');
|
||||
require('sass-loader');
|
||||
require("ts-loader");
|
||||
require("file-loader");
|
||||
require("style-loader");
|
||||
require("css-loader");
|
||||
require("sass-loader");
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const dotenv = require('dotenv');
|
||||
const express = require('express');
|
||||
const dotenv = require("dotenv");
|
||||
const express = require("express");
|
||||
|
||||
const readEnvFile = (pathToFile) => {
|
||||
const parsed = dotenv.config({ path: pathToFile }).parsed;
|
||||
|
||||
const parsed = dotenv.config({ path: pathToFile }).parsed;
|
||||
const env = {};
|
||||
|
||||
const env = {};
|
||||
for (const key in parsed) {
|
||||
env[key] = JSON.stringify(parsed[key]);
|
||||
}
|
||||
|
||||
for (const key in parsed) {
|
||||
env[key] = JSON.stringify(parsed[key]);
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
return env;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
entry: "./src/Index.tsx",
|
||||
mode: "development",
|
||||
output: {
|
||||
filename: "bundle.js",
|
||||
path: path.resolve(__dirname, "public", "dist"),
|
||||
publicPath: "/admin/dist/",
|
||||
entry: "./src/Index.tsx",
|
||||
mode: "development",
|
||||
output: {
|
||||
filename: "bundle.js",
|
||||
path: path.resolve(__dirname, "public", "dist"),
|
||||
publicPath: "/admin/dist/",
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".ts", ".tsx", ".js", ".jsx", ".json", ".css", ".scss"],
|
||||
alias: {
|
||||
react: path.resolve("./node_modules/react"),
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json', '.css', '.scss'],
|
||||
alias: {
|
||||
react: path.resolve('./node_modules/react'),
|
||||
}
|
||||
},
|
||||
externals: {
|
||||
'react-native-sqlite-storage': 'react-native-sqlite-storage'
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process': {
|
||||
'env': {
|
||||
...readEnvFile('/usr/src/app/dev-env/.env')
|
||||
}
|
||||
}
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
use: 'ts-loader'
|
||||
},
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
use: ['style-loader', 'css-loader', "sass-loader"]
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||
loader: 'file-loader'
|
||||
}
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
devMiddleware: {
|
||||
writeToDisk: true,
|
||||
},
|
||||
externals: {
|
||||
"react-native-sqlite-storage": "react-native-sqlite-storage",
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
process: {
|
||||
env: {
|
||||
...readEnvFile("/usr/src/app/dev-env/.env"),
|
||||
},
|
||||
allowedHosts: "all",
|
||||
setupMiddlewares: (middlewares, devServer) => {
|
||||
devServer.app.use('/admin/assets', express.static(path.resolve(__dirname, 'public', 'assets')));
|
||||
return middlewares;
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
use: "ts-loader",
|
||||
},
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
use: ["style-loader", "css-loader", "sass-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ["style-loader", "css-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||
loader: "file-loader",
|
||||
},
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
devMiddleware: {
|
||||
writeToDisk: true,
|
||||
},
|
||||
devtool: 'eval-source-map',
|
||||
}
|
||||
allowedHosts: "all",
|
||||
setupMiddlewares: (middlewares, devServer) => {
|
||||
devServer.app.use(
|
||||
"/admin/assets",
|
||||
express.static(path.resolve(__dirname, "public", "assets")),
|
||||
);
|
||||
return middlewares;
|
||||
},
|
||||
},
|
||||
devtool: "eval-source-map",
|
||||
};
|
||||
|
||||
@@ -38,25 +38,6 @@ RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/Comm
|
||||
RUN npm install
|
||||
COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
# Set version in ./Model/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/Model/package.json
|
||||
RUN npm install
|
||||
COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
# Set version in ./CommonServer/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/CommonServer/package.json
|
||||
RUN npm install
|
||||
COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
|
||||
ENV PRODUCTION=true
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
@@ -1,89 +1,86 @@
|
||||
import AuthenticationServiceHandler from './Service/Authentication';
|
||||
import DataTypeServiceHandler from './Service/DataType';
|
||||
import ErrorServiceHandler from './Service/Errors';
|
||||
import IntroductionServiceHandler from './Service/Introduction';
|
||||
import ModelServiceHandler from './Service/Model';
|
||||
import PageNotFoundServiceHandler from './Service/PageNotFound';
|
||||
import PaginationServiceHandler from './Service/Pagination';
|
||||
import PermissionServiceHandler from './Service/Permissions';
|
||||
import StatusServiceHandler from './Service/Status';
|
||||
import { StaticPath } from './Utils/Config';
|
||||
import ResourceUtil, { ModelDocumentation } from './Utils/Resources';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import FeatureSet from 'CommonServer/Types/FeatureSet';
|
||||
import AuthenticationServiceHandler from "./Service/Authentication";
|
||||
import DataTypeServiceHandler from "./Service/DataType";
|
||||
import ErrorServiceHandler from "./Service/Errors";
|
||||
import IntroductionServiceHandler from "./Service/Introduction";
|
||||
import ModelServiceHandler from "./Service/Model";
|
||||
import PageNotFoundServiceHandler from "./Service/PageNotFound";
|
||||
import PaginationServiceHandler from "./Service/Pagination";
|
||||
import PermissionServiceHandler from "./Service/Permissions";
|
||||
import StatusServiceHandler from "./Service/Status";
|
||||
import { StaticPath } from "./Utils/Config";
|
||||
import ResourceUtil, { ModelDocumentation } from "./Utils/Resources";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import FeatureSet from "Common/Server/Types/FeatureSet";
|
||||
import Express, {
|
||||
ExpressApplication,
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
ExpressStatic,
|
||||
} from 'CommonServer/Utils/Express';
|
||||
ExpressApplication,
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
ExpressStatic,
|
||||
} from "Common/Server/Utils/Express";
|
||||
|
||||
const APIReferenceFeatureSet: FeatureSet = {
|
||||
init: async (): Promise<void> => {
|
||||
const ResourceDictionary: Dictionary<ModelDocumentation> =
|
||||
ResourceUtil.getResourceDictionaryByPath();
|
||||
init: async (): Promise<void> => {
|
||||
const ResourceDictionary: Dictionary<ModelDocumentation> =
|
||||
ResourceUtil.getResourceDictionaryByPath();
|
||||
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
|
||||
app.use('/reference', ExpressStatic(StaticPath, { maxAge: 2592000 }));
|
||||
// Serve static files for the API reference with a cache max age of 30 days
|
||||
app.use("/reference", ExpressStatic(StaticPath, { maxAge: 2592000 }));
|
||||
|
||||
// Index page
|
||||
app.get(
|
||||
['/reference'],
|
||||
(_req: ExpressRequest, res: ExpressResponse) => {
|
||||
return res.redirect('/reference/introduction');
|
||||
}
|
||||
);
|
||||
// Redirect index page to the introduction page
|
||||
app.get(["/reference"], (_req: ExpressRequest, res: ExpressResponse) => {
|
||||
return res.redirect("/reference/introduction");
|
||||
});
|
||||
|
||||
app.get(
|
||||
['/reference/page-not-found'],
|
||||
(req: ExpressRequest, res: ExpressResponse) => {
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
}
|
||||
);
|
||||
// Handle "Page Not Found" page
|
||||
app.get(
|
||||
["/reference/page-not-found"],
|
||||
(req: ExpressRequest, res: ExpressResponse) => {
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
},
|
||||
);
|
||||
|
||||
// All Pages
|
||||
app.get(
|
||||
['/reference/:page'],
|
||||
(req: ExpressRequest, res: ExpressResponse) => {
|
||||
const page: string | undefined = req.params['page'];
|
||||
// Handle all other pages based on the "page" parameter
|
||||
app.get(
|
||||
["/reference/:page"],
|
||||
(req: ExpressRequest, res: ExpressResponse) => {
|
||||
const page: string | undefined = req.params["page"];
|
||||
|
||||
if (!page) {
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
}
|
||||
if (!page) {
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
}
|
||||
|
||||
const currentResource: ModelDocumentation | undefined =
|
||||
ResourceDictionary[page];
|
||||
const currentResource: ModelDocumentation | undefined =
|
||||
ResourceDictionary[page];
|
||||
|
||||
if (req.params['page'] === 'permissions') {
|
||||
return PermissionServiceHandler.executeResponse(req, res);
|
||||
} else if (req.params['page'] === 'authentication') {
|
||||
return AuthenticationServiceHandler.executeResponse(
|
||||
req,
|
||||
res
|
||||
);
|
||||
} else if (req.params['page'] === 'pagination') {
|
||||
return PaginationServiceHandler.executeResponse(req, res);
|
||||
} else if (req.params['page'] === 'errors') {
|
||||
return ErrorServiceHandler.executeResponse(req, res);
|
||||
} else if (req.params['page'] === 'introduction') {
|
||||
return IntroductionServiceHandler.executeResponse(req, res);
|
||||
} else if (req.params['page'] === 'status') {
|
||||
return StatusServiceHandler.executeResponse(req, res);
|
||||
} else if (req.params['page'] === 'data-types') {
|
||||
return DataTypeServiceHandler.executeResponse(req, res);
|
||||
} else if (currentResource) {
|
||||
return ModelServiceHandler.executeResponse(req, res);
|
||||
}
|
||||
// page not found
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
}
|
||||
);
|
||||
// Execute the appropriate service handler based on the "page" parameter
|
||||
if (req.params["page"] === "permissions") {
|
||||
return PermissionServiceHandler.executeResponse(req, res);
|
||||
} else if (req.params["page"] === "authentication") {
|
||||
return AuthenticationServiceHandler.executeResponse(req, res);
|
||||
} else if (req.params["page"] === "pagination") {
|
||||
return PaginationServiceHandler.executeResponse(req, res);
|
||||
} else if (req.params["page"] === "errors") {
|
||||
return ErrorServiceHandler.executeResponse(req, res);
|
||||
} else if (req.params["page"] === "introduction") {
|
||||
return IntroductionServiceHandler.executeResponse(req, res);
|
||||
} else if (req.params["page"] === "status") {
|
||||
return StatusServiceHandler.executeResponse(req, res);
|
||||
} else if (req.params["page"] === "data-types") {
|
||||
return DataTypeServiceHandler.executeResponse(req, res);
|
||||
} else if (currentResource) {
|
||||
return ModelServiceHandler.executeResponse(req, res);
|
||||
}
|
||||
// page not found
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
},
|
||||
);
|
||||
|
||||
app.get('/reference/*', (req: ExpressRequest, res: ExpressResponse) => {
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
});
|
||||
},
|
||||
app.get("/reference/*", (req: ExpressRequest, res: ExpressResponse) => {
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default APIReferenceFeatureSet;
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
import { ViewsPath } from '../Utils/Config';
|
||||
import ResourceUtil, { ModelDocumentation } from '../Utils/Resources';
|
||||
import { ExpressRequest, ExpressResponse } from 'CommonServer/Utils/Express';
|
||||
import { ViewsPath } from "../Utils/Config";
|
||||
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
|
||||
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
|
||||
|
||||
// Retrieve resources documentation
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
|
||||
|
||||
export default class ServiceHandler {
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
let pageTitle: string = '';
|
||||
let pageDescription: string = '';
|
||||
const page: string | undefined = req.params['page'];
|
||||
const pageData: any = {};
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
let pageTitle: string = "";
|
||||
let pageDescription: string = "";
|
||||
|
||||
pageTitle = 'Authentication';
|
||||
pageDescription =
|
||||
'Learn how to authenticate requests with OneUptime API';
|
||||
// Extract page parameter from request
|
||||
const page: string | undefined = req.params["page"];
|
||||
const pageData: any = {};
|
||||
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
// Set default page title and description for the authentication page
|
||||
pageTitle = "Authentication";
|
||||
pageDescription = "Learn how to authenticate requests with OneUptime API";
|
||||
|
||||
// Render the index page with the specified parameters
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,136 +1,126 @@
|
||||
import { CodeExamplesPath, ViewsPath } from '../Utils/Config';
|
||||
import ResourceUtil, { ModelDocumentation } from '../Utils/Resources';
|
||||
import LocalCache from 'CommonServer/Infrastructure/LocalCache';
|
||||
import { ExpressRequest, ExpressResponse } from 'CommonServer/Utils/Express';
|
||||
import LocalFile from 'CommonServer/Utils/LocalFile';
|
||||
import { CodeExamplesPath, ViewsPath } from "../Utils/Config";
|
||||
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
|
||||
import LocalCache from "Common/Server/Infrastructure/LocalCache";
|
||||
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
|
||||
import LocalFile from "Common/Server/Utils/LocalFile";
|
||||
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
|
||||
|
||||
export default class ServiceHandler {
|
||||
public static async executeResponse(
|
||||
_req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
const pageData: any = {};
|
||||
public static async executeResponse(
|
||||
_req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
const pageData: any = {};
|
||||
|
||||
pageData.selectCode = await LocalCache.getOrSetString(
|
||||
'data-type',
|
||||
'select',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/Select.md`
|
||||
);
|
||||
}
|
||||
pageData.selectCode = await LocalCache.getOrSetString(
|
||||
"data-type",
|
||||
"select",
|
||||
async () => {
|
||||
return await LocalFile.read(`${CodeExamplesPath}/DataTypes/Select.md`);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.sortCode = await LocalCache.getOrSetString(
|
||||
"data-type",
|
||||
"sort",
|
||||
async () => {
|
||||
return await LocalFile.read(`${CodeExamplesPath}/DataTypes/Sort.md`);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.equalToCode = await LocalCache.getOrSetString(
|
||||
"data-type",
|
||||
"equal-to",
|
||||
async () => {
|
||||
return await LocalFile.read(`${CodeExamplesPath}/DataTypes/EqualTo.md`);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.equalToOrNullCode = await LocalCache.getOrSetString(
|
||||
"data-type",
|
||||
"equal-to-or-null",
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/EqualToOrNull.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.sortCode = await LocalCache.getOrSetString(
|
||||
'data-type',
|
||||
'sort',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/Sort.md`
|
||||
);
|
||||
}
|
||||
pageData.greaterThanCode = await LocalCache.getOrSetString(
|
||||
"data-type",
|
||||
"greater-than",
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/GreaterThan.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.equalToCode = await LocalCache.getOrSetString(
|
||||
'data-type',
|
||||
'equal-to',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/EqualTo.md`
|
||||
);
|
||||
}
|
||||
pageData.greaterThanOrEqualCode = await LocalCache.getOrSetString(
|
||||
"data-type",
|
||||
"greater-than-or-equal",
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/GreaterThanOrEqual.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.equalToOrNullCode = await LocalCache.getOrSetString(
|
||||
'data-type',
|
||||
'equal-to-or-null',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/EqualToOrNull.md`
|
||||
);
|
||||
}
|
||||
pageData.lessThanCode = await LocalCache.getOrSetString(
|
||||
"data-type",
|
||||
"less-than",
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/LessThan.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.greaterThanCode = await LocalCache.getOrSetString(
|
||||
'data-type',
|
||||
'greater-than',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/GreaterThan.md`
|
||||
);
|
||||
}
|
||||
pageData.lessThanOrEqualCode = await LocalCache.getOrSetString(
|
||||
"data-type",
|
||||
"less-than-or-equal",
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/LessThanOrEqual.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.greaterThanOrEqualCode = await LocalCache.getOrSetString(
|
||||
'data-type',
|
||||
'greater-than-or-equal',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/GreaterThanOrEqual.md`
|
||||
);
|
||||
}
|
||||
pageData.isNullCode = await LocalCache.getOrSetString(
|
||||
"data-type",
|
||||
"is-null",
|
||||
async () => {
|
||||
return await LocalFile.read(`${CodeExamplesPath}/DataTypes/IsNull.md`);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.notNullCode = await LocalCache.getOrSetString(
|
||||
"data-type",
|
||||
"not-null",
|
||||
async () => {
|
||||
return await LocalFile.read(`${CodeExamplesPath}/DataTypes/NotNull.md`);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.notEqualToCode = await LocalCache.getOrSetString(
|
||||
"data-type",
|
||||
"not-equals",
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/NotEqualTo.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.lessThanCode = await LocalCache.getOrSetString(
|
||||
'data-type',
|
||||
'less-than',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/LessThan.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.lessThanOrEqualCode = await LocalCache.getOrSetString(
|
||||
'data-type',
|
||||
'less-than-or-equal',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/LessThanOrEqual.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.isNullCode = await LocalCache.getOrSetString(
|
||||
'data-type',
|
||||
'is-null',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/IsNull.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.notNullCode = await LocalCache.getOrSetString(
|
||||
'data-type',
|
||||
'not-null',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/NotNull.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.notEqualToCode = await LocalCache.getOrSetString(
|
||||
'data-type',
|
||||
'not-equals',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/DataTypes/NotEqualTo.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
res.status(200);
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: 'data-types',
|
||||
pageTitle: 'Data Types',
|
||||
pageDescription:
|
||||
'Data Types that can be used to interact with OneUptime API',
|
||||
resources: Resources,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
res.status(200);
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: "data-types",
|
||||
pageTitle: "Data Types",
|
||||
pageDescription:
|
||||
"Data Types that can be used to interact with OneUptime API",
|
||||
resources: Resources,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
import { ViewsPath } from '../Utils/Config';
|
||||
import ResourceUtil, { ModelDocumentation } from '../Utils/Resources';
|
||||
import { ExpressRequest, ExpressResponse } from 'CommonServer/Utils/Express';
|
||||
import { ViewsPath } from "../Utils/Config";
|
||||
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
|
||||
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
|
||||
|
||||
// Fetch a list of resources used in the application
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
|
||||
|
||||
export default class ServiceHandler {
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
let pageTitle: string = '';
|
||||
let pageDescription: string = '';
|
||||
const page: string | undefined = req.params['page'];
|
||||
const pageData: any = {};
|
||||
// Handles the HTTP response for a given request
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
let pageTitle: string = "";
|
||||
let pageDescription: string = "";
|
||||
|
||||
pageTitle = 'Errors';
|
||||
pageDescription = 'Learn more about how we return errors from API';
|
||||
// Get the 'page' parameter from the request
|
||||
const page: string | undefined = req.params["page"];
|
||||
const pageData: any = {};
|
||||
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
// Set the default page title and description
|
||||
pageTitle = "Errors";
|
||||
pageDescription = "Learn more about how we return errors from API";
|
||||
|
||||
// Render the response using the given view and data
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,40 @@
|
||||
import { ViewsPath } from '../Utils/Config';
|
||||
import ResourceUtil, { ModelDocumentation } from '../Utils/Resources';
|
||||
import { ExpressRequest, ExpressResponse } from 'CommonServer/Utils/Express';
|
||||
import { ViewsPath } from "../Utils/Config";
|
||||
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
|
||||
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
|
||||
|
||||
// Get all resources and featured resources from ResourceUtil
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
|
||||
const FeaturedResources: Array<ModelDocumentation> =
|
||||
ResourceUtil.getFeaturedResources();
|
||||
ResourceUtil.getFeaturedResources();
|
||||
|
||||
export default class ServiceHandler {
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
let pageTitle: string = '';
|
||||
let pageDescription: string = '';
|
||||
const page: string | undefined = req.params['page'];
|
||||
const pageData: any = {};
|
||||
// Handle the API request
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
// Initialize page title and description
|
||||
let pageTitle: string = "";
|
||||
let pageDescription: string = "";
|
||||
|
||||
pageData.featuredResources = FeaturedResources;
|
||||
pageTitle = 'Introduction';
|
||||
pageDescription = 'API Reference for OneUptime';
|
||||
// Get the requested page from the URL parameters
|
||||
const page: string | undefined = req.params["page"];
|
||||
const pageData: any = {};
|
||||
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
// Set featured resources for the page
|
||||
pageData.featuredResources = FeaturedResources;
|
||||
|
||||
// Set page title and description
|
||||
pageTitle = "Introduction";
|
||||
pageDescription = "API Reference for OneUptime";
|
||||
|
||||
// Render the index page with the required data
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,244 +1,269 @@
|
||||
import { CodeExamplesPath, ViewsPath } from '../Utils/Config';
|
||||
import ResourceUtil, { ModelDocumentation } from '../Utils/Resources';
|
||||
import PageNotFoundServiceHandler from './PageNotFound';
|
||||
import { AppApiRoute } from 'Common/ServiceRoute';
|
||||
import { ColumnAccessControl } from 'Common/Types/BaseDatabase/AccessControl';
|
||||
import { getTableColumns } from 'Common/Types/Database/TableColumn';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import { CodeExamplesPath, ViewsPath } from "../Utils/Config";
|
||||
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
|
||||
import PageNotFoundServiceHandler from "./PageNotFound";
|
||||
import { AppApiRoute } from "Common/ServiceRoute";
|
||||
import { ColumnAccessControl } from "Common/Types/BaseDatabase/AccessControl";
|
||||
import { getTableColumns } from "Common/Types/Database/TableColumn";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import Permission, {
|
||||
PermissionHelper,
|
||||
PermissionProps,
|
||||
} from 'Common/Types/Permission';
|
||||
import LocalCache from 'CommonServer/Infrastructure/LocalCache';
|
||||
import { ExpressRequest, ExpressResponse } from 'CommonServer/Utils/Express';
|
||||
import LocalFile from 'CommonServer/Utils/LocalFile';
|
||||
PermissionHelper,
|
||||
PermissionProps,
|
||||
} from "Common/Types/Permission";
|
||||
import LocalCache from "Common/Server/Infrastructure/LocalCache";
|
||||
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
|
||||
import LocalFile from "Common/Server/Utils/LocalFile";
|
||||
|
||||
// Get all resources and resource dictionary
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
|
||||
const ResourceDictionary: Dictionary<ModelDocumentation> =
|
||||
ResourceUtil.getResourceDictionaryByPath();
|
||||
ResourceUtil.getResourceDictionaryByPath();
|
||||
|
||||
// Get all permission props
|
||||
const PermissionDictionary: Dictionary<PermissionProps> =
|
||||
PermissionHelper.getAllPermissionPropsAsDictionary();
|
||||
PermissionHelper.getAllPermissionPropsAsDictionary();
|
||||
|
||||
export default class ServiceHandler {
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
let pageTitle: string = '';
|
||||
let pageDescription: string = '';
|
||||
let page: string | undefined = req.params['page'];
|
||||
const pageData: any = {};
|
||||
// Execute response for a given page
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
let pageTitle: string = "";
|
||||
let pageDescription: string = "";
|
||||
let page: string | undefined = req.params["page"];
|
||||
const pageData: any = {};
|
||||
|
||||
if (!page) {
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
}
|
||||
|
||||
const currentResource: ModelDocumentation | undefined =
|
||||
ResourceDictionary[page];
|
||||
|
||||
if (!currentResource) {
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
}
|
||||
|
||||
// Resource Page.
|
||||
pageTitle = currentResource.name;
|
||||
pageDescription = currentResource.description;
|
||||
|
||||
page = 'model';
|
||||
|
||||
const tableColumns: any = getTableColumns(currentResource.model);
|
||||
|
||||
for (const key in tableColumns) {
|
||||
const accessControl: ColumnAccessControl | null =
|
||||
currentResource.model.getColumnAccessControlFor(key);
|
||||
|
||||
if (!accessControl) {
|
||||
// remove columns with no access
|
||||
delete tableColumns[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
accessControl?.create.length === 0 &&
|
||||
accessControl?.read.length === 0 &&
|
||||
accessControl?.update.length === 0
|
||||
) {
|
||||
// remove columns with no access
|
||||
delete tableColumns[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
tableColumns[key].permissions = accessControl;
|
||||
}
|
||||
|
||||
delete tableColumns['deletedAt'];
|
||||
delete tableColumns['deletedByUserId'];
|
||||
delete tableColumns['deletedByUser'];
|
||||
delete tableColumns['version'];
|
||||
|
||||
pageData.title = currentResource.model.singularName;
|
||||
pageData.description = currentResource.model.tableDescription;
|
||||
pageData.columns = tableColumns;
|
||||
pageData.tablePermissions = {
|
||||
read: currentResource.model.readRecordPermissions.map(
|
||||
(permission: Permission) => {
|
||||
return PermissionDictionary[permission];
|
||||
}
|
||||
),
|
||||
update: currentResource.model.updateRecordPermissions.map(
|
||||
(permission: Permission) => {
|
||||
return PermissionDictionary[permission];
|
||||
}
|
||||
),
|
||||
delete: currentResource.model.deleteRecordPermissions.map(
|
||||
(permission: Permission) => {
|
||||
return PermissionDictionary[permission];
|
||||
}
|
||||
),
|
||||
create: currentResource.model.createRecordPermissions.map(
|
||||
(permission: Permission) => {
|
||||
return PermissionDictionary[permission];
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
pageData.listRequest = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'list-request',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/ListRequest.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.itemRequest = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'item-request',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/ItemRequest.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.itemResponse = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'item-response',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/ItemResponse.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.countRequest = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'count-request',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/CountRequest.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.countResponse = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'count-response',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/CountResponse.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.updateRequest = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'update-request',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/UpdateRequest.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.updateResponse = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'update-response',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/UpdateResponse.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.createRequest = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'create-request',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/CreateRequest.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.createResponse = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'create-response',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/CreateResponse.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.deleteRequest = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'delete-request',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/DeleteRequest.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.deleteResponse = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'delete-response',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/DeleteResponse.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.listResponse = await LocalCache.getOrSetString(
|
||||
'model',
|
||||
'list-response',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/ListResponse.md`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
pageData.exampleObjectID = ObjectID.generate();
|
||||
|
||||
pageData.apiPath =
|
||||
AppApiRoute.toString() +
|
||||
currentResource.model.crudApiPath?.toString();
|
||||
|
||||
pageData.isMasterAdminApiDocs =
|
||||
currentResource.model.isMasterAdminApiDocs;
|
||||
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
// Check if page is provided
|
||||
if (!page) {
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
}
|
||||
|
||||
// Get current resource
|
||||
const currentResource: ModelDocumentation | undefined =
|
||||
ResourceDictionary[page];
|
||||
|
||||
// Check if current resource exists
|
||||
if (!currentResource) {
|
||||
return PageNotFoundServiceHandler.executeResponse(req, res);
|
||||
}
|
||||
|
||||
// Set page title and description
|
||||
pageTitle = currentResource.name;
|
||||
pageDescription = currentResource.description;
|
||||
|
||||
page = "model";
|
||||
|
||||
// Get table columns for current resource
|
||||
const tableColumns: any = getTableColumns(currentResource.model);
|
||||
|
||||
// Filter out columns with no access
|
||||
for (const key in tableColumns) {
|
||||
const accessControl: ColumnAccessControl | null =
|
||||
currentResource.model.getColumnAccessControlFor(key);
|
||||
|
||||
if (!accessControl) {
|
||||
delete tableColumns[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
accessControl?.create.length === 0 &&
|
||||
accessControl?.read.length === 0 &&
|
||||
accessControl?.update.length === 0
|
||||
) {
|
||||
delete tableColumns[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
tableColumns[key].permissions = accessControl;
|
||||
}
|
||||
|
||||
// Remove unnecessary columns
|
||||
delete tableColumns["deletedAt"];
|
||||
delete tableColumns["deletedByUserId"];
|
||||
delete tableColumns["deletedByUser"];
|
||||
delete tableColumns["version"];
|
||||
|
||||
// Set page data
|
||||
pageData.title = currentResource.model.singularName;
|
||||
pageData.description = currentResource.model.tableDescription;
|
||||
pageData.columns = tableColumns;
|
||||
|
||||
pageData.tablePermissions = {
|
||||
read: currentResource.model.readRecordPermissions.map(
|
||||
(permission: Permission) => {
|
||||
return PermissionDictionary[permission];
|
||||
},
|
||||
),
|
||||
update: currentResource.model.updateRecordPermissions.map(
|
||||
(permission: Permission) => {
|
||||
return PermissionDictionary[permission];
|
||||
},
|
||||
),
|
||||
delete: currentResource.model.deleteRecordPermissions.map(
|
||||
(permission: Permission) => {
|
||||
return PermissionDictionary[permission];
|
||||
},
|
||||
),
|
||||
create: currentResource.model.createRecordPermissions.map(
|
||||
(permission: Permission) => {
|
||||
return PermissionDictionary[permission];
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
// Cache the list request data
|
||||
pageData.listRequest = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"list-request",
|
||||
async () => {
|
||||
// Read the list request data from a file
|
||||
return await LocalFile.read(`${CodeExamplesPath}/Model/ListRequest.md`);
|
||||
},
|
||||
);
|
||||
|
||||
// Cache the item request data
|
||||
pageData.itemRequest = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"item-request",
|
||||
async () => {
|
||||
// Read the item request data from a file
|
||||
return await LocalFile.read(`${CodeExamplesPath}/Model/ItemRequest.md`);
|
||||
},
|
||||
);
|
||||
|
||||
// Cache the item response data
|
||||
pageData.itemResponse = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"item-response",
|
||||
async () => {
|
||||
// Read the item response data from a file
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/ItemResponse.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Cache the count request data
|
||||
pageData.countRequest = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"count-request",
|
||||
async () => {
|
||||
// Read the count request data from a file
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/CountRequest.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Cache the count response data
|
||||
pageData.countResponse = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"count-response",
|
||||
async () => {
|
||||
// Read the CountResponse.md file
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/CountResponse.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.updateRequest = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"update-request",
|
||||
async () => {
|
||||
// Read the UpdateRequest.md file
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/UpdateRequest.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.updateResponse = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"update-response",
|
||||
async () => {
|
||||
// Read the UpdateResponse.md file
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/UpdateResponse.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.createRequest = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"create-request",
|
||||
async () => {
|
||||
// Read the CreateRequest.md file
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/CreateRequest.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.createResponse = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"create-response",
|
||||
async () => {
|
||||
// Read the CreateResponse.md file
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/CreateResponse.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.deleteRequest = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"delete-request",
|
||||
async () => {
|
||||
// Read the DeleteRequest.md file
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/DeleteRequest.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.deleteResponse = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"delete-response",
|
||||
async () => {
|
||||
// Read the DeleteResponse.md file
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/DeleteResponse.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Get list response from cache or set it if it's not available
|
||||
pageData.listResponse = await LocalCache.getOrSetString(
|
||||
"model",
|
||||
"list-response",
|
||||
async () => {
|
||||
// Read the list response from a file
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Model/ListResponse.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Generate a unique ID for the example object
|
||||
pageData.exampleObjectID = ObjectID.generate();
|
||||
|
||||
// Construct the API path for the current resource
|
||||
pageData.apiPath =
|
||||
AppApiRoute.toString() + currentResource.model.crudApiPath?.toString();
|
||||
|
||||
// Check if the current resource is a master admin API
|
||||
pageData.isMasterAdminApiDocs = currentResource.model.isMasterAdminApiDocs;
|
||||
|
||||
// Render the index page with the required data
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
import { ViewsPath } from '../Utils/Config';
|
||||
import ResourceUtil, { ModelDocumentation } from '../Utils/Resources';
|
||||
import { ExpressRequest, ExpressResponse } from 'CommonServer/Utils/Express';
|
||||
import { ViewsPath } from "../Utils/Config";
|
||||
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
|
||||
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
|
||||
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources(); // Get an array of model documentation resources
|
||||
|
||||
export default class ServiceHandler {
|
||||
public static async executeResponse(
|
||||
_req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
res.status(404);
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: '404',
|
||||
pageTitle: 'Page Not Found',
|
||||
pageDescription: "Page you're looking for is not found.",
|
||||
resources: Resources,
|
||||
pageData: {},
|
||||
});
|
||||
}
|
||||
// This is a static method that handles the response
|
||||
public static async executeResponse(
|
||||
_req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
// Set the HTTP status code to 404 (Not Found)
|
||||
res.status(404);
|
||||
|
||||
// Render the 'index' page with the given data
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: "404", // The page type (404 in this case)
|
||||
pageTitle: "Page Not Found", // The page title
|
||||
pageDescription: "Page you're looking for is not found.", // The page description
|
||||
resources: Resources, // The array of model documentation resources
|
||||
pageData: {}, // An empty object to hold any additional page data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,55 @@
|
||||
import { CodeExamplesPath, ViewsPath } from '../Utils/Config';
|
||||
import ResourceUtil, { ModelDocumentation } from '../Utils/Resources';
|
||||
import LocalCache from 'CommonServer/Infrastructure/LocalCache';
|
||||
import { ExpressRequest, ExpressResponse } from 'CommonServer/Utils/Express';
|
||||
import LocalFile from 'CommonServer/Utils/LocalFile';
|
||||
import { CodeExamplesPath, ViewsPath } from "../Utils/Config";
|
||||
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
|
||||
import LocalCache from "Common/Server/Infrastructure/LocalCache";
|
||||
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
|
||||
import LocalFile from "Common/Server/Utils/LocalFile";
|
||||
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources(); // Get all resources from ResourceUtil
|
||||
|
||||
export default class ServiceHandler {
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
let pageTitle: string = '';
|
||||
let pageDescription: string = '';
|
||||
const page: string | undefined = req.params['page'];
|
||||
const pageData: any = {};
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
let pageTitle: string = ""; // Initialize page title
|
||||
let pageDescription: string = ""; // Initialize page description
|
||||
const page: string | undefined = req.params["page"]; // Get the page parameter from the request
|
||||
const pageData: any = {}; // Initialize page data object
|
||||
|
||||
pageTitle = 'Pagination';
|
||||
pageDescription = 'Learn how to paginate requests with OneUptime API';
|
||||
// Set page title and description
|
||||
pageTitle = "Pagination";
|
||||
pageDescription = "Learn how to paginate requests with OneUptime API";
|
||||
|
||||
pageData.responseCode = await LocalCache.getOrSetString(
|
||||
'pagination',
|
||||
'response',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Pagination/Response.md`
|
||||
);
|
||||
}
|
||||
// Get response and request code from LocalCache or LocalFile
|
||||
pageData.responseCode = await LocalCache.getOrSetString(
|
||||
"pagination",
|
||||
"response",
|
||||
async () => {
|
||||
// Read Response.md file from CodeExamplesPath
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Pagination/Response.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
pageData.requestCode = await LocalCache.getOrSetString(
|
||||
'pagination',
|
||||
'request',
|
||||
async () => {
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Pagination/Request.md`
|
||||
);
|
||||
}
|
||||
pageData.requestCode = await LocalCache.getOrSetString(
|
||||
"pagination",
|
||||
"request",
|
||||
async () => {
|
||||
// Read Request.md file from CodeExamplesPath
|
||||
return await LocalFile.read(
|
||||
`${CodeExamplesPath}/Pagination/Request.md`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
// Render the page with the page data
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page, // Pass the page parameter
|
||||
resources: Resources, // Pass all resources
|
||||
pageTitle: pageTitle, // Pass the page title
|
||||
pageDescription: pageDescription, // Pass the page description
|
||||
pageData: pageData, // Pass the page data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,41 @@
|
||||
import { ViewsPath } from '../Utils/Config';
|
||||
import ResourceUtil, { ModelDocumentation } from '../Utils/Resources';
|
||||
import { PermissionHelper, PermissionProps } from 'Common/Types/Permission';
|
||||
import { ExpressRequest, ExpressResponse } from 'CommonServer/Utils/Express';
|
||||
import { ViewsPath } from "../Utils/Config";
|
||||
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
|
||||
import { PermissionHelper, PermissionProps } from "Common/Types/Permission";
|
||||
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
|
||||
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
|
||||
|
||||
export default class ServiceHandler {
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
let pageTitle: string = '';
|
||||
let pageDescription: string = '';
|
||||
const page: string | undefined = req.params['page'];
|
||||
const pageData: any = {};
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
// Initialize page title and description
|
||||
let pageTitle: string = "";
|
||||
let pageDescription: string = "";
|
||||
|
||||
pageTitle = 'Permissions';
|
||||
pageDescription = 'Learn how permissions work with OneUptime';
|
||||
// Get the requested page
|
||||
const page: string | undefined = req.params["page"];
|
||||
const pageData: any = {};
|
||||
|
||||
pageData.permissions = PermissionHelper.getAllPermissionProps().filter(
|
||||
(i: PermissionProps) => {
|
||||
return i.isAssignableToTenant;
|
||||
}
|
||||
);
|
||||
// Set page title and description
|
||||
pageTitle = "Permissions";
|
||||
pageDescription = "Learn how permissions work with OneUptime";
|
||||
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
// Filter permissions to only include those assignable to tenants
|
||||
pageData.permissions = PermissionHelper.getAllPermissionProps().filter(
|
||||
(i: PermissionProps) => {
|
||||
return i.isAssignableToTenant;
|
||||
},
|
||||
);
|
||||
|
||||
// Render the page
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import { ViewsPath } from '../Utils/Config';
|
||||
import ResourceUtil, { ModelDocumentation } from '../Utils/Resources';
|
||||
import { ExpressRequest, ExpressResponse } from 'CommonServer/Utils/Express';
|
||||
import { ViewsPath } from "../Utils/Config";
|
||||
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
|
||||
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
|
||||
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
|
||||
const resources: Array<ModelDocumentation> = ResourceUtil.getResources(); // Get resources from ResourceUtil
|
||||
|
||||
export default class ServiceHandler {
|
||||
public static async executeResponse(
|
||||
_req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
res.status(200);
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: 'status',
|
||||
pageTitle: 'Status',
|
||||
pageDescription: '200 - Success',
|
||||
resources: Resources,
|
||||
pageData: {},
|
||||
});
|
||||
}
|
||||
public static async executeResponse(
|
||||
_req: ExpressRequest, // Ignore request object
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
// Set HTTP status to 200
|
||||
res.status(200);
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
// Render index page with necessary data
|
||||
page: "status",
|
||||
pageTitle: "Status",
|
||||
pageDescription: "200 - Success",
|
||||
resources: resources, // Pass resources to the template
|
||||
pageData: {}, // Pass empty data to the template
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const ViewsPath: string = '/usr/src/app/FeatureSet/ApiReference/views';
|
||||
export const StaticPath: string = '/usr/src/app/FeatureSet/ApiReference/Static';
|
||||
export const ViewsPath: string = "/usr/src/app/FeatureSet/ApiReference/views";
|
||||
export const StaticPath: string = "/usr/src/app/FeatureSet/ApiReference/Static";
|
||||
export const CodeExamplesPath: string =
|
||||
'/usr/src/app/FeatureSet/ApiReference/CodeExamples';
|
||||
"/usr/src/app/FeatureSet/ApiReference/CodeExamples";
|
||||
|
||||
@@ -1,74 +1,77 @@
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import ArrayUtil from 'Common/Types/ArrayUtil';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import { IsBillingEnabled } from 'CommonServer/EnvironmentConfig';
|
||||
import Models from 'Model/Models/Index';
|
||||
import BaseModel from "Common/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
||||
import ArrayUtil from "Common/Types/ArrayUtil";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import { IsBillingEnabled } from "Common/Server/EnvironmentConfig";
|
||||
import Models from "Common/Models/DatabaseModels/Index";
|
||||
|
||||
export interface ModelDocumentation {
|
||||
name: string;
|
||||
path: string;
|
||||
model: BaseModel;
|
||||
description: string;
|
||||
name: string;
|
||||
path: string;
|
||||
model: BaseModel;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export default class ResourceUtil {
|
||||
public static getResources(): Array<ModelDocumentation> {
|
||||
const resources: Array<ModelDocumentation> = Models.filter(
|
||||
(model: typeof BaseModel) => {
|
||||
const modelInstance: BaseModel = new model();
|
||||
let showDocs: boolean = modelInstance.enableDocumentation;
|
||||
// Get all resources that should have documentation enabled
|
||||
public static getResources(): Array<ModelDocumentation> {
|
||||
const resources: Array<ModelDocumentation> = Models.filter(
|
||||
(model: typeof BaseModel) => {
|
||||
const modelInstance: BaseModel = new model();
|
||||
let showDocs: boolean = modelInstance.enableDocumentation;
|
||||
|
||||
if (modelInstance.isMasterAdminApiDocs && IsBillingEnabled) {
|
||||
showDocs = false;
|
||||
}
|
||||
|
||||
return showDocs;
|
||||
}
|
||||
)
|
||||
.map((model: typeof BaseModel) => {
|
||||
const modelInstance: BaseModel = new model();
|
||||
|
||||
return {
|
||||
name: modelInstance.singularName!,
|
||||
path: modelInstance.getAPIDocumentationPath(),
|
||||
model: modelInstance,
|
||||
description: modelInstance.tableDescription!,
|
||||
};
|
||||
})
|
||||
.sort(ArrayUtil.sortByFieldName('name'));
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
public static getFeaturedResources(): Array<ModelDocumentation> {
|
||||
const featuredResources: Array<string> = [
|
||||
'Monitor',
|
||||
'Scheduled Maintenance Event',
|
||||
'Status Page',
|
||||
'Incident',
|
||||
'Team',
|
||||
'On-Call Duty',
|
||||
'Label',
|
||||
'Team Member',
|
||||
];
|
||||
|
||||
return ResourceUtil.getResources().filter(
|
||||
(resource: ModelDocumentation) => {
|
||||
return featuredResources.includes(resource.name);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static getResourceDictionaryByPath(): Dictionary<ModelDocumentation> {
|
||||
const dict: Dictionary<ModelDocumentation> = {};
|
||||
|
||||
const resources: Array<ModelDocumentation> =
|
||||
ResourceUtil.getResources();
|
||||
|
||||
for (const resource of resources) {
|
||||
dict[resource.path] = resource;
|
||||
// If billing is enabled, do not show master admin API docs
|
||||
if (modelInstance.isMasterAdminApiDocs && IsBillingEnabled) {
|
||||
showDocs = false;
|
||||
}
|
||||
|
||||
return dict;
|
||||
return showDocs;
|
||||
},
|
||||
)
|
||||
.map((model: typeof BaseModel) => {
|
||||
const modelInstance: BaseModel = new model();
|
||||
|
||||
return {
|
||||
name: modelInstance.singularName!,
|
||||
path: modelInstance.getAPIDocumentationPath(),
|
||||
model: modelInstance,
|
||||
description: modelInstance.tableDescription!,
|
||||
};
|
||||
})
|
||||
.sort(ArrayUtil.sortByFieldName("name"));
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
// Get featured resources that are pre-selected
|
||||
public static getFeaturedResources(): Array<ModelDocumentation> {
|
||||
const featuredResources: Array<string> = [
|
||||
"Monitor",
|
||||
"Scheduled Maintenance Event",
|
||||
"Status Page",
|
||||
"Incident",
|
||||
"Team",
|
||||
"On-Call Duty",
|
||||
"Label",
|
||||
"Team Member",
|
||||
];
|
||||
|
||||
return ResourceUtil.getResources().filter(
|
||||
(resource: ModelDocumentation) => {
|
||||
return featuredResources.includes(resource.name);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Create a dictionary of resources indexed by their path
|
||||
public static getResourceDictionaryByPath(): Dictionary<ModelDocumentation> {
|
||||
const dict: Dictionary<ModelDocumentation> = {};
|
||||
|
||||
const resources: Array<ModelDocumentation> = ResourceUtil.getResources();
|
||||
|
||||
for (const resource of resources) {
|
||||
dict[resource.path] = resource;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user