TODO: initial retropilot-server commit

pull/4/head
Florian Brede 2021-05-16 04:34:03 +02:00
parent b26a3a6bdd
commit ec2d421d3d
14 changed files with 1778 additions and 0 deletions

8
.gitignore vendored 100644
View File

@ -0,0 +1,8 @@
node_modules/
realdata/
worker.log
server.log
package-lock.json
database.json
config.js
.vscode

80
certs/816350c0.0 100644
View File

@ -0,0 +1,80 @@
-----BEGIN CERTIFICATE-----
MIIDeTCCAmGgAwIBAgIUM4WmqKK7pqh7zhatTsUZlVwYHAcwDQYJKoZIhvcNAQEL
BQAwTDELMAkGA1UEBhMCV1cxEzARBgNVBAgMClNvbWUtU3RhdGUxEzARBgNVBAoM
ClJldHJvUGlsb3QxEzARBgNVBAMMCnJldHJvcGlsb3QwHhcNMjEwNTEzMTMwMjEw
WhcNMzEwNTExMTMwMjEwWjBMMQswCQYDVQQGEwJXVzETMBEGA1UECAwKU29tZS1T
dGF0ZTETMBEGA1UECgwKUmV0cm9QaWxvdDETMBEGA1UEAwwKcmV0cm9waWxvdDCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOZAGYdXNhj6T5nhueIKw2k2
eBfW4uCjrBkHRXxOUMGg/xs99QbLWzHCLzf0dZ8zWi3WykGeB14wYBQUaWdlvKIs
K0nnCVka+8nfUMgYvTBockpCbPCcU8Bw/m5420369s0zT2usZ8HvQOt0/JZAzb+W
RrSyhq+Ux8Ij4IW7wGS9pyiRo/9N1JTbOUkgOgo0n2fmBZ1yAZPZeqCkZbFxaHZj
Rc3EuP7TOQheHSg/XKToBoLWxRorHbMtGY+qU3TS+nzxhQPJs6AhE+Ntf4Pof6tV
ep6Lcv0fiQDXvgDsVgdmPg/GnO5C1YbkjyO2xdjhV1Wa5eEWHzLOWfoxUalFgbkC
AwEAAaNTMFEwHQYDVR0OBBYEFAJRfwdc2ww7LW/gNy7yOA3U7kAEMB8GA1UdIwQY
MBaAFAJRfwdc2ww7LW/gNy7yOA3U7kAEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAEQ2+6hhNX82wkB3EPmVKz5alLarT2i7GX2yR9634R4eLax2
Ai79edp6KbICuC5KjTRd7e/mQcT/5/YYNy2ecwAG2fGK8HrtWJJjraAbBgnO+7o1
JCt+393F5IZ/iSFRWHTdudXdbcCcUhYs+KXBU572X731ihPsctgrn6VHZdDsK5tr
M/vMDEnYNYZLxxysSKeCAVFGRvTvMjLkmmnfjKOTp9j/2AWmGS+emmyleIRQt2N1
pkVP9b4hFb01L36nqiAf8zcg1yj56rMxfoC+Vaez6Guq0LaViw3/T/1luA0Yi+3z
eUicIixgGiRp8xmmVeJngUFIp0otP8P1LXVOf0k=
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
33:85:a6:a8:a2:bb:a6:a8:7b:ce:16:ad:4e:c5:19:95:5c:18:1c:07
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = WW, ST = Some-State, O = RetroPilot, CN = retropilot
Validity
Not Before: May 13 13:02:10 2021 GMT
Not After : May 11 13:02:10 2031 GMT
Subject: C = WW, ST = Some-State, O = RetroPilot, CN = retropilot
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:e6:40:19:87:57:36:18:fa:4f:99:e1:b9:e2:0a:
c3:69:36:78:17:d6:e2:e0:a3:ac:19:07:45:7c:4e:
50:c1:a0:ff:1b:3d:f5:06:cb:5b:31:c2:2f:37:f4:
75:9f:33:5a:2d:d6:ca:41:9e:07:5e:30:60:14:14:
69:67:65:bc:a2:2c:2b:49:e7:09:59:1a:fb:c9:df:
50:c8:18:bd:30:68:72:4a:42:6c:f0:9c:53:c0:70:
fe:6e:78:db:4d:fa:f6:cd:33:4f:6b:ac:67:c1:ef:
40:eb:74:fc:96:40:cd:bf:96:46:b4:b2:86:af:94:
c7:c2:23:e0:85:bb:c0:64:bd:a7:28:91:a3:ff:4d:
d4:94:db:39:49:20:3a:0a:34:9f:67:e6:05:9d:72:
01:93:d9:7a:a0:a4:65:b1:71:68:76:63:45:cd:c4:
b8:fe:d3:39:08:5e:1d:28:3f:5c:a4:e8:06:82:d6:
c5:1a:2b:1d:b3:2d:19:8f:aa:53:74:d2:fa:7c:f1:
85:03:c9:b3:a0:21:13:e3:6d:7f:83:e8:7f:ab:55:
7a:9e:8b:72:fd:1f:89:00:d7:be:00:ec:56:07:66:
3e:0f:c6:9c:ee:42:d5:86:e4:8f:23:b6:c5:d8:e1:
57:55:9a:e5:e1:16:1f:32:ce:59:fa:31:51:a9:45:
81:b9
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
02:51:7F:07:5C:DB:0C:3B:2D:6F:E0:37:2E:F2:38:0D:D4:EE:40:04
X509v3 Authority Key Identifier:
keyid:02:51:7F:07:5C:DB:0C:3B:2D:6F:E0:37:2E:F2:38:0D:D4:EE:40:04
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
44:36:fb:a8:61:35:7f:36:c2:40:77:10:f9:95:2b:3e:5a:94:
b6:ab:4f:68:bb:19:7d:b2:47:de:b7:e1:1e:1e:2d:ac:76:02:
2e:fd:79:da:7a:29:b2:02:b8:2e:4a:8d:34:5d:ed:ef:e6:41:
c4:ff:e7:f6:18:37:2d:9e:73:00:06:d9:f1:8a:f0:7a:ed:58:
92:63:ad:a0:1b:06:09:ce:fb:ba:35:24:2b:7e:df:dd:c5:e4:
86:7f:89:21:51:58:74:dd:b9:d5:dd:6d:c0:9c:52:16:2c:f8:
a5:c1:53:9e:f6:5f:bd:f5:8a:13:ec:72:d8:2b:9f:a5:47:65:
d0:ec:2b:9b:6b:33:fb:cc:0c:49:d8:35:86:4b:c7:1c:ac:48:
a7:82:01:51:46:46:f4:ef:32:32:e4:9a:69:df:8c:a3:93:a7:
d8:ff:d8:05:a6:19:2f:9e:9a:6c:a5:78:84:50:b7:63:75:a6:
45:4f:f5:be:21:15:bd:35:2f:7e:a7:aa:20:1f:f3:37:20:d7:
28:f9:ea:b3:31:7e:80:be:55:a7:b3:e8:6b:aa:d0:b6:95:8b:
0d:ff:4f:fd:65:b8:0d:18:8b:ed:f3:79:48:9c:22:2c:60:1a:
24:69:f3:19:a6:55:e2:67:81:41:48:a7:4a:2d:3f:c3:f5:2d:
75:4e:7f:49
SHA1 Fingerprint=53:77:69:82:80:D7:A7:87:FA:05:73:06:C7:79:82:AB:3A:1B:D0:7F

21
certs/ca.crt 100644
View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDeTCCAmGgAwIBAgIUM4WmqKK7pqh7zhatTsUZlVwYHAcwDQYJKoZIhvcNAQEL
BQAwTDELMAkGA1UEBhMCV1cxEzARBgNVBAgMClNvbWUtU3RhdGUxEzARBgNVBAoM
ClJldHJvUGlsb3QxEzARBgNVBAMMCnJldHJvcGlsb3QwHhcNMjEwNTEzMTMwMjEw
WhcNMzEwNTExMTMwMjEwWjBMMQswCQYDVQQGEwJXVzETMBEGA1UECAwKU29tZS1T
dGF0ZTETMBEGA1UECgwKUmV0cm9QaWxvdDETMBEGA1UEAwwKcmV0cm9waWxvdDCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOZAGYdXNhj6T5nhueIKw2k2
eBfW4uCjrBkHRXxOUMGg/xs99QbLWzHCLzf0dZ8zWi3WykGeB14wYBQUaWdlvKIs
K0nnCVka+8nfUMgYvTBockpCbPCcU8Bw/m5420369s0zT2usZ8HvQOt0/JZAzb+W
RrSyhq+Ux8Ij4IW7wGS9pyiRo/9N1JTbOUkgOgo0n2fmBZ1yAZPZeqCkZbFxaHZj
Rc3EuP7TOQheHSg/XKToBoLWxRorHbMtGY+qU3TS+nzxhQPJs6AhE+Ntf4Pof6tV
ep6Lcv0fiQDXvgDsVgdmPg/GnO5C1YbkjyO2xdjhV1Wa5eEWHzLOWfoxUalFgbkC
AwEAAaNTMFEwHQYDVR0OBBYEFAJRfwdc2ww7LW/gNy7yOA3U7kAEMB8GA1UdIwQY
MBaAFAJRfwdc2ww7LW/gNy7yOA3U7kAEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAEQ2+6hhNX82wkB3EPmVKz5alLarT2i7GX2yR9634R4eLax2
Ai79edp6KbICuC5KjTRd7e/mQcT/5/YYNy2ecwAG2fGK8HrtWJJjraAbBgnO+7o1
JCt+393F5IZ/iSFRWHTdudXdbcCcUhYs+KXBU572X731ihPsctgrn6VHZdDsK5tr
M/vMDEnYNYZLxxysSKeCAVFGRvTvMjLkmmnfjKOTp9j/2AWmGS+emmyleIRQt2N1
pkVP9b4hFb01L36nqiAf8zcg1yj56rMxfoC+Vaez6Guq0LaViw3/T/1luA0Yi+3z
eUicIixgGiRp8xmmVeJngUFIp0otP8P1LXVOf0k=
-----END CERTIFICATE-----

30
certs/ca.key 100644
View File

