Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Te server refactor 1 #1864

Draft
wants to merge 5 commits into
base: edge
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ build/
prod.env
xids.csv
preprod.env
.venv/
1 change: 1 addition & 0 deletions server/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ dist
logs/
node_modules/
npm-debug.log*
.venv/
36 changes: 36 additions & 0 deletions server/src/routes/launchPrep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Utils from "../utils/common";
import cookies from "../utils/cookies";
import Session from "../session";
const COOKIES = cookies.COOKIES;

const setPermanentCookie = cookies.setPermanentCookie;
const setCookieTestCookie = cookies.setCookieTestCookie;
const makeSessionToken = Session.makeSessionToken;

function handle_GET_launchPrep(
req: {
headers?: { origin: string };
cookies: { [x: string]: any };
p: { dest: any };
},
res: { redirect: (arg0: any) => void }
) {
if (!req.cookies[COOKIES.PERMANENT_COOKIE]) {
setPermanentCookie(req, res, makeSessionToken());
}
setCookieTestCookie(req, res);

// Argument of type '{ redirect: (arg0: any) => void; }' is not assignable to parameter of type '{ cookie: (arg0: any, arg1: any, arg2: any) => void; }'.
// Property 'cookie' is missing in type '{ redirect: (arg0: any) => void; }' but required in type '{ cookie: (arg0: any, arg1: any, arg2: any) => void; }'.ts(2345)
// @ts-ignore
setCookie(req, res, "top", "ok", {
httpOnly: false, // not httpOnly - needed by JS
});

// using hex since it doesn't require escaping like base64.
const dest = Utils.hexToStr(req.p.dest);
const url = new URL(dest);
res.redirect(url.pathname + url.search + url.hash);
}

export default handle_GET_launchPrep;
259 changes: 259 additions & 0 deletions server/src/routes/math.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
import { getPca, PcaCacheItem } from "../utils/pca";
import fail from "../utils/fail";
import { queryP as pgQueryP } from "../db/pg-query";
import Utils from "../utils/common";
import { getZidForRid } from "../utils/zinvite";

import Config from "../config";

function handle_GET_math_pca(
req: any,
res: {
status: (
arg0: number
) => { (): any; new (): any; end: { (): void; new (): any } };
}
) {
// migrated off this path, old clients were causing timeout issues by polling repeatedly without waiting for a result for a previous poll.
res.status(304).end();
}

// Cache the knowledge of whether there are any pca results for a given zid.
// Needed to determine whether to return a 404 or a 304.
// zid -> boolean
const pcaResultsExistForZid = {};

