diff --git a/app/lib/admin/models.dart b/app/lib/admin/models.dart index 7a1f9ba99..8a2007167 100644 --- a/app/lib/admin/models.dart +++ b/app/lib/admin/models.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'package:clock/clock.dart'; -import 'package:pub_dev/shared/utils.dart'; import '../shared/datastore.dart' as db; @@ -77,13 +76,26 @@ class ModerationCase extends db.ExpandoModel { ModerationCase(); ModerationCase.init({ + required String caseId, required this.reporterUserId, required this.reporterEmail, required this.detectedBy, required this.kind, required this.status, }) { - id = createUuid(); + id = caseId; opened = clock.now().toUtc(); } } + +abstract class ModerationDetectedBy { + static const externalNotification = 'external-notification'; +} + +abstract class ModerationKind { + static const notification = 'notification'; +} + +abstract class ModerationStatus { + static const pending = 'pending'; +} diff --git a/app/lib/frontend/handlers/report.dart b/app/lib/frontend/handlers/report.dart index 9abc5f5a8..f8ca8f170 100644 --- a/app/lib/frontend/handlers/report.dart +++ b/app/lib/frontend/handlers/report.dart @@ -9,8 +9,10 @@ import 'package:clock/clock.dart'; import 'package:shelf/shelf.dart' as shelf; import '../../account/backend.dart'; +import '../../admin/models.dart'; import '../../frontend/email_sender.dart'; import '../../frontend/handlers/cache_control.dart'; +import '../../shared/datastore.dart'; import '../../shared/email.dart'; import '../../shared/exceptions.dart'; import '../../shared/handlers.dart'; @@ -39,7 +41,7 @@ Future processReportPageHandler( throw NotFoundException('Experimental flag is not enabled.'); } final now = clock.now().toUtc(); - final id = '${now.toIso8601String().split('T').first}/${createUuid()}'; + final caseId = '${now.toIso8601String().split('T').first}/${createUuid()}'; final isAuthenticated = requestContext.sessionData?.isAuthenticated ?? false; final user = isAuthenticated ? await requireAuthenticatedWebUser() : null; @@ -61,13 +63,27 @@ Future processReportPageHandler( maximum: 8192, ); + // If the email sending fails, we may have pending [ModerationCase] entities + // in the datastore. These would be reviewed and processed manually. + await withRetryTransaction(dbService, (tx) async { + final mc = ModerationCase.init( + caseId: caseId, + reporterUserId: user?.userId, + reporterEmail: userEmail!, + detectedBy: ModerationDetectedBy.externalNotification, + kind: ModerationKind.notification, + status: ModerationStatus.pending, + ); + tx.insert(mc); + }); + final bodyText = [ - 'New report recieved on ${now.toIso8601String()}: $id', + 'New report recieved on ${now.toIso8601String()}: $caseId', 'Message:\n${form.message}', ].join('\n\n'); await emailSender.sendMessage(createReportPageAdminEmail( - id: id, + id: caseId, userEmail: userEmail!, bodyText: bodyText, ));