@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,37D12FBFD89BC15B
sUpoN/V2oVSTx32FrH/H/UONQzBCraGiMbxhguOmiAVit8y+3oKd0J9J3j8ItZbT
RI/JlLjtd+bXIM21MlUaCc6OxRL9P9y6Bq9BsjslMrkAo6fgtC7BvJrPg8pcIL6C
kUMk4UueGXtYPMcyGWUaahPqqRQdbRc9oB8OJtAGD2KnSy96boSbTacrM+IkOTkN
Rkczl4Jul8/wrLxNaxxivGc3Ux62T/BUmqhh7TuRFbRd53+zTsge9OVsQfs3Qs6d
PL43AqVMiFRRk+Ed/EL/Ce4OmzLXfv2+1/8hh18MErR1lRlD/o3cg+qIso//j+aV
0/qid0M2zEhRziZv5eUMCWLg8HWODH+u8XnVzuXYhL9UfI8Yj/C9DSOHn6JR7c/3
VFxu0s60jEYFy2MP4hwa86zW8Ay5ogl8CqB9RZVvwNA+Q6PnLh5SEFhi4oMGJjiM
iSgBlFb2WPmunZo9WbgnGJXb5f5vW1VZp9qS8JIxVbWDdQveXImElf5q9MEM31UT
9ypng+yjpwGTYgNyyBgmnaUdGn4WhifjnHI81Ntuf5nxeiEmKLWSya0CA9nFwn4A
gUf8yKkZcOlsEgGnX/CfBtVuDbus4ZqOn5aXb2WZTJzmOvg0iUtPD0u4jBiv7vcq
WYofNcKKuVk3ej6OvIonz/8iRDy73d/9PFF2ISnV199AMnb7Zx5TAX4zNDGGb6Ba
ReiwpAI7QekRUX2Xvcwhh3lLpJfT+tfGOkDc0yndRSl7VGLdw8BfmTgN+NnT207N
HivlTrWwyOJmy/8UfpPFpPPcnV4CMjPzKzNBBBw55onvZTpEKUmdL3GPOk+MCvpI
OoBDJvEWPMq1AMkXFabhKcxkNfXgTHvk73EiVAZC6N11VxTwOidHuerv/9ZIt0Co
d2TRXbMKMLJ91Y38rtnhhtWgZpsT+IMfBYhv0QpXntkkNw/IfRyQ3FxQJjRssv+c
CQP1n8n8o8nAnPcbIXGWx5MY8V5iZEvPfY9cTI240zjGWRdkPMGPG+80otGrxHGi
MM1nJC0P7yruY4gNHTt+G0RoKDUE2A02fALWdq3ddH0ipy7/79x3EdNbB7RiJ/8Y
EZ2+7YWoxyWJK4yXXuHGjuMBew5B5MJdkbpOho9S1nULLRMlLP/Tl+HQwhc/Eb09
eaIpUWWz7sjLS57t0YlKK6thRJ5yQAL+CNbzYGOoQdS2nxFEWp4nj4ODXqiPSdeA
w8DY5AGlKxAOEgY5E0spp4yf8VGdGrPLtfbm3l9SxAL2vnvMrWvdU1f9HeDpOKks
kwdhYE73w48KCOZMdI0yLGOvtyuuI0Vv3B1ESGWS44LJGzwfW777ZqCqv9uoAU7g
sfZYJlmFNtGJB7nbCkAUJ9YeMYQrNJw2LXlhVbV5jxITmGRE59agsW0YrihKnoOr
dAD9A79wAr0P/kjGtrd7AFJglNBxCG3tPVxhqUY0QyaDJBSRT+bh4Iyx5Zd31EjC
9KxXZxW75fGA5+uTkjKjbwy2OlPS5DvYuItfbwrBX+WEXv9B8cODCX3Utz7o5Fxc
gFslEvipwy7tun2BhS4brUvRcuBVrph9ZMUurFCnLYj2Sp/+Kh7vBQ==
-----END RSA PRIVATE KEY-----

21
certs/ca.pem 100644
View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDeTCCAmGgAwIBAgIUM4WmqKK7pqh7zhatTsUZlVwYHAcwDQYJKoZIhvcNAQEL
BQAwTDELMAkGA1UEBhMCV1cxEzARBgNVBAgMClNvbWUtU3RhdGUxEzARBgNVBAoM
ClJldHJvUGlsb3QxEzARBgNVBAMMCnJldHJvcGlsb3QwHhcNMjEwNTEzMTMwMjEw
WhcNMzEwNTExMTMwMjEwWjBMMQswCQYDVQQGEwJXVzETMBEGA1UECAwKU29tZS1T
dGF0ZTETMBEGA1UECgwKUmV0cm9QaWxvdDETMBEGA1UEAwwKcmV0cm9waWxvdDCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOZAGYdXNhj6T5nhueIKw2k2
eBfW4uCjrBkHRXxOUMGg/xs99QbLWzHCLzf0dZ8zWi3WykGeB14wYBQUaWdlvKIs
K0nnCVka+8nfUMgYvTBockpCbPCcU8Bw/m5420369s0zT2usZ8HvQOt0/JZAzb+W
RrSyhq+Ux8Ij4IW7wGS9pyiRo/9N1JTbOUkgOgo0n2fmBZ1yAZPZeqCkZbFxaHZj
Rc3EuP7TOQheHSg/XKToBoLWxRorHbMtGY+qU3TS+nzxhQPJs6AhE+Ntf4Pof6tV
ep6Lcv0fiQDXvgDsVgdmPg/GnO5C1YbkjyO2xdjhV1Wa5eEWHzLOWfoxUalFgbkC
AwEAAaNTMFEwHQYDVR0OBBYEFAJRfwdc2ww7LW/gNy7yOA3U7kAEMB8GA1UdIwQY
MBaAFAJRfwdc2ww7LW/gNy7yOA3U7kAEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAEQ2+6hhNX82wkB3EPmVKz5alLarT2i7GX2yR9634R4eLax2
Ai79edp6KbICuC5KjTRd7e/mQcT/5/YYNy2ecwAG2fGK8HrtWJJjraAbBgnO+7o1
JCt+393F5IZ/iSFRWHTdudXdbcCcUhYs+KXBU572X731ihPsctgrn6VHZdDsK5tr
M/vMDEnYNYZLxxysSKeCAVFGRvTvMjLkmmnfjKOTp9j/2AWmGS+emmyleIRQt2N1
pkVP9b4hFb01L36nqiAf8zcg1yj56rMxfoC+Vaez6Guq0LaViw3/T/1luA0Yi+3z
eUicIixgGiRp8xmmVeJngUFIp0otP8P1LXVOf0k=
-----END CERTIFICATE-----

1
certs/ca.srl 100644
View File

@ -0,0 +1 @@
1F3B1C1D27FD0A48724A10572FEE340F57F6C425

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDHjCCAgYCFB87HB0n/QpIckoQVy/uNA9X9sQlMA0GCSqGSIb3DQEBCwUAMEwx
CzAJBgNVBAYTAldXMRMwEQYDVQQIDApTb21lLVN0YXRlMRMwEQYDVQQKDApSZXRy
b1BpbG90MRMwEQYDVQQDDApyZXRyb3BpbG90MB4XDTIxMDUxMzE1MDEwNloXDTMx
MDMyMjE1MDEwNlowSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRQwEgYDVQQK
DAtNeU9yZywgSW5jLjEZMBcGA1UEAwwQKi5jb21tYWRvdGFpLmNvbTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALXeelG3bjvVAHD2jKzLNixNv/pVWGdc
aQBO4/30eZg7hsYXasMazENAmmnB2msmPbyT1u/4el+QJebj3g4QQ/ntoJOZ2a64
gVP8RcU5n1DrCans5kM01KQzToWtLSPuFb+L3XqtLfBqW9GKC+RDBz6bmG8rUZiy
/+eKZjl7kHvsYWCZuW4YwNAJ6N5j4r6BhK/UfAy6LTRZ8FC3i836+9FuykySRRl/
Or8hQJND/u1+P42PA80gyu/KouvjiAhs2a8UsK0JMU6mKla8BI6rn4Kzmd8tMHLP
5HOg47Axihd6JwpcLOzEjJX5zcHXLBrqY6g6S28Yo5NcPkV6bYrvB3MCAwEAATAN
BgkqhkiG9w0BAQsFAAOCAQEARm6ArVT1zhmgLlloUn6L6RR2af+tXGt6X/7c7Vdy
4n1aTLcvyrshiDGFKA4g8MobOcnJ41s1i1L9p74GyIzCN9dBtXgIhUczZ9TQgGFR
o6bwN9IVpEvSldmeliAKtQBsFernAmYrAAak4dMEf+z8i0gNOnK/PaTNaNZDu02c
sVgMg0Os+aTLVGOWQ8K1qe6RlmuyNJ2Fu64NLNUzwkBk+voSAiQih3i1E5TZqc5K
vZiMHhi1xAoQ4FSYzmkdBQ9c0M7yjrmMDrfsKaGykeo4LE2lHMOgBQRrraDlw1VC
3EkMMCgmo/45kM3LYxkzg9GpSgrul42zKhZcpGmED1zWhQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICkDCCAXgCAQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRQwEgYDVQQK
DAtNeU9yZywgSW5jLjEZMBcGA1UEAwwQKi5jb21tYWRvdGFpLmNvbTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALXeelG3bjvVAHD2jKzLNixNv/pVWGdc
aQBO4/30eZg7hsYXasMazENAmmnB2msmPbyT1u/4el+QJebj3g4QQ/ntoJOZ2a64
gVP8RcU5n1DrCans5kM01KQzToWtLSPuFb+L3XqtLfBqW9GKC+RDBz6bmG8rUZiy
/+eKZjl7kHvsYWCZuW4YwNAJ6N5j4r6BhK/UfAy6LTRZ8FC3i836+9FuykySRRl/
Or8hQJND/u1+P42PA80gyu/KouvjiAhs2a8UsK0JMU6mKla8BI6rn4Kzmd8tMHLP
5HOg47Axihd6JwpcLOzEjJX5zcHXLBrqY6g6S28Yo5NcPkV6bYrvB3MCAwEAAaAA
MA0GCSqGSIb3DQEBCwUAA4IBAQAje+8fCINHo0oHOiBuc49jopLJyqvzkvIKd0rf
vdzmb7g0DMd8e/5cyWYLsmleUwOquZhuJ4bFT2MhofnvjeWkC6o79jtPaFCxU7y5
ABLOjyWlJCnJPVZyymTa+AlZrEVCWcYqvSozXpfdXCpN6+0O2BH2GkE7ne0nXvm1
V1AVSMpGldD4A18CE2hFmZWU6UyStYJjE8/1K4+RJTBWgYic8LJA/UeP7u0rYhRo
9Ex+I2LeCGZ1ocupNedbCeozZBq9NVbZ0bp4814G+GMxRS/mvOORvdf7OgyPIwsr
bOlXZIJ/7JKp0ED5lLcRreCle+XX1op7hiee+7IzxdaeALOR
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAtd56UbduO9UAcPaMrMs2LE2/+lVYZ1xpAE7j/fR5mDuGxhdq
wxrMQ0CaacHaayY9vJPW7/h6X5Al5uPeDhBD+e2gk5nZrriBU/xFxTmfUOsJqezm
QzTUpDNOha0tI+4Vv4vdeq0t8Gpb0YoL5EMHPpuYbytRmLL/54pmOXuQe+xhYJm5
bhjA0Ano3mPivoGEr9R8DLotNFnwULeLzfr70W7KTJJFGX86vyFAk0P+7X4/jY8D
zSDK78qi6+OICGzZrxSwrQkxTqYqVrwEjqufgrOZ3y0wcs/kc6DjsDGKF3onClws
7MSMlfnNwdcsGupjqDpLbxijk1w+RXptiu8HcwIDAQABAoIBADfff6i3N9B94cLS
m+lCDQSTZlVb/urSQxfrJLQSdYDFWORmsU/7XaGTqVywR4//kZvrt27F4aKWQG6s
tr2PVbLkxB5Ud8HrwR+yMyUiTMWpT7C6rQscoe9IK+l5iJKvRFMyfvp/Vcu0gTzg
skKQLRuY5b3RiyHkbCYQKNOkGIFZWmF0Q5qCwRAMF2zAmw7JgflkWcpq8P8YE6Fj
M2cNIzQloDUaWYO+EHwiBgWOWjOEASixBbdMZxv4B0/elWRdBR2FUww7jZ7L9Skw
vgydBsgF5v2vPdFgE4uMQU9LrOP+jZ3/87TeGi4Jf3mYa4L4SUkAHxRGs63FZilj
xJy/vfECgYEA7f645bWX5Bm/kZveU+oF+TUXVC5Y7qptoPe8MwAkI0N64wRKpTsg
kDU5OwSB5MAVEa4+tWGSoJFH4MBh0f7+Z4Y9NpupLRtFvpjjXQkzfjFauIQNVYcp
t2sjTgeeplDgkKkNTdZpyftulISQEhQefiaujjOGj/AbNg8hcsgweq0CgYEAw6DB
6qX8bhvD7TUlNR89DfkLLyg+XL+q69Km6Raprf6R6ynvZrojT3+GSzB13gm7AM1b
qw5hM2RYWdvjSz7uG/i/qt95eSwo9Zs84db1H9rcpMmwgVL4lOR8GUm1cjyZuDHJ
HYg0k7WubKTnn/JahyvUhIuGLRy3toW23Zye7p8CgYB4uWlZPJBb6KPAXOyebGuz
SjcXQJtkFZeeu4v/4uZx7Nz9I0QqJKAUBquNQHATdUw6t8SfhWs3f/qSC4STXWH3
aaTLepKKivcKA8vxeaVhMe/vAS0yYWnL3ND/1+WmQAhp2RcikM0A5EXnmIbsZMBD
BaQuBf0QLp/fADXbX6kX0QKBgBbpsUzGfDZ5Ug1qeB0st333eXPghs1eNarBVYNf
gCg89OJSWxPxIUmiahxMZMHI6fpCE0aJvKDEVATSWGDr0f+8ttZQrm2e2n1J3Wum
PbepbsZuSRocmbj2ugJMO9BRgL+uNRwcY+wZD4DyH58AR9IlNBMCNIj8IBhkuPJj
8cmJAoGBAMQYsTDkS120+ei3zK5qVfAIHfQPMhKZP/NqOggTBqZVpbYHGz12JMu3
IbwHEOJy9xIpkJiMBEiUF5P9zgl4XIp0ToD/pXvLteAAFSruellK+HiHEhxTXUVB
OEYYgvNYoTZas2O9srmUopn144QcDNyQK6dpCYjDvTgNlgyZG9aB
-----END RSA PRIVATE KEY-----

