git.iko.soy/rowtype-yoga/psfp
on master help sign in
Add some form of auth
f8046d6Mark Eibes
server/.gitignore+2−1
@@ -1,3 +1,4 @@
1+.authorized-keys
1/bower_components/
2/bower_components/
2/node_modules/
3/node_modules/
3/.pulp-cache/
4/.pulp-cache/
@@ -8,4 +9,4 @@
8/.purs*
9/.purs*
9/.psa*
10/.psa*
10/.spago/
11/.spago/
11−.DS_Store
12+.DS_Store
server/.vscode/settings.json+1−1
@@ -1,5 +1,5 @@
1{
1{
2− "editor.formatOnSave": false,
2+ "editor.formatOnSave": true,
3 "regreplace.commands": [
3 "regreplace.commands": [
4 {
4 {
5 "name": "replace double colon with unicode version",
5 "name": "replace double colon with unicode version",
server/access.lognew file+1
@@ -0,0 +1 @@
1+Sun, 09 Aug 2020 16:43:40 GMT: Denied request without token2020-08-09T16:53:17.487Z: Denied request without token2020-08-09T16:56:46.287Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:03.649Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:04.280Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:04.467Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:04.674Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:04.851Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:05.017Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:05.198Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:05.366Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:05.549Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:05.766Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:05.930Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:06.118Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:06.293Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:06.472Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K2020-08-09T16:57:06.831Z: Access granted to LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQXhUMDlUUngzbFo2QVEvL2RzNG5QVW5KMk1YT0pXQlVhNVUvS0drMjJnRjQ3SkxBSTM1VzhkN1NWWndvCkhhN2phZHZVM1BaNEFuaktVQXhUdysxNURYUWM0RDJycEx1Q3gvSzNGNXN2YUN5djY3UlhiTm5YOVJiRGo3a1cKNm5rRVg5SmVzdFRZbU9oQnB4MjRqQXVHSnV5bmFGUlhnNnJscG80dlBBVTYvZGw2d1hnQlBRNXFvZnJVVWlZcwo5T1NhS051VWdvRzZOMVVWSUYvS25Ya2M5UUpYbG1xSE1JMGovWE1FM01xZ2xuejY5ZUlCVjZqUVRFMldENW1tCllQQmpSdSs2eUxJVTZUUXNzZUZPSmJNY0thd0l6eEoxTlNkOVU2L25Ba3VZTWtzTGhlSllmd2xhQkZaL0ZweksKS3hoM0xQTVZRTFhEVjIzODlIclcxS3lTTitFdUMyVWJ3cllYTWttVHY4NUhUbkhoN3RvWG0xWkRSTTRlODE4egp5dnRnRmNPRFBtUS84ekwyMWpPdzRrei9RNDBNUGd4OW1vMGw0K3BmQzZTRjF6MUdiMER0OWtWT2VkdFkwdlVZCmJJZWVoV1BWSldnOThHRjdXOTFrcTFwQjN3b2tqdjZWa1ZHM0syMnE1QUhudnVsTjRITE92Y0djV1FLOGFwZVMKK3Z2UDVBdjJBM3JHSjBZSUdmL05ycDRaTFpHMHBYYlI4UVdNS2RkdUVVM1pYdFlxV0dTWEx0MlpyKzdNakFFdwpyQ2VjaVcraHNwMzArcUpJcGp4VUpBNXVCdFFyMHBDMGtrTkRITEdJSEpmcG5WRUU2MkJMSGFjczVVRkFsOFg0CmdhanV2MUlyQ0lMVFQ3cVZSczB6OEJaUE1uemd5SVJtYmF6K05PbjlhaGFTOVhFUWE3b0JHVDRwV2NUVHNhelMKUXdHMVdRdjNMMG54bjBMcmpoZGpPSnE1a2QxcDdpQlkwd0trRHZlNU1aUUxHK09FZ0IyYkhNZlloYUZzL21pMQppMnNndHRTYTByMWNTaGl3M0NPWVpSRmp5V3M9Cj1wWHlRCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0K
server/src/Auth/Handler.pursnew file+46
@@ -0,0 +1,46 @@
1+module Auth.Handler where
2+
3+import Prelude
4+import Auth.Types (Token(..))
5+import Data.Array (elem)
6+import Data.JSDate (now, toISOString)
7+import Data.Maybe (Maybe(..))
8+import Data.String (Pattern(..), stripPrefix)
9+import Data.String.Utils (lines)
10+import Effect.Aff (Aff)
11+import Effect.Aff.Class (liftAff)
12+import Effect.Class (liftEffect)
13+import Node.Encoding (Encoding(..))
14+import Node.Express.Handler (HandlerM, next)
15+import Node.Express.Request (getRequestHeader)
16+import Node.Express.Response (end, setStatus)
17+import Node.Express.Response as Response
18+import Node.FS.Aff (appendTextFile, readTextFile)
19+import Simple.JSON (write)
20+
21+readAllowedTokens ∷ Aff (Array Token)
22+readAllowedTokens = do
23+ text <- readTextFile UTF8 ".authorized-keys"
24+ pure $ Token <$> lines text
25+
26+authHandler ∷ Array Token -> HandlerM Unit
27+authHandler authorizedTokens = do
28+ authHeader <- getRequestHeader "Authorization"
29+ let maybeToken = authHeader >>= stripPrefix (Pattern "Bearer ") <#> Token
30+ ts <- now >>= toISOString # liftEffect
31+ let log s = liftAff $ appendTextFile UTF8 "access.log" $ ts <> ": " <> s
32+ case maybeToken of
33+ Nothing -> do
34+ setStatus 401
35+ log $ "Denied request without token"
36+ Response.send $ write { error: "Jetzt hör'n Sie mir mal zu! Erstens brauch ich ihren Namen!!" }
37+ end
38+ Just token@(Token raw)
39+ | elem token authorizedTokens -> do
40+ log $ "Access granted to " <> raw
41+ next
42+ Just (Token invalid) -> do
43+ setStatus 403
44+ log $ "Access denied to " <> invalid
45+ Response.send $ write { error: "Ich seh' ja ein das man hier der gläserne Mensch ist." }
46+ end
server/src/Auth/Types.pursnew file+7
@@ -0,0 +1,7 @@
1+module Auth.Types where
2+
3+import Prelude
4+
5+newtype Token = Token String
6+
7+derive instance eqToken ∷ Eq Token
server/src/JobQueue.purs+25−30
@@ -1,7 +1,6 @@
1module JobQueue where
1module JobQueue where
2
2
3import Prelude
3import Prelude
4−
5import Data.Array (delete, filter, find, length, snoc, zip)
4import Data.Array (delete, filter, find, length, snoc, zip)
6import Data.Foldable (for_)
5import Data.Foldable (for_)
7import Data.Generic.Rep (class Generic)
6import Data.Generic.Rep (class Generic)
@@ -19,17 +18,17 @@ import Effect.Class (class MonadEffect, liftEffect)
19import Effect.Ref (Ref)
18import Effect.Ref (Ref)
20import Effect.Ref as Ref
19import Effect.Ref as Ref
21
20
22−newtype JobId
21+newtype JobId = JobId UUID
23− = JobId UUID
22+
23+derive instance ntJobId ∷ Newtype JobId _
24+derive instance genericJobId ∷ Generic JobId _
24
25
25−derive instance ntJobId :: Newtype JobId _
26−derive instance genericJobId :: Generic JobId _
27−instance showJobId :: Show JobId where
26+instance showJobId ∷ Show JobId where
28 show = genericShow
27 show = genericShow
29
28
30−derive newtype instance jobIdEq :: Eq JobId
29+derive newtype instance jobIdEq ∷ Eq JobId
31−newtype NewJob a
30+
32− = NewJob (JobId -> a -> Aff Unit)
31+newtype NewJob a = NewJob (JobId -> a -> Aff Unit)
33
32
34data PendingJob a
33data PendingJob a
35 = PendingJob
34 = PendingJob
@@ -47,10 +46,10 @@ getAddedAt = case _ of
47 Pending (PendingJob { addedAt }) -> addedAt
46 Pending (PendingJob { addedAt }) -> addedAt
48 Running (RunningJob { addedAt }) -> addedAt
47 Running (RunningJob { addedAt }) -> addedAt
49
48
50−instance eqJob :: Eq (Job a) where
49+instance eqJob ∷ Eq (Job a) where
51 eq j1 j2 = getId j1 == getId j2
50 eq j1 j2 = getId j1 == getId j2
52
51
53−instance ordJob :: Ord (Job a) where
52+instance ordJob ∷ Ord (Job a) where
54 compare j1 j2 = compare (getAddedAt j1) (getAddedAt j2)
53 compare j1 j2 = compare (getAddedAt j1) (getAddedAt j2)
55
54
56getId ∷ ∀ a. Job a -> JobId
55getId ∷ ∀ a. Job a -> JobId
@@ -58,8 +57,7 @@ getId = case _ of
58 Pending (PendingJob { id }) -> id
57 Pending (PendingJob { id }) -> id
59 Running (RunningJob { id }) -> id
58 Running (RunningJob { id }) -> id
60
59
61−newtype RunningJob a
60+newtype RunningJob a = RunningJob
62− = RunningJob
63 { id ∷ JobId
61 { id ∷ JobId
64 , addedAt ∷ JSDate
62 , addedAt ∷ JSDate
65 , startedAt ∷ JSDate
63 , startedAt ∷ JSDate
@@ -67,15 +65,15 @@ newtype RunningJob a
67 , resource ∷ a
65 , resource ∷ a
68 }
66 }
69
67
70−type QueueParams
68+type QueueParams =
71− = { maxSize ∷ Int
69+ { maxSize ∷ Int
72− , timeout ∷ Milliseconds
70+ , timeout ∷ Milliseconds
73− }
71+ }
72+
73+newtype ResourcePool a = ResourcePool (Array a)
74
74
75−newtype ResourcePool a
75+derive instance ntResourcePool ∷ Newtype (ResourcePool a) _
76− = ResourcePool (Array a)
77
76
78−derive instance ntResourcePool :: Newtype (ResourcePool a) _
79data Queue a
77data Queue a
80 = Queue QueueParams (Ref (ResourcePool a)) (Ref (Array (Job a)))
78 = Queue QueueParams (Ref (ResourcePool a)) (Ref (Array (Job a)))
81
79
@@ -90,9 +88,10 @@ data EnqueueResult
90 = Enqueued JobId
88 = Enqueued JobId
91 | QueueFull
89 | QueueFull
92
90
93−derive instance eqEnqueueResult :: Eq EnqueueResult
91+derive instance eqEnqueueResult ∷ Eq EnqueueResult
94−derive instance genericEnqueueResult :: Generic EnqueueResult _
92+derive instance genericEnqueueResult ∷ Generic EnqueueResult _
93+
95−instance showEnqueueResult :: Show EnqueueResult where
94+instance showEnqueueResult ∷ Show EnqueueResult where
96 show = genericShow
95 show = genericShow
97
96
98startJob ∷ ∀ a. Eq a => Queue a -> a -> PendingJob a -> Effect Unit
97startJob ∷ ∀ a. Eq a => Queue a -> a -> PendingJob a -> Effect Unit
@@ -100,14 +99,12 @@ startJob q@(Queue { timeout } _ jobsRef) resource (PendingJob { id, job, addedAt
100 startedAt <- JSDate.now
99 startedAt <- JSDate.now
101 iso <- JSDate.toISOString startedAt
100 iso <- JSDate.toISOString startedAt
102 -- log $ "Starting Job: " <> toString (unwrap id) <> " at: " <> iso
101 -- log $ "Starting Job: " <> toString (unwrap id) <> " at: " <> iso
103− let
104− remove = removeFromQueue id q
102+ let remove = removeFromQueue id q
105 fiber <-
103 fiber <-
106 launchAff <<< sequential
104 launchAff <<< sequential
107 $ parallel (job resource *> remove)
105 $ parallel (job resource *> remove)
108 *> parallel (delay timeout *> remove)
106 *> parallel (delay timeout *> remove)
109− let
110− running = RunningJob { id, fiber, addedAt, startedAt, resource }
107+ let running = RunningJob { id, fiber, addedAt, startedAt, resource }
111 jobsRef
108 jobsRef
112 # Ref.modify_ (map \j -> if getId j == id then Running running else j)
109 # Ref.modify_ (map \j -> if getId j == id then Running running else j)
113
110
@@ -151,7 +148,6 @@ removeStaleJobs q@(Queue { timeout } resourceRef jobsRef) = do
151 $ \(RunningJob { startedAt, id }) -> do
148 $ \(RunningJob { startedAt, id }) -> do
152 let
149 let
153 runningSince = (nowMillis - JSDate.getTime startedAt) # Milliseconds
150 runningSince = (nowMillis - JSDate.getTime startedAt) # Milliseconds
154−
155 timedOut = runningSince > timeout
151 timedOut = runningSince > timeout
156 when timedOut $ launchAff_ (removeFromQueue id q)
152 when timedOut $ launchAff_ (removeFromQueue id q)
157
153
@@ -179,8 +175,7 @@ enqueue (NewJob newJob) q@(Queue { maxSize, timeout } _ jobsRef) = do
179 pure QueueFull
175 pure QueueFull
180 else do
176 else do
181 addedAt <- JSDate.now
177 addedAt <- JSDate.now
182− let
183− new = Pending (PendingJob { id, addedAt, job: newJob id })
178+ let new = Pending (PendingJob { id, addedAt, job: newJob id })
184 jobsRef # Ref.modify_ (_ `snoc` new)
179 jobsRef # Ref.modify_ (_ `snoc` new)
185 startJobs q
180 startJobs q
186 pure (Enqueued id)
181 pure (Enqueued id)
server/src/JobQueueSpec.purs+10−14
@@ -1,7 +1,6 @@
1module JobQueueSpec where
1module JobQueueSpec where
2
2
3import Prelude
3import Prelude
4−
5import Data.Time.Duration (class Duration, Seconds(..), fromDuration)
4import Data.Time.Duration (class Duration, Seconds(..), fromDuration)
6import Effect.Aff (Milliseconds(..), delay)
5import Effect.Aff (Milliseconds(..), delay)
7import Effect.Class (liftEffect)
6import Effect.Class (liftEffect)
@@ -36,14 +35,14 @@ writeRefJob ref a d =
36 delay millis
35 delay millis
37 Ref.write a ref # liftEffect
36 Ref.write a ref # liftEffect
38
37
39−pool :: ResourcePool Unit
38+pool ∷ ResourcePool Unit
40−pool = ResourcePool [unit, unit, unit]
39+pool = ResourcePool [ unit, unit, unit ]
41
40
42spec ∷ Spec Unit
41spec ∷ Spec Unit
43spec = do
42spec = do
44 describe "The Queue" do
43 describe "The Queue" do
45 it "can't enqueue if maxSize is 0" do
44 it "can't enqueue if maxSize is 0" do
46− q <- mkQueue (params { maxSize = 0 }) pool
45+ q <- mkQueue (params { maxSize = 0 }) pool
47 r <- Q.enqueue job1 q # liftEffect
46 r <- Q.enqueue job1 q # liftEffect
48 r `shouldEqual` QueueFull
47 r `shouldEqual` QueueFull
49 it "can enqueue if maxSize is 1" do
48 it "can enqueue if maxSize is 1" do
@@ -67,10 +66,9 @@ spec = do
67 delay (9.0 # Milliseconds)
66 delay (9.0 # Milliseconds)
68 res `shouldEqual` false
67 res `shouldEqual` false
69 it "runs multiple jobs" do
68 it "runs multiple jobs" do
70− q <- mkQueue (params { maxSize = 3, timeout = (2.0 # Seconds # fromDuration) }) (ResourcePool [unit])
69+ q <- mkQueue (params { maxSize = 3, timeout = (2.0 # Seconds # fromDuration) }) (ResourcePool [ unit ])
71 ref <- Ref.new 0 # liftEffect
70 ref <- Ref.new 0 # liftEffect
72− let
73− enq n delay = Q.enqueue (writeRefJob ref n delay) q # liftEffect
71+ let enq n delay = Q.enqueue (writeRefJob ref n delay) q # liftEffect
74 r₁ <- enq 1 (0.1 # Seconds)
72 r₁ <- enq 1 (0.1 # Seconds)
75 r₁ `shouldNotEqual` QueueFull
73 r₁ `shouldNotEqual` QueueFull
76 r₂ <- enq 2 (0.2 # Seconds)
74 r₂ <- enq 2 (0.2 # Seconds)
@@ -85,10 +83,9 @@ spec = do
85 delay (0.3 # Seconds # fromDuration)
83 delay (0.3 # Seconds # fromDuration)
86 Ref.read ref # liftEffect >>= shouldEqual 3 -- ~350ms
84 Ref.read ref # liftEffect >>= shouldEqual 3 -- ~350ms
87 it "respects pool size" do
85 it "respects pool size" do
88− q <- mkQueue (params { maxSize = 3, timeout = (2.0 # Seconds # fromDuration) }) (ResourcePool [unit])
86+ q <- mkQueue (params { maxSize = 3, timeout = (2.0 # Seconds # fromDuration) }) (ResourcePool [ unit ])
89 ref <- Ref.new 0 # liftEffect
87 ref <- Ref.new 0 # liftEffect
90− let
91− enq n delay = Q.enqueue (writeRefJob ref n delay) q # liftEffect
88+ let enq n delay = Q.enqueue (writeRefJob ref n delay) q # liftEffect
92 r₁ <- enq 1 (0.1 # Seconds)
89 r₁ <- enq 1 (0.1 # Seconds)
93 r₁ `shouldNotEqual` QueueFull
90 r₁ `shouldNotEqual` QueueFull
94 r₂ <- enq 2 (0.1 # Seconds)
91 r₂ <- enq 2 (0.1 # Seconds)
@@ -103,10 +100,9 @@ spec = do
103 delay (0.3 # Seconds # fromDuration)
100 delay (0.3 # Seconds # fromDuration)
104 Ref.read ref # liftEffect >>= shouldEqual 3 -- ~350ms
101 Ref.read ref # liftEffect >>= shouldEqual 3 -- ~350ms
105 it "respects pool size (2)" do
102 it "respects pool size (2)" do
106− q <- mkQueue (params { maxSize = 3, timeout = (2.0 # Seconds # fromDuration) }) (ResourcePool [unit, unit, unit])
103+ q <- mkQueue (params { maxSize = 3, timeout = (2.0 # Seconds # fromDuration) }) (ResourcePool [ unit, unit, unit ])
107 ref <- Ref.new 0 # liftEffect
104 ref <- Ref.new 0 # liftEffect
108− let
109− enq n delay = Q.enqueue (writeRefJob ref n delay) q # liftEffect
105+ let enq n delay = Q.enqueue (writeRefJob ref n delay) q # liftEffect
110 r₁ <- enq 1 (0.1 # Seconds)
106 r₁ <- enq 1 (0.1 # Seconds)
111 r₂ <- enq 2 (0.1 # Seconds)
107 r₂ <- enq 2 (0.1 # Seconds)
112 r₃ <- enq 3 (0.1 # Seconds)
108 r₃ <- enq 3 (0.1 # Seconds)
@@ -117,7 +113,7 @@ spec = do
117 delay (0.15 # Seconds # fromDuration)
113 delay (0.15 # Seconds # fromDuration)
118 Ref.read ref # liftEffect >>= shouldEqual 3 -- ~150ms
114 Ref.read ref # liftEffect >>= shouldEqual 3 -- ~150ms
119 it "can kill processes" do
115 it "can kill processes" do
120− q <- mkQueue (params { maxSize = 1, timeout = (1.0 # Seconds # fromDuration) }) (ResourcePool [unit])
116+ q <- mkQueue (params { maxSize = 1, timeout = (1.0 # Seconds # fromDuration) }) (ResourcePool [ unit ])
121 resultRef <- Ref.new false # liftEffect
117 resultRef <- Ref.new false # liftEffect
122 let
118 let
123 job =
119 job =
server/src/Main.purs+43−57
@@ -1,9 +1,10 @@
1module Main where
1module Main where
2
2
3import Prelude
3import Prelude
4−
4+import Auth.Handler (authHandler, readAllowedTokens)
5+import Auth.Types (Token(..))
5import Control.Parallel (parOneOf, parTraverse)
6import Control.Parallel (parOneOf, parTraverse)
6−import Data.Array ((..))
7+import Data.Array (elem, (..))
7import Data.Bifunctor (lmap)
8import Data.Bifunctor (lmap)
8import Data.Either (Either(..))
9import Data.Either (Either(..))
9import Data.Int (fromString)
10import Data.Int (fromString)
@@ -25,10 +26,10 @@ import Node.ChildProcess (ExecResult)
25import Node.Encoding (Encoding(..))
26import Node.Encoding (Encoding(..))
26import Node.Express.App (App, listenHttp, listenHttps, use, useExternal)
27import Node.Express.App (App, listenHttp, listenHttps, use, useExternal)
27import Node.Express.App as E
28import Node.Express.App as E
28−import Node.Express.Handler (HandlerM(..), Handler)
29+import Node.Express.Handler (Handler, HandlerM(..), next)
29import Node.Express.Middleware.Static (static)
30import Node.Express.Middleware.Static (static)
30−import Node.Express.Request (getBody')
31+import Node.Express.Request (getBody', getRequestHeader)
31−import Node.Express.Response (setStatus)
32+import Node.Express.Response (end, setStatus)
32import Node.Express.Response as Response
33import Node.Express.Response as Response
33import Node.Express.Types (Request, Response)
34import Node.Express.Types (Request, Response)
34import Node.FS.Aff (readTextFile)
35import Node.FS.Aff (readTextFile)
@@ -48,40 +49,41 @@ toBody result =
48 $ ado
49 $ ado
49 stdout <- Buffer.toString UTF8 result.stdout
50 stdout <- Buffer.toString UTF8 result.stdout
50 stderr <- Buffer.toString UTF8 result.stderr
51 stderr <- Buffer.toString UTF8 result.stderr
51− let
52− (code ∷ Maybe Int) = asErrorWithCode result >>= _.code
52+ let (code ∷ Maybe Int) = asErrorWithCode result >>= _.code
53 in { code, stdout, stderr } ∷ RunResult
53 in { code, stdout, stderr } ∷ RunResult
54
54
55−type ErrorWithCode
55+type ErrorWithCode =
56− = { code ∷ Maybe Int }
56+ { code ∷ Maybe Int }
57
57
58asErrorWithCode ∷ ∀ a. a -> Maybe ErrorWithCode
58asErrorWithCode ∷ ∀ a. a -> Maybe ErrorWithCode
59asErrorWithCode = read_ <<< unsafeToForeign
59asErrorWithCode = read_ <<< unsafeToForeign
60
60
61runCode ∷ ∀ d. Duration d => d -> Folder -> Aff (Maybe ExecResult)
61runCode ∷ ∀ d. Duration d => d -> Folder -> Aff (Maybe ExecResult)
62−runCode timeout folder = parOneOf
62+runCode timeout folder =
63+ parOneOf
63− [ Just <$> execCommand folder "exec node run.js"
64+ [ Just <$> execCommand folder "exec node run.js"
64− , Nothing <$ delay (timeout # fromDuration)
65+ , Nothing <$ delay (timeout # fromDuration)
65− ]
66+ ]
66
67
67compileAndRunJob ∷ CompileRequest -> (Handler -> Aff Unit) -> NewJob PscIdeConnection
68compileAndRunJob ∷ CompileRequest -> (Handler -> Aff Unit) -> NewJob PscIdeConnection
68compileAndRunJob json handle =
69compileAndRunJob json handle =
69 NewJob \jobId conn -> do
70 NewJob \jobId conn -> do
70 stringOrErr <- attempt $ compileCode json.code conn
71 stringOrErr <- attempt $ compileCode json.code conn
71− case ((readJSON <$> stringOrErr) ∷ _ _ (_ _ CompileResult)) of
72+ case ((readJSON <$> stringOrErr) ∷ _ _ (_ _ CompileResult)) of
72 Left e -> do
73 Left e -> do
73 handle $ setStatus 500
74 handle $ setStatus 500
74 log $ "Aff failed with " <> message e
75 log $ "Aff failed with " <> message e
75 handle $ Response.send $ write {}
76 handle $ Response.send $ write {}
77+ Right (Right res)
76− Right (Right res) | res.resultType == "error" -> do
78+ | res.resultType == "error" -> do
77− handle $ setStatus 422
79+ handle $ setStatus 422
78− handle $ Response.send $ write res
80+ handle $ Response.send $ write res
79 Right (Right res) -> do
81 Right (Right res) -> do
80− runResult <- runCode timeout (getFolder conn)
82+ runResult <- runCode timeout (getFolder conn)
81 case runResult of
83 case runResult of
82 Nothing -> do
84 Nothing -> do
83 handle $ setStatus 408
85 handle $ setStatus 408
84− handle $ Response.send $ write { error: "Timed out after running for " <> show timeout}
86+ handle $ Response.send $ write { error: "Timed out after running for " <> show timeout }
85 Just rr -> do
87 Just rr -> do
86 resultBody <- toBody rr
88 resultBody <- toBody rr
87 handle $ Response.send $ write (resultBody ∷ Body.RunResult)
89 handle $ Response.send $ write (resultBody ∷ Body.RunResult)
@@ -89,8 +91,8 @@ compileAndRunJob json handle =
89 handle $ setStatus 500
91 handle $ setStatus 500
90 log $ "Could not decode: " <> show (lmap (const "no way") stringOrErr) <> "\nErrors: " <> show errs
92 log $ "Could not decode: " <> show (lmap (const "no way") stringOrErr) <> "\nErrors: " <> show errs
91 handle $ Response.send $ write {}
93 handle $ Response.send $ write {}
92− where
94+ where
93− timeout = 1.0 # Seconds
95+ timeout = 1.0 # Seconds
94
96
95compileAndRunHandler ∷ Queue PscIdeConnection -> Handler
97compileAndRunHandler ∷ Queue PscIdeConnection -> Handler
96compileAndRunHandler queue = do
98compileAndRunHandler queue = do
@@ -99,7 +101,6 @@ compileAndRunHandler queue = do
99 HandlerM \req res next -> do
101 HandlerM \req res next -> do
100 let
102 let
101 handle = unHandler req res next
103 handle = unHandler req res next
102−
103 runJob = compileAndRunJob json handle
104 runJob = compileAndRunJob json handle
104 queueRes <- Q.enqueue runJob queue # liftEffect
105 queueRes <- Q.enqueue runJob queue # liftEffect
105 case queueRes of
106 case queueRes of
@@ -121,19 +122,20 @@ destFolder = "../playgrounds/"
121main ∷ Effect Unit
122main ∷ Effect Unit
122main = do
123main = do
123 launchAff_ do
124 launchAff_ do
125+ tokens <- readAllowedTokens
124 cpus <- numCpus # liftEffect
126 cpus <- numCpus # liftEffect
125 let
127 let
126 poolSize = max 2 (cpus / 2) -- use at least 2
128 poolSize = max 2 (cpus / 2) -- use at least 2
127−
128 mkFolder = Folder <<< (destFolder <> _) <<< show
129 mkFolder = Folder <<< (destFolder <> _) <<< show
129−
130+ connections <-
131+ (1 .. poolSize)
130− connections <- (1 .. poolSize) # parTraverse \n -> do
132+ # parTraverse \n -> do
133+ let
131− let folder = mkFolder n
134+ folder = mkFolder n
132− port = 14100 + n
135+ port = 14100 + n
133− log $ "Copying to folder " <> (un Folder folder)
136+ log $ "Copying to folder " <> (un Folder folder)
134− copy srcFolder (un Folder folder)
137+ copy srcFolder (un Folder folder)
135− mkConnection folder port
138+ mkConnection folder port
136−
137 let pool = ResourcePool connections
139 let pool = ResourcePool connections
138 q <-
140 q <-
139 Q.mkQueue
141 Q.mkQueue
@@ -141,38 +143,22 @@ main = do
141 , timeout: 10.0 # Seconds # fromDuration
143 , timeout: 10.0 # Seconds # fromDuration
142 }
144 }
143 pool
145 pool
144− serverSetup (makeApp q)
146+ serverSetup (makeApp tokens q)
145
147
146−makeApp ∷ Queue PscIdeConnection -> App
148+makeApp ∷ Array Token -> Queue PscIdeConnection -> App
147−makeApp q = do
149+makeApp tokens q = do
148 use $ static "assets"
150 use $ static "assets"
149 useExternal jsonBodyParser
151 useExternal jsonBodyParser
152+ use (authHandler tokens)
150 E.post "/compileAndRun" (compileAndRunHandler q)
153 E.post "/compileAndRun" (compileAndRunHandler q)
151
154
152−data CertificatesToUse
153− = UseLocalCertificates
154− | DoNotUseLocalCertificates
155−
156−parseCertificatesToUse ∷ String -> CertificatesToUse
157−parseCertificatesToUse = case _ of
158− "LOCAL" -> UseLocalCertificates
159− _ -> DoNotUseLocalCertificates
160−
161serverSetup ∷ App -> Aff Server
155serverSetup ∷ App -> Aff Server
162serverSetup app = do
156serverSetup app = do
163 maybePortString <- lookupEnv "PORT" # liftEffect
157 maybePortString <- lookupEnv "PORT" # liftEffect
164− let
165− port = maybePortString >>= fromString # fromMaybe 14188
158+ let port = maybePortString >>= fromString # fromMaybe 14188
166− useLocalCertificates <- fromMaybe DoNotUseLocalCertificates <<< map parseCertificatesToUse <$> lookupEnv "CERTS" # liftEffect
167− listen <- case useLocalCertificates of
168− UseLocalCertificates -> do
169− httpsOptions <- makeHttpsOptions
170− pure $ listenHttps app (port ∷ Int) httpsOptions
171− DoNotUseLocalCertificates -> pure $ listenHttp app port
172− liftEffect $ listen \_ -> info $ "psfp server started on port " <> show port
159+ liftEffect $ (listenHttp app port) \_ -> info $ "psfp server started on port " <> show port
173 where
160 where
174− makeHttpsOptions = do
161+ makeHttpsOptions = do
175− key <- readTextFile UTF8 "server.key"
162+ key <- readTextFile UTF8 "server.key"
176− cert <- readTextFile UTF8 "server.cert"
163+ cert <- readTextFile UTF8 "server.cert"
177− pure { key, cert }
164+ pure { key, cert }
178−
server/src/Middleware/JsonBodyParser.purs+1−2
@@ -1,9 +1,8 @@
1module Middleware.JsonBodyParser where
1module Middleware.JsonBodyParser where
2
2
3import Prelude
3import Prelude
4−
5import Data.Function.Uncurried (Fn3)
4import Data.Function.Uncurried (Fn3)
6import Effect (Effect)
5import Effect (Effect)
7import Node.Express.Types (Request, Response)
6import Node.Express.Types (Request, Response)
8
7
9−foreign import jsonBodyParser :: Fn3 Request Response (Effect Unit) (Effect Unit)
8+foreign import jsonBodyParser ∷ Fn3 Request Response (Effect Unit) (Effect Unit)
server/src/Node/OS.purs+1−1
@@ -2,4 +2,4 @@ module Node.OS where
2
2
3import Effect (Effect)
3import Effect (Effect)
4
4
5−foreign import numCpus :: Effect Int
5+foreign import numCpus ∷ Effect Int
server/src/Playground/Playground.purs+7−8
@@ -1,18 +1,17 @@
1module Playground.Playground where
1module Playground.Playground where
2
2
3import Prelude
3import Prelude
4−
5import Control.Promise (Promise, toAff)
4import Control.Promise (Promise, toAff)
6import Data.Function.Uncurried (Fn2, runFn2)
5import Data.Function.Uncurried (Fn2, runFn2)
7import Data.Newtype (class Newtype)
6import Data.Newtype (class Newtype)
8import Effect.Aff (Aff)
7import Effect.Aff (Aff)
9
8
10−newtype Folder
9+newtype Folder = Folder String
11− = Folder String
10+
12−derive instance ntFolder :: Newtype Folder _
11+derive instance ntFolder ∷ Newtype Folder _
13−derive newtype instance eqFolder :: Eq Folder
12+derive newtype instance eqFolder ∷ Eq Folder
14
13
15−foreign import copyImpl :: Fn2 String String (Promise Unit)
14+foreign import copyImpl ∷ Fn2 String String (Promise Unit)
16
15
17−copy :: String -> String -> Aff Unit
16+copy ∷ String -> String -> Aff Unit
18−copy src target = runFn2 copyImpl src target # toAff
17+copy src target = runFn2 copyImpl src target # toAff
server/src/PscIdeClient.purs+16−21
@@ -1,7 +1,6 @@
1module PscIdeClient where
1module PscIdeClient where
2
2
3import Prelude
3import Prelude
4−
5import Data.Either (Either(..), either)
4import Data.Either (Either(..), either)
6import Data.Maybe (Maybe(..))
5import Data.Maybe (Maybe(..))
7import Data.Newtype (class Newtype, un, unwrap)
6import Data.Newtype (class Newtype, un, unwrap)
@@ -22,12 +21,12 @@ import Node.Net.Socket as Socket
22import Playground.Playground (Folder(..))
21import Playground.Playground (Folder(..))
23import Simple.JSON (writeJSON)
22import Simple.JSON (writeJSON)
24
23
25−type BuildCommand
24+type BuildCommand =
26− = { command ∷ String
25+ { command ∷ String
27− , params ∷
26+ , params ∷
28− { file ∷ String
27+ { file ∷ String
29− }
30 }
28 }
29+ }
31
30
32loadCommand ∷
31loadCommand ∷
33 { command ∷ String
32 { command ∷ String
@@ -63,17 +62,16 @@ startIdeServer folder port = do
63 loadPscIde folder port
62 loadPscIde folder port
64 pure cp
63 pure cp
65 where
64 where
66− infoString =
65+ infoString =
67− " in folder "
66+ " in folder "
68− <> un Folder folder
67+ <> un Folder folder
69− <> " on port "
68+ <> " on port "
70− <> show port
69+ <> show port
71
70
72execCommand ∷ Folder -> String -> Aff CP.ExecResult
71execCommand ∷ Folder -> String -> Aff CP.ExecResult
73execCommand folder command =
72execCommand folder command =
74 makeAff \callback -> do
73 makeAff \callback -> do
75− let
76− options = CP.defaultExecOptions { cwd = Just (un Folder folder) }
74+ let options = CP.defaultExecOptions { cwd = Just (un Folder folder) }
77 childProcess <- CP.exec command options (callback <<< Right)
75 childProcess <- CP.exec command options (callback <<< Right)
78 pure $ effectCanceler ((log $ "Killing " <> show (CP.pid childProcess)) *> CP.kill SIGKILL childProcess)
76 pure $ effectCanceler ((log $ "Killing " <> show (CP.pid childProcess)) *> CP.kill SIGKILL childProcess)
79
77
@@ -87,22 +85,21 @@ restartIdeServer folder port processRef = do
87spawnProcess ∷ Folder -> String -> Array String -> Aff ChildProcess
85spawnProcess ∷ Folder -> String -> Array String -> Aff ChildProcess
88spawnProcess folder command args =
86spawnProcess folder command args =
89 makeAff \callback -> do
87 makeAff \callback -> do
90− let
91− options = CP.defaultSpawnOptions { cwd = Just (un Folder folder), stdio = Just <$> [Ignore, Ignore, Ignore] }
88+ let options = CP.defaultSpawnOptions { cwd = Just (un Folder folder), stdio = Just <$> [ Ignore, Ignore, Ignore ] }
92 childProcess <- CP.spawn command args options
89 childProcess <- CP.spawn command args options
93 callback (Right childProcess)
90 callback (Right childProcess)
94 pure $ effectCanceler
91 pure $ effectCanceler
95 $ (log $ "Killing " <> show (CP.pid childProcess))
92 $ (log $ "Killing " <> show (CP.pid childProcess))
96 *> CP.kill SIGKILL childProcess
93 *> CP.kill SIGKILL childProcess
97
94
98−newtype PscIdeConnection
95+newtype PscIdeConnection = PscIdeConnection
99− = PscIdeConnection
100 { serverProcessRef ∷ Ref ChildProcess
96 { serverProcessRef ∷ Ref ChildProcess
101 , port ∷ Int
97 , port ∷ Int
102 , folder ∷ Folder
98 , folder ∷ Folder
103 }
99 }
104
100
105derive instance ntPscIdeConnection ∷ Newtype PscIdeConnection _
101derive instance ntPscIdeConnection ∷ Newtype PscIdeConnection _
102+
106instance eqPscIdeConnection ∷ Eq PscIdeConnection where
103instance eqPscIdeConnection ∷ Eq PscIdeConnection where
107 eq c1 c2 = (unwrap c1).port == (unwrap c2).port
104 eq c1 c2 = (unwrap c1).port == (unwrap c2).port
108
105
@@ -125,8 +122,7 @@ loadPscIde folder port = do
125 true -> mempty -- should be covered in onError
122 true -> mempty -- should be covered in onError
126 false -> do
123 false -> do
127 affCb (Right unit)
124 affCb (Right unit)
128− let
129− command = writeJSON buildCommand <> "\n"
125+ let command = writeJSON buildCommand <> "\n"
130 Socket.onReady socket (void $ Socket.writeString socket command UTF8 mempty)
126 Socket.onReady socket (void $ Socket.writeString socket command UTF8 mempty)
131 pure (closeSocketCanceller socket)
127 pure (closeSocketCanceller socket)
132
128
@@ -147,8 +143,7 @@ compileCode code (PscIdeConnection { port, folder, serverProcessRef }) = do
147 log $ "Enough data on " <> show port <> " ending socket\n"
143 log $ "Enough data on " <> show port <> " ending socket\n"
148 void $ Socket.endString socket "" UTF8 mempty
144 void $ Socket.endString socket "" UTF8 mempty
149 affCb (Right newStr)
145 affCb (Right newStr)
150− let
151− command = writeJSON buildCommand
146+ let command = writeJSON buildCommand
152 Socket.onReady socket do
147 Socket.onReady socket do
153 log $ "Socket " <> show port <> " ready"
148 log $ "Socket " <> show port <> " ready"
154 void $ Socket.writeString socket (command <> "\n") UTF8 mempty
149 void $ Socket.writeString socket (command <> "\n") UTF8 mempty