Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix login issue #9012

Merged
merged 6 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,26 @@ import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/acti
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-ui';

export const RecordActionMenuEntriesSetter = () => {
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataIdComponentState,
);
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);

if (!isDefined(contextStoreCurrentObjectMetadataId)) {
const objectMetadataItem = objectMetadataItems.find(
(item) => item.id === contextStoreCurrentObjectMetadataId,
);

if (
!isDefined(contextStoreCurrentObjectMetadataId) ||
!isDefined(objectMetadataItem)
) {
return null;
}

Expand Down
5 changes: 3 additions & 2 deletions packages/twenty-front/src/modules/auth/components/Logo.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import styled from '@emotion/styled';
import { getImageAbsoluteURI, isDefined } from 'twenty-ui';
import { isNonEmptyString } from '@sniptt/guards';
import { getImageAbsoluteURI } from 'twenty-ui';

type LogoProps = {
primaryLogo?: string | null;
Expand Down Expand Up @@ -48,7 +49,7 @@ export const Logo = (props: LogoProps) => {
const primaryLogoUrl = getImageAbsoluteURI(
props.primaryLogo ?? defaultPrimaryLogoUrl,
);
Comment on lines 49 to 51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: check if primaryLogo could also be an empty string - may need similar validation

const secondaryLogoUrl = isDefined(props.secondaryLogo)
const secondaryLogoUrl = isNonEmptyString(props.secondaryLogo)
? getImageAbsoluteURI(props.secondaryLogo)
: null;

Expand Down
2 changes: 1 addition & 1 deletion packages/twenty-front/src/modules/auth/hooks/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ import { getTimeFormatFromWorkspaceTimeFormat } from '@/localization/utils/getTi
import { currentUserState } from '../states/currentUserState';
import { tokenPairState } from '../states/tokenPairState';

import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
import { useIsCurrentLocationOnAWorkspaceSubdomain } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspaceSubdomain';
import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
import { useReadWorkspaceSubdomainFromCurrentLocation } from '@/domain-manager/hooks/useReadWorkspaceSubdomainFromCurrentLocation';
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,59 +1,35 @@
import { useEffect } from 'react';
import { useRecoilCallback, useRecoilValue } from 'recoil';
import { useRecoilValue } from 'recoil';

import { useIsLogged } from '@/auth/hooks/useIsLogged';
import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useLoadMockedObjectMetadataItems } from '@/object-metadata/hooks/useLoadMockedObjectMetadataItems';
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: typo in import path - 'useRefreshObjectMetadataItem' should be 'useRefreshObjectMetadataItems' to match the actual hook name

import { WorkspaceActivationStatus } from '~/generated/graphql';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';

export const ObjectMetadataItemsLoadEffect = () => {
const currentUser = useRecoilValue(currentUserState);
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const isLoggedIn = useIsLogged();

const {
objectMetadataItems: newObjectMetadataItems,
loading: isObjectMetadataLoading,
} = useFindManyObjectMetadataItems({
skip: !isLoggedIn,
});

const updateObjectMetadataItems = useRecoilCallback(
({ set, snapshot }) =>
() => {
const toSetObjectMetadataItems =
isUndefinedOrNull(currentUser) ||
currentWorkspace?.activationStatus !==
WorkspaceActivationStatus.Active
? generatedMockObjectMetadataItems
: newObjectMetadataItems;

if (
!isObjectMetadataLoading &&
!isDeeplyEqual(
snapshot.getLoadable(objectMetadataItemsState).getValue(),
toSetObjectMetadataItems,
)
) {
set(objectMetadataItemsState, toSetObjectMetadataItems);
}
},
[
currentUser,
currentWorkspace?.activationStatus,
isObjectMetadataLoading,
newObjectMetadataItems,
],
);
const { refreshObjectMetadataItems } = useRefreshObjectMetadataItems();
const { loadMockedObjectMetadataItems } = useLoadMockedObjectMetadataItems();

useEffect(() => {
updateObjectMetadataItems();
}, [updateObjectMetadataItems]);
if (
isUndefinedOrNull(currentUser) ||
currentWorkspace?.activationStatus !== WorkspaceActivationStatus.Active
) {
loadMockedObjectMetadataItems();
} else {
refreshObjectMetadataItems();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: refreshObjectMetadataItems is an async function but the await is missing here

}
}, [
currentUser,
currentWorkspace?.activationStatus,
loadMockedObjectMetadataItems,
refreshObjectMetadataItems,
]);

return <></>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useRecoilCallback } from 'recoil';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';

