Skip to content

Commit 424de1e

Browse files
Fix batch import users, fix logs in the server (WIP)
1 parent bf54c32 commit 424de1e

13 files changed

Lines changed: 112 additions & 88 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ private/
22
school_data_hub_website/
33
insights/
44
legal/
5-
.cursor/
5+
.cursor/
6+
.claude/

school_data_hub_flutter/lib/features/user/presentation/batch_import_users/batch_import_users_page.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class _BatchImportUsersPageState extends State<BatchImportUsersPage> {
8787
_log.info(
8888
'[BatchImport] Chunk done — created=${chunkResult.successCount}, '
8989
'errors=${chunkResult.failureCount}, '
90-
'total created=$_progressCreated, total errors=$_progressErrors',
90+
'total created=${allCredentials.length}, total errors=${allErrors.length}',
9191
);
9292
},
9393
onError: (Object e, StackTrace? st) {
@@ -343,9 +343,14 @@ class _BatchImportUsersPageState extends State<BatchImportUsersPage> {
343343
),
344344
const Gap(8),
345345
Row(
346+
mainAxisSize: MainAxisSize.min,
346347
children: [
347348
ElevatedButton.icon(
348-
style: AppStyles.actionButtonStyle,
349+
style: AppStyles.actionButtonStyle.copyWith(
350+
minimumSize: WidgetStateProperty.all(
351+
const Size(0, 50),
352+
),
353+
),
349354
onPressed: _printCredentials,
350355
icon: const Icon(Icons.print),
351356
label: const Text('Drucken'),

school_data_hub_server/config/development.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,9 @@ database:
4141
redis:
4242
enabled: false
4343
host: localhost
44-
port: 8091
44+
port: 8091
45+
46+
sessionLogs:
47+
cleanupInterval: 24h
48+
retentionPeriod: 30d
49+
retentionCount: 5000

school_data_hub_server/config/production.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@ redis:
4747
port: 6379
4848

4949
maxRequestSize: 10485760
50+
51+
sessionLogs:
52+
cleanupInterval: 24h
53+
retentionPeriod: 30d
54+
retentionCount: 5000

school_data_hub_server/config/staging.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,8 @@ redis:
5151
port: 6379
5252

5353
maxRequestSize: 10485760
54+
55+
sessionLogs:
56+
cleanupInterval: 24h
57+
retentionPeriod: 30d
58+
retentionCount: 5000

school_data_hub_server/lib/src/_features/admin/endpoints/admin_user_endpoint.dart

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
import 'package:logging/logging.dart';
21
import 'package:school_data_hub_server/src/generated/protocol.dart';
32
import 'package:serverpod/serverpod.dart';
43
import 'package:serverpod_auth_server/module.dart';
54
import 'package:serverpod_auth_server/serverpod_auth_server.dart' as auth;
65

7-
final _log = Logger('AdminUserEndpoint');
8-
96
/// The endpoint for admin operations.
107
/// This endpoint requires the user to be logged in and have admin scope.
118
class AdminUserEndpoint extends Endpoint {
@@ -70,14 +67,15 @@ class AdminUserEndpoint extends Endpoint {
7067
int? credit,
7168
Set<int>? pupilsAuth,
7269
}) async {
73-
final UserInfo? userInfo =
74-
await auth.Emails.createUser(session, userName, email, password);
70+
// Declared outside try so the catch can reference it for cleanup.
71+
UserInfo? userInfo;
72+
try {
73+
userInfo = await auth.Emails.createUser(session, userName, email, password);
7574

76-
if (userInfo?.id == null) {
77-
throw Exception('Failed to create user');
78-
}
75+
if (userInfo?.id == null) {
76+
throw Exception('Failed to create user');
77+
}
7978

80-
try {
8179
// Update fullName (direct, no transaction)
8280
userInfo!.fullName = fullName;
8381
await auth.UserInfo.db.updateRow(session, userInfo);
@@ -114,13 +112,37 @@ class AdminUserEndpoint extends Endpoint {
114112
await User.db.insertRow(session, user);
115113
return user;
116114
} catch (e) {
117-
// auth.Emails.createUser already committed; clean up orphaned auth account
118-
try {
119-
await auth.UserInfo.db.deleteRow(session, userInfo!);
120-
} catch (cleanupError) {
121-
_log.warning(
122-
'[_createOneUser] Failed to clean up orphaned auth for ${userInfo!.id}: $cleanupError',
123-
);
115+
if (userInfo?.id != null) {
116+
// Exception happened after auth.Emails.createUser returned — delete the
117+
// UserInfo it committed.
118+
try {
119+
await auth.UserInfo.db.deleteRow(session, userInfo!);
120+
} catch (cleanupError) {
121+
session.log(
122+
'[_createOneUser] Failed to clean up orphaned auth for '
123+
'${userInfo!.id}: $cleanupError',
124+
level: LogLevel.warning,
125+
);
126+
}
127+
} else if (e.toString().contains('serverpod_email_auth')) {
128+
// auth.Emails.createUser inserted UserInfo then threw on the EmailAuth
129+
// insert (e.g. duplicate email). The new UserInfo is orphaned — find it
130+
// by userName and remove it.
131+
try {
132+
final orphan = await auth.UserInfo.db.findFirstRow(
133+
session,
134+
where: (t) => t.userName.equals(userName),
135+
);
136+
if (orphan != null) {
137+
await auth.UserInfo.db.deleteRow(session, orphan);
138+
}
139+
} catch (cleanupError) {
140+
session.log(
141+
'[_createOneUser] Failed to clean up orphaned UserInfo for '
142+
'$userName: $cleanupError',
143+
level: LogLevel.warning,
144+
);
145+
}
124146
}
125147
rethrow;
126148
}

school_data_hub_server/lib/src/_features/pupil/endpoints/pupil_update_endpoint.dart

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
import 'package:logging/logging.dart';
21
import 'package:school_data_hub_server/src/_features/pupil/schemas/pupil_schemas.dart';
32
import 'package:school_data_hub_server/src/generated/protocol.dart';
43
import 'package:school_data_hub_server/src/helpers/hub_document_helper.dart';
54
import 'package:serverpod/serverpod.dart';
65

7-
final _log = Logger('PupilUpdateEndpoint');
8-
96
class PupilUpdateEndpoint extends Endpoint {
107
@override
118
bool get requireLogin => true;
@@ -134,14 +131,15 @@ class PupilUpdateEndpoint extends Endpoint {
134131

135132
switch (documentType) {
136133
case PupilDocumentType.avatar:
137-
_log.info(
134+
session.log(
138135
'Updating pupil avatar: id: [${hubDocumentInDatabase.id}] documentID [${hubDocumentInDatabase.documentId}]');
139136

140137
// if the pupil has an avatar, delete it
141138

142139
if (pupil.avatar != null) {
143-
_log.warning(
144-
'Deleting old avatar document: ${pupil.avatar!.documentId}');
140+
session.log(
141+
'Deleting old avatar document: ${pupil.avatar!.documentId}',
142+
level: LogLevel.warning);
145143

146144
// delete the old avatar file from the storage
147145
session.storage.deleteFile(
@@ -166,14 +164,15 @@ class PupilUpdateEndpoint extends Endpoint {
166164
break;
167165

168166
case PupilDocumentType.avatarAuth:
169-
_log.info(
167+
session.log(
170168
'Updating pupil avatarAuth: id: [${hubDocumentInDatabase.id}] documentID [${hubDocumentInDatabase.documentId}]');
171169

172170
// if the pupil has an avatar auth, delete it
173171

174172
if (pupil.avatarAuth != null) {
175-
_log.warning(
176-
'Deleting old avatar auth document: ${pupil.avatar!.documentId}');
173+
session.log(
174+
'Deleting old avatar auth document: ${pupil.avatar!.documentId}',
175+
level: LogLevel.warning);
177176

178177
// delete the old avatar file from the storage
179178
session.storage.deleteFile(
@@ -199,13 +198,14 @@ class PupilUpdateEndpoint extends Endpoint {
199198
break;
200199

201200
case PupilDocumentType.publicMediaAuth:
202-
_log.info(
201+
session.log(
203202
'Updating pupil public media auth: id: [${hubDocumentInDatabase.id}] documentID [${hubDocumentInDatabase.documentId}]');
204203

205204
// if the pupil has a public media auth, delete it
206205
if (pupil.publicMediaAuthDocument != null) {
207-
_log.warning(
208-
'Deleting old public media auth document: ${pupil.publicMediaAuthDocument!.documentId}');
206+
session.log(
207+
'Deleting old public media auth document: ${pupil.publicMediaAuthDocument!.documentId}',
208+
level: LogLevel.warning);
209209

210210
// delete the old public media auth document file from the storage
211211
session.storage.deleteFile(
@@ -225,7 +225,7 @@ class PupilUpdateEndpoint extends Endpoint {
225225
}
226226

227227
// update the pupil with the new publi media a
228-
_log.info(
228+
session.log(
229229
'Updating pupil public media auth with id: [${hubDocumentInDatabase.id}] documentID [${hubDocumentInDatabase.documentId}]');
230230

231231
await PupilData.db.attachRow.publicMediaAuthDocument(
@@ -239,7 +239,7 @@ class PupilUpdateEndpoint extends Endpoint {
239239
final updatedPupil = await PupilData.db
240240
.findById(session, pupil.id!, include: PupilSchemas.allInclude);
241241

242-
_log.fine('Updated pupil : ${updatedPupil!.toJson()}');
242+
session.log('Updated pupil : ${updatedPupil!.toJson()}', level: LogLevel.debug);
243243
session.messages.postMessage('hub_events_stream', updatedPupil);
244244
return updatedPupil;
245245
}

school_data_hub_server/lib/src/_features/schoolday_events/endpoints/schoolday_event_endpoint.dart

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import 'dart:async';
22

3-
import 'package:logging/logging.dart';
43
import 'package:school_data_hub_server/src/_features/schoolday_events/helpers/schoolday_event_notification_helper.dart';
54
import 'package:school_data_hub_server/src/generated/protocol.dart';
65
import 'package:serverpod/serverpod.dart';
76

8-
final _log = Logger('SchooldayEventEndpoint');
9-
107
class SchooldayEventEndpoint extends Endpoint {
118
@override
129
bool get requireLogin => true;
@@ -213,8 +210,9 @@ class SchooldayEventEndpoint extends Endpoint {
213210
// attach the processed file to the event
214211
// if the pupil had a processed file, delete it
215212
if (schooldayEvent.processedDocument != null) {
216-
_log.warning(
217-
'Deleting old schoolday event processed document: ${schooldayEvent.processedDocument!.documentId}');
213+
session.log(
214+
'Deleting old schoolday event processed document: ${schooldayEvent.processedDocument!.documentId}',
215+
level: LogLevel.warning);
218216
// delete the old processed file from the storage
219217
session.storage.deleteFile(
220218
storageId: 'private',
@@ -230,7 +228,7 @@ class SchooldayEventEndpoint extends Endpoint {
230228
// TODO: Consider exceptions and handle them gracefully here
231229
}
232230
// update the schoolday event with the new file
233-
_log.info(
231+
session.log(
234232
'Updating schoolday event document: id: [${hubDocumentInDatabase.id}] documentID [${hubDocumentInDatabase.documentId}]');
235233
// pupil.avatar = hubDocument;
236234
await SchooldayEvent.db.attachRow.processedDocument(
@@ -241,8 +239,9 @@ class SchooldayEventEndpoint extends Endpoint {
241239
// attach the file to the event
242240
// if the pupil had a file, delete it
243241
if (schooldayEvent.document != null) {
244-
_log.warning(
245-
'Deleting old schoolday event document: ${schooldayEvent.document!.documentId}');
242+
session.log(
243+
'Deleting old schoolday event document: ${schooldayEvent.document!.documentId}',
244+
level: LogLevel.warning);
246245
// delete the old file from the storage
247246
session.storage.deleteFile(
248247
storageId: 'private',
@@ -256,7 +255,7 @@ class SchooldayEventEndpoint extends Endpoint {
256255
// TODO: Consider exceptions and handle them gracefully here
257256
}
258257
// update the schoolday event with the new file
259-
_log.info(
258+
session.log(
260259
'Updating schoolday event document: id: [${hubDocumentInDatabase.id}] documentID [${hubDocumentInDatabase.documentId}]');
261260
// pupil.avatar = hubDocument;
262261
await SchooldayEvent.db.attachRow.document(
@@ -274,7 +273,7 @@ class SchooldayEventEndpoint extends Endpoint {
274273
schoolday: Schoolday.include(),
275274
));
276275

277-
_log.fine('Updated event : ${updatedEvent!.toJson()}');
276+
session.log('Updated event : ${updatedEvent!.toJson()}', level: LogLevel.debug);
278277
session.messages.postMessage('hub_events_stream', updatedEvent);
279278
return updatedEvent;
280279
}

school_data_hub_server/lib/src/_features/schoolday_events/helpers/schoolday_event_notification_helper.dart

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import 'dart:async';
22

3-
import 'package:logging/logging.dart';
43
import 'package:school_data_hub_server/src/generated/protocol.dart';
54
import 'package:school_data_hub_server/src/utils/matrix_notifications/matrix_notifications.dart';
65
import 'package:serverpod/serverpod.dart';
76

8-
final Logger _log = Logger('SchooldayEventNotificationHelper');
9-
107
class SchooldayEventNotificationHelper {
118
/// Returns plain text notification message
129
@@ -38,8 +35,9 @@ class SchooldayEventNotificationHelper {
3835
// Send notification to all recipients
3936
if (recipients.isEmpty) {
4037
// Fallback to default recipient if no matches found
41-
_log.warning(
42-
'No recipients found for schoolday event ${eventWithSchoolday.id}');
38+
session.log(
39+
'No recipients found for schoolday event ${eventWithSchoolday.id}',
40+
level: LogLevel.warning);
4341
return;
4442
}
4543

@@ -64,7 +62,7 @@ class SchooldayEventNotificationHelper {
6462
),
6563
));
6664
} catch (e) {
67-
_log.severe('Error sending matrix notification: $e');
65+
session.log('Error sending matrix notification: $e', level: LogLevel.error);
6866
}
6967
}
7068
}

school_data_hub_server/lib/src/_features/user/endpoints/user_endpoints.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import 'package:logging/logging.dart';
2-
import 'package:school_data_hub_server/src/generated/protocol.dart';
31
import 'package:school_data_hub_server/src/_features/user/helpers/get_user_devices.dart';
2+
import 'package:school_data_hub_server/src/generated/protocol.dart';
43
import 'package:serverpod/serverpod.dart';
54
import 'package:serverpod_auth_server/serverpod_auth_server.dart';
65

76
class UserEndpoint extends Endpoint {
8-
final _log = Logger('UserEndpoint');
97
@override
108
bool get requireLogin => true;
119

@@ -65,12 +63,10 @@ class UserEndpoint extends Endpoint {
6563

6664
Future<bool> changePassword(
6765
Session session, String oldPassword, String newPassword) async {
68-
_log.info('oldPassword: $oldPassword');
69-
_log.info('newPassword: $newPassword');
7066
// Get the authenticated user
7167
final authenticationInfo = await session.authenticated;
7268
if (authenticationInfo == null) {
73-
_log.severe('User is not authenticated');
69+
session.log('User is not authenticated', level: LogLevel.error);
7470
return false; // User is not authenticated
7571
}
7672
final result = await Emails.changePassword(

0 commit comments

Comments
 (0)