mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 08:42:13 +02:00
Compare commits
782 Commits
7.0.1791
...
filter-ref
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aea1ec24ef | ||
|
|
7c250a25e3 | ||
|
|
e7799adc1c | ||
|
|
2c949150ef | ||
|
|
983892d8b3 | ||
|
|
054234116b | ||
|
|
d6aafa8493 | ||
|
|
b5407b47b2 | ||
|
|
f84bec7e66 | ||
|
|
43354d850b | ||
|
|
1977dc3e62 | ||
|
|
c2bd27b0f9 | ||
|
|
264c0f0347 | ||
|
|
0de98ae262 | ||
|
|
573a12075b | ||
|
|
0a85e245d7 | ||
|
|
41d01cceee | ||
|
|
dcd9aeccb3 | ||
|
|
c59ebc2373 | ||
|
|
643f4acddd | ||
|
|
b3fb21af59 | ||
|
|
2164f45a68 | ||
|
|
8a4afe992c | ||
|
|
f35e50bab3 | ||
|
|
63064c587c | ||
|
|
edd11ffade | ||
|
|
b7e97a29ee | ||
|
|
fdf6e3dfa3 | ||
|
|
b59b8257cc | ||
|
|
9bde353e93 | ||
|
|
a2c2867469 | ||
|
|
bd3ba94da8 | ||
|
|
8e6800c17b | ||
|
|
e481e043dd | ||
|
|
a4e6552c91 | ||
|
|
6ce5cde46e | ||
|
|
22014c81c4 | ||
|
|
bff63cdf58 | ||
|
|
5f9d57a099 | ||
|
|
ca100f9de9 | ||
|
|
68dbb010aa | ||
|
|
5cd213a750 | ||
|
|
26683914bc | ||
|
|
8a5adfd589 | ||
|
|
d2eefeabba | ||
|
|
1148b59416 | ||
|
|
848c0c8100 | ||
|
|
21f40961cf | ||
|
|
2f74fbe0a8 | ||
|
|
571784a523 | ||
|
|
7c60fe8009 | ||
|
|
1a2fe1d16d | ||
|
|
a28a041c8d | ||
|
|
cfcce00060 | ||
|
|
da0fa045e6 | ||
|
|
859f62bf93 | ||
|
|
6a1cc1bd8b | ||
|
|
5b9abe8aea | ||
|
|
d0ec483a0f | ||
|
|
c03a3d6f56 | ||
|
|
8761dbd75b | ||
|
|
5541922b25 | ||
|
|
8693ab065e | ||
|
|
fe4daa7937 | ||
|
|
20d7f2f8b4 | ||
|
|
7f94f3d4d4 | ||
|
|
bc46370f7c | ||
|
|
b3346a9702 | ||
|
|
f793f7dd16 | ||
|
|
766f1f6178 | ||
|
|
9f76748037 | ||
|
|
469e06280a | ||
|
|
aede9af03d | ||
|
|
0bc7cf345f | ||
|
|
95c7b10ce0 | ||
|
|
daceab164c | ||
|
|
2d28fbaf85 | ||
|
|
8d2cbe49ad | ||
|
|
86f262583c | ||
|
|
4e748d1626 | ||
|
|
1dbea8e636 | ||
|
|
3991acf5fc | ||
|
|
1285830a9b | ||
|
|
96d3243205 | ||
|
|
08cbdb15d0 | ||
|
|
f59284e4d6 | ||
|
|
0c5d0e81a5 | ||
|
|
82829a5b97 | ||
|
|
d3c188a804 | ||
|
|
85ffc36fad | ||
|
|
8d34e9ccbe | ||
|
|
c6643d7f7c | ||
|
|
1433823efe | ||
|
|
bd5902ed6d | ||
|
|
b64666f0ff | ||
|
|
9706cced86 | ||
|
|
49cb3024ef | ||
|
|
2fec007c5f | ||
|
|
0f40f5f730 | ||
|
|
120ca78760 | ||
|
|
d50fdf3464 | ||
|
|
34ddb8efdb | ||
|
|
3724cce4f0 | ||
|
|
d864992bdb | ||
|
|
3515752a95 | ||
|
|
53d66db8cb | ||
|
|
548250f837 | ||
|
|
b3b343d4aa | ||
|
|
69a8f438f7 | ||
|
|
7b571f15a9 | ||
|
|
d783eaea9d | ||
|
|
2e5aafe151 | ||
|
|
dea938bcbb | ||
|
|
7c16b14ab4 | ||
|
|
e22ff26dc0 | ||
|
|
75bd6ca6ed | ||
|
|
f0bdb76999 | ||
|
|
a9e9d14f69 | ||
|
|
21daac0400 | ||
|
|
3cdf71906b | ||
|
|
0714161460 | ||
|
|
78b7252743 | ||
|
|
2ba406f802 | ||
|
|
48ebfc17dd | ||
|
|
9d180a2dcb | ||
|
|
0f2a970ede | ||
|
|
4ab3c99fe4 | ||
|
|
ae2e452d27 | ||
|
|
825d733f92 | ||
|
|
27039bc646 | ||
|
|
1bb1d80b09 | ||
|
|
983f587ce4 | ||
|
|
e1f9325512 | ||
|
|
96c27c86ca | ||
|
|
a172596e46 | ||
|
|
3d30e9356a | ||
|
|
7c04ecec35 | ||
|
|
80511a5f8d | ||
|
|
6692fb00b8 | ||
|
|
83e207a7de | ||
|
|
d62aa7f5a6 | ||
|
|
8a130f96e2 | ||
|
|
66a470ebe1 | ||
|
|
30a59aa21e | ||
|
|
7a6a5c141b | ||
|
|
c4bf458074 | ||
|
|
ae873e296c | ||
|
|
4a973d7107 | ||
|
|
9bbf460b97 | ||
|
|
c348a8bd05 | ||
|
|
76d8f5d5ef | ||
|
|
72ac305aad | ||
|
|
1ac05450e7 | ||
|
|
67165375ac | ||
|
|
6325e36bd0 | ||
|
|
e6375087cd | ||
|
|
737e234ec0 | ||
|
|
1427d52c9f | ||
|
|
8377285a27 | ||
|
|
7414c6563d | ||
|
|
0669e96a5d | ||
|
|
54a6101315 | ||
|
|
eaaef7280f | ||
|
|
b368633ff9 | ||
|
|
37b25c3a8f | ||
|
|
191d0154b4 | ||
|
|
1f786699d2 | ||
|
|
00b5453b3f | ||
|
|
077ca1037b | ||
|
|
1be827741e | ||
|
|
d51e818d10 | ||
|
|
d312c2b0cf | ||
|
|
96a09353fb | ||
|
|
fe40c5bb8d | ||
|
|
7cbbce4067 | ||
|
|
2870d28354 | ||
|
|
899bc1d205 | ||
|
|
308dba7f24 | ||
|
|
a0e565485b | ||
|
|
35c21d7611 | ||
|
|
a0ef85b8cd | ||
|
|
818f8cc421 | ||
|
|
eaeb40ec49 | ||
|
|
74273e6e9b | ||
|
|
c092adfa3d | ||
|
|
9eec9042bd | ||
|
|
496c2ca32c | ||
|
|
499ab07cd3 | ||
|
|
c634526215 | ||
|
|
cda9921f5d | ||
|
|
d3ca5a97ea | ||
|
|
f22ae5844d | ||
|
|
a71190493a | ||
|
|
caf3377cee | ||
|
|
f0e4d6bf71 | ||
|
|
f34e4a23d6 | ||
|
|
e03ecb0e0d | ||
|
|
0a8574678c | ||
|
|
0fdf63ce0c | ||
|
|
d1586f75da | ||
|
|
f47b53c5b9 | ||
|
|
7523c3ad4c | ||
|
|
11d580a373 | ||
|
|
9a78c4c24d | ||
|
|
ec936b43e2 | ||
|
|
3bc2e639d9 | ||
|
|
fa672be557 | ||
|
|
695f16ca1e | ||
|
|
003b638170 | ||
|
|
1cf208387f | ||
|
|
625cb49939 | ||
|
|
7d44eb1a33 | ||
|
|
ab42ca1ef9 | ||
|
|
910aa113b5 | ||
|
|
1516f81180 | ||
|
|
b9f86d029a | ||
|
|
ecfc47ee59 | ||
|
|
5d9a11fb48 | ||
|
|
894d15918b | ||
|
|
73e6a20503 | ||
|
|
ef0dc5305a | ||
|
|
73dc057c99 | ||
|
|
092564434f | ||
|
|
7623fe7df4 | ||
|
|
da03636bd0 | ||
|
|
21b119d8a0 | ||
|
|
4ee62fcca4 | ||
|
|
8acfb0f798 | ||
|
|
6e2d343264 | ||
|
|
3dd03cda46 | ||
|
|
87c8993141 | ||
|
|
323601db3a | ||
|
|
1ac7d4920f | ||
|
|
617a436eb5 | ||
|
|
7e6af77d70 | ||
|
|
59a6771e73 | ||
|
|
b88320bcbb | ||
|
|
06bafdfce6 | ||
|
|
441aef4823 | ||
|
|
0f86812258 | ||
|
|
f601e93101 | ||
|
|
bf8d4757b1 | ||
|
|
4b69717687 | ||
|
|
0647f7d22a | ||
|
|
ae6852d5eb | ||
|
|
d4902784c2 | ||
|
|
e2644586b6 | ||
|
|
cc80f6fa54 | ||
|
|
32c78f24e9 | ||
|
|
8b0f23e18f | ||
|
|
b76d16262c | ||
|
|
4c60f080f0 | ||
|
|
5f40c393b1 | ||
|
|
7b22d293f2 | ||
|
|
7f6c247652 | ||
|
|
267950bb54 | ||
|
|
d4b6bf6ca0 | ||
|
|
9d0c110a74 | ||
|
|
9d3c4df5e7 | ||
|
|
ca89818cc1 | ||
|
|
6b2caebd0d | ||
|
|
f716ece35f | ||
|
|
6dcc72e708 | ||
|
|
47a718ad2a | ||
|
|
e4e3d5d362 | ||
|
|
73b4987b32 | ||
|
|
7574d3921a | ||
|
|
c6b47c3a1b | ||
|
|
a5fec2560a | ||
|
|
b139d667d2 | ||
|
|
906a42c218 | ||
|
|
529b22c97c | ||
|
|
89f3508ce0 | ||
|
|
a9cc7f41ba | ||
|
|
cc517d36dc | ||
|
|
8363279050 | ||
|
|
7c0f9c307a | ||
|
|
3a7c9f7fab | ||
|
|
ba993ba09a | ||
|
|
fca1f80243 | ||
|
|
96926ffa63 | ||
|
|
48b850d350 | ||
|
|
ab6a01e59b | ||
|
|
cd5eb62f61 | ||
|
|
d04aa43b6d | ||
|
|
308fe269fc | ||
|
|
0f92e37712 | ||
|
|
7f41cebdf7 | ||
|
|
351734d661 | ||
|
|
961de94f81 | ||
|
|
2a89d57560 | ||
|
|
039e0b17a4 | ||
|
|
77802eec58 | ||
|
|
5db4cc8d21 | ||
|
|
f44260ee41 | ||
|
|
895bacfc11 | ||
|
|
de6363574c | ||
|
|
2a07d62146 | ||
|
|
3138415bd0 | ||
|
|
527ba63c94 | ||
|
|
6910648daf | ||
|
|
9fb68afe79 | ||
|
|
350f808b44 | ||
|
|
6438953c91 | ||
|
|
7f98e469cc | ||
|
|
d49ba093f0 | ||
|
|
1ebcbeeba2 | ||
|
|
c4eb6cd44a | ||
|
|
48a58951e8 | ||
|
|
414f8c7208 | ||
|
|
cb1d640c56 | ||
|
|
8a271608e8 | ||
|
|
dd3627dca8 | ||
|
|
fe6c917862 | ||
|
|
0dfa3f912e | ||
|
|
d8bfab3ae0 | ||
|
|
d7a9adf791 | ||
|
|
fb070b9448 | ||
|
|
add313980c | ||
|
|
29a909d547 | ||
|
|
13b10ca874 | ||
|
|
d0738ad64d | ||
|
|
a21b9ca555 | ||
|
|
f0e2910bd5 | ||
|
|
8f84027813 | ||
|
|
970b6fbb92 | ||
|
|
c8db6f237f | ||
|
|
01477fd102 | ||
|
|
0bd173dd93 | ||
|
|
87b7bbad16 | ||
|
|
2524684b1f | ||
|
|
6cc00a1e5b | ||
|
|
c2285848cd | ||
|
|
506a89aeb3 | ||
|
|
7c0221e6b8 | ||
|
|
e9d8c5b2bc | ||
|
|
8e5fc32f4d | ||
|
|
2eaeec326f | ||
|
|
c5823308af | ||
|
|
f0e255eb24 | ||
|
|
bccd2fd5d2 | ||
|
|
534c798028 | ||
|
|
2a48b585a4 | ||
|
|
8de699cb94 | ||
|
|
03ffeba683 | ||
|
|
153d55b774 | ||
|
|
1b95dfa5ed | ||
|
|
d9451af991 | ||
|
|
da375dce63 | ||
|
|
d12e2f7622 | ||
|
|
d0e9120559 | ||
|
|
d35ae2f075 | ||
|
|
92076dad8b | ||
|
|
a1ded23b46 | ||
|
|
4d618e6d93 | ||
|
|
d3e461e0bd | ||
|
|
7a94f4fcbc | ||
|
|
2758b5273b | ||
|
|
79d18ab920 | ||
|
|
13dd7c2db5 | ||
|
|
2486cd93c0 | ||
|
|
980c82ce31 | ||
|
|
1f6a4dbf40 | ||
|
|
b14f518461 | ||
|
|
70d243aa7b | ||
|
|
3f3996b155 | ||
|
|
5b3915fc65 | ||
|
|
53d33eb0fb | ||
|
|
c2722cb66a | ||
|
|
2ccc7a8061 | ||
|
|
97244b5c2c | ||
|
|
90a0988e06 | ||
|
|
a3778b8ce6 | ||
|
|
79bac7fd3f | ||
|
|
112863a52b | ||
|
|
c1150c06e0 | ||
|
|
fcfb3f7e50 | ||
|
|
7c6c5ccac1 | ||
|
|
03ad9bf349 | ||
|
|
f95ab83ae2 | ||
|
|
a25951f330 | ||
|
|
5b0f450802 | ||
|
|
be2b32822b | ||
|
|
6ebc601121 | ||
|
|
14d049bd0f | ||
|
|
47b5f44796 | ||
|
|
ec8bb3d67b | ||
|
|
71abac2c3f | ||
|
|
21a227d67a | ||
|
|
df0783e4d4 | ||
|
|
a9a14b1253 | ||
|
|
dc13aacb13 | ||
|
|
7f7d4c7388 | ||
|
|
f6642d5582 | ||
|
|
e85f216b9e | ||
|
|
04289df987 | ||
|
|
5e0bb766d6 | ||
|
|
e23c979911 | ||
|
|
cf43a3fb4e | ||
|
|
5d809015ee | ||
|
|
6b30e9aa2f | ||
|
|
29c4a43968 | ||
|
|
3a29d7b703 | ||
|
|
a4cbe21683 | ||
|
|
113a42b17f | ||
|
|
4682467bde | ||
|
|
f7c2415b63 | ||
|
|
c243409a0e | ||
|
|
b89760ab1a | ||
|
|
3acb419667 | ||
|
|
12a98bcb5f | ||
|
|
bca41d7dbc | ||
|
|
1875d97d6f | ||
|
|
b49336a224 | ||
|
|
95ac77ac4e | ||
|
|
6ef91fd1b7 | ||
|
|
e0bcfd31bf | ||
|
|
4b5cc40542 | ||
|
|
44db5b57e5 | ||
|
|
382c71be93 | ||
|
|
445a8d3f35 | ||
|
|
356bacf9a0 | ||
|
|
5a84cc1b1d | ||
|
|
2e7ea183e6 | ||
|
|
f8eaeb52a1 | ||
|
|
b23aac7b21 | ||
|
|
1ca9ecede1 | ||
|
|
39d7b76e23 | ||
|
|
bcefa8ae3a | ||
|
|
3c022e30ea | ||
|
|
1401daab06 | ||
|
|
11fd2d3c65 | ||
|
|
210420d180 | ||
|
|
d8f9e64aa4 | ||
|
|
f57596391d | ||
|
|
8282669fbd | ||
|
|
f38d8ae452 | ||
|
|
7726bad4e2 | ||
|
|
a50510819b | ||
|
|
27e9ee9721 | ||
|
|
d43765645e | ||
|
|
0c73bd8292 | ||
|
|
ad1c6152a0 | ||
|
|
9b8b1a87fe | ||
|
|
58b17bd27b | ||
|
|
e6f348ed0d | ||
|
|
dbd8ebcb35 | ||
|
|
d5c58356fc | ||
|
|
aedbda27c4 | ||
|
|
2715d544e7 | ||
|
|
41f1470685 | ||
|
|
00670b46de | ||
|
|
40ec2b54ec | ||
|
|
06303618c8 | ||
|
|
e66ac04566 | ||
|
|
65b124ca22 | ||
|
|
25ee0404ee | ||
|
|
0334117a40 | ||
|
|
5d3d19d210 | ||
|
|
ad4c29b74a | ||
|
|
74ad8fc725 | ||
|
|
fd5e7272de | ||
|
|
730d01a73f | ||
|
|
7f23c0e5a9 | ||
|
|
e2671832e8 | ||
|
|
0e7f96856c | ||
|
|
59fff01663 | ||
|
|
e8c6c3c0a7 | ||
|
|
3d00471677 | ||
|
|
692ebdb006 | ||
|
|
37ee7b25f2 | ||
|
|
2bca4963a0 | ||
|
|
efded8947f | ||
|
|
d663b0c65f | ||
|
|
9f354383f7 | ||
|
|
73be1fa956 | ||
|
|
07b10cfdf7 | ||
|
|
630f6767ab | ||
|
|
4d1c007d68 | ||
|
|
3b85e4fe99 | ||
|
|
35daff14d9 | ||
|
|
73f3ef7622 | ||
|
|
f11f1c23c5 | ||
|
|
c52501ef0e | ||
|
|
78e9ad439c | ||
|
|
e35dba93ce | ||
|
|
1991977af1 | ||
|
|
acf35e1c4c | ||
|
|
36f290edf0 | ||
|
|
1ad1aebc1b | ||
|
|
e11b781fc5 | ||
|
|
bc1ca32991 | ||
|
|
b668281ad6 | ||
|
|
2cbabe7a67 | ||
|
|
03e1545ebd | ||
|
|
2a16389a37 | ||
|
|
32a10892a4 | ||
|
|
39f0e66b35 | ||
|
|
ed7e70aa8d | ||
|
|
d1697c37df | ||
|
|
0c2774214a | ||
|
|
9bc98e0a35 | ||
|
|
20dff66769 | ||
|
|
6ac98d2531 | ||
|
|
586cbdb424 | ||
|
|
689de266a1 | ||
|
|
0015906bac | ||
|
|
5070d8137a | ||
|
|
d4d8848124 | ||
|
|
abcde37960 | ||
|
|
1fbdc19645 | ||
|
|
c17740b315 | ||
|
|
1568f5ef94 | ||
|
|
c4d7f345e2 | ||
|
|
ec0c1b102f | ||
|
|
72559f146d | ||
|
|
37829e3ef6 | ||
|
|
389397d31b | ||
|
|
6d68154855 | ||
|
|
9d50cbb889 | ||
|
|
b6932d07ac | ||
|
|
d8b1ed9516 | ||
|
|
8a0b94a18f | ||
|
|
a03524496d | ||
|
|
ebc9835374 | ||
|
|
9bfcc4f0c8 | ||
|
|
7ed0d55479 | ||
|
|
74b57c6d83 | ||
|
|
34697dd8ca | ||
|
|
c390a88866 | ||
|
|
8a5984faad | ||
|
|
80f2e12a1f | ||
|
|
5f3d208557 | ||
|
|
b2492db82a | ||
|
|
6a6fa1876d | ||
|
|
57db166e8a | ||
|
|
afdc891942 | ||
|
|
37a13ce853 | ||
|
|
8ebcb1b659 | ||
|
|
0e2e440a90 | ||
|
|
fa06d208dc | ||
|
|
69cac18ca7 | ||
|
|
40ba8bd2de | ||
|
|
a89e7db46e | ||
|
|
1f8c0e6b71 | ||
|
|
d547660284 | ||
|
|
d99f5310e7 | ||
|
|
fcc9231d1e | ||
|
|
68ea2c4e9a | ||
|
|
dfaa31ba41 | ||
|
|
1e1fe04eb0 | ||
|
|
6612e44ae5 | ||
|
|
206f0199e9 | ||
|
|
90ceb9cfc6 | ||
|
|
f503a45f3b | ||
|
|
375afe288a | ||
|
|
0e0bd71ead | ||
|
|
e3017f1680 | ||
|
|
f5c9ba7e3d | ||
|
|
ab92f256a5 | ||
|
|
af1f3a3bae | ||
|
|
a30392730e | ||
|
|
eb6c3888ad | ||
|
|
e22d39eea2 | ||
|
|
1d93fdf73b | ||
|
|
641c8de84b | ||
|
|
2fb509b98c | ||
|
|
39c7f6f2ba | ||
|
|
3f5f16cb74 | ||
|
|
038684e512 | ||
|
|
28f4f0f37c | ||
|
|
e793456980 | ||
|
|
bbff753799 | ||
|
|
5246f9f9cb | ||
|
|
fb35f23971 | ||
|
|
30db649f40 | ||
|
|
5088a14665 | ||
|
|
4f2302aa3c | ||
|
|
559c124294 | ||
|
|
b285caeea4 | ||
|
|
ed3e0e05c4 | ||
|
|
f61e44f1d7 | ||
|
|
c6e47ddc83 | ||
|
|
bc72200378 | ||
|
|
1b61d7a641 | ||
|
|
1ac7c646d5 | ||
|
|
fedb247972 | ||
|
|
4da6d9f492 | ||
|
|
899649d55e | ||
|
|
fac6e23b7e | ||
|
|
f7739ab4a8 | ||
|
|
f7c33cb799 | ||
|
|
466ebc8c41 | ||
|
|
676e2c654d | ||
|
|
ac0bafe560 | ||
|
|
5d90c2992f | ||
|
|
8e8019a9f8 | ||
|
|
267d9ccd0d | ||
|
|
8dabddd5ed | ||
|
|
c052cadc98 | ||
|
|
c893990722 | ||
|
|
1fbc80db8e | ||
|
|
1c3e184566 | ||
|
|
4335377285 | ||
|
|
4c27494b7a | ||
|
|
95f487c3d4 | ||
|
|
830393c74f | ||
|
|
393336ad60 | ||
|
|
8c8b323a14 | ||
|
|
85551a5608 | ||
|
|
ec5b1fc88d | ||
|
|
bab7f6331e | ||
|
|
592ff4efb7 | ||
|
|
3e0c5e8846 | ||
|
|
c9d5d92934 | ||
|
|
15739f99b5 | ||
|
|
8513d969db | ||
|
|
0def943102 | ||
|
|
da8fb1e032 | ||
|
|
48087e1ce4 | ||
|
|
56187ed256 | ||
|
|
80ff772e48 | ||
|
|
e82fffdc55 | ||
|
|
547beaf890 | ||
|
|
e2da673f53 | ||
|
|
32473b0d51 | ||
|
|
ed404dfdd1 | ||
|
|
57b99e2e99 | ||
|
|
34d7c9b152 | ||
|
|
87ad6dd73b | ||
|
|
c188d23e0c | ||
|
|
d7ecd18975 | ||
|
|
67d241bb5e | ||
|
|
a41bbb301a | ||
|
|
672f2ecd70 | ||
|
|
8428677714 | ||
|
|
efa0990250 | ||
|
|
c03d250a62 | ||
|
|
0c9cedafa1 | ||
|
|
39f0834cc2 | ||
|
|
151b8c2262 | ||
|
|
cdb9c0ee5a | ||
|
|
48427a341c | ||
|
|
7477aeca94 | ||
|
|
04415198fc | ||
|
|
d9cfd750a4 | ||
|
|
57e3b7ba9b | ||
|
|
95fc43f3a5 | ||
|
|
90689bfb1a | ||
|
|
c484a8484c | ||
|
|
0dbbbfda61 | ||
|
|
4278b1014c | ||
|
|
3954e7600c | ||
|
|
ea04be214f | ||
|
|
4bcd12b659 | ||
|
|
13de53166a | ||
|
|
b01e4ecbd7 | ||
|
|
61d6ca9a20 | ||
|
|
08c9fc9311 | ||
|
|
ac46ba74d0 | ||
|
|
a382a6f207 | ||
|
|
9dfb9f0dc6 | ||
|
|
7b98ccb590 | ||
|
|
0803f44407 | ||
|
|
36182cbe95 | ||
|
|
6e894d7b79 | ||
|
|
4bb0c36050 | ||
|
|
044347296c | ||
|
|
59ef638ad9 | ||
|
|
8a9902941d | ||
|
|
dd6a36df65 | ||
|
|
6c238e0e6b | ||
|
|
a61c496a78 | ||
|
|
8b2a819bd5 | ||
|
|
e3a28bf0e7 | ||
|
|
c149991884 | ||
|
|
544bb37581 | ||
|
|
5c1b8d420d | ||
|
|
d76eebceb0 | ||
|
|
993a01659b | ||
|
|
8e08dea7ec | ||
|
|
82e5977e15 | ||
|
|
53481a58eb | ||
|
|
faa225d275 | ||
|
|
263d659761 | ||
|
|
7f0535459e | ||
|
|
4901749ae6 | ||
|
|
bab36f9b86 | ||
|
|
2757bc01c7 | ||
|
|
d6affe3949 | ||
|
|
3304191b29 | ||
|
|
6a6b60afe8 | ||
|
|
400ebf994d | ||
|
|
328a5d49f4 | ||
|
|
52acb22acc | ||
|
|
08339a5247 | ||
|
|
f9bf1e45be | ||
|
|
ba59f77563 | ||
|
|
316fefbf53 | ||
|
|
ab9619ca40 | ||
|
|
735a476097 | ||
|
|
7538367b0a | ||
|
|
b73a11992c | ||
|
|
94412fb515 | ||
|
|
a3916be807 | ||
|
|
3576fdfbb5 | ||
|
|
14016d23d8 | ||
|
|
1056123d31 | ||
|
|
96a5f75731 | ||
|
|
18e8b7cb00 | ||
|
|
e64c9f6e4c | ||
|
|
5976fc22f5 | ||
|
|
750fe2867c | ||
|
|
5c89ed97e6 | ||
|
|
27090a346c | ||
|
|
d23aff2e73 | ||
|
|
f1852a95d0 | ||
|
|
6eb3b1f80d | ||
|
|
8dc65283d8 | ||
|
|
324572d37d | ||
|
|
aa764cdb14 | ||
|
|
0ca18bebd5 | ||
|
|
f742fcb3c4 | ||
|
|
0839cbe297 | ||
|
|
0914ae6bda | ||
|
|
e7dd5adb2a | ||
|
|
b85ec355f5 | ||
|
|
ec0dafb7a6 | ||
|
|
e8316078c8 | ||
|
|
0b1069f9a8 | ||
|
|
bd3959e844 | ||
|
|
bf8561a166 | ||
|
|
e92ee600c9 | ||
|
|
31fc399a5e | ||
|
|
1acfedeebb | ||
|
|
5895f515a0 | ||
|
|
2830293949 | ||
|
|
2d060e335d | ||
|
|
6b01d73903 | ||
|
|
10ebfda7d0 | ||
|
|
98c3581022 | ||
|
|
859aca85a7 | ||
|
|
b94402525e | ||
|
|
efbdc85ade | ||
|
|
efd8a0e79f | ||
|
|
6067babb9f | ||
|
|
4451b5343d | ||
|
|
6ebeef5397 | ||
|
|
56d2d348fb | ||
|
|
3a21e8df47 | ||
|
|
81ab8d135f | ||
|
|
f9c9b9addf | ||
|
|
b29a19c6e9 | ||
|
|
d1d1734183 | ||
|
|
decc09e3b9 | ||
|
|
1a3f8fca1a | ||
|
|
b5ff064a27 | ||
|
|
d91e944c2e | ||
|
|
98b8e89d71 | ||
|
|
b516a7a350 | ||
|
|
ebc1724093 | ||
|
|
0954336a69 | ||
|
|
8552556289 | ||
|
|
d329160fc0 | ||
|
|
d1af2e62bf | ||
|
|
a06e5035df | ||
|
|
b2a15ff460 | ||
|
|
eb53b5a81d | ||
|
|
363f7b81e8 | ||
|
|
4e55541026 | ||
|
|
deb0aaf35f | ||
|
|
fdcf8c66fa | ||
|
|
f9f9aa4433 | ||
|
|
5f50a11786 | ||
|
|
4648d5232a | ||
|
|
f282344c78 | ||
|
|
b12d6c713e | ||
|
|
6d6e7ce013 | ||
|
|
99aa76ea81 | ||
|
|
dbd2462958 | ||
|
|
1dbfea9051 | ||
|
|
9a11bfbed7 |
@@ -113,6 +113,7 @@
|
||||
"no-console": "error",
|
||||
"no-undef": "error",
|
||||
"no-empty": "error",
|
||||
"no-control-regex": "off",
|
||||
"prefer-arrow-callback": "error",
|
||||
"constructor-super": "error",
|
||||
"no-case-declarations": "error",
|
||||
|
||||
31
.github/workflows/build.yml
vendored
31
.github/workflows/build.yml
vendored
@@ -25,6 +25,21 @@ jobs:
|
||||
- name: build docker image
|
||||
run: sudo docker build -f ./Accounts/Dockerfile .
|
||||
|
||||
docker-build-isolated-vm:
|
||||
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 ./IsolatedVM/Dockerfile .
|
||||
|
||||
|
||||
docker-build-otel-collector:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -57,6 +72,22 @@ jobs:
|
||||
- name: build docker image
|
||||
run: sudo docker build -f ./App/Dockerfile .
|
||||
|
||||
docker-build-e2e:
|
||||
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 ./E2E/Dockerfile .
|
||||
|
||||
docker-build-admin-dashboard:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
|
||||
14
.github/workflows/compile.yml
vendored
14
.github/workflows/compile.yml
vendored
@@ -25,6 +25,19 @@ jobs:
|
||||
- run: cd CommonUI && npm install --force
|
||||
- run: cd Accounts && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-isolated-vm:
|
||||
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 CommonServer && npm install
|
||||
- run: cd IsolatedVM && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-common-server:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
@@ -161,6 +174,7 @@ jobs:
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd E2E && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-probe:
|
||||
|
||||
124
.github/workflows/release.yml
vendored
124
.github/workflows/release.yml
vendored
@@ -151,6 +151,126 @@ jobs:
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
e2e-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/e2e
|
||||
ghcr.io/oneuptime/e2e
|
||||
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 e2e.
|
||||
|
||||
- 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: ./E2E/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}}
|
||||
|
||||
isolated-vm-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/isolated-vm
|
||||
ghcr.io/oneuptime/isolated-vm
|
||||
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 isolated-vm.
|
||||
|
||||
- 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: ./IsolatedVM/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-server-docker-image-deploy:
|
||||
needs: generate-build-number
|
||||
runs-on: ubuntu-latest
|
||||
@@ -918,4 +1038,6 @@ jobs:
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
125
.github/workflows/test-release.yaml
vendored
125
.github/workflows/test-release.yaml
vendored
@@ -80,6 +80,68 @@ jobs:
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
e2e-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/e2e
|
||||
ghcr.io/oneuptime/e2e
|
||||
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 e2e.
|
||||
|
||||
- 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: ./E2E/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-server-docker-image-deploy:
|
||||
needs: generate-build-number
|
||||
runs-on: ubuntu-latest
|
||||
@@ -202,6 +264,67 @@ jobs:
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
isolated-vm-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/isolated-vm
|
||||
ghcr.io/oneuptime/isolated-vm
|
||||
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 isolated-vm.
|
||||
|
||||
- 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: ./IsolatedVM/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}}
|
||||
|
||||
status-page-docker-image-deploy:
|
||||
needs: generate-build-number
|
||||
runs-on: ubuntu-latest
|
||||
@@ -819,7 +942,7 @@ jobs:
|
||||
|
||||
test-helm-chart:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ infrastructure-agent-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]
|
||||
needs: [ infrastructure-agent-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:
|
||||
|
||||
62
.github/workflows/test.e2e.yaml
vendored
Normal file
62
.github/workflows/test.e2e.yaml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: E2E Tests
|
||||
|
||||
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:
|
||||
# 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
|
||||
- run: npm run dev
|
||||
- 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/playwright-report
|
||||
./E2E/test-results
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
2
.github/workflows/test.probe.yaml
vendored
2
.github/workflows/test.probe.yaml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: self-hosted # this needs to be self-hosted because ICMP checks are disbled in hosted runners
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -102,4 +102,12 @@ Examples/otel-dotnet/obj/*
|
||||
|
||||
InfrastructureAgent/sea-prep.blob
|
||||
InfrastructureAgent/InfrastructureAgent
|
||||
InfrastructureAgent/build/*
|
||||
InfrastructureAgent/build/*
|
||||
|
||||
|
||||
InfrastructureAgent/err.log
|
||||
InfrastructureAgent/out.log
|
||||
InfrastructureAgent/daemon.pid
|
||||
App/greenlock/.greenlockrc
|
||||
App/greenlock/greenlock.d/config.json
|
||||
App/greenlock/greenlock.d/config.json.bak
|
||||
|
||||
42
.vscode/launch.json
vendored
42
.vscode/launch.json
vendored
@@ -83,6 +83,20 @@
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/IsolatedVM",
|
||||
"name": "Isolated VM: Debug with Docker",
|
||||
"port": 9974,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/Workflow",
|
||||
@@ -139,34 +153,6 @@
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/Ingestor",
|
||||
"name": "Probe API: Debug with Docker",
|
||||
"port": 9251,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/Identity",
|
||||
"name": "File: Debug with Docker",
|
||||
"port": 9012,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/HttpTestServer",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:21.6-alpine3.18
|
||||
FROM node:21.7.2-alpine3.18
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ ENV GIT_SHA=${GIT_SHA}
|
||||
ENV APP_VERSION=${APP_VERSION}
|
||||
|
||||
|
||||
# IF APP_VERSION is not set, set it to 1.0.0
|
||||
RUN if [ -z "$APP_VERSION" ]; then export APP_VERSION=1.0.0; fi
|
||||
|
||||
|
||||
# Install bash.
|
||||
RUN apk add bash && apk add curl
|
||||
|
||||
@@ -25,6 +29,8 @@ RUN mkdir /usr/src
|
||||
|
||||
WORKDIR /usr/src/Common
|
||||
COPY ./Common/package*.json /usr/src/Common/
|
||||
# Set version in ./Common/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/Common/package.json
|
||||
RUN npm install
|
||||
COPY ./Common /usr/src/Common
|
||||
|
||||
@@ -32,6 +38,8 @@ 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
|
||||
|
||||
@@ -39,6 +47,8 @@ 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
|
||||
|
||||
@@ -49,6 +59,8 @@ COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -10,7 +10,17 @@ const app: ExpressApplication = Express.getExpressApp();
|
||||
const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
try {
|
||||
// init the app
|
||||
await App(APP_NAME, undefined, true);
|
||||
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);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"watch": ["webpack.config.js"],
|
||||
"exec": "export DEBUG=express:* && printenv > /usr/src/app/dev-env/.env && echo 'HOST=localhost' >> /usr/src/app/dev-env/.env && echo 'USE_HTTPS=false' >> /usr/src/app/dev-env/.env && webpack-dev-server --port=3003 --mode=development"
|
||||
"exec": "export DEBUG=express:* && printenv > /usr/src/app/dev-env/.env && echo 'USE_HTTPS=false' >> /usr/src/app/dev-env/.env && webpack-dev-server --port=3003 --mode=development"
|
||||
}
|
||||
297
Accounts/package-lock.json
generated
297
Accounts/package-lock.json
generated
@@ -1,24 +1,24 @@
|
||||
{
|
||||
"name": "accounts",
|
||||
"name": "@oneuptime/accounts",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "accounts",
|
||||
"name": "@oneuptime/accounts",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"CommonServer": "file:../CommonServer",
|
||||
"CommonUI": "file:../CommonUI",
|
||||
"css-loader": "^6.10.0",
|
||||
"css-loader": "^6.11.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.19.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"Model": "file:../Model",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-router-dom": "^6.22.1",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"sass-loader": "^13.3.3",
|
||||
"style-loader": "^3.3.4",
|
||||
"ts-loader": "^9.5.1",
|
||||
@@ -35,19 +35,19 @@
|
||||
}
|
||||
},
|
||||
"../Common": {
|
||||
"name": "common",
|
||||
"name": "@oneuptime/common",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"axios": "^1.6.7",
|
||||
"axios": "^1.6.8",
|
||||
"crypto-js": "^4.1.1",
|
||||
"json5": "^2.2.3",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"posthog-js": "^1.104.4",
|
||||
"reflect-metadata": "^0.2.1",
|
||||
"posthog-js": "^1.116.6",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"slugify": "^1.6.5",
|
||||
"typeorm": "^0.3.20",
|
||||
"uuid": "^8.3.2"
|
||||
@@ -61,49 +61,46 @@
|
||||
}
|
||||
},
|
||||
"../CommonServer": {
|
||||
"name": "common-server",
|
||||
"name": "@oneuptime/common-server",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@clickhouse/client": "^0.2.7",
|
||||
"@elastic/elasticsearch": "^8.11.0",
|
||||
"@clickhouse/client": "^0.2.10",
|
||||
"@elastic/elasticsearch": "^8.12.1",
|
||||
"@opentelemetry/api": "^1.7.0",
|
||||
"@opentelemetry/api-logs": "^0.48.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.48.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.48.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.48.0",
|
||||
"@opentelemetry/api-logs": "^0.49.1",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.43.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.49.1",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.49.1",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.49.1",
|
||||
"@opentelemetry/id-generator-aws-xray": "^1.2.1",
|
||||
"@opentelemetry/instrumentation-express": "^0.35.0",
|
||||
"@opentelemetry/instrumentation-http": "^0.48.0",
|
||||
"@opentelemetry/sdk-logs": "^0.48.0",
|
||||
"@opentelemetry/sdk-logs": "^0.49.1",
|
||||
"@opentelemetry/sdk-metrics": "^1.21.0",
|
||||
"@opentelemetry/sdk-node": "^0.48.0",
|
||||
"@opentelemetry/sdk-trace-node": "^1.21.0",
|
||||
"@socket.io/redis-adapter": "^8.2.1",
|
||||
"acme-client": "^5.3.0",
|
||||
"airtable": "^0.12.2",
|
||||
"axios": "^1.6.4",
|
||||
"bullmq": "^5.3.3",
|
||||
"Common": "file:../Common",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"cron-parser": "^4.8.1",
|
||||
"dotenv": "^16.4.1",
|
||||
"ejs": "^3.1.8",
|
||||
"express": "^4.17.3",
|
||||
"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",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^6.9.9",
|
||||
"nodemailer": "^6.9.10",
|
||||
"pg": "^8.7.3",
|
||||
"socket.io": "^4.7.2",
|
||||
"socket.io": "^4.7.4",
|
||||
"stripe": "^10.17.0",
|
||||
"twilio": "^4.19.3",
|
||||
"twilio": "^4.22.0",
|
||||
"typeorm": "^0.3.20",
|
||||
"typeorm-extension": "^2.2.13",
|
||||
"vm2": "^3.9.14"
|
||||
"typeorm-extension": "^2.2.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^6.3.1",
|
||||
@@ -124,43 +121,48 @@
|
||||
}
|
||||
},
|
||||
"../CommonUI": {
|
||||
"name": "common-ui",
|
||||
"name": "@oneuptime/common-ui",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.24.1",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@opentelemetry/api": "^1.7.0",
|
||||
"@opentelemetry/context-zone": "^1.21.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.48.0",
|
||||
"@opentelemetry/instrumentation": "^0.48.0",
|
||||
"@opentelemetry/instrumentation-fetch": "^0.48.0",
|
||||
"@opentelemetry/instrumentation-xml-http-request": "^0.48.0",
|
||||
"@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",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
"Common": "file:../Common",
|
||||
"formik": "^2.2.9",
|
||||
"history": "^5.3.0",
|
||||
"lodash": "^4.17.21",
|
||||
"Model": "file:../Model",
|
||||
"moment-timezone": "^0.5.44",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-big-calendar": "^1.8.7",
|
||||
"react-big-calendar": "^1.11.2",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-dropzone": "^14.2.2",
|
||||
"react-error-boundary": "^4.0.12",
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-highlight": "^0.15.0",
|
||||
"react-markdown": "^8.0.3",
|
||||
"react-router-dom": "^6.21.3",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-select": "^5.4.0",
|
||||
"react-spinners": "^0.13.6",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactflow": "^11.10.3",
|
||||
"reactflow": "^11.10.4",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"socket.io-client": "^4.7.4",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"tippy.js": "^6.3.7",
|
||||
"universal-cookie": "^4.0.4",
|
||||
"use-async-effect": "^2.2.6"
|
||||
@@ -186,9 +188,9 @@
|
||||
}
|
||||
},
|
||||
"../Model": {
|
||||
"name": "model",
|
||||
"name": "@oneuptime/model",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"typeorm": "^0.3.20"
|
||||
@@ -265,9 +267,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.1.tgz",
|
||||
"integrity": "sha512-zcU0gM3z+3iqj8UX45AmWY810l3oUmXM7uH4dt5xtzvMhRtYVhKGOmgOd1877dOPPepfCjUv57w+syamWIYe7w==",
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
|
||||
"integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
@@ -660,12 +662,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.4",
|
||||
"content-type": "~1.0.5",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
@@ -673,7 +675,7 @@
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.1",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
@@ -761,13 +763,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
|
||||
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
||||
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.1",
|
||||
"set-function-length": "^1.1.1"
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"set-function-length": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -920,9 +927,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -939,15 +946,15 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/css-loader": {
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.10.0.tgz",
|
||||
"integrity": "sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw==",
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz",
|
||||
"integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==",
|
||||
"dependencies": {
|
||||
"icss-utils": "^5.1.0",
|
||||
"postcss": "^8.4.33",
|
||||
"postcss-modules-extract-imports": "^3.0.0",
|
||||
"postcss-modules-local-by-default": "^4.0.4",
|
||||
"postcss-modules-scope": "^3.1.1",
|
||||
"postcss-modules-extract-imports": "^3.1.0",
|
||||
"postcss-modules-local-by-default": "^4.0.5",
|
||||
"postcss-modules-scope": "^3.2.0",
|
||||
"postcss-modules-values": "^4.0.0",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"semver": "^7.5.4"
|
||||
@@ -999,16 +1006,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/define-data-property": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
|
||||
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.1",
|
||||
"gopd": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.0"
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
@@ -1086,6 +1096,25 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
||||
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz",
|
||||
@@ -1160,16 +1189,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.1",
|
||||
"body-parser": "1.20.2",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"cookie": "0.6.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@@ -1322,15 +1351,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
|
||||
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"has-proto": "^1.0.1",
|
||||
"has-symbols": "^1.0.3",
|
||||
"hasown": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@@ -1378,20 +1411,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/has-property-descriptors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
|
||||
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.2"
|
||||
"es-define-property": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
|
||||
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@@ -1411,9 +1444,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
|
||||
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
@@ -1889,9 +1922,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-modules-extract-imports": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
|
||||
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
|
||||
"integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >= 14"
|
||||
},
|
||||
@@ -1900,9 +1933,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-modules-local-by-default": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz",
|
||||
"integrity": "sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz",
|
||||
"integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==",
|
||||
"dependencies": {
|
||||
"icss-utils": "^5.0.0",
|
||||
"postcss-selector-parser": "^6.0.2",
|
||||
@@ -1916,9 +1949,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-modules-scope": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz",
|
||||
"integrity": "sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz",
|
||||
"integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==",
|
||||
"dependencies": {
|
||||
"postcss-selector-parser": "^6.0.4"
|
||||
},
|
||||
@@ -1944,9 +1977,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-selector-parser": {
|
||||
"version": "6.0.13",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
|
||||
"integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
|
||||
"version": "6.0.16",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
|
||||
"integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
@@ -2017,9 +2050,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
@@ -2054,11 +2087,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.22.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.1.tgz",
|
||||
"integrity": "sha512-0pdoRGwLtemnJqn1K0XHUbnKiX0S4X8CgvVVmHGOWmofESj31msHo/1YiqcJWK7Wxfq2a4uvvtS01KAQyWK/CQ==",
|
||||
"version": "6.22.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
|
||||
"integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.15.1"
|
||||
"@remix-run/router": "1.15.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -2068,12 +2101,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.22.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.1.tgz",
|
||||
"integrity": "sha512-iwMyyyrbL7zkKY7MRjOVRy+TMnS/OPusaFVxM2P11x9dzSzGmLsebkCvYirGq0DWB9K9hOspHYYtDz33gE5Duw==",
|
||||
"version": "6.22.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
|
||||
"integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.15.1",
|
||||
"react-router": "6.22.1"
|
||||
"@remix-run/router": "1.15.3",
|
||||
"react-router": "6.22.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -2253,14 +2286,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/set-function-length": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
|
||||
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.1",
|
||||
"get-intrinsic": "^1.2.1",
|
||||
"define-data-property": "^1.1.4",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"gopd": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.0"
|
||||
"has-property-descriptors": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -2272,13 +2307,17 @@
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
|
||||
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
"object-inspect": "^1.9.0"
|
||||
"call-bind": "^1.0.7",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"object-inspect": "^1.13.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
|
||||
@@ -29,14 +29,14 @@
|
||||
"Common": "file:../Common",
|
||||
"CommonServer": "file:../CommonServer",
|
||||
"CommonUI": "file:../CommonUI",
|
||||
"css-loader": "^6.10.0",
|
||||
"css-loader": "^6.11.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.19.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"Model": "file:../Model",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-router-dom": "^6.22.1",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"sass-loader": "^13.3.3",
|
||||
"style-loader": "^3.3.4",
|
||||
"ts-loader": "^9.5.1",
|
||||
|
||||
@@ -8,12 +8,13 @@ import ModelForm, { FormType } from 'CommonUI/src/Components/Forms/ModelForm';
|
||||
import { LOGIN_API_URL } from '../Utils/ApiPaths';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import LoginUtil from '../Utils/Login';
|
||||
import LoginUtil from 'CommonUI/src/Utils/Login';
|
||||
import UserUtil from 'CommonUI/src/Utils/User';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import { DASHBOARD_URL } from 'CommonUI/src/Config';
|
||||
import Alert, { AlertType } from 'CommonUI/src/Components/Alerts/Alert';
|
||||
import UiAnalytics from 'CommonUI/src/Utils/Analytics';
|
||||
import useAsyncEffect from 'use-async-effect';
|
||||
|
||||
const LoginPage: () => JSX.Element = () => {
|
||||
const apiUrl: URL = LOGIN_API_URL;
|
||||
@@ -28,6 +29,16 @@ const LoginPage: () => JSX.Element = () => {
|
||||
|
||||
const [showSsoTip, setShowSSOTip] = useState<boolean>(false);
|
||||
|
||||
const [initialValues, setInitialValues] = React.useState<JSONObject>({});
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
if (Navigation.getQueryStringByName('email')) {
|
||||
setInitialValues({
|
||||
email: Navigation.getQueryStringByName('email'),
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="">
|
||||
@@ -66,9 +77,14 @@ const LoginPage: () => JSX.Element = () => {
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: 'Email',
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
placeholder: 'jeff@example.com',
|
||||
required: true,
|
||||
disabled: Boolean(
|
||||
initialValues && initialValues['email']
|
||||
),
|
||||
title: 'Email',
|
||||
dataTestId: 'email',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -85,6 +101,7 @@ const LoginPage: () => JSX.Element = () => {
|
||||
url: new Route('/accounts/forgot-password'),
|
||||
openLinkInNewTab: false,
|
||||
},
|
||||
dataTestId: 'password',
|
||||
},
|
||||
]}
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
@@ -106,7 +123,7 @@ const LoginPage: () => JSX.Element = () => {
|
||||
}}
|
||||
maxPrimaryButtonWidth={true}
|
||||
footer={
|
||||
<div className="actions pointer text-center mt-4 hover:underline fw-semibold">
|
||||
<div className="actions text-center mt-4 hover:underline fw-semibold">
|
||||
<div>
|
||||
{!showSsoTip && (
|
||||
<div
|
||||
@@ -121,11 +138,9 @@ const LoginPage: () => JSX.Element = () => {
|
||||
|
||||
{showSsoTip && (
|
||||
<div className="text-gray-500 text-sm">
|
||||
Please sign in with your username
|
||||
and password. Once you have signed
|
||||
in, you'll be able to sign in
|
||||
via SSO that's configured for
|
||||
your project.
|
||||
Please sign in with your SSO
|
||||
provider like Okta, Auth0, Entra ID
|
||||
or any other SAML 2.0 provider.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import Link from 'CommonUI/src/Components/Link/Link';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import OneUptimeLogo from 'CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg';
|
||||
import LoginUtil from '../Utils/Login';
|
||||
import LoginUtil from 'CommonUI/src/Utils/Login';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import UserUtil from 'CommonUI/src/Utils/User';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
@@ -104,6 +104,7 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
required: true,
|
||||
disabled: Boolean(initialValues && initialValues['email']),
|
||||
title: 'Email',
|
||||
dataTestId: 'email',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -113,6 +114,7 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
placeholder: 'Jeff Smith',
|
||||
required: true,
|
||||
title: 'Full Name',
|
||||
dataTestId: 'name',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -126,6 +128,7 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
placeholder: 'Acme, Inc.',
|
||||
required: true,
|
||||
title: 'Company Name',
|
||||
dataTestId: 'companyName',
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -139,6 +142,7 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
required: true,
|
||||
placeholder: '+11234567890',
|
||||
title: 'Phone Number',
|
||||
dataTestId: 'companyPhoneNumber',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -155,6 +159,7 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
placeholder: 'Password',
|
||||
title: 'Password',
|
||||
required: true,
|
||||
dataTestId: 'password',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -170,6 +175,7 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
overrideFieldKey: 'confirmPassword',
|
||||
required: true,
|
||||
showEvenIfPermissionDoesNotExist: true,
|
||||
dataTestId: 'confirmPassword',
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
// these options are overrides used only by ts-node
|
||||
// same as the --compilerOptions flag and the TS_NODE_COMPILER_OPTIONS environment variable
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
"module": "commonjs",
|
||||
"resolveJsonModule": true,
|
||||
}
|
||||
},
|
||||
"compilerOptions": {
|
||||
@@ -105,6 +106,7 @@
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:21.6-alpine3.18
|
||||
FROM node:21.7.2-alpine3.18
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ ENV GIT_SHA=${GIT_SHA}
|
||||
ENV APP_VERSION=${APP_VERSION}
|
||||
|
||||
|
||||
# IF APP_VERSION is not set, set it to 1.0.0
|
||||
RUN if [ -z "$APP_VERSION" ]; then export APP_VERSION=1.0.0; fi
|
||||
|
||||
|
||||
# Install bash.
|
||||
RUN apk add bash && apk add curl
|
||||
|
||||
@@ -25,12 +29,16 @@ RUN mkdir /usr/src
|
||||
|
||||
WORKDIR /usr/src/Common
|
||||
COPY ./Common/package*.json /usr/src/Common/
|
||||
# Set version in ./Common/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/Common/package.json
|
||||
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
|
||||
|
||||
@@ -38,6 +46,8 @@ 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
|
||||
|
||||
@@ -48,6 +58,8 @@ COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -10,7 +10,18 @@ const app: ExpressApplication = Express.getExpressApp();
|
||||
const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
try {
|
||||
// init the app
|
||||
await App(APP_NAME, undefined, true);
|
||||
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);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"watch": ["webpack.config.js"],
|
||||
"exec": "export DEBUG=express:* && printenv > /usr/src/app/dev-env/.env && echo 'HOST=localhost' >> /usr/src/app/dev-env/.env && echo 'USE_HTTPS=false' >> /usr/src/app/dev-env/.env && webpack-dev-server --port=3158 --mode=development"
|
||||
"exec": "export DEBUG=express:* && printenv > /usr/src/app/dev-env/.env && echo 'USE_HTTPS=false' >> /usr/src/app/dev-env/.env && webpack-dev-server --port=3158 --mode=development"
|
||||
}
|
||||
26
AdminDashboard/package-lock.json
generated
26
AdminDashboard/package-lock.json
generated
@@ -16,7 +16,7 @@
|
||||
"Model": "file:../Model",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-router-dom": "^6.22.1",
|
||||
"react-router-dom": "^6.22.2",
|
||||
"style-loader": "^3.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -4009,9 +4009,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.1.tgz",
|
||||
"integrity": "sha512-zcU0gM3z+3iqj8UX45AmWY810l3oUmXM7uH4dt5xtzvMhRtYVhKGOmgOd1877dOPPepfCjUv57w+syamWIYe7w==",
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz",
|
||||
"integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
@@ -16936,11 +16936,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.22.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.1.tgz",
|
||||
"integrity": "sha512-0pdoRGwLtemnJqn1K0XHUbnKiX0S4X8CgvVVmHGOWmofESj31msHo/1YiqcJWK7Wxfq2a4uvvtS01KAQyWK/CQ==",
|
||||
"version": "6.22.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz",
|
||||
"integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.15.1"
|
||||
"@remix-run/router": "1.15.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -16950,12 +16950,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.22.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.1.tgz",
|
||||
"integrity": "sha512-iwMyyyrbL7zkKY7MRjOVRy+TMnS/OPusaFVxM2P11x9dzSzGmLsebkCvYirGq0DWB9K9hOspHYYtDz33gE5Duw==",
|
||||
"version": "6.22.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz",
|
||||
"integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.15.1",
|
||||
"react-router": "6.22.1"
|
||||
"@remix-run/router": "1.15.2",
|
||||
"react-router": "6.22.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"Model": "file:../Model",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-router-dom": "^6.22.1",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"style-loader": "^3.3.4"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -19,7 +19,7 @@ import Users from './Pages/Users/Index';
|
||||
import Logout from './Pages/Logout/Logout';
|
||||
|
||||
// Settings Pages.
|
||||
import SettingsEmail from './Pages/Settings/SMTP/Index';
|
||||
import SettingsEmail from './Pages/Settings/Email/Index';
|
||||
import SettingsCallSMS from './Pages/Settings/CallSMS/Index';
|
||||
import SettingsProbes from './Pages/Settings/Probes/Index';
|
||||
import SettingsAuthentication from './Pages/Settings/Authentication/Index';
|
||||
|
||||
@@ -41,7 +41,7 @@ const DashboardProjectPicker: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
<Toggle
|
||||
title="Yearly Plan"
|
||||
initialValue={isSubscriptionPlanYearly}
|
||||
value={isSubscriptionPlanYearly}
|
||||
description="(Save 20%)"
|
||||
onChange={(value: boolean) => {
|
||||
setIsSubscriptionPlanYearly(value);
|
||||
|
||||
@@ -164,7 +164,7 @@ const Projects: FunctionComponent = (): ReactElement => {
|
||||
return (
|
||||
<Toggle
|
||||
title="Yearly Plan"
|
||||
initialValue={isSubscriptionPlanYearly}
|
||||
value={isSubscriptionPlanYearly}
|
||||
description="(Save 20%)"
|
||||
onChange={(value: boolean) => {
|
||||
setIsSubscriptionPlanYearly(value);
|
||||
@@ -222,16 +222,30 @@ const Projects: FunctionComponent = (): ReactElement => {
|
||||
noItemsMessage={'No projects found.'}
|
||||
formFields={fields}
|
||||
showRefreshButton={true}
|
||||
showFilterButton={true}
|
||||
viewPageRoute={Navigation.getCurrentRoute()}
|
||||
columns={[
|
||||
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,
|
||||
isFilterable: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -239,7 +253,6 @@ const Projects: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
title: 'Created At',
|
||||
type: FieldType.DateTime,
|
||||
isFilterable: true,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -12,7 +12,6 @@ import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import ModelAPI from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
import PageLoader from 'CommonUI/src/Components/Loader/PageLoader';
|
||||
import ErrorMessage from 'CommonUI/src/Components/ErrorMessage/ErrorMessage';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import DropdownUtil from 'CommonUI/src/Utils/Dropdown';
|
||||
import Pill from 'CommonUI/src/Components/Pill/Pill';
|
||||
import { Green, Red } from 'Common/Types/BrandColors';
|
||||
@@ -89,6 +88,42 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
>
|
||||
{/* 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={{
|
||||
@@ -183,7 +218,8 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
title: 'Use SSL / TLS',
|
||||
stepId: 'server-info',
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
description: 'Make email communication secure?',
|
||||
description:
|
||||
'If you use port 465, please enable this. Do not enable this if you use port 587.',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -287,7 +323,7 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
)}
|
||||
|
||||
{emailServerType === EmailServerType.Sendgrid ? (
|
||||
<CardModelDetail
|
||||
<CardModelDetail<GlobalConfig>
|
||||
name="Sendgrid Settings"
|
||||
cardProps={{
|
||||
title: 'Sendgrid Settings',
|
||||
@@ -339,7 +375,7 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
title: '',
|
||||
placeholder: 'None',
|
||||
getElement: (item: JSONObject) => {
|
||||
getElement: (item: GlobalConfig) => {
|
||||
if (
|
||||
item['sendgridApiKey'] &&
|
||||
item['sendgridFromEmail'] &&
|
||||
@@ -7,14 +7,12 @@ import DashboardSideMenu from '../SideMenu';
|
||||
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import Probe from 'Model/Models/Probe';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import { Green, Red } from 'Common/Types/BrandColors';
|
||||
import Statusbubble from 'CommonUI/src/Components/StatusBubble/StatusBubble';
|
||||
import ProbeElement from 'CommonUI/src/Components/Probe/Probe';
|
||||
import IsNull from 'Common/Types/BaseDatabase/IsNull';
|
||||
import Banner from 'CommonUI/src/Components/Banner/Banner';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import { ButtonStyleType } from 'CommonUI/src/Components/Button/Button';
|
||||
import ConfirmModal from 'CommonUI/src/Components/Modal/ConfirmModal';
|
||||
@@ -24,7 +22,7 @@ import { ErrorFunction, VoidFunction } from 'Common/Types/FunctionTypes';
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
const [showKeyModal, setShowKeyModal] = useState<boolean>(false);
|
||||
|
||||
const [currentProbe, setCurrentProbe] = useState<JSONObject | null>(null);
|
||||
const [currentProbe, setCurrentProbe] = useState<Probe | null>(null);
|
||||
|
||||
return (
|
||||
<Page
|
||||
@@ -57,9 +55,7 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
openInNewTab={true}
|
||||
title="Need help with setting up Global Probes?"
|
||||
description="Here is a guide which will help you get set up"
|
||||
link={URL.fromString(
|
||||
'https://github.com/OneUptime/oneuptime/blob/master/Docs/Probe/CustomProbe.md'
|
||||
)}
|
||||
link={Route.fromString('/docs/probe/custom-probe')}
|
||||
/>
|
||||
|
||||
<ModelTable<Probe>
|
||||
@@ -81,7 +77,6 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
modelAPI={AdminModelAPI}
|
||||
noItemsMessage={'No probes found.'}
|
||||
showRefreshButton={true}
|
||||
showFilterButton={true}
|
||||
onBeforeCreate={(item: Probe) => {
|
||||
item.isGlobalProbe = true;
|
||||
return Promise.resolve(item);
|
||||
@@ -130,7 +125,7 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
title: 'Show ID and Key',
|
||||
buttonStyleType: ButtonStyleType.NORMAL,
|
||||
onClick: async (
|
||||
item: JSONObject,
|
||||
item: Probe,
|
||||
onCompleteAction: VoidFunction,
|
||||
onError: ErrorFunction
|
||||
) => {
|
||||
@@ -146,6 +141,22 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
},
|
||||
]}
|
||||
filters={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: 'Name',
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: 'Description',
|
||||
type: FieldType.Text,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
@@ -153,8 +164,8 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
title: 'Name',
|
||||
type: FieldType.Text,
|
||||
isFilterable: true,
|
||||
getElement: (item: JSONObject): ReactElement => {
|
||||
|
||||
getElement: (item: Probe): ReactElement => {
|
||||
return <ProbeElement probe={item} />;
|
||||
},
|
||||
},
|
||||
@@ -164,7 +175,6 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
title: 'Description',
|
||||
type: FieldType.Text,
|
||||
isFilterable: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -172,15 +182,12 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
title: 'Status',
|
||||
type: FieldType.Text,
|
||||
isFilterable: false,
|
||||
getElement: (item: JSONObject): ReactElement => {
|
||||
getElement: (item: Probe): ReactElement => {
|
||||
if (
|
||||
item &&
|
||||
item['lastAlive'] &&
|
||||
OneUptimeDate.getNumberOfMinutesBetweenDates(
|
||||
OneUptimeDate.fromString(
|
||||
item['lastAlive'] as string
|
||||
),
|
||||
OneUptimeDate.fromString(item['lastAlive']),
|
||||
OneUptimeDate.getCurrentDate()
|
||||
) < 5
|
||||
) {
|
||||
|
||||
@@ -72,16 +72,37 @@ const Users: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
]}
|
||||
showRefreshButton={true}
|
||||
showFilterButton={true}
|
||||
viewPageRoute={Navigation.getCurrentRoute()}
|
||||
columns={[
|
||||
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,
|
||||
isFilterable: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -89,7 +110,6 @@ const Users: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
title: 'Email',
|
||||
type: FieldType.Email,
|
||||
isFilterable: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -97,7 +117,6 @@ const Users: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
title: 'Created At',
|
||||
type: FieldType.DateTime,
|
||||
isFilterable: true,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
// these options are overrides used only by ts-node
|
||||
// same as the --compilerOptions flag and the TS_NODE_COMPILER_OPTIONS environment variable
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
"module": "commonjs",
|
||||
"resolveJsonModule": true,
|
||||
}
|
||||
},
|
||||
"compilerOptions": {
|
||||
@@ -105,6 +106,7 @@
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ ENV GIT_SHA=${GIT_SHA}
|
||||
ENV APP_VERSION=${APP_VERSION}
|
||||
|
||||
|
||||
# IF APP_VERSION is not set, set it to 1.0.0
|
||||
RUN if [ -z "$APP_VERSION" ]; then export APP_VERSION=1.0.0; fi
|
||||
|
||||
|
||||
# Install bash.
|
||||
RUN apk add bash && apk add curl
|
||||
|
||||
@@ -29,12 +33,16 @@ RUN mkdir /usr/src
|
||||
|
||||
WORKDIR /usr/src/Common
|
||||
COPY ./Common/package*.json /usr/src/Common/
|
||||
# Set version in ./Common/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/Common/package.json
|
||||
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
|
||||
|
||||
@@ -42,6 +50,8 @@ 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
|
||||
|
||||
@@ -53,6 +63,8 @@ WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY ./App/package*.json /usr/src/app/
|
||||
# Set version in ./App/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/app/package.json
|
||||
RUN npm install
|
||||
|
||||
# Expose ports.
|
||||
|
||||
@@ -16,58 +16,74 @@ import StatusServiceHandler from './Service/Status';
|
||||
import DataTypeServiceHandler from './Service/DataType';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import { StaticPath } from './Utils/Config';
|
||||
import FeatureSet from 'CommonServer/Types/FeatureSet';
|
||||
|
||||
const ResourceDictionary: Dictionary<ModelDocumentation> =
|
||||
ResourceUtil.getResourceDictionaryByPath();
|
||||
const APIReferenceFeatureSet: FeatureSet = {
|
||||
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 }));
|
||||
app.use('/reference', ExpressStatic(StaticPath, { maxAge: 2592000 }));
|
||||
|
||||
// Index page
|
||||
app.get(['/reference'], (_req: ExpressRequest, res: ExpressResponse) => {
|
||||
return res.redirect('/reference/introduction');
|
||||
});
|
||||
// Index 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);
|
||||
}
|
||||
);
|
||||
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'];
|
||||
// All Pages
|
||||
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);
|
||||
});
|
||||
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;
|
||||
|
||||
@@ -18,7 +18,7 @@ export default class ServiceHandler {
|
||||
|
||||
pageData.featuredResources = FeaturedResources;
|
||||
pageTitle = 'Introduction';
|
||||
pageDescription = 'API Documentation for OneUptime';
|
||||
pageDescription = 'API Reference for OneUptime';
|
||||
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="font-bold text-xl">API Documentation</h1>
|
||||
<h1 class="font-bold text-xl">API Reference</h1>
|
||||
<p class="lead">Use the OneUptime API to access any resource in your projects, create automated
|
||||
workflows, and more
|
||||
and
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
# Development
|
||||
# Local Development
|
||||
|
||||
For local development you need to use docker-compose.dev.yml file.
|
||||
|
||||
49
App/FeatureSet/Docs/Content/introduction/getting-started.md
Normal file
49
App/FeatureSet/Docs/Content/introduction/getting-started.md
Normal file
@@ -0,0 +1,49 @@
|
||||
### OneUptime: The Complete Open-Source Observability Platform
|
||||
|
||||
OneUptime is a comprehensive solution for monitoring and managing your online services. Whether you need to check the availability of your website, dashboard, API, or any other online resource, OneUptime can alert your team when downtime happens and keep your customers informed with a status page. OneUptime also helps you handle incidents, set up on-call rotations, run tests, secure your services, analyze logs, track performance, and debug errors.
|
||||
|
||||
OneUptime replaces multiple tools with one integrated platform:
|
||||
|
||||
|
||||
##### Uptime Monitoring
|
||||
|
||||
Monitor the availability and response time of your online services from multiple locations around the world. Get notified via email, SMS, Slack, or other channels when something goes wrong. Replace tools like Pingdom.
|
||||
|
||||
|
||||
##### Status Pages
|
||||
|
||||
Communicate with your customers and stakeholders during downtime or maintenance. Create a custom-branded status page that shows the current status and history of your services. Replace tools like StatusPage.io.
|
||||
|
||||
|
||||
##### Incident Management
|
||||
|
||||
Manage incidents from start to finish with a collaborative workflow. Create incident reports, assign tasks, update stakeholders, and document resolutions. Replace tools like Incident.io.
|
||||
|
||||
|
||||
##### On Call and Alerts
|
||||
|
||||
Schedule on-call shifts for your team and define escalation policies. Ensure that the right person is notified at the right time when an incident occurs. Replace tools like PagerDuty.
|
||||
|
||||
|
||||
##### Logs Management
|
||||
|
||||
Collect, store, and analyze logs from your online services. Search, filter, and visualize log data to gain insights and troubleshoot issues. Replace tools like Loggly.
|
||||
|
||||
|
||||
##### Workflows
|
||||
|
||||
Integrate OneUptime with your existing tools and automate your workflows. Integrate with tools like Slack, Jira, GitHub, and 5000+ more.
|
||||
|
||||
|
||||
##### Application Performance Monitoring
|
||||
|
||||
Measure and optimize the performance of your online apps and services. Track key metrics such as traces, response time, throughput, error rate, and user satisfaction. Replace tools like NewRelic and DataDog.
|
||||
|
||||
##### Error Tracking
|
||||
|
||||
Detect and diagnose errors in your online services. Get detailed error reports with stack traces, context, and user feedback. Replace tools like Sentry.
|
||||
|
||||
##### Reliability Autopilot
|
||||
|
||||
Scan your code and fix performance issues and errors automatically. Get recommendations for improving the reliability of your online services.
|
||||
|
||||
@@ -36,13 +36,13 @@ You can find the full list of supported sources [here](https://www.fluentd.org/d
|
||||
|
||||
On the telemetry service page, click on "Create Telemetry Service" button.
|
||||
|
||||

|
||||

|
||||
|
||||
Once you create a telemetry service, click on "View Service" and you will be redirected to the telemetry service page.
|
||||
|
||||
Click on View Service Token and copy the token. You will need this token to configure the telemetry service. **Please keep this token safe.**
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## Configuration
|
||||
@@ -8,13 +8,13 @@ After you sign up to OneUptime and create a project. Click on more in the Naviga
|
||||
|
||||
On the telemetry service page, click on "Create Telemetry Service" button.
|
||||
|
||||

|
||||

|
||||
|
||||
Once you create a telemetry service, click on "View Service" and you will be redirected to the telemetry service page.
|
||||
|
||||
Click on View Service Token and copy the token. You will need this token to configure the telemetry service. Please keep this token safe.
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
### Step 2 - Configure the telemetry service in your application.
|
||||
@@ -5,18 +5,89 @@ import Express, {
|
||||
ExpressStatic,
|
||||
ExpressApplication,
|
||||
} from 'CommonServer/Utils/Express';
|
||||
import { StaticPath, ViewsPath } from './Utils/Config';
|
||||
import { ContentPath, StaticPath, ViewsPath } from './Utils/Config';
|
||||
import DocsNav, { NavGroup, NavLink } from './Utils/Nav';
|
||||
import LocalFile from 'CommonServer/Utils/LocalFile';
|
||||
import DocsRender from './Utils/Render';
|
||||
import logger from 'CommonServer/Utils/Logger';
|
||||
import FeatureSet from 'CommonServer/Types/FeatureSet';
|
||||
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
const DocsFeatureSet: FeatureSet = {
|
||||
init: async (): Promise<void> => {
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
|
||||
app.get('/docs', (_req: ExpressRequest, res: ExpressResponse) => {
|
||||
res.render(`${ViewsPath}/index`, {
|
||||
support: false,
|
||||
footerCards: true,
|
||||
cta: true,
|
||||
blackLogo: false,
|
||||
requestDemoCta: false,
|
||||
});
|
||||
});
|
||||
app.get('/docs', (_req: ExpressRequest, res: ExpressResponse) => {
|
||||
res.redirect('/docs/introduction/getting-started');
|
||||
});
|
||||
|
||||
app.use('/docs/static', ExpressStatic(StaticPath));
|
||||
app.get(
|
||||
'/docs/:categorypath/:pagepath',
|
||||
async (_req: ExpressRequest, res: ExpressResponse) => {
|
||||
try {
|
||||
const fullPath: string =
|
||||
`${_req.params['categorypath']}/${_req.params['pagepath']}`.toLowerCase();
|
||||
|
||||
// read file from Content folder.
|
||||
let contentInMarkdown: string = await LocalFile.read(
|
||||
`${ContentPath}/${fullPath}.md`
|
||||
);
|
||||
|
||||
// remove first line from content because we dont want to show title in content. Title is already in nav.
|
||||
|
||||
contentInMarkdown = contentInMarkdown
|
||||
.split('\n')
|
||||
.slice(1)
|
||||
.join('\n');
|
||||
|
||||
const renderedContent: string = await DocsRender.render(
|
||||
contentInMarkdown
|
||||
);
|
||||
|
||||
const currentCategory: NavGroup | undefined = DocsNav.find(
|
||||
(category: NavGroup) => {
|
||||
return category.links.find((link: NavLink) => {
|
||||
return link.url
|
||||
.toLocaleLowerCase()
|
||||
.includes(fullPath);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const currrentNavLink: NavLink | undefined =
|
||||
currentCategory?.links.find((link: NavLink) => {
|
||||
return link.url
|
||||
.toLocaleLowerCase()
|
||||
.includes(fullPath);
|
||||
});
|
||||
|
||||
if (!currentCategory || !currrentNavLink) {
|
||||
// render not found.
|
||||
|
||||
res.status(404);
|
||||
return res.render(`${ViewsPath}/NotFound`, {
|
||||
nav: DocsNav,
|
||||
});
|
||||
}
|
||||
|
||||
res.render(`${ViewsPath}/Index`, {
|
||||
nav: DocsNav,
|
||||
content: renderedContent,
|
||||
category: currentCategory,
|
||||
link: currrentNavLink,
|
||||
githubPath: fullPath,
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
res.status(500);
|
||||
return res.render(`${ViewsPath}/ServerError`, {
|
||||
nav: DocsNav,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
app.use('/docs/static', ExpressStatic(StaticPath));
|
||||
},
|
||||
};
|
||||
|
||||
export default DocsFeatureSet;
|
||||
|
||||
3178
App/FeatureSet/Docs/Static/css/style.css
Normal file
3178
App/FeatureSet/Docs/Static/css/style.css
Normal file
File diff suppressed because it is too large
Load Diff
BIN
App/FeatureSet/Docs/Static/fonts/f1.woff2
Normal file
BIN
App/FeatureSet/Docs/Static/fonts/f1.woff2
Normal file
Binary file not shown.
BIN
App/FeatureSet/Docs/Static/fonts/f2.woff2
Normal file
BIN
App/FeatureSet/Docs/Static/fonts/f2.woff2
Normal file
Binary file not shown.
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
@@ -50,5 +50,5 @@ nvm alias default node
|
||||
nvm use default
|
||||
|
||||
# Now install
|
||||
npm install -g ts-node
|
||||
npm install -g tsx
|
||||
npm install -g @oneuptime/infrastructure-agent
|
||||
@@ -1,2 +1,3 @@
|
||||
export const ViewsPath: string = '/usr/src/app/FeatureSet/Docs/Views';
|
||||
export const StaticPath: string = '/usr/src/app/FeatureSet/Docs/Static';
|
||||
export const ContentPath: string = '/usr/src/app/FeatureSet/Docs/Content';
|
||||
|
||||
63
App/FeatureSet/Docs/Utils/Nav.ts
Normal file
63
App/FeatureSet/Docs/Utils/Nav.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
export interface NavLink {
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface NavGroup {
|
||||
title: string;
|
||||
links: NavLink[];
|
||||
}
|
||||
|
||||
const DocsNav: NavGroup[] = [
|
||||
{
|
||||
title: 'Introduction',
|
||||
links: [
|
||||
{
|
||||
title: 'Getting Started',
|
||||
url: '/docs/introduction/getting-started',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Installation',
|
||||
links: [
|
||||
{
|
||||
title: 'Local Development',
|
||||
url: '/docs/installation/local-development',
|
||||
},
|
||||
{
|
||||
title: 'Docker Compose',
|
||||
url: '/docs/installation/docker-compose',
|
||||
},
|
||||
{
|
||||
title: 'Kubernetes and Helm',
|
||||
url: 'https://artifacthub.io/packages/helm/oneuptime/oneuptime',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Monitor',
|
||||
links: [
|
||||
{
|
||||
title: 'JavaScript Expressions',
|
||||
url: '/docs/monitor/javascript-expression',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Probe',
|
||||
links: [
|
||||
{ title: 'Custom Probes', url: '/docs/probe/custom-probe' },
|
||||
{ title: 'IP Addresses', url: '/docs/probe/ip-address' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Telemetry',
|
||||
links: [
|
||||
{ title: 'OpenTelemetry', url: '/docs/telemetry/open-telemetry' },
|
||||
{ title: 'Fluentd', url: '/docs/telemetry/fluentd' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default DocsNav;
|
||||
50
App/FeatureSet/Docs/Utils/Render.ts
Normal file
50
App/FeatureSet/Docs/Utils/Render.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Renderer, marked } from 'marked';
|
||||
|
||||
export default class DocsRender {
|
||||
public static async render(markdownContent: string): Promise<string> {
|
||||
const renderer: Renderer = this.getBlogRenderer();
|
||||
|
||||
return await marked(markdownContent, {
|
||||
renderer: renderer,
|
||||
});
|
||||
}
|
||||
|
||||
private static getBlogRenderer(): Renderer {
|
||||
const renderer: Renderer = new Renderer();
|
||||
|
||||
renderer.paragraph = function (text) {
|
||||
return `<p class="mt-2 mb-2 leading-8 text-gray-600">${text}</p>`;
|
||||
};
|
||||
|
||||
renderer.blockquote = function (quote) {
|
||||
return `<blockquote class="p-4 pt-1 pb-1 my-4 border-s-4 border-indigo-500">
|
||||
<div class="leading-8 text-gray-600">${quote}</div>
|
||||
</blockquote>`;
|
||||
};
|
||||
|
||||
renderer.image = function (href, _title, text) {
|
||||
return `<img src="${href}" alt="${text}" class="rounded-md shadow-md" />`;
|
||||
};
|
||||
|
||||
renderer.code = function (code, language) {
|
||||
return `<pre class="language-${language} rounded-md"><code class="language-${language} rounded-md">${code}</code></pre>`;
|
||||
};
|
||||
|
||||
renderer.heading = function (text, level) {
|
||||
if (level === 1) {
|
||||
return `<h1 class="my-5 mt-8 text-4xl font-bold tracking-tight text-gray-800">${text}</h1>`;
|
||||
} else if (level === 2) {
|
||||
return `<h2 class="my-5 mt-8 text-3xl font-bold tracking-tight text-gray-800">${text}</h2>`;
|
||||
} else if (level === 3) {
|
||||
return `<h3 class="my-5 mt-8 text-2xl font-bold tracking-tight text-gray-800">${text}</h3>`;
|
||||
} else if (level === 4) {
|
||||
return `<h4 class="my-5 mt-8 text-xl font-bold tracking-tight text-gray-800">${text}</h4>`;
|
||||
} else if (level === 5) {
|
||||
return `<h5 class="my-5 mt-8 text-lg font-bold tracking-tight text-gray-800">${text}</h5>`;
|
||||
}
|
||||
return `<h6 class="my-5 tracking-tight font-bold text-gray-800">${text}</h6>`;
|
||||
};
|
||||
|
||||
return renderer;
|
||||
}
|
||||
}
|
||||
34
App/FeatureSet/Docs/Views/Index.ejs
Normal file
34
App/FeatureSet/Docs/Views/Index.ejs
Normal file
@@ -0,0 +1,34 @@
|
||||
<html lang="en" class="h-full antialiased __variable_e66fe9 __variable_b436a8 light nbuofyfqi idc0_350"
|
||||
style="color-scheme: light;">
|
||||
|
||||
<head>
|
||||
<%- include('./Partials/Head.ejs') %>
|
||||
</head>
|
||||
|
||||
<body class="flex min-h-full bg-white ">
|
||||
|
||||
<div class="flex w-full flex-col">
|
||||
<%- include('./Partials/Header.ejs') %>
|
||||
|
||||
<div class="relative mx-auto flex w-full max-w-8xl flex-auto justify-center sm:px-2 lg:px-8 xl:px-12">
|
||||
<div class="hidden lg:relative lg:block lg:flex-none">
|
||||
<div class="absolute inset-y-0 right-0 w-[50vw] bg-slate-50 "></div>
|
||||
<div
|
||||
class="absolute bottom-0 right-0 top-16 hidden h-12 w-px bg-gradient-to-t from-slate-800 ">
|
||||
</div>
|
||||
<div class="absolute bottom-0 right-0 top-28 hidden w-px bg-slate-800 "></div>
|
||||
<div
|
||||
class="sticky top-[4.75rem] -ml-0.5 h-[calc(100vh-4.75rem)] w-64 overflow-y-auto overflow-x-hidden py-16 pl-0.5 pr-8 xl:w-72 xl:pr-16">
|
||||
<%- include('./Partials/Nav.ejs') %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min-w-0 max-w-2xl flex-auto px-4 py-16 lg:max-w-none lg:pl-8 lg:pr-0 xl:px-16">
|
||||
<%- include('./Partials/Content.ejs', { category: category, link: link, content: content }) %>
|
||||
<%- include('./Partials/OpenSourceCommitment.ejs', { githubPath: githubPath }) %>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
0
App/FeatureSet/Docs/Views/NotFound.ejs
Normal file
0
App/FeatureSet/Docs/Views/NotFound.ejs
Normal file
11
App/FeatureSet/Docs/Views/Partials/Content.ejs
Normal file
11
App/FeatureSet/Docs/Views/Partials/Content.ejs
Normal file
@@ -0,0 +1,11 @@
|
||||
<article>
|
||||
<header class="mb-9 space-y-1">
|
||||
<p class="text-base font-bold text-sky-500"><%- category.title %></p>
|
||||
<h1 class="font-bold text-3xl tracking-tight text-slate-900 "><%- link.title %>
|
||||
</h1>
|
||||
</header>
|
||||
<div
|
||||
class="prose prose-slate max-w-none prose-headings:scroll-mt-28 prose-headings:font-display prose-headings:font-normal lg:prose-headings:scroll-mt-[8.5rem] prose-lead:text-slate-500 prose-a:font-semibold prose-a:no-underline prose-a:shadow-[inset_0_-2px_0_0_var(--tw-prose-background,#fff),inset_0_calc(-1*(var(--tw-prose-underline-size,4px)+2px))_0_0_var(--tw-prose-underline,theme(colors.sky.300))] hover:prose-a:[--tw-prose-underline-size:6px] prose-pre:rounded-xl prose-pre:bg-slate-900 prose-pre:shadow-lg ">
|
||||
<%- content %>
|
||||
</div>
|
||||
</article>
|
||||
15
App/FeatureSet/Docs/Views/Partials/Head.ejs
Normal file
15
App/FeatureSet/Docs/Views/Partials/Head.ejs
Normal file
@@ -0,0 +1,15 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preload" href="/docs/static/fonts/f1.woff2" as="font" crossorigin="" type="font/woff2">
|
||||
<link rel="preload" href="/docs/static/fonts/f2.woff2" as="font" crossorigin="" type="font/woff2">
|
||||
<link rel="stylesheet" href="/docs/static/css/style.css" crossorigin="" data-precedence="next">
|
||||
<title>OneUptime Documentation</title>
|
||||
<meta name="description"
|
||||
content="Cache every single thing your app could ever do ahead of time, so your code never even has to run at all.">
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16">
|
||||
<meta name="next-size-adjust">
|
||||
<link rel="stylesheet" href="/docs/static/css/style.css" crossorigin="" data-precedence="next" />
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/a11y-dark.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
<script>hljs.highlightAll();</script>
|
||||
17
App/FeatureSet/Docs/Views/Partials/Header.ejs
Normal file
17
App/FeatureSet/Docs/Views/Partials/Header.ejs
Normal file
@@ -0,0 +1,17 @@
|
||||
<header
|
||||
class="sticky top-0 z-50 flex flex-none flex-wrap items-center justify-between bg-white px-4 py-5 shadow-md shadow-slate-900/5 transition duration-500 sm:px-6 lg:px-8 ">
|
||||
|
||||
<div class="relative flex flex-grow basis-0 items-center"><a aria-label="Home page" href="/">
|
||||
<img class="h-8 w-auto" src="/img/3-transparent.svg" alt="">
|
||||
</a></div>
|
||||
|
||||
<div class="relative flex basis-0 justify-end gap-6 sm:gap-8 md:flex-grow">
|
||||
<a class="group" aria-label="GitHub" target="_blank" href="https://github.com/oneuptime/oneuptime"><svg
|
||||
aria-hidden="true" viewBox="0 0 16 16"
|
||||
class="h-6 w-6 fill-slate-400 group-hover:fill-slate-500 ">
|
||||
<path
|
||||
d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z">
|
||||
</path>
|
||||
</svg></a>
|
||||
</div>
|
||||
</header>
|
||||
33
App/FeatureSet/Docs/Views/Partials/Nav.ejs
Normal file
33
App/FeatureSet/Docs/Views/Partials/Nav.ejs
Normal file
@@ -0,0 +1,33 @@
|
||||
<nav class="text-base lg:text-sm">
|
||||
<ul role="list" class="space-y-9">
|
||||
<% for(var i=0; i<nav.length; i++) {%>
|
||||
<li>
|
||||
<h2 class="font-display font-medium text-slate-900 "><%- nav[i].title -%>
|
||||
</h2>
|
||||
|
||||
<ul role="list"
|
||||
class="mt-2 space-y-2 border-l-2 border-slate-100 lg:mt-4 lg:space-y-4 lg:border-slate-200 ">
|
||||
<% if(nav[i].links.length> 0) { %>
|
||||
<% for(var j=0; j<nav[i].links.length; j++) {%>
|
||||
<% if(link.url===nav[i].links[j].url) { %>
|
||||
<li class="relative"><a
|
||||
class="block w-full pl-3.5 before:pointer-events-none before:absolute before:-left-1 before:top-1/2 before:h-1.5 before:w-1.5 before:-translate-y-1/2 before:rounded-full font-semibold text-sky-500 before:bg-sky-500"
|
||||
href="<%- nav[i].links[j].url -%>"><%-
|
||||
nav[i].links[j].title -%></a>
|
||||
</li>
|
||||
<% } else { %>
|
||||
<li class="relative"><a
|
||||
class="block w-full pl-3.5 before:pointer-events-none before:absolute before:-left-1 before:top-1/2 before:h-1.5 before:w-1.5 before:-translate-y-1/2 before:rounded-full text-slate-500 before:hidden before:bg-slate-300 hover:text-slate-600 hover:before:block "
|
||||
href="<%- nav[i].links[j].url -%>"><%-
|
||||
nav[i].links[j].title -%></a></li>
|
||||
|
||||
<% } %>
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<% } %>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
22
App/FeatureSet/Docs/Views/Partials/OpenSourceCommitment.ejs
Normal file
22
App/FeatureSet/Docs/Views/Partials/OpenSourceCommitment.ejs
Normal file
@@ -0,0 +1,22 @@
|
||||
<div class=" font-medium leading-6 text-gray-900 mt-24">Our Commitment to Open Source
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-gray-900">
|
||||
<ul role="list" class="divide-y divide-gray-100 rounded-md border border-gray-200">
|
||||
<li class="items-center justify-between py-4 pl-4 pr-5 leading-6">
|
||||
<div class="items-center">
|
||||
|
||||
<div class="p-1 space-y-2">
|
||||
|
||||
|
||||
|
||||
<div>Everything we do at OneUptime is 100% open-source. You can contribute and improve this post here <a class="underline"
|
||||
target="_blank"
|
||||
href="https://github.com/OneUptime/oneuptime/tree/master/App/FeatureSet/Docs/Content/<%- githubPath -%>.md">here.</a></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
13
App/FeatureSet/Docs/Views/Partials/Pagination.ejs
Normal file
13
App/FeatureSet/Docs/Views/Partials/Pagination.ejs
Normal file
@@ -0,0 +1,13 @@
|
||||
<dl class="mt-12 flex border-t border-slate-200 pt-6 ">
|
||||
<div class="ml-auto text-right">
|
||||
<dt class="font-display text-sm font-medium text-slate-900 ">Next</dt>
|
||||
<dd class="mt-1"><a
|
||||
class="flex items-center gap-x-1 text-base font-semibold text-slate-500 hover:text-slate-600 "
|
||||
href="/docs/installation">Installation<svg viewBox="0 0 16 16" aria-hidden="true"
|
||||
class="h-4 w-4 flex-none fill-current">
|
||||
<path
|
||||
d="m9.182 13.423-1.17-1.16 3.505-3.505H3V7.065h8.517l-3.506-3.5L9.181 2.4l5.512 5.511-5.511 5.512Z">
|
||||
</path>
|
||||
</svg></a></dd>
|
||||
</div>
|
||||
</dl>
|
||||
0
App/FeatureSet/Docs/Views/ServerError.ejs
Normal file
0
App/FeatureSet/Docs/Views/ServerError.ejs
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -58,7 +58,7 @@ export default (product: string): Product => {
|
||||
{
|
||||
question:
|
||||
'Do I need to buy a monitoring solution to monitor my resources?',
|
||||
answer: 'PagerDuty needs a seperate monitoring solution that you need to buy which then sends data to PagerDuty for on-call and incident management. OneUptime has a built in monitoring solution as well. You use one product, your team has one dashboard, save time, simplify ops.',
|
||||
answer: 'PagerDuty needs a separate monitoring solution that you need to buy which then sends data to PagerDuty for on-call and incident management. OneUptime has a built in monitoring solution as well. You use one product, your team has one dashboard, save time, simplify ops.',
|
||||
},
|
||||
{
|
||||
question:
|
||||
@@ -215,7 +215,7 @@ export default (product: string): Product => {
|
||||
{
|
||||
question:
|
||||
'Do I need to buy a monitoring solution to monitor my resources?',
|
||||
answer: 'StatusPage.io needs a seperate monitoring solution that you need to buy which then sends data to StatusPage.io. OneUptime has a built in monitoring solution as well. You use one product, your team has one dashboard, save time, simplify ops.',
|
||||
answer: 'StatusPage.io needs a separate monitoring solution that you need to buy which then sends data to StatusPage.io. OneUptime has a built in monitoring solution as well. You use one product, your team has one dashboard, save time, simplify ops.',
|
||||
},
|
||||
{
|
||||
question:
|
||||
|
||||
@@ -59,10 +59,8 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/vs2015.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/a11y-dark.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/yaml.min.js"></script>
|
||||
<script>hljs.highlightAll();</script>
|
||||
|
||||
</head>
|
||||
|
||||
@@ -314,6 +314,15 @@
|
||||
<span class="text-sm text-gray-500">On-Call and Alerts</span>
|
||||
</li>
|
||||
|
||||
<li class="flex space-x-3">
|
||||
|
||||
<%- include('./Partials/tick-icon') %>
|
||||
|
||||
|
||||
|
||||
<span class="text-sm text-gray-500">Monitor VM's & Servers</span>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -324,14 +333,7 @@
|
||||
|
||||
|
||||
|
||||
<li class="flex space-x-3">
|
||||
|
||||
<%- include('./Partials/comingsoon-icon') %>
|
||||
|
||||
|
||||
|
||||
<span class="text-sm text-gray-500">Monitor VM's & Servers</span>
|
||||
</li>
|
||||
|
||||
|
||||
<li class="flex space-x-3">
|
||||
|
||||
|
||||
@@ -97,7 +97,12 @@
|
||||
<ul role="list" class="mt-6 space-y-4">
|
||||
|
||||
<li>
|
||||
<a href="/reference" class="text-sm leading-6 text-gray-600 hover:text-gray-900">API Documentation</a>
|
||||
<a href="/docs" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Docs</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="/reference" class="text-sm leading-6 text-gray-600 hover:text-gray-900">API Reference</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
@@ -33,6 +33,29 @@
|
||||
top: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/*Chrome*/
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
input[type='range']::-webkit-slider-thumb {
|
||||
width: 20px;
|
||||
-webkit-appearance: none;
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
background: #4b5563;
|
||||
}
|
||||
}
|
||||
|
||||
/*Firefox*/
|
||||
input[type='range']::-moz-range-thumb {
|
||||
width: 16px;
|
||||
-webkit-appearance: none;
|
||||
border-radius: 50%;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
background: #4b5563;
|
||||
border-color: #4b5563;
|
||||
}
|
||||
</style>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
|
||||
@@ -55,15 +78,15 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
|
||||
posthog.init('phc_lrbfSHsDc1YOhfbabPI8ncLCKz8eqeGdmu0O6IRKaz1',{api_host:'https://eu.posthog.com', autocapture: false})
|
||||
!function (t, e) { var o, n, p, r; e.__SV || (window.posthog = e, e._i = [], e.init = function (i, s, a) { function g(t, e) { var o = e.split("."); 2 == o.length && (t = t[o[0]], e = o[1]), t[e] = function () { t.push([e].concat(Array.prototype.slice.call(arguments, 0))) } } (p = t.createElement("script")).type = "text/javascript", p.async = !0, p.src = s.api_host + "/static/array.js", (r = t.getElementsByTagName("script")[0]).parentNode.insertBefore(p, r); var u = e; for (void 0 !== a ? u = e[a] = [] : a = "posthog", u.people = u.people || [], u.toString = function (t) { var e = "posthog"; return "posthog" !== a && (e += "." + a), t || (e += " (stub)"), e }, u.people.toString = function () { return u.toString(1) + ".people (stub)" }, o = "capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys".split(" "), n = 0; n < o.length; n++)g(u, o[n]); e._i.push([i, s, a]) }, e.__SV = 1) }(document, window.posthog || []);
|
||||
posthog.init('phc_lrbfSHsDc1YOhfbabPI8ncLCKz8eqeGdmu0O6IRKaz1', { api_host: 'https://eu.posthog.com', autocapture: false })
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
//check if utm_source is present in the URL and save it to localstorage.
|
||||
|
||||
if(posthog){
|
||||
if (posthog) {
|
||||
posthog.capture('home/page_view', {
|
||||
'page': {
|
||||
'path': window.location.pathname,
|
||||
@@ -117,4 +140,4 @@
|
||||
<link rel="icon" href="/img/ou-wb.svg">
|
||||
<link rel="image_src" type="image/png" href="/img/hou-wb.svg">
|
||||
<link rel="canonical" href="/">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
@@ -39,7 +39,7 @@
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="text-4xl font-bold tracking-tight text-gray-900 sm:text-6xl">How engineers build reliable software.</h1>
|
||||
<h1 class="text-4xl font-bold tracking-tight text-gray-900 sm:text-6xl">Build reliable software.</h1>
|
||||
<p class="mt-6 text-xl sm:text-2xl leading-8 text-gray-600">Monitor, Observe, Debug, Resolve. Everything you need to build reliable software in one open source platform.</p>
|
||||
<div class="mt-10 flex items-center justify-center gap-x-6">
|
||||
<a href="/accounts/register" class="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Get started</a>
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<h2 class="text-3xl font-bold tracking-tight text-gray-900">Monitor Anything.</h2>
|
||||
<p class="mt-4 text-lg text-gray-500">Websites, API, IPv4, IPv6, Docker containers, Servers, VM's or send data inbound (most of these are coming very soon). OneUptime can monitor any resource you have.</p>
|
||||
<p class="mt-4 text-lg text-gray-500">Websites, API, IPv4, IPv6, Docker containers, Servers, VM's or send data inbound. OneUptime can monitor any resource you have.</p>
|
||||
<div class="mt-6">
|
||||
<a href="/accounts/register" class="rounded-md bg-indigo-600 px-3.5 py-1.5 text-base font-semibold leading-7 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 hover:text-white">Get started</a>
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@
|
||||
<div class="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5">
|
||||
<div class="relative grid gap-6 bg-white px-5 py-6 sm:gap-8 sm:p-8">
|
||||
|
||||
<a href="/reference" class="-m-3 flex items-start rounded-lg p-3 hover:bg-gray-50">
|
||||
<a href="/docs" class="-m-3 flex items-start rounded-lg p-3 hover:bg-gray-50">
|
||||
<!-- Heroicon name: outline/lifebuoy -->
|
||||
<svg class="h-6 w-6 flex-shrink-0 text-indigo-600" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
@@ -246,7 +246,21 @@
|
||||
</svg>
|
||||
|
||||
<div class="ml-4">
|
||||
<p class="text-base font-medium text-gray-900">API Documentation</p>
|
||||
<p class="text-base font-medium text-gray-900">Docs</p>
|
||||
<p class="mt-1 text-sm text-gray-500">Learn more about OneUptime by reading our docs.</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="/reference" class="-m-3 flex items-start rounded-lg p-3 hover:bg-gray-50">
|
||||
<!-- Heroicon name: outline/lifebuoy -->
|
||||
<svg class="h-6 w-6 flex-shrink-0 text-indigo-600" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m6.75 7.5 3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0 0 21 18V6a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 6v12a2.25 2.25 0 0 0 2.25 2.25Z" />
|
||||
</svg>
|
||||
|
||||
|
||||
<div class="ml-4">
|
||||
<p class="text-base font-medium text-gray-900">API Reference</p>
|
||||
<p class="mt-1 text-sm text-gray-500">Connect OneUptime with the rest of your software stack.</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -393,140 +393,200 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto mt-16 max-w-2xl rounded-lg ring-1 ring-gray-200 sm:mt-20 lg:mx-0 lg:flex lg:max-w-none">
|
||||
<div class="p-8 sm:p-10 lg:flex-auto">
|
||||
<h3 class="text-2xl font-bold tracking-tight text-gray-900">Active Monitoring</h3>
|
||||
<p class="mt-6 text-base leading-7 text-gray-600">Pricing for montoring website, API, IP addresses and more.
|
||||
We pass on the cost of monitoring to you, so you can focus on your core business activities and maintain
|
||||
uptime. </p>
|
||||
<div class="mt-10 flex items-center gap-x-4">
|
||||
<h4 class="flex-none text-sm font-semibold leading-6 text-indigo-600">What’s included</h4>
|
||||
<div class="h-px flex-auto bg-gray-100"></div>
|
||||
<div class="mx-auto mt-16 max-w-2xl rounded-lg ring-1 ring-gray-200 sm:mt-20 lg:mx-0 lg:max-w-none">
|
||||
<div class="lg:flex">
|
||||
<div class="p-8 sm:p-10 lg:flex-auto">
|
||||
<h3 class="text-2xl font-bold tracking-tight text-gray-900">Active Monitoring</h3>
|
||||
<p class="mt-6 text-base leading-7 text-gray-600">Pricing for montoring website, API, IP addresses and
|
||||
more.
|
||||
We pass on the cost of monitoring to you, so you can focus on your core business activities and maintain
|
||||
uptime. </p>
|
||||
<div class="mt-10 flex items-center gap-x-4">
|
||||
<h4 class="flex-none text-sm font-semibold leading-6 text-indigo-600">What’s included</h4>
|
||||
<div class="h-px flex-auto bg-gray-100"></div>
|
||||
</div>
|
||||
<ul role="list"
|
||||
class="mt-8 grid grid-cols-1 gap-4 text-sm leading-6 text-gray-600 sm:grid-cols-2 sm:gap-6">
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Monitor from multiple locations.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Monitor resource every minute.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Logs stored for 90 days. Custom retention available for enterprise customers.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Set custom criteria based on response body, headers, status, etc.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul role="list" class="mt-8 grid grid-cols-1 gap-4 text-sm leading-6 text-gray-600 sm:grid-cols-2 sm:gap-6">
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Monitor from multiple locations.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Monitor resource every minute.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Logs stored for 90 days. Custom retention available for enterprise customers.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Set custom criteria based on response body, headers, status, etc.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="-mt-2 p-2 lg:mt-0 lg:w-full lg:max-w-md lg:flex-shrink-0">
|
||||
<div
|
||||
class="rounded-lg bg-gray-50 py-10 text-center ring-1 ring-inset ring-gray-900/5 lg:flex lg:flex-col lg:justify-center lg:py-16">
|
||||
<div class="mx-auto max-w-xs px-8">
|
||||
<p class="text-base font-semibold text-gray-600">Only pay for what you use.</p>
|
||||
<p class="mt-6 flex items-baseline justify-center gap-x-2">
|
||||
<div class="text-5xl font-bold tracking-tight text-gray-900">$1</div>
|
||||
<div class="text-sm font-semibold leading-6 tracking-wide text-gray-600">per monitor per month</div>
|
||||
</p>
|
||||
<a href="/accounts/register"
|
||||
class="mt-8 block w-full hover:text-white rounded-md border border-gray-800 bg-gray-800 py-2 text-center text-sm font-semibold text-white hover:bg-gray-900">Free
|
||||
14 day trial</a>
|
||||
<p class="mt-6 text-xs leading-5 text-gray-600">If you have more than 100 monitors, contact us for a
|
||||
discount.</p>
|
||||
<div class="-mt-2 p-2 lg:mt-0 lg:w-full lg:max-w-md lg:flex-shrink-0">
|
||||
<div
|
||||
class="rounded-lg bg-gray-50 py-10 text-center ring-1 ring-inset ring-gray-900/5 lg:flex lg:flex-col lg:justify-center lg:py-16">
|
||||
<div class="mx-auto max-w-xs px-8">
|
||||
<p class="text-base font-semibold text-gray-600">Only pay for what you use.</p>
|
||||
<p class="mt-6 flex items-baseline justify-center gap-x-2">
|
||||
<div id="total-monitor-price" class="text-5xl font-bold tracking-tight text-gray-900">$1</div>
|
||||
<div id="total-monitor-price-description"
|
||||
class="text-sm font-semibold leading-6 tracking-wide text-gray-600">per monitor per month</div>
|
||||
</p>
|
||||
<a href="/accounts/register"
|
||||
class="mt-8 block w-full hover:text-white rounded-md border border-gray-800 bg-gray-800 py-2 text-center text-sm font-semibold text-white hover:bg-gray-900">Free
|
||||
14 day trial</a>
|
||||
<p class="mt-6 text-xs leading-5 text-gray-600">If you have more than 100 monitors, contact us for a
|
||||
discount.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="p-8 sm:p-10">
|
||||
<div class="flex justify-between">
|
||||
<p class="text-base font-semibold text-gray-500">Select Number of Active
|
||||
Monitors</p>
|
||||
<p id="total-number-of-monitors" class="text-base font-semibold text-gray-600">1 Monitor</p>
|
||||
</div>
|
||||
<input id="default-range" type="range" value="1" max="100" min="0" step="1"
|
||||
onchange="updateMonitorPrice(this.value)" oninput="updateMonitorPrice(this.value)"
|
||||
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer ">
|
||||
|
||||
<div>
|
||||
<p id="more-than-100-monitors-message" style="display: none;" class="text-base text-gray-600">If you have
|
||||
more than 100 monitors, contact sales@oneuptime.com for a discount.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mx-auto mt-16 max-w-2xl rounded-lg ring-1 ring-gray-200 sm:mt-20 lg:mx-0 lg:flex lg:max-w-none">
|
||||
<div class="p-8 sm:p-10 lg:flex-auto">
|
||||
<h3 class="text-2xl font-bold tracking-tight text-gray-900">Telemetry: Logs, Traces and Metrics.</h3>
|
||||
<p class="mt-6 text-base leading-7 text-gray-600">We charge $0.10 for every GB of data ingested (for 15 day
|
||||
retention). It's the lowest in the industry and 8X cheaper than DataDog. </p>
|
||||
<div class="mt-10 flex items-center gap-x-4">
|
||||
<h4 class="flex-none text-sm font-semibold leading-6 text-indigo-600">What’s included</h4>
|
||||
<div class="h-px flex-auto bg-gray-100"></div>
|
||||
<div class="mx-auto mt-16 max-w-2xl rounded-lg ring-1 ring-gray-200 sm:mt-20 lg:mx-0 lg:max-w-none">
|
||||
<div class="lg:flex">
|
||||
<div class="p-8 sm:p-10 lg:flex-auto">
|
||||
<h3 class="text-2xl font-bold tracking-tight text-gray-900">Telemetry: Logs, Traces and Metrics.</h3>
|
||||
<p class="mt-6 text-base leading-7 text-gray-600">We charge $0.10 for every GB of data ingested (for 15
|
||||
day
|
||||
retention). It's the lowest in the industry and 8X cheaper than DataDog. </p>
|
||||
<div class="mt-10 flex items-center gap-x-4">
|
||||
<h4 class="flex-none text-sm font-semibold leading-6 text-indigo-600">What’s included</h4>
|
||||
<div class="h-px flex-auto bg-gray-100"></div>
|
||||
</div>
|
||||
<ul role="list"
|
||||
class="mt-8 grid grid-cols-1 gap-4 text-sm leading-6 text-gray-600 sm:grid-cols-2 sm:gap-6">
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Ingest logs, traces and metrics from any service or app.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Custom data retention. We store your data for 15 days by default.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Search TB's of data in seconds.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Native integration with OpenTelemetry.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul role="list" class="mt-8 grid grid-cols-1 gap-4 text-sm leading-6 text-gray-600 sm:grid-cols-2 sm:gap-6">
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Ingest logs, traces and metrics from any service or app.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Custom data retention. We store your data for 15 days by default.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Search TB's of data in seconds.
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Native integration with OpenTelemetry.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="-mt-2 p-2 lg:mt-0 lg:w-full lg:max-w-md lg:flex-shrink-0">
|
||||
<div
|
||||
class="rounded-lg bg-gray-50 py-10 text-center ring-1 ring-inset ring-gray-900/5 lg:flex lg:flex-col lg:justify-center lg:py-16">
|
||||
<div class="mx-auto max-w-xs px-8">
|
||||
<p class="text-base font-semibold text-gray-600">Only pay for what you use.</p>
|
||||
<p class="mt-6 flex items-baseline justify-center gap-x-2">
|
||||
<div class="text-5xl font-bold tracking-tight text-gray-900">$0.10</div>
|
||||
<div class="text-sm font-semibold leading-6 tracking-wide text-gray-600">per GB ingested (15 day
|
||||
retention)</div>
|
||||
</p>
|
||||
<a href="/accounts/register"
|
||||
class="mt-8 block w-full hover:text-white rounded-md border border-gray-800 bg-gray-800 py-2 text-center text-sm font-semibold text-white hover:bg-gray-900">Free
|
||||
14 day trial</a>
|
||||
<p class="mt-6 text-xs leading-5 text-gray-600">If you ingest more than 10 TB of data every month. Contact us for a discount.</p>
|
||||
<div class="-mt-2 p-2 lg:mt-0 lg:w-full lg:max-w-md lg:flex-shrink-0">
|
||||
<div
|
||||
class="rounded-lg bg-gray-50 py-10 text-center ring-1 ring-inset ring-gray-900/5 lg:flex lg:flex-col lg:justify-center lg:py-16">
|
||||
<div class="mx-auto max-w-xs px-8">
|
||||
<p class="text-base font-semibold text-gray-600">Only pay for what you use.</p>
|
||||
<p class="mt-6 flex items-baseline justify-center gap-x-2">
|
||||
<div id="total-telemetry-price" class="text-5xl font-bold tracking-tight text-gray-900">$0.10</div>
|
||||
<div id="total-telemetry-price-description"
|
||||
class="text-sm font-semibold leading-6 tracking-wide text-gray-600">per GB ingested (15 day
|
||||
retention)</div>
|
||||
</p>
|
||||
<a href="/accounts/register"
|
||||
class="mt-8 block w-full hover:text-white rounded-md border border-gray-800 bg-gray-800 py-2 text-center text-sm font-semibold text-white hover:bg-gray-900">Free
|
||||
14 day trial</a>
|
||||
<p class="mt-6 text-xs leading-5 text-gray-600">If you ingest more than 1 TB of data every month.
|
||||
Contact us for a discount.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-8 sm:p-10">
|
||||
<div class="flex justify-between">
|
||||
<p class="text-base font-semibold text-gray-500">GB's ingested per month</p>
|
||||
<p id="total-number-of-gb-ingested" class="text-base font-semibold text-gray-600">1 GB</p>
|
||||
</div>
|
||||
<input id="default-range" type="range" value="1" max="1000" min="0" step="1"
|
||||
onchange="updateTelemetryPrice(this.value, null)" oninput="updateTelemetryPrice(this.value, null)"
|
||||
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer ">
|
||||
<div>
|
||||
<p id="more-than-10-tb-ingested-message" style="display: none;" class="text-base text-gray-600">If you are
|
||||
ingesting more than 1 TB of data every month, contact sales@oneuptime.com for a discount.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-8 sm:p-10 -mt-8">
|
||||
<div class="flex justify-between">
|
||||
<p class="text-base font-semibold text-gray-500">Data Rentention (in days)</p>
|
||||
<p id="data-rentention-in-days" class="text-base font-semibold text-gray-600">15 Days</p>
|
||||
</div>
|
||||
<input id="default-range" type="range" value="15" max="180" min="0" step="1"
|
||||
onchange="updateTelemetryPrice(null, this.value)" oninput="updateTelemetryPrice(null,this.value)"
|
||||
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer ">
|
||||
<div>
|
||||
<p id="more-than-6-months-rentention" style="display: none;" class="text-base text-gray-600">If you're
|
||||
looking for data rention of more than 6 months. contact sales@oneuptime.com for a discount. </p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -553,11 +613,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1336,6 +1391,103 @@
|
||||
|
||||
|
||||
|
||||
function updateMonitorPrice(monitorCount) {
|
||||
|
||||
if (typeof monitorCount !== 'number') {
|
||||
monitorCount = parseInt(monitorCount);
|
||||
}
|
||||
|
||||
const perMonitorPriceInUSDPerMonth = 1;
|
||||
|
||||
const monthlyPrice = monitorCount * perMonitorPriceInUSDPerMonth;
|
||||
|
||||
// set the price in the UI
|
||||
document.getElementById("total-monitor-price").innerHTML = `$${monthlyPrice}`;
|
||||
|
||||
// set description
|
||||
document.getElementById("total-monitor-price-description").innerHTML = `for ${monitorCount} monitors per month.`;
|
||||
|
||||
// update monitor count - total-number-of-monitors
|
||||
document.getElementById("total-number-of-monitors").innerHTML = monitorCount + " Monitors";
|
||||
|
||||
if (monitorCount <= 1) {
|
||||
document.getElementById("total-number-of-monitors").innerHTML = monitorCount + " Monitor";
|
||||
}
|
||||
|
||||
if (monitorCount === 100) {
|
||||
// show more than 100 monitors message
|
||||
document.getElementById("more-than-100-monitors-message").style.display = "block";
|
||||
} else {
|
||||
// hide more than 100 monitors
|
||||
document.getElementById("more-than-100-monitors-message").style.display = "none";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let numberOfGb = 1;
|
||||
let numberOfDays = 15;
|
||||
|
||||
function updateTelemetryPrice(updatedNumberOfGb, updatedNumberOfDays) {
|
||||
|
||||
if (typeof updatedNumberOfGb !== 'number' && updatedNumberOfGb) {
|
||||
updatedNumberOfGb = parseInt(updatedNumberOfGb);
|
||||
}
|
||||
|
||||
if (typeof updatedNumberOfDays !== 'number' && updatedNumberOfDays) {
|
||||
updatedNumberOfDays = parseInt(updatedNumberOfDays);
|
||||
}
|
||||
|
||||
if (updatedNumberOfGb) {
|
||||
numberOfGb = updatedNumberOfGb;
|
||||
}
|
||||
|
||||
if (updatedNumberOfDays) {
|
||||
numberOfDays = updatedNumberOfDays;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const perGbPerDayPrice = 0.10 / 15; // 0.10 USD per GB for 15 days.
|
||||
|
||||
const monthlyPrice = numberOfGb * numberOfDays * perGbPerDayPrice;
|
||||
|
||||
|
||||
// set the price in the UI
|
||||
|
||||
document.getElementById("total-telemetry-price").innerHTML = `$${monthlyPrice.toFixed(2)}`;
|
||||
|
||||
// set description
|
||||
|
||||
document.getElementById("total-telemetry-price-description").innerHTML = `for ${numberOfGb} GB ingested, retained for ${numberOfDays} days.`;
|
||||
|
||||
// update telemetry count - total-number-of-telemetry
|
||||
|
||||
document.getElementById("total-number-of-gb-ingested").innerHTML = numberOfGb + " GB";
|
||||
document.getElementById("data-rentention-in-days").innerHTML = numberOfDays + " Days";
|
||||
|
||||
|
||||
|
||||
if (numberOfGb === 1000) {
|
||||
// show more than 100 monitors message
|
||||
document.getElementById("more-than-10-tb-ingested-message").style.display = "block";
|
||||
} else {
|
||||
// hide more than 100 monitors
|
||||
document.getElementById("more-than-10-tb-ingested-message").style.display = "none";
|
||||
}
|
||||
|
||||
|
||||
if (numberOfDays === 180) {
|
||||
// show more than 100 monitors message
|
||||
document.getElementById("more-than-6-months-rentention").style.display = "block";
|
||||
} else {
|
||||
// hide more than 100 monitors
|
||||
document.getElementById("more-than-6-months-rentention").style.display = "none";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<%- include('footer') -%>
|
||||
|
||||
@@ -26,7 +26,6 @@ import OneUptimeDate from 'Common/Types/Date';
|
||||
import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import logger from 'CommonServer/Utils/Logger';
|
||||
import PartialEntity from 'Common/Types/Database/PartialEntity';
|
||||
import Email from 'Common/Types/Email';
|
||||
import Name from 'Common/Types/Name';
|
||||
import AuthenticationEmail from '../Utils/AuthenticationEmail';
|
||||
@@ -60,7 +59,10 @@ router.post(
|
||||
const data: JSONObject = req.body['data'];
|
||||
|
||||
/* Creating a type that is a partial of the TBaseModel type. */
|
||||
const partialUser: PartialEntity<User> = data;
|
||||
const partialUser: User = BaseModel.fromJSON(
|
||||
data as JSONObject,
|
||||
User
|
||||
) as User;
|
||||
|
||||
if (IsBillingEnabled) {
|
||||
//ALERT: Delete data.role so user don't accidently sign up as master-admin from the API.
|
||||
@@ -106,10 +108,14 @@ router.post(
|
||||
let savedUser: User | null = null;
|
||||
|
||||
if (alreadySavedUser) {
|
||||
//@ts-ignore
|
||||
savedUser = await UserService.updateOneByIdAndFetch({
|
||||
id: alreadySavedUser.id!,
|
||||
data: partialUser,
|
||||
data: {
|
||||
password: partialUser.password!,
|
||||
name: partialUser.name!,
|
||||
companyPhoneNumber: partialUser.companyPhoneNumber!,
|
||||
companyName: partialUser.companyName!,
|
||||
},
|
||||
select: {
|
||||
email: true,
|
||||
_id: true,
|
||||
@@ -121,10 +127,7 @@ router.post(
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const user: User = BaseModel.fromJSON(
|
||||
partialUser as JSONObject,
|
||||
User
|
||||
) as User;
|
||||
const user: User = partialUser;
|
||||
|
||||
savedUser = await UserService.create({
|
||||
data: user,
|
||||
@@ -179,10 +182,18 @@ router.post(
|
||||
savedUser.id!
|
||||
);
|
||||
|
||||
const token: string = JSONWebToken.sign(
|
||||
savedUser,
|
||||
OneUptimeDate.getSecondsInDays(new PositiveNumber(30))
|
||||
);
|
||||
const token: string = JSONWebToken.signUserLoginToken({
|
||||
tokenData: {
|
||||
userId: savedUser.id!,
|
||||
email: savedUser.email!,
|
||||
name: savedUser.name!,
|
||||
isMasterAdmin: savedUser.isMasterAdmin!,
|
||||
isGlobalLogin: true, // This is a general login without SSO. So, we will set this to true. This will give access to all the projects that dont require SSO.
|
||||
},
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(
|
||||
new PositiveNumber(30)
|
||||
),
|
||||
});
|
||||
|
||||
// Set a cookie with token.
|
||||
CookieUtil.setCookie(res, CookieUtil.getUserTokenKey(), token, {
|
||||
@@ -272,7 +283,7 @@ router.post(
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
}
|
||||
|
||||
return Response.sendErrorResponse(
|
||||
@@ -387,7 +398,7 @@ router.post(
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -479,7 +490,7 @@ router.post(
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -496,7 +507,7 @@ router.post(
|
||||
try {
|
||||
CookieUtil.removeAllCookies(req, res);
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -570,10 +581,18 @@ router.post(
|
||||
alreadySavedUser.password.toString() ===
|
||||
user.password!.toString()
|
||||
) {
|
||||
const token: string = JSONWebToken.sign(
|
||||
alreadySavedUser,
|
||||
OneUptimeDate.getSecondsInDays(new PositiveNumber(30))
|
||||
);
|
||||
const token: string = JSONWebToken.signUserLoginToken({
|
||||
tokenData: {
|
||||
userId: alreadySavedUser.id!,
|
||||
email: alreadySavedUser.email!,
|
||||
name: alreadySavedUser.name!,
|
||||
isMasterAdmin: alreadySavedUser.isMasterAdmin!,
|
||||
isGlobalLogin: true, // This is a general login without SSO. So, we will set this to true. This will give access to all the projects that dont require SSO.
|
||||
},
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(
|
||||
new PositiveNumber(30)
|
||||
),
|
||||
});
|
||||
|
||||
// Set a cookie with token.
|
||||
CookieUtil.setCookie(
|
||||
|
||||
@@ -62,10 +62,10 @@ router.post(
|
||||
|
||||
// if found then generate a token and return it.
|
||||
|
||||
const token: string = JSONWebToken.sign(
|
||||
{ resellerId: resellerId },
|
||||
OneUptimeDate.getDayInSeconds(365)
|
||||
);
|
||||
const token: string = JSONWebToken.sign({
|
||||
data: { resellerId: resellerId },
|
||||
expiresInSeconds: OneUptimeDate.getDayInSeconds(365),
|
||||
});
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, {
|
||||
access: token,
|
||||
|
||||
@@ -32,6 +32,7 @@ import Hostname from 'Common/Types/API/Hostname';
|
||||
import Protocol from 'Common/Types/API/Protocol';
|
||||
import DatabaseConfig from 'CommonServer/DatabaseConfig';
|
||||
import CookieUtil from 'CommonServer/Utils/Cookie';
|
||||
import { Host, HttpProtocol } from 'CommonServer/EnvironmentConfig';
|
||||
|
||||
const router: ExpressRouter = Express.getRouter();
|
||||
|
||||
@@ -67,7 +68,10 @@ router.get(
|
||||
isEnabled: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
signOnURL: true,
|
||||
issuerURL: true,
|
||||
projectId: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
@@ -92,310 +96,374 @@ router.get(
|
||||
);
|
||||
}
|
||||
|
||||
return Response.redirect(req, res, projectSSO.signOnURL);
|
||||
if (!projectSSO.issuerURL) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Issuer not found')
|
||||
);
|
||||
}
|
||||
|
||||
const samlRequestUrl: URL = SSOUtil.createSAMLRequestUrl({
|
||||
acsUrl: URL.fromString(
|
||||
`${HttpProtocol}${Host}/identity/idp-login/${projectSSO.projectId?.toString()}/${projectSSO.id?.toString()}`
|
||||
),
|
||||
signOnUrl: projectSSO.signOnURL!,
|
||||
issuerUrl: URL.fromString(
|
||||
`${HttpProtocol}${Host}/${projectSSO.projectId?.toString()}/${projectSSO.id?.toString()}`
|
||||
),
|
||||
});
|
||||
|
||||
return Response.redirect(req, res, samlRequestUrl);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/idp-login/:projectId/:projectSsoId',
|
||||
async (req: ExpressRequest, res: ExpressResponse): Promise<void> => {
|
||||
return await loginUserWithSso(req, res);
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/idp-login/:projectId/:projectSsoId',
|
||||
async (req: ExpressRequest, res: ExpressResponse): Promise<void> => {
|
||||
try {
|
||||
const samlResponseBase64: string = req.body.SAMLResponse;
|
||||
return await loginUserWithSso(req, res);
|
||||
}
|
||||
);
|
||||
|
||||
const samlResponse: string = Buffer.from(
|
||||
samlResponseBase64,
|
||||
'base64'
|
||||
).toString();
|
||||
type LoginUserWithSsoFunction = (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
) => Promise<void>;
|
||||
|
||||
const response: JSONObject = await xml2js.parseStringPromise(
|
||||
samlResponse
|
||||
const loginUserWithSso: LoginUserWithSsoFunction = async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const samlResponseBase64: string = req.body.SAMLResponse;
|
||||
|
||||
if (!samlResponseBase64) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('SAMLResponse not found')
|
||||
);
|
||||
}
|
||||
|
||||
let issuerUrl: string = '';
|
||||
let email: Email | null = null;
|
||||
const samlResponse: string = Buffer.from(
|
||||
samlResponseBase64,
|
||||
'base64'
|
||||
).toString();
|
||||
|
||||
if (!req.params['projectId']) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Project ID not found')
|
||||
);
|
||||
}
|
||||
const response: JSONObject = await xml2js.parseStringPromise(
|
||||
samlResponse
|
||||
);
|
||||
|
||||
if (!req.params['projectSsoId']) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Project SSO ID not found')
|
||||
);
|
||||
}
|
||||
let issuerUrl: string = '';
|
||||
let email: Email | null = null;
|
||||
|
||||
const projectSSO: ProjectSSO | null =
|
||||
await ProjectSSOService.findOneBy({
|
||||
query: {
|
||||
projectId: new ObjectID(req.params['projectId']),
|
||||
_id: req.params['projectSsoId'],
|
||||
isEnabled: true,
|
||||
},
|
||||
select: {
|
||||
signOnURL: true,
|
||||
issuerURL: true,
|
||||
publicCertificate: true,
|
||||
teams: {
|
||||
_id: true,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
if (!req.params['projectId']) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Project ID not found')
|
||||
);
|
||||
}
|
||||
|
||||
if (!projectSSO) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('SSO Config not found')
|
||||
);
|
||||
}
|
||||
if (!req.params['projectSsoId']) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Project SSO ID not found')
|
||||
);
|
||||
}
|
||||
|
||||
// redirect to Identity Provider.
|
||||
|
||||
if (!projectSSO.issuerURL) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Issuer URL not found')
|
||||
);
|
||||
}
|
||||
|
||||
// redirect to Identity Provider.
|
||||
|
||||
if (!projectSSO.signOnURL) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Sign on URL not found')
|
||||
);
|
||||
}
|
||||
|
||||
if (!projectSSO.publicCertificate) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Public Certificate not found')
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
SSOUtil.isPayloadValid(response);
|
||||
|
||||
if (
|
||||
!SSOUtil.isSignatureValid(
|
||||
samlResponse,
|
||||
projectSSO.publicCertificate
|
||||
)
|
||||
) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException(
|
||||
'Signature is not valid or Public Certificate configured with this SSO provider is not valid'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
issuerUrl = SSOUtil.getIssuer(response);
|
||||
email = SSOUtil.getEmail(response);
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Exception) {
|
||||
return Response.sendErrorResponse(req, res, err);
|
||||
}
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new ServerException()
|
||||
);
|
||||
}
|
||||
|
||||
if (projectSSO.issuerURL.toString() !== issuerUrl) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Issuer URL does not match')
|
||||
);
|
||||
}
|
||||
|
||||
// Check if he already belongs to the project, If he does - then log in.
|
||||
|
||||
let alreadySavedUser: User | null = await UserService.findOneBy({
|
||||
query: { email: email },
|
||||
select: {
|
||||
_id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
isMasterAdmin: true,
|
||||
isEmailVerified: true,
|
||||
profilePictureId: true,
|
||||
const projectSSO: ProjectSSO | null = await ProjectSSOService.findOneBy(
|
||||
{
|
||||
query: {
|
||||
projectId: new ObjectID(req.params['projectId']),
|
||||
_id: req.params['projectSsoId'],
|
||||
isEnabled: true,
|
||||
},
|
||||
select: {
|
||||
signOnURL: true,
|
||||
issuerURL: true,
|
||||
publicCertificate: true,
|
||||
teams: {
|
||||
_id: true,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!projectSSO) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('SSO Config not found')
|
||||
);
|
||||
}
|
||||
|
||||
// redirect to Identity Provider.
|
||||
|
||||
if (!projectSSO.issuerURL) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Issuer URL not found')
|
||||
);
|
||||
}
|
||||
|
||||
// redirect to Identity Provider.
|
||||
|
||||
if (!projectSSO.signOnURL) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Sign on URL not found')
|
||||
);
|
||||
}
|
||||
|
||||
if (!projectSSO.publicCertificate) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Public Certificate not found')
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
SSOUtil.isPayloadValid(response);
|
||||
|
||||
if (
|
||||
!SSOUtil.isSignatureValid(
|
||||
samlResponse,
|
||||
projectSSO.publicCertificate
|
||||
)
|
||||
) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException(
|
||||
'Signature is not valid or Public Certificate configured with this SSO provider is not valid'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
issuerUrl = SSOUtil.getIssuer(response);
|
||||
email = SSOUtil.getEmail(response);
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Exception) {
|
||||
return Response.sendErrorResponse(req, res, err);
|
||||
}
|
||||
return Response.sendErrorResponse(req, res, new ServerException());
|
||||
}
|
||||
|
||||
if (projectSSO.issuerURL.toString() !== issuerUrl) {
|
||||
logger.error(
|
||||
'Issuer URL does not match. It should be ' +
|
||||
projectSSO.issuerURL.toString() +
|
||||
' but it is ' +
|
||||
issuerUrl.toString()
|
||||
);
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException('Issuer URL does not match')
|
||||
);
|
||||
}
|
||||
|
||||
// Check if he already belongs to the project, If he does - then log in.
|
||||
|
||||
let alreadySavedUser: User | null = await UserService.findOneBy({
|
||||
query: { email: email },
|
||||
select: {
|
||||
_id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
isMasterAdmin: true,
|
||||
isEmailVerified: true,
|
||||
profilePictureId: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
let isNewUser: boolean = false;
|
||||
|
||||
if (!alreadySavedUser) {
|
||||
// this should never happen because user is logged in before he signs in with SSO UNLESS he initiates the login though the IDP.
|
||||
|
||||
/// Create a user.
|
||||
|
||||
alreadySavedUser = await UserService.createByEmail({
|
||||
email,
|
||||
isEmailVerified: true,
|
||||
generateRandomPassword: true,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
let isNewUser: boolean = false;
|
||||
isNewUser = true;
|
||||
}
|
||||
|
||||
if (!alreadySavedUser) {
|
||||
// this should never happen because user is logged in before he signs in with SSO UNLESS he initiates the login though the IDP.
|
||||
// If he does not then add him to teams that he should belong and log in.
|
||||
// This should never happen because email is verified before he logs in with SSO.
|
||||
if (!alreadySavedUser.isEmailVerified && !isNewUser) {
|
||||
await AuthenticationEmail.sendVerificationEmail(alreadySavedUser!);
|
||||
|
||||
/// Create a user.
|
||||
|
||||
alreadySavedUser = await UserService.createByEmail(email, {
|
||||
isRoot: true,
|
||||
});
|
||||
|
||||
isNewUser = true;
|
||||
}
|
||||
|
||||
// If he does not then add him to teams that he should belong and log in.
|
||||
if (!alreadySavedUser.isEmailVerified && !isNewUser) {
|
||||
await AuthenticationEmail.sendVerificationEmail(
|
||||
alreadySavedUser
|
||||
);
|
||||
|
||||
return Response.render(
|
||||
req,
|
||||
res,
|
||||
'/usr/src/app/FeatureSet/Identity/Views/Message.ejs',
|
||||
{
|
||||
title: 'Email not verified.',
|
||||
message:
|
||||
'Email is not verified. We have sent you an email with the verification link. Please do not forget to check spam.',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// check if the user already belongs to the project
|
||||
const teamMemberCount: PositiveNumber =
|
||||
await TeamMemberService.countBy({
|
||||
query: {
|
||||
projectId: new ObjectID(
|
||||
req.params['projectId'] as string
|
||||
),
|
||||
userId: alreadySavedUser.id!,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (teamMemberCount.toNumber() === 0) {
|
||||
// user not in project, add him to default teams.
|
||||
|
||||
if (!projectSSO.teams || projectSSO.teams.length === 0) {
|
||||
return Response.render(
|
||||
req,
|
||||
res,
|
||||
'/usr/src/app/FeatureSet/Identity/Views/Message.ejs',
|
||||
{
|
||||
title: 'No teams added.',
|
||||
message:
|
||||
'No teams have been added to this SSO config. Please contact your admin and have default teams added.',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
for (const team of projectSSO.teams) {
|
||||
// add user to team
|
||||
let teamMember: TeamMember = new TeamMember();
|
||||
teamMember.projectId = new ObjectID(
|
||||
req.params['projectId'] as string
|
||||
);
|
||||
teamMember.userId = alreadySavedUser.id!;
|
||||
teamMember.hasAcceptedInvitation = true;
|
||||
teamMember.invitationAcceptedAt =
|
||||
OneUptimeDate.getCurrentDate();
|
||||
teamMember.teamId = team.id!;
|
||||
|
||||
teamMember = await TeamMemberService.create({
|
||||
data: teamMember,
|
||||
props: {
|
||||
isRoot: true,
|
||||
ignoreHooks: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isNewUser) {
|
||||
return Response.render(
|
||||
req,
|
||||
res,
|
||||
'/usr/src/app/FeatureSet/Identity/Views/Message.ejs',
|
||||
{
|
||||
title: 'You have not signed up so far.',
|
||||
message:
|
||||
'You need to sign up for an account on OneUptime with this email:' +
|
||||
email.toString() +
|
||||
'. Once you have signed up, you can use SSO to log in to your project.',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const projectId: ObjectID = new ObjectID(
|
||||
req.params['projectId'] as string
|
||||
);
|
||||
|
||||
const token: string = JSONWebToken.sign(
|
||||
{
|
||||
userId: alreadySavedUser.id!,
|
||||
projectId: projectId,
|
||||
email: email,
|
||||
isMasterAdmin: false,
|
||||
},
|
||||
OneUptimeDate.getSecondsInDays(new PositiveNumber(30))
|
||||
);
|
||||
|
||||
// Refresh Permissions for this user here.
|
||||
await AccessTokenService.refreshUserAllPermissions(
|
||||
alreadySavedUser.id!
|
||||
);
|
||||
|
||||
const host: Hostname = await DatabaseConfig.getHost();
|
||||
const httpProtocol: Protocol =
|
||||
await DatabaseConfig.getHttpProtocol();
|
||||
|
||||
CookieUtil.setCookie(
|
||||
res,
|
||||
CookieUtil.getUserSSOKey(projectId),
|
||||
token,
|
||||
{
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(
|
||||
new PositiveNumber(30)
|
||||
),
|
||||
httpOnly: true,
|
||||
}
|
||||
);
|
||||
|
||||
return Response.redirect(
|
||||
return Response.render(
|
||||
req,
|
||||
res,
|
||||
new URL(
|
||||
httpProtocol,
|
||||
host,
|
||||
new Route(DashboardRoute.toString()).addRoute(
|
||||
'/' + req.params['projectId']
|
||||
),
|
||||
'sso_token=' + token
|
||||
)
|
||||
'/usr/src/app/FeatureSet/Identity/Views/Message.ejs',
|
||||
{
|
||||
title: 'Email not verified.',
|
||||
message:
|
||||
'Email is not verified. We have sent you an email with the verification link. Please do not forget to check spam.',
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
Response.sendErrorResponse(req, res, new ServerException());
|
||||
}
|
||||
|
||||
// check if the user already belongs to the project
|
||||
const teamMemberCount: PositiveNumber = await TeamMemberService.countBy(
|
||||
{
|
||||
query: {
|
||||
projectId: new ObjectID(req.params['projectId'] as string),
|
||||
userId: alreadySavedUser!.id!,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (teamMemberCount.toNumber() === 0) {
|
||||
// user not in project, add him to default teams.
|
||||
|
||||
if (!projectSSO.teams || projectSSO.teams.length === 0) {
|
||||
return Response.render(
|
||||
req,
|
||||
res,
|
||||
'/usr/src/app/FeatureSet/Identity/Views/Message.ejs',
|
||||
{
|
||||
title: 'No teams added.',
|
||||
message:
|
||||
'No teams have been added to this SSO config. Please contact your admin and have default teams added.',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
for (const team of projectSSO.teams) {
|
||||
// add user to team
|
||||
let teamMember: TeamMember = new TeamMember();
|
||||
teamMember.projectId = new ObjectID(
|
||||
req.params['projectId'] as string
|
||||
);
|
||||
teamMember.userId = alreadySavedUser.id!;
|
||||
teamMember.hasAcceptedInvitation = true;
|
||||
teamMember.invitationAcceptedAt =
|
||||
OneUptimeDate.getCurrentDate();
|
||||
teamMember.teamId = team.id!;
|
||||
|
||||
teamMember = await TeamMemberService.create({
|
||||
data: teamMember,
|
||||
props: {
|
||||
isRoot: true,
|
||||
ignoreHooks: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const projectId: ObjectID = new ObjectID(
|
||||
req.params['projectId'] as string
|
||||
);
|
||||
|
||||
const ssoToken: string = JSONWebToken.sign({
|
||||
data: {
|
||||
userId: alreadySavedUser.id!,
|
||||
projectId: projectId,
|
||||
name: alreadySavedUser.name!,
|
||||
email: email,
|
||||
isMasterAdmin: false,
|
||||
isGeneralLogin: false,
|
||||
},
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(
|
||||
new PositiveNumber(30)
|
||||
),
|
||||
});
|
||||
|
||||
const oneUptimeToken: string = JSONWebToken.signUserLoginToken({
|
||||
tokenData: {
|
||||
userId: alreadySavedUser.id!,
|
||||
email: alreadySavedUser.email!,
|
||||
name: alreadySavedUser.name!,
|
||||
isMasterAdmin: alreadySavedUser.isMasterAdmin!,
|
||||
isGlobalLogin: false, // This is a general login without SSO. So, we will set this to false. This will give access to all the projects that dont require SSO.
|
||||
},
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(
|
||||
new PositiveNumber(30)
|
||||
),
|
||||
});
|
||||
|
||||
// Set a cookie with token.
|
||||
CookieUtil.setCookie(
|
||||
res,
|
||||
CookieUtil.getUserTokenKey(),
|
||||
oneUptimeToken,
|
||||
{
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(
|
||||
new PositiveNumber(30)
|
||||
),
|
||||
httpOnly: true,
|
||||
}
|
||||
);
|
||||
|
||||
CookieUtil.setCookie(
|
||||
res,
|
||||
CookieUtil.getUserSSOKey(projectId),
|
||||
ssoToken,
|
||||
{
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(
|
||||
new PositiveNumber(30)
|
||||
),
|
||||
httpOnly: true,
|
||||
}
|
||||
);
|
||||
|
||||
// Refresh Permissions for this user here.
|
||||
await AccessTokenService.refreshUserAllPermissions(
|
||||
alreadySavedUser.id!
|
||||
);
|
||||
|
||||
const host: Hostname = await DatabaseConfig.getHost();
|
||||
const httpProtocol: Protocol = await DatabaseConfig.getHttpProtocol();
|
||||
|
||||
return Response.redirect(
|
||||
req,
|
||||
res,
|
||||
new URL(
|
||||
httpProtocol,
|
||||
host,
|
||||
new Route(DashboardRoute.toString()).addRoute(
|
||||
'/' + req.params['projectId']
|
||||
)
|
||||
)
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
Response.sendErrorResponse(req, res, new ServerException());
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -51,7 +51,7 @@ router.post(
|
||||
CookieUtil.getUserTokenKey(statusPageId)
|
||||
); // remove the cookie.
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -178,7 +178,7 @@ router.post(
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
}
|
||||
|
||||
throw new BadDataException(
|
||||
@@ -320,7 +320,7 @@ router.post(
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -389,10 +389,12 @@ router.post(
|
||||
});
|
||||
|
||||
if (alreadySavedUser) {
|
||||
const token: string = JSONWebToken.sign(
|
||||
alreadySavedUser,
|
||||
OneUptimeDate.getSecondsInDays(new PositiveNumber(30))
|
||||
);
|
||||
const token: string = JSONWebToken.sign({
|
||||
data: alreadySavedUser,
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(
|
||||
new PositiveNumber(30)
|
||||
),
|
||||
});
|
||||
|
||||
CookieUtil.setCookie(
|
||||
res,
|
||||
|
||||
@@ -25,6 +25,7 @@ import StatusPagePrivateUserService from 'CommonServer/Services/StatusPagePrivat
|
||||
import HashedString from 'Common/Types/HashedString';
|
||||
import StatusPageService from 'CommonServer/Services/StatusPageService';
|
||||
import CookieUtil from 'CommonServer/Utils/Cookie';
|
||||
import { Host, HttpProtocol } from 'CommonServer/EnvironmentConfig';
|
||||
|
||||
const router: ExpressRouter = Express.getRouter();
|
||||
|
||||
@@ -65,6 +66,8 @@ router.get(
|
||||
},
|
||||
select: {
|
||||
signOnURL: true,
|
||||
statusPageId: true,
|
||||
_id: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
@@ -89,7 +92,17 @@ router.get(
|
||||
);
|
||||
}
|
||||
|
||||
return Response.redirect(req, res, statusPageSSO.signOnURL);
|
||||
const samlRequestUrl: URL = SSOUtil.createSAMLRequestUrl({
|
||||
acsUrl: URL.fromString(
|
||||
`${HttpProtocol}${Host}/identity/status-page-idp-login/${statusPageSSO.statusPageId?.toString()}/${statusPageSSO.id?.toString()}`
|
||||
),
|
||||
signOnUrl: statusPageSSO.signOnURL!,
|
||||
issuerUrl: URL.fromString(
|
||||
`${HttpProtocol}${Host}/${statusPageSSO.statusPageId?.toString()}/${statusPageSSO.id?.toString()}`
|
||||
),
|
||||
});
|
||||
|
||||
return Response.redirect(req, res, samlRequestUrl);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -270,10 +283,12 @@ router.post(
|
||||
});
|
||||
}
|
||||
|
||||
const token: string = JSONWebToken.sign(
|
||||
alreadySavedUser,
|
||||
OneUptimeDate.getSecondsInDays(new PositiveNumber(30))
|
||||
);
|
||||
const token: string = JSONWebToken.sign({
|
||||
data: alreadySavedUser,
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(
|
||||
new PositiveNumber(30)
|
||||
),
|
||||
});
|
||||
|
||||
CookieUtil.setCookie(
|
||||
res,
|
||||
|
||||
@@ -5,20 +5,27 @@ import SsoAPI from './API/SSO';
|
||||
import ResellerAPI from './API/Reseller';
|
||||
import StatusPageSsoAPI from './API/StatusPageSSO';
|
||||
import StatusPageAuthenticationAPI from './API/StatusPageAuthentication';
|
||||
import FeatureSet from 'CommonServer/Types/FeatureSet';
|
||||
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
const IdentityFeatureSet: FeatureSet = {
|
||||
init: async (): Promise<void> => {
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
|
||||
const APP_NAME: string = 'api/identity';
|
||||
const APP_NAME: string = 'api/identity';
|
||||
|
||||
app.use([`/${APP_NAME}`, '/'], AuthenticationAPI);
|
||||
app.use([`/${APP_NAME}`, '/'], AuthenticationAPI);
|
||||
|
||||
app.use([`/${APP_NAME}`, '/'], ResellerAPI);
|
||||
app.use([`/${APP_NAME}`, '/'], ResellerAPI);
|
||||
|
||||
app.use([`/${APP_NAME}`, '/'], SsoAPI);
|
||||
app.use([`/${APP_NAME}`, '/'], SsoAPI);
|
||||
|
||||
app.use([`/${APP_NAME}`, '/'], StatusPageSsoAPI);
|
||||
app.use([`/${APP_NAME}`, '/'], StatusPageSsoAPI);
|
||||
|
||||
app.use(
|
||||
[`/${APP_NAME}/status-page`, '/status-page'],
|
||||
StatusPageAuthenticationAPI
|
||||
);
|
||||
app.use(
|
||||
[`/${APP_NAME}/status-page`, '/status-page'],
|
||||
StatusPageAuthenticationAPI
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default IdentityFeatureSet;
|
||||
|
||||
@@ -4,8 +4,34 @@ import Email from 'Common/Types/Email';
|
||||
import xmldom from 'xmldom';
|
||||
import xmlCrypto, { FileKeyInfo } from 'xml-crypto';
|
||||
import logger from 'CommonServer/Utils/Logger';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import Text from 'Common/Types/Text';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import zlib from 'zlib';
|
||||
|
||||
export default class SSOUtil {
|
||||
public static createSAMLRequestUrl(data: {
|
||||
acsUrl: URL;
|
||||
signOnUrl: URL;
|
||||
issuerUrl: URL;
|
||||
}): URL {
|
||||
const { acsUrl, signOnUrl } = data;
|
||||
|
||||
const samlRequest: string = `<samlp:AuthnRequest xmlns="urn:oasis:names:tc:SAML:2.0:metadata" ID="${Text.generateRandomText(
|
||||
10
|
||||
).toUpperCase()}" Version="2.0" IssueInstant="${OneUptimeDate.getCurrentDate().toISOString()}" IsPassive="false" AssertionConsumerServiceURL="${acsUrl.toString()}" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ForceAuthn="false"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">${data.issuerUrl.toString()}</Issuer></samlp:AuthnRequest>`;
|
||||
|
||||
const deflated: Buffer = zlib.deflateRawSync(samlRequest);
|
||||
|
||||
const base64Encoded: string = deflated.toString('base64');
|
||||
|
||||
return URL.fromString(signOnUrl.toString()).addQueryParam(
|
||||
'SAMLRequest',
|
||||
base64Encoded,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public static isPayloadValid(payload: JSONObject): void {
|
||||
if (
|
||||
!payload['saml2p:Response'] &&
|
||||
@@ -18,11 +44,13 @@ export default class SSOUtil {
|
||||
payload =
|
||||
(payload['saml2p:Response'] as JSONObject) ||
|
||||
(payload['samlp:Response'] as JSONObject) ||
|
||||
(payload['samlp:Response'] as JSONObject);
|
||||
(payload['samlp:Response'] as JSONObject) ||
|
||||
(payload['Response'] as JSONObject);
|
||||
|
||||
const issuers: JSONArray =
|
||||
(payload['saml2:Issuer'] as JSONArray) ||
|
||||
(payload['saml:Issuer'] as JSONArray);
|
||||
(payload['saml:Issuer'] as JSONArray) ||
|
||||
(payload['Issuer'] as JSONArray);
|
||||
|
||||
if (issuers.length === 0) {
|
||||
throw new BadRequestException('Issuers not found');
|
||||
@@ -47,7 +75,8 @@ export default class SSOUtil {
|
||||
|
||||
const samlAssertion: JSONArray =
|
||||
(payload['saml2:Assertion'] as JSONArray) ||
|
||||
(payload['saml:Assertion'] as JSONArray);
|
||||
(payload['saml:Assertion'] as JSONArray) ||
|
||||
(payload['Assertion'] as JSONArray);
|
||||
|
||||
if (!samlAssertion || samlAssertion.length === 0) {
|
||||
throw new BadRequestException('SAML Assertion not found');
|
||||
@@ -55,7 +84,8 @@ export default class SSOUtil {
|
||||
|
||||
const samlSubject: JSONArray =
|
||||
((samlAssertion[0] as JSONObject)['saml2:Subject'] as JSONArray) ||
|
||||
((samlAssertion[0] as JSONObject)['saml:Subject'] as JSONArray);
|
||||
((samlAssertion[0] as JSONObject)['saml:Subject'] as JSONArray) ||
|
||||
((samlAssertion[0] as JSONObject)['Subject'] as JSONArray);
|
||||
|
||||
if (!samlSubject || samlSubject.length === 0) {
|
||||
throw new BadRequestException('SAML Subject not found');
|
||||
@@ -63,7 +93,8 @@ export default class SSOUtil {
|
||||
|
||||
const samlNameId: JSONArray =
|
||||
((samlSubject[0] as JSONObject)['saml2:NameID'] as JSONArray) ||
|
||||
((samlSubject[0] as JSONObject)['saml:NameID'] as JSONArray);
|
||||
((samlSubject[0] as JSONObject)['saml:NameID'] as JSONArray) ||
|
||||
((samlSubject[0] as JSONObject)['NameID'] as JSONArray);
|
||||
|
||||
if (!samlNameId || samlNameId.length === 0) {
|
||||
throw new BadRequestException('SAML NAME ID not found');
|
||||
@@ -120,11 +151,13 @@ export default class SSOUtil {
|
||||
|
||||
payload =
|
||||
(payload['saml2p:Response'] as JSONObject) ||
|
||||
(payload['samlp:Response'] as JSONObject);
|
||||
(payload['samlp:Response'] as JSONObject) ||
|
||||
(payload['Response'] as JSONObject);
|
||||
|
||||
const samlAssertion: JSONArray =
|
||||
(payload['saml2:Assertion'] as JSONArray) ||
|
||||
(payload['saml:Assertion'] as JSONArray);
|
||||
(payload['saml:Assertion'] as JSONArray) ||
|
||||
(payload['Assertion'] as JSONArray);
|
||||
|
||||
if (!samlAssertion || samlAssertion.length === 0) {
|
||||
throw new BadRequestException('SAML Assertion not found');
|
||||
@@ -132,7 +165,8 @@ export default class SSOUtil {
|
||||
|
||||
const samlSubject: JSONArray =
|
||||
((samlAssertion[0] as JSONObject)['saml2:Subject'] as JSONArray) ||
|
||||
((samlAssertion[0] as JSONObject)['saml:Subject'] as JSONArray);
|
||||
((samlAssertion[0] as JSONObject)['saml:Subject'] as JSONArray) ||
|
||||
((samlAssertion[0] as JSONObject)['Subject'] as JSONArray);
|
||||
|
||||
if (!samlSubject || samlSubject.length === 0) {
|
||||
throw new BadRequestException('SAML Subject not found');
|
||||
@@ -140,7 +174,8 @@ export default class SSOUtil {
|
||||
|
||||
const samlNameId: JSONArray =
|
||||
((samlSubject[0] as JSONObject)['saml2:NameID'] as JSONArray) ||
|
||||
((samlSubject[0] as JSONObject)['saml:NameID'] as JSONArray);
|
||||
((samlSubject[0] as JSONObject)['saml:NameID'] as JSONArray) ||
|
||||
((samlSubject[0] as JSONObject)['NameID'] as JSONArray);
|
||||
|
||||
if (!samlNameId || samlNameId.length === 0) {
|
||||
throw new BadRequestException('SAML NAME ID not found');
|
||||
@@ -160,11 +195,13 @@ export default class SSOUtil {
|
||||
|
||||
payload =
|
||||
(payload['saml2p:Response'] as JSONObject) ||
|
||||
(payload['samlp:Response'] as JSONObject);
|
||||
(payload['samlp:Response'] as JSONObject) ||
|
||||
(payload['Response'] as JSONObject);
|
||||
|
||||
const issuers: JSONArray =
|
||||
(payload['saml2:Issuer'] as JSONArray) ||
|
||||
(payload['saml:Issuer'] as JSONArray);
|
||||
(payload['saml:Issuer'] as JSONArray) ||
|
||||
(payload['Issuer'] as JSONArray);
|
||||
|
||||
if (issuers.length === 0) {
|
||||
throw new BadRequestException('Issuers not found');
|
||||
|
||||
@@ -32,7 +32,7 @@ router.post(
|
||||
customTwilioConfig: body['customTwilioConfig'] as any,
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -137,7 +137,7 @@ router.post('/test', async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
);
|
||||
}
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -44,7 +44,7 @@ router.post(
|
||||
(body['userOnCallLogTimelineId'] as ObjectID) || undefined,
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ router.post(
|
||||
}
|
||||
);
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -135,7 +135,7 @@ router.post('/test', async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
);
|
||||
}
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -98,7 +98,7 @@ router.post('/test', async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
);
|
||||
}
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -11,7 +11,9 @@ import TwilioConfig from 'Common/Types/CallAndSMS/TwilioConfig';
|
||||
export const InternalSmtpPassword: string =
|
||||
process.env['INTERNAL_SMTP_PASSWORD'] || '';
|
||||
|
||||
export const InternalSmtpHost: Hostname = new Hostname('haraka');
|
||||
export const InternalSmtpHost: Hostname = new Hostname(
|
||||
process.env['INTERNAL_SMTP_HOST'] || 'haraka'
|
||||
);
|
||||
|
||||
export const InternalSmtpPort: Port = new Port(2525);
|
||||
|
||||
|
||||
@@ -7,11 +7,18 @@ import SmsAPI from './API/SMS';
|
||||
import CallAPI from './API/Call';
|
||||
import SMTPConfigAPI from './API/SMTPConfig';
|
||||
import './Utils/Handlebars';
|
||||
import FeatureSet from 'CommonServer/Types/FeatureSet';
|
||||
|
||||
const APP_NAME: string = 'api/notification';
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
const NotificationFeatureSet: FeatureSet = {
|
||||
init: async (): Promise<void> => {
|
||||
const APP_NAME: string = 'api/notification';
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
|
||||
app.use([`/${APP_NAME}/email`, '/email'], MailAPI);
|
||||
app.use([`/${APP_NAME}/sms`, '/sms'], SmsAPI);
|
||||
app.use([`/${APP_NAME}/call`, '/call'], CallAPI);
|
||||
app.use([`/${APP_NAME}/smtp-config`, '/smtp-config'], SMTPConfigAPI);
|
||||
app.use([`/${APP_NAME}/email`, '/email'], MailAPI);
|
||||
app.use([`/${APP_NAME}/sms`, '/sms'], SmsAPI);
|
||||
app.use([`/${APP_NAME}/call`, '/call'], CallAPI);
|
||||
app.use([`/${APP_NAME}/smtp-config`, '/smtp-config'], SMTPConfigAPI);
|
||||
},
|
||||
};
|
||||
|
||||
export default NotificationFeatureSet;
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
{{> DetailBoxField title="Current State: " text=currentState }}
|
||||
{{> DetailBoxField title="Resources Affected: " text=resourcesAffected }}
|
||||
{{> DetailBoxField title="Severity: " text=incidentSeverity }}
|
||||
{{> DetailBoxField title="Root Cause: " text=rootCause }}
|
||||
{{> DetailBoxField title="Root Cause: " text="" }}
|
||||
{{> DetailBoxField title="" text=rootCause }}
|
||||
{{> DetailBoxField title="Description: " text="" }}
|
||||
{{> DetailBoxField title="" text=incidentDescription }}
|
||||
{{> DetailBoxEnd this }}
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
{{> DetailBoxField title="Current State: " text=currentState }}
|
||||
{{> DetailBoxField title="Resources Affected: " text=resourcesAffected }}
|
||||
{{> DetailBoxField title="Severity: " text=incidentSeverity }}
|
||||
{{> DetailBoxField title="Root Cause: " text=rootCause }}
|
||||
{{> DetailBoxField title="Root Cause: " text="" }}
|
||||
{{> DetailBoxField title="" text=rootCause }}
|
||||
{{> DetailBoxField title="Description: " text="" }}
|
||||
{{> DetailBoxField title="" text=incidentDescription }}
|
||||
{{> DetailBoxEnd this }}
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
{{> DetailBoxStart this }}
|
||||
{{> DetailBoxField title="Monitor Name:" text=monitorName }}
|
||||
{{> DetailBoxField title="New Status: " text=currentStatus }}
|
||||
{{> DetailBoxField title="Root Cause: " text=rootCause }}
|
||||
{{> DetailBoxField title="Root Cause: " text="" }}
|
||||
{{> DetailBoxField title="" text=rootCause }}
|
||||
{{> DetailBoxField title="Status changed at: " text="" }}
|
||||
{{> DetailBoxField title="" text=statusChangedAt }}
|
||||
{{> DetailBoxField title="Description: " text="" }}
|
||||
|
||||
28
App/FeatureSet/Notification/Templates/ProbeOffline.hbs
Normal file
28
App/FeatureSet/Notification/Templates/ProbeOffline.hbs
Normal file
@@ -0,0 +1,28 @@
|
||||
{{> Start this}}
|
||||
|
||||
{{> Logo this}}
|
||||
|
||||
{{> EmailTitle title="ACTION REQUIRED: OneUptime Probe cannot monitor your resources." }}
|
||||
|
||||
{{> InfoBlock info="We have detected that the OneUptime Probe is offline and cannot monitor your resources. This may be due to a network issue, a firewall blocking the probe, or the probe being powered off. Please take the necessary steps to bring the probe back online." }}
|
||||
|
||||
{{> DetailBoxStart this }}
|
||||
{{> DetailBoxField title="Probe Name: " text=probeName }}
|
||||
{{> DetailBoxField title="Probe Description: " text=probeDescription }}
|
||||
{{#if projectId}}
|
||||
{{> DetailBoxField title="Project ID: " text=projectId }}
|
||||
{{else}}
|
||||
{{> DetailBoxField title="Global Probe: " text="This is a global probe." }}
|
||||
{{/if}}
|
||||
{{> DetailBoxField title="Probe ID: " text=probeId }}
|
||||
{{#if hostname}}
|
||||
{{> DetailBoxField title="Hostname: " text=hostname }}
|
||||
{{/if}}
|
||||
{{> DetailBoxField title="Issue: " text=issue }}
|
||||
{{> DetailBoxEnd this }}
|
||||
|
||||
{{> InfoBlock info=emailReason }}
|
||||
|
||||
{{> Footer this}}
|
||||
|
||||
{{> End this}}
|
||||
@@ -1 +0,0 @@
|
||||
{"manager":"/usr/src/app/FeatureSet/Workers/Utils/Greenlock/Manager.ts","configDir":"./greenlock.d"}
|
||||
@@ -0,0 +1,50 @@
|
||||
import DataMigrationBase from './DataMigrationBase';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import TelemetryService from 'Model/Models/TelemetryService';
|
||||
import TelemetryServiceService from 'CommonServer/Services/TelemetryServiceService';
|
||||
import { BrightColors } from 'Common/Types/BrandColors';
|
||||
import ArrayUtil from 'Common/Types/ArrayUtil';
|
||||
|
||||
export default class AddTelemetryServiceColor extends DataMigrationBase {
|
||||
public constructor() {
|
||||
super('AddTelemetryServiceColor');
|
||||
}
|
||||
|
||||
public override async migrate(): Promise<void> {
|
||||
// get all the users with email isVerified true.
|
||||
|
||||
const services: Array<TelemetryService> =
|
||||
await TelemetryServiceService.findBy({
|
||||
query: {},
|
||||
select: {
|
||||
_id: true,
|
||||
serviceColor: true,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const service of services) {
|
||||
if (!service.serviceColor) {
|
||||
(service.serviceColor =
|
||||
ArrayUtil.selectItemByRandom(BrightColors)),
|
||||
await TelemetryServiceService.updateOneById({
|
||||
id: service.id!,
|
||||
data: {
|
||||
serviceColor: service.serviceColor,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async rollback(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import DataMigrationBase from './DataMigrationBase';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import StatusPageDomainService from 'CommonServer/Services/StatusPageDomainService';
|
||||
import logger from 'CommonServer/Utils/Logger';
|
||||
import StatusPageDomain from 'Model/Models/StatusPageDomain';
|
||||
|
||||
export default class GenerateNewCertsForStatusPage extends DataMigrationBase {
|
||||
public constructor() {
|
||||
super('GenerateNewCertsForStatusPage');
|
||||
}
|
||||
|
||||
public override async migrate(): Promise<void> {
|
||||
// get all domains in greenlock certs.
|
||||
const statusPageDomains: Array<StatusPageDomain> =
|
||||
await StatusPageDomainService.findBy({
|
||||
query: {},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
});
|
||||
|
||||
// now order these domains
|
||||
|
||||
for (const statusPageDomain of statusPageDomains) {
|
||||
// get status page domain.
|
||||
|
||||
try {
|
||||
await StatusPageDomainService.orderCert(statusPageDomain);
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async rollback(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,9 @@ import MigrateToMeteredSubscription from './MigrateToMeteredSubscription';
|
||||
import MoveEnableSubscribersToEnableEmailSubscribersOnStatusPage from './MoveEnableSubscribersToEnableEmailSubscribersOnStatusPage';
|
||||
import UpdateActiveMonitorCountToBillingProvider from './UpdateActiveMonitorCountToBillingProvider';
|
||||
import UpdateGlobalConfigFromEnv from './UpdateGlobalCongfigFromEnv';
|
||||
import AddTelemetryServiceColor from './AddTelemetryServiceColor';
|
||||
import MoveGreenlockCertsToAcmeCerts from './MoveGreenlockCertsToAcmeCerts';
|
||||
import GenerateNewCertsForStatusPage from './GenerateNewCertsForStatusPage';
|
||||
|
||||
// This is the order in which the migrations will be run. Add new migrations to the end of the array.
|
||||
|
||||
@@ -47,6 +50,9 @@ const DataMigrations: Array<DataMigrationBase> = [
|
||||
new ChangeLogSeverityColumnTypeFromTextToNumber(),
|
||||
new AddAttributeColumnToSpanAndLog(),
|
||||
new AddSecretKeyToIncomingRequestMonitor(),
|
||||
new AddTelemetryServiceColor(),
|
||||
new MoveGreenlockCertsToAcmeCerts(),
|
||||
new GenerateNewCertsForStatusPage(),
|
||||
];
|
||||
|
||||
export default DataMigrations;
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import GreenlockCertificateService from 'CommonServer/Services/GreenlockCertificateService';
|
||||
import DataMigrationBase from './DataMigrationBase';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import StatusPageDomainService from 'CommonServer/Services/StatusPageDomainService';
|
||||
import logger from 'CommonServer/Utils/Logger';
|
||||
import GreenlockCertificate from 'Model/Models/GreenlockCertificate';
|
||||
import StatusPageDomain from 'Model/Models/StatusPageDomain';
|
||||
|
||||
export default class MoveGreenlockCertsToAcmeCerts extends DataMigrationBase {
|
||||
public constructor() {
|
||||
super('MoveGreenlockCertsToAcmeCerts');
|
||||
}
|
||||
|
||||
public override async migrate(): Promise<void> {
|
||||
const allDomains: Array<string> = [];
|
||||
|
||||
// get all domains in greenlock certs.
|
||||
const greenlockCerts: GreenlockCertificate[] =
|
||||
await GreenlockCertificateService.findBy({
|
||||
query: {},
|
||||
select: {
|
||||
key: true,
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_MAX,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const greenlockCert of greenlockCerts) {
|
||||
if (!greenlockCert.key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (allDomains.includes(greenlockCert.key!)) {
|
||||
// this domain already exists in acme certs.
|
||||
continue;
|
||||
}
|
||||
|
||||
allDomains.push(greenlockCert.key!);
|
||||
}
|
||||
|
||||
// now order these domains
|
||||
|
||||
for (const domain of allDomains) {
|
||||
// get status page domain.
|
||||
|
||||
const statusPageDomain: StatusPageDomain | null =
|
||||
await StatusPageDomainService.findOneBy({
|
||||
query: {
|
||||
fullDomain: domain,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!statusPageDomain) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
await StatusPageDomainService.orderCert(statusPageDomain);
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async rollback(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -27,14 +27,15 @@ import './Jobs/ScheduledMaintenance/ChangeStateToEnded';
|
||||
import './Jobs/ScheduledMaintenance/SendNotificationToSubscribers';
|
||||
import './Jobs/ScheduledMaintenanceStateTimeline/SendNotificationToSubscribers';
|
||||
|
||||
// Telemetry Service
|
||||
import './Jobs/TelemetryService/DeleteOldData';
|
||||
|
||||
// Scheduled Event Notes
|
||||
import './Jobs/ScheduledMaintenancePublicNote/SendNotificationToSubscribers';
|
||||
|
||||
// Certs Routers
|
||||
import StatusPageCerts from './Jobs/StatusPageCerts/StatusPageCerts';
|
||||
import './Jobs/StatusPageCerts/StatusPageCerts';
|
||||
|
||||
// Express
|
||||
import Express, { ExpressApplication } from 'CommonServer/Utils/Express';
|
||||
import JobDictionary from './Utils/JobDictionary';
|
||||
|
||||
// Monitor Owners
|
||||
@@ -75,15 +76,12 @@ import AnalyticsTableManagement from './Utils/AnalyticsDatabase/TableManegement'
|
||||
|
||||
import './Jobs/Workflow/TimeoutJobs';
|
||||
import './Jobs/MeteredPlan/ReportTelemetryMeteredPlan';
|
||||
|
||||
// Monitor Metrics
|
||||
import './Jobs/MonitorMetrics/MonitorMetricsByMinute';
|
||||
|
||||
import { PromiseVoidFunction } from 'Common/Types/FunctionTypes';
|
||||
|
||||
const APP_NAME: string = 'api/workers';
|
||||
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
|
||||
//cert routes.
|
||||
app.use(`/${APP_NAME.toLocaleLowerCase()}`, StatusPageCerts);
|
||||
|
||||
const WorkersFeatureSet: FeatureSet = {
|
||||
init: async (): Promise<void> => {
|
||||
try {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
LogDataIngestMeteredPlan,
|
||||
TracesDataIngestMetredPlan,
|
||||
MetricsDataIngestMeteredPlan,
|
||||
ActiveMonitoringMeteredPlan,
|
||||
} from 'CommonServer/Types/Billing/MeteredPlan/AllMeteredPlans';
|
||||
import Sleep from 'Common/Types/Sleep';
|
||||
|
||||
@@ -42,24 +43,36 @@ RunCron(
|
||||
});
|
||||
|
||||
for (const project of projects) {
|
||||
if (project.id) {
|
||||
await LogDataIngestMeteredPlan.reportQuantityToBillingProvider(
|
||||
project.id
|
||||
try {
|
||||
if (project.id) {
|
||||
await LogDataIngestMeteredPlan.reportQuantityToBillingProvider(
|
||||
project.id
|
||||
);
|
||||
|
||||
await Sleep.sleep(1000);
|
||||
|
||||
await MetricsDataIngestMeteredPlan.reportQuantityToBillingProvider(
|
||||
project.id
|
||||
);
|
||||
|
||||
await Sleep.sleep(1000);
|
||||
|
||||
await TracesDataIngestMetredPlan.reportQuantityToBillingProvider(
|
||||
project.id
|
||||
);
|
||||
|
||||
await Sleep.sleep(1000);
|
||||
|
||||
await ActiveMonitoringMeteredPlan.reportQuantityToBillingProvider(
|
||||
project.id!
|
||||
);
|
||||
|
||||
await Sleep.sleep(1000);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`MeteredPlan:ReportTelemetryMeteredPlan Error while reporting telemetry for project ${project.id}: ${error}`
|
||||
);
|
||||
|
||||
await Sleep.sleep(1000);
|
||||
|
||||
await MetricsDataIngestMeteredPlan.reportQuantityToBillingProvider(
|
||||
project.id
|
||||
);
|
||||
|
||||
await Sleep.sleep(1000);
|
||||
|
||||
await TracesDataIngestMetredPlan.reportQuantityToBillingProvider(
|
||||
project.id
|
||||
);
|
||||
|
||||
await Sleep.sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import RunCron from '../../Utils/Cron';
|
||||
import { EVERY_MINUTE } from 'Common/Utils/CronTime';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import MonitorMetricsByMinuteService from 'CommonServer/Services/MonitorMetricsByMinuteService';
|
||||
import LessThan from 'Common/Types/BaseDatabase/LessThan';
|
||||
|
||||
RunCron(
|
||||
'MonitorMetrics:HardDeleteMonitorMetricsByMinute',
|
||||
{ schedule: EVERY_MINUTE, runOnStartup: false },
|
||||
async () => {
|
||||
const oneHourAgo: Date = OneUptimeDate.getSomeMinutesAgo(60);
|
||||
|
||||
// Delete all monitor metrics older than one hour
|
||||
|
||||
await MonitorMetricsByMinuteService.deleteBy({
|
||||
query: {
|
||||
createdAt: new LessThan(oneHourAgo),
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -1,456 +1,51 @@
|
||||
import { EVERY_HOUR, EVERY_MINUTE } from 'Common/Utils/CronTime';
|
||||
import { EVERY_FIFTEEN_MINUTE } from 'Common/Utils/CronTime';
|
||||
import RunCron from '../../Utils/Cron';
|
||||
import { IsDevelopment } from 'CommonServer/EnvironmentConfig';
|
||||
import StatusPageDomain from 'Model/Models/StatusPageDomain';
|
||||
import StatusPageDomainService from 'CommonServer/Services/StatusPageDomainService';
|
||||
// @ts-ignore
|
||||
import Greenlock from 'greenlock';
|
||||
import logger from 'CommonServer/Utils/Logger';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import Express, {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
ExpressRouter,
|
||||
NextFunction,
|
||||
} from 'CommonServer/Utils/Express';
|
||||
import ClusterKeyAuthorization from 'CommonServer/Middleware/ClusterKeyAuthorization';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import Response from 'CommonServer/Utils/Response';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
|
||||
const router: ExpressRouter = Express.getRouter();
|
||||
|
||||
const greenlock: any = Greenlock.create({
|
||||
configFile: '/usr/src/app/FeatureSet/Workers/greenlockrc',
|
||||
packageRoot: `/usr/src/app`,
|
||||
manager: '/usr/src/app/FeatureSet/Workers/Utils/Greenlock/Manager.ts',
|
||||
approveDomains: async (opts: any) => {
|
||||
const domain: StatusPageDomain | null =
|
||||
await StatusPageDomainService.findOneBy({
|
||||
query: {
|
||||
fullDomain: opts.domain,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
fullDomain: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!domain) {
|
||||
throw new BadDataException(
|
||||
`Domain ${opts.domain} does not exist in StatusPageDomain`
|
||||
);
|
||||
}
|
||||
|
||||
return opts; // or Promise.resolve(opts);
|
||||
},
|
||||
store: {
|
||||
module: '/usr/src/app/FeatureSet/Workers/Utils/Greenlock/Store.ts',
|
||||
},
|
||||
// Staging for testing environments
|
||||
// staging: IsDevelopment,
|
||||
|
||||
// This should be the contact who receives critical bug and security notifications
|
||||
// Optionally, you may receive other (very few) updates, such as important new features
|
||||
maintainerEmail: 'lets-encrypt@oneuptime.com',
|
||||
|
||||
// for an RFC 8555 / RFC 7231 ACME client user agent
|
||||
packageAgent: 'oneuptime/1.0.0',
|
||||
|
||||
notify: function (event: string, details: any) {
|
||||
if ('error' === event) {
|
||||
logger.error('Greenlock Notify: ' + event);
|
||||
logger.error(details);
|
||||
}
|
||||
logger.info('Greenlock Notify: ' + event);
|
||||
logger.info(details);
|
||||
},
|
||||
|
||||
agreeToTerms: true,
|
||||
challenges: {
|
||||
'http-01': {
|
||||
module: '/usr/src/app/FeatureSet/Workers/Utils/Greenlock/HttpChallenge.ts',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Delete
|
||||
router.delete(
|
||||
`/certs`,
|
||||
ClusterKeyAuthorization.isAuthorizedServiceMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
const body: JSONObject = req.body;
|
||||
|
||||
if (!body['domain']) {
|
||||
throw new BadDataException('Domain is required');
|
||||
}
|
||||
|
||||
await greenlock.remove({
|
||||
subject: body['domain'],
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Create
|
||||
router.post(
|
||||
`/certs`,
|
||||
ClusterKeyAuthorization.isAuthorizedServiceMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
const body: JSONObject = req.body;
|
||||
|
||||
if (!body['domain']) {
|
||||
throw new BadDataException('Domain is required');
|
||||
}
|
||||
|
||||
await greenlock.add({
|
||||
subject: body['domain'],
|
||||
altnames: [body['domain']],
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Create
|
||||
router.get(
|
||||
`/certs`,
|
||||
ClusterKeyAuthorization.isAuthorizedServiceMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
const body: JSONObject = req.body;
|
||||
|
||||
if (!body['domain']) {
|
||||
throw new BadDataException('Domain is required');
|
||||
}
|
||||
|
||||
const site: JSONObject = await greenlock.get({
|
||||
servername: body['domain'] as string,
|
||||
});
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, site);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
RunCron(
|
||||
'StatusPageCerts:RenewCerts',
|
||||
{ schedule: IsDevelopment ? EVERY_MINUTE : EVERY_HOUR, runOnStartup: true },
|
||||
{
|
||||
schedule: IsDevelopment ? EVERY_FIFTEEN_MINUTE : EVERY_FIFTEEN_MINUTE,
|
||||
runOnStartup: true,
|
||||
},
|
||||
async () => {
|
||||
logger.info('Renewing Certs...');
|
||||
await greenlock.renew();
|
||||
await StatusPageDomainService.renewCertsWhichAreExpiringSoon();
|
||||
logger.info('Renew Completed...');
|
||||
}
|
||||
);
|
||||
|
||||
RunCron(
|
||||
'StatusPageCerts:OrderCerts',
|
||||
{ schedule: IsDevelopment ? EVERY_MINUTE : EVERY_HOUR, runOnStartup: true },
|
||||
async () => {
|
||||
// Fetch all domains where certs are added to greenlock.
|
||||
|
||||
const domains: Array<StatusPageDomain> =
|
||||
await StatusPageDomainService.findBy({
|
||||
query: {
|
||||
isAddedToGreenlock: true,
|
||||
isSslProvisioned: false,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
greenlockConfig: true,
|
||||
fullDomain: true,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(`Certificates to Order: ${domains.length}`);
|
||||
|
||||
for (const domain of domains) {
|
||||
logger.info(
|
||||
`StatusPageCerts:OrderCerts - Ordering Certificate ${domain.fullDomain}`
|
||||
);
|
||||
|
||||
greenlock.order(domain.greenlockConfig).catch((err: any) => {
|
||||
logger.error(
|
||||
`StatusPageCerts:OrderCerts - Failed for domain ${domain.fullDomain}`
|
||||
);
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
RunCron(
|
||||
'StatusPageCerts:AddCerts',
|
||||
{ schedule: IsDevelopment ? EVERY_MINUTE : EVERY_HOUR, runOnStartup: true },
|
||||
async () => {
|
||||
const domains: Array<StatusPageDomain> =
|
||||
await StatusPageDomainService.findBy({
|
||||
query: {
|
||||
isAddedToGreenlock: false,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
fullDomain: true,
|
||||
cnameVerificationToken: true,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const domain of domains) {
|
||||
logger.info(
|
||||
`StatusPageCerts:AddCerts - Checking CNAME ${domain.fullDomain}`
|
||||
);
|
||||
|
||||
// Check CNAME validation and if that fails. Remove certs from Greenlock.
|
||||
const isValid: boolean = await checkCnameValidation(
|
||||
domain.fullDomain!,
|
||||
domain.cnameVerificationToken!
|
||||
);
|
||||
|
||||
if (isValid) {
|
||||
logger.info(
|
||||
`StatusPageCerts:AddCerts - CNAME for ${domain.fullDomain} is valid. Adding domain to greenlock.`
|
||||
);
|
||||
|
||||
await StatusPageDomainService.updateOneById({
|
||||
id: domain.id!,
|
||||
data: {
|
||||
isCnameVerified: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
await greenlock.add({
|
||||
subject: domain.fullDomain,
|
||||
altnames: [domain.fullDomain],
|
||||
});
|
||||
|
||||
await StatusPageDomainService.updateOneById({
|
||||
id: domain.id!,
|
||||
data: {
|
||||
isAddedToGreenlock: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`StatusPageCerts:AddCerts - ${domain.fullDomain} added to greenlock.`
|
||||
);
|
||||
} else {
|
||||
logger.info(
|
||||
`StatusPageCerts:AddCerts - CNAME for ${domain.fullDomain} is invalid. Removing cert`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
RunCron(
|
||||
'StatusPageCerts:RemoveCerts',
|
||||
{ schedule: IsDevelopment ? EVERY_MINUTE : EVERY_HOUR, runOnStartup: true },
|
||||
async () => {
|
||||
// Fetch all domains where certs are added to greenlock.
|
||||
|
||||
const domains: Array<StatusPageDomain> =
|
||||
await StatusPageDomainService.findBy({
|
||||
query: {
|
||||
isAddedToGreenlock: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
fullDomain: true,
|
||||
cnameVerificationToken: true,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const domain of domains) {
|
||||
logger.info(
|
||||
`StatusPageCerts:RemoveCerts - Checking CNAME ${domain.fullDomain}`
|
||||
);
|
||||
|
||||
// Check CNAME validation and if that fails. Remove certs from Greenlock.
|
||||
const isValid: boolean = await checkCnameValidation(
|
||||
domain.fullDomain!,
|
||||
domain.cnameVerificationToken!
|
||||
);
|
||||
|
||||
if (!isValid) {
|
||||
logger.info(
|
||||
`StatusPageCerts:RemoveCerts - CNAME for ${domain.fullDomain} is invalid. Removing domain from greenlock.`
|
||||
);
|
||||
|
||||
await greenlock.remove({
|
||||
subject: domain.fullDomain,
|
||||
});
|
||||
|
||||
await StatusPageDomainService.updateOneById({
|
||||
id: domain.id!,
|
||||
data: {
|
||||
isAddedToGreenlock: false,
|
||||
isCnameVerified: false,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`StatusPageCerts:RemoveCerts - ${domain.fullDomain} removed from greenlock.`
|
||||
);
|
||||
} else {
|
||||
logger.info(
|
||||
`StatusPageCerts:RemoveCerts - CNAME for ${domain.fullDomain} is valid`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
RunCron(
|
||||
'StatusPageCerts:CheckSslProvisioningStatus',
|
||||
{ schedule: IsDevelopment ? EVERY_MINUTE : EVERY_HOUR, runOnStartup: true },
|
||||
{
|
||||
schedule: IsDevelopment ? EVERY_FIFTEEN_MINUTE : EVERY_FIFTEEN_MINUTE,
|
||||
runOnStartup: true,
|
||||
},
|
||||
async () => {
|
||||
// Fetch all domains where certs are added to greenlock.
|
||||
|
||||
const domains: Array<StatusPageDomain> =
|
||||
await StatusPageDomainService.findBy({
|
||||
query: {
|
||||
isAddedToGreenlock: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
fullDomain: true,
|
||||
cnameVerificationToken: true,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const domain of domains) {
|
||||
logger.info(
|
||||
`StatusPageCerts:RemoveCerts - Checking CNAME ${domain.fullDomain}`
|
||||
);
|
||||
|
||||
// Check CNAME validation and if that fails. Remove certs from Greenlock.
|
||||
const isValid: boolean = await isSslProvisioned(
|
||||
domain.fullDomain!,
|
||||
domain.cnameVerificationToken!
|
||||
);
|
||||
|
||||
if (!isValid) {
|
||||
await StatusPageDomainService.updateOneById({
|
||||
id: domain.id!,
|
||||
data: {
|
||||
isSslProvisioned: false,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await StatusPageDomainService.updateOneById({
|
||||
id: domain.id!,
|
||||
data: {
|
||||
isSslProvisioned: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
await StatusPageDomainService.updateSslProvisioningStatusForAllDomains();
|
||||
}
|
||||
);
|
||||
|
||||
type CheckCnameValidationFunction = (
|
||||
fulldomain: string,
|
||||
token: string
|
||||
) => Promise<boolean>;
|
||||
|
||||
const checkCnameValidation: CheckCnameValidationFunction = async (
|
||||
fulldomain: string,
|
||||
token: string
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const result: AxiosResponse = await axios.get(
|
||||
'http://' +
|
||||
fulldomain +
|
||||
'/status-page-api/cname-verification/' +
|
||||
token
|
||||
);
|
||||
|
||||
if (result.status === 200) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (err) {
|
||||
logger.info('Failed checking for CNAME ' + fulldomain);
|
||||
logger.info('Token: ' + token);
|
||||
logger.info(err);
|
||||
return false;
|
||||
RunCron(
|
||||
'StatusPageCerts:OrderSSL',
|
||||
{
|
||||
schedule: IsDevelopment ? EVERY_FIFTEEN_MINUTE : EVERY_FIFTEEN_MINUTE,
|
||||
runOnStartup: true,
|
||||
},
|
||||
async () => {
|
||||
await StatusPageDomainService.orderSSLForDomainsWhichAreNotOrderedYet();
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
type IsSSLProvisionedFunction = (
|
||||
fulldomain: string,
|
||||
token: string
|
||||
) => Promise<boolean>;
|
||||
|
||||
const isSslProvisioned: IsSSLProvisionedFunction = async (
|
||||
fulldomain: string,
|
||||
token: string
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const result: AxiosResponse = await axios.get(
|
||||
'https://' +
|
||||
fulldomain +
|
||||
'/status-page-api/cname-verification/' +
|
||||
token
|
||||
);
|
||||
|
||||
if (result.status === 200) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (err) {
|
||||
return false;
|
||||
RunCron(
|
||||
'StatusPageCerts:VerifyCnameWhoseCnameisNotVerified',
|
||||
{
|
||||
schedule: IsDevelopment ? EVERY_FIFTEEN_MINUTE : EVERY_FIFTEEN_MINUTE,
|
||||
runOnStartup: true,
|
||||
},
|
||||
async () => {
|
||||
await StatusPageDomainService.verifyCnameWhoseCnameisNotVerified();
|
||||
}
|
||||
};
|
||||
|
||||
export default router;
|
||||
);
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { IsDevelopment } from 'CommonServer/EnvironmentConfig';
|
||||
import RunCron from '../../Utils/Cron';
|
||||
import { EVERY_HOUR, EVERY_MINUTE } from 'Common/Utils/CronTime';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import TelemetryService from 'Model/Models/TelemetryService';
|
||||
import TelemetryServiceService from 'CommonServer/Services/TelemetryServiceService';
|
||||
import LogService from 'CommonServer/Services/LogService';
|
||||
import QueryHelper from 'CommonServer/Types/AnalyticsDatabase/QueryHelper';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import SpanService from 'CommonServer/Services/SpanService';
|
||||
|
||||
RunCron(
|
||||
'TelemetryService:DeleteOldData',
|
||||
{
|
||||
schedule: IsDevelopment ? EVERY_MINUTE : EVERY_HOUR,
|
||||
runOnStartup: false,
|
||||
},
|
||||
async () => {
|
||||
// get a list of all the telemetry services.
|
||||
|
||||
const telemetryService: Array<TelemetryService> =
|
||||
await TelemetryServiceService.findBy({
|
||||
query: {},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
select: {
|
||||
retainTelemetryDataForDays: true,
|
||||
projectId: true,
|
||||
_id: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const service of telemetryService) {
|
||||
let dataRententionDays: number | undefined =
|
||||
service.retainTelemetryDataForDays;
|
||||
|
||||
if (!dataRententionDays) {
|
||||
dataRententionDays = 15; // default to 15 days.
|
||||
}
|
||||
|
||||
// delete logs
|
||||
|
||||
await LogService.deleteBy({
|
||||
query: {
|
||||
createdAt: QueryHelper.lessThan(
|
||||
OneUptimeDate.getSomeDaysAgo(dataRententionDays)
|
||||
),
|
||||
serviceId: service.id,
|
||||
projectId: service.projectId,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
// delete spans
|
||||
|
||||
await SpanService.deleteBy({
|
||||
query: {
|
||||
createdAt: QueryHelper.lessThan(
|
||||
OneUptimeDate.getSomeDaysAgo(dataRententionDays)
|
||||
),
|
||||
serviceId: service.id,
|
||||
projectId: service.projectId,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
// TOOD: delete metrics.
|
||||
}
|
||||
}
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user