@stamhoofd/backend 2.77.3 → 2.77.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +12 -3
- package/package.json +10 -10
- package/src/email-recipient-loaders/members.ts +13 -3
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +7 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +10 -0
- package/src/helpers/AdminPermissionChecker.ts +34 -25
- package/src/helpers/MemberUserSyncer.ts +64 -2
- package/src/services/UniqueUserService.ts +61 -0
package/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { AuditLogService } from './src/services/AuditLogService';
|
|
|
19
19
|
import { DocumentService } from './src/services/DocumentService';
|
|
20
20
|
import { FileSignService } from './src/services/FileSignService';
|
|
21
21
|
import { PlatformMembershipService } from './src/services/PlatformMembershipService';
|
|
22
|
+
import { UniqueUserService } from './src/services/UniqueUserService';
|
|
22
23
|
|
|
23
24
|
process.on('unhandledRejection', (error: Error) => {
|
|
24
25
|
console.error('unhandledRejection');
|
|
@@ -49,13 +50,21 @@ const seeds = async () => {
|
|
|
49
50
|
console.error(e);
|
|
50
51
|
}
|
|
51
52
|
};
|
|
53
|
+
const bootTime = process.hrtime();
|
|
52
54
|
|
|
53
55
|
const start = async () => {
|
|
54
56
|
console.log('Running server at v' + Version);
|
|
55
57
|
loadLogger();
|
|
58
|
+
|
|
56
59
|
await GlobalHelper.load();
|
|
60
|
+
await UniqueUserService.check();
|
|
61
|
+
|
|
62
|
+
// Init platform shared struct: otherwise permissions won't work with missing responsibilities
|
|
63
|
+
await Platform.getSharedStruct();
|
|
57
64
|
|
|
58
65
|
const router = new Router();
|
|
66
|
+
|
|
67
|
+
// Note: we should load endpoints one by once to have a reliable order of url matching
|
|
59
68
|
await router.loadAllEndpoints(__dirname + '/src/endpoints/global/*');
|
|
60
69
|
await router.loadAllEndpoints(__dirname + '/src/endpoints/admin/*');
|
|
61
70
|
await router.loadAllEndpoints(__dirname + '/src/endpoints/auth');
|
|
@@ -95,9 +104,6 @@ const start = async () => {
|
|
|
95
104
|
// Add CORS headers
|
|
96
105
|
routerServer.addResponseMiddleware(CORSMiddleware);
|
|
97
106
|
|
|
98
|
-
// Init platform shared struct: otherwise permissions won't work with missing responsibilities
|
|
99
|
-
await Platform.getSharedStruct();
|
|
100
|
-
|
|
101
107
|
// Register Excel loaders
|
|
102
108
|
await import('./src/excel-loaders/members');
|
|
103
109
|
await import('./src/excel-loaders/payments');
|
|
@@ -111,6 +117,9 @@ const start = async () => {
|
|
|
111
117
|
|
|
112
118
|
routerServer.listen(STAMHOOFD.PORT ?? 9090);
|
|
113
119
|
|
|
120
|
+
const hrend = process.hrtime(bootTime);
|
|
121
|
+
console.log('🟢 HTTP server started in ' + Math.ceil(hrend[0] * 1000 + hrend[1] / 1000000) + 'ms');
|
|
122
|
+
|
|
114
123
|
resumeEmails().catch(console.error);
|
|
115
124
|
|
|
116
125
|
if (routerServer.server) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.77.
|
|
3
|
+
"version": "2.77.4",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
"@simonbackx/simple-encoding": "2.20.0",
|
|
38
38
|
"@simonbackx/simple-endpoints": "1.19.1",
|
|
39
39
|
"@simonbackx/simple-logging": "^1.0.1",
|
|
40
|
-
"@stamhoofd/backend-i18n": "2.77.
|
|
41
|
-
"@stamhoofd/backend-middleware": "2.77.
|
|
42
|
-
"@stamhoofd/email": "2.77.
|
|
43
|
-
"@stamhoofd/models": "2.77.
|
|
44
|
-
"@stamhoofd/queues": "2.77.
|
|
45
|
-
"@stamhoofd/sql": "2.77.
|
|
46
|
-
"@stamhoofd/structures": "2.77.
|
|
47
|
-
"@stamhoofd/utility": "2.77.
|
|
40
|
+
"@stamhoofd/backend-i18n": "2.77.4",
|
|
41
|
+
"@stamhoofd/backend-middleware": "2.77.4",
|
|
42
|
+
"@stamhoofd/email": "2.77.4",
|
|
43
|
+
"@stamhoofd/models": "2.77.4",
|
|
44
|
+
"@stamhoofd/queues": "2.77.4",
|
|
45
|
+
"@stamhoofd/sql": "2.77.4",
|
|
46
|
+
"@stamhoofd/structures": "2.77.4",
|
|
47
|
+
"@stamhoofd/utility": "2.77.4",
|
|
48
48
|
"archiver": "^7.0.1",
|
|
49
49
|
"aws-sdk": "^2.885.0",
|
|
50
50
|
"axios": "1.6.8",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"publishConfig": {
|
|
65
65
|
"access": "public"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "e41b2942f787a4e8eb1f77ebb7c9b68d33fb4e69"
|
|
68
68
|
}
|
|
@@ -25,9 +25,19 @@ Email.recipientLoaders.set(EmailRecipientFilterType.Members, {
|
|
|
25
25
|
|
|
26
26
|
count: async (query: LimitedFilteredRequest) => {
|
|
27
27
|
query.filter = mergeFilters([query.filter, {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
$and: [
|
|
29
|
+
{
|
|
30
|
+
email: {
|
|
31
|
+
$neq: null,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
email: {
|
|
36
|
+
$neq: '',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
|
|
31
41
|
}]);
|
|
32
42
|
const q = await GetMembersEndpoint.buildQuery(query);
|
|
33
43
|
return await q.count();
|
|
@@ -11,6 +11,7 @@ import { PeriodHelper } from '../../../helpers/PeriodHelper';
|
|
|
11
11
|
import { SetupStepUpdater } from '../../../helpers/SetupStepUpdater';
|
|
12
12
|
import { TagHelper } from '../../../helpers/TagHelper';
|
|
13
13
|
import { PlatformMembershipService } from '../../../services/PlatformMembershipService';
|
|
14
|
+
import { MemberUserSyncer } from '../../../helpers/MemberUserSyncer';
|
|
14
15
|
|
|
15
16
|
type Params = Record<string, never>;
|
|
16
17
|
type Query = undefined;
|
|
@@ -49,6 +50,7 @@ export class PatchPlatformEndpoint extends Endpoint<
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
const platform = await Platform.getShared();
|
|
53
|
+
let shouldUpdateUserPermissions = false;
|
|
52
54
|
|
|
53
55
|
if (request.body.privateConfig) {
|
|
54
56
|
// Did we patch roles?
|
|
@@ -62,6 +64,7 @@ export class PatchPlatformEndpoint extends Endpoint<
|
|
|
62
64
|
platform.privateConfig.roles,
|
|
63
65
|
request.body.privateConfig.roles,
|
|
64
66
|
);
|
|
67
|
+
shouldUpdateUserPermissions = true;
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
if (request.body.privateConfig.emails) {
|
|
@@ -234,6 +237,10 @@ export class PatchPlatformEndpoint extends Endpoint<
|
|
|
234
237
|
SetupStepUpdater.updateSetupStepsForAllOrganizationsInCurrentPeriod().catch(console.error);
|
|
235
238
|
}
|
|
236
239
|
|
|
240
|
+
if (shouldUpdateUserPermissions) {
|
|
241
|
+
await MemberUserSyncer.updatePermissionsForPlatform();
|
|
242
|
+
}
|
|
243
|
+
|
|
237
244
|
return new Response(await Platform.getSharedPrivateStruct());
|
|
238
245
|
}
|
|
239
246
|
|
|
@@ -11,6 +11,7 @@ import { Context } from '../../../../helpers/Context';
|
|
|
11
11
|
import { SetupStepUpdater } from '../../../../helpers/SetupStepUpdater';
|
|
12
12
|
import { TagHelper } from '../../../../helpers/TagHelper';
|
|
13
13
|
import { ViesHelper } from '../../../../helpers/ViesHelper';
|
|
14
|
+
import { MemberUserSyncer } from '../../../../helpers/MemberUserSyncer';
|
|
14
15
|
|
|
15
16
|
type Params = Record<string, never>;
|
|
16
17
|
type Query = undefined;
|
|
@@ -65,6 +66,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
65
66
|
|
|
66
67
|
const errors = new SimpleErrors();
|
|
67
68
|
let shouldUpdateSetupSteps = false;
|
|
69
|
+
let shouldUpdateUserPermissions = false;
|
|
68
70
|
let updateTags = false;
|
|
69
71
|
|
|
70
72
|
if (await Context.auth.hasFullAccess(organization.id)) {
|
|
@@ -128,6 +130,10 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
128
130
|
organization.privateMeta.balanceNotificationSettings = patchObject(organization.privateMeta.balanceNotificationSettings, request.body.privateMeta.balanceNotificationSettings);
|
|
129
131
|
organization.privateMeta.recordAnswers = request.body.privateMeta.recordAnswers.applyTo(organization.privateMeta.recordAnswers);
|
|
130
132
|
|
|
133
|
+
if (request.body.privateMeta.responsibilities || request.body.privateMeta.roles) {
|
|
134
|
+
shouldUpdateUserPermissions = true;
|
|
135
|
+
}
|
|
136
|
+
|
|
131
137
|
if (request.body.privateMeta.mollieProfile !== undefined) {
|
|
132
138
|
organization.privateMeta.mollieProfile = patchObject(organization.privateMeta.mollieProfile, request.body.privateMeta.mollieProfile);
|
|
133
139
|
}
|
|
@@ -392,6 +398,10 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
392
398
|
await SetupStepUpdater.updateForOrganization(organization);
|
|
393
399
|
}
|
|
394
400
|
|
|
401
|
+
if (shouldUpdateUserPermissions) {
|
|
402
|
+
await MemberUserSyncer.updatePermissionsForOrganization(organization.id);
|
|
403
|
+
}
|
|
404
|
+
|
|
395
405
|
if (updateTags) {
|
|
396
406
|
await TagHelper.updateOrganizations();
|
|
397
407
|
}
|
|
@@ -852,8 +852,6 @@ export class AdminPermissionChecker {
|
|
|
852
852
|
* Return a list of RecordSettings the current user can view or edit
|
|
853
853
|
*/
|
|
854
854
|
async getAccessibleRecordCategories(member: MemberWithRegistrations, level: PermissionLevel = PermissionLevel.Read): Promise<RecordCategory[]> {
|
|
855
|
-
const isUserManager = this.isUserManager(member);
|
|
856
|
-
|
|
857
855
|
// First list all organizations this member is part of
|
|
858
856
|
const organizations: Organization[] = [];
|
|
859
857
|
|
|
@@ -875,14 +873,6 @@ export class AdminPermissionChecker {
|
|
|
875
873
|
// Check if we have access to their data
|
|
876
874
|
const recordCategories: RecordCategory[] = [];
|
|
877
875
|
for (const organization of organizations) {
|
|
878
|
-
if (isUserManager) {
|
|
879
|
-
for (const category of organization.meta.recordsConfiguration.recordCategories) {
|
|
880
|
-
if (category.checkPermissionForUserManager(level)) {
|
|
881
|
-
recordCategories.push(category);
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
|
|
886
876
|
const permissions = await this.getOrganizationPermissions(organization);
|
|
887
877
|
|
|
888
878
|
if (!permissions) {
|
|
@@ -891,7 +881,7 @@ export class AdminPermissionChecker {
|
|
|
891
881
|
|
|
892
882
|
// Now add all records of this organization
|
|
893
883
|
for (const category of organization.meta.recordsConfiguration.recordCategories) {
|
|
894
|
-
if (
|
|
884
|
+
if (recordCategories.find(c => c.id === category.id)) {
|
|
895
885
|
// Already added
|
|
896
886
|
continue;
|
|
897
887
|
}
|
|
@@ -916,7 +906,7 @@ export class AdminPermissionChecker {
|
|
|
916
906
|
|
|
917
907
|
// Platform data
|
|
918
908
|
const platformPermissions = this.platformPermissions;
|
|
919
|
-
if (platformPermissions
|
|
909
|
+
if (platformPermissions) {
|
|
920
910
|
for (const category of this.platform.config.recordsConfiguration.recordCategories) {
|
|
921
911
|
if (recordCategories.find(c => c.id === category.id)) {
|
|
922
912
|
// Already added
|
|
@@ -926,11 +916,6 @@ export class AdminPermissionChecker {
|
|
|
926
916
|
if (platformPermissions?.hasResourceAccess(PermissionsResourceType.RecordCategories, category.id, level)) {
|
|
927
917
|
recordCategories.push(category);
|
|
928
918
|
}
|
|
929
|
-
else if (isUserManager) {
|
|
930
|
-
if (category.checkPermissionForUserManager(level)) {
|
|
931
|
-
recordCategories.push(category);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
919
|
}
|
|
935
920
|
}
|
|
936
921
|
|
|
@@ -1069,21 +1054,45 @@ export class AdminPermissionChecker {
|
|
|
1069
1054
|
async getAccessibleRecordSet(member: MemberWithRegistrations, level: PermissionLevel = PermissionLevel.Read): Promise<Set<string>> {
|
|
1070
1055
|
const categories = await this.getAccessibleRecordCategories(member, level);
|
|
1071
1056
|
const set = new Set<string>();
|
|
1057
|
+
|
|
1058
|
+
for (const category of categories) {
|
|
1059
|
+
for (const record of category.getAllRecords()) {
|
|
1060
|
+
set.add(record.id);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1072
1064
|
const isUserManager = this.isUserManager(member);
|
|
1073
1065
|
|
|
1066
|
+
// Also include those we can access as user manager
|
|
1074
1067
|
if (isUserManager) {
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1068
|
+
const allCategories = this.platform.config.recordsConfiguration.recordCategories.slice();
|
|
1069
|
+
|
|
1070
|
+
// First list all organizations this member is part of
|
|
1071
|
+
const organizations: Organization[] = [];
|
|
1072
|
+
|
|
1073
|
+
if (member.organizationId) {
|
|
1074
|
+
if (this.checkScope(member.organizationId)) {
|
|
1075
|
+
organizations.push(await this.getOrganization(member.organizationId));
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
for (const registration of member.registrations) {
|
|
1080
|
+
if (this.checkScope(registration.organizationId)) {
|
|
1081
|
+
if (!organizations.find(o => o.id === registration.organizationId)) {
|
|
1082
|
+
organizations.push(await this.getOrganization(registration.organizationId));
|
|
1079
1083
|
}
|
|
1080
1084
|
}
|
|
1081
1085
|
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1086
|
+
|
|
1087
|
+
for (const organization of organizations) {
|
|
1088
|
+
allCategories.push(...organization.meta.recordsConfiguration.recordCategories);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
for (const category of allCategories) {
|
|
1085
1092
|
for (const record of category.getAllRecords()) {
|
|
1086
|
-
|
|
1093
|
+
if (record.checkPermissionForUserManager(level)) {
|
|
1094
|
+
set.add(record.id);
|
|
1095
|
+
}
|
|
1087
1096
|
}
|
|
1088
1097
|
}
|
|
1089
1098
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { CachedBalance, Member, MemberResponsibilityRecord, MemberWithRegistrations, User } from '@stamhoofd/models';
|
|
1
|
+
import { CachedBalance, Member, MemberResponsibilityRecord, MemberWithRegistrations, Organization, Platform, User } from '@stamhoofd/models';
|
|
2
2
|
import { SQL } from '@stamhoofd/sql';
|
|
3
|
-
import { AuditLogSource, MemberDetails, Permissions, UserPermissions } from '@stamhoofd/structures';
|
|
3
|
+
import { AuditLogSource, MemberDetails, PermissionRole, Permissions, UserPermissions } from '@stamhoofd/structures';
|
|
4
4
|
import crypto from 'crypto';
|
|
5
5
|
import basex from 'base-x';
|
|
6
6
|
import { AuditLogService } from '../services/AuditLogService';
|
|
@@ -129,6 +129,20 @@ export class MemberUserSyncerStatic {
|
|
|
129
129
|
return MemberResponsibilityRecord.fromRows(rows, MemberResponsibilityRecord.table);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
async updatePermissionsForOrganization(organizationId: string) {
|
|
133
|
+
const admins = await User.getAdmins(organizationId);
|
|
134
|
+
for (const admin of admins) {
|
|
135
|
+
await this.updateInheritedPermissions(admin);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async updatePermissionsForPlatform() {
|
|
140
|
+
const admins = await User.getPlatformAdmins();
|
|
141
|
+
for (const admin of admins) {
|
|
142
|
+
await this.updateInheritedPermissions(admin);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
132
146
|
async updateInheritedPermissions(user: User) {
|
|
133
147
|
const responsibilities = user.memberId ? (await this.getResponsibilitiesForMembers([user.memberId])) : [];
|
|
134
148
|
|
|
@@ -173,6 +187,54 @@ export class MemberUserSyncerStatic {
|
|
|
173
187
|
}
|
|
174
188
|
}
|
|
175
189
|
|
|
190
|
+
// Update roles (remove roles from permissions that no longer exist)
|
|
191
|
+
for (const organizationId of user.permissions.organizationPermissions.keys()) {
|
|
192
|
+
const organizationPermissions = user.permissions.organizationPermissions.get(organizationId);
|
|
193
|
+
if (!organizationPermissions) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const roles = organizationPermissions.roles;
|
|
198
|
+
if (roles.length === 0) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const organization = await Organization.getByID(organizationId);
|
|
203
|
+
if (!organization) {
|
|
204
|
+
// Delete key
|
|
205
|
+
user.permissions.organizationPermissions.delete(organizationId);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const availableRoles = organization.privateMeta.roles;
|
|
210
|
+
const newRoles: PermissionRole[] = [];
|
|
211
|
+
for (const role of roles) {
|
|
212
|
+
const roleInfo = availableRoles.find(r => r.id === role.id);
|
|
213
|
+
if (roleInfo) {
|
|
214
|
+
role.name = roleInfo.name;
|
|
215
|
+
newRoles.push(role);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
organizationPermissions.roles = newRoles;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const globalPermissions = user.permissions.globalPermissions;
|
|
222
|
+
if (globalPermissions) {
|
|
223
|
+
const roles = globalPermissions.roles;
|
|
224
|
+
if (roles.length > 0) {
|
|
225
|
+
const availableRoles = (await Platform.getSharedPrivateStruct()).privateConfig.roles;
|
|
226
|
+
const newRoles: PermissionRole[] = [];
|
|
227
|
+
for (const role of roles) {
|
|
228
|
+
const roleInfo = availableRoles.find(r => r.id === role.id);
|
|
229
|
+
if (roleInfo) {
|
|
230
|
+
role.name = roleInfo.name;
|
|
231
|
+
newRoles.push(role);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
globalPermissions.roles = newRoles;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
176
238
|
// Platform permissions
|
|
177
239
|
user.permissions.clearEmptyPermissions();
|
|
178
240
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Database } from '@simonbackx/simple-database';
|
|
2
|
+
import { logger, StyledText } from '@simonbackx/simple-logging';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
const UNIQUE_KEY_NAME = 'email';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This service is responsible for creating MySQL unique constraints on boot depending on the environment configuration.
|
|
9
|
+
* If STAMHOOFD.userMode = 'platform' then we'll create a unique constraint on the email column of the user table.
|
|
10
|
+
* If not, we'll delete the constraint if it exists.
|
|
11
|
+
*/
|
|
12
|
+
export class UniqueUserService {
|
|
13
|
+
static async hasUniqueConstraint() {
|
|
14
|
+
const [results] = await Database.select('SHOW INDEX FROM `users` WHERE Key_name = ?', [UNIQUE_KEY_NAME]);
|
|
15
|
+
return results.length > 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static async check() {
|
|
19
|
+
await logger.setContext({
|
|
20
|
+
prefixes: [
|
|
21
|
+
new StyledText(`[UniqueUserService] `).addClass('unique-user-service', 'tag'),
|
|
22
|
+
],
|
|
23
|
+
tags: ['unique-user-service'],
|
|
24
|
+
}, async () => {
|
|
25
|
+
if (STAMHOOFD.userMode === 'platform') {
|
|
26
|
+
if (!(await this.hasUniqueConstraint())) {
|
|
27
|
+
console.warn('Unique constraint is missing. Creating it now...');
|
|
28
|
+
await this.createConstraint();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
if (await this.hasUniqueConstraint()) {
|
|
33
|
+
console.warn('Unique constraint exists but should be removed. Deleting it now...');
|
|
34
|
+
await this.dropConstraint();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static async createConstraint() {
|
|
41
|
+
try {
|
|
42
|
+
await Database.statement('ALTER TABLE `users` ADD UNIQUE INDEX `' + UNIQUE_KEY_NAME + '` (`email`) USING BTREE;');
|
|
43
|
+
console.log('Unique constraint created.');
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
console.error(chalk.red('Failed to create unique constraint on email column of users table:'));
|
|
47
|
+
console.error(e);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static async dropConstraint() {
|
|
52
|
+
try {
|
|
53
|
+
await Database.statement('ALTER TABLE `users` DROP INDEX `' + UNIQUE_KEY_NAME + '`;');
|
|
54
|
+
console.log('Unique constraint dropped.');
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
console.error(chalk.red('Failed to drop unique constraint on email column of users table:'));
|
|
58
|
+
console.error(e);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|