function handle_GET_math_pca2(
req: { p: { zid: any; math_tick: any; ifNoneMatch: any } },
res: {
status: (
arg0: number
) => { (): any; new (): any; end: { (): void; new (): any } };
set: (arg0: {
"Content-Type": string;
"Content-Encoding": string;
Etag: string;
}) => void;
send: (arg0: any) => void;
}
) {
let zid = req.p.zid;
let math_tick = req.p.math_tick;

let ifNoneMatch = req.p.ifNoneMatch;
if (ifNoneMatch) {
if (math_tick !== undefined) {
return fail(
res,
400,
"Expected either math_tick param or If-Not-Match header, but not both."
);
}
if (ifNoneMatch.includes("*")) {
math_tick = 0;
} else {
let entries = ifNoneMatch.split(/ *, */).map((x: string) => {
return Number(
x
.replace(/^[wW]\//, "")
.replace(/^"/, "")
.replace(/"$/, "")
);
});
math_tick = Math.min(...entries);
}
} else if (math_tick === undefined) {
math_tick = -1;
}
function finishWith304or404() {
// Element implicitly has an 'any' type
// because expression of type 'any' can't be used to index type '{ } '.ts(7053)
// @ts-ignore
if (pcaResultsExistForZid[zid]) {
res.status(304).end();
} else {
// Technically, this should probably be a 404, but
// the red errors make it hard to see other errors
// in Chrome Developer Tools.
res.status(304).end();
// res.status(404).end();
}
}

getPca(zid, math_tick)
.then(function (data: PcaCacheItem | undefined) {
if (data) {
// The buffer is gzipped beforehand to cut down on server effort in re-gzipping the same json string for each response.
// We can't cache this endpoint on Cloudflare because the response changes too freqently, so it seems like the best way
// is to cache the gzipped json'd buffer here on the server.
res.set({
"Content-Type": "application/json",
"Content-Encoding": "gzip",
Etag: '"' + data.asPOJO.math_tick + '"',
});
res.send(data.asBufferOfGzippedJson);
} else {
// check whether we should return a 304 or a 404
// Element implicitly has an 'any' type
// because expression of type 'any' can't be used to index type '{ } '.ts(7053)
// @ts-ignore
if (pcaResultsExistForZid[zid] === undefined) {
// This server doesn't know yet if there are any PCA results in the DB
// So try querying from -1
return getPca(zid, -1).then(function (data: any) {
let exists = !!data;
// Element implicitly has an 'any' type
// because expression of type 'any' can't be used to index type '{ } '.ts(7053)
// @ts-ignore
pcaResultsExistForZid[zid] = exists;
finishWith304or404();
});
} else {
finishWith304or404();
}
}
})
.catch(function (err: any) {
fail(res, 500, err);
});
}

function handle_POST_math_update(
req: { p: { zid: any; uid?: any; math_update_type: any } },
res: {
status: (
arg0: number
) => { (): any; new (): any; json: { (arg0: {}): void; new (): any } };
}
) {
let zid = req.p.zid;
let uid = req.p.uid;
let math_env = Config.mathEnv;
let math_update_type = req.p.math_update_type;

Utils.isModerator(zid, uid).then((hasPermission: any) => {
if (!hasPermission) {
return fail(res, 500, "handle_POST_math_update_permission");
}
return pgQueryP(
"insert into worker_tasks (task_type, task_data, task_bucket, math_env) values ('update_math', $1, $2, $3);",
[
JSON.stringify({
zid: zid,
math_update_type: math_update_type,
}),
zid,
math_env,
]
)
.then(() => {
res.status(200).json({});
})
.catch((err: any) => {
return fail(res, 500, "polis_err_POST_math_update", err);
});
});
}

function handle_GET_math_correlationMatrix(
req: { p: { rid: any; math_tick: any } },
res: {
status: (
arg0: number
) => {
(): any;
new (): any;
json: { (arg0: { status: string }): void; new (): any };
};
json: (arg0: any) => void;
}
) {
let rid = req.p.rid;
let math_env = Config.mathEnv;
let math_tick = req.p.math_tick;

function finishAsPending() {
res.status(202).json({
status: "pending",
});
}

function hasCommentSelections() {
return pgQueryP(
"select * from report_comment_selections where rid = ($1) and selection = 1;",
[rid]
// Argument of type '(rows: string | any[]) => boolean' is not assignable to parameter of type '(value: unknown) => boolean | PromiseLike<boolean>'.
// Types of parameters 'rows' and 'value' are incompatible.
// Type 'unknown' is not assignable to type 'string | any[]'.
// Type 'unknown' is not assignable to type 'any[]'.ts(2345)
// @ts-ignore
).then((rows: string | any[]) => {
return rows.length > 0;
});
}

let requestExistsPromise = pgQueryP(
"select * from worker_tasks where task_type = 'generate_report_data' and math_env=($2) " +
"and task_bucket = ($1) " +
// "and attempts < 3 " +
"and (task_data->>'math_tick')::int >= ($3) " +
"and finished_time is NULL;",
[rid, math_env, math_tick]
);

let resultExistsPromise = pgQueryP(
"select * from math_report_correlationmatrix where rid = ($1) and math_env = ($2) and math_tick >= ($3);",
[rid, math_env, math_tick]
);

Promise.all([resultExistsPromise, getZidForRid(rid)])
.then((a: any[]) => {
let rows = a[0];
let zid = a[1];
if (!rows || !rows.length) {
// Argument of type '(requests_rows: string | any[]) => globalThis.Promise<void> | undefined' is not assignable to parameter of type '(value: unknown) => void | PromiseLike<void | undefined> | undefined'.
// Types of parameters 'requests_rows' and 'value' are incompatible.
// Type 'unknown' is not assignable to type 'string | any[]'.
// Type 'unknown' is not assignable to type 'any[]'.ts(2345)
// @ts-ignore
return requestExistsPromise.then((requests_rows: string | any[]) => {
const shouldAddTask = !requests_rows || !requests_rows.length;
// const shouldAddTask = true;

if (shouldAddTask) {
return hasCommentSelections().then((hasSelections: any) => {
if (!hasSelections) {
return res.status(202).json({
status: "polis_report_needs_comment_selection",
});
}
return pgQueryP(
"insert into worker_tasks (task_type, task_data, task_bucket, math_env) values ('generate_report_data', $1, $2, $3);",
[
JSON.stringify({
rid: rid,
zid: zid,
math_tick: math_tick,
}),
rid,
math_env,
]
).then(finishAsPending);
});
}
finishAsPending();
});
}
res.json(rows[0].data);
})
.catch((err: any) => {
return fail(res, 500, "polis_err_GET_math_correlationMatrix", err);
});
}

export {
handle_GET_math_pca,
handle_GET_math_pca2,
handle_POST_math_update,
handle_GET_math_correlationMatrix,
};
80 changes: 80 additions & 0 deletions server/src/routes/metadataAnswers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { query as pgQuery } from "../db/pg-query";
import Utils from "../utils/common";
import fail from "../utils/fail";

function getZidForAnswer(
pmaid: any,
callback: {
(err: any, zid: any): void;
(arg0: string | null, arg1?: undefined): void;
}
) {
pgQuery(
"SELECT zid FROM participant_metadata_answers WHERE pmaid = ($1);",
[pmaid],
function (err: any, result: { rows: string | any[] }) {
if (err) {
callback(err);
return;
}
if (!result.rows || !result.rows.length) {
callback("polis_err_zid_missing_for_answer");
return;
}
callback(null, result.rows[0].zid);
}
);
}

function deleteMetadataAnswer(
pmaid: any,
callback: { (err: any): void; (arg0: null): void }
) {
pgQuery(
"update participant_metadata_answers set alive = FALSE where pmaid = ($1);",
[pmaid],
function (err: any) {
if (err) {
callback(err);
return;
}
callback(null);
}
);
}

function handle_DELETE_metadata_answers(
req: { p: { uid?: any; pmaid: any } },
res: { send: (arg0: number) => void }
) {
let uid = req.p.uid;
let pmaid = req.p.pmaid;

getZidForAnswer(pmaid, function (err: any, zid: any) {
if (err) {
fail(res, 500, "polis_err_delete_participant_metadata_answers_zid", err);
return;
}
Utils.isConversationOwner(zid, uid, function (err: any) {
if (err) {
fail(
res,
403,
"polis_err_delete_participant_metadata_answers_auth",
err
);
return;
}

deleteMetadataAnswer(pmaid, function (err: any) {
if (err) {
fail(res, 500, "polis_err_delete_participant_metadata_answers", err);
return;
}
res.send(200);
});
});
});
}

export default handle_DELETE_metadata_answers;
Loading
Loading