Merge branch 'main' into shell-updates
This commit is contained in:
commit
db8a3d1ee0
132 changed files with 3609 additions and 5150 deletions
|
|
@ -1,61 +1,52 @@
|
|||
FROM node:20-bullseye as builder
|
||||
FROM node:20 as base
|
||||
|
||||
ARG METAMIGO_DIR=/opt/metamigo
|
||||
RUN mkdir -p ${METAMIGO_DIR}/
|
||||
WORKDIR ${METAMIGO_DIR}
|
||||
COPY package.json tsconfig.json ${METAMIGO_DIR}/
|
||||
COPY . ${METAMIGO_DIR}/
|
||||
FROM base AS builder
|
||||
ARG APP_DIR=/opt/metamigo-frontend
|
||||
RUN mkdir -p ${APP_DIR}/
|
||||
RUN npm i -g turbo
|
||||
WORKDIR ${APP_DIR}
|
||||
COPY . .
|
||||
RUN turbo prune --scope=@digiresilience/metamigo-frontend --docker
|
||||
|
||||
RUN npm --no-install tsc --build --verbose
|
||||
RUN npm install
|
||||
RUN npm run build
|
||||
|
||||
RUN rm -Rf ./node_modules
|
||||
FROM base AS installer
|
||||
ARG APP_DIR=/opt/metamigo-frontend
|
||||
WORKDIR ${APP_DIR}
|
||||
COPY .gitignore .gitignore
|
||||
COPY --from=builder ${APP_DIR}/out/json/ .
|
||||
COPY --from=builder ${APP_DIR}/out/package-lock.json ./package-lock.json
|
||||
RUN npm ci --omit=dev
|
||||
|
||||
FROM node:20-bullseye as clean
|
||||
ARG METAMIGO_DIR=/opt/metamigo
|
||||
|
||||
COPY --from=builder ${METAMIGO_DIR} ${METAMIGO_DIR}/
|
||||
|
||||
RUN rm -Rf ./node_modules
|
||||
|
||||
FROM node:20-bullseye as pristine
|
||||
LABEL maintainer="Abel Luck <abel@guardianproject.info>"
|
||||
COPY --from=builder ${APP_DIR}/out/full/ .
|
||||
RUN npm i -g turbo
|
||||
RUN turbo run build --filter=metamigo-frontend
|
||||
|
||||
FROM base AS runner
|
||||
ARG APP_DIR=/opt/metamigo-frontend
|
||||
WORKDIR ${APP_DIR}/
|
||||
ARG BUILD_DATE
|
||||
ARG VERSION
|
||||
LABEL maintainer="Darren Clarke <darren@redaranj.com>"
|
||||
LABEL org.label-schema.build-date=$BUILD_DATE
|
||||
LABEL org.label-schema.version=$VERSION
|
||||
ENV APP_DIR ${APP_DIR}
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
|
||||
apt-get install -y --no-install-recommends --fix-missing \
|
||||
postgresql-client dumb-init ffmpeg
|
||||
|
||||
ARG METAMIGO_DIR=/opt/metamigo
|
||||
ENV METAMIGO_DIR ${METAMIGO_DIR}
|
||||
RUN mkdir -p ${METAMIGO_DIR}
|
||||
RUN chown -R node:node ${METAMIGO_DIR}/
|
||||
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
COPY --from=clean ${METAMIGO_DIR}/ ${METAMIGO_DIR}/
|
||||
|
||||
WORKDIR ${METAMIGO_DIR}
|
||||
apt-get install -y --no-install-recommends \
|
||||
dumb-init
|
||||
RUN mkdir -p ${APP_DIR}
|
||||
RUN chown -R node ${APP_DIR}/
|
||||
|
||||
USER node
|
||||
|
||||
WORKDIR ${APP_DIR}
|
||||
COPY --from=installer ${APP_DIR}/node_modules/ ./node_modules/
|
||||
COPY --from=installer ${APP_DIR}/packages/ ./packages/
|
||||
COPY --from=installer ${APP_DIR}/apps/metamigo-frontend/ ./apps/metamigo-frontend/
|
||||
COPY --from=installer ${APP_DIR}/package.json ./package.json
|
||||
USER root
|
||||
WORKDIR ${APP_DIR}/apps/metamigo-frontend/
|
||||
RUN chmod +x docker-entrypoint.sh
|
||||
USER node
|
||||
EXPOSE 3000
|
||||
EXPOSE 3001
|
||||
EXPOSE 3002
|
||||
ENV PORT 3000
|
||||
ENV NODE_ENV production
|
||||
|
||||
ARG BUILD_DATE
|
||||
ARG VCS_REF
|
||||
ARG VCS_URL="https://gitlab.com/digiresilience/link/metamigo"
|
||||
ARG VERSION
|
||||
LABEL org.label-schema.schema-version="1.0"
|
||||
LABEL org.label-schema.name="digiresilience.org/link/metamigo"
|
||||
LABEL org.label-schema.description="part of CDR Link"
|
||||
LABEL org.label-schema.build-date=$BUILD_DATE
|
||||
LABEL org.label-schema.vcs-url=$VCS_URL
|
||||
LABEL org.label-schema.vcs-ref=$VCS_REF
|
||||
LABEL org.label-schema.version=$VERSION
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
ENTRYPOINT ["/opt/metamigo-frontend/apps/metamigo-frontend/docker-entrypoint.sh"]
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ const handleRequestCode = async ({
|
|||
verifyMode,
|
||||
id,
|
||||
onSuccess,
|
||||
onFailure,
|
||||
onError,
|
||||
captchaCode = undefined,
|
||||
}: any) => {
|
||||
if (verifyMode === MODE.SMS) console.log("REQUESTING sms");
|
||||
|
|
@ -160,7 +160,7 @@ const handleRequestCode = async ({
|
|||
if (response && response.ok) {
|
||||
onSuccess();
|
||||
} else {
|
||||
onFailure(response.status || 400);
|
||||
onError(response.status || 400);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("Failed to request verification code:", error);
|
||||
|
|
@ -171,7 +171,7 @@ const VerificationCodeRequest = ({
|
|||
verifyMode,
|
||||
data,
|
||||
onSuccess,
|
||||
onFailure,
|
||||
onError,
|
||||
}: any) => {
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
|
|
@ -179,10 +179,10 @@ const VerificationCodeRequest = ({
|
|||
verifyMode,
|
||||
id: data.id,
|
||||
onSuccess,
|
||||
onFailure,
|
||||
onError,
|
||||
});
|
||||
})();
|
||||
}, [data.id, onFailure, onSuccess, verifyMode]);
|
||||
}, [data.id, onError, onSuccess, verifyMode]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -204,7 +204,7 @@ const VerificationCaptcha = ({
|
|||
verifyMode,
|
||||
data,
|
||||
onSuccess,
|
||||
onFailure,
|
||||
onError,
|
||||
handleClose,
|
||||
}: any) => {
|
||||
const [code, setCode] = React.useState(undefined);
|
||||
|
|
@ -216,7 +216,7 @@ const VerificationCaptcha = ({
|
|||
verifyMode,
|
||||
id: data.id,
|
||||
onSuccess,
|
||||
onFailure,
|
||||
onError,
|
||||
captchaCode: code,
|
||||
});
|
||||
setSubmitting(false);
|
||||
|
|
@ -367,7 +367,7 @@ const VerificationCodeDialog = (props: any) => {
|
|||
props.handleClose();
|
||||
};
|
||||
|
||||
const onFailure = (code: number) => {
|
||||
const onError = (code: number) => {
|
||||
if (code === 402 || code === 500) {
|
||||
setStage("captcha");
|
||||
} else {
|
||||
|
|
@ -385,7 +385,7 @@ const VerificationCodeDialog = (props: any) => {
|
|||
<VerificationCodeRequest
|
||||
mode={props.verifyMode}
|
||||
onSuccess={onRequestSuccess}
|
||||
onFailure={onFailure}
|
||||
onError={onError}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -400,7 +400,7 @@ const VerificationCodeDialog = (props: any) => {
|
|||
<VerificationCaptcha
|
||||
mode={props.verifyMode}
|
||||
onSuccess={onRequestSuccess}
|
||||
onFailure={onRestartVerification}
|
||||
onError={onRestartVerification}
|
||||
handleClose={handleClose}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import {
|
|||
import { useSession } from "next-auth/react";
|
||||
import { UserRoleInput } from "./shared";
|
||||
|
||||
const UserCreate: FC<CreateProps> = (props: any) => {
|
||||
const UserCreate: FC<CreateProps> = () => {
|
||||
const { data: session } = useSession();
|
||||
return (
|
||||
<Create {...props} title="Create Users">
|
||||
<Create title="Create Users">
|
||||
<SimpleForm>
|
||||
<TextInput source="email" />
|
||||
<TextInput source="name" />
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import {
|
|||
Toolbar,
|
||||
SaveButton,
|
||||
DeleteButton,
|
||||
EditProps,
|
||||
useRedirect,
|
||||
useRecordContext,
|
||||
} from "react-admin";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { UserRoleInput } from "./shared";
|
||||
|
|
@ -23,16 +23,20 @@ const useStyles = makeStyles((_theme: any) => ({
|
|||
}));
|
||||
|
||||
const UserEditToolbar = (props: any) => {
|
||||
const classes = useStyles(props);
|
||||
const classes = useStyles();
|
||||
const redirect = useRedirect();
|
||||
const record = useRecordContext();
|
||||
const {session} = props;
|
||||
|
||||
const shouldDisableDelete = !session || !session.user || session.user.id === record.id;
|
||||
|
||||
return (
|
||||
<Toolbar className={classes.defaultToolbar} {...props}>
|
||||
<Toolbar className={classes.defaultToolbar}>
|
||||
<SaveButton
|
||||
label="save"
|
||||
mutationOptions={{ onSuccess: () => redirect("/users") }}
|
||||
/>
|
||||
<DeleteButton disabled={props.session.user.id === props.record.id} />
|
||||
<DeleteButton disabled={shouldDisableDelete} />
|
||||
</Toolbar>
|
||||
);
|
||||
};
|
||||
|
|
@ -43,11 +47,11 @@ const UserTitle = ({ record }: { record?: any }) => {
|
|||
return <span>User {title}</span>;
|
||||
};
|
||||
|
||||
const UserEdit = (props: EditProps) => {
|
||||
const UserEdit = () => {
|
||||
const { data: session } = useSession();
|
||||
|
||||
return (
|
||||
<Edit title={<UserTitle />} {...props}>
|
||||
<Edit title={<UserTitle />}>
|
||||
<SimpleForm toolbar={<UserEditToolbar session={session} />}>
|
||||
<TextInput disabled source="id" />
|
||||
<TextInput source="email" />
|
||||
|
|
|
|||
|
|
@ -6,11 +6,10 @@ import {
|
|||
TextField,
|
||||
EmailField,
|
||||
BooleanField,
|
||||
ListProps,
|
||||
} from "react-admin";
|
||||
|
||||
const UserList = (props: ListProps) => (
|
||||
<List {...props} exporter={false}>
|
||||
const UserList = () => (
|
||||
<List exporter={false}>
|
||||
<Datagrid rowClick="edit">
|
||||
<EmailField source="email" />
|
||||
<DateField source="emailVerified" />
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
import { SelectInput } from "react-admin";
|
||||
import { SelectInput, useRecordContext } from "react-admin";
|
||||
|
||||
export const UserRoleInput = (props: any) => (
|
||||
<SelectInput
|
||||
source="userRole"
|
||||
choices={[
|
||||
{ id: "NONE", name: "None" },
|
||||
{ id: "USER", name: "User" },
|
||||
{ id: "ADMIN", name: "Admin" },
|
||||
]}
|
||||
disabled={props.session.user.id === props.record.id}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
export const UserRoleInput = (props: any) => {
|
||||
const record = useRecordContext();
|
||||
return (
|
||||
<SelectInput
|
||||
source="userRole"
|
||||
choices={[
|
||||
{ id: "NONE", name: "None" },
|
||||
{ id: "USER", name: "User" },
|
||||
{ id: "ADMIN", name: "Admin" },
|
||||
]}
|
||||
disabled={props.session.user.id === record.id}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ const Sidebar = ({ record }: any) => {
|
|||
|
||||
const WhatsappBotShow = (props: ShowProps) => {
|
||||
const refresh = useRefresh();
|
||||
const { data } = useGetOne("whatsappBots", props.id as any);
|
||||
const { data } = useGetOne("whatsappBots", {id: props.id});
|
||||
|
||||
const { data: registerData, error: registerError } = useSWR(
|
||||
data && !data?.isVerified
|
||||
|
|
|
|||
|
|
@ -1,23 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd ${AMIGO_DIR}
|
||||
|
||||
if [[ "$1" == "api" ]]; then
|
||||
echo "docker-entrypoint: starting api server"
|
||||
./cli db -- migrate
|
||||
exec dumb-init ./cli api
|
||||
elif [[ "$1" == "worker" ]]; then
|
||||
echo "docker-entrypoint: starting worker"
|
||||
exec dumb-init ./cli worker
|
||||
elif [[ "$1" == "frontend" ]]; then
|
||||
echo "docker-entrypoint: starting frontend"
|
||||
exec dumb-init yarn workspace @app/frontend start
|
||||
elif [[ "$1" == "cli" ]]; then
|
||||
echo "docker-entrypoint: starting frontend"
|
||||
shift 1
|
||||
exec ./cli "$@"
|
||||
else
|
||||
echo "docker-entrypoint: missing argument, one of: api, worker, frontend, cli"
|
||||
exit 1
|
||||
fi
|
||||
echo "starting leafcutter"
|
||||
exec dumb-init npm run start
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -100,13 +100,15 @@ export const getIdentity = async (
|
|||
|
||||
const cloudflareAccountProvider = "cloudflare-access";
|
||||
|
||||
const cloudflareAuthorizeCallback = (
|
||||
req: IncomingMessage,
|
||||
domain: string,
|
||||
verifier: VerifyFn,
|
||||
adapter: Adapter
|
||||
): (() => Promise<any>) => async () => {
|
||||
/*
|
||||
const cloudflareAuthorizeCallback =
|
||||
(
|
||||
req: IncomingMessage,
|
||||
domain: string,
|
||||
verifier: VerifyFn,
|
||||
adapter: Adapter
|
||||
): (() => Promise<any>) =>
|
||||
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
|
||||
|
|
|
|||
|
|
@ -8,8 +8,5 @@ export const metamigoDataProvider = async (client: any) => {
|
|||
{},
|
||||
{ introspection: { schema: schema.data.__schema } }
|
||||
);
|
||||
|
||||
const dataProvider = async (type: any, resource: any, params: any) => graphqlDataProvider(type, resource, params);
|
||||
|
||||
return dataProvider;
|
||||
return graphqlDataProvider;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
/* eslint-disable unicorn/no-null */
|
||||
/* eslint-disable max-params */
|
||||
import type { Adapter } from "next-auth/adapters";
|
||||
// @ts-expect-error: Missing export
|
||||
import type { AppOptions } from "next-auth";
|
||||
import type {
|
||||
Adapter,
|
||||
AdapterAccount,
|
||||
AdapterSession,
|
||||
AdapterUser,
|
||||
} from "next-auth/adapters";
|
||||
import * as Wreck from "@hapi/wreck";
|
||||
import * as Boom from "@hapi/boom";
|
||||
|
||||
|
|
@ -18,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;
|
||||
|
|
@ -70,7 +72,7 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
|
|||
json: "force",
|
||||
});
|
||||
|
||||
async function getAdapter(_appOptions: AppOptions) {
|
||||
function getAdapter(): Adapter {
|
||||
async function createUser(profile: Profile) {
|
||||
try {
|
||||
if (!profile.createdBy) profile = { ...profile, createdBy: "nextauth" };
|
||||
|
|
@ -106,19 +108,23 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
|
|||
}
|
||||
}
|
||||
|
||||
async function getUserByProviderAccountId(
|
||||
providerId: string,
|
||||
providerAccountId: string
|
||||
) {
|
||||
async function getUserByAccount({
|
||||
providerAccountId,
|
||||
provider,
|
||||
}: {
|
||||
providerAccountId: string;
|
||||
provider: string;
|
||||
}) {
|
||||
try {
|
||||
const { payload } = await wreck.get(
|
||||
`getUserByProviderAccountId/${providerId}/${providerAccountId}`
|
||||
`getUserByAccount/${provider}/${providerAccountId}`
|
||||
);
|
||||
|
||||
return payload;
|
||||
} catch (error) {
|
||||
if (Boom.isBoom(error, 404)) return null;
|
||||
throw new Error("GET_USER_BY_PROVIDER_ACCOUNT_ID");
|
||||
console.log(error);
|
||||
throw new Error("GET_USER_BY_ACCOUNT");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -134,52 +140,46 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
|
|||
}
|
||||
}
|
||||
|
||||
async function linkAccount(
|
||||
userId: string,
|
||||
providerId: string,
|
||||
providerType: string,
|
||||
providerAccountId: string,
|
||||
refreshToken: string,
|
||||
accessToken: string,
|
||||
accessTokenExpires: number
|
||||
) {
|
||||
async function linkAccount(account: AdapterAccount) {
|
||||
try {
|
||||
const payload = {
|
||||
userId,
|
||||
providerId,
|
||||
providerType,
|
||||
providerAccountId: `${providerAccountId}`, // must be a string
|
||||
refreshToken,
|
||||
accessToken,
|
||||
accessTokenExpires,
|
||||
};
|
||||
await wreck.put("linkAccount", {
|
||||
payload,
|
||||
});
|
||||
} catch {
|
||||
await wreck.put("linkAccount", { payload: account } as any);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new Error("LINK_ACCOUNT_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
async function createSession(user: User) {
|
||||
try {
|
||||
const { payload } = await wreck.post("createSession", {
|
||||
payload: user,
|
||||
});
|
||||
|
||||
const { payload }: { payload: AdapterSession } = await wreck.post(
|
||||
"createSession",
|
||||
{
|
||||
payload: user,
|
||||
}
|
||||
);
|
||||
payload.expires = new Date(payload.expires);
|
||||
return payload;
|
||||
} catch {
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new Error("CREATE_SESSION_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
async function getSession(sessionToken: string) {
|
||||
async function getSessionAndUser(sessionToken: string) {
|
||||
try {
|
||||
const { payload } = await wreck.get(`getSession/${sessionToken}`);
|
||||
return payload;
|
||||
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);
|
||||
if (Boom.isBoom(error, 404)) return null;
|
||||
throw new Error("GET_SESSION_ERROR");
|
||||
throw new Error("GET_SESSION_AND_USER_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -213,21 +213,18 @@ export const MetamigoAdapter = (config: IAppConfig): Adapter => {
|
|||
createUser,
|
||||
getUser,
|
||||
getUserByEmail,
|
||||
getUserByProviderAccountId,
|
||||
getUserByAccount,
|
||||
updateUser,
|
||||
// deleteUser,
|
||||
linkAccount,
|
||||
// unlinkAccount,
|
||||
createSession,
|
||||
getSession,
|
||||
getSessionAndUser,
|
||||
updateSession,
|
||||
deleteSession,
|
||||
// @ts-expect-error: Type error
|
||||
} as AdapterInstance<Profile, User, Session, unknown>;
|
||||
}
|
||||
|
||||
return {
|
||||
// @ts-expect-error: non-existent property
|
||||
getAdapter,
|
||||
};
|
||||
return getAdapter();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "metamigo-frontend",
|
||||
"name": "@digiresilience/metamigo-frontend",
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -60,38 +60,20 @@ const nextAuthOptions = (config: IAppConfig, req: NextApiRequest) => {
|
|||
return {
|
||||
secret: nextAuth.secret,
|
||||
session: {
|
||||
jwt: true,
|
||||
strategy: "database",
|
||||
maxAge: 8 * 60 * 60, // 8 hours
|
||||
},
|
||||
jwt: {
|
||||
secret: nextAuth.secret,
|
||||
encryption: false,
|
||||
signingKey: nextAuth.signingKey,
|
||||
encryptionKey: nextAuth.encryptionKey,
|
||||
},
|
||||
providers,
|
||||
adapter,
|
||||
callbacks: {
|
||||
async session(session: any, token: any) {
|
||||
// make the user id available in the react client
|
||||
session.user.id = token.userId;
|
||||
async session({ session, user }: any) {
|
||||
session.user.id = user.id;
|
||||
session.user.userRole = user.userRole;
|
||||
return session;
|
||||
},
|
||||
async jwt(token: any, user: any) {
|
||||
const isSignIn = Boolean(user);
|
||||
// Add auth_time to token on signin in
|
||||
if (isSignIn) {
|
||||
// not sure what this does
|
||||
// if (!token.aud) token.aud;
|
||||
|
||||
token.aud = nextAuth.audience;
|
||||
token.picture = user.avatar;
|
||||
token.userId = user.id;
|
||||
token.role = user.userRole ? `app_${user.userRole}` : "app_anonymous";
|
||||
}
|
||||
|
||||
return token;
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export default createProxyMiddleware({
|
|||
target:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "http://metamigo-api:3001"
|
||||
: "http://localhost:3001",
|
||||
: "http://127.0.0.1:3001",
|
||||
changeOrigin: true,
|
||||
pathRewrite: { "^/graphql": "/graphql" },
|
||||
xfwd: true,
|
||||
|
|
@ -20,8 +20,6 @@ export default createProxyMiddleware({
|
|||
let token = req.cookies["__Secure-next-auth.session-token"];
|
||||
if (!token) token = req.cookies["next-auth.session-token"];
|
||||
|
||||
// console.log(req.body);
|
||||
// if (req.body.query) console.log(req.body.query);
|
||||
if (token) {
|
||||
proxyReq.setHeader("authorization", `Bearer ${token}`);
|
||||
proxyReq.removeHeader("cookie");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue