fix: typing/dependecy/validation fixes, installed ts-reset & zod, fixed responses never being sent on invalid query params
This commit is contained in:
parent
ece6deface
commit
cc4bef8ec4
4 changed files with 211 additions and 203 deletions
|
|
@ -1,62 +1,114 @@
|
|||
|
||||
import { promises as fs } from "fs";
|
||||
import fetch from 'node-fetch';
|
||||
import * as path from "path";
|
||||
import { authenticate } from "@google-cloud/local-auth";
|
||||
import { google } from "googleapis";
|
||||
import { OAuth2Client } from 'google-auth-library';
|
||||
|
||||
import { JWTInput, OAuth2Client } from "google-auth-library";
|
||||
import {
|
||||
GoogleAuth,
|
||||
JSONClient,
|
||||
} from "google-auth-library/build/src/auth/googleauth.js";
|
||||
import z from "zod";
|
||||
|
||||
//setup google auth
|
||||
const SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"];
|
||||
const TOKEN_PATH = path.join(process.cwd(), "res/token.secret.json");
|
||||
const CREDENTIALS_PATH = path.join(process.cwd(), "res/credentials.secret.json");
|
||||
|
||||
const CREDENTIALS_PATH = path.join(
|
||||
process.cwd(),
|
||||
"res/credentials.secret.json",
|
||||
);
|
||||
|
||||
/**
|
||||
* Load or request or authorization to call APIs.
|
||||
*/
|
||||
*/
|
||||
export async function authorize(): Promise<
|
||||
GoogleAuth<JSONClient> | OAuth2Client
|
||||
> {
|
||||
const savedClient = await loadSavedCredentialsIfExist();
|
||||
if (savedClient) {
|
||||
return savedClient;
|
||||
}
|
||||
|
||||
export async function authorize() {
|
||||
let client: any = await loadSavedCredentialsIfExist();
|
||||
if (client) {
|
||||
return client;
|
||||
}
|
||||
client = await authenticate({
|
||||
scopes: SCOPES,
|
||||
keyfilePath: CREDENTIALS_PATH,
|
||||
});
|
||||
if (client.credentials) {
|
||||
await saveCredentials(client);
|
||||
}
|
||||
return client;
|
||||
const oauthClient = await authenticate({
|
||||
scopes: SCOPES,
|
||||
keyfilePath: CREDENTIALS_PATH,
|
||||
});
|
||||
|
||||
if (oauthClient.credentials) {
|
||||
await saveCredentials(oauthClient);
|
||||
}
|
||||
|
||||
return oauthClient;
|
||||
}
|
||||
|
||||
async function loadSavedCredentialsIfExist() {
|
||||
try {
|
||||
const content = await fs.readFile(TOKEN_PATH);
|
||||
const credentials = JSON.parse(content.toString());
|
||||
return google.auth.fromJSON(credentials);
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
const jwtInputZod = z.object({
|
||||
type: z.string(),
|
||||
client_id: z.string(),
|
||||
client_secret: z.string(),
|
||||
refresh_token: z.string(),
|
||||
}) satisfies z.ZodType<JWTInput>;
|
||||
|
||||
async function loadSavedCredentialsIfExist(): Promise<GoogleAuth<JSONClient> | null> {
|
||||
try {
|
||||
const content = await fs.readFile(TOKEN_PATH);
|
||||
const jwtInputRaw = JSON.parse(content.toString());
|
||||
|
||||
const jwtInputRes = jwtInputZod.safeParse(jwtInputRaw);
|
||||
if (!jwtInputRes.success) {
|
||||
throw new Error("Malformed JWT/token credentials");
|
||||
}
|
||||
|
||||
const jwtInput = jwtInputRes.data;
|
||||
const jsonClient = google.auth.fromJSON(jwtInput);
|
||||
const auth = new GoogleAuth({ authClient: jsonClient });
|
||||
return auth;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const credentialsZod = z.object({
|
||||
installed: z.object({
|
||||
client_id: z.string(),
|
||||
project_id: z.string(),
|
||||
auth_uri: z.string(),
|
||||
token_uri: z.string(),
|
||||
auth_provider_x509_cert_url: z.string(),
|
||||
client_secret: z.string(),
|
||||
redirect_uris: z.array(z.string()),
|
||||
}),
|
||||
web: z.optional(
|
||||
z.object({
|
||||
client_id: z.string(),
|
||||
project_id: z.string(),
|
||||
auth_uri: z.string(),
|
||||
token_uri: z.string(),
|
||||
auth_provider_x509_cert_url: z.string(),
|
||||
client_secret: z.string(),
|
||||
redirect_uris: z.array(z.string()),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
/**
|
||||
* Serializes credentials to a file compatible with GoogleAuth.fromJSON.
|
||||
*
|
||||
* @param {OAuth2Client} client
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function saveCredentials(client: OAuth2Client) {
|
||||
const content = await fs.readFile(CREDENTIALS_PATH);
|
||||
const keys = JSON.parse(content.toString());
|
||||
const key = keys.installed || keys.web;
|
||||
const payload = JSON.stringify({
|
||||
type: "authorized_user",
|
||||
client_id: key.client_id,
|
||||
client_secret: key.client_secret,
|
||||
refresh_token: client.credentials.refresh_token,
|
||||
});
|
||||
await fs.writeFile(TOKEN_PATH, payload);
|
||||
}
|
||||
async function saveCredentials(client: OAuth2Client): Promise<void> {
|
||||
const content = await fs.readFile(CREDENTIALS_PATH);
|
||||
const keysRaw = JSON.parse(content.toString());
|
||||
|
||||
const keysRes = credentialsZod.safeParse(keysRaw);
|
||||
if (!keysRes.success) {
|
||||
throw new Error("Malformed credentials/keys");
|
||||
}
|
||||
|
||||
const keys = keysRes.data;
|
||||
const key = keys.installed ?? keys.web;
|
||||
const payload = JSON.stringify({
|
||||
type: "authorized_user",
|
||||
client_id: key.client_id,
|
||||
client_secret: key.client_secret,
|
||||
refresh_token: client.credentials.refresh_token,
|
||||
});
|
||||
|
||||
await fs.writeFile(TOKEN_PATH, payload);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,16 +6,12 @@ import { google, sheets_v4 } from "googleapis";
|
|||
import { OAuth2Client } from "google-auth-library";
|
||||
import { appDataSource } from "./config/dbconfig.js";
|
||||
import { authorize } from "./auth.js";
|
||||
import { Lemma, WordForm, Lect } from "./db/dbmodel.js";
|
||||
import {
|
||||
Lemma,
|
||||
WordForm,
|
||||
Example,
|
||||
Media,
|
||||
Definition,
|
||||
Comment,
|
||||
PartOfSpeech,
|
||||
Lect,
|
||||
} from "./db/dbmodel.js";
|
||||
GoogleAuth,
|
||||
JSONClient,
|
||||
} from "google-auth-library/build/src/auth/googleauth.js";
|
||||
import "@total-typescript/ts-reset";
|
||||
|
||||
const RELOAD_SHEET_ON_START = false;
|
||||
|
||||
|
|
@ -56,7 +52,7 @@ function initExpress() {
|
|||
const search_term = req.query.search_term?.toString();
|
||||
|
||||
if (!search_term) {
|
||||
return;
|
||||
return void res.sendStatus(400);
|
||||
}
|
||||
|
||||
const lemmas: Lemma[] = (
|
||||
|
|
@ -81,21 +77,21 @@ function initExpress() {
|
|||
const name = req.query.name?.toString();
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
return void res.sendStatus(400);
|
||||
}
|
||||
|
||||
const lect = await Lect.findOne({
|
||||
where: { name: name },
|
||||
relations: { word_forms: { lemma: true } },
|
||||
});
|
||||
|
||||
|
||||
res.status(200).send({ lect });
|
||||
});
|
||||
|
||||
app.get("/lects", async (req, res) => {
|
||||
const lects = await Lect.find()
|
||||
res.status(200).send({
|
||||
lects
|
||||
app.get("/lects", async (_req, res) => {
|
||||
const lects = await Lect.find();
|
||||
res.status(200).send({
|
||||
lects,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -106,13 +102,13 @@ function initExpress() {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {google.auth.OAuth2Client} auth The authenticated Google OAuth client.
|
||||
* @param auth The authenticated Google OAuth client.
|
||||
*/
|
||||
async function loadSheet(auth: OAuth2Client) {
|
||||
async function loadSheet(auth: OAuth2Client | GoogleAuth<JSONClient>) {
|
||||
const lect_repository = appDataSource.getRepository(Lect);
|
||||
const word_form_repository = appDataSource.getRepository(WordForm);
|
||||
const lemma_repository = appDataSource.getRepository(Lemma);
|
||||
const options: any = { version: "v4", auth };
|
||||
const options: sheets_v4.Options = { version: "v4", auth };
|
||||
const sheets = google.sheets(options);
|
||||
const res = await sheets.spreadsheets.values.get({
|
||||
spreadsheetId: "1-YkCeynx_-KYdubvt14augSPo37_20YgUv_f-i8HVwY",
|
||||
|
|
@ -181,9 +177,9 @@ async function loadSheet(auth: OAuth2Client) {
|
|||
for (let i = 0; i < rows.length; i++) {
|
||||
const cell = row?.[i];
|
||||
if (
|
||||
cell === null
|
||||
|| cell === undefined
|
||||
|| (typeof cell === "string" && cell.length === 0)
|
||||
cell === null ||
|
||||
cell === undefined ||
|
||||
(typeof cell === "string" && cell.length === 0)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -201,7 +197,9 @@ async function loadSheet(auth: OAuth2Client) {
|
|||
for (let i = 0; i < lemmas.length; i += 100) {
|
||||
await lemma_repository.save(lemmas.slice(i, i + 100));
|
||||
console.log(
|
||||
`Saved ${Math.min(i + 100, lemmas.length)} / ${lemmas.length} lemmas...`,
|
||||
`Saved ${Math.min(i + 100, lemmas.length)} / ${
|
||||
lemmas.length
|
||||
} lemmas...`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue