diff --git a/keyserver/src/creators/day-creator.js b/keyserver/src/creators/day-creator.js --- a/keyserver/src/creators/day-creator.js +++ b/keyserver/src/creators/day-creator.js @@ -3,9 +3,11 @@ import { ServerError } from 'lib/utils/errors.js'; import createIDs from './id-creator.js'; -import { dbQuery, SQL } from '../database/database.js'; - -const MYSQL_DUPLICATE_ENTRY_FOR_KEY_ERROR_CODE = 1062; +import { + dbQuery, + MYSQL_DUPLICATE_ENTRY_FOR_KEY_ERROR_CODE, + SQL, +} from '../database/database.js'; async function fetchOrCreateDayID( threadID: string, diff --git a/keyserver/src/creators/invite-link-creator.js b/keyserver/src/creators/invite-link-creator.js --- a/keyserver/src/creators/invite-link-creator.js +++ b/keyserver/src/creators/invite-link-creator.js @@ -8,7 +8,11 @@ import { ServerError } from 'lib/utils/errors.js'; import createIDs from './id-creator.js'; -import { dbQuery, SQL } from '../database/database.js'; +import { + dbQuery, + MYSQL_DUPLICATE_ENTRY_FOR_KEY_ERROR_CODE, + SQL, +} from '../database/database.js'; import { fetchPrimaryInviteLinks } from '../fetchers/link-fetchers.js'; import { fetchServerThreadInfos } from '../fetchers/thread-fetchers.js'; import { checkThreadPermission } from '../fetchers/thread-permission-fetchers.js'; @@ -21,7 +25,7 @@ request: CreateOrUpdatePublicLinkRequest, ): Promise { if (!secretRegex.test(request.name)) { - throw new ServerError('invalid_parameters'); + throw new ServerError('invalid_characters'); } const permissionPromise = checkThreadPermission( @@ -64,7 +68,10 @@ `; try { await dbQuery(query); - } catch { + } catch (e) { + if (e.errno === MYSQL_DUPLICATE_ENTRY_FOR_KEY_ERROR_CODE) { + throw new ServerError('already_in_use'); + } throw new ServerError('invalid_parameters'); } return { @@ -92,17 +99,21 @@ ) `; let result = null; + const deleteIDs = SQL` + DELETE FROM ids + WHERE id = ${id} + `; try { result = (await dbQuery(createLinkQuery))[0]; - } catch { + } catch (e) { + await dbQuery(deleteIDs); + if (e.errno === MYSQL_DUPLICATE_ENTRY_FOR_KEY_ERROR_CODE) { + throw new ServerError('already_in_use'); + } throw new ServerError('invalid_parameters'); } if (result.affectedRows === 0) { - const deleteIDs = SQL` - DELETE FROM ids - WHERE id = ${id} - `; await dbQuery(deleteIDs); throw new ServerError('invalid_parameters'); } diff --git a/keyserver/src/database/database.js b/keyserver/src/database/database.js --- a/keyserver/src/database/database.js +++ b/keyserver/src/database/database.js @@ -13,6 +13,8 @@ const SQLStatement: SQLStatementType = SQL.SQLStatement; +const MYSQL_DUPLICATE_ENTRY_FOR_KEY_ERROR_CODE = 1062; + let migrationConnection; async function getMigrationConnection() { if (migrationConnection) { @@ -203,4 +205,5 @@ setConnectionContext, dbQuery, rawSQL, + MYSQL_DUPLICATE_ENTRY_FOR_KEY_ERROR_CODE, }; diff --git a/lib/shared/invite-links.js b/lib/shared/invite-links.js new file mode 100644 --- /dev/null +++ b/lib/shared/invite-links.js @@ -0,0 +1,11 @@ +// @flow + +const inviteLinkErrorMessages = { + invalid_characters: 'Link cannot contain any spaces or special characters.', + offensive_words: 'No offensive or abusive words allowed.', + already_in_use: 'Public link URL already in use.', +}; + +const defaultErrorMessage = 'Unknown error.'; + +export { inviteLinkErrorMessages, defaultErrorMessage }; diff --git a/native/invite-links/manage-public-link-screen.react.js b/native/invite-links/manage-public-link-screen.react.js --- a/native/invite-links/manage-public-link-screen.react.js +++ b/native/invite-links/manage-public-link-screen.react.js @@ -6,6 +6,10 @@ import { inviteLinkUrl } from 'lib/facts/links.js'; import { useInviteLinksActions } from 'lib/hooks/invite-links.js'; import { primaryInviteLinksSelector } from 'lib/selectors/invite-links-selectors.js'; +import { + defaultErrorMessage, + inviteLinkErrorMessages, +} from 'lib/shared/invite-links.js'; import type { ThreadInfo } from 'lib/types/thread-types.js'; import Button from '../components/button.react.js'; @@ -40,7 +44,11 @@ let errorComponent = null; if (error) { - errorComponent = {error}; + errorComponent = ( + + {inviteLinkErrorMessages[error] ?? defaultErrorMessage} + + ); } const onDisableButtonClick = React.useCallback(() => { diff --git a/web/invite-links/manage/edit-link-modal.react.js b/web/invite-links/manage/edit-link-modal.react.js --- a/web/invite-links/manage/edit-link-modal.react.js +++ b/web/invite-links/manage/edit-link-modal.react.js @@ -6,6 +6,10 @@ import { useModalContext } from 'lib/components/modal-provider.react.js'; import { inviteLinkUrl } from 'lib/facts/links.js'; import { useInviteLinksActions } from 'lib/hooks/invite-links.js'; +import { + defaultErrorMessage, + inviteLinkErrorMessages, +} from 'lib/shared/invite-links.js'; import type { InviteLink } from 'lib/types/link-types.js'; import type { ThreadInfo } from 'lib/types/thread-types.js'; @@ -40,7 +44,11 @@ let errorComponent = null; if (error) { - errorComponent =
{error}
; + errorComponent = ( +
+ {inviteLinkErrorMessages[error] ?? defaultErrorMessage} +
+ ); } let disableLinkComponent = null;