29
config.sample.js 100644
View File

@ -0,0 +1,29 @@
var config = {
applicationSalt: 'RANDOM_SEED',
databaseFile: 'database.json',
allowAccountRegistration: true,
httpInterface: '0.0.0.0',
httpPort: 3000,
httpsInterface: '0.0.0.0',
httpsPort: 4430,
sslKey: 'certs/retropilot.key',
sslCrt: 'certs/retropilot.crt',
baseUploadUrl: 'http://192.168.1.165:3000/backend/post_upload',
baseDriveDownloadUrl: 'http://192.168.1.165:3000/realdata/',
baseDriveDownloadPathMapping: '/realdata', // path mapping of above download url for expressjs, prefix with "/"
storagePath: 'realdata/', // relative or absolute ( "/..." for absolute path )
deviceStorageQuotaMb: 200000,
deviceDriveQuota: 1000,
deviceDriveExpirationDays: 30,
cabanaUrl: 'http://192.168.1.165:3001/'
};
module.exports = config;

32
package.json 100644
View File

@ -0,0 +1,32 @@
{
"name": "retropilot-server",
"version": "1.0.0",
"description": "replacement for comma.ai backend and useradmin dashboard. can be combined with a modified cabana instance.",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"@commaai/log_reader": "^0.8.0",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"directory-tree": "^2.2.9",
"express": "^4.17.1",
"express-fileupload": "^1.2.1",
"fast-folder-size": "^1.3.0",
"ffprobe": "^1.1.2",
"ffprobe-static": "^3.0.0",
"file-chunked": "^1.0.4",
"htmlspecialchars": "^1.0.5",
"jsonwebtoken": "^8.5.1",
"log4js": "^6.3.0",
"lowdb": "^1.0.0",
"multer": "^1.4.2",
"proper-lockfile": "^4.1.2",
"sendmail": "^1.6.1"
}
}

1045
server.js 100644

File diff suppressed because it is too large Load Diff

BIN
static/favicon.ico 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

449
worker.js 100644
View File

