npm run fmt

This commit is contained in:
Abel Luck 2023-06-07 11:18:58 +00:00
parent 21fe35da05
commit 11c595619d
21 changed files with 155 additions and 137 deletions

View file

@ -7,7 +7,8 @@ export const registerNextAuth = async (
server: Hapi.Server, server: Hapi.Server,
config: IAppConfig config: IAppConfig
): Promise<void> => { ): Promise<void> => {
const nextAuthAdapterFactory: any = (request: Hapi.Request) => new NextAuthAdapter(request.db()); const nextAuthAdapterFactory: any = (request: Hapi.Request) =>
new NextAuthAdapter(request.db());
await server.register({ await server.register({
plugin: NextAuthPlugin, plugin: NextAuthPlugin,

View file

@ -38,5 +38,5 @@ export const register = async (
await registerSwagger(server); await registerSwagger(server);
await registerCloudflareAccessJwt(server, config); await registerCloudflareAccessJwt(server, config);
await registerAuthBearer(server, config); await registerAuthBearer(server, config);
await registerPostgraphile(server, config) await registerPostgraphile(server, config);
}; };

View file

@ -66,7 +66,7 @@ export const VoiceProviderRoutes = Helpers.withDefaults([
}, },
]); ]);
class VoiceLineRecordController extends CrudControllerBase(VoiceLineRecord) { } class VoiceLineRecordController extends CrudControllerBase(VoiceLineRecord) {}
const validator = (): Record<string, Hapi.RouteOptionsValidate> => ({ const validator = (): Record<string, Hapi.RouteOptionsValidate> => ({
create: { create: {

View file

@ -91,7 +91,7 @@ export const TwilioRoutes = Helpers.noAuth([
}, },
async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) { async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) {
const { voiceLineId } = request.params; const { voiceLineId } = request.params;
const { To } = request.payload as { To: string; }; const { To } = request.payload as { To: string };
const voiceLine = await request.db().voiceLines.findBy({ number: To }); const voiceLine = await request.db().voiceLines.findBy({ number: To });
if (!voiceLine) return Boom.notFound(); if (!voiceLine) return Boom.notFound();
if (voiceLine.id !== voiceLineId) return Boom.badRequest(); if (voiceLine.id !== voiceLineId) return Boom.badRequest();
@ -193,7 +193,7 @@ export const TwilioRoutes = Helpers.noAuth([
}, },
}, },
async handler(request: Hapi.Request, h: Hapi.ResponseToolkit) { async handler(request: Hapi.Request, h: Hapi.ResponseToolkit) {
const { providerId } = request.params as { providerId: string; }; const { providerId } = request.params as { providerId: string };
const provider: SavedVoiceProvider = await request const provider: SavedVoiceProvider = await request
.db() .db()
.voiceProviders.findById({ id: providerId }); .voiceProviders.findById({ id: providerId });

View file

@ -1,3 +1,2 @@
export * from "./server/index.js" export * from "./server/index.js";
export * from "./logger.js" export * from "./logger.js";

View file

@ -1,3 +1,3 @@
export {default as AppBar} from "./AppBar"; export { default as AppBar } from "./AppBar";
export {default as Layout} from "./Layout"; export { default as Layout } from "./Layout";
export {default as Menu} from "./Menu"; export { default as Menu } from "./Menu";

View file

@ -13,7 +13,9 @@ export const theme = {
background: { background: {
default: "#fff", default: "#fff",
}, },
getContrastText(color: string) { return color === "#ffffff" ? "#000" : "#fff"; }, getContrastText(color: string) {
return color === "#ffffff" ? "#000" : "#fff";
},
}, },
shape: { shape: {
borderRadius: 5, borderRadius: 5,

View file

@ -25,8 +25,7 @@ const customEnglishMessages: TranslationMessages = {
signalBots: { signalBots: {
name: "Signal Bot |||| Signal Bots", name: "Signal Bot |||| Signal Bots",
verifyDialog: { verifyDialog: {
sms: sms: "Please enter the verification code sent via SMS to %{phoneNumber}",
"Please enter the verification code sent via SMS to %{phoneNumber}",
voice: voice:
"Please answer the call from Signal to %{phoneNumber} and enter the verification code", "Please answer the call from Signal to %{phoneNumber} and enter the verification code",
}, },

View file

@ -100,13 +100,15 @@ export const getIdentity = async (
const cloudflareAccountProvider = "cloudflare-access"; const cloudflareAccountProvider = "cloudflare-access";
const cloudflareAuthorizeCallback = ( const cloudflareAuthorizeCallback =
req: IncomingMessage, (
domain: string, req: IncomingMessage,
verifier: VerifyFn, domain: string,
adapter: Adapter verifier: VerifyFn,
): (() => Promise<any>) => async () => { adapter: Adapter
/* ): (() => Promise<any>) =>
async () => {
/*
lots of little variables in here. lots of little variables in here.
@ -118,75 +120,75 @@ const cloudflareAuthorizeCallback = (
profile: this is the accumulated user information we have that we will fetch/build the user record with profile: this is the accumulated user information we have that we will fetch/build the user record with
*/ */
const { token, decoded } = await verifyRequest(verifier, req); const { token, decoded } = await verifyRequest(verifier, req);
const profile = { const profile = {
email: undefined, email: undefined,
name: undefined, name: undefined,
avatar: undefined, avatar: undefined,
};
if (decoded.email) profile.email = decoded.email;
if (decoded.name) profile.name = decoded.name;
const identity = await getIdentity(domain, token);
if (identity.email) profile.email = identity.email;
if (identity.name) profile.name = identity.name;
if (!profile.email)
throw new Error("cloudflare access authorization: email not found");
const providerId = `cfaccess|${identity.idp.type}|${identity.idp.id}`;
const providerAccountId = identity.user_uuid;
if (!providerAccountId)
throw new Error(
"cloudflare access authorization: missing provider account id"
);
const {
getUserByProviderAccountId,
getUserByEmail,
createUser,
linkAccount,
} =
// @ts-expect-error: non-existent property
await adapter.getAdapter({} as any);
const userByProviderAccountId = await getUserByProviderAccountId(
providerId,
providerAccountId
);
if (userByProviderAccountId) {
return userByProviderAccountId;
}
const userByEmail = await getUserByEmail(profile.email);
if (userByEmail) {
// we will not explicitly link accounts
throw new Error(
"cloudflare access authorization: user exists for email address, but is not linked."
);
}
const user = await createUser(profile);
// between the previous line and the next line exists a transactional bug
// https://github.com/nextauthjs/next-auth/issues/876
// hopefully we don't experience it
await linkAccount(
user.id,
providerId,
cloudflareAccountProvider,
providerAccountId,
// the following are unused but are specified for completness
undefined,
undefined,
undefined
);
return user;
}; };
if (decoded.email) profile.email = decoded.email;
if (decoded.name) profile.name = decoded.name;
const identity = await getIdentity(domain, token);
if (identity.email) profile.email = identity.email;
if (identity.name) profile.name = identity.name;
if (!profile.email)
throw new Error("cloudflare access authorization: email not found");
const providerId = `cfaccess|${identity.idp.type}|${identity.idp.id}`;
const providerAccountId = identity.user_uuid;
if (!providerAccountId)
throw new Error(
"cloudflare access authorization: missing provider account id"
);
const {
getUserByProviderAccountId,
getUserByEmail,
createUser,
linkAccount,
} =
// @ts-expect-error: non-existent property
await adapter.getAdapter({} as any);
const userByProviderAccountId = await getUserByProviderAccountId(
providerId,
providerAccountId
);
if (userByProviderAccountId) {
return userByProviderAccountId;
}
const userByEmail = await getUserByEmail(profile.email);
if (userByEmail) {
// we will not explicitly link accounts
throw new Error(
"cloudflare access authorization: user exists for email address, but is not linked."
);
}
const user = await createUser(profile);
// between the previous line and the next line exists a transactional bug
// https://github.com/nextauthjs/next-auth/issues/876
// hopefully we don't experience it
await linkAccount(
user.id,
providerId,
cloudflareAccountProvider,
providerAccountId,
// the following are unused but are specified for completness
undefined,
undefined,
undefined
);
return user;
};
/** /**
* @param audience the cloudflare access audience id * @param audience the cloudflare access audience id

View file

@ -1,5 +1,10 @@
/* eslint-disable unicorn/no-null */ /* eslint-disable unicorn/no-null */
import type { Adapter, AdapterAccount, AdapterSession, AdapterUser } from "next-auth/adapters"; import type {
Adapter,
AdapterAccount,
AdapterSession,
AdapterUser,
} from "next-auth/adapters";
import * as Wreck from "@hapi/wreck"; import * as Wreck from "@hapi/wreck";
import * as Boom from "@hapi/boom"; import * as Boom from "@hapi/boom";
@ -15,7 +20,7 @@ export interface Profile {
createdBy: string; createdBy: string;
} }
export type User = Profile & { id: string; createdAt: Date; updatedAt: Date; }; export type User = Profile & { id: string; createdAt: Date; updatedAt: Date };
export interface Session { export interface Session {
userId: string; userId: string;
@ -103,7 +108,13 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
} }
} }
async function getUserByAccount({ providerAccountId, provider }: { providerAccountId: string, provider: string }) { async function getUserByAccount({
providerAccountId,
provider,
}: {
providerAccountId: string;
provider: string;
}) {
try { try {
const { payload } = await wreck.get( const { payload } = await wreck.get(
`getUserByAccount/${provider}/${providerAccountId}` `getUserByAccount/${provider}/${providerAccountId}`
@ -112,7 +123,7 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
return payload; return payload;
} catch (error) { } catch (error) {
if (Boom.isBoom(error, 404)) return null; if (Boom.isBoom(error, 404)) return null;
console.log(error) console.log(error);
throw new Error("GET_USER_BY_ACCOUNT"); throw new Error("GET_USER_BY_ACCOUNT");
} }
} }
@ -129,12 +140,10 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
} }
} }
async function linkAccount( async function linkAccount(account: AdapterAccount) {
account: AdapterAccount
) {
try { try {
await wreck.put("linkAccount", {payload: account} as any ); await wreck.put("linkAccount", { payload: account } as any);
} catch(error) { } catch (error) {
console.log(error); console.log(error);
throw new Error("LINK_ACCOUNT_ERROR"); throw new Error("LINK_ACCOUNT_ERROR");
} }
@ -142,25 +151,33 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
async function createSession(user: User) { async function createSession(user: User) {
try { try {
const { payload }: {payload: AdapterSession} = await wreck.post("createSession", { const { payload }: { payload: AdapterSession } = await wreck.post(
payload: user, "createSession",
}); {
payload.expires = new Date(payload.expires) payload: user,
}
);
payload.expires = new Date(payload.expires);
return payload; return payload;
} catch(error) { } catch (error) {
console.log(error) console.log(error);
throw new Error("CREATE_SESSION_ERROR"); throw new Error("CREATE_SESSION_ERROR");
} }
} }
async function getSessionAndUser(sessionToken: string) { async function getSessionAndUser(sessionToken: string) {
try { try {
const {payload}: {payload: any} = await wreck.get(`getSessionAndUser/${sessionToken}`); const { payload }: { payload: any } = await wreck.get(
const { session, user }: {session: AdapterSession, user: AdapterUser} = payload; `getSessionAndUser/${sessionToken}`
session.expires = new Date(session.expires) );
return {session, user} const {
session,
user,
}: { session: AdapterSession; user: AdapterUser } = payload;
session.expires = new Date(session.expires);
return { session, user };
} catch (error) { } catch (error) {
console.log(error) console.log(error);
if (Boom.isBoom(error, 404)) return null; if (Boom.isBoom(error, 404)) return null;
throw new Error("GET_SESSION_AND_USER_ERROR"); throw new Error("GET_SESSION_AND_USER_ERROR");
} }

View file

@ -4,7 +4,8 @@ export const E164Regex = /^\+[1-9]\d{1,14}$/;
/** /**
* Returns true if the number is a valid E164 number * Returns true if the number is a valid E164 number
*/ */
export const isValidE164Number = (phoneNumber: string) => E164Regex.test(phoneNumber); export const isValidE164Number = (phoneNumber: string) =>
E164Regex.test(phoneNumber);
/** /**
* Given a phone number approximation, will clean out whitespace and punctuation. * Given a phone number approximation, will clean out whitespace and punctuation.

View file

@ -38,7 +38,7 @@
"test": "echo no tests", "test": "echo no tests",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.graphql && next lint && prettier --ignore-path .eslintignore \"**/*.{js,jsx,ts,tsx,graphql,md}\" --write", "lint": "eslint --ext .js,.jsx,.ts,.tsx,.graphql && next lint && prettier --ignore-path .eslintignore \"**/*.{js,jsx,ts,tsx,graphql,md}\" --write",
"fix:lint": "eslint --ext .js,.jsx,.ts,.tsx,.graphql --fix", "fix:lint": "eslint --ext .js,.jsx,.ts,.tsx,.graphql --fix",
"fmt": "prettier --ignore-path .eslintignore \"**/*.{js,jsx,ts,tsx,graphql,md}\" --list-different" "fmt": "prettier --ignore-path .eslintignore \"**/*.{js,jsx,ts,tsx,graphql,md}\" --write"
}, },
"devDependencies": { "devDependencies": {
"@next/eslint-plugin-next": "^13.4.4", "@next/eslint-plugin-next": "^13.4.4",

View file

@ -7,7 +7,8 @@
"dev": "dotenv -- turbo run dev --concurrency 30", "dev": "dotenv -- turbo run dev --concurrency 30",
"build": "turbo build --concurrency 30", "build": "turbo build --concurrency 30",
"dev:metamigo": "make dev-metamigo && dotenv -- turbo run dev --concurrency 30 --filter=!link --filter=!leafcutter", "dev:metamigo": "make dev-metamigo && dotenv -- turbo run dev --concurrency 30 --filter=!link --filter=!leafcutter",
"migrate": "dotenv -- npm run migrate --workspace=@digiresilience/metamigo-cli" "migrate": "dotenv -- npm run migrate --workspace=@digiresilience/metamigo-cli",
"fmt": "turbo run fmt"
}, },
"packageManager": "npm@9.3.1", "packageManager": "npm@9.3.1",
"workspaces": [ "workspaces": [

View file

@ -62,12 +62,11 @@ const register = async (
server: Hapi.Server, server: Hapi.Server,
pluginOpts?: any pluginOpts?: any
): Promise<void> => { ): Promise<void> => {
const options: any = const options: any = Hoek.applyToDefaults(
Hoek.applyToDefaults( // a little type gymnastics here to workaround poor typing
// a little type gymnastics here to workaround poor typing defaultOptions as any,
defaultOptions as any, pluginOpts
pluginOpts ) as any;
) as any;
if (!options.nextAuthAdapterFactory) { if (!options.nextAuthAdapterFactory) {
throw new Error( throw new Error(

View file

@ -2,9 +2,7 @@ import type { Adapter } from "next-auth/adapters";
import type { NumberSchema, StringSchema, ObjectSchema } from "joi"; import type { NumberSchema, StringSchema, ObjectSchema } from "joi";
import type { Request } from "@hapi/hapi"; import type { Request } from "@hapi/hapi";
export type AdapterFactory = ( export type AdapterFactory = (request: Request) => Adapter;
request: Request
) => Adapter;
export interface NextAuthPluginOptions { export interface NextAuthPluginOptions {
nextAuthAdapterFactory: Adapter; nextAuthAdapterFactory: Adapter;

View file

@ -268,60 +268,60 @@ export const configSchema = {
doc: "The full base zammad api url", doc: "The full base zammad api url",
format: String, format: String,
default: undefined, default: undefined,
env: "ZAMMAD_API_URL" env: "ZAMMAD_API_URL",
}, },
zammadApiKey: { zammadApiKey: {
doc: "The zammad api key", doc: "The zammad api key",
format: String, format: String,
default: undefined, default: undefined,
sensitive: true, sensitive: true,
env: "ZAMMAD_API_KEY" env: "ZAMMAD_API_KEY",
}, },
labelStudioApiUrl: { labelStudioApiUrl: {
doc: "The full base label studio api url", doc: "The full base label studio api url",
format: String, format: String,
default: undefined, default: undefined,
env: "LABEL_STUDIO_API_URL" env: "LABEL_STUDIO_API_URL",
}, },
labelStudioApiKey: { labelStudioApiKey: {
doc: "The label studio api key", doc: "The label studio api key",
format: String, format: String,
default: undefined, default: undefined,
sensitive: true, sensitive: true,
env: "LABEL_STUDIO_API_KEY" env: "LABEL_STUDIO_API_KEY",
}, },
contributorId: { contributorId: {
doc: "The leafcutter contributor id", doc: "The leafcutter contributor id",
format: String, format: String,
default: undefined, default: undefined,
env: "LEAFCUTTER_CONTRIBUTOR_ID" env: "LEAFCUTTER_CONTRIBUTOR_ID",
}, },
contributorName: { contributorName: {
doc: "The leafcutter contributor name", doc: "The leafcutter contributor name",
format: String, format: String,
default: undefined, default: undefined,
env: "LEAFCUTTER_CONTRIBUTOR_NAME" env: "LEAFCUTTER_CONTRIBUTOR_NAME",
}, },
opensearchApiUrl: { opensearchApiUrl: {
doc: "The opensearch api url", doc: "The opensearch api url",
format: String, format: String,
default: undefined, default: undefined,
env: "OPENSEARCH_API_URL" env: "OPENSEARCH_API_URL",
}, },
opensearchUsername: { opensearchUsername: {
doc: "The opensearch username", doc: "The opensearch username",
format: String, format: String,
default: undefined, default: undefined,
env: "OPENSEARCH_USERNAME" env: "OPENSEARCH_USERNAME",
}, },
opensearchPassword: { opensearchPassword: {
doc: "The opensearch password", doc: "The opensearch password",
format: String, format: String,
default: undefined, default: undefined,
sensative: true, sensative: true,
env: "OPENSEARCH_PASSWORD" env: "OPENSEARCH_PASSWORD",
}, },
} },
}; };
// define the interfaces for the concrete config objects // define the interfaces for the concrete config objects

View file

@ -2,7 +2,7 @@ import process from "node:process";
import { existsSync } from "node:fs"; import { existsSync } from "node:fs";
import path from "node:path"; import path from "node:path";
import { exec } from "node:child_process"; import { exec } from "node:child_process";
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from "node:url";
import type { IAppConfig } from "@digiresilience/metamigo-config"; import type { IAppConfig } from "@digiresilience/metamigo-config";
/** /**
@ -37,8 +37,8 @@ export const migrateWrapper = async (
}; };
const cmd = `npx --no-install graphile-migrate ${commands.join(" ")}`; const cmd = `npx --no-install graphile-migrate ${commands.join(" ")}`;
const __dirname = path.dirname(fileURLToPath(import.meta.url)); const __dirname = path.dirname(fileURLToPath(import.meta.url));
const dbDir = path.resolve(__dirname, '../../'); const dbDir = path.resolve(__dirname, "../../");
const gmrcPath = path.resolve(__dirname, '../../.gmrc'); const gmrcPath = path.resolve(__dirname, "../../.gmrc");
if (!existsSync(gmrcPath)) { if (!existsSync(gmrcPath)) {
throw new Error(`graphile migrate config not found at ${gmrcPath}`); throw new Error(`graphile migrate config not found at ${gmrcPath}`);
} }

View file

@ -81,7 +81,7 @@ export const SettingsService = (
return s.value; return s.value;
}, },
async save<T>(settingInfo: SettingInfo<T>, value: T): Promise<T> { async save<T>(settingInfo: SettingInfo<T>, value: T): Promise<T> {
const s = await repo.upsert(settingInfo.name, value); const s = await repo.upsert(settingInfo.name, value);
return s.value; return s.value;
}, },

View file

@ -50,5 +50,4 @@ export class WhatsappBotRecordRepository extends RepositoryBase(
[this.schemaTable, verified, bot.id] [this.schemaTable, verified, bot.id]
); );
} }
} }