diff --git a/apps/metamigo-api/src/app/plugins/hapi-nextauth.ts b/apps/metamigo-api/src/app/plugins/hapi-nextauth.ts index 0df40d7..ff9e2bb 100644 --- a/apps/metamigo-api/src/app/plugins/hapi-nextauth.ts +++ b/apps/metamigo-api/src/app/plugins/hapi-nextauth.ts @@ -7,7 +7,8 @@ export const registerNextAuth = async ( server: Hapi.Server, config: IAppConfig ): Promise => { - const nextAuthAdapterFactory: any = (request: Hapi.Request) => new NextAuthAdapter(request.db()); + const nextAuthAdapterFactory: any = (request: Hapi.Request) => + new NextAuthAdapter(request.db()); await server.register({ plugin: NextAuthPlugin, diff --git a/apps/metamigo-api/src/app/plugins/index.ts b/apps/metamigo-api/src/app/plugins/index.ts index c687c8f..b88d325 100644 --- a/apps/metamigo-api/src/app/plugins/index.ts +++ b/apps/metamigo-api/src/app/plugins/index.ts @@ -38,5 +38,5 @@ export const register = async ( await registerSwagger(server); await registerCloudflareAccessJwt(server, config); await registerAuthBearer(server, config); - await registerPostgraphile(server, config) + await registerPostgraphile(server, config); }; diff --git a/apps/metamigo-api/src/app/routes/voice/index.ts b/apps/metamigo-api/src/app/routes/voice/index.ts index 80dfc3e..88e5928 100644 --- a/apps/metamigo-api/src/app/routes/voice/index.ts +++ b/apps/metamigo-api/src/app/routes/voice/index.ts @@ -66,7 +66,7 @@ export const VoiceProviderRoutes = Helpers.withDefaults([ }, ]); -class VoiceLineRecordController extends CrudControllerBase(VoiceLineRecord) { } +class VoiceLineRecordController extends CrudControllerBase(VoiceLineRecord) {} const validator = (): Record => ({ create: { diff --git a/apps/metamigo-api/src/app/routes/voice/twilio/index.ts b/apps/metamigo-api/src/app/routes/voice/twilio/index.ts index 5303809..7becce3 100644 --- a/apps/metamigo-api/src/app/routes/voice/twilio/index.ts +++ b/apps/metamigo-api/src/app/routes/voice/twilio/index.ts @@ -91,7 +91,7 @@ export const TwilioRoutes = Helpers.noAuth([ }, async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) { 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 }); if (!voiceLine) return Boom.notFound(); if (voiceLine.id !== voiceLineId) return Boom.badRequest(); @@ -193,7 +193,7 @@ export const TwilioRoutes = Helpers.noAuth([ }, }, 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 .db() .voiceProviders.findById({ id: providerId }); diff --git a/apps/metamigo-api/src/index.ts b/apps/metamigo-api/src/index.ts index 9d6a07a..cd5387f 100644 --- a/apps/metamigo-api/src/index.ts +++ b/apps/metamigo-api/src/index.ts @@ -1,3 +1,2 @@ -export * from "./server/index.js" -export * from "./logger.js" - +export * from "./server/index.js"; +export * from "./logger.js"; diff --git a/apps/metamigo-api/src/main.ts b/apps/metamigo-api/src/main.ts index 5c3eeb9..a0d42c0 100644 --- a/apps/metamigo-api/src/main.ts +++ b/apps/metamigo-api/src/main.ts @@ -5,4 +5,4 @@ async function runServer(): Promise { await startWithout(["worker"]); } -runServer(); \ No newline at end of file +runServer(); diff --git a/apps/metamigo-frontend/components/layout/index.ts b/apps/metamigo-frontend/components/layout/index.ts index c253b2f..a49f09d 100644 --- a/apps/metamigo-frontend/components/layout/index.ts +++ b/apps/metamigo-frontend/components/layout/index.ts @@ -1,3 +1,3 @@ -export {default as AppBar} from "./AppBar"; -export {default as Layout} from "./Layout"; -export {default as Menu} from "./Menu"; +export { default as AppBar } from "./AppBar"; +export { default as Layout } from "./Layout"; +export { default as Menu } from "./Menu"; diff --git a/apps/metamigo-frontend/components/layout/themes.ts b/apps/metamigo-frontend/components/layout/themes.ts index 0099f72..e361795 100644 --- a/apps/metamigo-frontend/components/layout/themes.ts +++ b/apps/metamigo-frontend/components/layout/themes.ts @@ -13,7 +13,9 @@ export const theme = { background: { default: "#fff", }, - getContrastText(color: string) { return color === "#ffffff" ? "#000" : "#fff"; }, + getContrastText(color: string) { + return color === "#ffffff" ? "#000" : "#fff"; + }, }, shape: { borderRadius: 5, diff --git a/apps/metamigo-frontend/i18n/en.ts b/apps/metamigo-frontend/i18n/en.ts index 19a3177..a31aa9b 100644 --- a/apps/metamigo-frontend/i18n/en.ts +++ b/apps/metamigo-frontend/i18n/en.ts @@ -25,8 +25,7 @@ const customEnglishMessages: TranslationMessages = { signalBots: { name: "Signal Bot |||| Signal Bots", verifyDialog: { - sms: - "Please enter the verification code sent via SMS to %{phoneNumber}", + sms: "Please enter the verification code sent via SMS to %{phoneNumber}", voice: "Please answer the call from Signal to %{phoneNumber} and enter the verification code", }, diff --git a/apps/metamigo-frontend/lib/cloudflare.ts b/apps/metamigo-frontend/lib/cloudflare.ts index afb2e17..db75cbf 100644 --- a/apps/metamigo-frontend/lib/cloudflare.ts +++ b/apps/metamigo-frontend/lib/cloudflare.ts @@ -100,13 +100,15 @@ export const getIdentity = async ( const cloudflareAccountProvider = "cloudflare-access"; -const cloudflareAuthorizeCallback = ( - req: IncomingMessage, - domain: string, - verifier: VerifyFn, - adapter: Adapter -): (() => Promise) => async () => { - /* +const cloudflareAuthorizeCallback = + ( + req: IncomingMessage, + domain: string, + verifier: VerifyFn, + adapter: Adapter + ): (() => Promise) => + async () => { + /* 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 */ - const { token, decoded } = await verifyRequest(verifier, req); + const { token, decoded } = await verifyRequest(verifier, req); - const profile = { - email: undefined, - name: undefined, - avatar: undefined, + const profile = { + email: undefined, + name: 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 diff --git a/apps/metamigo-frontend/lib/nextauth-adapter.ts b/apps/metamigo-frontend/lib/nextauth-adapter.ts index 3a8d17a..b2af2cc 100644 --- a/apps/metamigo-frontend/lib/nextauth-adapter.ts +++ b/apps/metamigo-frontend/lib/nextauth-adapter.ts @@ -1,5 +1,10 @@ /* 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 Boom from "@hapi/boom"; @@ -15,7 +20,7 @@ export interface Profile { 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 { 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 { const { payload } = await wreck.get( `getUserByAccount/${provider}/${providerAccountId}` @@ -112,7 +123,7 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => { return payload; } catch (error) { if (Boom.isBoom(error, 404)) return null; - console.log(error) + console.log(error); throw new Error("GET_USER_BY_ACCOUNT"); } } @@ -129,12 +140,10 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => { } } - async function linkAccount( - account: AdapterAccount - ) { + async function linkAccount(account: AdapterAccount) { try { - await wreck.put("linkAccount", {payload: account} as any ); - } catch(error) { + await wreck.put("linkAccount", { payload: account } as any); + } catch (error) { console.log(error); throw new Error("LINK_ACCOUNT_ERROR"); } @@ -142,25 +151,33 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => { async function createSession(user: User) { try { - const { payload }: {payload: AdapterSession} = await wreck.post("createSession", { - payload: user, - }); - payload.expires = new Date(payload.expires) + const { payload }: { payload: AdapterSession } = await wreck.post( + "createSession", + { + payload: user, + } + ); + payload.expires = new Date(payload.expires); return payload; - } catch(error) { - console.log(error) + } catch (error) { + console.log(error); throw new Error("CREATE_SESSION_ERROR"); } } async function getSessionAndUser(sessionToken: string) { try { - const {payload}: {payload: any} = await wreck.get(`getSessionAndUser/${sessionToken}`); - const { session, user }: {session: AdapterSession, user: AdapterUser} = payload; - session.expires = new Date(session.expires) - return {session, user} + const { payload }: { payload: any } = await wreck.get( + `getSessionAndUser/${sessionToken}` + ); + const { + session, + user, + }: { session: AdapterSession; user: AdapterUser } = payload; + session.expires = new Date(session.expires); + return { session, user }; } catch (error) { - console.log(error) + console.log(error); if (Boom.isBoom(error, 404)) return null; throw new Error("GET_SESSION_AND_USER_ERROR"); } diff --git a/apps/metamigo-frontend/lib/phone-numbers.ts b/apps/metamigo-frontend/lib/phone-numbers.ts index bde973b..8a669fc 100644 --- a/apps/metamigo-frontend/lib/phone-numbers.ts +++ b/apps/metamigo-frontend/lib/phone-numbers.ts @@ -4,7 +4,8 @@ export const E164Regex = /^\+[1-9]\d{1,14}$/; /** * 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. diff --git a/apps/metamigo-frontend/package.json b/apps/metamigo-frontend/package.json index a8681c1..8dd78ca 100644 --- a/apps/metamigo-frontend/package.json +++ b/apps/metamigo-frontend/package.json @@ -38,7 +38,7 @@ "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", "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": { "@next/eslint-plugin-next": "^13.4.4", diff --git a/package.json b/package.json index 362580c..9cba1fa 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "dev": "dotenv -- turbo run dev --concurrency 30", "build": "turbo build --concurrency 30", "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", "workspaces": [ diff --git a/packages/hapi-nextauth/src/index.ts b/packages/hapi-nextauth/src/index.ts index e593556..7300ac3 100644 --- a/packages/hapi-nextauth/src/index.ts +++ b/packages/hapi-nextauth/src/index.ts @@ -62,12 +62,11 @@ const register = async ( server: Hapi.Server, pluginOpts?: any ): Promise => { - const options: any = - Hoek.applyToDefaults( - // a little type gymnastics here to workaround poor typing - defaultOptions as any, - pluginOpts - ) as any; + const options: any = Hoek.applyToDefaults( + // a little type gymnastics here to workaround poor typing + defaultOptions as any, + pluginOpts + ) as any; if (!options.nextAuthAdapterFactory) { throw new Error( diff --git a/packages/hapi-nextauth/src/types.ts b/packages/hapi-nextauth/src/types.ts index 8a96c3d..4909f8a 100644 --- a/packages/hapi-nextauth/src/types.ts +++ b/packages/hapi-nextauth/src/types.ts @@ -2,9 +2,7 @@ import type { Adapter } from "next-auth/adapters"; import type { NumberSchema, StringSchema, ObjectSchema } from "joi"; import type { Request } from "@hapi/hapi"; -export type AdapterFactory = ( - request: Request -) => Adapter; +export type AdapterFactory = (request: Request) => Adapter; export interface NextAuthPluginOptions { nextAuthAdapterFactory: Adapter; diff --git a/packages/metamigo-config/src/index.ts b/packages/metamigo-config/src/index.ts index d3c008e..ecac1fb 100644 --- a/packages/metamigo-config/src/index.ts +++ b/packages/metamigo-config/src/index.ts @@ -268,60 +268,60 @@ export const configSchema = { doc: "The full base zammad api url", format: String, default: undefined, - env: "ZAMMAD_API_URL" + env: "ZAMMAD_API_URL", }, zammadApiKey: { doc: "The zammad api key", format: String, default: undefined, sensitive: true, - env: "ZAMMAD_API_KEY" + env: "ZAMMAD_API_KEY", }, labelStudioApiUrl: { doc: "The full base label studio api url", format: String, default: undefined, - env: "LABEL_STUDIO_API_URL" + env: "LABEL_STUDIO_API_URL", }, labelStudioApiKey: { doc: "The label studio api key", format: String, default: undefined, sensitive: true, - env: "LABEL_STUDIO_API_KEY" + env: "LABEL_STUDIO_API_KEY", }, contributorId: { doc: "The leafcutter contributor id", format: String, default: undefined, - env: "LEAFCUTTER_CONTRIBUTOR_ID" + env: "LEAFCUTTER_CONTRIBUTOR_ID", }, contributorName: { doc: "The leafcutter contributor name", format: String, default: undefined, - env: "LEAFCUTTER_CONTRIBUTOR_NAME" + env: "LEAFCUTTER_CONTRIBUTOR_NAME", }, opensearchApiUrl: { doc: "The opensearch api url", format: String, default: undefined, - env: "OPENSEARCH_API_URL" + env: "OPENSEARCH_API_URL", }, opensearchUsername: { doc: "The opensearch username", format: String, default: undefined, - env: "OPENSEARCH_USERNAME" + env: "OPENSEARCH_USERNAME", }, opensearchPassword: { doc: "The opensearch password", format: String, default: undefined, sensative: true, - env: "OPENSEARCH_PASSWORD" + env: "OPENSEARCH_PASSWORD", }, - } + }, }; // define the interfaces for the concrete config objects diff --git a/packages/metamigo-db/src/helpers.ts b/packages/metamigo-db/src/helpers.ts index f4e7c1f..70532e0 100644 --- a/packages/metamigo-db/src/helpers.ts +++ b/packages/metamigo-db/src/helpers.ts @@ -2,7 +2,7 @@ import process from "node:process"; import { existsSync } from "node:fs"; import path from "node:path"; import { exec } from "node:child_process"; -import { fileURLToPath } from 'node:url'; +import { fileURLToPath } from "node:url"; 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 __dirname = path.dirname(fileURLToPath(import.meta.url)); - const dbDir = path.resolve(__dirname, '../../'); - const gmrcPath = path.resolve(__dirname, '../../.gmrc'); + const dbDir = path.resolve(__dirname, "../../"); + const gmrcPath = path.resolve(__dirname, "../../.gmrc"); if (!existsSync(gmrcPath)) { throw new Error(`graphile migrate config not found at ${gmrcPath}`); } diff --git a/packages/metamigo-db/src/records/settings.ts b/packages/metamigo-db/src/records/settings.ts index aac5450..3412b98 100644 --- a/packages/metamigo-db/src/records/settings.ts +++ b/packages/metamigo-db/src/records/settings.ts @@ -81,7 +81,7 @@ export const SettingsService = ( return s.value; }, - async save(settingInfo: SettingInfo, value: T): Promise { + async save(settingInfo: SettingInfo, value: T): Promise { const s = await repo.upsert(settingInfo.name, value); return s.value; }, diff --git a/packages/metamigo-db/src/records/whatsapp/bots.ts b/packages/metamigo-db/src/records/whatsapp/bots.ts index c86368f..9de0e59 100644 --- a/packages/metamigo-db/src/records/whatsapp/bots.ts +++ b/packages/metamigo-db/src/records/whatsapp/bots.ts @@ -50,5 +50,4 @@ export class WhatsappBotRecordRepository extends RepositoryBase( [this.schemaTable, verified, bot.id] ); } - } diff --git a/packages/montar/src/index.spec.ts b/packages/montar/src/index.spec.ts index 422cc3e..134c95e 100644 --- a/packages/montar/src/index.spec.ts +++ b/packages/montar/src/index.spec.ts @@ -16,7 +16,7 @@ const mutableObj = defState("mutableObj", { }); type FortyOneAdder = () => number; - + type Incrementer = (n: number) => number; const inc41 = defState("inc41", {