export const useLoadMockedObjectMetadataItems = () => {
const loadMockedObjectMetadataItems = useRecoilCallback(
({ set, snapshot }) =>
() => {
if (
!isDeeplyEqual(
snapshot.getLoadable(objectMetadataItemsState).getValue(),
generatedMockObjectMetadataItems,
Comment on lines +12 to +13
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: getLoadable().getValue() may throw if atom is in error state. Consider using try/catch.

)
) {
set(objectMetadataItemsState, generatedMockObjectMetadataItems);
}
},
[],
);
return {
loadMockedObjectMetadataItems,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries';
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '@/object-metadata/utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems';
import { useRecoilCallback } from 'recoil';
import { ObjectMetadataItemsQuery } from '~/generated-metadata/graphql';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';

export const useRefreshObjectMetadataItems = () => {
const client = useApolloMetadataClient();

const refreshObjectMetadataItems = async () => {
const result = await client.query<ObjectMetadataItemsQuery>({
query: FIND_MANY_OBJECT_METADATA_ITEMS,
variables: {},
});
Comment on lines +14 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Missing error handling for failed GraphQL queries. Add try/catch block.

Comment on lines +14 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding fetchPolicy to prevent stale cache data during refresh


const objectMetadataItems =
mapPaginatedObjectMetadataItemsToObjectMetadataItems({
pagedObjectMetadataItems: result.data,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: result.data could be undefined here, causing runtime error

});

replaceObjectMetadataItemIfDifferent(objectMetadataItems);
};

const replaceObjectMetadataItemIfDifferent = useRecoilCallback(
({ set, snapshot }) =>
(toSetObjectMetadataItems: ObjectMetadataItem[]) => {
if (
!isDeeplyEqual(
snapshot.getLoadable(objectMetadataItemsState).getValue(),
toSetObjectMetadataItems,
)
) {
set(objectMetadataItemsState, toSetObjectMetadataItems);
}
},
[],
);
return {
refreshObjectMetadataItems,
};
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { isNonEmptyString } from '@sniptt/guards';
import React from 'react';
import {
Button,
Expand Down Expand Up @@ -115,7 +116,9 @@ export const ImageInput = ({
hiddenFileInput.current?.click();
};

const pictureURI = isDefined(picture) ? getImageAbsoluteURI(picture) : null;
const pictureURI = isNonEmptyString(picture)
? getImageAbsoluteURI(picture)
: null;

return (
<StyledContainer className={className}>
Expand Down
2 changes: 1 addition & 1 deletion packages/twenty-front/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export default defineConfig(({ command, mode }) => {
envPrefix: 'REACT_APP_',

define: {
'process.env': {
_env_: {
REACT_APP_SERVER_BASE_URL,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class FileController {
@Req() req: Request,
) {
const folderPath = checkFilePath(params[0]);

const filename = checkFilename(params['filename']);

const workspaceId = (req as any)?.workspaceId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Unsafe type assertion. Consider proper typing of the Request object or middleware to ensure workspaceId exists

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,35 @@ import { FileUpload, GraphQLUpload } from 'graphql-upload';

import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';

import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
import { FileService } from 'src/engine/core-modules/file/services/file.service';
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
import { User } from 'src/engine/core-modules/user/user.entity';
import { ActivateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-input';
import { ActivateWorkspaceOutput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-output';
import { PublicWorkspaceDataOutput } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output';
import { UpdateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/update-workspace-input';
import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace';
import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspaceGraphqlApiExceptionHandler';
import {
WorkspaceException,
WorkspaceExceptionCode,
} from 'src/engine/core-modules/workspace/workspace.exception';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
import { OriginHeader } from 'src/engine/decorators/auth/origin-header.decorator';
import { DemoEnvGuard } from 'src/engine/guards/demo.env.guard';
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { assert } from 'src/utils/assert';
import { isDefined } from 'src/utils/is-defined';
import { streamToBuffer } from 'src/utils/stream-to-buffer';
import {
WorkspaceException,
WorkspaceExceptionCode,
} from 'src/engine/core-modules/workspace/workspace.exception';
import { PublicWorkspaceDataOutput } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output';
import { ActivateWorkspaceOutput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-output';
import { OriginHeader } from 'src/engine/decorators/auth/origin-header.decorator';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace';
import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspaceGraphqlApiExceptionHandler';

import { Workspace } from './workspace.entity';

Expand Down Expand Up @@ -193,9 +193,23 @@ export class WorkspaceResolver {
),
);

let workspaceLogoWithToken = '';

if (workspace.logo) {
try {
const workspaceLogoToken = await this.fileService.encodeFileToken({
workspaceId: workspace.id,
});

workspaceLogoWithToken = `${workspace.logo}?token=${workspaceLogoToken}`;
} catch (e) {
workspaceLogoWithToken = workspace.logo;
}
Comment on lines +205 to +207
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider logging the error before falling back to raw logo URL

}

return {
id: workspace.id,
logo: workspace.logo,
logo: workspaceLogoWithToken,
displayName: workspace.displayName,
subdomain: workspace.subdomain,
authProviders: getAuthProvidersByWorkspace(workspace),
Expand Down
Loading