@ -0,0 +1,449 @@
const config = require('./config');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const log4js = require('log4js');
const lockfile = require('proper-lockfile');
var http = require('http');
var https = require('https');
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const cookieParser=require('cookie-parser');
const jwt = require('jsonwebtoken');
const low = require('lowdb');
const FileSync = require('lowdb/adapters/FileSync');
const sendmail = require('sendmail')();
const htmlspecialchars = require('htmlspecialchars');
const dirTree = require("directory-tree");
const { resolve } = require('path');
const Reader = require('@commaai/log_reader');
var ffprobe = require('ffprobe'),
ffprobeStatic = require('ffprobe-static');
const { exception } = require('console');
const adapter = new FileSync(config.databaseFile);
const db = low(adapter);
log4js.configure({
appenders: { logfile: { type: "file", filename: "worker.log" }, out: { type: "console"} },
categories: { default: { appenders: ['out', 'logfile'], level: 'info' } }
});
var logger = log4js.getLogger('default');
function initializeDatabase() {
db.read();
if (!db.has('devices').value()) {
logger.error("database not initialized, exit worker");
process.exit();
}
}
function initializeStorage() {
var verifiedPath = mkDirByPathSync(config.storagePath, {isRelativeToScript: (config.storagePath.indexOf("/")===0 ? false : true)});
if (verifiedPath!=null)
logger.info("Verified storage path "+verifiedPath);
else {
logger.error("Unable to verify storage path '"+config.storagePath+"', check filesystem / permissions");
process.exit();
}
}
function validateJWTToken(token, publicKey) {
try {
var decoded = jwt.verify(token.replace("JWT ", ""), publicKey, { algorithms: ['RS256'] });
return decoded;
} catch (exception) {
console.log(exception);
}
return null;
}
function formatDate(timestampMs) {
return new Date(timestampMs).toISOString().replace(/T/, ' ').replace(/\..+/, '');
}
function formatDuration(durationSeconds) {
var secs = durationSeconds % 60;
var mins = Math.floor(durationSeconds / 60);
var hours = Math.floor(mins / 60);
mins = mins % 60;
var response='';
if (hours>0) response+=hours+'h ';
if (hours>0 || mins>0) response+=mins+'m ';
response+=secs+'s';
return response;
}
function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) {
const sep = path.sep;
const initDir = path.isAbsolute(targetDir) ? sep : '';
const baseDir = isRelativeToScript ? __dirname : '.';
return targetDir.split(sep).reduce((parentDir, childDir) => {
const curDir = path.resolve(baseDir, parentDir, childDir);
try {
fs.mkdirSync(curDir);
} catch (err) {
if (err.code === 'EEXIST') { // curDir already exists!
return curDir;
}
// To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.
if (err.code === 'ENOENT') { // Throw the original parentDir error on curDir `ENOENT` failure.
logger.error(`EACCES: permission denied, mkdir '${parentDir}'`);
return null;
}
const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1;
if (!caughtErr || (caughtErr && curDir === path.resolve(targetDir))) {
logger.error("'EACCES', 'EPERM', 'EISDIR' during mkdir");
return null;
}
}
return curDir;
}, initDir);
}
function simpleStringify (object){
var simpleObject = {};
for (var prop in object ){
if (!object.hasOwnProperty(prop)){
continue;
}
if (typeof(object[prop]) == 'object'){
continue;
}
if (typeof(object[prop]) == 'function'){
continue;
}
simpleObject[prop] = object[prop];
}
return JSON.stringify(simpleObject); // returns cleaned up JSON
};
function writeFileSync(path, buffer, permission) {
var fileDescriptor;
try {
fileDescriptor = fs.openSync(path, 'w', permission);
} catch (e) {
fs.chmodSync(path, permission);
fileDescriptor = fs.openSync(path, 'w', permission);
}
if (fileDescriptor) {
fs.writeSync(fileDescriptor, buffer, 0, buffer.length, 0);
fs.closeSync(fileDescriptor);
logger.info("writeFileSync wiriting to '"+path+"' successful");
return true;
}
logger.error("writeFileSync writing to '"+path+"' failed");
return false;
}
function moveUploadedFile(buffer, directory, filename) {
logger.info("moveUploadedFile called with '"+filename+"' -> '"+directory+"'");
if (directory.indexOf("..")>=0 || filename.indexOf("..")>=0) {
logger.error("moveUploadedFile failed, .. in directory or filename");
return false;
}
if (config.storagePath.lastIndexOf("/")!==config.storagePath.length-1)
directory='/'+directory;
if (directory.lastIndexOf("/")!==directory.length-1)
directory=directory+'/';
var finalPath = mkDirByPathSync(config.storagePath+directory, {isRelativeToScript: (config.storagePath.indexOf("/")===0 ? false : true)});
if (finalPath && finalPath.length>0) {
if (writeFileSync(finalPath+"/"+filename, buffer, 0o660)) {
logger.info("moveUploadedFile successfully written '"+(finalPath+"/"+filename)+"'");
return finalPath+"/"+filename;
}
logger.error("moveUploadedFile failed to writeFileSync");
return false;
}
logger.error("moveUploadedFile invalid final path, check permissions to create / write '"+(config.storagePath+directory)+"'");
return false;
};
var segmentProcessQueue=[];
var segmentProcessPosition=0;
var affectedDrives={};
var rlog_lastTs=0;
var rlog_prevLat=-1000;
var rlog_prevLng=-1000;
var rlog_totalDist = 0;
var qcamera_duration = 0;
function processSegmentRLog(rLogPath) {
rlog_lastTs=0;
rlog_prevLat=-1000;
rlog_prevLng=-1000;
rlog_totalDist = 0;
return new Promise(
function(resolve, reject) {
var readStream = fs.createReadStream(rLogPath);
var reader = Reader(readStream);
readStream.on('close', function () {
resolve();
});
reader(function (obj) {
try {
if (obj['LogMonoTime']!==undefined && obj['LogMonoTime']-rlog_lastTs>=1000000*1000*1 && obj['GpsLocation']!==undefined) {
logger.info('processSegmentRLog GpsLocation @ '+obj['LogMonoTime']+': '+obj['GpsLocation']['Latitude']+' '+obj['GpsLocation']['Longitude']);
if (rlog_prevLat!=-1000) {
var lat1=rlog_prevLat;
var lat2=obj['GpsLocation']['Latitude'];
var lon1=rlog_prevLng;
var lon2=obj['GpsLocation']['Longitude'];
var p = 0.017453292519943295; // Math.PI / 180
var c = Math.cos;
var a = 0.5 - c((lat2 - lat1) * p)/2 +
c(lat1 * p) * c(lat2 * p) *
(1 - c((lon2 - lon1) * p))/2;
var dist_m = 1000 * 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
rlog_totalDist+=dist_m;
//console.log('---> distance traveled is: '+dist_m);
}
rlog_prevLat=obj['GpsLocation']['Latitude'];
rlog_prevLng=obj['GpsLocation']['Longitude'];
rlog_lastTs = obj['LogMonoTime'];
}
} catch(exception) {
}
});
}
);
}
function processSegmentVideo(qcameraPath) {
qcamera_duration=0;
return new Promise(function(resolve, reject) {
ffprobe(qcameraPath, { path: ffprobeStatic.path })
.then(function (info) {
if (info['streams']!==undefined && info['streams'][0]!==undefined && info['streams'][0]['duration']!==undefined)
qcamera_duration = info['streams'][0]['duration'];
logger.info('processSegmentVideo duration: '+qcamera_duration+'s');
resolve();
})
.catch(function (err) {
console.error(err);
logger.error('processSegmentVideo error: '+err);
resolve();
});
});
}
function processSegmentsRecursive() {
if (segmentProcessQueue.length<=segmentProcessPosition)
return updateDrives();
var segmentWrapper = segmentProcessQueue[segmentProcessPosition];
var segment = db.get('drive_segments').find({dongle_id: segmentWrapper.segment.dongle_id, drive_identifier: segmentWrapper.segment.drive_identifier, segment_id: segmentWrapper.segment.segment_id});
const uploadComplete = segmentWrapper.uploadComplete;
const driveIdentifier = segmentWrapper.driveIdentifier;
const fileStatus = segmentWrapper.fileStatus;
logger.info('processSegmentsRecursive '+segment.value().dongle_id+' '+segment.value().drive_identifier+' '+segment.value().segment_id);
var p1 = processSegmentRLog(fileStatus['rlog.bz2']);
var p2 = processSegmentVideo(fileStatus['qcamera.ts']);
Promise.all([p1, p2]).then((values) => {
logger.info('processSegmentsRecursive '+segment.value().dongle_id+' '+segment.value().drive_identifier+' '+segment.value().segment_id+' '+(Math.round(rlog_totalDist*100)/100)+'m, duration: '+qcamera_duration+'s');
var updates={duration: qcamera_duration, distance_meters: Math.round(rlog_totalDist*10)/10, is_processed: true, upload_complete: uploadComplete, is_stalled: false};
segment.assign(updates).write();
affectedDrives[driveIdentifier]=true;
segmentProcessPosition++;
setTimeout(function() {processSegmentsRecursive();}, 0);
}).catch(function () { });
}
function updateSegments() {
segmentProcessQueue=[];
segmentProcessPosition=0;
affectedDrives={};
drive_segments = db.get('drive_segments').filter({upload_complete: false, is_stalled: false}).sortBy('created').take(10000).value();
for (var t=0; t<drive_segments.length; t++) {
var segment = drive_segments[t];
var dongleIdHash = crypto.createHmac('sha256', config.applicationSalt).update(segment.dongle_id).digest('hex');
var driveIdentifierHash = crypto.createHmac('sha256', config.applicationSalt).update(segment.drive_identifier).digest('hex');
const directoryTree = dirTree(config.storagePath+segment.dongle_id+"/"+dongleIdHash+"/"+driveIdentifierHash+"/"+segment.drive_identifier+"/"+segment.segment_id);
var qcamera = false;
var fcamera = false;
var dcamera = false;
var qlog = false;
var rlog = false;
var fileStatus = {'fcamera.hevc': false, 'dcamera.hevc' : false, 'qcamera.ts': false, 'qlog.bz2' : false, 'rlog.bz2' : false};
for (var i in directoryTree.children) {
fileStatus[directoryTree.children[i].name]=directoryTree.children[i].path;
}
var uploadComplete=false;
if (fileStatus['qcamera.ts']!==false && fileStatus['fcamera.hevc']!==false && fileStatus['rlog.bz2']!==false && fileStatus['qlog.bz2']!==false) // upload complete
uploadComplete=true;
if (fileStatus['qcamera.ts']!==false && fileStatus['rlog.bz2']!==false && !segment.is_processed) { // can process
segmentProcessQueue.push({segment: segment, fileStatus: fileStatus, uploadComplete: uploadComplete, driveIdentifier: segment.dongle_id+"|"+segment.drive_identifier});
}
else if (uploadComplete) {
logger.info('updateSegments uploadComplete for '+segment.dongle_id+' '+segment.drive_identifier+' '+segment.segment_id);
var updateSegment = db.get('drive_segments').find({dongle_id: segment.dongle_id, drive_identifier: segment.drive_identifier, segment_id: segment.segment_id});
var updates={upload_complete: true, is_stalled: false};
updateSegment.assign(updates).write();
affectedDrives[segment.dongle_id+"|"+segment.drive_identifier]=true;
}
else if (Date.now()-segment.created>10*24*3600*1000) { // ignore non-uploaded segments after 10 days until a new upload_url is requested (which resets is_stalled)
logger.info('updateSegments isStalled for '+segment.dongle_id+' '+segment.drive_identifier+' '+segment.segment_id);
var updateSegment = db.get('drive_segments').find({dongle_id: segment.dongle_id, drive_identifier: segment.drive_identifier, segment_id: segment.segment_id});
var updates={is_stalled: true};
updateSegment.assign(updates).write();
}
if (segmentProcessQueue.length>50) // we process at most 50 segments per batch
break;
}
if (segmentProcessQueue.length>0)
processSegmentsRecursive();
else // if no data is to be collected, call updateDrives to update those where eventually just the last segment completed the upload
updateDrives();
}
function updateDrives() {
// go through all affected drives and update them / complete and/or build m3u8
logger.info("updateDrives - affected drives: "+JSON.stringify(affectedDrives));
for (const [key, value] of Object.entries(affectedDrives)) {
[dongleId, driveIdentifier] = key.split('|');
var drive = db.get('drives').find({ identifier: driveIdentifier, dongle_id: dongleId});
if (!drive.value()) continue;
var dongleIdHash = crypto.createHmac('sha256', config.applicationSalt).update(drive.value().dongle_id).digest('hex');
var driveIdentifierHash = crypto.createHmac('sha256', config.applicationSalt).update(drive.value().identifier).digest('hex');
var driveUrl=config.baseDriveDownloadUrl+drive.value().dongle_id+"/"+dongleIdHash+"/"+driveIdentifierHash+"/"+drive.value().identifier;
var drivePath=config.storagePath+drive.value().dongle_id+"/"+dongleIdHash+"/"+driveIdentifierHash+"/"+drive.value().identifier;
var uploadComplete=true;
var isProcessed=true;
var totalDistanceMeters=0;
var totalDurationSeconds=0;
var playlistSegmentStrings='';
drive_segments = db.get('drive_segments').filter({drive_identifier: driveIdentifier, dongle_id: dongleId}).sortBy('created').take(10000).value();
for (var t=0; t<drive_segments.length; t++) {
if (!drive_segments[t].upload_complete) uploadComplete=false;
if (!drive_segments[t].is_processed) isProcessed=false;
else {
totalDistanceMeters+=parseFloat(drive_segments[t].distance_meters);
totalDurationSeconds+=parseFloat(drive_segments[t].duration);
playlistSegmentStrings+=`#EXTINF:`+drive_segments[t].duration+`,`+drive_segments[t].segment_id+`\n`+
driveUrl+`/`+drive_segments[t].segment_id+`/qcamera.ts\n`;
}
}
var updates = {distance_meters: Math.round(totalDistanceMeters), duration: totalDurationSeconds, upload_complete : uploadComplete, is_processed : isProcessed};
if (uploadComplete) {
updates['filesize'] = 0;
const execSync = require('child_process').execSync;
try {
var dongleIdHash = crypto.createHmac('sha256', config.applicationSalt).update(dongleId).digest('hex');
var driveIdentifierHash = crypto.createHmac('sha256', config.applicationSalt).update(driveIdentifier).digest('hex');
updates['filesize'] = parseInt(execSync("du -s "+drivePath+" | awk -F'\t' '{print $1;}'").toString()); // in kilobytes
}
catch (exception) {}
}
logger.info("updateDrives drive "+dongleId+" "+driveIdentifier+" uploadComplete: "+JSON.stringify(updates));
drive.assign(updates).write();
if (isProcessed) {
// create the playlist file m3u8 for cabana
var playlist = `#EXTM3U\n`+
`#EXT-X-VERSION:3\n`+
`#EXT-X-TARGETDURATION:61\n`+
`#EXT-X-MEDIA-SEQUENCE:0\n`+
`#EXT-X-PLAYLIST-TYPE:VOD\n`+
playlistSegmentStrings+`\n`+
`#EXT-X-ENDLIST`;
fs.writeFileSync(drivePath+'/qcamera.m3u8', playlist);
}
}
setTimeout(function() {mainWorkerLoop();}, 0);
}
function deleteExpiredDrives() {
// TODO: implement
}
function removeDeletedDrivesPhysically() {
// TODO: implement
}
function mainWorkerLoop() {
if (Date.now()-startTime>60*3600*1000) {
logger.info("EXIT WORKER AFTER 1 HOUR TO PREVENT MEMORY LEAKS...");
process.exit();
}
deleteExpiredDrives();
removeDeletedDrivesPhysically();
setTimeout(function() {updateSegments();}, 5000);
}
startTime=Date.now();
lockfile.lock('retropilot_worker.lock', { realpath: false, stale: 30000, update: 2000 })
.then((release) => {
logger.info("STARTING WORKER...");
initializeDatabase();
initializeStorage();
setTimeout(function() {mainWorkerLoop();}, 0);
}).catch((e) => {
console.error(e)
process.exit();
});