Compare commits
313 commits
publish-re
...
master
Author | SHA1 | Date | |
---|---|---|---|
c813819c4d | |||
|
1660c1033c | ||
b75db26783 | |||
|
820e1aa819 | ||
bba7e336dd | |||
|
c9f8b1d0bb | ||
8a1b4c4f9a | |||
|
f94dd07902 | ||
7c081e9e0c | |||
36d1bc7018 | |||
|
846bb4c0f6 | ||
2a3b118d0a | |||
687913aad4 | |||
657c78f6d7 | |||
52eb65e545 | |||
|
8d2d4b168b | ||
|
9dadb97ad0 | ||
|
c42e500c21 | ||
|
b93fd63ff2 | ||
|
75c26b8783 | ||
e7221e2595 | |||
ca2cc3c7ef | |||
|
7d953cd6ae | ||
|
c9c3666c8c | ||
92f57872ed | |||
b518ff1b53 | |||
22984a7a1e | |||
|
d97c45a12f | ||
|
2f9256c0bf | ||
|
19a6aac68a | ||
e304bc8a7d | |||
8bd0561c48 | |||
9ad6cdce87 | |||
|
6aa4958dfa | ||
|
06ccb1438e | ||
|
cdfea981b1 | ||
426b6a2e78 | |||
e7635c8ebf | |||
|
6680038ce6 | ||
|
fef5e21874 | ||
25f5683278 | |||
|
34b8c60553 | ||
0a9bbea35d | |||
|
5b88b3c3e4 | ||
ce0f375388 | |||
02329c694f | |||
|
910b95f853 | ||
|
55bd0c5684 | ||
7d34e25109 | |||
ac3dbd757c | |||
0bf415a7a4 | |||
|
9f16c7f33a | ||
|
24d656d384 | ||
|
734133246c | ||
e158480950 | |||
38e26ab078 | |||
9fa6faa315 | |||
948a475b35 | |||
a7d7707859 | |||
|
0a4b337acd | ||
|
388f9f86e6 | ||
|
75cb05f44a | ||
3435774235 | |||
634075240c | |||
193b9037b5 | |||
2d68b2d527 | |||
|
3dbe0f9ec0 | ||
|
c1db73984c | ||
|
1acd1ea9dc | ||
|
4c4c840a88 | ||
|
a8caa90f1e | ||
b0969f23be | |||
|
bc17778e3d | ||
d2b6843f32 | |||
|
1b3a38b9b2 | ||
7a61b93405 | |||
|
740034d469 | ||
67ff07ed12 | |||
|
f8a1754218 | ||
e02f8ab354 | |||
|
ff4a9c78d8 | ||
8e3a548f39 | |||
|
e8a55135ae | ||
90a07c7460 | |||
|
ffaed65744 | ||
8a3ab62126 | |||
4c79e6ce29 | |||
9c38283fc7 | |||
|
643b15fa84 | ||
|
2ade878fd6 | ||
|
c7c0eac818 | ||
780daffb6f | |||
|
dac0a91e98 | ||
f6af8dad1f | |||
|
a63ada21d9 | ||
029f286c50 | |||
|
50b76783e2 | ||
ef78076d26 | |||
|
df38dd1b7f | ||
2237463682 | |||
|
ffe68716cd | ||
4969f8b9bd | |||
|
bbd31726f6 | ||
fb9c2bf79b | |||
|
9aaf64a1cd | ||
75d30bbbfd | |||
d89f92fa61 | |||
5870ed74b3 | |||
|
442b891da3 | ||
|
adb11eb3a8 | ||
|
65ea05b085 | ||
945663a08b | |||
|
4ebbae064c | ||
20d6adee2c | |||
|
5d0c95520d | ||
6f7d959b01 | |||
|
09555eaa7e | ||
77360122c0 | |||
|
127053d0d4 | ||
e76bed7f17 | |||
5711c84386 | |||
|
901b8f52d8 | ||
e868ac0379 | |||
|
d2ad8bd7de | ||
444b675f22 | |||
|
81d00312ba | ||
67dc419eff | |||
|
b31177df45 | ||
|
a7b911abde | ||
b9e1e6a6df | |||
|
71c1e6969a | ||
b4143619b1 | |||
180754ca72 | |||
233a779e23 | |||
|
94e165c194 | ||
|
f329f75cdd | ||
|
b669887498 | ||
5b03f41e06 | |||
b75e9143eb | |||
a2766c4c57 | |||
|
bccf22ac63 | ||
|
fd22df83ba | ||
|
77731be1c7 | ||
0d4034d286 | |||
|
2dfc7fddd1 | ||
1e120ff30e | |||
a370faa3f7 | |||
7987576d43 | |||
|
0e5cedbe31 | ||
|
8b1ce6c2a7 | ||
|
775aa130f2 | ||
7fbd6f3dc4 | |||
|
87088e5352 | ||
354be7219d | |||
a8fec032c3 | |||
2d17a4e9af | |||
bfaf8dcbd5 | |||
899deadd7b | |||
59027f4e52 | |||
|
9e3da7f535 | ||
|
9630b0e960 | ||
|
873ce697e9 | ||
|
8d154dddbf | ||
|
42fb307332 | ||
|
7f04ea8392 | ||
884012970d | |||
48572b0a0e | |||
d8848e50d7 | |||
ce9fb50738 | |||
|
8580984e50 | ||
|
969be4993b | ||
|
000be3e63b | ||
|
058dda3d38 | ||
8b492cb55a | |||
55a250fd95 | |||
|
bfcf224172 | ||
|
09ae40ec5d | ||
6728e9c8c4 | |||
|
a7e86d10a7 | ||
1077812e63 | |||
|
18e148f354 | ||
324a1179bb | |||
|
16b049b0f6 | ||
ca76bf9caf | |||
e8e9fb6392 | |||
|
775aff77a4 | ||
|
27054b9025 | ||
0fbf91200e | |||
|
ecdfcf110d | ||
bbba3f8d97 | |||
|
f76073dacf | ||
2b85036aa6 | |||
|
c1878ed382 | ||
3115eeedee | |||
c811e77bd7 | |||
9524a9fd15 | |||
|
05f27518d4 | ||
|
3e7de1d759 | ||
|
e12a802619 | ||
a19795a340 | |||
64554b057c | |||
a83cc9b05e | |||
3678fca26a | |||
|
e2df341f19 | ||
|
49afa8c94b | ||
98220fcbc8 | |||
89e2f00382 | |||
518a5d58cb | |||
|
e0b777bc04 | ||
b2a06a4883 | |||
cd81262f69 | |||
316cc0589a | |||
3b3dc7b346 | |||
|
cad1334fe4 | ||
|
416fff2227 | ||
|
4b1e58456d | ||
|
d937fe257f | ||
|
b74b069727 | ||
|
ba89f7f378 | ||
|
3dec37e8b8 | ||
|
8da9c17714 | ||
648424fdf3 | |||
c066d42272 | |||
bb9e8bae33 | |||
acd1dddcf4 | |||
8e129c64d9 | |||
37fc8897af | |||
858fae15f6 | |||
8c3fe53071 | |||
bacacdd192 | |||
1d7c8640de | |||
a561059cce | |||
0e08bb1b77 | |||
|
eb98fea3b0 | ||
290f6c5cc1 | |||
bbf64ba1fb | |||
|
6e6b814b86 | ||
|
171718b788 | ||
|
f30463b0ac | ||
6e68747978 | |||
ab2915ece1 | |||
|
f00aba852c | ||
|
38b73dac8c | ||
|
4f53b67d9b | ||
|
9e7b1740f4 | ||
|
06a5669157 | ||
|
dcd6e5619b | ||
|
0737797c26 | ||
|
5630b2e41c | ||
|
18d59fc61b | ||
|
da0ce2c61b | ||
|
c421692595 | ||
|
d54c238529 | ||
dade31ba60 | |||
af79e82881 | |||
3a619a681d | |||
|
48186185c5 | ||
|
0efb1b7555 | ||
|
d15589d118 | ||
88136c8ab5 | |||
2685fdbe0e | |||
9192b32904 | |||
ed190a9f1a | |||
d44e982acd | |||
9d212797d5 | |||
d2651f0800 | |||
5cb3bcdca5 | |||
21e89209a1 | |||
39011330f3 | |||
|
d94d6d7717 | ||
|
84a8a4331c | ||
|
aad5797299 | ||
|
bcc489c577 | ||
|
79cdf68b8f | ||
|
07910c1190 | ||
|
86b044703c | ||
|
253aaf4542 | ||
|
34a203ec49 | ||
|
f46d680405 | ||
5ffbed57e0 | |||
|
711cdd64d2 | ||
ab2f2c0a90 | |||
ba379f1999 | |||
0ba21e184d | |||
abf4ded284 | |||
cfc459dde1 | |||
ae1bc888f3 | |||
3ba7b591f5 | |||
b48cc8ffc7 | |||
76075099be | |||
241ccab5fc | |||
802bf71325 | |||
2879a4da01 | |||
be03da096e | |||
f17e38c531 | |||
dfa83b1031 | |||
5e7d1cae65 | |||
3e5dca9069 | |||
c8ecd29e94 | |||
c0a125d5f1 | |||
6b93524027 | |||
b5b83b955a | |||
3c445411e9 | |||
786a9afe22 | |||
cdbf6788f0 | |||
0dc74c1c05 | |||
8e0e94f98b | |||
ca34646b0c | |||
43dc36554d | |||
d4d08e9d9a | |||
fb472ed6c6 | |||
dcfc290099 | |||
6620cc6857 |
120 changed files with 11720 additions and 8529 deletions
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
|
@ -84,7 +84,7 @@ jobs:
|
||||||
|
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
if: matrix.version == 'stable' && (github.repository == 'mcapthca/survey')
|
if: matrix.version == 'stable' && (github.repository == 'mcapthca/survey')
|
||||||
uses: JamesIves/github-pages-deploy-action@3.7.1
|
uses: JamesIves/github-pages-deploy-action@v4.6.4
|
||||||
with:
|
with:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
BRANCH: gh-pages
|
BRANCH: gh-pages
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -17,3 +17,4 @@ __pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
src/sailfish/
|
src/sailfish/
|
||||||
|
src/libcachebust_data.json
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT\n synced_till\n FROM\n survey_mcaptcha_campaign\n WHERE \n campaign_id = $1;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "synced_till",
|
||||||
|
"type_info": "Int4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "05b7fe6d93a4c988e9eae32f4a57e369f9ddc703b8fd3251c6baa52b60c98a1d"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "INSERT INTO survey_admins \n (name , password, secret) VALUES ($1, $2, $3)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar",
|
||||||
|
"Text",
|
||||||
|
"Varchar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "0d22134cc5076304b7895827f006ee8269cc500f400114a7472b83f0f1c568b5"
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT name, id FROM survey_campaigns ORDER BY id;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": []
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "10924f3726a45c3bc709118375d691f2867bbcd50dc47a000ac9bf3ff878c97c"
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "INSERT INTO survey_responses (\n user_id,\n campaign_id,\n device_user_provided,\n device_software_recognised,\n threads,\n submitted_at,\n submission_bench_type_id\n ) VALUES (\n $1, $2, $3, $4, $5, $6,\n (SELECT ID FROM survey_bench_type WHERE name = $7)\n )\n RETURNING ID;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Int4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid",
|
||||||
|
"Uuid",
|
||||||
|
"Varchar",
|
||||||
|
"Varchar",
|
||||||
|
"Int4",
|
||||||
|
"Timestamptz",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "117f1ae18f6a3936f27446b75b555951fe217d3a3cefe40a006fdd3cb31f0ac4"
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "INSERT INTO survey_mcaptcha_upload_job_states \n (name) VALUES ($1) ON CONFLICT (name) DO NOTHING;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "11ff04344412d1a2e5fdb1ab654fe4e90c2ba897bb4889426031ffacc2ae06e4"
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT password FROM survey_admins WHERE name = ($1)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "password",
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "1373df097fa0e58b23a374753318ae53a44559aa0e7eb64680185baf1c481723"
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT\n survey_admins.name\n FROM\n survey_admins\n INNER JOIN survey_campaigns ON\n survey_admins.ID = survey_campaigns.user_id\n WHERE\n survey_campaigns.ID = $1\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "15a8484de6f035e56c34ce3f6979eadea81f125933f76261c8b3c8319d43bbe0"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE\n survey_mcaptcha_campaign\n SET\n synced_till = $1\n WHERE \n campaign_id = $2; ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Int4",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "163a1ab861234bbf52b1b1c03bbac0d37bbbb539146f93c6fba24ffd80ad1485"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n UPDATE\n survey_mcaptcha_upload_jobs\n SET\n job_state = (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1),\n scheduled_at = $2\n WHERE public_id = $3;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Timestamptz",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "18495d6198079fdb8e4806d8a59aa0a1abee44a8b568ce74fa275ab936e8362f"
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT name, password FROM survey_admins WHERE email = ($1)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "password",
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "19686bfe8772cbc6831d46d18994e2b9aa40c7181eae9a31e51451cce95f04e8"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "DELETE \n FROM survey_campaigns \n WHERE \n user_id = (\n SELECT \n ID \n FROM \n survey_admins \n WHERE \n name = $1\n )\n AND\n id = ($2)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "1972be28a6bda2c3a3764a836e95c8cb0c5db277fc4c8a9b19951a03166c6492"
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "insert into survey_admins \n (name , password, email, secret) values ($1, $2, $3, $4)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar",
|
||||||
|
"Text",
|
||||||
|
"Varchar",
|
||||||
|
"Varchar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "1b7e17bfc949fa97e8dec1f95e35a02bcf3aa1aa72a1f6f6c8884e885fc3b953"
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT\n survey_mcaptcha_upload_jobs.ID,\n survey_mcaptcha_upload_jobs.public_id,\n survey_mcaptcha_campaign.campaign_id,\n survey_mcaptcha_campaign.public_id as campaign_public_id,\n survey_mcaptcha_upload_job_states.name,\n survey_mcaptcha_upload_jobs.created_at,\n survey_mcaptcha_upload_jobs.scheduled_at,\n survey_mcaptcha_upload_jobs.finished_at\n\n FROM survey_mcaptcha_upload_jobs\n INNER JOIN\n survey_mcaptcha_upload_job_states\n ON\n survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state\n INNER JOIN\n survey_mcaptcha_campaign\n ON\n survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id\n WHERE\n survey_mcaptcha_upload_job_states.name = $1;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "public_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "campaign_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "campaign_public_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "created_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 6,
|
||||||
|
"name": "scheduled_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 7,
|
||||||
|
"name": "finished_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "1e41c42d89762ff4dc4b60a534a54db2741b325727c01852cbc68ea8442d15ef"
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT\n public_id\n FROM\n survey_mcaptcha_campaign\n WHERE\n campaign_id = $1\n AND\n url_id = (SELECT ID FROM survey_mcaptcha_hostname WHERE secret = $2);",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "public_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "2904486838bed381aa00f6a1b1e9b860a74b07b15256f3764434901471ff820b"
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "DELETE FROM survey_admins WHERE name = ($1)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "2ccaecfee4d2f29ef5278188b304017719720aa986d680d4727a1facbb869c7a"
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT EXISTS (SELECT 1 from survey_mcaptcha_upload_job_states WHERE name = $1)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "exists",
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "2d18e0fad79c6df26465f82eca20cdfca35a710f34a54ac115d23435762a3038"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "INSERT INTO\n survey_mcaptcha_campaign (campaign_id, public_id, url_id)\n VALUES ($1, $2, (SELECT ID FROM survey_mcaptcha_hostname WHERE secret = $3));",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar",
|
||||||
|
"Varchar",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "38a517b011519ec80d35d12ea463e7aed1f25290a5f3e8b19c5aa781da362ae3"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n INSERT INTO survey_users (created_at, id) VALUES($1, $2)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Timestamptz",
|
||||||
|
"Uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "43b3e771f38bf8059832169227705be06a28925af1b3799ffef5371d511fd138"
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT\n difficulty\n FROM\n survey_benches\n WHERE\n duration <= $1\n ORDER BY difficulty ASC LIMIT 1 OFFSET $2;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "difficulty",
|
||||||
|
"type_info": "Int4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Float4",
|
||||||
|
"Int8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "52c16c2c0759140af6348ef7de56b74151a20532ceebc8ee41d079decee3acb5"
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT EXISTS (SELECT 1 from survey_admins WHERE name = $1)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "exists",
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "536541ecf2e1c0403c74b6e2e09b42b73a7741ae4a348ff539ac410022e03ace"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE survey_admins set password = $1\n WHERE name = $2",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "55dde28998a6d12744806035f0a648494a403c7d09ea3caf91bf54869a81aa73"
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT\n survey_responses.ID,\n survey_responses.device_software_recognised,\n survey_responses.threads,\n survey_responses.user_id,\n survey_responses.submitted_at,\n survey_responses.device_user_provided,\n survey_bench_type.name\n FROM\n survey_responses\n INNER JOIN survey_bench_type ON\n survey_responses.submission_bench_type_id = survey_bench_type.ID\n WHERE\n survey_responses.campaign_id = (\n SELECT ID FROM survey_campaigns\n WHERE\n ID = $1\n AND\n user_id = (SELECT ID FROM survey_admins WHERE name = $2)\n )\n LIMIT $3 OFFSET $4",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "device_software_recognised",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "threads",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "user_id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "submitted_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "device_user_provided",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 6,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid",
|
||||||
|
"Text",
|
||||||
|
"Int8",
|
||||||
|
"Int8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "57c673ad8529371d77aa305917cf680dd2273ead74c3583ef0322f472b1d33fd"
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT name \n FROM survey_campaigns\n WHERE \n id = $1\n AND\n user_id = (SELECT ID from survey_admins WHERE name = $2)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "58ec3b8f98c27e13ec2732f8ee23f6eb9845ac5d9fd97b1e5c9f2eed4b1f5693"
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT EXISTS (SELECT 1 from survey_mcaptcha_hostname WHERE url = $1)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "exists",
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "5c1ad3208ece06ba7a503d650e15d06906e56018798cba2b4672c393327131aa"
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT COUNT(difficulty) FROM survey_benches WHERE duration <= $1;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "count",
|
||||||
|
"type_info": "Int8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Float4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "63370a30a4ff6d31292a3cb632c66184ccff75583e21df5ddf5e8872f710d3d2"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE survey_admins set email = $1\n WHERE name = $2",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "683707dbc847b37c58c29aaad0d1a978c9fe0657da13af99796e4461134b5a43"
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT EXISTS (SELECT 1 from survey_admins WHERE email = $1)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "exists",
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "6a26daa84578aed2b2085697cb8358ed7c0a50ba9597fd387b4b09b0a8a154db"
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "INSERT INTO\n survey_mcaptcha_analytics (\n campaign_id, time, difficulty_factor, worker_type\n )\n VALUES ((\n SELECT\n ID\n FROM\n survey_mcaptcha_campaign\n WHERE \n campaign_id = $1\n ), $2, $3, $4\n );",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Int4",
|
||||||
|
"Int4",
|
||||||
|
"Varchar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "6c8fda20aa4a9174a5b008032d493773274ebfbf9dc204d89609cdff1ebc0335"
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT \n name, id\n FROM \n survey_campaigns \n WHERE\n user_id = (\n SELECT \n ID\n FROM \n survey_admins\n WHERE\n name = $1\n )",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "70cc7bfc9b6ff5b68db70c069c0947d51bfc4a53cedc020016ee25ff98586c93"
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT id, time, difficulty_factor, worker_type FROM survey_mcaptcha_analytics\n WHERE \n campaign_id = (\n SELECT \n ID FROM survey_mcaptcha_campaign \n WHERE \n public_id = $1\n )\n ORDER BY ID\n OFFSET $2 LIMIT $3\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "time",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "difficulty_factor",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "worker_type",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Int8",
|
||||||
|
"Int8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "714925a5209400a17bcafe23c34ce9546106e8bdd788c27ee579b278e671bcb0"
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT\n survey_mcaptcha_upload_jobs.ID,\n survey_mcaptcha_upload_jobs.public_id,\n survey_mcaptcha_campaign.campaign_id,\n survey_mcaptcha_campaign.public_id as campaign_public_id,\n survey_mcaptcha_upload_job_states.name,\n survey_mcaptcha_upload_jobs.created_at,\n survey_mcaptcha_upload_jobs.scheduled_at,\n survey_mcaptcha_upload_jobs.finished_at\n\n FROM survey_mcaptcha_upload_jobs\n INNER JOIN\n survey_mcaptcha_upload_job_states\n ON\n survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state\n INNER JOIN\n survey_mcaptcha_campaign\n ON\n survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id\n WHERE\n survey_mcaptcha_upload_jobs.public_id = $1",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "public_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "campaign_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "campaign_public_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "created_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 6,
|
||||||
|
"name": "scheduled_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 7,
|
||||||
|
"name": "finished_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "722f2d297a318f9804c1388d427d069a315b45c0c85c0b344d34cd8928b22c9c"
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT\n survey_responses.ID,\n survey_responses.device_software_recognised,\n survey_responses.threads,\n survey_responses.user_id,\n survey_responses.submitted_at,\n survey_responses.device_user_provided,\n survey_bench_type.name\n FROM\n survey_responses\n INNER JOIN survey_bench_type ON\n survey_responses.submission_bench_type_id = survey_bench_type.ID\n WHERE\n survey_bench_type.name = $3\n AND\n survey_responses.campaign_id = (\n SELECT ID FROM survey_campaigns\n WHERE\n ID = $1\n AND\n user_id = (SELECT ID FROM survey_admins WHERE name = $2)\n )\n LIMIT $4 OFFSET $5",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "device_software_recognised",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "threads",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "user_id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "submitted_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "device_user_provided",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 6,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid",
|
||||||
|
"Text",
|
||||||
|
"Text",
|
||||||
|
"Int8",
|
||||||
|
"Int8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "74c41e33f91cf31ea13582c8b3ca464544374842450d580517ca2bd01d67402e"
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT EXISTS (\n SELECT\n url\n FROM\n survey_mcaptcha_hostname\n WHERE secret = $1\n )",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "exists",
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "7d764a7b1c2991dda7498f243c6e4bd83fdf431e3510f9afb0ef5e9b10f35181"
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n INSERT INTO survey_campaigns (\n user_id, ID, name, difficulties, created_at\n ) VALUES(\n (SELECT id FROM survey_admins WHERE name = $1),\n $2, $3, $4, $5\n );",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Uuid",
|
||||||
|
"Varchar",
|
||||||
|
"Int4Array",
|
||||||
|
"Timestamptz"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "82feafc36533144e49ba374c8c47ca4aa0d6558a9803778ad28cfa7b62382c3e"
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT\n created_at,\n ID\n FROM\n survey_users\n WHERE\n ID = $1\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "created_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "858a4c06a5c1ba7adb79bcac7d42d106d09d0cbff10c197f2242dcb5c437a1df"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "INSERT INTO survey_mcaptcha_hostname (url, secret) VALUES ($1, $2)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar",
|
||||||
|
"Varchar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "94205e3e65a8f6bf315a282ec8fcc64119dc08e5565925bb2a3f5fccf663b5ab"
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT difficulties FROM survey_campaigns WHERE id = $1;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "difficulties",
|
||||||
|
"type_info": "Int4Array"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "9cdade613ce724631cc3f187510758ee0929e93ff3f8ce81fe35594756644246"
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT EXISTS (\n SELECT\n ID\n FROM\n survey_mcaptcha_campaign\n WHERE\n campaign_id = $1\n AND\n url_id = (\n SELECT\n ID\n FROM\n survey_mcaptcha_hostname\n WHERE\n secret = $2\n )\n )",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "exists",
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "9da39f618b9dea08360d4c1625650b5055de47a7e89f99ffc589b99d22b8ac9d"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "DELETE FROM survey_mcaptcha_hostname WHERE secret = $1 AND url =$2",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "a3cddc0ace32cfb7df70e171b2618c7fe6d7824bbfcbae8248905e927049528b"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "INSERT INTO survey_benches \n (resp_id, difficulty, duration) \n VALUES ($1, $2, $3);",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Int4",
|
||||||
|
"Int4",
|
||||||
|
"Float4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "a721cfa249acf328c2f29c4cf8c2aeba1a635bcf49d18ced5474caa10b7cae4f"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE survey_admins set secret = $1\n WHERE name = $2",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "ab951c5c318174c6538037947c2f52c61bcfe5e5be1901379b715e77f5214dd2"
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT\n duration,\n difficulty\n FROM\n survey_benches\n WHERE\n resp_id = $1\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "duration",
|
||||||
|
"type_info": "Float4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "difficulty",
|
||||||
|
"type_info": "Int4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Int4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "b2619292aa6bd1ac38dca152cbe607b795a151ddc212361a3c6d8c70ea1c93eb"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE survey_mcaptcha_hostname set secret = $1 WHERE url = $2",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "baabef729999fe63426b3b2373f1ecbf294d4bfddbce04209269644f4a7511ed"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE survey_admins set name = $1\n WHERE name = $2",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "c757589ef26a005e3285e7ab20d8a44c4f2e1cb125f8db061dd198cc380bf807"
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT\n survey_mcaptcha_upload_jobs.ID,\n survey_mcaptcha_upload_jobs.public_id,\n survey_mcaptcha_campaign.campaign_id,\n survey_mcaptcha_campaign.public_id as campaign_public_id,\n survey_mcaptcha_upload_job_states.name,\n survey_mcaptcha_upload_jobs.created_at,\n survey_mcaptcha_upload_jobs.scheduled_at,\n survey_mcaptcha_upload_jobs.finished_at\n\n FROM survey_mcaptcha_upload_jobs\n INNER JOIN\n survey_mcaptcha_upload_job_states\n ON\n survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state\n INNER JOIN\n survey_mcaptcha_campaign\n ON\n survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id\n WHERE\n survey_mcaptcha_campaign.campaign_id = $1\n AND\n survey_mcaptcha_upload_job_states.name = $2;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "public_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "campaign_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "campaign_public_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "created_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 6,
|
||||||
|
"name": "scheduled_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 7,
|
||||||
|
"name": "finished_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "ca41f4e15fa5c5657a525ed9385a92214b644194443ae165957d9659d30dc3f9"
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT\n survey_mcaptcha_campaign.campaign_id,\n survey_mcaptcha_upload_jobs.public_id,\n survey_mcaptcha_hostname.url\n FROM\n survey_mcaptcha_campaign\n INNER JOIN\n survey_mcaptcha_upload_jobs\n ON\n survey_mcaptcha_upload_jobs.campaign_id = survey_mcaptcha_campaign.ID\n INNER JOIN\n survey_mcaptcha_hostname\n ON\n survey_mcaptcha_hostname.ID = survey_mcaptcha_campaign.url_id\n WHERE\n survey_mcaptcha_upload_jobs.job_state = (\n SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1\n )\n AND\n survey_mcaptcha_upload_jobs.finished_at is NULL\n AND\n survey_mcaptcha_upload_jobs.scheduled_at is NULL\n ORDER BY created_at ASC;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "campaign_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "public_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "url",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "d7a099c6f381fd02ad6a114b0146e4e52f7886f0164d05ccd3f1818a2a70cf67"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "DELETE FROM\n survey_mcaptcha_campaign\n WHERE\n campaign_id = $1\n AND\n url_id = (\n SELECT\n ID\n FROM\n survey_mcaptcha_hostname\n WHERE\n secret = $2\n )",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "dbe5d5c450a50bb829a39e6149eb4e6307547120b10762140d250f163b584a23"
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT secret FROM survey_admins WHERE name = ($1)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "secret",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "e9cf5d6d8c9e8327d5c809d47a14a933f324e267f1e7dbb48e1caf1c021adc3f"
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "INSERT INTO survey_mcaptcha_upload_jobs\n (campaign_id, job_state, created_at, public_id)\n VALUES (\n (SELECT ID FROM survey_mcaptcha_campaign WHERE campaign_id = $1),\n (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $2),\n $3, $4)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text",
|
||||||
|
"Timestamptz",
|
||||||
|
"Varchar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "ebfc456dd76b3fb2e5484f935703ad6aa4712c782222f2015b92916827f81079"
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT ID, name, difficulties, created_at FROM survey_campaigns",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "difficulties",
|
||||||
|
"type_info": "Int4Array"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "created_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": []
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "efa0e41910fa5bcb187ba9e2fc8f37bee5b25ffe9a2d175f39a69899bc559965"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n UPDATE\n survey_mcaptcha_upload_jobs\n SET\n job_state = (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1),\n finished_at = $2\n WHERE public_id = $3;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Timestamptz",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "fade9f99846165c34486f6492ece38148bf0dd2d79e1a4f97b8cbf04015ceff0"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "INSERT INTO survey_response_tokens \n (resp_id, user_id, id)\n VALUES ($1, $2, $3);",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Int4",
|
||||||
|
"Uuid",
|
||||||
|
"Uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "fcdc5fe5d496eb516c805e64ec96d9626b74ab33cd6e75e5a08ae88967403b72"
|
||||||
|
}
|
|
@ -7,15 +7,18 @@ pipeline:
|
||||||
- OPEN_API_DOCS=8e77345f1597e40c2e266cb4e6dee74888918a61
|
- OPEN_API_DOCS=8e77345f1597e40c2e266cb4e6dee74888918a61
|
||||||
- COMPILED_DATE=2021-07-21
|
- COMPILED_DATE=2021-07-21
|
||||||
commands:
|
commands:
|
||||||
- curl -fsSL https://deb.nodesource.com/setup_16.x | bash - &&\
|
- apt-get update
|
||||||
- apt update && apt-get -y --no-install-recommends install nodejs tar gpg curl wget
|
- apt-get install -y ca-certificates curl gnupg tar wget libssl-dev
|
||||||
|
- mkdir -p /etc/apt/keyrings
|
||||||
|
- curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||||
|
- NODE_MAJOR=18 echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
|
||||||
|
- apt-get -y install nodejs npm
|
||||||
- npm install --global yarn
|
- npm install --global yarn
|
||||||
- rustup component add rustfmt
|
- rustup component add rustfmt
|
||||||
- rustup component add clippy
|
- rustup component add clippy
|
||||||
- make dev-env
|
- make dev-env
|
||||||
- make migrate
|
- make migrate
|
||||||
- make frontend
|
- make frontend
|
||||||
- make lint
|
|
||||||
- make test
|
- make test
|
||||||
|
|
||||||
build_docker_img:
|
build_docker_img:
|
||||||
|
|
2266
Cargo.lock
generated
2266
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
33
Cargo.toml
33
Cargo.toml
|
@ -8,7 +8,7 @@ documentation = "https://github.con/mCaptcha/survey"
|
||||||
readme = "https://github.com/mCaptcha/survey/blob/master/README.md"
|
readme = "https://github.com/mCaptcha/survey/blob/master/README.md"
|
||||||
license = "AGPLv3 or later version"
|
license = "AGPLv3 or later version"
|
||||||
authors = ["realaravinth <realaravinth@batsense.net>"]
|
authors = ["realaravinth <realaravinth@batsense.net>"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
default-run = "survey"
|
default-run = "survey"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
@ -22,35 +22,36 @@ name = "tests-migrate"
|
||||||
path = "./src/tests-migrate.rs"
|
path = "./src/tests-migrate.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = "4.0.1"
|
actix-web = "4.3"
|
||||||
actix-identity = "0.4.0"
|
actix-identity = "0.4.0"
|
||||||
actix-session = { version = "0.6.1", features = ["cookie-session"]}
|
actix-session = { version = "0.10.0", features = ["cookie-session"]}
|
||||||
actix-http = "3.0.4"
|
actix-http = "3.0.4"
|
||||||
actix-rt = "2"
|
actix-rt = "2"
|
||||||
actix-cors = "0.6.1"
|
actix-cors = "0.7.0"
|
||||||
actix-files = "0.6.0"
|
actix-files = "0.6.0"
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
#actix = "0.12"
|
#actix = "0.12"
|
||||||
actix-web-codegen-const-routes = { version = "0.1.0", tag = "0.1.0", git = "https://github.com/realaravinth/actix-web-codegen-const-routes" }
|
actix-web-codegen-const-routes = "0.2.0"
|
||||||
|
|
||||||
#libmcaptcha = { branch = "master", git = "https://github.com/mCaptcha/libmcaptcha", features = ["full"] }
|
#libmcaptcha = { branch = "master", git = "https://github.com/mCaptcha/libmcaptcha", features = ["full"] }
|
||||||
|
|
||||||
futures = "0.3.15"
|
futures = "0.3.15"
|
||||||
|
|
||||||
sqlx = { version = "0.6.2", features = [ "runtime-actix-rustls", "postgres", "time", "offline" ] }
|
sqlx = { version = "0.8", features = [ "runtime-tokio-rustls", "postgres", "time", "uuid" ] }
|
||||||
|
|
||||||
|
#argon2-creds = "0.2.3"
|
||||||
argon2-creds = { branch = "master", git = "https://github.com/realaravinth/argon2-creds"}
|
argon2-creds = { branch = "master", git = "https://github.com/realaravinth/argon2-creds"}
|
||||||
|
|
||||||
derive_builder = "0.11"
|
derive_builder = "0.20"
|
||||||
validator = { version = "0.14", features = ["derive"]}
|
validator = { version = "0.18", features = ["derive"]}
|
||||||
derive_more = "0.99"
|
derive_more = "0.99"
|
||||||
|
|
||||||
config = "0.11"
|
config = "0.14"
|
||||||
|
|
||||||
serde = "1"
|
serde = "1"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
||||||
pretty_env_logger = "0.4"
|
pretty_env_logger = "0.5"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
@ -58,18 +59,21 @@ url = { version = "2.2", features = ["serde"] }
|
||||||
urlencoding = "2.1.0"
|
urlencoding = "2.1.0"
|
||||||
|
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
uuid = { version = "0.8.2", features = ["v4", "serde"] }
|
uuid = { version = "1.4.1", features = ["v4", "serde"] }
|
||||||
|
|
||||||
mime_guess = "2.0.3"
|
mime_guess = "2.0.3"
|
||||||
rust-embed = "6.0.0"
|
rust-embed = "8.0.0"
|
||||||
|
#libcachebust = "0.3.0"
|
||||||
cache-buster = { git = "https://github.com/realaravinth/cache-buster" }
|
cache-buster = { git = "https://github.com/realaravinth/cache-buster" }
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
|
|
||||||
#sailfish = "0.3.2"
|
#sailfish = "0.3.2"
|
||||||
tracing = { version = "0.1.37", features = ["log"] }
|
tracing = { version = "0.1.37", features = ["log"] }
|
||||||
tera = { version="1.17.1", features=["builtins"]}
|
tera = { version="1.17.1", features=["builtins"]}
|
||||||
tokio = { version = "1.25.0", features = ["fs"] }
|
tokio = { version = "1.25.0", features = ["fs", "macros"] }
|
||||||
csv-async = { version = "1.2.5", features = ["serde", "tokio"] }
|
csv-async = { version = "1.2.5", features = ["serde", "tokio"] }
|
||||||
|
async-trait = "0.1.68"
|
||||||
|
reqwest = { version = "0.12.0", features = ["json", "gzip", "native-tls-vendored"] }
|
||||||
|
|
||||||
#tokio = "1.11.0"
|
#tokio = "1.11.0"
|
||||||
|
|
||||||
|
@ -81,10 +85,9 @@ version = "0.2"
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
sqlx = { version = "0.6.2", features = [ "runtime-actix-rustls", "uuid", "postgres", "time", "offline" ] }
|
|
||||||
#serde_yaml = "0.8.17"
|
#serde_yaml = "0.8.17"
|
||||||
|
sqlx = { version = "0.8", features = [ "runtime-tokio-rustls", "uuid", "postgres", "time"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
#yaml-rust = "0.4.5"
|
|
||||||
cache-buster = { version = "0.2.0", git = "https://github.com/realaravinth/cache-buster" }
|
cache-buster = { version = "0.2.0", git = "https://github.com/realaravinth/cache-buster" }
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM node:16.11-bullseye-slim as frontend
|
FROM node:20-bookworm-slim as frontend
|
||||||
LABEL org.opencontainers.image.source https://github.com/mCaptcha/survey
|
LABEL org.opencontainers.image.source https://github.com/mCaptcha/survey
|
||||||
RUN apt-get update && apt-get install -y make
|
RUN apt-get update && apt-get install -y make
|
||||||
COPY package.json yarn.lock /src/
|
COPY package.json yarn.lock /src/
|
||||||
|
@ -8,14 +8,14 @@ RUN yarn install
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN make frontend
|
RUN make frontend
|
||||||
|
|
||||||
FROM rust:1-slim-bullseye as rust
|
FROM rust:latest as rust
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
RUN apt-get update && apt-get install -y git
|
RUN apt-get update && apt-get install -y git libssl-dev
|
||||||
COPY . /src
|
COPY . /src
|
||||||
COPY --from=frontend /src/static/cache/bundle /src/static/cache/bundle
|
COPY --from=frontend /src/static/cache/bundle /src/static/cache/bundle
|
||||||
RUN cargo build --release
|
RUN cargo build --release
|
||||||
|
|
||||||
FROM debian:bullseye-slim
|
FROM debian:bookworm
|
||||||
RUN useradd -ms /bin/bash -u 1001 mcaptcha-survey
|
RUN useradd -ms /bin/bash -u 1001 mcaptcha-survey
|
||||||
WORKDIR /home/mcaptcha-survey
|
WORKDIR /home/mcaptcha-survey
|
||||||
COPY --from=rust /src/target/release/survey /usr/local/bin/
|
COPY --from=rust /src/target/release/survey /usr/local/bin/
|
||||||
|
|
32
Makefile
32
Makefile
|
@ -1,3 +1,15 @@
|
||||||
|
define deploy_dependencies
|
||||||
|
@-docker create --name ${db} \
|
||||||
|
-e POSTGRES_PASSWORD=password \
|
||||||
|
-p 5433:5432 \
|
||||||
|
postgres
|
||||||
|
docker start ${db}
|
||||||
|
endef
|
||||||
|
|
||||||
|
define run_migrations
|
||||||
|
cargo run --bin tests-migrate
|
||||||
|
endef
|
||||||
|
|
||||||
default: frontend ## Debug build
|
default: frontend ## Debug build
|
||||||
cargo build
|
cargo build
|
||||||
|
|
||||||
|
@ -19,11 +31,25 @@ dev-env: ## Download development dependencies
|
||||||
cargo fetch
|
cargo fetch
|
||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
|
|
||||||
|
env.db.recreate: ## Deploy dependencies
|
||||||
|
@-docker rm -f ${db}
|
||||||
|
$(call deploy_dependencies)
|
||||||
|
sleep 5
|
||||||
|
$(call run_migrations)
|
||||||
|
|
||||||
|
env.db: ## Deploy dependencies
|
||||||
|
$(call deploy_dependencies)
|
||||||
|
sleep 5
|
||||||
|
$(call run_migrations)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
doc: ## Prepare documentation
|
doc: ## Prepare documentation
|
||||||
cargo doc --no-deps --workspace --all-features
|
cargo doc --no-deps --workspace --all-features
|
||||||
|
|
||||||
docker: ## Build docker images
|
docker: ## Build docker images
|
||||||
docker build -t mcaptcha/survey:master -t mcaptcha/survey:latest .
|
docker buildx build -t mcaptcha/survey:master -t mcaptcha/survey:latest . --load
|
||||||
|
|
||||||
docker-publish: docker ## Build and publish docker images
|
docker-publish: docker ## Build and publish docker images
|
||||||
docker push mcaptcha/survey:master
|
docker push mcaptcha/survey:master
|
||||||
|
@ -43,7 +69,7 @@ lint: ## Lint codebase
|
||||||
yarn lint
|
yarn lint
|
||||||
|
|
||||||
migrate: ## Run database migrations
|
migrate: ## Run database migrations
|
||||||
cargo run --bin tests-migrate
|
$(call run_migrations)
|
||||||
|
|
||||||
release: frontend ## Release build
|
release: frontend ## Release build
|
||||||
cargo build --release
|
cargo build --release
|
||||||
|
@ -60,7 +86,7 @@ sqlx-offline-data: ## prepare sqlx offline data
|
||||||
test: frontend ## Run tests
|
test: frontend ## Run tests
|
||||||
echo 'static/' && tree static || true
|
echo 'static/' && tree static || true
|
||||||
echo 'tree/' && tree assets || true
|
echo 'tree/' && tree assets || true
|
||||||
cargo test --all-features --no-fail-fast
|
cargo test --all-features --no-fail-fast -j 1
|
||||||
|
|
||||||
xml-test-coverage: migrate ## Generate cobertura.xml test coverage
|
xml-test-coverage: migrate ## Generate cobertura.xml test coverage
|
||||||
cargo tarpaulin -t 1200 --out Xml
|
cargo tarpaulin -t 1200 --out Xml
|
||||||
|
|
|
@ -10,7 +10,7 @@ services:
|
||||||
RUST_LOG: debug
|
RUST_LOG: debug
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:13.2
|
image: postgres:16.4
|
||||||
volumes:
|
volumes:
|
||||||
- mcaptcha-survey-data:/var/lib/postgresql/
|
- mcaptcha-survey-data:/var/lib/postgresql/
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
-- SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
--
|
||||||
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS survey_users (
|
CREATE TABLE IF NOT EXISTS survey_users (
|
||||||
ID UUID PRIMARY KEY NOT NULL UNIQUE,
|
ID UUID PRIMARY KEY NOT NULL UNIQUE,
|
||||||
created_at TIMESTAMPTZ NOT NULL
|
created_at TIMESTAMPTZ NOT NULL
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
-- SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
--
|
||||||
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
-- Add migration script here
|
-- Add migration script here
|
||||||
CREATE TABLE IF NOT EXISTS survey_admins (
|
CREATE TABLE IF NOT EXISTS survey_admins (
|
||||||
name VARCHAR(100) NOT NULL UNIQUE,
|
name VARCHAR(100) NOT NULL UNIQUE,
|
||||||
|
|
|
@ -1,2 +1,6 @@
|
||||||
|
-- SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
--
|
||||||
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
ALTER TABLE survey_responses
|
ALTER TABLE survey_responses
|
||||||
ADD COLUMN submitted_at TIMESTAMPTZ NOT NULL DEFAULT now();
|
ADD COLUMN submitted_at TIMESTAMPTZ NOT NULL DEFAULT now();
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
-- SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
--
|
||||||
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS survey_bench_type (
|
CREATE TABLE IF NOT EXISTS survey_bench_type (
|
||||||
name VARCHAR(30) UNIQUE NOT NULL,
|
name VARCHAR(30) UNIQUE NOT NULL,
|
||||||
ID SERIAL PRIMARY KEY NOT NULL
|
ID SERIAL PRIMARY KEY NOT NULL
|
||||||
|
|
42
migrations/20230701082846_survey_mcaptcha_upload.sql
Normal file
42
migrations/20230701082846_survey_mcaptcha_upload.sql
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
-- SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
--
|
||||||
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS survey_mcaptcha_hostname (
|
||||||
|
url VARCHAR(3000) UNIQUE NOT NULL,
|
||||||
|
secret VARCHAR(100) UNIQUE NOT NULL,
|
||||||
|
ID SERIAL PRIMARY KEY NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS survey_mcaptcha_campaign (
|
||||||
|
campaign_id VARCHAR(100) NOT NULL,
|
||||||
|
public_id VARCHAR(100) NOT NULL,
|
||||||
|
url_id INTEGER NOT NULL references survey_mcaptcha_hostname(ID) ON DELETE CASCADE,
|
||||||
|
synced_till INTEGER NOT NULL DEFAULT 0,
|
||||||
|
ID SERIAL PRIMARY KEY NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS survey_mcaptcha_analytics (
|
||||||
|
campaign_id INTEGER references survey_mcaptcha_campaign(ID) ON DELETE CASCADE,
|
||||||
|
time INTEGER NOT NULL,
|
||||||
|
difficulty_factor INTEGER NOT NULL,
|
||||||
|
worker_type VARCHAR(100) NOT NULL,
|
||||||
|
ID SERIAL PRIMARY KEY NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS survey_mcaptcha_upload_job_states (
|
||||||
|
name VARCHAR(20) NOT NULL UNIQUE,
|
||||||
|
ID SERIAL PRIMARY KEY NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS survey_mcaptcha_upload_jobs (
|
||||||
|
campaign_id INTEGER references survey_mcaptcha_campaign(ID) ON DELETE CASCADE,
|
||||||
|
public_id varchar(100) NOT NULL UNIQUE,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
scheduled_at timestamptz DEFAULT NULL,
|
||||||
|
finished_at timestamptz DEFAULT NULL,
|
||||||
|
job_state INTEGER references survey_mcaptcha_upload_job_states(ID) ON DELETE CASCADE,
|
||||||
|
ID SERIAL PRIMARY KEY NOT NULL
|
||||||
|
);
|
8920
package-lock.json
generated
8920
package-lock.json
generated
File diff suppressed because it is too large
Load diff
28
package.json
28
package.json
|
@ -10,26 +10,26 @@
|
||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^27.0.2",
|
"@types/jest": "^29.0.0",
|
||||||
"@types/jsdom": "^16.2.10",
|
"@types/jsdom": "^21.0.0",
|
||||||
"@types/node": "^16.10.5",
|
"@types/node": "^20.0.0",
|
||||||
"@types/sinon": "^10.0.0",
|
"@types/sinon": "^17.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||||
"@typescript-eslint/parser": "^5.0.0",
|
"@typescript-eslint/parser": "^8.0.0",
|
||||||
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
|
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
|
||||||
"dart-sass": "^1.25.0",
|
"dart-sass": "^1.25.0",
|
||||||
"eslint": "^8.0.1",
|
"eslint": "^9.0.0",
|
||||||
"jest": "^27.2.5",
|
"jest": "^29.0.0",
|
||||||
"jest-fetch-mock": "^3.0.3",
|
"jest-fetch-mock": "^3.0.3",
|
||||||
"jsdom": "^18.0.0",
|
"jsdom": "^25.0.0",
|
||||||
"sinon": "^11.1.2",
|
"sinon": "^19.0.0",
|
||||||
"ts-jest": "^27.0.5",
|
"ts-jest": "^29.0.0",
|
||||||
"ts-loader": "^9.2.6",
|
"ts-loader": "^9.2.6",
|
||||||
"ts-node": "^10.3.0",
|
"ts-node": "^10.3.0",
|
||||||
"typescript": "^4.4.4",
|
"typescript": "^5.0.0",
|
||||||
"webpack": "^5.0.0",
|
"webpack": "^5.0.0",
|
||||||
"webpack-cli": "^4.6.0",
|
"webpack-cli": "^5.0.0",
|
||||||
"webpack-dev-server": "^4.3.1"
|
"webpack-dev-server": "^5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mcaptcha/vanilla-glue": "^0.1.0-alpha-3",
|
"@mcaptcha/vanilla-glue": "^0.1.0-alpha-3",
|
||||||
|
|
21
renovate.json
Normal file
21
renovate.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"config:recommended",
|
||||||
|
":dependencyDashboard"
|
||||||
|
],
|
||||||
|
"labels": [
|
||||||
|
"renovate-bot"
|
||||||
|
],
|
||||||
|
"prHourlyLimit": 0,
|
||||||
|
"timezone": "Asia/kolkata",
|
||||||
|
"prCreation": "immediate",
|
||||||
|
"vulnerabilityAlerts": {
|
||||||
|
"enabled": true,
|
||||||
|
"labels": [
|
||||||
|
"renovate-bot",
|
||||||
|
"renovate-security",
|
||||||
|
"security"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
591
sqlx-data.json
591
sqlx-data.json
|
@ -1,592 +1,3 @@
|
||||||
{
|
{
|
||||||
"db": "PostgreSQL",
|
"db": "PostgreSQL"
|
||||||
"0d22134cc5076304b7895827f006ee8269cc500f400114a7472b83f0f1c568b5": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Varchar",
|
|
||||||
"Text",
|
|
||||||
"Varchar"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "INSERT INTO survey_admins \n (name , password, secret) VALUES ($1, $2, $3)"
|
|
||||||
},
|
|
||||||
"117f1ae18f6a3936f27446b75b555951fe217d3a3cefe40a006fdd3cb31f0ac4": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Int4"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Uuid",
|
|
||||||
"Uuid",
|
|
||||||
"Varchar",
|
|
||||||
"Varchar",
|
|
||||||
"Int4",
|
|
||||||
"Timestamptz",
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "INSERT INTO survey_responses (\n user_id,\n campaign_id,\n device_user_provided,\n device_software_recognised,\n threads,\n submitted_at,\n submission_bench_type_id\n ) VALUES (\n $1, $2, $3, $4, $5, $6,\n (SELECT ID FROM survey_bench_type WHERE name = $7)\n )\n RETURNING ID;"
|
|
||||||
},
|
|
||||||
"1373df097fa0e58b23a374753318ae53a44559aa0e7eb64680185baf1c481723": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "password",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Text"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT password FROM survey_admins WHERE name = ($1)"
|
|
||||||
},
|
|
||||||
"15a8484de6f035e56c34ce3f6979eadea81f125933f76261c8b3c8319d43bbe0": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Uuid"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT\n survey_admins.name\n FROM\n survey_admins\n INNER JOIN survey_campaigns ON\n survey_admins.ID = survey_campaigns.user_id\n WHERE\n survey_campaigns.ID = $1\n "
|
|
||||||
},
|
|
||||||
"19686bfe8772cbc6831d46d18994e2b9aa40c7181eae9a31e51451cce95f04e8": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "password",
|
|
||||||
"ordinal": 1,
|
|
||||||
"type_info": "Text"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT name, password FROM survey_admins WHERE email = ($1)"
|
|
||||||
},
|
|
||||||
"1972be28a6bda2c3a3764a836e95c8cb0c5db277fc4c8a9b19951a03166c6492": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Text",
|
|
||||||
"Uuid"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "DELETE \n FROM survey_campaigns \n WHERE \n user_id = (\n SELECT \n ID \n FROM \n survey_admins \n WHERE \n name = $1\n )\n AND\n id = ($2)"
|
|
||||||
},
|
|
||||||
"1b7e17bfc949fa97e8dec1f95e35a02bcf3aa1aa72a1f6f6c8884e885fc3b953": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Varchar",
|
|
||||||
"Text",
|
|
||||||
"Varchar",
|
|
||||||
"Varchar"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "insert into survey_admins \n (name , password, email, secret) values ($1, $2, $3, $4)"
|
|
||||||
},
|
|
||||||
"2ccaecfee4d2f29ef5278188b304017719720aa986d680d4727a1facbb869c7a": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "DELETE FROM survey_admins WHERE name = ($1)"
|
|
||||||
},
|
|
||||||
"43b3e771f38bf8059832169227705be06a28925af1b3799ffef5371d511fd138": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Timestamptz",
|
|
||||||
"Uuid"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "\n INSERT INTO survey_users (created_at, id) VALUES($1, $2)"
|
|
||||||
},
|
|
||||||
"536541ecf2e1c0403c74b6e2e09b42b73a7741ae4a348ff539ac410022e03ace": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "exists",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Bool"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
null
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT EXISTS (SELECT 1 from survey_admins WHERE name = $1)"
|
|
||||||
},
|
|
||||||
"55dde28998a6d12744806035f0a648494a403c7d09ea3caf91bf54869a81aa73": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Text",
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "UPDATE survey_admins set password = $1\n WHERE name = $2"
|
|
||||||
},
|
|
||||||
"57c673ad8529371d77aa305917cf680dd2273ead74c3583ef0322f472b1d33fd": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Int4"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "device_software_recognised",
|
|
||||||
"ordinal": 1,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "threads",
|
|
||||||
"ordinal": 2,
|
|
||||||
"type_info": "Int4"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "user_id",
|
|
||||||
"ordinal": 3,
|
|
||||||
"type_info": "Uuid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "submitted_at",
|
|
||||||
"ordinal": 4,
|
|
||||||
"type_info": "Timestamptz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "device_user_provided",
|
|
||||||
"ordinal": 5,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"ordinal": 6,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Uuid",
|
|
||||||
"Text",
|
|
||||||
"Int8",
|
|
||||||
"Int8"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT\n survey_responses.ID,\n survey_responses.device_software_recognised,\n survey_responses.threads,\n survey_responses.user_id,\n survey_responses.submitted_at,\n survey_responses.device_user_provided,\n survey_bench_type.name\n FROM\n survey_responses\n INNER JOIN survey_bench_type ON\n survey_responses.submission_bench_type_id = survey_bench_type.ID\n WHERE\n survey_responses.campaign_id = (\n SELECT ID FROM survey_campaigns\n WHERE\n ID = $1\n AND\n user_id = (SELECT ID FROM survey_admins WHERE name = $2)\n )\n LIMIT $3 OFFSET $4"
|
|
||||||
},
|
|
||||||
"58ec3b8f98c27e13ec2732f8ee23f6eb9845ac5d9fd97b1e5c9f2eed4b1f5693": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Uuid",
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT name \n FROM survey_campaigns\n WHERE \n id = $1\n AND\n user_id = (SELECT ID from survey_admins WHERE name = $2)"
|
|
||||||
},
|
|
||||||
"683707dbc847b37c58c29aaad0d1a978c9fe0657da13af99796e4461134b5a43": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Varchar",
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "UPDATE survey_admins set email = $1\n WHERE name = $2"
|
|
||||||
},
|
|
||||||
"6a26daa84578aed2b2085697cb8358ed7c0a50ba9597fd387b4b09b0a8a154db": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "exists",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Bool"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
null
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT EXISTS (SELECT 1 from survey_admins WHERE email = $1)"
|
|
||||||
},
|
|
||||||
"70cc7bfc9b6ff5b68db70c069c0947d51bfc4a53cedc020016ee25ff98586c93": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"ordinal": 1,
|
|
||||||
"type_info": "Uuid"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT \n name, id\n FROM \n survey_campaigns \n WHERE\n user_id = (\n SELECT \n ID\n FROM \n survey_admins\n WHERE\n name = $1\n )"
|
|
||||||
},
|
|
||||||
"74c41e33f91cf31ea13582c8b3ca464544374842450d580517ca2bd01d67402e": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Int4"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "device_software_recognised",
|
|
||||||
"ordinal": 1,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "threads",
|
|
||||||
"ordinal": 2,
|
|
||||||
"type_info": "Int4"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "user_id",
|
|
||||||
"ordinal": 3,
|
|
||||||
"type_info": "Uuid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "submitted_at",
|
|
||||||
"ordinal": 4,
|
|
||||||
"type_info": "Timestamptz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "device_user_provided",
|
|
||||||
"ordinal": 5,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"ordinal": 6,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Uuid",
|
|
||||||
"Text",
|
|
||||||
"Text",
|
|
||||||
"Int8",
|
|
||||||
"Int8"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT\n survey_responses.ID,\n survey_responses.device_software_recognised,\n survey_responses.threads,\n survey_responses.user_id,\n survey_responses.submitted_at,\n survey_responses.device_user_provided,\n survey_bench_type.name\n FROM\n survey_responses\n INNER JOIN survey_bench_type ON\n survey_responses.submission_bench_type_id = survey_bench_type.ID\n WHERE\n survey_bench_type.name = $3\n AND\n survey_responses.campaign_id = (\n SELECT ID FROM survey_campaigns\n WHERE\n ID = $1\n AND\n user_id = (SELECT ID FROM survey_admins WHERE name = $2)\n )\n LIMIT $4 OFFSET $5"
|
|
||||||
},
|
|
||||||
"82feafc36533144e49ba374c8c47ca4aa0d6558a9803778ad28cfa7b62382c3e": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Text",
|
|
||||||
"Uuid",
|
|
||||||
"Varchar",
|
|
||||||
"Int4Array",
|
|
||||||
"Timestamptz"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "\n INSERT INTO survey_campaigns (\n user_id, ID, name, difficulties, created_at\n ) VALUES(\n (SELECT id FROM survey_admins WHERE name = $1),\n $2, $3, $4, $5\n );"
|
|
||||||
},
|
|
||||||
"858a4c06a5c1ba7adb79bcac7d42d106d09d0cbff10c197f2242dcb5c437a1df": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "created_at",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Timestamptz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"ordinal": 1,
|
|
||||||
"type_info": "Uuid"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Uuid"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT\n created_at,\n ID\n FROM\n survey_users\n WHERE\n ID = $1\n "
|
|
||||||
},
|
|
||||||
"9cdade613ce724631cc3f187510758ee0929e93ff3f8ce81fe35594756644246": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "difficulties",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Int4Array"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Uuid"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT difficulties FROM survey_campaigns WHERE id = $1;"
|
|
||||||
},
|
|
||||||
"a721cfa249acf328c2f29c4cf8c2aeba1a635bcf49d18ced5474caa10b7cae4f": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Int4",
|
|
||||||
"Int4",
|
|
||||||
"Float4"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "INSERT INTO survey_benches \n (resp_id, difficulty, duration) \n VALUES ($1, $2, $3);"
|
|
||||||
},
|
|
||||||
"ab951c5c318174c6538037947c2f52c61bcfe5e5be1901379b715e77f5214dd2": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Varchar",
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "UPDATE survey_admins set secret = $1\n WHERE name = $2"
|
|
||||||
},
|
|
||||||
"b2619292aa6bd1ac38dca152cbe607b795a151ddc212361a3c6d8c70ea1c93eb": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "duration",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Float4"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "difficulty",
|
|
||||||
"ordinal": 1,
|
|
||||||
"type_info": "Int4"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Int4"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT\n duration,\n difficulty\n FROM\n survey_benches\n WHERE\n resp_id = $1\n "
|
|
||||||
},
|
|
||||||
"c757589ef26a005e3285e7ab20d8a44c4f2e1cb125f8db061dd198cc380bf807": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Varchar",
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "UPDATE survey_admins set name = $1\n WHERE name = $2"
|
|
||||||
},
|
|
||||||
"e9cf5d6d8c9e8327d5c809d47a14a933f324e267f1e7dbb48e1caf1c021adc3f": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "secret",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Text"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT secret FROM survey_admins WHERE name = ($1)"
|
|
||||||
},
|
|
||||||
"efa0e41910fa5bcb187ba9e2fc8f37bee5b25ffe9a2d175f39a69899bc559965": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"ordinal": 0,
|
|
||||||
"type_info": "Uuid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"ordinal": 1,
|
|
||||||
"type_info": "Varchar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "difficulties",
|
|
||||||
"ordinal": 2,
|
|
||||||
"type_info": "Int4Array"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "created_at",
|
|
||||||
"ordinal": 3,
|
|
||||||
"type_info": "Timestamptz"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "SELECT ID, name, difficulties, created_at FROM survey_campaigns"
|
|
||||||
},
|
|
||||||
"fcdc5fe5d496eb516c805e64ec96d9626b74ab33cd6e75e5a08ae88967403b72": {
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"nullable": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Int4",
|
|
||||||
"Uuid",
|
|
||||||
"Uuid"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "INSERT INTO survey_response_tokens \n (resp_id, user_id, id)\n VALUES ($1, $2, $3);"
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,18 +1,6 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub mod v1;
|
pub mod v1;
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
|
|
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
use argon2_creds::Config;
|
use argon2_creds::Config;
|
||||||
|
@ -126,12 +115,12 @@ mod tests {
|
||||||
const PASSWORD: &str = "longpassword2";
|
const PASSWORD: &str = "longpassword2";
|
||||||
const EMAIL: &str = "updatepassuser@a.com";
|
const EMAIL: &str = "updatepassuser@a.com";
|
||||||
|
|
||||||
|
let data = get_test_data().await;
|
||||||
{
|
{
|
||||||
let data = get_test_data().await;
|
|
||||||
delete_user(NAME, &data).await;
|
delete_user(NAME, &data).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (data, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
|
let (_, signin_resp) = register_and_signin(&data, NAME, EMAIL, PASSWORD).await;
|
||||||
let cookies = get_cookie!(signin_resp);
|
let cookies = get_cookie!(signin_resp);
|
||||||
let app = get_app!(data).await;
|
let app = get_app!(data).await;
|
||||||
|
|
||||||
|
@ -164,6 +153,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
bad_post_req_test(
|
bad_post_req_test(
|
||||||
|
&data,
|
||||||
NAME,
|
NAME,
|
||||||
new_password,
|
new_password,
|
||||||
ROUTES.admin.account.update_password,
|
ROUTES.admin.account.update_password,
|
||||||
|
@ -179,6 +169,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
bad_post_req_test(
|
bad_post_req_test(
|
||||||
|
&data,
|
||||||
NAME,
|
NAME,
|
||||||
new_password,
|
new_password,
|
||||||
ROUTES.admin.account.update_password,
|
ROUTES.admin.account.update_password,
|
||||||
|
|
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
use actix_web::test;
|
use actix_web::test;
|
||||||
|
@ -34,12 +22,12 @@ async fn uname_email_exists_works() {
|
||||||
const PASSWORD: &str = "longpassword2";
|
const PASSWORD: &str = "longpassword2";
|
||||||
const EMAIL: &str = "testuserexists@a.com2";
|
const EMAIL: &str = "testuserexists@a.com2";
|
||||||
|
|
||||||
|
let data = get_test_data().await;
|
||||||
{
|
{
|
||||||
let data = get_test_data().await;
|
|
||||||
delete_user(NAME, &data).await;
|
delete_user(NAME, &data).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (data, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
|
let (_, signin_resp) = register_and_signin(&data, NAME, EMAIL, PASSWORD).await;
|
||||||
let cookies = get_cookie!(signin_resp);
|
let cookies = get_cookie!(signin_resp);
|
||||||
let app = get_app!(data).await;
|
let app = get_app!(data).await;
|
||||||
|
|
||||||
|
@ -124,14 +112,14 @@ async fn email_udpate_password_validation_del_userworks() {
|
||||||
const NAME2: &str = "eupdauser";
|
const NAME2: &str = "eupdauser";
|
||||||
const EMAIL2: &str = "eupdauser@a.com";
|
const EMAIL2: &str = "eupdauser@a.com";
|
||||||
|
|
||||||
|
let data = get_test_data().await;
|
||||||
{
|
{
|
||||||
let data = get_test_data().await;
|
|
||||||
delete_user(NAME, &data).await;
|
delete_user(NAME, &data).await;
|
||||||
delete_user(NAME2, &data).await;
|
delete_user(NAME2, &data).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = register_and_signin(NAME2, EMAIL2, PASSWORD).await;
|
let _ = register_and_signin(&data, NAME2, EMAIL2, PASSWORD).await;
|
||||||
let (data, _creds, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
|
let (_creds, signin_resp) = register_and_signin(&data, NAME, EMAIL, PASSWORD).await;
|
||||||
let cookies = get_cookie!(signin_resp);
|
let cookies = get_cookie!(signin_resp);
|
||||||
let app = get_app!(data).await;
|
let app = get_app!(data).await;
|
||||||
|
|
||||||
|
@ -152,6 +140,7 @@ async fn email_udpate_password_validation_del_userworks() {
|
||||||
// check duplicate email while duplicate email
|
// check duplicate email while duplicate email
|
||||||
email_payload.email = EMAIL2.into();
|
email_payload.email = EMAIL2.into();
|
||||||
bad_post_req_test(
|
bad_post_req_test(
|
||||||
|
&data,
|
||||||
NAME,
|
NAME,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
ROUTES.admin.account.update_email,
|
ROUTES.admin.account.update_email,
|
||||||
|
@ -165,6 +154,7 @@ async fn email_udpate_password_validation_del_userworks() {
|
||||||
password: NAME.into(),
|
password: NAME.into(),
|
||||||
};
|
};
|
||||||
bad_post_req_test(
|
bad_post_req_test(
|
||||||
|
&data,
|
||||||
NAME,
|
NAME,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
ROUTES.admin.account.delete,
|
ROUTES.admin.account.delete,
|
||||||
|
@ -207,9 +197,8 @@ async fn username_update_works() {
|
||||||
const NAME2: &str = "terstusrtds";
|
const NAME2: &str = "terstusrtds";
|
||||||
const NAME_CHANGE: &str = "terstusrtdsxx";
|
const NAME_CHANGE: &str = "terstusrtdsxx";
|
||||||
|
|
||||||
|
let data = get_test_data().await;
|
||||||
{
|
{
|
||||||
let data = get_test_data().await;
|
|
||||||
|
|
||||||
futures::join!(
|
futures::join!(
|
||||||
delete_user(NAME, &data),
|
delete_user(NAME, &data),
|
||||||
delete_user(NAME2, &data),
|
delete_user(NAME2, &data),
|
||||||
|
@ -217,8 +206,8 @@ async fn username_update_works() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = register_and_signin(NAME2, EMAIL2, PASSWORD).await;
|
let _ = register_and_signin(&data, NAME2, EMAIL2, PASSWORD).await;
|
||||||
let (data, _creds, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
|
let (_creds, signin_resp) = register_and_signin(&data, NAME, EMAIL, PASSWORD).await;
|
||||||
let cookies = get_cookie!(signin_resp);
|
let cookies = get_cookie!(signin_resp);
|
||||||
let app = get_app!(data).await;
|
let app = get_app!(data).await;
|
||||||
|
|
||||||
|
@ -238,6 +227,7 @@ async fn username_update_works() {
|
||||||
// check duplicate username with duplicate username
|
// check duplicate username with duplicate username
|
||||||
username_udpate.username = NAME2.into();
|
username_udpate.username = NAME2.into();
|
||||||
bad_post_req_test(
|
bad_post_req_test(
|
||||||
|
&data,
|
||||||
NAME_CHANGE,
|
NAME_CHANGE,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
ROUTES.admin.account.update_username,
|
ROUTES.admin.account.update_username,
|
||||||
|
|
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
use actix_web::http::header;
|
use actix_web::http::header;
|
||||||
|
|
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
|
@ -585,13 +574,13 @@ mod tests {
|
||||||
const DEVICE_SOFTWARE_RECOGNISED: &str = "Foobar.v2";
|
const DEVICE_SOFTWARE_RECOGNISED: &str = "Foobar.v2";
|
||||||
const THREADS: i32 = 4;
|
const THREADS: i32 = 4;
|
||||||
|
|
||||||
|
let data = get_test_data().await;
|
||||||
{
|
{
|
||||||
let data = get_test_data().await;
|
|
||||||
delete_user(NAME, &data).await;
|
delete_user(NAME, &data).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (data, _creds, signin_resp) =
|
let (_creds, signin_resp) =
|
||||||
register_and_signin(NAME, EMAIL, PASSWORD).await;
|
register_and_signin(&data, NAME, EMAIL, PASSWORD).await;
|
||||||
let cookies = get_cookie!(signin_resp);
|
let cookies = get_cookie!(signin_resp);
|
||||||
let survey = get_survey_user(data.clone()).await;
|
let survey = get_survey_user(data.clone()).await;
|
||||||
let survey_cookie = get_cookie!(survey);
|
let survey_cookie = get_cookie!(survey);
|
||||||
|
@ -682,6 +671,7 @@ mod tests {
|
||||||
assert_eq!(responses, res);
|
assert_eq!(responses, res);
|
||||||
|
|
||||||
bad_post_req_test_witout_payload(
|
bad_post_req_test_witout_payload(
|
||||||
|
&data,
|
||||||
NAME,
|
NAME,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
&V1_API_ROUTES.admin.campaign.delete.replace("{uuid}", NAME),
|
&V1_API_ROUTES.admin.campaign.delete.replace("{uuid}", NAME),
|
||||||
|
|
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use actix_auth_middleware::*;
|
use actix_auth_middleware::*;
|
||||||
use actix_web::web::ServiceConfig;
|
use actix_web::web::ServiceConfig;
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use actix_web::http::{header, StatusCode};
|
use actix_web::http::{header, StatusCode};
|
||||||
use actix_web::test;
|
use actix_web::test;
|
||||||
|
@ -53,11 +41,11 @@ async fn auth_works() {
|
||||||
delete_user(NAME, &data).await;
|
delete_user(NAME, &data).await;
|
||||||
|
|
||||||
// 1. Register and signin
|
// 1. Register and signin
|
||||||
let (_, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
|
let (_, signin_resp) = register_and_signin(&data, NAME, EMAIL, PASSWORD).await;
|
||||||
let cookies = get_cookie!(signin_resp);
|
let cookies = get_cookie!(signin_resp);
|
||||||
|
|
||||||
// Sign in with email
|
// Sign in with email
|
||||||
signin(EMAIL, PASSWORD).await;
|
signin(&data, EMAIL, PASSWORD).await;
|
||||||
|
|
||||||
// 2. check if duplicate username is allowed
|
// 2. check if duplicate username is allowed
|
||||||
let mut msg = Register {
|
let mut msg = Register {
|
||||||
|
@ -67,6 +55,7 @@ async fn auth_works() {
|
||||||
email: Some(EMAIL.into()),
|
email: Some(EMAIL.into()),
|
||||||
};
|
};
|
||||||
bad_post_req_test(
|
bad_post_req_test(
|
||||||
|
&data,
|
||||||
NAME,
|
NAME,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
ROUTES.admin.auth.register,
|
ROUTES.admin.auth.register,
|
||||||
|
@ -78,6 +67,7 @@ async fn auth_works() {
|
||||||
let name = format!("{}dupemail", NAME);
|
let name = format!("{}dupemail", NAME);
|
||||||
msg.username = name;
|
msg.username = name;
|
||||||
bad_post_req_test(
|
bad_post_req_test(
|
||||||
|
&data,
|
||||||
NAME,
|
NAME,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
ROUTES.admin.auth.register,
|
ROUTES.admin.auth.register,
|
||||||
|
@ -92,6 +82,7 @@ async fn auth_works() {
|
||||||
password: msg.password.clone(),
|
password: msg.password.clone(),
|
||||||
};
|
};
|
||||||
bad_post_req_test(
|
bad_post_req_test(
|
||||||
|
&data,
|
||||||
NAME,
|
NAME,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
ROUTES.admin.auth.login,
|
ROUTES.admin.auth.login,
|
||||||
|
@ -102,6 +93,7 @@ async fn auth_works() {
|
||||||
|
|
||||||
creds.login = "nonexistantuser@example.com".into();
|
creds.login = "nonexistantuser@example.com".into();
|
||||||
bad_post_req_test(
|
bad_post_req_test(
|
||||||
|
&data,
|
||||||
NAME,
|
NAME,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
ROUTES.admin.auth.login,
|
ROUTES.admin.auth.login,
|
||||||
|
@ -115,6 +107,7 @@ async fn auth_works() {
|
||||||
creds.password = NAME.into();
|
creds.password = NAME.into();
|
||||||
|
|
||||||
bad_post_req_test(
|
bad_post_req_test(
|
||||||
|
&data,
|
||||||
NAME,
|
NAME,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
ROUTES.admin.auth.login,
|
ROUTES.admin.auth.login,
|
||||||
|
|
|
@ -1,18 +1,7 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
mod auth;
|
mod auth;
|
||||||
mod protected;
|
mod protected;
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
use actix_web::test;
|
use actix_web::test;
|
||||||
|
@ -29,13 +17,13 @@ async fn protected_routes_work() {
|
||||||
const EMAIL: &str = "testuser119@a.com2";
|
const EMAIL: &str = "testuser119@a.com2";
|
||||||
|
|
||||||
let get_protected_urls = [V1_API_ROUTES.admin.auth.logout];
|
let get_protected_urls = [V1_API_ROUTES.admin.auth.logout];
|
||||||
|
let data = get_test_data().await;
|
||||||
|
|
||||||
{
|
{
|
||||||
let data = get_test_data().await;
|
|
||||||
delete_user(NAME, &data).await;
|
delete_user(NAME, &data).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (data, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
|
let (_, signin_resp) = register_and_signin(&data, NAME, EMAIL, PASSWORD).await;
|
||||||
let cookies = get_cookie!(signin_resp);
|
let cookies = get_cookie!(signin_resp);
|
||||||
let app = get_app!(data).await;
|
let app = get_app!(data).await;
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
|
819
src/api/v1/mcaptcha/db.rs
Normal file
819
src/api/v1/mcaptcha/db.rs
Normal file
|
@ -0,0 +1,819 @@
|
||||||
|
// Copyright (C) 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use url::Url;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::api::v1::get_random;
|
||||||
|
use crate::db::{
|
||||||
|
JobState, JOB_STATES, JOB_STATE_CREATE, JOB_STATE_FINISH, JOB_STATE_RUNNING,
|
||||||
|
};
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::mcaptcha::PerformanceAnalytics;
|
||||||
|
use crate::Data;
|
||||||
|
|
||||||
|
use sqlx::types::time::OffsetDateTime;
|
||||||
|
|
||||||
|
fn now_unix_time_stamp() -> OffsetDateTime {
|
||||||
|
OffsetDateTime::now_utc()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Data {
|
||||||
|
/// Check if an mCaptcha instance is registered on the database
|
||||||
|
pub async fn mcaptcha_url_exists(&self, url: &str) -> ServiceResult<bool> {
|
||||||
|
let res = sqlx::query!(
|
||||||
|
"SELECT EXISTS (SELECT 1 from survey_mcaptcha_hostname WHERE url = $1)",
|
||||||
|
url
|
||||||
|
)
|
||||||
|
.fetch_one(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut resp = false;
|
||||||
|
if let Some(x) = res.exists {
|
||||||
|
if x {
|
||||||
|
resp = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register an mCaptcha instance
|
||||||
|
pub async fn mcaptcha_register_instance(&self, url: &str) -> ServiceResult<String> {
|
||||||
|
let secret = get_random(32);
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO survey_mcaptcha_hostname (url, secret) VALUES ($1, $2)",
|
||||||
|
url,
|
||||||
|
&secret,
|
||||||
|
)
|
||||||
|
.execute(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the secret of an mCaptcha instance
|
||||||
|
pub async fn mcaptcha_update_secret(&self, url: &str) -> ServiceResult<String> {
|
||||||
|
let secret = get_random(32);
|
||||||
|
sqlx::query!(
|
||||||
|
"UPDATE survey_mcaptcha_hostname set secret = $1 WHERE url = $2",
|
||||||
|
&secret,
|
||||||
|
url
|
||||||
|
)
|
||||||
|
.execute(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Authenticate an mCaptcha instance and return its URL
|
||||||
|
pub async fn mcaptcha_authenticate(&self, secret: &str) -> ServiceResult<()> {
|
||||||
|
let res = sqlx::query!(
|
||||||
|
"SELECT EXISTS (
|
||||||
|
SELECT
|
||||||
|
url
|
||||||
|
FROM
|
||||||
|
survey_mcaptcha_hostname
|
||||||
|
WHERE secret = $1
|
||||||
|
)",
|
||||||
|
secret
|
||||||
|
)
|
||||||
|
.fetch_one(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !matches!(res.exists, Some(true)) {
|
||||||
|
return Err(ServiceError::WrongPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete mCaptcha instance from database
|
||||||
|
pub async fn mcaptcha_delete_mcaptcha_instance(
|
||||||
|
&self,
|
||||||
|
url: &str,
|
||||||
|
secret: &str,
|
||||||
|
) -> ServiceResult<()> {
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM survey_mcaptcha_hostname WHERE secret = $1 AND url =$2",
|
||||||
|
secret,
|
||||||
|
url
|
||||||
|
)
|
||||||
|
.execute(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/// Delete mCaptcha campaign from database
|
||||||
|
pub async fn mcaptcha_delete_mcaptcha_campaign(
|
||||||
|
&self,
|
||||||
|
campaign_id: &Uuid,
|
||||||
|
secret: &str,
|
||||||
|
) -> ServiceResult<()> {
|
||||||
|
let campaign_str = campaign_id.to_string();
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM
|
||||||
|
survey_mcaptcha_campaign
|
||||||
|
WHERE
|
||||||
|
campaign_id = $1
|
||||||
|
AND
|
||||||
|
url_id = (
|
||||||
|
SELECT
|
||||||
|
ID
|
||||||
|
FROM
|
||||||
|
survey_mcaptcha_hostname
|
||||||
|
WHERE
|
||||||
|
secret = $2
|
||||||
|
)",
|
||||||
|
&campaign_str,
|
||||||
|
secret
|
||||||
|
)
|
||||||
|
.execute(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if an mCaptcha instance campaign is registered on DB
|
||||||
|
pub async fn mcaptcha_campaign_is_registered(
|
||||||
|
&self,
|
||||||
|
campaign_id: &Uuid,
|
||||||
|
secret: &str,
|
||||||
|
) -> ServiceResult<bool> {
|
||||||
|
let campaign_str = campaign_id.to_string();
|
||||||
|
|
||||||
|
let res = sqlx::query!(
|
||||||
|
"SELECT EXISTS (
|
||||||
|
SELECT
|
||||||
|
ID
|
||||||
|
FROM
|
||||||
|
survey_mcaptcha_campaign
|
||||||
|
WHERE
|
||||||
|
campaign_id = $1
|
||||||
|
AND
|
||||||
|
url_id = (
|
||||||
|
SELECT
|
||||||
|
ID
|
||||||
|
FROM
|
||||||
|
survey_mcaptcha_hostname
|
||||||
|
WHERE
|
||||||
|
secret = $2
|
||||||
|
)
|
||||||
|
)",
|
||||||
|
&campaign_str,
|
||||||
|
secret
|
||||||
|
)
|
||||||
|
.fetch_one(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut resp = false;
|
||||||
|
if let Some(x) = res.exists {
|
||||||
|
if x {
|
||||||
|
resp = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register an mCaptcha instance campaign on DB
|
||||||
|
pub async fn mcaptcha_register_campaign(
|
||||||
|
&self,
|
||||||
|
campaign_id: &Uuid,
|
||||||
|
secret: &str,
|
||||||
|
) -> ServiceResult<()> {
|
||||||
|
let campaign_str = campaign_id.to_string();
|
||||||
|
let public_id = Uuid::new_v4();
|
||||||
|
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO
|
||||||
|
survey_mcaptcha_campaign (campaign_id, public_id, url_id)
|
||||||
|
VALUES ($1, $2, (SELECT ID FROM survey_mcaptcha_hostname WHERE secret = $3));",
|
||||||
|
&campaign_str,
|
||||||
|
&public_id.to_string(),
|
||||||
|
secret,
|
||||||
|
)
|
||||||
|
.execute(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register an mCaptcha instance campaign on DB
|
||||||
|
pub async fn mcaptcha_get_campaign_public_id(
|
||||||
|
&self,
|
||||||
|
campaign_id: &Uuid,
|
||||||
|
secret: &str,
|
||||||
|
) -> ServiceResult<Uuid> {
|
||||||
|
let campaign_str = campaign_id.to_string();
|
||||||
|
struct S {
|
||||||
|
public_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = sqlx::query_as!(
|
||||||
|
S,
|
||||||
|
"SELECT
|
||||||
|
public_id
|
||||||
|
FROM
|
||||||
|
survey_mcaptcha_campaign
|
||||||
|
WHERE
|
||||||
|
campaign_id = $1
|
||||||
|
AND
|
||||||
|
url_id = (SELECT ID FROM survey_mcaptcha_hostname WHERE secret = $2);",
|
||||||
|
&campaign_str,
|
||||||
|
secret,
|
||||||
|
)
|
||||||
|
.fetch_one(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Uuid::parse_str(&res.public_id).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an mCaptcha instance campaign checkpoint
|
||||||
|
pub async fn mcaptcha_get_checkpoint(
|
||||||
|
&self,
|
||||||
|
campaign_id: &Uuid,
|
||||||
|
) -> ServiceResult<usize> {
|
||||||
|
let campaign_str = campaign_id.to_string();
|
||||||
|
|
||||||
|
struct CheckPoint {
|
||||||
|
synced_till: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkpoint = sqlx::query_as!(
|
||||||
|
CheckPoint,
|
||||||
|
"SELECT
|
||||||
|
synced_till
|
||||||
|
FROM
|
||||||
|
survey_mcaptcha_campaign
|
||||||
|
WHERE
|
||||||
|
campaign_id = $1;",
|
||||||
|
&campaign_str,
|
||||||
|
)
|
||||||
|
.fetch_one(&self.db)
|
||||||
|
.await?;
|
||||||
|
let checkpoint = checkpoint.synced_till as usize;
|
||||||
|
Ok(checkpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set an mCaptcha instance campaign checkpoint
|
||||||
|
pub async fn mcaptcha_set_checkpoint(
|
||||||
|
&self,
|
||||||
|
campaign_id: &Uuid,
|
||||||
|
checkpoint: usize,
|
||||||
|
) -> ServiceResult<()> {
|
||||||
|
let campaign_str = campaign_id.to_string();
|
||||||
|
sqlx::query!(
|
||||||
|
"UPDATE
|
||||||
|
survey_mcaptcha_campaign
|
||||||
|
SET
|
||||||
|
synced_till = $1
|
||||||
|
WHERE
|
||||||
|
campaign_id = $2; ",
|
||||||
|
checkpoint as i32,
|
||||||
|
&campaign_str,
|
||||||
|
)
|
||||||
|
.execute(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store mCaptcha instance campaign analytics
|
||||||
|
pub async fn mcaptcha_insert_analytics(
|
||||||
|
&self,
|
||||||
|
campaign_id: &Uuid,
|
||||||
|
r: &PerformanceAnalytics,
|
||||||
|
) -> ServiceResult<()> {
|
||||||
|
let campaign_str = campaign_id.to_string();
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO
|
||||||
|
survey_mcaptcha_analytics (
|
||||||
|
campaign_id, time, difficulty_factor, worker_type
|
||||||
|
)
|
||||||
|
VALUES ((
|
||||||
|
SELECT
|
||||||
|
ID
|
||||||
|
FROM
|
||||||
|
survey_mcaptcha_campaign
|
||||||
|
WHERE
|
||||||
|
campaign_id = $1
|
||||||
|
), $2, $3, $4
|
||||||
|
);",
|
||||||
|
&campaign_str,
|
||||||
|
r.time as i32,
|
||||||
|
r.difficulty_factor as i32,
|
||||||
|
&r.worker_type,
|
||||||
|
)
|
||||||
|
.execute(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// fetch PoW analytics
|
||||||
|
pub async fn mcaptcha_analytics_fetch(
|
||||||
|
&self,
|
||||||
|
public_id: &Uuid,
|
||||||
|
limit: usize,
|
||||||
|
offset: usize,
|
||||||
|
) -> ServiceResult<Vec<PerformanceAnalytics>> {
|
||||||
|
let public_id_str = public_id.to_string();
|
||||||
|
struct P {
|
||||||
|
id: i32,
|
||||||
|
time: i32,
|
||||||
|
difficulty_factor: i32,
|
||||||
|
worker_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<P> for PerformanceAnalytics {
|
||||||
|
fn from(v: P) -> Self {
|
||||||
|
Self {
|
||||||
|
time: v.time as u32,
|
||||||
|
difficulty_factor: v.difficulty_factor as u32,
|
||||||
|
worker_type: v.worker_type,
|
||||||
|
id: v.id as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut c = sqlx::query_as!(
|
||||||
|
P,
|
||||||
|
"SELECT id, time, difficulty_factor, worker_type FROM survey_mcaptcha_analytics
|
||||||
|
WHERE
|
||||||
|
campaign_id = (
|
||||||
|
SELECT
|
||||||
|
ID FROM survey_mcaptcha_campaign
|
||||||
|
WHERE
|
||||||
|
public_id = $1
|
||||||
|
)
|
||||||
|
ORDER BY ID
|
||||||
|
OFFSET $2 LIMIT $3
|
||||||
|
",
|
||||||
|
&public_id_str,
|
||||||
|
offset as i32,
|
||||||
|
limit as i32
|
||||||
|
)
|
||||||
|
.fetch_all(&self.db)
|
||||||
|
.await?;
|
||||||
|
let mut res = Vec::with_capacity(c.len());
|
||||||
|
for i in c.drain(0..) {
|
||||||
|
res.push(i.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_next_job_to_run(&self) -> ServiceResult<Option<SchedulerJob>> {
|
||||||
|
let res = match sqlx::query_as!(
|
||||||
|
InnerSchedulerJob,
|
||||||
|
"SELECT
|
||||||
|
survey_mcaptcha_campaign.campaign_id,
|
||||||
|
survey_mcaptcha_upload_jobs.public_id,
|
||||||
|
survey_mcaptcha_hostname.url
|
||||||
|
FROM
|
||||||
|
survey_mcaptcha_campaign
|
||||||
|
INNER JOIN
|
||||||
|
survey_mcaptcha_upload_jobs
|
||||||
|
ON
|
||||||
|
survey_mcaptcha_upload_jobs.campaign_id = survey_mcaptcha_campaign.ID
|
||||||
|
INNER JOIN
|
||||||
|
survey_mcaptcha_hostname
|
||||||
|
ON
|
||||||
|
survey_mcaptcha_hostname.ID = survey_mcaptcha_campaign.url_id
|
||||||
|
WHERE
|
||||||
|
survey_mcaptcha_upload_jobs.job_state = (
|
||||||
|
SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1
|
||||||
|
)
|
||||||
|
AND
|
||||||
|
survey_mcaptcha_upload_jobs.finished_at is NULL
|
||||||
|
AND
|
||||||
|
survey_mcaptcha_upload_jobs.scheduled_at is NULL
|
||||||
|
ORDER BY created_at ASC;",
|
||||||
|
&JOB_STATE_CREATE.name
|
||||||
|
)
|
||||||
|
.fetch_one(&self.db)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(res) => Ok(Some(res.into())),
|
||||||
|
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_job(&self, campaign_id: &Uuid) -> ServiceResult<Uuid> {
|
||||||
|
let now = now_unix_time_stamp();
|
||||||
|
|
||||||
|
if let Some(unfinished_job) =
|
||||||
|
self.get_unfinished_job_for_campaign(campaign_id).await?
|
||||||
|
{
|
||||||
|
return Ok(unfinished_job.public_job_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let public_id = Uuid::new_v4();
|
||||||
|
let public_id_str = public_id.to_string();
|
||||||
|
|
||||||
|
let campaign_str = campaign_id.to_string();
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO survey_mcaptcha_upload_jobs
|
||||||
|
(campaign_id, job_state, created_at, public_id)
|
||||||
|
VALUES (
|
||||||
|
(SELECT ID FROM survey_mcaptcha_campaign WHERE campaign_id = $1),
|
||||||
|
(SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $2),
|
||||||
|
$3, $4)",
|
||||||
|
&campaign_str,
|
||||||
|
&JOB_STATE_CREATE.name,
|
||||||
|
now,
|
||||||
|
public_id_str
|
||||||
|
)
|
||||||
|
.execute(&self.db)
|
||||||
|
.await?;
|
||||||
|
Ok(public_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_unfinished_job_for_campaign(
|
||||||
|
&self,
|
||||||
|
campaign_id: &Uuid,
|
||||||
|
) -> ServiceResult<Option<Job>> {
|
||||||
|
let res = match sqlx::query_as!(
|
||||||
|
InnerJob,
|
||||||
|
"
|
||||||
|
SELECT
|
||||||
|
survey_mcaptcha_upload_jobs.ID,
|
||||||
|
survey_mcaptcha_upload_jobs.public_id,
|
||||||
|
survey_mcaptcha_campaign.campaign_id,
|
||||||
|
survey_mcaptcha_campaign.public_id as campaign_public_id,
|
||||||
|
survey_mcaptcha_upload_job_states.name,
|
||||||
|
survey_mcaptcha_upload_jobs.created_at,
|
||||||
|
survey_mcaptcha_upload_jobs.scheduled_at,
|
||||||
|
survey_mcaptcha_upload_jobs.finished_at
|
||||||
|
|
||||||
|
FROM survey_mcaptcha_upload_jobs
|
||||||
|
INNER JOIN
|
||||||
|
survey_mcaptcha_upload_job_states
|
||||||
|
ON
|
||||||
|
survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state
|
||||||
|
INNER JOIN
|
||||||
|
survey_mcaptcha_campaign
|
||||||
|
ON
|
||||||
|
survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id
|
||||||
|
WHERE
|
||||||
|
survey_mcaptcha_campaign.campaign_id = $1
|
||||||
|
AND
|
||||||
|
survey_mcaptcha_upload_job_states.name = $2;",
|
||||||
|
&campaign_id.to_string(),
|
||||||
|
&JOB_STATE_CREATE.name
|
||||||
|
)
|
||||||
|
.fetch_one(&self.db)
|
||||||
|
.await {
|
||||||
|
Ok(res) => Ok(Some(res.into())),
|
||||||
|
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_job(&self, public_id: &uuid::Uuid) -> ServiceResult<Option<Job>> {
|
||||||
|
let res = match sqlx::query_as!(
|
||||||
|
InnerJob,
|
||||||
|
"
|
||||||
|
SELECT
|
||||||
|
survey_mcaptcha_upload_jobs.ID,
|
||||||
|
survey_mcaptcha_upload_jobs.public_id,
|
||||||
|
survey_mcaptcha_campaign.campaign_id,
|
||||||
|
survey_mcaptcha_campaign.public_id as campaign_public_id,
|
||||||
|
survey_mcaptcha_upload_job_states.name,
|
||||||
|
survey_mcaptcha_upload_jobs.created_at,
|
||||||
|
survey_mcaptcha_upload_jobs.scheduled_at,
|
||||||
|
survey_mcaptcha_upload_jobs.finished_at
|
||||||
|
|
||||||
|
FROM survey_mcaptcha_upload_jobs
|
||||||
|
INNER JOIN
|
||||||
|
survey_mcaptcha_upload_job_states
|
||||||
|
ON
|
||||||
|
survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state
|
||||||
|
INNER JOIN
|
||||||
|
survey_mcaptcha_campaign
|
||||||
|
ON
|
||||||
|
survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id
|
||||||
|
WHERE
|
||||||
|
survey_mcaptcha_upload_jobs.public_id = $1",
|
||||||
|
&public_id.to_string()
|
||||||
|
)
|
||||||
|
.fetch_one(&self.db)
|
||||||
|
.await {
|
||||||
|
Ok(res) => Ok(Some(res.into())),
|
||||||
|
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_all_jobs_of_state(
|
||||||
|
&self,
|
||||||
|
state: &JobState,
|
||||||
|
) -> ServiceResult<Vec<Job>> {
|
||||||
|
let mut res = sqlx::query_as!(
|
||||||
|
InnerJob,
|
||||||
|
"
|
||||||
|
SELECT
|
||||||
|
survey_mcaptcha_upload_jobs.ID,
|
||||||
|
survey_mcaptcha_upload_jobs.public_id,
|
||||||
|
survey_mcaptcha_campaign.campaign_id,
|
||||||
|
survey_mcaptcha_campaign.public_id as campaign_public_id,
|
||||||
|
survey_mcaptcha_upload_job_states.name,
|
||||||
|
survey_mcaptcha_upload_jobs.created_at,
|
||||||
|
survey_mcaptcha_upload_jobs.scheduled_at,
|
||||||
|
survey_mcaptcha_upload_jobs.finished_at
|
||||||
|
|
||||||
|
FROM survey_mcaptcha_upload_jobs
|
||||||
|
INNER JOIN
|
||||||
|
survey_mcaptcha_upload_job_states
|
||||||
|
ON
|
||||||
|
survey_mcaptcha_upload_job_states.ID = survey_mcaptcha_upload_jobs.job_state
|
||||||
|
INNER JOIN
|
||||||
|
survey_mcaptcha_campaign
|
||||||
|
ON
|
||||||
|
survey_mcaptcha_campaign.ID = survey_mcaptcha_upload_jobs.campaign_id
|
||||||
|
WHERE
|
||||||
|
survey_mcaptcha_upload_job_states.name = $1;",
|
||||||
|
&state.name
|
||||||
|
)
|
||||||
|
.fetch_all(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let res = res.drain(0..).map(|r| r.into()).collect();
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn mark_job_scheduled(&self, job: &SchedulerJob) -> ServiceResult<()> {
|
||||||
|
let now = now_unix_time_stamp();
|
||||||
|
sqlx::query!(
|
||||||
|
"
|
||||||
|
UPDATE
|
||||||
|
survey_mcaptcha_upload_jobs
|
||||||
|
SET
|
||||||
|
job_state = (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1),
|
||||||
|
scheduled_at = $2
|
||||||
|
WHERE public_id = $3;",
|
||||||
|
&JOB_STATE_RUNNING.name,
|
||||||
|
now,
|
||||||
|
&job.public_job_id.to_string(),
|
||||||
|
)
|
||||||
|
.execute(&self.db)
|
||||||
|
.await
|
||||||
|
?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn mark_job_finished(&self, job: &SchedulerJob) -> ServiceResult<()> {
|
||||||
|
let now = now_unix_time_stamp();
|
||||||
|
sqlx::query!(
|
||||||
|
"
|
||||||
|
UPDATE
|
||||||
|
survey_mcaptcha_upload_jobs
|
||||||
|
SET
|
||||||
|
job_state = (SELECT ID FROM survey_mcaptcha_upload_job_states WHERE name = $1),
|
||||||
|
finished_at = $2
|
||||||
|
WHERE public_id = $3;",
|
||||||
|
&JOB_STATE_FINISH.name,
|
||||||
|
now,
|
||||||
|
&job.public_job_id.to_string(),
|
||||||
|
)
|
||||||
|
.execute(&self.db)
|
||||||
|
.await
|
||||||
|
?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct SchedulerJob {
|
||||||
|
pub campaign_id: Uuid,
|
||||||
|
pub public_job_id: Uuid,
|
||||||
|
pub url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
struct InnerSchedulerJob {
|
||||||
|
campaign_id: String,
|
||||||
|
public_id: String,
|
||||||
|
url: String,
|
||||||
|
}
|
||||||
|
impl From<InnerSchedulerJob> for SchedulerJob {
|
||||||
|
fn from(j: InnerSchedulerJob) -> Self {
|
||||||
|
SchedulerJob {
|
||||||
|
campaign_id: Uuid::parse_str(&j.campaign_id).unwrap(),
|
||||||
|
public_job_id: Uuid::parse_str(&j.public_id).unwrap(),
|
||||||
|
url: Url::parse(&j.url).unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Job {
|
||||||
|
pub state: JobState,
|
||||||
|
pub campaign_id: Uuid,
|
||||||
|
pub campaign_public_id: Uuid,
|
||||||
|
pub public_job_id: Uuid,
|
||||||
|
pub id: u32,
|
||||||
|
pub created_at: OffsetDateTime,
|
||||||
|
pub scheduled_at: Option<OffsetDateTime>,
|
||||||
|
pub finished_at: Option<OffsetDateTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InnerJob {
|
||||||
|
name: String,
|
||||||
|
campaign_id: String,
|
||||||
|
public_id: String,
|
||||||
|
campaign_public_id: String,
|
||||||
|
id: i32,
|
||||||
|
created_at: OffsetDateTime,
|
||||||
|
scheduled_at: Option<OffsetDateTime>,
|
||||||
|
finished_at: Option<OffsetDateTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<InnerJob> for Job {
|
||||||
|
fn from(j: InnerJob) -> Self {
|
||||||
|
Job {
|
||||||
|
state: (JOB_STATES)
|
||||||
|
.iter()
|
||||||
|
.find(|d| d.name == j.name)
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.to_owned(),
|
||||||
|
id: j.id as u32,
|
||||||
|
created_at: j.created_at,
|
||||||
|
scheduled_at: j.scheduled_at,
|
||||||
|
finished_at: j.finished_at,
|
||||||
|
|
||||||
|
campaign_id: Uuid::parse_str(&j.campaign_id).unwrap(),
|
||||||
|
campaign_public_id: Uuid::parse_str(&j.campaign_public_id).unwrap(),
|
||||||
|
public_job_id: Uuid::parse_str(&j.public_id).unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{mcaptcha::PerformanceAnalytics, tests::*};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_db_mcaptcha_works() {
|
||||||
|
let url = Url::parse("http://test_add_campaign.example").unwrap();
|
||||||
|
let data = get_test_data().await;
|
||||||
|
let url_str = url.to_string();
|
||||||
|
if data.mcaptcha_url_exists(&url_str).await.unwrap() {
|
||||||
|
let secret = data.mcaptcha_update_secret(&url_str).await.unwrap();
|
||||||
|
data.mcaptcha_delete_mcaptcha_instance(&url_str, &secret)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
assert!(!data.mcaptcha_url_exists(&url_str).await.unwrap());
|
||||||
|
|
||||||
|
let secret = data.mcaptcha_register_instance(&url_str).await.unwrap();
|
||||||
|
assert!(data.mcaptcha_url_exists(&url_str).await.unwrap());
|
||||||
|
let secret2 = data.mcaptcha_update_secret(&url_str).await.unwrap();
|
||||||
|
assert_ne!(secret2, secret);
|
||||||
|
let secret = secret2;
|
||||||
|
|
||||||
|
assert!(data.mcaptcha_authenticate(&secret).await.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
data.mcaptcha_authenticate("foo").await.err(),
|
||||||
|
Some(ServiceError::WrongPassword)
|
||||||
|
);
|
||||||
|
|
||||||
|
let uuid = Uuid::new_v4();
|
||||||
|
|
||||||
|
if data
|
||||||
|
.mcaptcha_campaign_is_registered(&uuid, &secret)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
{
|
||||||
|
data.mcaptcha_delete_mcaptcha_campaign(&uuid, &secret)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(!data
|
||||||
|
.mcaptcha_campaign_is_registered(&uuid, &secret)
|
||||||
|
.await
|
||||||
|
.unwrap());
|
||||||
|
data.mcaptcha_register_campaign(&uuid, &secret)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(data
|
||||||
|
.mcaptcha_campaign_is_registered(&uuid, &secret)
|
||||||
|
.await
|
||||||
|
.unwrap());
|
||||||
|
|
||||||
|
assert_eq!(data.mcaptcha_get_checkpoint(&uuid).await.unwrap(), 0);
|
||||||
|
data.mcaptcha_set_checkpoint(&uuid, 1).await.unwrap();
|
||||||
|
assert_eq!(data.mcaptcha_get_checkpoint(&uuid).await.unwrap(), 1);
|
||||||
|
|
||||||
|
let analytics = PerformanceAnalytics {
|
||||||
|
id: 1,
|
||||||
|
time: 1,
|
||||||
|
difficulty_factor: 1,
|
||||||
|
worker_type: "foo".to_string(),
|
||||||
|
};
|
||||||
|
data.mcaptcha_insert_analytics(&uuid, &analytics)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let public_id = data
|
||||||
|
.mcaptcha_get_campaign_public_id(&uuid, &secret)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let db_analytics = data
|
||||||
|
.mcaptcha_analytics_fetch(&public_id, 50, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(db_analytics.len(), 1);
|
||||||
|
assert_eq!(db_analytics[0].time, analytics.time);
|
||||||
|
assert_eq!(
|
||||||
|
db_analytics[0].difficulty_factor,
|
||||||
|
analytics.difficulty_factor
|
||||||
|
);
|
||||||
|
assert_eq!(db_analytics[0].worker_type, analytics.worker_type);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
data.mcaptcha_analytics_fetch(&public_id, 50, 1)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
vec![]
|
||||||
|
);
|
||||||
|
|
||||||
|
// job related stuff
|
||||||
|
|
||||||
|
let job1_public_id = data.add_job(&uuid).await.unwrap();
|
||||||
|
let job = data.get_job(&job1_public_id).await.unwrap().unwrap();
|
||||||
|
assert_eq!(public_id, job.campaign_public_id);
|
||||||
|
assert_eq!(
|
||||||
|
data.get_unfinished_job_for_campaign(&uuid)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap(),
|
||||||
|
job
|
||||||
|
);
|
||||||
|
let job2_public_id = data.add_job(&uuid).await.unwrap();
|
||||||
|
let job2 = data.get_job(&job2_public_id).await.unwrap().unwrap();
|
||||||
|
assert_eq!(job2, job);
|
||||||
|
let scheduler_job = data.get_next_job_to_run().await.unwrap().unwrap();
|
||||||
|
assert_eq!(scheduler_job.url, url);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
data.get_next_job_to_run()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.public_job_id,
|
||||||
|
job.public_job_id
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(job.created_at < now_unix_time_stamp());
|
||||||
|
assert!(job.scheduled_at.is_none());
|
||||||
|
assert!(job.finished_at.is_none());
|
||||||
|
assert_eq!(
|
||||||
|
data.get_all_jobs_of_state(&JOB_STATE_CREATE).await.unwrap(),
|
||||||
|
vec![job.clone()]
|
||||||
|
);
|
||||||
|
|
||||||
|
data.mark_job_scheduled(&scheduler_job).await.unwrap();
|
||||||
|
assert!(data.get_next_job_to_run().await.unwrap().is_none(),);
|
||||||
|
let job = data.get_job(&job.public_job_id).await.unwrap().unwrap();
|
||||||
|
assert!(job.scheduled_at.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
data.get_all_jobs_of_state(&JOB_STATE_RUNNING)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
vec![job.clone()]
|
||||||
|
);
|
||||||
|
|
||||||
|
data.mark_job_finished(&scheduler_job).await.unwrap();
|
||||||
|
let job = data.get_job(&job.public_job_id).await.unwrap().unwrap();
|
||||||
|
assert!(job.finished_at.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
data.get_all_jobs_of_state(&JOB_STATE_FINISH).await.unwrap(),
|
||||||
|
vec![job.clone()]
|
||||||
|
);
|
||||||
|
|
||||||
|
let job2_public_id = data.add_job(&uuid).await.unwrap();
|
||||||
|
let job2 = data.get_job(&job2_public_id).await.unwrap().unwrap();
|
||||||
|
assert_ne!(job2.public_job_id, job.public_job_id);
|
||||||
|
assert_eq!(
|
||||||
|
data.get_next_job_to_run()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.public_job_id,
|
||||||
|
job2.public_job_id
|
||||||
|
);
|
||||||
|
assert_eq!(public_id, job2.campaign_public_id);
|
||||||
|
}
|
||||||
|
}
|
260
src/api/v1/mcaptcha/hooks.rs
Normal file
260
src/api/v1/mcaptcha/hooks.rs
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
// Copyright (C) 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use actix_web::web::ServiceConfig;
|
||||||
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use url::Url;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::api::v1::ROUTES;
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::AppData;
|
||||||
|
|
||||||
|
pub fn services(cfg: &mut ServiceConfig) {
|
||||||
|
cfg.service(register);
|
||||||
|
cfg.service(upload);
|
||||||
|
cfg.service(download);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct MCaptchaInstance {
|
||||||
|
pub url: Url,
|
||||||
|
pub auth_token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web_codegen_const_routes::post(path = "ROUTES.mcaptcha.register")]
|
||||||
|
async fn register(
|
||||||
|
data: AppData,
|
||||||
|
payload: web::Json<MCaptchaInstance>,
|
||||||
|
) -> ServiceResult<impl Responder> {
|
||||||
|
/* Summary
|
||||||
|
* 1. Check if secret exists
|
||||||
|
* 2. If not, add hostname and create secret
|
||||||
|
* 3. Post to mCaptcha
|
||||||
|
*/
|
||||||
|
|
||||||
|
let url_str = payload.url.to_string();
|
||||||
|
let secret = if data.mcaptcha_url_exists(&url_str).await? {
|
||||||
|
data.mcaptcha_update_secret(&url_str).await?
|
||||||
|
} else {
|
||||||
|
data.mcaptcha_register_instance(&url_str).await?
|
||||||
|
};
|
||||||
|
|
||||||
|
let payload = payload.into_inner();
|
||||||
|
data.mcaptcha
|
||||||
|
.share_secret(payload.url, secret, payload.auth_token)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, Eq, PartialEq, Clone)]
|
||||||
|
pub struct UploadJobCreated {
|
||||||
|
id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
|
||||||
|
pub struct Secret {
|
||||||
|
pub secret: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web_codegen_const_routes::post(path = "ROUTES.mcaptcha.upload")]
|
||||||
|
async fn upload(
|
||||||
|
data: AppData,
|
||||||
|
campaign: web::Path<uuid::Uuid>,
|
||||||
|
payload: web::Json<Secret>,
|
||||||
|
) -> ServiceResult<impl Responder> {
|
||||||
|
/* TODO
|
||||||
|
* 1. Authenticate: Get URL from secret
|
||||||
|
* 2. Check if campaign exists
|
||||||
|
* 3. If not: create campaign
|
||||||
|
* 4. Get last known sync point
|
||||||
|
* 5. Download results
|
||||||
|
* 6. Update sync point
|
||||||
|
*/
|
||||||
|
data.mcaptcha_authenticate(&payload.secret).await?;
|
||||||
|
// let campaign_str = campaign.to_string();
|
||||||
|
|
||||||
|
if !data
|
||||||
|
.mcaptcha_campaign_is_registered(&campaign, &payload.secret)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
data.mcaptcha_register_campaign(&campaign, &payload.secret)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = UploadJobCreated {
|
||||||
|
id: data.add_job(&campaign).await?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(HttpResponse::Created().json(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Page {
|
||||||
|
pub page: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web_codegen_const_routes::get(path = "ROUTES.mcaptcha.download")]
|
||||||
|
async fn download(
|
||||||
|
data: AppData,
|
||||||
|
page: web::Query<Page>,
|
||||||
|
public_id: web::Path<uuid::Uuid>,
|
||||||
|
) -> ServiceResult<impl Responder> {
|
||||||
|
const LIMIT: usize = 50;
|
||||||
|
let offset = LIMIT as isize * ((page.page as isize) - 1);
|
||||||
|
let offset = if offset < 0 { 0 } else { offset };
|
||||||
|
let public_id = public_id.into_inner();
|
||||||
|
let resp = data
|
||||||
|
.mcaptcha_analytics_fetch(&public_id, LIMIT, offset as usize)
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Secret;
|
||||||
|
use crate::api::v1::get_random;
|
||||||
|
use crate::mcaptcha::PerformanceAnalytics;
|
||||||
|
use crate::tests::*;
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
use actix_web::test;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn mcaptcha_hooks_work() {
|
||||||
|
let mcaptcha_instance =
|
||||||
|
url::Url::parse("http://mcaptcha_hooks_work.example.org").unwrap();
|
||||||
|
let mcaptcha_instance_str = mcaptcha_instance.to_string();
|
||||||
|
let campaign_id = uuid::Uuid::new_v4();
|
||||||
|
|
||||||
|
let (data, client) = get_test_data_with_mcaptcha_client().await;
|
||||||
|
let app = get_app!(data).await;
|
||||||
|
|
||||||
|
let mcaptcha_downloader =
|
||||||
|
crate::mcaptcha::MCaptchaDownloader::new(AppData::new(data.clone()));
|
||||||
|
let (mcaptcha_downloader_killer, mcaptcha_downloader_job) =
|
||||||
|
mcaptcha_downloader.start_job().await.unwrap();
|
||||||
|
|
||||||
|
if data
|
||||||
|
.mcaptcha_url_exists(&mcaptcha_instance_str)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
{
|
||||||
|
let secret = data
|
||||||
|
.mcaptcha_update_secret(&mcaptcha_instance_str)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
data.mcaptcha_delete_mcaptcha_instance(&mcaptcha_instance_str, &secret)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = super::MCaptchaInstance {
|
||||||
|
url: mcaptcha_instance.clone(),
|
||||||
|
auth_token: get_random(23),
|
||||||
|
};
|
||||||
|
|
||||||
|
let resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&payload, V1_API_ROUTES.mcaptcha.register).to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
let secret = {
|
||||||
|
let mut mcaptcha = payload.url.clone();
|
||||||
|
mcaptcha.set_path("/api/v1/survey/secret");
|
||||||
|
let mut x = client.client.write().unwrap();
|
||||||
|
x.remove(&mcaptcha.to_string()).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let resp2 = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&payload, V1_API_ROUTES.mcaptcha.register).to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp2.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
let secret2 = {
|
||||||
|
let mut mcaptcha = payload.url.clone();
|
||||||
|
mcaptcha.set_path("/api/v1/survey/secret");
|
||||||
|
let mut x = client.client.write().unwrap();
|
||||||
|
x.remove(&mcaptcha.to_string()).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_ne!(secret, secret2);
|
||||||
|
let secret = secret2;
|
||||||
|
|
||||||
|
let payload = Secret {
|
||||||
|
secret: secret.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if data
|
||||||
|
.mcaptcha_campaign_is_registered(&campaign_id, &secret)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
{
|
||||||
|
data.mcaptcha_delete_mcaptcha_campaign(&campaign_id, &secret)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(
|
||||||
|
&payload,
|
||||||
|
&V1_API_ROUTES
|
||||||
|
.mcaptcha
|
||||||
|
.get_upload_route(&campaign_id.to_string())
|
||||||
|
)
|
||||||
|
.to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::CREATED);
|
||||||
|
let job: super::UploadJobCreated = test::read_body_json(resp).await;
|
||||||
|
loop {
|
||||||
|
if let Some(job) = data.get_job(&job.id).await.unwrap() {
|
||||||
|
if job.state == *crate::db::JOB_STATE_FINISH {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::time::sleep(std::time::Duration::new(1, 0)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let public_id = data
|
||||||
|
.mcaptcha_get_campaign_public_id(&campaign_id, &secret)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let expected = crate::mcaptcha::tests::BENCHMARK.clone();
|
||||||
|
|
||||||
|
let got = data
|
||||||
|
.mcaptcha_analytics_fetch(&public_id, 50, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for i in 0..2 {
|
||||||
|
assert_eq!(got[i].time, expected[i].time);
|
||||||
|
assert_eq!(got[i].difficulty_factor, expected[i].difficulty_factor);
|
||||||
|
assert_eq!(got[i].worker_type, expected[i].worker_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
let resp = get_request!(
|
||||||
|
&app,
|
||||||
|
&V1_API_ROUTES
|
||||||
|
.mcaptcha
|
||||||
|
.get_download_route(&public_id.to_string(), 0)
|
||||||
|
);
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
let resp: Vec<PerformanceAnalytics> = test::read_body_json(resp).await;
|
||||||
|
assert_eq!(resp.len(), 2);
|
||||||
|
assert_eq!(resp, got);
|
||||||
|
mcaptcha_downloader_killer.send(()).unwrap();
|
||||||
|
mcaptcha_downloader_job.await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
46
src/api/v1/mcaptcha/mod.rs
Normal file
46
src/api/v1/mcaptcha/mod.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright (C) 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use actix_web::web::ServiceConfig;
|
||||||
|
|
||||||
|
pub mod db;
|
||||||
|
pub mod hooks;
|
||||||
|
|
||||||
|
pub fn services(cfg: &mut ServiceConfig) {
|
||||||
|
hooks::services(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod routes {
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Mcaptcha {
|
||||||
|
pub upload: &'static str,
|
||||||
|
pub download: &'static str,
|
||||||
|
pub register: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mcaptcha {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
register: "/mcaptcha/api/v1/register",
|
||||||
|
upload: "/mcaptcha/api/v1/{campaign_id}/upload",
|
||||||
|
download: "/mcapthca/api/v1/{campaign_id}/download",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_download_route(&self, campaign_id: &str, page: usize) -> String {
|
||||||
|
format!(
|
||||||
|
"{}?page={}",
|
||||||
|
self.download.replace("{campaign_id}", campaign_id),
|
||||||
|
page
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_upload_route(&self, campaign_id: &str) -> String {
|
||||||
|
self.upload.replace("{campaign_id}", campaign_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,7 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
|
|
|
@ -1,33 +1,26 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use actix_web::web::ServiceConfig;
|
use actix_web::web::ServiceConfig;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::types::Uuid;
|
use sqlx::types::Uuid;
|
||||||
|
|
||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod bench;
|
pub mod bench;
|
||||||
|
pub mod mcaptcha;
|
||||||
mod meta;
|
mod meta;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
|
pub mod stats;
|
||||||
pub use routes::ROUTES;
|
pub use routes::ROUTES;
|
||||||
|
|
||||||
pub fn services(cfg: &mut ServiceConfig) {
|
pub fn services(cfg: &mut ServiceConfig) {
|
||||||
meta::services(cfg);
|
meta::services(cfg);
|
||||||
bench::services(cfg);
|
bench::services(cfg);
|
||||||
admin::services(cfg);
|
admin::services(cfg);
|
||||||
|
mcaptcha::services(cfg);
|
||||||
|
stats::services(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_random(len: usize) -> String {
|
pub fn get_random(len: usize) -> String {
|
||||||
|
|
|
@ -1,24 +1,15 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::admin::routes::Admin;
|
use super::admin::routes::Admin;
|
||||||
use super::bench::routes::Benches;
|
use super::bench::routes::Benches;
|
||||||
|
use super::mcaptcha::routes::Mcaptcha;
|
||||||
use super::meta::routes::Meta;
|
use super::meta::routes::Meta;
|
||||||
|
use super::stats::routes::Stats;
|
||||||
|
|
||||||
pub const ROUTES: Routes = Routes::new();
|
pub const ROUTES: Routes = Routes::new();
|
||||||
|
|
||||||
|
@ -27,6 +18,8 @@ pub struct Routes {
|
||||||
pub admin: Admin,
|
pub admin: Admin,
|
||||||
pub meta: Meta,
|
pub meta: Meta,
|
||||||
pub benches: Benches,
|
pub benches: Benches,
|
||||||
|
pub mcaptcha: Mcaptcha,
|
||||||
|
pub stats: Stats,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Routes {
|
impl Routes {
|
||||||
|
@ -35,6 +28,8 @@ impl Routes {
|
||||||
admin: Admin::new(),
|
admin: Admin::new(),
|
||||||
meta: Meta::new(),
|
meta: Meta::new(),
|
||||||
benches: Benches::new(),
|
benches: Benches::new(),
|
||||||
|
mcaptcha: Mcaptcha::new(),
|
||||||
|
stats: Stats::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
256
src/api/v1/stats.rs
Normal file
256
src/api/v1/stats.rs
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
|
use derive_builder::Builder;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::AppData;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Builder, Serialize)]
|
||||||
|
pub struct BuildDetails {
|
||||||
|
pub version: &'static str,
|
||||||
|
pub git_commit_hash: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod routes {
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Stats {
|
||||||
|
pub percentile_benches: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stats {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
percentile_benches: "/api/v1/stats/benches/percentile",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get difficulty factor with max time limit for percentile of stats
|
||||||
|
#[actix_web_codegen_const_routes::post(
|
||||||
|
path = "crate::V1_API_ROUTES.stats.percentile_benches"
|
||||||
|
)]
|
||||||
|
async fn percentile_benches(
|
||||||
|
data: AppData,
|
||||||
|
payload: web::Json<PercentileReq>,
|
||||||
|
) -> ServiceResult<impl Responder> {
|
||||||
|
struct Count {
|
||||||
|
count: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = sqlx::query_as!(
|
||||||
|
Count,
|
||||||
|
"SELECT COUNT(difficulty) FROM survey_benches WHERE duration <= $1;",
|
||||||
|
payload.time as f32
|
||||||
|
)
|
||||||
|
.fetch_one(&data.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if count.count.is_none() {
|
||||||
|
return Ok(HttpResponse::Ok().json(PercentileResp {
|
||||||
|
difficulty_factor: None,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = count.count.unwrap();
|
||||||
|
|
||||||
|
if count < 2 {
|
||||||
|
return Ok(HttpResponse::Ok().json(PercentileResp {
|
||||||
|
difficulty_factor: None,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let location = ((count - 1) as f64 * (payload.percentile / 100.00)) + 1.00;
|
||||||
|
let fraction = location - location.floor();
|
||||||
|
|
||||||
|
async fn get_data_at_location(
|
||||||
|
data: &crate::Data,
|
||||||
|
time: u32,
|
||||||
|
location: i64,
|
||||||
|
) -> ServiceResult<Option<u32>> {
|
||||||
|
struct Difficulty {
|
||||||
|
difficulty: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
match sqlx::query_as!(
|
||||||
|
Difficulty,
|
||||||
|
"SELECT
|
||||||
|
difficulty
|
||||||
|
FROM
|
||||||
|
survey_benches
|
||||||
|
WHERE
|
||||||
|
duration <= $1
|
||||||
|
ORDER BY difficulty ASC LIMIT 1 OFFSET $2;",
|
||||||
|
time as f32,
|
||||||
|
location as i64 - 1,
|
||||||
|
)
|
||||||
|
.fetch_one(&data.db)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(res) => Ok(Some(res.difficulty.unwrap() as u32)),
|
||||||
|
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fraction > 0.00 {
|
||||||
|
if let (Some(base), Some(ceiling)) = (
|
||||||
|
get_data_at_location(&data, payload.time, location.floor() as i64).await?,
|
||||||
|
get_data_at_location(&data, payload.time, location.floor() as i64 + 1)
|
||||||
|
.await?,
|
||||||
|
) {
|
||||||
|
let res = base as u32 + ((ceiling - base) as f64 * fraction).floor() as u32;
|
||||||
|
|
||||||
|
return Ok(HttpResponse::Ok().json(PercentileResp {
|
||||||
|
difficulty_factor: Some(res),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(base) =
|
||||||
|
get_data_at_location(&data, payload.time, location.floor() as i64).await?
|
||||||
|
{
|
||||||
|
let res = base as u32;
|
||||||
|
|
||||||
|
return Ok(HttpResponse::Ok().json(PercentileResp {
|
||||||
|
difficulty_factor: Some(res),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(HttpResponse::Ok().json(PercentileResp {
|
||||||
|
difficulty_factor: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Builder, Serialize)]
|
||||||
|
/// Health check return datatype
|
||||||
|
pub struct PercentileReq {
|
||||||
|
time: u32,
|
||||||
|
percentile: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Builder, Serialize)]
|
||||||
|
/// Health check return datatype
|
||||||
|
pub struct PercentileResp {
|
||||||
|
difficulty_factor: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||||
|
cfg.service(percentile_benches);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use actix_web::{http::StatusCode, test, App};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::api::v1::services;
|
||||||
|
use crate::tests::get_test_data;
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn stats_bench_work() {
|
||||||
|
use crate::tests::*;
|
||||||
|
|
||||||
|
const NAME: &str = "benchstatsuesr";
|
||||||
|
const EMAIL: &str = "benchstatsuesr@testadminuser.com";
|
||||||
|
const PASSWORD: &str = "longpassword2";
|
||||||
|
|
||||||
|
const DEVICE_USER_PROVIDED: &str = "foo";
|
||||||
|
const DEVICE_SOFTWARE_RECOGNISED: &str = "Foobar.v2";
|
||||||
|
const THREADS: i32 = 4;
|
||||||
|
|
||||||
|
let data = get_test_data().await;
|
||||||
|
{
|
||||||
|
delete_user(NAME, &data).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (creds, signin_resp) =
|
||||||
|
register_and_signin(&data, NAME, EMAIL, PASSWORD).await;
|
||||||
|
let cookies = get_cookie!(signin_resp);
|
||||||
|
let app = get_app!(data).await;
|
||||||
|
|
||||||
|
let survey = get_survey_user(data.clone()).await;
|
||||||
|
let survey_cookie = get_cookie!(survey);
|
||||||
|
let campaign = create_new_campaign(NAME, data.clone(), cookies.clone()).await;
|
||||||
|
let campaign_config =
|
||||||
|
get_campaign_config(&campaign, data.clone(), survey_cookie.clone()).await;
|
||||||
|
|
||||||
|
assert_eq!(DIFFICULTIES.to_vec(), campaign_config.difficulties);
|
||||||
|
|
||||||
|
let submit_payload = crate::api::v1::bench::Submission {
|
||||||
|
device_user_provided: DEVICE_USER_PROVIDED.into(),
|
||||||
|
device_software_recognised: DEVICE_SOFTWARE_RECOGNISED.into(),
|
||||||
|
threads: THREADS,
|
||||||
|
benches: BENCHES.clone(),
|
||||||
|
submission_type: crate::api::v1::bench::SubmissionType::Wasm,
|
||||||
|
};
|
||||||
|
|
||||||
|
submit_bench(&submit_payload, &campaign, survey_cookie, data.clone()).await;
|
||||||
|
|
||||||
|
let msg = PercentileReq {
|
||||||
|
time: 1,
|
||||||
|
percentile: 99.00,
|
||||||
|
};
|
||||||
|
let resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&msg, V1_API_ROUTES.stats.percentile_benches).to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
let resp: PercentileResp = test::read_body_json(resp).await;
|
||||||
|
|
||||||
|
assert!(resp.difficulty_factor.is_none());
|
||||||
|
|
||||||
|
let msg = PercentileReq {
|
||||||
|
time: 1,
|
||||||
|
percentile: 100.00,
|
||||||
|
};
|
||||||
|
|
||||||
|
let resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&msg, V1_API_ROUTES.stats.percentile_benches).to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
let resp: PercentileResp = test::read_body_json(resp).await;
|
||||||
|
|
||||||
|
assert!(resp.difficulty_factor.is_none());
|
||||||
|
|
||||||
|
let msg = PercentileReq {
|
||||||
|
time: 2,
|
||||||
|
percentile: 100.00,
|
||||||
|
};
|
||||||
|
|
||||||
|
let resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&msg, V1_API_ROUTES.stats.percentile_benches).to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
let resp: PercentileResp = test::read_body_json(resp).await;
|
||||||
|
|
||||||
|
assert_eq!(resp.difficulty_factor.unwrap(), 2);
|
||||||
|
|
||||||
|
let msg = PercentileReq {
|
||||||
|
time: 5,
|
||||||
|
percentile: 90.00,
|
||||||
|
};
|
||||||
|
|
||||||
|
let resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&msg, V1_API_ROUTES.stats.percentile_benches).to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
let resp: PercentileResp = test::read_body_json(resp).await;
|
||||||
|
|
||||||
|
assert_eq!(resp.difficulty_factor.unwrap(), 4);
|
||||||
|
delete_user(NAME, &data).await;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use std::future::Future;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -23,6 +11,7 @@ use sqlx::types::Uuid;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
use tokio::sync::oneshot::{self, error::TryRecvError, Sender};
|
use tokio::sync::oneshot::{self, error::TryRecvError, Sender};
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
use crate::api::v1::admin::campaigns::runners::get_results;
|
use crate::api::v1::admin::campaigns::runners::get_results;
|
||||||
use crate::api::v1::admin::campaigns::SurveyResponse;
|
use crate::api::v1::admin::campaigns::SurveyResponse;
|
||||||
|
@ -213,30 +202,30 @@ impl Archiver {
|
||||||
pub async fn init_archive_job(
|
pub async fn init_archive_job(
|
||||||
self,
|
self,
|
||||||
data: AppData,
|
data: AppData,
|
||||||
) -> ServiceResult<(Sender<bool>, impl Future)> {
|
) -> ServiceResult<(Sender<bool>, JoinHandle<()>)> {
|
||||||
let (tx, mut rx) = oneshot::channel();
|
let (tx, mut rx) = oneshot::channel();
|
||||||
|
|
||||||
|
fn can_run(rx: &mut oneshot::Receiver<bool>) -> bool {
|
||||||
|
match rx.try_recv() {
|
||||||
|
Err(TryRecvError::Empty) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let job = async move {
|
let job = async move {
|
||||||
loop {
|
loop {
|
||||||
// let rx = self.rx.as_mut().unwrap();
|
if !can_run(&mut rx) {
|
||||||
match rx.try_recv() {
|
log::info!("Killing archive loop: received signal");
|
||||||
// The channel is currently empty
|
break;
|
||||||
Ok(_) => {
|
}
|
||||||
|
|
||||||
|
for _ in 0..data.settings.publish.duration {
|
||||||
|
if !can_run(&mut rx) {
|
||||||
log::info!("Killing archive loop: received signal");
|
log::info!("Killing archive loop: received signal");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(TryRecvError::Empty) => {
|
tokio::time::sleep(std::time::Duration::new(1, 0)).await;
|
||||||
let _ = self.archive(&data).await;
|
|
||||||
|
|
||||||
tokio::time::sleep(std::time::Duration::new(
|
|
||||||
data.settings.publish.duration,
|
|
||||||
0,
|
|
||||||
))
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
Err(TryRecvError::Closed) => break,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = self.archive(&data).await;
|
let _ = self.archive(&data).await;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -349,8 +338,8 @@ mod tests {
|
||||||
const DEVICE_SOFTWARE_RECOGNISED: &str = "Foobar.v2";
|
const DEVICE_SOFTWARE_RECOGNISED: &str = "Foobar.v2";
|
||||||
const THREADS: i32 = 4;
|
const THREADS: i32 = 4;
|
||||||
|
|
||||||
|
let data = get_test_data().await;
|
||||||
{
|
{
|
||||||
let data = get_test_data().await;
|
|
||||||
delete_user(NAME, &data).await;
|
delete_user(NAME, &data).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,8 +348,8 @@ mod tests {
|
||||||
//self.write_campaign_file(&campaign, &archive).await?;
|
//self.write_campaign_file(&campaign, &archive).await?;
|
||||||
//self.write_benchmark_file(&campaign, &archive, data).await?;
|
//self.write_benchmark_file(&campaign, &archive, data).await?;
|
||||||
|
|
||||||
let (data, _creds, signin_resp) =
|
let (creds, signin_resp) =
|
||||||
register_and_signin(NAME, EMAIL, PASSWORD).await;
|
register_and_signin(&data, NAME, EMAIL, PASSWORD).await;
|
||||||
let cookies = get_cookie!(signin_resp);
|
let cookies = get_cookie!(signin_resp);
|
||||||
let survey = get_survey_user(data.clone()).await;
|
let survey = get_survey_user(data.clone()).await;
|
||||||
let survey_cookie = get_cookie!(survey);
|
let survey_cookie = get_cookie!(survey);
|
||||||
|
|
3
src/cache_buster_data.json.license
Normal file
3
src/cache_buster_data.json.license
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
30
src/data.rs
30
src/data.rs
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
//! App data: database connections, etc.
|
//! App data: database connections, etc.
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -22,6 +11,7 @@ use argon2_creds::{Config, ConfigBuilder, PasswordPolicy};
|
||||||
use sqlx::postgres::PgPoolOptions;
|
use sqlx::postgres::PgPoolOptions;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
use crate::mcaptcha::*;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
|
|
||||||
/// App data
|
/// App data
|
||||||
|
@ -30,6 +20,8 @@ pub struct Data {
|
||||||
pub db: PgPool,
|
pub db: PgPool,
|
||||||
pub creds: Config,
|
pub creds: Config,
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
|
|
||||||
|
pub mcaptcha: Box<dyn MCaptchaClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
|
@ -45,7 +37,10 @@ impl Data {
|
||||||
|
|
||||||
#[cfg(not(tarpaulin_include))]
|
#[cfg(not(tarpaulin_include))]
|
||||||
/// create new instance of app data
|
/// create new instance of app data
|
||||||
pub async fn new(settings: Settings) -> Arc<Self> {
|
pub async fn new(
|
||||||
|
settings: Settings,
|
||||||
|
mcaptcha: Box<dyn MCaptchaClient>,
|
||||||
|
) -> Arc<Self> {
|
||||||
let creds = Self::get_creds();
|
let creds = Self::get_creds();
|
||||||
let c = creds.clone();
|
let c = creds.clone();
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
|
@ -67,6 +62,7 @@ impl Data {
|
||||||
db,
|
db,
|
||||||
creds,
|
creds,
|
||||||
settings,
|
settings,
|
||||||
|
mcaptcha,
|
||||||
};
|
};
|
||||||
|
|
||||||
Arc::new(data)
|
Arc::new(data)
|
||||||
|
|
91
src/db.rs
Normal file
91
src/db.rs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct JobState {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JobState {
|
||||||
|
pub fn new(name: String) -> Self {
|
||||||
|
Self { name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref JOB_STATE_CREATE: JobState = JobState::new("job.state.create".into());
|
||||||
|
pub static ref JOB_STATE_FINISH: JobState = JobState::new("job.state.finish".into());
|
||||||
|
pub static ref JOB_STATE_RUNNING: JobState =
|
||||||
|
JobState::new("job.state.running".into());
|
||||||
|
pub static ref JOB_STATES: [&'static JobState; 3] =
|
||||||
|
[&*JOB_STATE_CREATE, &*JOB_STATE_FINISH, &*JOB_STATE_RUNNING];
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn job_state_exists(
|
||||||
|
db: &PgPool,
|
||||||
|
job_state: &JobState,
|
||||||
|
) -> sqlx::error::Result<bool> {
|
||||||
|
let res = sqlx::query!(
|
||||||
|
"SELECT EXISTS (SELECT 1 from survey_mcaptcha_upload_job_states WHERE name = $1)",
|
||||||
|
job_state.name,
|
||||||
|
)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut resp = false;
|
||||||
|
if let Some(x) = res.exists {
|
||||||
|
resp = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_job_states(db: &PgPool) -> sqlx::error::Result<()> {
|
||||||
|
for j in &*JOB_STATES {
|
||||||
|
if !job_state_exists(db, j).await? {
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO survey_mcaptcha_upload_job_states
|
||||||
|
(name) VALUES ($1) ON CONFLICT (name) DO NOTHING;",
|
||||||
|
j.name
|
||||||
|
)
|
||||||
|
.execute(db)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn migrate_db(db: &PgPool) -> sqlx::error::Result<()> {
|
||||||
|
sqlx::migrate!("./migrations/").run(db).await?;
|
||||||
|
create_job_states(db).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_mcaptcha_job_states_exist() {
|
||||||
|
// can't use crate::tests::get_test_data because this module is used by
|
||||||
|
// ./src/tests-migrate.rs too, which doesn't load tests module
|
||||||
|
let settings = crate::settings::Settings::new().unwrap();
|
||||||
|
let db = sqlx::postgres::PgPoolOptions::new()
|
||||||
|
.max_connections(2)
|
||||||
|
.connect(&settings.database.url)
|
||||||
|
.await
|
||||||
|
.expect("Unable to form database pool");
|
||||||
|
|
||||||
|
migrate_db(&db).await.unwrap();
|
||||||
|
|
||||||
|
for e in (*JOB_STATES).iter() {
|
||||||
|
println!("checking job state {}", e.name);
|
||||||
|
assert!(job_state_exists(&db, e).await.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
|
|
||||||
use argon2_creds::errors::CredsError;
|
use argon2_creds::errors::CredsError;
|
||||||
|
|
39
src/main.rs
39
src/main.rs
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -30,7 +19,9 @@ use log::info;
|
||||||
mod api;
|
mod api;
|
||||||
mod archive;
|
mod archive;
|
||||||
mod data;
|
mod data;
|
||||||
|
mod db;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
mod mcaptcha;
|
||||||
mod pages;
|
mod pages;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod static_assets;
|
mod static_assets;
|
||||||
|
@ -86,14 +77,21 @@ async fn main() -> std::io::Result<()> {
|
||||||
);
|
);
|
||||||
|
|
||||||
let settings = Settings::new().unwrap();
|
let settings = Settings::new().unwrap();
|
||||||
let data = Data::new(settings.clone()).await;
|
let mcaptcha: Box<dyn mcaptcha::MCaptchaClient> =
|
||||||
sqlx::migrate!("./migrations/").run(&data.db).await.unwrap();
|
Box::new(mcaptcha::MCaptchaClientReqwest::default());
|
||||||
|
|
||||||
|
let data = Data::new(settings.clone(), mcaptcha).await;
|
||||||
|
db::migrate_db(&data.db).await.unwrap();
|
||||||
let data = actix_web::web::Data::new(data);
|
let data = actix_web::web::Data::new(data);
|
||||||
|
|
||||||
let arch = archive::Archiver::new(&data.settings);
|
let arch = archive::Archiver::new(&data.settings);
|
||||||
let (archive_kiler, archive_job) =
|
let (archive_kiler, archive_job) =
|
||||||
arch.init_archive_job(data.clone()).await.unwrap();
|
arch.init_archive_job(data.clone()).await.unwrap();
|
||||||
|
|
||||||
|
let mcaptcha_downloader = mcaptcha::MCaptchaDownloader::new(data.clone());
|
||||||
|
let (mcaptcha_downloader_killer, mcaptcha_downloader_job) =
|
||||||
|
mcaptcha_downloader.start_job().await.unwrap();
|
||||||
|
|
||||||
let ip = settings.server.get_ip();
|
let ip = settings.server.get_ip();
|
||||||
println!("Starting server on: http://{}", ip);
|
println!("Starting server on: http://{}", ip);
|
||||||
|
|
||||||
|
@ -123,8 +121,9 @@ async fn main() -> std::io::Result<()> {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
archive_kiler.send(true).unwrap();
|
let _ = mcaptcha_downloader_killer.send(());
|
||||||
archive_job.await;
|
let _ = archive_kiler.send(true);
|
||||||
|
let _ = tokio::join!(archive_job, mcaptcha_downloader_job);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
303
src/mcaptcha.rs
Normal file
303
src/mcaptcha.rs
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
// Copyright (C) 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::sync::oneshot;
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
use tokio::time::sleep;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::{api::v1::mcaptcha::db::SchedulerJob, errors::*, AppData};
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
* 1. Define traits to interact with mCaptcha
|
||||||
|
* 2. Implement trait with request 3. Implement mocking for testing
|
||||||
|
* 4. Load to crate::data::Data
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
|
||||||
|
/// Proof-of-Work CAPTCHA performance analytics
|
||||||
|
pub struct PerformanceAnalytics {
|
||||||
|
/// log ID
|
||||||
|
pub id: usize,
|
||||||
|
/// time taken to generate proof
|
||||||
|
pub time: u32,
|
||||||
|
/// difficulty factor for which the proof was generated
|
||||||
|
pub difficulty_factor: u32,
|
||||||
|
/// worker/client type: wasm, javascript, python, etc.
|
||||||
|
pub worker_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait MCaptchaClient:
|
||||||
|
std::marker::Send + std::marker::Sync + CloneMCaptchaClient
|
||||||
|
{
|
||||||
|
async fn share_secret(
|
||||||
|
&self,
|
||||||
|
mut mcaptcha: Url,
|
||||||
|
secret: String,
|
||||||
|
auth_token: String,
|
||||||
|
) -> ServiceResult<()>;
|
||||||
|
async fn download_benchmarks(
|
||||||
|
&self,
|
||||||
|
mut mcaptcha: Url,
|
||||||
|
campaign_id: &str,
|
||||||
|
page: usize,
|
||||||
|
) -> ServiceResult<Vec<PerformanceAnalytics>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait to clone MCaptchaClient
|
||||||
|
pub trait CloneMCaptchaClient {
|
||||||
|
/// clone client
|
||||||
|
fn clone_client(&self) -> Box<dyn MCaptchaClient>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> CloneMCaptchaClient for T
|
||||||
|
where
|
||||||
|
T: MCaptchaClient + Clone + 'static,
|
||||||
|
{
|
||||||
|
fn clone_client(&self) -> Box<dyn MCaptchaClient> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Box<dyn MCaptchaClient> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
(**self).clone_client()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MCaptchaClientReqwest {
|
||||||
|
client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MCaptchaClientReqwest {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
client: Client::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl MCaptchaClient for MCaptchaClientReqwest {
|
||||||
|
async fn share_secret(
|
||||||
|
&self,
|
||||||
|
mut mcaptcha: Url,
|
||||||
|
secret: String,
|
||||||
|
auth_token: String,
|
||||||
|
) -> ServiceResult<()> {
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct S {
|
||||||
|
secret: String,
|
||||||
|
auth_token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = S { secret, auth_token };
|
||||||
|
mcaptcha.set_path("/api/v1/survey/secret");
|
||||||
|
self.client.post(mcaptcha).json(&msg).send().await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn download_benchmarks(
|
||||||
|
&self,
|
||||||
|
mut mcaptcha: Url,
|
||||||
|
campaign_id: &str,
|
||||||
|
page: usize,
|
||||||
|
) -> ServiceResult<Vec<PerformanceAnalytics>> {
|
||||||
|
mcaptcha.set_path(&format!("/api/v1/survey/takeout/{campaign_id}/get"));
|
||||||
|
mcaptcha.set_query(Some(&format!("page={page}")));
|
||||||
|
let res = self
|
||||||
|
.client
|
||||||
|
.get(mcaptcha)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MCaptchaDownloader {
|
||||||
|
data: AppData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MCaptchaDownloader {
|
||||||
|
pub fn new(data: AppData) -> Self {
|
||||||
|
Self { data }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_run(rx: &mut oneshot::Receiver<()>) -> bool {
|
||||||
|
matches!(rx.try_recv(), Err(oneshot::error::TryRecvError::Empty))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start_job(
|
||||||
|
&self,
|
||||||
|
) -> ServiceResult<(oneshot::Sender<()>, JoinHandle<()>)> {
|
||||||
|
let (tx, mut rx) = oneshot::channel();
|
||||||
|
let this = self.clone();
|
||||||
|
let fut = async move {
|
||||||
|
loop {
|
||||||
|
if !Self::can_run(&mut rx) {
|
||||||
|
log::info!("stopping survey uploads");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let task = this.data.get_next_job_to_run().await.unwrap();
|
||||||
|
if task.is_none() {
|
||||||
|
for _ in 0..5 {
|
||||||
|
if !Self::can_run(&mut rx) {
|
||||||
|
log::info!("Stopping survey uploads");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sleep(Duration::new(1, 0)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let task = task.unwrap();
|
||||||
|
this.data.mark_job_scheduled(&task).await.unwrap();
|
||||||
|
this.exec_job(&task, &mut rx).await.unwrap();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let handle = tokio::spawn(fut);
|
||||||
|
Ok((tx, handle))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn exec_job(
|
||||||
|
&self,
|
||||||
|
job: &SchedulerJob,
|
||||||
|
rx: &mut oneshot::Receiver<()>,
|
||||||
|
) -> ServiceResult<()> {
|
||||||
|
let checkpoint = self.data.mcaptcha_get_checkpoint(&job.campaign_id).await?;
|
||||||
|
const LIMIT: usize = 50;
|
||||||
|
let mut page = 1 + (checkpoint / LIMIT);
|
||||||
|
let campaign_str = job.campaign_id.to_string();
|
||||||
|
log::info!("getting page {page} from {campaign_str}");
|
||||||
|
loop {
|
||||||
|
if !Self::can_run(rx) {
|
||||||
|
log::info!("Stopping survey downloads");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = self
|
||||||
|
.data
|
||||||
|
.mcaptcha
|
||||||
|
.download_benchmarks(job.url.clone(), &campaign_str, page)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !Self::can_run(rx) {
|
||||||
|
log::info!("Stopping survey downloads");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let skip = checkpoint - ((page - 1) * LIMIT);
|
||||||
|
let new_records = res.len() - skip as usize;
|
||||||
|
let mut skip = skip as isize;
|
||||||
|
for r in res.drain(0..) {
|
||||||
|
if skip > 0 {
|
||||||
|
skip -= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.data
|
||||||
|
.mcaptcha_insert_analytics(&job.campaign_id, &r)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
self.data
|
||||||
|
.mcaptcha_set_checkpoint(&job.campaign_id, new_records)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !Self::can_run(rx) {
|
||||||
|
log::info!("Stopping survey downloads");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
page += 1;
|
||||||
|
if res.len() < LIMIT {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.data.mark_job_finished(job).await.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref BENCHMARK: Vec<PerformanceAnalytics> = vec![
|
||||||
|
PerformanceAnalytics {
|
||||||
|
id: 1,
|
||||||
|
time: 2,
|
||||||
|
difficulty_factor: 3,
|
||||||
|
worker_type: "foo".to_string(),
|
||||||
|
},
|
||||||
|
PerformanceAnalytics {
|
||||||
|
id: 4,
|
||||||
|
time: 5,
|
||||||
|
difficulty_factor: 6,
|
||||||
|
worker_type: "bar".to_string(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TestClient {
|
||||||
|
pub client: Arc<RwLock<HashMap<String, String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TestClient {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
client: Arc::new(RwLock::new(HashMap::default())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl MCaptchaClient for TestClient {
|
||||||
|
async fn share_secret(
|
||||||
|
&self,
|
||||||
|
mut mcaptcha: Url,
|
||||||
|
secret: String,
|
||||||
|
auth_token: String,
|
||||||
|
) -> ServiceResult<()> {
|
||||||
|
mcaptcha.set_path("/api/v1/survey/secret");
|
||||||
|
let mut x = self.client.write().unwrap();
|
||||||
|
x.insert(mcaptcha.to_string(), secret);
|
||||||
|
drop(x);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn download_benchmarks(
|
||||||
|
&self,
|
||||||
|
mcaptcha: Url,
|
||||||
|
campaign_id: &str,
|
||||||
|
page: usize,
|
||||||
|
) -> ServiceResult<Vec<PerformanceAnalytics>> {
|
||||||
|
println!(
|
||||||
|
"mcaptcha_url {}, campaign_id {}, page: {page}",
|
||||||
|
mcaptcha, campaign_id
|
||||||
|
);
|
||||||
|
let res = BENCHMARK.clone();
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use actix_web::http::header::ContentType;
|
use actix_web::http::header::ContentType;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use tera::Context;
|
use tera::Context;
|
||||||
|
@ -109,7 +98,8 @@ mod tests {
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn auth_join_form_works() {
|
async fn auth_join_form_works() {
|
||||||
let settings = Settings::new().unwrap();
|
let settings = Settings::new().unwrap();
|
||||||
let data = Data::new(settings).await;
|
// let data = Data::new(settings).await;
|
||||||
|
let data = get_test_data().await;
|
||||||
const NAME: &str = "testuserformjoin";
|
const NAME: &str = "testuserformjoin";
|
||||||
const NAME2: &str = "testuserformjoin2";
|
const NAME2: &str = "testuserformjoin2";
|
||||||
const EMAIL: &str = "testuserformjoin@a.com";
|
const EMAIL: &str = "testuserformjoin@a.com";
|
||||||
|
|
|
@ -1,19 +1,8 @@
|
||||||
/*
|
// Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
*
|
//
|
||||||
* This program is free software: you can redistribute it and/or modify
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue