From 7f554d23fcb80b39b263c73f6e87bb6f08c0d9fc Mon Sep 17 00:00:00 2001 From: Baozier Date: Mon, 13 Jan 2025 23:12:51 -0500 Subject: [PATCH 1/4] Add basic org manaqement pages on admin-panel --- admin-panel/app/Setup.tsx | 10 + admin-panel/app/[lang]/dashboard/page.tsx | 3 +- admin-panel/app/[lang]/orgs/[id]/page.tsx | 299 ++++++++++++++++++ admin-panel/app/[lang]/orgs/new/page.tsx | 81 +++++ admin-panel/app/[lang]/orgs/page.tsx | 86 ++++++ admin-panel/app/[lang]/orgs/useEditOrg.tsx | 119 ++++++++ admin-panel/app/api/v1/orgs/[id]/route.ts | 45 +++ admin-panel/app/api/v1/orgs/route.ts | 22 ++ admin-panel/services/auth/api.ts | 335 +++++++++++++++------ admin-panel/tools/route.ts | 1 + admin-panel/translations/en.json | 22 ++ docs/auth-server.md | 4 + package-lock.json | 4 +- server/src/__tests__/normal/org.test.ts | 64 ++++ server/src/__tests__/normal/other.test.tsx | 1 + server/src/configs/type.ts | 1 + server/src/handlers/other.ts | 1 + server/src/middlewares/config.ts | 10 + server/src/routes/org.tsx | 7 +- server/src/scripts/swagger.json | 4 +- server/wrangler.toml | 1 + 21 files changed, 1014 insertions(+), 106 deletions(-) create mode 100644 admin-panel/app/[lang]/orgs/[id]/page.tsx create mode 100644 admin-panel/app/[lang]/orgs/new/page.tsx create mode 100644 admin-panel/app/[lang]/orgs/page.tsx create mode 100644 admin-panel/app/[lang]/orgs/useEditOrg.tsx create mode 100644 admin-panel/app/api/v1/orgs/[id]/route.ts create mode 100644 admin-panel/app/api/v1/orgs/route.ts diff --git a/admin-panel/app/Setup.tsx b/admin-panel/app/Setup.tsx index c612c72..8e6f594 100644 --- a/admin-panel/app/Setup.tsx +++ b/admin-panel/app/Setup.tsx @@ -120,6 +120,7 @@ const LayoutSetup = ({ children } : PropsWithChildren) => { const configs = useSignalValue(configSignal) const showLogs = configs?.ENABLE_SIGN_IN_LOG || configs?.ENABLE_SMS_LOG || configs?.ENABLE_EMAIL_LOG + const showOrg = configs?.ENABLE_ORG useEffect( () => { @@ -187,6 +188,15 @@ const LayoutSetup = ({ children } : PropsWithChildren) => { > {t('layout.scopes')} + {showOrg && ( + + {t('layout.orgs')} + + )} {!!showLogs && ( { 'ENABLE_EMAIL_VERIFICATION', 'ENABLE_PASSWORD_RESET', 'ENABLE_USER_APP_CONSENT', + 'ENABLE_ORG' ], }, { @@ -78,7 +79,7 @@ const Page = () => { value: [ 'ACCOUNT_LOCKOUT_THRESHOLD', 'ACCOUNT_LOCKOUT_EXPIRES_IN', 'UNLOCK_ACCOUNT_VIA_PASSWORD_RESET', 'PASSWORD_RESET_EMAIL_THRESHOLD', - 'ACCOUNT_LOCKOUT_THRESHOLD', 'EMAIL_MFA_EMAIL_THRESHOLD', + 'CHANGE_EMAIL_EMAIL_THRESHOLD', 'EMAIL_MFA_EMAIL_THRESHOLD', 'SMS_MFA_MESSAGE_THRESHOLD', ], }, diff --git a/admin-panel/app/[lang]/orgs/[id]/page.tsx b/admin-panel/app/[lang]/orgs/[id]/page.tsx new file mode 100644 index 0000000..23e14ba --- /dev/null +++ b/admin-panel/app/[lang]/orgs/[id]/page.tsx @@ -0,0 +1,299 @@ +'use client' + +import { + Table, + TextInput, +} from 'flowbite-react' +import { useTranslations } from 'next-intl' +import { useParams } from 'next/navigation' +import { useState } from 'react' +import useEditOrg from 'app/[lang]/orgs/useEditOrg' +import { routeTool } from 'tools' +import SaveButton from 'components/SaveButton' +import FieldError from 'components/FieldError' +import SubmitError from 'components/SubmitError' +import PageTitle from 'components/PageTitle' +import DeleteButton from 'components/DeleteButton' +import useLocaleRouter from 'hooks/useLocaleRoute' +import { + useGetApiV1OrgsByIdQuery, usePutApiV1OrgsByIdMutation, useDeleteApiV1OrgsByIdMutation +} from 'services/auth/api' + +const Page = () => { + const { id } = useParams() + + const t = useTranslations() + const router = useLocaleRouter() + + const { data } = useGetApiV1OrgsByIdQuery({ id: Number(id) }) + const [updateOrg, { isLoading: isUpdating }] = usePutApiV1OrgsByIdMutation() + const [deleteOrg, { isLoading: isDeleting }] = useDeleteApiV1OrgsByIdMutation() + + const org = data?.org + + const { + values, errors, onChange, + } = useEditOrg(org) + const [showErrors, setShowErrors] = useState(false) + + const handleSave = async () => { + if (Object.values(errors).some((val) => !!val)) { + setShowErrors(true) + return + } + + await updateOrg({ + id: Number(id), + putOrgReq: values, + }) + } + + const handleDelete = async () => { + await deleteOrg({ id: Number(id) }) + + router.push(routeTool.Internal.Orgs) + } + + if (!org) return null + + return ( +
+ +
+ + + {t('common.property')} + {t('common.value')} + + + + {t('orgs.name')} + + onChange( + 'name', + e.target.value, + )} + value={values.name} /> + {showErrors && } + + + + {t('orgs.companyLogoUrl')} + + onChange( + 'companyLogoUrl', + e.target.value, + )} + value={values.companyLogoUrl} + /> + + + + {t('orgs.fontFamily')} + + onChange( + 'fontFamily', + e.target.value, + )} + value={values.fontFamily} + /> + + + + {t('orgs.fontUrl')} + + onChange( + 'fontUrl', + e.target.value, + )} + value={values.fontUrl} + /> + + + + {t('orgs.layoutColor')} + + onChange( + 'layoutColor', + e.target.value, + )} + value={values.layoutColor} + /> + + + + {t('orgs.labelColor')} + + onChange( + 'labelColor', + e.target.value, + )} + value={values.labelColor} + /> + + + + {t('orgs.primaryButtonColor')} + + onChange( + 'primaryButtonColor', + e.target.value, + )} + value={values.primaryButtonColor} + /> + + + + {t('orgs.primaryButtonLabelColor')} + + onChange( + 'primaryButtonLabelColor', + e.target.value, + )} + value={values.primaryButtonLabelColor} + /> + + + + {t('orgs.primaryButtonBorderColor')} + + onChange( + 'primaryButtonBorderColor', + e.target.value, + )} + value={values.primaryButtonBorderColor} + /> + + + + {t('orgs.secondaryButtonColor')} + + onChange( + 'secondaryButtonColor', + e.target.value, + )} + value={values.secondaryButtonColor} + /> + + + + {t('orgs.secondaryButtonLabelColor')} + + onChange( + 'secondaryButtonLabelColor', + e.target.value, + )} + value={values.secondaryButtonLabelColor} + /> + + + + {t('orgs.secondaryButtonBorderColor')} + + onChange( + 'secondaryButtonBorderColor', + e.target.value, + )} + value={values.secondaryButtonBorderColor} + /> + + + + {t('orgs.criticalIndicatorColor')} + + onChange( + 'criticalIndicatorColor', + e.target.value, + )} + value={values.criticalIndicatorColor} + /> + + + + {t('orgs.termsLink')} + + onChange( + 'termsLink', + e.target.value, + )} + value={values.termsLink} + /> + + + + {t('orgs.privacyPolicyLink')} + + onChange( + 'privacyPolicyLink', + e.target.value, + )} + value={values.privacyPolicyLink} + /> + + + + {t('common.createdAt')} + {org.createdAt} UTC + + + {t('common.updatedAt')} + {org.updatedAt} UTC + + +
+
+ +
+ + +
+
+ ) +} + +export default Page diff --git a/admin-panel/app/[lang]/orgs/new/page.tsx b/admin-panel/app/[lang]/orgs/new/page.tsx new file mode 100644 index 0000000..af36756 --- /dev/null +++ b/admin-panel/app/[lang]/orgs/new/page.tsx @@ -0,0 +1,81 @@ +'use client' + +import { + Table, + TextInput, +} from 'flowbite-react' +import { useTranslations } from 'next-intl' +import { useState } from 'react' +import useEditOrg from 'app/[lang]/orgs/useEditOrg' +import { routeTool } from 'tools' +import PageTitle from 'components/PageTitle' +import SaveButton from 'components/SaveButton' +import useLocaleRouter from 'hooks/useLocaleRoute' +import FieldError from 'components/FieldError' +import SubmitError from 'components/SubmitError' +import { usePostApiV1OrgsMutation } from 'services/auth/api' + +const Page = () => { + const t = useTranslations() + const router = useLocaleRouter() + + const { + values, errors, onChange, + } = useEditOrg(undefined) + const [showErrors, setShowErrors] = useState(false) + const [createOrg, { isLoading: isCreating }] = usePostApiV1OrgsMutation() + + const handleSubmit = async () => { + if (Object.values(errors).some((val) => !!val)) { + setShowErrors(true) + return + } + + const res = await createOrg({ postOrgReq: values }) + + if (res.data?.org?.id) { + router.push(`${routeTool.Internal.Orgs}/${res.data.org.id}`) + } + } + + return ( +
+ +
+ + + {t('common.property')} + {t('common.value')} + + + + {t('orgs.name')} + + onChange( + 'name', + e.target.value, + )} + value={values.name} + /> + {showErrors && } + + + +
+
+ + +
+ ) +} + +export default Page diff --git a/admin-panel/app/[lang]/orgs/page.tsx b/admin-panel/app/[lang]/orgs/page.tsx new file mode 100644 index 0000000..ae1d4fd --- /dev/null +++ b/admin-panel/app/[lang]/orgs/page.tsx @@ -0,0 +1,86 @@ +'use client' + +import { Table } from 'flowbite-react' +import { useTranslations } from 'next-intl' +import useCurrentLocale from 'hooks/useCurrentLocale' +import { + routeTool, +} from 'tools' +import EditLink from 'components/EditLink' +import PageTitle from 'components/PageTitle' +import CreateButton from 'components/CreateButton' +import { + useGetApiV1OrgsQuery, Org +} from 'services/auth/api' + +const Page = () => { + const t = useTranslations() + const locale = useCurrentLocale() + + const { data } = useGetApiV1OrgsQuery() + const orgs = data?.orgs ?? [] + + const renderEditButton = (org: Org) => { + return ( + + ) + } + + return ( +
+
+ + +
+ + + {t('orgs.org')} + + + {t('orgs.name')} + + + + {orgs.map((org) => ( + + +
+
+
+ {org.name} +
+
+ {renderEditButton(org)} +
+
+
+
+
+ ))} +
+ + {orgs.map((org) => ( + + +
+ {org.name} +
+
+ + {renderEditButton(org)} + +
+ ))} +
+
+
+ ) +} + +export default Page diff --git a/admin-panel/app/[lang]/orgs/useEditOrg.tsx b/admin-panel/app/[lang]/orgs/useEditOrg.tsx new file mode 100644 index 0000000..073b4c7 --- /dev/null +++ b/admin-panel/app/[lang]/orgs/useEditOrg.tsx @@ -0,0 +1,119 @@ +import { + useEffect, + useMemo, useState, +} from 'react' +import { useTranslations } from 'next-intl' +import { Org } from 'services/auth/api' + +const useEditOrg = (org: Org | undefined) => { + const t = useTranslations() + + const [name, setName] = useState('') + const [companyLogoUrl, setCompanyLogoUrl] = useState('') + const [fontFamily, setFontFamily] = useState('') + const [fontUrl, setFontUrl] = useState('') + const [layoutColor, setLayoutColor] = useState('') + const [labelColor, setLabelColor] = useState('') + const [primaryButtonColor, setPrimaryButtonColor] = useState('') + const [primaryButtonLabelColor, setPrimaryButtonLabelColor] = useState('') + const [primaryButtonBorderColor, setPrimaryButtonBorderColor] = useState('') + const [secondaryButtonColor, setSecondaryButtonColor] = useState('') + const [secondaryButtonLabelColor, setSecondaryButtonLabelColor] = useState('') + const [secondaryButtonBorderColor, setSecondaryButtonBorderColor] = useState('') + const [criticalIndicatorColor, setCriticalIndicatorColor] = useState('') + const [termsLink, setTermsLink] = useState('') + const [privacyPolicyLink, setPrivacyPolicyLink] = useState('') + + useEffect( + () => { + setName(org?.name ?? '') + setCompanyLogoUrl(org?.companyLogoUrl ?? '') + setFontFamily(org?.fontFamily ?? '') + setFontUrl(org?.fontUrl ?? '') + setLayoutColor(org?.layoutColor ?? '') + setLabelColor(org?.labelColor ?? '') + setPrimaryButtonColor(org?.primaryButtonColor ?? '') + setPrimaryButtonLabelColor(org?.primaryButtonLabelColor ?? '') + setPrimaryButtonBorderColor(org?.primaryButtonBorderColor ?? '') + setSecondaryButtonColor(org?.secondaryButtonColor ?? '') + setSecondaryButtonLabelColor(org?.secondaryButtonLabelColor ?? '') + setSecondaryButtonBorderColor(org?.secondaryButtonBorderColor ?? '') + setCriticalIndicatorColor(org?.criticalIndicatorColor ?? '') + setTermsLink(org?.termsLink ?? '') + setPrivacyPolicyLink(org?.privacyPolicyLink ?? '') + }, + [org], + ) + + const values = { + name, companyLogoUrl, fontFamily, fontUrl, layoutColor, labelColor, + primaryButtonColor, primaryButtonLabelColor, primaryButtonBorderColor, + secondaryButtonColor, secondaryButtonLabelColor, secondaryButtonBorderColor, + criticalIndicatorColor, termsLink, privacyPolicyLink + } + + const errors = useMemo( + () => ({ name: values.name.trim().length ? undefined : t('common.fieldIsRequired') }), + [values, t], + ) + + const onChange = ( + key: string, value: string | string[], + ) => { + switch (key) { + case 'name': + setName(value as string) + break + case 'companyLogoUrl': + setCompanyLogoUrl(value as string) + break + case 'fontFamily': + setFontFamily(value as string) + break + case 'fontUrl': + setFontUrl(value as string) + break + case 'layoutColor': + setLayoutColor(value as string) + break + case 'labelColor': + setLabelColor(value as string) + break + case 'primaryButtonColor': + setPrimaryButtonColor(value as string) + break + case 'primaryButtonLabelColor': + setPrimaryButtonLabelColor(value as string) + break + case 'primaryButtonBorderColor': + setPrimaryButtonBorderColor(value as string) + break + case 'secondaryButtonColor': + setSecondaryButtonColor(value as string) + break + case 'secondaryButtonLabelColor': + setSecondaryButtonLabelColor(value as string) + break + case 'secondaryButtonBorderColor': + setSecondaryButtonBorderColor(value as string) + break + case 'criticalIndicatorColor': + setCriticalIndicatorColor(value as string) + break + case 'termsLink': + setTermsLink(value as string) + break + case 'privacyPolicyLink': + setPrivacyPolicyLink(value as string) + break + } + } + + return { + values, + errors, + onChange, + } +} + +export default useEditOrg diff --git a/admin-panel/app/api/v1/orgs/[id]/route.ts b/admin-panel/app/api/v1/orgs/[id]/route.ts new file mode 100644 index 0000000..e28e088 --- /dev/null +++ b/admin-panel/app/api/v1/orgs/[id]/route.ts @@ -0,0 +1,45 @@ +import { + sendS2SRequest, + throwForbiddenError, +} from 'app/api/request' + +type Params = { + id: string; +} + +export async function GET ( + request: Request, context: { params: Params }, +) { + const id = context.params.id + + return sendS2SRequest({ + method: 'GET', + uri: `/api/v1/orgs/${id}`, + }) +} + +export async function PUT ( + request: Request, context: { params: Params }, +) { + const id = context.params.id + + const reqBody = await request.json() + if (!reqBody) return throwForbiddenError() + + return sendS2SRequest({ + method: 'PUT', + uri: `/api/v1/orgs/${id}`, + body: JSON.stringify(reqBody), + }) +} + +export async function DELETE ( + request: Request, context: { params: Params }, +) { + const id = context.params.id + + return sendS2SRequest({ + method: 'DELETE', + uri: `/api/v1/orgs/${id}`, + }) +} diff --git a/admin-panel/app/api/v1/orgs/route.ts b/admin-panel/app/api/v1/orgs/route.ts new file mode 100644 index 0000000..3f72d51 --- /dev/null +++ b/admin-panel/app/api/v1/orgs/route.ts @@ -0,0 +1,22 @@ +import { + sendS2SRequest, + throwForbiddenError, +} from 'app/api/request' + +export async function GET () { + return sendS2SRequest({ + method: 'GET', + uri: '/api/v1/orgs', + }) +} + +export async function POST (request: Request) { + const reqBody = await request.json() + if (!reqBody) return throwForbiddenError() + + return sendS2SRequest({ + method: 'POST', + uri: '/api/v1/orgs', + body: JSON.stringify(reqBody), + }) +} diff --git a/admin-panel/services/auth/api.ts b/admin-panel/services/auth/api.ts index 8906360..c815823 100644 --- a/admin-panel/services/auth/api.ts +++ b/admin-panel/services/auth/api.ts @@ -1,39 +1,42 @@ -import { authApi as api } from './' +import { authApi as api } from "./"; export const addTagTypes = [ - 'Scopes', - 'Roles', - 'Apps', - 'Users', - 'Logs', -] as const + "Scopes", + "Roles", + "Orgs", + "Apps", + "Users", + "Logs", +] as const; const injectedRtkApi = api - .enhanceEndpoints({ addTagTypes }) + .enhanceEndpoints({ + addTagTypes, + }) .injectEndpoints({ endpoints: (build) => ({ getApiV1Scopes: build.query< GetApiV1ScopesApiResponse, GetApiV1ScopesApiArg >({ - query: () => ({ url: '/api/v1/scopes' }), - providesTags: ['Scopes'], + query: () => ({ url: `/api/v1/scopes` }), + providesTags: ["Scopes"], }), postApiV1Scopes: build.mutation< PostApiV1ScopesApiResponse, PostApiV1ScopesApiArg >({ query: (queryArg) => ({ - url: '/api/v1/scopes', - method: 'POST', + url: `/api/v1/scopes`, + method: "POST", body: queryArg.postScopeReq, }), - invalidatesTags: ['Scopes'], + invalidatesTags: ["Scopes"], }), getApiV1ScopesById: build.query< GetApiV1ScopesByIdApiResponse, GetApiV1ScopesByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/scopes/${queryArg.id}` }), - providesTags: ['Scopes'], + providesTags: ["Scopes"], }), putApiV1ScopesById: build.mutation< PutApiV1ScopesByIdApiResponse, @@ -41,10 +44,10 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/scopes/${queryArg.id}`, - method: 'PUT', + method: "PUT", body: queryArg.putScopeReq, }), - invalidatesTags: ['Scopes'], + invalidatesTags: ["Scopes"], }), deleteApiV1ScopesById: build.mutation< DeleteApiV1ScopesByIdApiResponse, @@ -52,31 +55,33 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/scopes/${queryArg.id}`, - method: 'DELETE', + method: "DELETE", }), - invalidatesTags: ['Scopes'], - }), - getApiV1Roles: build.query({ - query: () => ({ url: '/api/v1/roles' }), - providesTags: ['Roles'], - }), + invalidatesTags: ["Scopes"], + }), + getApiV1Roles: build.query( + { + query: () => ({ url: `/api/v1/roles` }), + providesTags: ["Roles"], + } + ), postApiV1Roles: build.mutation< PostApiV1RolesApiResponse, PostApiV1RolesApiArg >({ query: (queryArg) => ({ - url: '/api/v1/roles', - method: 'POST', + url: `/api/v1/roles`, + method: "POST", body: queryArg.postRoleReq, }), - invalidatesTags: ['Roles'], + invalidatesTags: ["Roles"], }), getApiV1RolesById: build.query< GetApiV1RolesByIdApiResponse, GetApiV1RolesByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/roles/${queryArg.id}` }), - providesTags: ['Roles'], + providesTags: ["Roles"], }), putApiV1RolesById: build.mutation< PutApiV1RolesByIdApiResponse, @@ -84,10 +89,10 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/roles/${queryArg.id}`, - method: 'PUT', + method: "PUT", body: queryArg.putRoleReq, }), - invalidatesTags: ['Roles'], + invalidatesTags: ["Roles"], }), deleteApiV1RolesById: build.mutation< DeleteApiV1RolesByIdApiResponse, @@ -95,31 +100,74 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/roles/${queryArg.id}`, - method: 'DELETE', + method: "DELETE", + }), + invalidatesTags: ["Roles"], + }), + getApiV1Orgs: build.query({ + query: () => ({ url: `/api/v1/orgs` }), + providesTags: ["Orgs"], + }), + postApiV1Orgs: build.mutation< + PostApiV1OrgsApiResponse, + PostApiV1OrgsApiArg + >({ + query: (queryArg) => ({ + url: `/api/v1/orgs`, + method: "POST", + body: queryArg.postOrgReq, + }), + invalidatesTags: ["Orgs"], + }), + getApiV1OrgsById: build.query< + GetApiV1OrgsByIdApiResponse, + GetApiV1OrgsByIdApiArg + >({ + query: (queryArg) => ({ url: `/api/v1/orgs/${queryArg.id}` }), + providesTags: ["Orgs"], + }), + putApiV1OrgsById: build.mutation< + PutApiV1OrgsByIdApiResponse, + PutApiV1OrgsByIdApiArg + >({ + query: (queryArg) => ({ + url: `/api/v1/orgs/${queryArg.id}`, + method: "PUT", + body: queryArg.putOrgReq, }), - invalidatesTags: ['Roles'], + invalidatesTags: ["Orgs"], + }), + deleteApiV1OrgsById: build.mutation< + DeleteApiV1OrgsByIdApiResponse, + DeleteApiV1OrgsByIdApiArg + >({ + query: (queryArg) => ({ + url: `/api/v1/orgs/${queryArg.id}`, + method: "DELETE", + }), + invalidatesTags: ["Orgs"], }), getApiV1Apps: build.query({ - query: () => ({ url: '/api/v1/apps' }), - providesTags: ['Apps'], + query: () => ({ url: `/api/v1/apps` }), + providesTags: ["Apps"], }), postApiV1Apps: build.mutation< PostApiV1AppsApiResponse, PostApiV1AppsApiArg >({ query: (queryArg) => ({ - url: '/api/v1/apps', - method: 'POST', + url: `/api/v1/apps`, + method: "POST", body: queryArg.postAppReq, }), - invalidatesTags: ['Apps'], + invalidatesTags: ["Apps"], }), getApiV1AppsById: build.query< GetApiV1AppsByIdApiResponse, GetApiV1AppsByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/apps/${queryArg.id}` }), - providesTags: ['Apps'], + providesTags: ["Apps"], }), putApiV1AppsById: build.mutation< PutApiV1AppsByIdApiResponse, @@ -127,10 +175,10 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/apps/${queryArg.id}`, - method: 'PUT', + method: "PUT", body: queryArg.putAppReq, }), - invalidatesTags: ['Apps'], + invalidatesTags: ["Apps"], }), deleteApiV1AppsById: build.mutation< DeleteApiV1AppsByIdApiResponse, @@ -138,27 +186,29 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/apps/${queryArg.id}`, - method: 'DELETE', + method: "DELETE", }), - invalidatesTags: ['Apps'], - }), - getApiV1Users: build.query({ - query: (queryArg) => ({ - url: '/api/v1/users', - params: { - page_size: queryArg.pageSize, - page_number: queryArg.pageNumber, - search: queryArg.search, - }, - }), - providesTags: ['Users'], - }), + invalidatesTags: ["Apps"], + }), + getApiV1Users: build.query( + { + query: (queryArg) => ({ + url: `/api/v1/users`, + params: { + page_size: queryArg.pageSize, + page_number: queryArg.pageNumber, + search: queryArg.search, + }, + }), + providesTags: ["Users"], + } + ), getApiV1UsersByAuthId: build.query< GetApiV1UsersByAuthIdApiResponse, GetApiV1UsersByAuthIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}` }), - providesTags: ['Users'], + providesTags: ["Users"], }), putApiV1UsersByAuthId: build.mutation< PutApiV1UsersByAuthIdApiResponse, @@ -166,10 +216,10 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}`, - method: 'PUT', + method: "PUT", body: queryArg.putUserReq, }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), deleteApiV1UsersByAuthId: build.mutation< DeleteApiV1UsersByAuthIdApiResponse, @@ -177,16 +227,18 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}`, - method: 'DELETE', + method: "DELETE", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), getApiV1UsersByAuthIdLockedIps: build.query< GetApiV1UsersByAuthIdLockedIpsApiResponse, GetApiV1UsersByAuthIdLockedIpsApiArg >({ - query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/locked-ips` }), - providesTags: ['Users'], + query: (queryArg) => ({ + url: `/api/v1/users/${queryArg.authId}/locked-ips`, + }), + providesTags: ["Users"], }), deleteApiV1UsersByAuthIdLockedIps: build.mutation< DeleteApiV1UsersByAuthIdLockedIpsApiResponse, @@ -194,9 +246,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/locked-ips`, - method: 'DELETE', + method: "DELETE", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), postApiV1UsersByAuthIdVerifyEmail: build.mutation< PostApiV1UsersByAuthIdVerifyEmailApiResponse, @@ -204,16 +256,18 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/verify-email`, - method: 'POST', + method: "POST", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), getApiV1UsersByAuthIdConsentedApps: build.query< GetApiV1UsersByAuthIdConsentedAppsApiResponse, GetApiV1UsersByAuthIdConsentedAppsApiArg >({ - query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/consented-apps` }), - providesTags: ['Users'], + query: (queryArg) => ({ + url: `/api/v1/users/${queryArg.authId}/consented-apps`, + }), + providesTags: ["Users"], }), deleteApiV1UsersByAuthIdConsentedAppsAndAppId: build.mutation< DeleteApiV1UsersByAuthIdConsentedAppsAndAppIdApiResponse, @@ -221,9 +275,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/consented-apps/${queryArg.appId}`, - method: 'DELETE', + method: "DELETE", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), postApiV1UsersByAuthIdEmailMfa: build.mutation< PostApiV1UsersByAuthIdEmailMfaApiResponse, @@ -231,9 +285,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/email-mfa`, - method: 'POST', + method: "POST", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), deleteApiV1UsersByAuthIdEmailMfa: build.mutation< DeleteApiV1UsersByAuthIdEmailMfaApiResponse, @@ -241,9 +295,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/email-mfa`, - method: 'DELETE', + method: "DELETE", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), postApiV1UsersByAuthIdOtpMfa: build.mutation< PostApiV1UsersByAuthIdOtpMfaApiResponse, @@ -251,9 +305,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/otp-mfa`, - method: 'POST', + method: "POST", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), deleteApiV1UsersByAuthIdOtpMfa: build.mutation< DeleteApiV1UsersByAuthIdOtpMfaApiResponse, @@ -261,9 +315,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/otp-mfa`, - method: 'DELETE', + method: "DELETE", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), postApiV1UsersByAuthIdSmsMfa: build.mutation< PostApiV1UsersByAuthIdSmsMfaApiResponse, @@ -271,9 +325,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/sms-mfa`, - method: 'POST', + method: "POST", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), deleteApiV1UsersByAuthIdSmsMfa: build.mutation< DeleteApiV1UsersByAuthIdSmsMfaApiResponse, @@ -281,9 +335,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/sms-mfa`, - method: 'DELETE', + method: "DELETE", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), postApiV1UsersByAuthIdAccountLinkingAndLinkingAuthId: build.mutation< PostApiV1UsersByAuthIdAccountLinkingAndLinkingAuthIdApiResponse, @@ -291,9 +345,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/account-linking/${queryArg.linkingAuthId}`, - method: 'POST', + method: "POST", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), deleteApiV1UsersByAuthIdAccountLinking: build.mutation< DeleteApiV1UsersByAuthIdAccountLinkingApiResponse, @@ -301,74 +355,74 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/account-linking`, - method: 'DELETE', + method: "DELETE", }), - invalidatesTags: ['Users'], + invalidatesTags: ["Users"], }), getApiV1LogsEmail: build.query< GetApiV1LogsEmailApiResponse, GetApiV1LogsEmailApiArg >({ query: (queryArg) => ({ - url: '/api/v1/logs/email', + url: `/api/v1/logs/email`, params: { page_size: queryArg.pageSize, page_number: queryArg.pageNumber, }, }), - providesTags: ['Logs'], + providesTags: ["Logs"], }), getApiV1LogsEmailById: build.query< GetApiV1LogsEmailByIdApiResponse, GetApiV1LogsEmailByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/logs/email/${queryArg.id}` }), - providesTags: ['Logs'], + providesTags: ["Logs"], }), getApiV1LogsSms: build.query< GetApiV1LogsSmsApiResponse, GetApiV1LogsSmsApiArg >({ query: (queryArg) => ({ - url: '/api/v1/logs/sms', + url: `/api/v1/logs/sms`, params: { page_size: queryArg.pageSize, page_number: queryArg.pageNumber, }, }), - providesTags: ['Logs'], + providesTags: ["Logs"], }), getApiV1LogsSmsById: build.query< GetApiV1LogsSmsByIdApiResponse, GetApiV1LogsSmsByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/logs/sms/${queryArg.id}` }), - providesTags: ['Logs'], + providesTags: ["Logs"], }), getApiV1LogsSignIn: build.query< GetApiV1LogsSignInApiResponse, GetApiV1LogsSignInApiArg >({ query: (queryArg) => ({ - url: '/api/v1/logs/sign-in', + url: `/api/v1/logs/sign-in`, params: { page_size: queryArg.pageSize, page_number: queryArg.pageNumber, }, }), - providesTags: ['Logs'], + providesTags: ["Logs"], }), getApiV1LogsSignInById: build.query< GetApiV1LogsSignInByIdApiResponse, GetApiV1LogsSignInByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/logs/sign-in/${queryArg.id}` }), - providesTags: ['Logs'], + providesTags: ["Logs"], }), }), overrideExisting: false, - }) -export { injectedRtkApi as authApi } + }); +export { injectedRtkApi as authApi }; export type GetApiV1ScopesApiResponse = /** status 200 A list of scopes */ { scopes?: Scope[]; }; @@ -431,6 +485,37 @@ export type DeleteApiV1RolesByIdApiArg = { /** The unique ID of the role */ id: number; }; +export type GetApiV1OrgsApiResponse = /** status 200 A list of orgs */ { + orgs?: Org[]; +}; +export type GetApiV1OrgsApiArg = void; +export type PostApiV1OrgsApiResponse = /** status 201 undefined */ { + org?: Org; +}; +export type PostApiV1OrgsApiArg = { + postOrgReq: PostOrgReq; +}; +export type GetApiV1OrgsByIdApiResponse = + /** status 200 A single org object */ { + org?: Org; + }; +export type GetApiV1OrgsByIdApiArg = { + /** The unique ID of the org */ + id: number; +}; +export type PutApiV1OrgsByIdApiResponse = /** status 200 undefined */ { + org?: Org; +}; +export type PutApiV1OrgsByIdApiArg = { + /** The unique ID of the org */ + id: number; + putOrgReq: PutOrgReq; +}; +export type DeleteApiV1OrgsByIdApiResponse = unknown; +export type DeleteApiV1OrgsByIdApiArg = { + /** The unique ID of the org */ + id: number; +}; export type GetApiV1AppsApiResponse = /** status 200 A list of apps */ { apps?: App[]; }; @@ -644,7 +729,7 @@ export type Scope = { id: number; name: string; note: string; - type: 'spa' | 's2s'; + type: "spa" | "s2s"; createdAt: string; updatedAt: string; deletedAt: string | null; @@ -662,7 +747,7 @@ export type ScopeDetail = Scope & { }; export type PostScopeReq = { name: string; - type: 'spa' | 's2s'; + type: "spa" | "s2s"; note?: string; locales?: { locale: string; @@ -693,12 +778,55 @@ export type PutRoleReq = { name?: string; note?: string; }; +export type Org = { + id: number; + name: string; + companyLogoUrl: string; + fontFamily: string; + fontUrl: string; + layoutColor: string; + labelColor: string; + primaryButtonColor: string; + primaryButtonLabelColor: string; + primaryButtonBorderColor: string; + secondaryButtonColor: string; + secondaryButtonLabelColor: string; + secondaryButtonBorderColor: string; + criticalIndicatorColor: string; + emailSenderName: string; + termsLink: string; + privacyPolicyLink: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; +}; +export type PostOrgReq = { + name: string; +}; +export type PutOrgReq = { + name?: string; + companyLogoUrl?: string; + fontFamily?: string; + fontUrl?: string; + layoutColor?: string; + labelColor?: string; + primaryButtonColor?: string; + primaryButtonLabelColor?: string; + primaryButtonBorderColor?: string; + secondaryButtonColor?: string; + secondaryButtonLabelColor?: string; + secondaryButtonBorderColor?: string; + criticalIndicatorColor?: string; + emailSenderName?: string; + termsLink?: string; + privacyPolicyLink?: string; +}; export type App = { id: number; clientId: string; name: string; isActive: boolean; - type: 'spa' | 's2s'; + type: "spa" | "s2s"; secret: string; redirectUris: string[]; createdAt: string; @@ -710,7 +838,7 @@ export type AppDetail = App & { }; export type PostAppReq = { name: string; - type: 'spa' | 's2s'; + type: "spa" | "s2s"; scopes: string[]; redirectUris: string[]; }; @@ -798,6 +926,13 @@ export const { useLazyGetApiV1RolesByIdQuery, usePutApiV1RolesByIdMutation, useDeleteApiV1RolesByIdMutation, + useGetApiV1OrgsQuery, + useLazyGetApiV1OrgsQuery, + usePostApiV1OrgsMutation, + useGetApiV1OrgsByIdQuery, + useLazyGetApiV1OrgsByIdQuery, + usePutApiV1OrgsByIdMutation, + useDeleteApiV1OrgsByIdMutation, useGetApiV1AppsQuery, useLazyGetApiV1AppsQuery, usePostApiV1AppsMutation, @@ -838,4 +973,4 @@ export const { useLazyGetApiV1LogsSignInQuery, useGetApiV1LogsSignInByIdQuery, useLazyGetApiV1LogsSignInByIdQuery, -} = injectedRtkApi +} = injectedRtkApi; diff --git a/admin-panel/tools/route.ts b/admin-panel/tools/route.ts index fc946ea..0375d9f 100644 --- a/admin-panel/tools/route.ts +++ b/admin-panel/tools/route.ts @@ -4,6 +4,7 @@ export enum Internal { Roles = '/roles', Apps = '/apps', Scopes = '/scopes', + Orgs = '/orgs', Logs = '/logs', Account = '/account', } diff --git a/admin-panel/translations/en.json b/admin-panel/translations/en.json index 1f496f3..e5cd5a7 100644 --- a/admin-panel/translations/en.json +++ b/admin-panel/translations/en.json @@ -7,6 +7,7 @@ "roles": "Manage Roles", "apps": "Manage Apps", "scopes": "Manage Scopes", + "orgs": "Manage Orgs", "logs": "View Logs", "dashboard": "Dashboard", "account": "Account" @@ -126,6 +127,27 @@ "locales": "Locales", "localeNote": "This will be displayed on the user app consent page." }, + "orgs": { + "title": "Orgs", + "role": "Org", + "name": "Name", + "status": "Status", + "new": "Create an org", + "companyLogoUrl": "Logo Url", + "fontFamily": "Font Family", + "fontUrl": "Font Url", + "layoutColor": "Layout Color", + "labelColor": "Label Color", + "primaryButtonColor": "Primary Button Color", + "primaryButtonLabelColor": "Primary Button Label Color", + "primaryButtonBorderColor": "Primary Button Border Color", + "secondaryButtonColor": "Secondary Button Color", + "secondaryButtonLabelColor": "Secondary Button Label Color", + "secondaryButtonBorderColor": "Secondary Button Border Color", + "criticalIndicatorColor": "Critical Indicator Color", + "termsLink": "Terms Link", + "privacyPolicyLink": "Privacy Policy Link" + }, "logs": { "receiver": "Receiver", "success": "Success", diff --git a/docs/auth-server.md b/docs/auth-server.md index 1baf6f9..c6ac476 100644 --- a/docs/auth-server.md +++ b/docs/auth-server.md @@ -324,6 +324,10 @@ npm run prod:deploy - **Description:** If set to true, users will receive an email to verify their email address after signing up. [Email functionality setup required](#email-functionality-setup) +#### ENABLE_ORG +- **Default:** false +- **Description:** Determines if organization feature are enabled in the application. If set to true, users will have the ability to create and manage organizations through S2S api and admin panel. + ### Auth Configs #### AUTHORIZATION_CODE_EXPIRES_IN diff --git a/package-lock.json b/package-lock.json index e64769e..56e2844 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "melody-auth", - "version": "1.1.2", + "version": "1.1.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "melody-auth", - "version": "1.1.2", + "version": "1.1.4", "license": "MIT", "workspaces": [ "shared", diff --git a/server/src/__tests__/normal/org.test.ts b/server/src/__tests__/normal/org.test.ts index e7af2ce..ecd9bc9 100644 --- a/server/src/__tests__/normal/org.test.ts +++ b/server/src/__tests__/normal/org.test.ts @@ -65,6 +65,8 @@ describe( test( 'should return all orgs', async () => { + global.process.env.ENABLE_ORG = true as unknown as string + await createNewOrg() const res = await app.request( BaseRoute, @@ -75,12 +77,16 @@ describe( expect(json.orgs.length).toBe(1) expect(json).toStrictEqual({ orgs: [newOrg] }) + + global.process.env.ENABLE_ORG = false as unknown as string }, ) test( 'should return all org with read_org scope', async () => { + global.process.env.ENABLE_ORG = true as unknown as string + await attachIndividualScopes(db) await createNewOrg() const res = await app.request( @@ -99,12 +105,36 @@ describe( expect(json.orgs.length).toBe(1) expect(json).toStrictEqual({ orgs: [newOrg] }) + + global.process.env.ENABLE_ORG = false as unknown as string + }, + ) + + test( + 'should return 401 if org not enabled in config', + async () => { + await attachIndividualScopes(db) + const res = await app.request( + BaseRoute, + { + headers: { + Authorization: `Bearer ${await getS2sToken( + db, + Scope.ReadOrg, + )}`, + }, + }, + mock(db), + ) + expect(res.status).toBe(400) }, ) test( 'should return 401 without proper scope', async () => { + global.process.env.ENABLE_ORG = true as unknown as string + await attachIndividualScopes(db) const res = await app.request( BaseRoute, @@ -126,6 +156,8 @@ describe( mock(db), ) expect(res1.status).toBe(401) + + global.process.env.ENABLE_ORG = false as unknown as string }, ) }, @@ -137,6 +169,8 @@ describe( test( 'should return org by id', async () => { + global.process.env.ENABLE_ORG = true as unknown as string + await createNewOrg() const res = await app.request( `${BaseRoute}/1`, @@ -146,12 +180,16 @@ describe( const json = await res.json() expect(json).toStrictEqual({ org: newOrg }) + + global.process.env.ENABLE_ORG = false as unknown as string }, ) test( 'should return 404 when can not find org by id', async () => { + global.process.env.ENABLE_ORG = true as unknown as string + await createNewOrg() const res = await app.request( `${BaseRoute}/2`, @@ -160,6 +198,8 @@ describe( ) expect(res.status).toBe(404) + + global.process.env.ENABLE_ORG = false as unknown as string }, ) }, @@ -171,25 +211,35 @@ describe( test( 'should create org', async () => { + global.process.env.ENABLE_ORG = true as unknown as string + const res = await createNewOrg() const json = await res.json() expect(json).toStrictEqual({ org: newOrg }) + + global.process.env.ENABLE_ORG = false as unknown as string }, ) test( 'should trigger unique constraint', async () => { + global.process.env.ENABLE_ORG = true as unknown as string + await createNewOrg() const res1 = await createNewOrg() expect(res1.status).toBe(500) + + global.process.env.ENABLE_ORG = false as unknown as string }, ) test( 'should create org with write_org scope', async () => { + global.process.env.ENABLE_ORG = true as unknown as string + await attachIndividualScopes(db) const res = await createNewOrg(await getS2sToken( db, @@ -198,12 +248,16 @@ describe( const json = await res.json() expect(json).toStrictEqual({ org: newOrg }) + + global.process.env.ENABLE_ORG = false as unknown as string }, ) test( 'should return 401 without proper scope', async () => { + global.process.env.ENABLE_ORG = true as unknown as string + const res = await createNewOrg(await getS2sToken( db, Scope.ReadOrg, @@ -212,6 +266,8 @@ describe( const res1 = await createNewOrg('') expect(res1.status).toBe(401) + + global.process.env.ENABLE_ORG = false as unknown as string }, ) }, @@ -223,6 +279,8 @@ describe( test( 'should update org', async () => { + global.process.env.ENABLE_ORG = true as unknown as string + await createNewOrg() const updateObj = { name: 'test name 1' } const res = await app.request( @@ -242,6 +300,8 @@ describe( ...updateObj, }, }) + + global.process.env.ENABLE_ORG = false as unknown as string }, ) }, @@ -253,6 +313,8 @@ describe( test( 'should delete org', async () => { + global.process.env.ENABLE_ORG = true as unknown as string + await createNewOrg() const res = await app.request( `${BaseRoute}/1`, @@ -270,6 +332,8 @@ describe( mock(db), ) expect(checkRes.status).toBe(404) + + global.process.env.ENABLE_ORG = false as unknown as string }, ) }, diff --git a/server/src/__tests__/normal/other.test.tsx b/server/src/__tests__/normal/other.test.tsx index cf47a97..725e39d 100644 --- a/server/src/__tests__/normal/other.test.tsx +++ b/server/src/__tests__/normal/other.test.tsx @@ -77,6 +77,7 @@ describe( ENABLE_SMS_LOG: false, ENABLE_SIGN_IN_LOG: false, ENABLE_PASSWORD_SIGN_IN: true, + ENABLE_ORG: false, LAYOUT_COLOR: 'lightgray', LABEL_COLOR: 'black', PRIMARY_BUTTON_COLOR: 'white', diff --git a/server/src/configs/type.ts b/server/src/configs/type.ts index 6a4c79f..f75764b 100644 --- a/server/src/configs/type.ts +++ b/server/src/configs/type.ts @@ -71,6 +71,7 @@ export type Bindings = { ENABLE_LOCALE_SELECTOR: boolean; TERMS_LINK: string; PRIVACY_POLICY_LINK: string; + ENABLE_ORG: boolean; ENABLE_EMAIL_LOG: boolean; ENABLE_SMS_LOG: boolean; ENABLE_SIGN_IN_LOG: boolean; diff --git a/server/src/handlers/other.ts b/server/src/handlers/other.ts index 04252bb..17bd6ec 100644 --- a/server/src/handlers/other.ts +++ b/server/src/handlers/other.ts @@ -62,6 +62,7 @@ export const getSystemInfo = async (c: Context) => { SECONDARY_BUTTON_BORDER_COLOR: environment.SECONDARY_BUTTON_BORDER_COLOR, CRITICAL_INDICATOR_COLOR: environment.CRITICAL_INDICATOR_COLOR, ENABLE_PASSWORD_SIGN_IN: environment.ENABLE_PASSWORD_SIGN_IN, + ENABLE_ORG: environment.ENABLE_ORG } return c.json({ configs }) diff --git a/server/src/middlewares/config.ts b/server/src/middlewares/config.ts index 0262aa0..a3e01b5 100644 --- a/server/src/middlewares/config.ts +++ b/server/src/middlewares/config.ts @@ -36,6 +36,16 @@ export const enablePasswordReset = async ( await next() } +export const enableOrg = async ( + c: Context, next: Next, +) => { + const { ENABLE_ORG: enabledOrg } = env(c) + + if (!enabledOrg) throw new errorConfig.Forbidden() + + await next() +} + export const enableGoogleSignIn = async ( c: Context, next: Next, ) => { diff --git a/server/src/routes/org.tsx b/server/src/routes/org.tsx index 2dfd26b..0440ace 100644 --- a/server/src/routes/org.tsx +++ b/server/src/routes/org.tsx @@ -3,7 +3,7 @@ import { routeConfig, typeConfig, } from 'configs' import { orgHandler } from 'handlers' -import { authMiddleware } from 'middlewares' +import { authMiddleware, configMiddleware } from 'middlewares' const BaseRoute = routeConfig.InternalRoute.ApiOrgs const orgRoutes = new Hono() @@ -31,6 +31,7 @@ export default orgRoutes */ orgRoutes.get( `${BaseRoute}`, + configMiddleware.enableOrg, authMiddleware.s2sReadOrg, orgHandler.getOrgs, ) @@ -62,6 +63,7 @@ orgRoutes.get( */ orgRoutes.get( `${BaseRoute}/:id`, + configMiddleware.enableOrg, authMiddleware.s2sReadOrg, orgHandler.getOrg, ) @@ -91,6 +93,7 @@ orgRoutes.get( */ orgRoutes.post( `${BaseRoute}`, + configMiddleware.enableOrg, authMiddleware.s2sWriteOrg, orgHandler.postOrg, ) @@ -127,6 +130,7 @@ orgRoutes.post( */ orgRoutes.put( `${BaseRoute}/:id`, + configMiddleware.enableOrg, authMiddleware.s2sWriteOrg, orgHandler.putOrg, ) @@ -151,6 +155,7 @@ orgRoutes.put( */ orgRoutes.delete( `${BaseRoute}/:id`, + configMiddleware.enableOrg, authMiddleware.s2sWriteOrg, orgHandler.deleteOrg, ) diff --git a/server/src/scripts/swagger.json b/server/src/scripts/swagger.json index 10fc6c3..c60b3eb 100644 --- a/server/src/scripts/swagger.json +++ b/server/src/scripts/swagger.json @@ -426,12 +426,12 @@ "termsLink": { "type": "string", "minLength": 0, - "maxLength": 20 + "maxLength": 250 }, "privacyPolicyLink": { "type": "string", "minLength": 0, - "maxLength": 20 + "maxLength": 250 } } }, diff --git a/server/wrangler.toml b/server/wrangler.toml index 5b698b8..c365606 100644 --- a/server/wrangler.toml +++ b/server/wrangler.toml @@ -33,6 +33,7 @@ ENABLE_NAMES=true NAMES_IS_REQUIRED=false ENABLE_USER_APP_CONSENT=true ENABLE_EMAIL_VERIFICATION=true # Please set up your mailer first https://auth.valuemelody.com/auth-server.html#mailer-setup +ENABLE_ORG=false # Auth AUTHORIZATION_CODE_EXPIRES_IN=300 From 120d9000044deca439a6d7b38c4c21e161f9b13d Mon Sep 17 00:00:00 2001 From: Baozier Date: Mon, 13 Jan 2025 23:17:28 -0500 Subject: [PATCH 2/4] Add translation --- admin-panel/translations/fr.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/admin-panel/translations/fr.json b/admin-panel/translations/fr.json index 40404a9..b4378e7 100644 --- a/admin-panel/translations/fr.json +++ b/admin-panel/translations/fr.json @@ -7,6 +7,7 @@ "roles": "Gérer les rôles", "apps": "Gérer les applications", "scopes": "Gérer les portées", + "orgs": "Gérer les organisations", "logs": "Afficher les journaux", "dashboard": "Tableau de bord", "account": "Compte" @@ -126,6 +127,27 @@ "locales": "Langues", "localeNote": "Ceci sera affiché sur la page de consentement de l'application utilisateur." }, + "orgs": { + "title": "Organisations", + "role": "Organisation", + "name": "Nom", + "status": "Statut", + "new": "Créer une organisation", + "companyLogoUrl": "URL du logo", + "fontFamily": "Police d’écriture", + "fontUrl": "URL de la police", + "layoutColor": "Couleur de la mise en page", + "labelColor": "Couleur des étiquettes", + "primaryButtonColor": "Couleur du bouton principal", + "primaryButtonLabelColor": "Couleur du texte du bouton principal", + "primaryButtonBorderColor": "Couleur de la bordure du bouton principal", + "secondaryButtonColor": "Couleur du bouton secondaire", + "secondaryButtonLabelColor": "Couleur du texte du bouton secondaire", + "secondaryButtonBorderColor": "Couleur de la bordure du bouton secondaire", + "criticalIndicatorColor": "Couleur de l'indicateur critique", + "termsLink": "Lien des conditions d'utilisation", + "privacyPolicyLink": "Lien de la politique de confidentialité" + }, "logs": { "receiver": "Destinataire", "success": "Succès", From 54d753e194b28ec4d74935f1310b3e98bf656d90 Mon Sep 17 00:00:00 2001 From: Baozier Date: Mon, 13 Jan 2025 23:23:37 -0500 Subject: [PATCH 3/4] fix lint --- admin-panel/app/[lang]/dashboard/page.tsx | 2 +- admin-panel/app/[lang]/orgs/[id]/page.tsx | 2 +- admin-panel/app/[lang]/orgs/page.tsx | 6 +- admin-panel/app/[lang]/orgs/useEditOrg.tsx | 43 +++- admin-panel/services/auth/api.ts | 232 ++++++++++----------- server/src/handlers/other.ts | 2 +- server/src/routes/org.tsx | 4 +- 7 files changed, 154 insertions(+), 137 deletions(-) diff --git a/admin-panel/app/[lang]/dashboard/page.tsx b/admin-panel/app/[lang]/dashboard/page.tsx index 02be1a6..fccae46 100644 --- a/admin-panel/app/[lang]/dashboard/page.tsx +++ b/admin-panel/app/[lang]/dashboard/page.tsx @@ -50,7 +50,7 @@ const Page = () => { 'ENABLE_EMAIL_VERIFICATION', 'ENABLE_PASSWORD_RESET', 'ENABLE_USER_APP_CONSENT', - 'ENABLE_ORG' + 'ENABLE_ORG', ], }, { diff --git a/admin-panel/app/[lang]/orgs/[id]/page.tsx b/admin-panel/app/[lang]/orgs/[id]/page.tsx index 23e14ba..dc4c974 100644 --- a/admin-panel/app/[lang]/orgs/[id]/page.tsx +++ b/admin-panel/app/[lang]/orgs/[id]/page.tsx @@ -16,7 +16,7 @@ import PageTitle from 'components/PageTitle' import DeleteButton from 'components/DeleteButton' import useLocaleRouter from 'hooks/useLocaleRoute' import { - useGetApiV1OrgsByIdQuery, usePutApiV1OrgsByIdMutation, useDeleteApiV1OrgsByIdMutation + useGetApiV1OrgsByIdQuery, usePutApiV1OrgsByIdMutation, useDeleteApiV1OrgsByIdMutation, } from 'services/auth/api' const Page = () => { diff --git a/admin-panel/app/[lang]/orgs/page.tsx b/admin-panel/app/[lang]/orgs/page.tsx index ae1d4fd..90677b7 100644 --- a/admin-panel/app/[lang]/orgs/page.tsx +++ b/admin-panel/app/[lang]/orgs/page.tsx @@ -3,14 +3,12 @@ import { Table } from 'flowbite-react' import { useTranslations } from 'next-intl' import useCurrentLocale from 'hooks/useCurrentLocale' -import { - routeTool, -} from 'tools' +import { routeTool } from 'tools' import EditLink from 'components/EditLink' import PageTitle from 'components/PageTitle' import CreateButton from 'components/CreateButton' import { - useGetApiV1OrgsQuery, Org + useGetApiV1OrgsQuery, Org, } from 'services/auth/api' const Page = () => { diff --git a/admin-panel/app/[lang]/orgs/useEditOrg.tsx b/admin-panel/app/[lang]/orgs/useEditOrg.tsx index 073b4c7..09afc22 100644 --- a/admin-panel/app/[lang]/orgs/useEditOrg.tsx +++ b/admin-panel/app/[lang]/orgs/useEditOrg.tsx @@ -45,12 +45,39 @@ const useEditOrg = (org: Org | undefined) => { [org], ) - const values = { - name, companyLogoUrl, fontFamily, fontUrl, layoutColor, labelColor, - primaryButtonColor, primaryButtonLabelColor, primaryButtonBorderColor, - secondaryButtonColor, secondaryButtonLabelColor, secondaryButtonBorderColor, - criticalIndicatorColor, termsLink, privacyPolicyLink - } + const values = useMemo(() => ({ + name, + companyLogoUrl, + fontFamily, + fontUrl, + layoutColor, + labelColor, + primaryButtonColor, + primaryButtonLabelColor, + primaryButtonBorderColor, + secondaryButtonColor, + secondaryButtonLabelColor, + secondaryButtonBorderColor, + criticalIndicatorColor, + termsLink, + privacyPolicyLink, + }), [ + name, + companyLogoUrl, + fontFamily, + fontUrl, + layoutColor, + labelColor, + primaryButtonColor, + primaryButtonLabelColor, + primaryButtonBorderColor, + secondaryButtonColor, + secondaryButtonLabelColor, + secondaryButtonBorderColor, + criticalIndicatorColor, + termsLink, + privacyPolicyLink, + ]) const errors = useMemo( () => ({ name: values.name.trim().length ? undefined : t('common.fieldIsRequired') }), @@ -68,8 +95,8 @@ const useEditOrg = (org: Org | undefined) => { setCompanyLogoUrl(value as string) break case 'fontFamily': - setFontFamily(value as string) - break + setFontFamily(value as string) + break case 'fontUrl': setFontUrl(value as string) break diff --git a/admin-panel/services/auth/api.ts b/admin-panel/services/auth/api.ts index c815823..16cc186 100644 --- a/admin-panel/services/auth/api.ts +++ b/admin-panel/services/auth/api.ts @@ -1,42 +1,40 @@ -import { authApi as api } from "./"; +import { authApi as api } from './' export const addTagTypes = [ - "Scopes", - "Roles", - "Orgs", - "Apps", - "Users", - "Logs", -] as const; + 'Scopes', + 'Roles', + 'Orgs', + 'Apps', + 'Users', + 'Logs', +] as const const injectedRtkApi = api - .enhanceEndpoints({ - addTagTypes, - }) + .enhanceEndpoints({ addTagTypes }) .injectEndpoints({ endpoints: (build) => ({ getApiV1Scopes: build.query< GetApiV1ScopesApiResponse, GetApiV1ScopesApiArg >({ - query: () => ({ url: `/api/v1/scopes` }), - providesTags: ["Scopes"], + query: () => ({ url: '/api/v1/scopes' }), + providesTags: ['Scopes'], }), postApiV1Scopes: build.mutation< PostApiV1ScopesApiResponse, PostApiV1ScopesApiArg >({ query: (queryArg) => ({ - url: `/api/v1/scopes`, - method: "POST", + url: '/api/v1/scopes', + method: 'POST', body: queryArg.postScopeReq, }), - invalidatesTags: ["Scopes"], + invalidatesTags: ['Scopes'], }), getApiV1ScopesById: build.query< GetApiV1ScopesByIdApiResponse, GetApiV1ScopesByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/scopes/${queryArg.id}` }), - providesTags: ["Scopes"], + providesTags: ['Scopes'], }), putApiV1ScopesById: build.mutation< PutApiV1ScopesByIdApiResponse, @@ -44,10 +42,10 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/scopes/${queryArg.id}`, - method: "PUT", + method: 'PUT', body: queryArg.putScopeReq, }), - invalidatesTags: ["Scopes"], + invalidatesTags: ['Scopes'], }), deleteApiV1ScopesById: build.mutation< DeleteApiV1ScopesByIdApiResponse, @@ -55,33 +53,31 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/scopes/${queryArg.id}`, - method: "DELETE", + method: 'DELETE', }), - invalidatesTags: ["Scopes"], - }), - getApiV1Roles: build.query( - { - query: () => ({ url: `/api/v1/roles` }), - providesTags: ["Roles"], - } - ), + invalidatesTags: ['Scopes'], + }), + getApiV1Roles: build.query({ + query: () => ({ url: '/api/v1/roles' }), + providesTags: ['Roles'], + }), postApiV1Roles: build.mutation< PostApiV1RolesApiResponse, PostApiV1RolesApiArg >({ query: (queryArg) => ({ - url: `/api/v1/roles`, - method: "POST", + url: '/api/v1/roles', + method: 'POST', body: queryArg.postRoleReq, }), - invalidatesTags: ["Roles"], + invalidatesTags: ['Roles'], }), getApiV1RolesById: build.query< GetApiV1RolesByIdApiResponse, GetApiV1RolesByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/roles/${queryArg.id}` }), - providesTags: ["Roles"], + providesTags: ['Roles'], }), putApiV1RolesById: build.mutation< PutApiV1RolesByIdApiResponse, @@ -89,10 +85,10 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/roles/${queryArg.id}`, - method: "PUT", + method: 'PUT', body: queryArg.putRoleReq, }), - invalidatesTags: ["Roles"], + invalidatesTags: ['Roles'], }), deleteApiV1RolesById: build.mutation< DeleteApiV1RolesByIdApiResponse, @@ -100,31 +96,31 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/roles/${queryArg.id}`, - method: "DELETE", + method: 'DELETE', }), - invalidatesTags: ["Roles"], + invalidatesTags: ['Roles'], }), getApiV1Orgs: build.query({ - query: () => ({ url: `/api/v1/orgs` }), - providesTags: ["Orgs"], + query: () => ({ url: '/api/v1/orgs' }), + providesTags: ['Orgs'], }), postApiV1Orgs: build.mutation< PostApiV1OrgsApiResponse, PostApiV1OrgsApiArg >({ query: (queryArg) => ({ - url: `/api/v1/orgs`, - method: "POST", + url: '/api/v1/orgs', + method: 'POST', body: queryArg.postOrgReq, }), - invalidatesTags: ["Orgs"], + invalidatesTags: ['Orgs'], }), getApiV1OrgsById: build.query< GetApiV1OrgsByIdApiResponse, GetApiV1OrgsByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/orgs/${queryArg.id}` }), - providesTags: ["Orgs"], + providesTags: ['Orgs'], }), putApiV1OrgsById: build.mutation< PutApiV1OrgsByIdApiResponse, @@ -132,10 +128,10 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/orgs/${queryArg.id}`, - method: "PUT", + method: 'PUT', body: queryArg.putOrgReq, }), - invalidatesTags: ["Orgs"], + invalidatesTags: ['Orgs'], }), deleteApiV1OrgsById: build.mutation< DeleteApiV1OrgsByIdApiResponse, @@ -143,31 +139,31 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/orgs/${queryArg.id}`, - method: "DELETE", + method: 'DELETE', }), - invalidatesTags: ["Orgs"], + invalidatesTags: ['Orgs'], }), getApiV1Apps: build.query({ - query: () => ({ url: `/api/v1/apps` }), - providesTags: ["Apps"], + query: () => ({ url: '/api/v1/apps' }), + providesTags: ['Apps'], }), postApiV1Apps: build.mutation< PostApiV1AppsApiResponse, PostApiV1AppsApiArg >({ query: (queryArg) => ({ - url: `/api/v1/apps`, - method: "POST", + url: '/api/v1/apps', + method: 'POST', body: queryArg.postAppReq, }), - invalidatesTags: ["Apps"], + invalidatesTags: ['Apps'], }), getApiV1AppsById: build.query< GetApiV1AppsByIdApiResponse, GetApiV1AppsByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/apps/${queryArg.id}` }), - providesTags: ["Apps"], + providesTags: ['Apps'], }), putApiV1AppsById: build.mutation< PutApiV1AppsByIdApiResponse, @@ -175,10 +171,10 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/apps/${queryArg.id}`, - method: "PUT", + method: 'PUT', body: queryArg.putAppReq, }), - invalidatesTags: ["Apps"], + invalidatesTags: ['Apps'], }), deleteApiV1AppsById: build.mutation< DeleteApiV1AppsByIdApiResponse, @@ -186,29 +182,27 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/apps/${queryArg.id}`, - method: "DELETE", + method: 'DELETE', }), - invalidatesTags: ["Apps"], - }), - getApiV1Users: build.query( - { - query: (queryArg) => ({ - url: `/api/v1/users`, - params: { - page_size: queryArg.pageSize, - page_number: queryArg.pageNumber, - search: queryArg.search, - }, - }), - providesTags: ["Users"], - } - ), + invalidatesTags: ['Apps'], + }), + getApiV1Users: build.query({ + query: (queryArg) => ({ + url: '/api/v1/users', + params: { + page_size: queryArg.pageSize, + page_number: queryArg.pageNumber, + search: queryArg.search, + }, + }), + providesTags: ['Users'], + }), getApiV1UsersByAuthId: build.query< GetApiV1UsersByAuthIdApiResponse, GetApiV1UsersByAuthIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}` }), - providesTags: ["Users"], + providesTags: ['Users'], }), putApiV1UsersByAuthId: build.mutation< PutApiV1UsersByAuthIdApiResponse, @@ -216,10 +210,10 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}`, - method: "PUT", + method: 'PUT', body: queryArg.putUserReq, }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), deleteApiV1UsersByAuthId: build.mutation< DeleteApiV1UsersByAuthIdApiResponse, @@ -227,18 +221,16 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}`, - method: "DELETE", + method: 'DELETE', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), getApiV1UsersByAuthIdLockedIps: build.query< GetApiV1UsersByAuthIdLockedIpsApiResponse, GetApiV1UsersByAuthIdLockedIpsApiArg >({ - query: (queryArg) => ({ - url: `/api/v1/users/${queryArg.authId}/locked-ips`, - }), - providesTags: ["Users"], + query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/locked-ips` }), + providesTags: ['Users'], }), deleteApiV1UsersByAuthIdLockedIps: build.mutation< DeleteApiV1UsersByAuthIdLockedIpsApiResponse, @@ -246,9 +238,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/locked-ips`, - method: "DELETE", + method: 'DELETE', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), postApiV1UsersByAuthIdVerifyEmail: build.mutation< PostApiV1UsersByAuthIdVerifyEmailApiResponse, @@ -256,18 +248,16 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/verify-email`, - method: "POST", + method: 'POST', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), getApiV1UsersByAuthIdConsentedApps: build.query< GetApiV1UsersByAuthIdConsentedAppsApiResponse, GetApiV1UsersByAuthIdConsentedAppsApiArg >({ - query: (queryArg) => ({ - url: `/api/v1/users/${queryArg.authId}/consented-apps`, - }), - providesTags: ["Users"], + query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/consented-apps` }), + providesTags: ['Users'], }), deleteApiV1UsersByAuthIdConsentedAppsAndAppId: build.mutation< DeleteApiV1UsersByAuthIdConsentedAppsAndAppIdApiResponse, @@ -275,9 +265,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/consented-apps/${queryArg.appId}`, - method: "DELETE", + method: 'DELETE', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), postApiV1UsersByAuthIdEmailMfa: build.mutation< PostApiV1UsersByAuthIdEmailMfaApiResponse, @@ -285,9 +275,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/email-mfa`, - method: "POST", + method: 'POST', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), deleteApiV1UsersByAuthIdEmailMfa: build.mutation< DeleteApiV1UsersByAuthIdEmailMfaApiResponse, @@ -295,9 +285,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/email-mfa`, - method: "DELETE", + method: 'DELETE', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), postApiV1UsersByAuthIdOtpMfa: build.mutation< PostApiV1UsersByAuthIdOtpMfaApiResponse, @@ -305,9 +295,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/otp-mfa`, - method: "POST", + method: 'POST', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), deleteApiV1UsersByAuthIdOtpMfa: build.mutation< DeleteApiV1UsersByAuthIdOtpMfaApiResponse, @@ -315,9 +305,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/otp-mfa`, - method: "DELETE", + method: 'DELETE', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), postApiV1UsersByAuthIdSmsMfa: build.mutation< PostApiV1UsersByAuthIdSmsMfaApiResponse, @@ -325,9 +315,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/sms-mfa`, - method: "POST", + method: 'POST', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), deleteApiV1UsersByAuthIdSmsMfa: build.mutation< DeleteApiV1UsersByAuthIdSmsMfaApiResponse, @@ -335,9 +325,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/sms-mfa`, - method: "DELETE", + method: 'DELETE', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), postApiV1UsersByAuthIdAccountLinkingAndLinkingAuthId: build.mutation< PostApiV1UsersByAuthIdAccountLinkingAndLinkingAuthIdApiResponse, @@ -345,9 +335,9 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/account-linking/${queryArg.linkingAuthId}`, - method: "POST", + method: 'POST', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), deleteApiV1UsersByAuthIdAccountLinking: build.mutation< DeleteApiV1UsersByAuthIdAccountLinkingApiResponse, @@ -355,74 +345,74 @@ const injectedRtkApi = api >({ query: (queryArg) => ({ url: `/api/v1/users/${queryArg.authId}/account-linking`, - method: "DELETE", + method: 'DELETE', }), - invalidatesTags: ["Users"], + invalidatesTags: ['Users'], }), getApiV1LogsEmail: build.query< GetApiV1LogsEmailApiResponse, GetApiV1LogsEmailApiArg >({ query: (queryArg) => ({ - url: `/api/v1/logs/email`, + url: '/api/v1/logs/email', params: { page_size: queryArg.pageSize, page_number: queryArg.pageNumber, }, }), - providesTags: ["Logs"], + providesTags: ['Logs'], }), getApiV1LogsEmailById: build.query< GetApiV1LogsEmailByIdApiResponse, GetApiV1LogsEmailByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/logs/email/${queryArg.id}` }), - providesTags: ["Logs"], + providesTags: ['Logs'], }), getApiV1LogsSms: build.query< GetApiV1LogsSmsApiResponse, GetApiV1LogsSmsApiArg >({ query: (queryArg) => ({ - url: `/api/v1/logs/sms`, + url: '/api/v1/logs/sms', params: { page_size: queryArg.pageSize, page_number: queryArg.pageNumber, }, }), - providesTags: ["Logs"], + providesTags: ['Logs'], }), getApiV1LogsSmsById: build.query< GetApiV1LogsSmsByIdApiResponse, GetApiV1LogsSmsByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/logs/sms/${queryArg.id}` }), - providesTags: ["Logs"], + providesTags: ['Logs'], }), getApiV1LogsSignIn: build.query< GetApiV1LogsSignInApiResponse, GetApiV1LogsSignInApiArg >({ query: (queryArg) => ({ - url: `/api/v1/logs/sign-in`, + url: '/api/v1/logs/sign-in', params: { page_size: queryArg.pageSize, page_number: queryArg.pageNumber, }, }), - providesTags: ["Logs"], + providesTags: ['Logs'], }), getApiV1LogsSignInById: build.query< GetApiV1LogsSignInByIdApiResponse, GetApiV1LogsSignInByIdApiArg >({ query: (queryArg) => ({ url: `/api/v1/logs/sign-in/${queryArg.id}` }), - providesTags: ["Logs"], + providesTags: ['Logs'], }), }), overrideExisting: false, - }); -export { injectedRtkApi as authApi }; + }) +export { injectedRtkApi as authApi } export type GetApiV1ScopesApiResponse = /** status 200 A list of scopes */ { scopes?: Scope[]; }; @@ -729,7 +719,7 @@ export type Scope = { id: number; name: string; note: string; - type: "spa" | "s2s"; + type: 'spa' | 's2s'; createdAt: string; updatedAt: string; deletedAt: string | null; @@ -747,7 +737,7 @@ export type ScopeDetail = Scope & { }; export type PostScopeReq = { name: string; - type: "spa" | "s2s"; + type: 'spa' | 's2s'; note?: string; locales?: { locale: string; @@ -826,7 +816,7 @@ export type App = { clientId: string; name: string; isActive: boolean; - type: "spa" | "s2s"; + type: 'spa' | 's2s'; secret: string; redirectUris: string[]; createdAt: string; @@ -838,7 +828,7 @@ export type AppDetail = App & { }; export type PostAppReq = { name: string; - type: "spa" | "s2s"; + type: 'spa' | 's2s'; scopes: string[]; redirectUris: string[]; }; @@ -973,4 +963,4 @@ export const { useLazyGetApiV1LogsSignInQuery, useGetApiV1LogsSignInByIdQuery, useLazyGetApiV1LogsSignInByIdQuery, -} = injectedRtkApi; +} = injectedRtkApi diff --git a/server/src/handlers/other.ts b/server/src/handlers/other.ts index 17bd6ec..6bbe955 100644 --- a/server/src/handlers/other.ts +++ b/server/src/handlers/other.ts @@ -62,7 +62,7 @@ export const getSystemInfo = async (c: Context) => { SECONDARY_BUTTON_BORDER_COLOR: environment.SECONDARY_BUTTON_BORDER_COLOR, CRITICAL_INDICATOR_COLOR: environment.CRITICAL_INDICATOR_COLOR, ENABLE_PASSWORD_SIGN_IN: environment.ENABLE_PASSWORD_SIGN_IN, - ENABLE_ORG: environment.ENABLE_ORG + ENABLE_ORG: environment.ENABLE_ORG, } return c.json({ configs }) diff --git a/server/src/routes/org.tsx b/server/src/routes/org.tsx index 0440ace..946673c 100644 --- a/server/src/routes/org.tsx +++ b/server/src/routes/org.tsx @@ -3,7 +3,9 @@ import { routeConfig, typeConfig, } from 'configs' import { orgHandler } from 'handlers' -import { authMiddleware, configMiddleware } from 'middlewares' +import { + authMiddleware, configMiddleware, +} from 'middlewares' const BaseRoute = routeConfig.InternalRoute.ApiOrgs const orgRoutes = new Hono() From e85a83a82ce2728cf5e4c13ea4bf042c0409b68e Mon Sep 17 00:00:00 2001 From: Baozier Date: Mon, 13 Jan 2025 23:25:32 -0500 Subject: [PATCH 4/4] fix lint --- admin-panel/app/[lang]/orgs/useEditOrg.tsx | 69 +++++++++++----------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/admin-panel/app/[lang]/orgs/useEditOrg.tsx b/admin-panel/app/[lang]/orgs/useEditOrg.tsx index 09afc22..f4819f6 100644 --- a/admin-panel/app/[lang]/orgs/useEditOrg.tsx +++ b/admin-panel/app/[lang]/orgs/useEditOrg.tsx @@ -45,39 +45,42 @@ const useEditOrg = (org: Org | undefined) => { [org], ) - const values = useMemo(() => ({ - name, - companyLogoUrl, - fontFamily, - fontUrl, - layoutColor, - labelColor, - primaryButtonColor, - primaryButtonLabelColor, - primaryButtonBorderColor, - secondaryButtonColor, - secondaryButtonLabelColor, - secondaryButtonBorderColor, - criticalIndicatorColor, - termsLink, - privacyPolicyLink, - }), [ - name, - companyLogoUrl, - fontFamily, - fontUrl, - layoutColor, - labelColor, - primaryButtonColor, - primaryButtonLabelColor, - primaryButtonBorderColor, - secondaryButtonColor, - secondaryButtonLabelColor, - secondaryButtonBorderColor, - criticalIndicatorColor, - termsLink, - privacyPolicyLink, - ]) + const values = useMemo( + () => ({ + name, + companyLogoUrl, + fontFamily, + fontUrl, + layoutColor, + labelColor, + primaryButtonColor, + primaryButtonLabelColor, + primaryButtonBorderColor, + secondaryButtonColor, + secondaryButtonLabelColor, + secondaryButtonBorderColor, + criticalIndicatorColor, + termsLink, + privacyPolicyLink, + }), + [ + name, + companyLogoUrl, + fontFamily, + fontUrl, + layoutColor, + labelColor, + primaryButtonColor, + primaryButtonLabelColor, + primaryButtonBorderColor, + secondaryButtonColor, + secondaryButtonLabelColor, + secondaryButtonBorderColor, + criticalIndicatorColor, + termsLink, + privacyPolicyLink, + ], + ) const errors = useMemo( () => ({ name: values.name.trim().length ? undefined : t('common.fieldIsRequired') }),