Skip to content

emoji suggestions - #39

Closed
PrivateGER wants to merge 31 commits into
developfrom
feat/emoji-suggestions
Closed

emoji suggestions#39
PrivateGER wants to merge 31 commits into
developfrom
feat/emoji-suggestions

Conversation

@PrivateGER

@PrivateGER PrivateGER commented Aug 12, 2026

Copy link
Copy Markdown
Owner

What

Why

Additional info (optional)

Checklist

  • Read the contribution guide
  • Test working in a local environment
  • (If needed) Add story of storybook
  • (If needed) Update CHANGELOG.md
  • (If possible) Add tests

Summary by CodeRabbit

  • New Features
    • Users can propose custom emojis with images, metadata, aliases, licenses, and visibility settings.
    • Added a dedicated emoji suggestions page to review pending submissions, track status, and cancel personal suggestions.
    • Moderators can browse, accept, or reject emoji suggestions.
    • Added navigation links from the emoji page and tools menu.
    • Added API support and English localization for emoji suggestion workflows.

PrivateGER and others added 30 commits June 12, 2026 23:11
i/update wrote to the user_profile table but only emitted 'userUpdated',
which purges userByIdCache alone. The userProfileCache entry (populated
at the start of the same request) was never invalidated - not even in
the local process - so the endpoint returned stale profile data and all
workers kept serving old settings until the 30-minute cache lifetime
expired. Every other profile-writing call site already emits
'updateUserProfile'; this was the one exception.

This also un-breaks federation of profile-only changes: the stale
re-fetch made profileNeedsPublishing() compare the cached object against
itself, so bio/location/birthday/fields edits were never delivered to
followers unless a user-table field changed too.

Also:
- compare 'fields' structurally in profileNeedsPublishing; Array.join
  rendered every element as "[object Object]" so changes were invisible
- emit 'updateUserProfile' before packing the response in i/update-email
  (and include the email keys), so the response reflects the new data
- correct a backwards comment in CacheService.onProfileEvent

https://claude.ai/code/session_01AB5H3dSqWnemYD2EjYRZkp
The randomized refresh delay was written as 10_1000 (101,000 ms), an
apparent typo for 10_000. Stale cached instance meta could persist for
over a minute and a half after page load before the async refresh ran.

https://claude.ai/code/session_01AB5H3dSqWnemYD2EjYRZkp
The "active user" refactor (126d7e2) made isActiveLocalUser() require
user.approved unconditionally, while its sibling assertActiveLocalUser()
and the signin check only enforce approval when
meta.approvalRequiredForSignup is enabled.

Local accounts created before the approval-signup migration
(1697580470000) default to approved=false and were never backfilled, so
on instances that don't require approval, every such account started
returning 404 from /@user.atom, /@user.rss, and /@user.json (and other
isActiveUser-gated routes like WebFinger and user SSR).

Align isActiveLocalUser() with assertActiveLocalUser(): only treat an
unapproved user as inactive when the instance actually requires
approval for signup.

https://claude.ai/code/session_01ALjqseHZ7wvaepdXwBko4d
Both endpoints wrote to the users table but neither injected
InternalEventService nor emitted an invalidation event, leaving
userByIdCache stale for up to 5 minutes after a moderator action.

Also fix a pre-existing missing await on moderationLogService.log() in
unset-user-banner, which could silently swallow audit log write errors.
… activities

attachLdSignature() called this.jsonLdService.signRsaSignature2017(...)
directly, but JsonLdService only exposes use(), which returns the JsonLd
instance that actually implements signRsaSignature2017. This caused
"this.jsonLdService.signRsaSignature2017 is not a function" when signing
activities delivered to relays.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JzqoS7cjvodFiWW6qZxyXL
Fix jsonLdService method call in ApRendererService
Fixes all findings from the Lua MRF review: P0 wasm stack corruption,
decision loss on snapshot failure, unsanitized rewrites, missing filter
validation, DI singleton, structured source errors, moderation logs,
policy caching, unlist/host-prefix bugs, typing and response schemas.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds emoji suggestion persistence, moderation services, six API endpoints, Misskey client types, frontend submission and review pages, navigation, routing, and English localization.

Changes

Emoji suggestions

Layer / File(s) Summary
Suggestion storage and contracts
packages/backend/migration/*, packages/backend/src/models/*, packages/backend/src/misc/json-schema.ts, packages/backend/src/server/api/emoji-suggestion.ts, packages/misskey-js/src/*
Adds the emoji_suggestion table, TypeORM entity, repository, packed schema, API schemas, endpoint types, and EmojiSuggestion client type.
Suggestion service workflows
packages/backend/src/core/EmojiSuggestionService.ts, packages/backend/src/core/entities/EmojiSuggestionEntityService.ts, packages/backend/src/core/CoreModule.ts, packages/backend/test/unit/EmojiSuggestionService.ts
Adds creation, acceptance, cancellation, rejection, entity packing, dependency injection, module exports, and tests for concurrency and rollback behavior.
Suggestion API endpoints
packages/backend/src/server/api/endpoints/emoji-suggestions/*, packages/backend/src/server/api/endpoints/admin/emoji-suggestions/*, packages/backend/src/server/api/endpoint-list.ts
Adds authenticated user endpoints and moderator endpoints for listing and processing suggestions.
Suggestion client experience
packages/frontend/src/pages/emoji-suggestions.vue, packages/frontend/src/pages/emoji-edit-dialog.vue, packages/frontend/src/pages/about.emojis.vue, packages/frontend/src/router.definition.ts, packages/frontend/src/ui/_common_/common.ts, locales/index.d.ts, sharkey-locales/en-US.yml
Adds suggestion submission and review UI, navigation, routing, localized labels, and confirmation messages.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  actor Proposer
  participant EmojiSuggestionsPage
  participant EmojiSuggestionAPI
  participant EmojiSuggestionService
  actor Moderator
  participant EmojiEntityService

  Proposer->>EmojiSuggestionsPage: Submit emoji suggestion
  EmojiSuggestionsPage->>EmojiSuggestionAPI: Call emoji-suggestions/create
  EmojiSuggestionAPI->>EmojiSuggestionService: Validate and store suggestion
  EmojiSuggestionService-->>EmojiSuggestionsPage: Return packed suggestion
  Moderator->>EmojiSuggestionsPage: Accept suggestion
  EmojiSuggestionsPage->>EmojiSuggestionAPI: Call admin/emoji-suggestions/accept
  EmojiSuggestionAPI->>EmojiSuggestionService: Claim and create emoji
  EmojiSuggestionService->>EmojiEntityService: Create accepted emoji
  EmojiEntityService-->>Moderator: Return packed emoji
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description contains only the template and does not explain the changes, motivation, or testing. Complete the What and Why sections, and add relevant testing information and checklist updates.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding emoji suggestion functionality.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/emoji-suggestions

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/frontend/src/pages/about.emojis.vue

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

packages/frontend/src/pages/emoji-edit-dialog.vue

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

packages/frontend/src/pages/emoji-suggestions.vue

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

  • 2 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d514a6d52b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +134 to +138
const claimed = await this.emojiSuggestionsRepository.delete({
id: suggestion.id,
userId: suggestion.userId,
fileId: suggestion.fileId,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep the suggestion row while acceptance is in progress

Deleting the row here releases the unique name constraint before the file copy and emoji creation finish. If another user submits the same name during that interval, the new suggestion succeeds; a successful acceptance then leaves that duplicate pending even though the emoji now exists, while a failed acceptance cannot restore the original because restoreSuggestion() hits the new row's unique constraint. Use a durable claimed/status state that continues to reserve the name until acceptance commits or rolls back.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
packages/backend/test/unit/EmojiSuggestionService.ts (1)

89-239: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tests for the remaining decision branches.

The suite covers the concurrency paths well. These branches remain untested:

  • unsupportedFileType and duplicateName in create.
  • tooManyPendingSuggestions at the MAX_PENDING_EMOJI_SUGGESTIONS boundary.
  • NFC normalization of name, category, and aliases.
  • cancel for the owner and for a non-owner, which asserts the userId scoping in the delete criteria.
  • reject.

Do you want me to generate these tests?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/test/unit/EmojiSuggestionService.ts` around lines 89 - 239,
Add unit tests in the EmojiSuggestionService suite for the remaining decision
branches: verify create returns unsupportedFileType and duplicateName, enforces
tooManyPendingSuggestions at MAX_PENDING_EMOJI_SUGGESTIONS, and NFC-normalizes
name, category, and aliases; also cover cancel success for the owner and failure
for a non-owner with userId-scoped delete criteria, plus the reject flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/backend/migration/1786507200000-EmojiSuggestion.js`:
- Around line 15-17: Add the required semicolon to the name property declaration
in the EmojiSuggestion1786507200000 class, satisfying the `@stylistic/semi` lint
rule without changing its value or surrounding migration logic.

In `@packages/backend/src/core/EmojiSuggestionService.ts`:
- Around line 132-153: Replace the delete-then-restore flow in the suggestion
acceptance path with durable status-based claiming: add and use a
pending/accepted/rejected status, conditionally update the row from pending when
claiming, and retain it until emoji creation succeeds. Update the success and
rejection paths to persist the corresponding status instead of deleting the
suggestion, preserving the audit record and avoiding reliance on
restoreSuggestion after crashes or failed compensation.
- Around line 203-207: Update the cleanup flow around driveService.deleteFile
and restoreSuggestion so cleanup failures are caught, logged through an injected
logger, and not rethrown, preserving the original error for propagation at Line
213. Add the logger dependency using the service’s existing dependency-injection
conventions and log failures from both cleanup operations with relevant context.
- Around line 81-91: Update the duplicate check in EmojiSuggestionService using
emojiSuggestionsRepository.exists so the name condition is evaluated globally
rather than combined with userId. Preserve the fileId condition for
file-specific duplicates and continue returning duplicateSuggestion when either
check matches.

In `@packages/backend/src/server/api/endpoints/emoji-suggestions/create.ts`:
- Around line 36-45: Update EmojiSuggestionService.create to enforce the
per-user pending-suggestion limit atomically: perform the count check and
suggestion insert within the same transaction while locking the relevant
user/count state, or use an equivalent database-enforced reservation. Preserve
the existing limit behavior while preventing concurrent requests from exceeding
it; the endpoint handler should continue delegating through create.

---

Nitpick comments:
In `@packages/backend/test/unit/EmojiSuggestionService.ts`:
- Around line 89-239: Add unit tests in the EmojiSuggestionService suite for the
remaining decision branches: verify create returns unsupportedFileType and
duplicateName, enforces tooManyPendingSuggestions at
MAX_PENDING_EMOJI_SUGGESTIONS, and NFC-normalizes name, category, and aliases;
also cover cancel success for the owner and failure for a non-owner with
userId-scoped delete criteria, plus the reject flow.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: db7d6e1d-a216-4645-9522-67778ed7a4a5

📥 Commits

Reviewing files that changed from the base of the PR and between 218ccd8 and d514a6d.

📒 Files selected for processing (29)
  • locales/index.d.ts
  • packages/backend/migration/1786507200000-EmojiSuggestion.js
  • packages/backend/src/core/CoreModule.ts
  • packages/backend/src/core/EmojiSuggestionService.ts
  • packages/backend/src/core/entities/EmojiSuggestionEntityService.ts
  • packages/backend/src/di-symbols.ts
  • packages/backend/src/misc/json-schema.ts
  • packages/backend/src/models/EmojiSuggestion.ts
  • packages/backend/src/models/RepositoryModule.ts
  • packages/backend/src/models/_.ts
  • packages/backend/src/models/json-schema/emoji-suggestion.ts
  • packages/backend/src/postgres.ts
  • packages/backend/src/server/api/emoji-suggestion.ts
  • packages/backend/src/server/api/endpoint-list.ts
  • packages/backend/src/server/api/endpoints/admin/emoji-suggestions/accept.ts
  • packages/backend/src/server/api/endpoints/admin/emoji-suggestions/list.ts
  • packages/backend/src/server/api/endpoints/admin/emoji-suggestions/reject.ts
  • packages/backend/src/server/api/endpoints/emoji-suggestions/cancel.ts
  • packages/backend/src/server/api/endpoints/emoji-suggestions/create.ts
  • packages/backend/src/server/api/endpoints/emoji-suggestions/list.ts
  • packages/backend/test/unit/EmojiSuggestionService.ts
  • packages/frontend/src/pages/about.emojis.vue
  • packages/frontend/src/pages/emoji-edit-dialog.vue
  • packages/frontend/src/pages/emoji-suggestions.vue
  • packages/frontend/src/router.definition.ts
  • packages/frontend/src/ui/_common_/common.ts
  • packages/misskey-js/src/api.types.ts
  • packages/misskey-js/src/entities.ts
  • sharkey-locales/en-US.yml

Comment on lines +15 to +17
export class EmojiSuggestion1786507200000 {
name = 'EmojiSuggestion1786507200000'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the missing semicolon.

ESLint reports @stylistic/semi at Line 16. Lint will fail in CI.

🔧 Proposed fix
-	name = 'EmojiSuggestion1786507200000'
+	name = 'EmojiSuggestion1786507200000';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export class EmojiSuggestion1786507200000 {
name = 'EmojiSuggestion1786507200000'
export class EmojiSuggestion1786507200000 {
name = 'EmojiSuggestion1786507200000';
🧰 Tools
🪛 ESLint

[error] 16-17: Missing semicolon.

(@stylistic/semi)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/migration/1786507200000-EmojiSuggestion.js` around lines 15
- 17, Add the required semicolon to the name property declaration in the
EmojiSuggestion1786507200000 class, satisfying the `@stylistic/semi` lint rule
without changing its value or surrounding migration logic.

Source: Linters/SAST tools

Comment on lines +81 to +91
const [pendingCount, duplicateSuggestion] = await Promise.all([
this.emojiSuggestionsRepository.countBy({ userId: user.id }),
this.emojiSuggestionsRepository.exists({
where: [
{ userId: user.id, name },
{ fileId: file.id },
],
}),
]);
if (pendingCount >= MAX_PENDING_EMOJI_SUGGESTIONS) return { ok: false, reason: 'tooManyPendingSuggestions' };
if (duplicateSuggestion) return { ok: false, reason: 'duplicateSuggestion' };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Check the pending name globally to return the correct reason.

IDX_emoji_suggestion_name is globally unique, but the preflight query scopes the name to userId. If another user already has a pending suggestion for the same name, the insert violates the constraint and the caller receives duplicateSuggestion, which describes their own duplicate submission. Scope the name check to the name only.

🔧 Proposed fix
 		const [pendingCount, duplicateSuggestion] = await Promise.all([
 			this.emojiSuggestionsRepository.countBy({ userId: user.id }),
 			this.emojiSuggestionsRepository.exists({
 				where: [
-					{ userId: user.id, name },
+					{ name },
 					{ fileId: file.id },
 				],
 			}),
 		]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [pendingCount, duplicateSuggestion] = await Promise.all([
this.emojiSuggestionsRepository.countBy({ userId: user.id }),
this.emojiSuggestionsRepository.exists({
where: [
{ userId: user.id, name },
{ fileId: file.id },
],
}),
]);
if (pendingCount >= MAX_PENDING_EMOJI_SUGGESTIONS) return { ok: false, reason: 'tooManyPendingSuggestions' };
if (duplicateSuggestion) return { ok: false, reason: 'duplicateSuggestion' };
const [pendingCount, duplicateSuggestion] = await Promise.all([
this.emojiSuggestionsRepository.countBy({ userId: user.id }),
this.emojiSuggestionsRepository.exists({
where: [
{ name },
{ fileId: file.id },
],
}),
]);
if (pendingCount >= MAX_PENDING_EMOJI_SUGGESTIONS) return { ok: false, reason: 'tooManyPendingSuggestions' };
if (duplicateSuggestion) return { ok: false, reason: 'duplicateSuggestion' };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/src/core/EmojiSuggestionService.ts` around lines 81 - 91,
Update the duplicate check in EmojiSuggestionService using
emojiSuggestionsRepository.exists so the name condition is evaluated globally
rather than combined with userId. Preserve the fileId condition for
file-specific duplicates and continue returning duplicateSuggestion when either
check matches.

Comment on lines +132 to +153
// Consume the suggestion before doing any work. This makes acceptance,
// cancellation, rejection, and another acceptance mutually exclusive.
const claimed = await this.emojiSuggestionsRepository.delete({
id: suggestion.id,
userId: suggestion.userId,
fileId: suggestion.fileId,
});
if (claimed.affected !== 1) return { ok: false, reason: 'noSuchSuggestion' };

const restoreSuggestion = async () => {
await this.emojiSuggestionsRepository.insert({
id: suggestion.id,
userId: suggestion.userId,
fileId: suggestion.fileId,
name: suggestion.name,
category: suggestion.category,
aliases: suggestion.aliases,
license: suggestion.license,
localOnly: suggestion.localOnly,
isSensitive: suggestion.isSensitive,
});
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

The delete-then-restore compensation can lose suggestions permanently.

The claim deletes the row before any external work starts. Recovery depends on restoreSuggestion running in the same process. If the process crashes, the database connection drops, or the compensating insert fails, the suggestion is gone and no emoji exists. The proposer loses the submission with no record.

Two durable alternatives:

  • Add a status column (pending/accepted/rejected) and claim by a conditional UPDATE ... WHERE status = 'pending'. Delete or keep the row only after the emoji creation succeeds.
  • Wrap the claim and the emoji insert in one transaction with SELECT ... FOR UPDATE on the suggestion row, and move the Drive copy before the transaction.

The first option also preserves an audit trail of moderation decisions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/src/core/EmojiSuggestionService.ts` around lines 132 - 153,
Replace the delete-then-restore flow in the suggestion acceptance path with
durable status-based claiming: add and use a pending/accepted/rejected status,
conditionally update the row from pending when claiming, and retain it until
emoji creation succeeds. Update the success and rejection paths to persist the
corresponding status instead of deleting the suggestion, preserving the audit
record and avoiding reliance on restoreSuggestion after crashes or failed
compensation.

Comment on lines +203 to +207
try {
await this.driveService.deleteFile(emojiFile, false, moderator);
} finally {
await restoreSuggestion();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Preserve the original failure when cleanup fails.

If driveService.deleteFile throws, its error propagates out of the catch block and replaces error. The same happens if restoreSuggestion throws inside finally. The root cause of the acceptance failure is then lost, and no logger records it.

Swallow and log the cleanup errors so the original error reaches Line 213.

🔧 Proposed fix
-				try {
-					await this.driveService.deleteFile(emojiFile, false, moderator);
-				} finally {
-					await restoreSuggestion();
-				}
+				try {
+					await this.driveService.deleteFile(emojiFile, false, moderator);
+				} catch (cleanupError) {
+					this.logger.error('Failed to delete the emoji file copy after a failed acceptance', { cleanupError });
+				}
+				try {
+					await restoreSuggestion();
+				} catch (restoreError) {
+					this.logger.error('Failed to restore the emoji suggestion after a failed acceptance', { restoreError });
+				}

This requires an injected logger. Do you want me to add one?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/src/core/EmojiSuggestionService.ts` around lines 203 - 207,
Update the cleanup flow around driveService.deleteFile and restoreSuggestion so
cleanup failures are caught, logged through an injected logger, and not
rethrown, preserving the original error for propagation at Line 213. Add the
logger dependency using the service’s existing dependency-injection conventions
and log failures from both cleanup operations with relevant context.

Comment on lines +36 to +45
super(meta, paramDef, async (ps, me) => {
const result = await this.emojiSuggestionService.create({
name: ps.name,
fileId: ps.fileId,
category: ps.category ?? null,
aliases: ps.aliases ?? [],
license: ps.license ?? null,
isSensitive: ps.isSensitive ?? false,
localOnly: ps.localOnly ?? false,
}, me);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Enforce the pending-suggestion limit atomically.

EmojiSuggestionService.create counts existing rows before it inserts the suggestion. Concurrent requests can all pass the limit check before any insert completes. A user with nine pending suggestions can then create more than ten distinct suggestions.

Lock the per-user count and insert in one transaction, or use a database-enforced reservation mechanism. The endpoint rate limit does not prevent this race.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/src/server/api/endpoints/emoji-suggestions/create.ts` around
lines 36 - 45, Update EmojiSuggestionService.create to enforce the per-user
pending-suggestion limit atomically: perform the count check and suggestion
insert within the same transaction while locking the relevant user/count state,
or use an equivalent database-enforced reservation. Preserve the existing limit
behavior while preventing concurrent requests from exceeding it; the endpoint
handler should continue delegating through create.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants