whitelabel-db 1.3.3 → 1.3.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.
@@ -42,6 +42,7 @@ const LoyaltyProgramTier_1 = require("../models/LoyaltyProgramTier");
42
42
  const BookingLoyaltyRecord_1 = require("../models/BookingLoyaltyRecord");
43
43
  const migration_1 = require("./migration");
44
44
  const connect = (connectionDetails) => __awaiter(void 0, void 0, void 0, function* () {
45
+ var _a;
45
46
  const connection = new sequelize_typescript_1.Sequelize({
46
47
  dialect: 'postgres',
47
48
  host: connectionDetails.host,
@@ -93,8 +94,31 @@ const connect = (connectionDetails) => __awaiter(void 0, void 0, void 0, functio
93
94
  }
94
95
  // Only create tables that don't exist, don't alter existing schema
95
96
  // This prevents conflicts with migrations
96
- yield connection.sync({ force: false, alter: false });
97
- console.log('Database sync completed successfully.');
97
+ try {
98
+ yield connection.sync({ force: false, alter: false });
99
+ console.log('Database sync completed successfully.');
100
+ }
101
+ catch (syncError) {
102
+ // Ignore errors about creating indexes on primary key columns
103
+ // Primary keys already have indexes, so Sequelize trying to create
104
+ // a separate index on them will fail
105
+ const errorMessage = (syncError === null || syncError === void 0 ? void 0 : syncError.message) || '';
106
+ const errorSql = (syncError === null || syncError === void 0 ? void 0 : syncError.sql) || '';
107
+ const originalCode = ((_a = syncError === null || syncError === void 0 ? void 0 : syncError.original) === null || _a === void 0 ? void 0 : _a.code) || '';
108
+ if ((errorMessage.includes('already exists') &&
109
+ errorSql.includes('CREATE INDEX')) ||
110
+ originalCode === '42P07' || // duplicate_table
111
+ originalCode === '42710' || // duplicate_object (index already exists)
112
+ (errorSql.includes('CREATE INDEX') &&
113
+ errorSql.includes('_id"') &&
114
+ errorSql.includes('"id")'))) {
115
+ console.log('Sync completed with expected warnings (indexes on primary keys may already exist).');
116
+ }
117
+ else {
118
+ console.error('Error during database sync:', syncError);
119
+ throw syncError;
120
+ }
121
+ }
98
122
  return connection;
99
123
  }
100
124
  catch (err) {
@@ -0,0 +1,3 @@
1
+ import { QueryInterface } from 'sequelize';
2
+ export declare const up: (queryInterface: QueryInterface) => Promise<void>;
3
+ export declare const down: (queryInterface: QueryInterface) => Promise<void>;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.down = exports.up = void 0;
13
+ // Purpose: Add phoneNo, country, language, currency columns to guests table
14
+ const sequelize_1 = require("sequelize");
15
+ const up = (queryInterface) => __awaiter(void 0, void 0, void 0, function* () {
16
+ try {
17
+ // Check which columns already exist
18
+ const tableDescription = yield queryInterface.describeTable('guests');
19
+ const existingColumns = Object.keys(tableDescription);
20
+ // Add phoneNo column
21
+ if (!existingColumns.includes('phoneNo')) {
22
+ yield queryInterface.addColumn('guests', 'phoneNo', {
23
+ type: sequelize_1.DataTypes.STRING,
24
+ allowNull: true,
25
+ });
26
+ console.log('phoneNo column added successfully.');
27
+ }
28
+ else {
29
+ console.log('phoneNo column already exists, skipping.');
30
+ }
31
+ // Add country column
32
+ if (!existingColumns.includes('country')) {
33
+ yield queryInterface.addColumn('guests', 'country', {
34
+ type: sequelize_1.DataTypes.STRING,
35
+ allowNull: true,
36
+ });
37
+ console.log('country column added successfully.');
38
+ }
39
+ else {
40
+ console.log('country column already exists, skipping.');
41
+ }
42
+ // Add language column
43
+ if (!existingColumns.includes('language')) {
44
+ yield queryInterface.addColumn('guests', 'language', {
45
+ type: sequelize_1.DataTypes.STRING,
46
+ allowNull: true,
47
+ });
48
+ console.log('language column added successfully.');
49
+ }
50
+ else {
51
+ console.log('language column already exists, skipping.');
52
+ }
53
+ // Add currency column
54
+ if (!existingColumns.includes('currency')) {
55
+ yield queryInterface.addColumn('guests', 'currency', {
56
+ type: sequelize_1.DataTypes.STRING,
57
+ allowNull: true,
58
+ });
59
+ console.log('currency column added successfully.');
60
+ }
61
+ else {
62
+ console.log('currency column already exists, skipping.');
63
+ }
64
+ // Add index for phoneNo for faster lookups
65
+ try {
66
+ yield queryInterface.addIndex('guests', ['phoneNo'], {
67
+ name: 'guests_phoneNo_idx',
68
+ });
69
+ console.log('phoneNo index added successfully.');
70
+ }
71
+ catch (error) {
72
+ if (error.message.includes('already exists')) {
73
+ console.log('phoneNo index already exists, skipping.');
74
+ }
75
+ else {
76
+ throw error;
77
+ }
78
+ }
79
+ // Add unique index for (phoneNo, projectId) only when phoneNo is not null
80
+ try {
81
+ yield queryInterface.addIndex('guests', ['phoneNo', 'projectId'], {
82
+ unique: true,
83
+ where: {
84
+ phoneNo: {
85
+ [sequelize_1.Op.ne]: null,
86
+ },
87
+ },
88
+ name: 'guests_phoneNo_projectId_unique',
89
+ });
90
+ console.log('phoneNo-projectId unique index added successfully.');
91
+ }
92
+ catch (error) {
93
+ if (error.message.includes('already exists')) {
94
+ console.log('phoneNo-projectId unique index already exists, skipping.');
95
+ }
96
+ else {
97
+ throw error;
98
+ }
99
+ }
100
+ console.log('phoneNo, country, language, currency columns added to guests table successfully.');
101
+ }
102
+ catch (error) {
103
+ console.error('Error while adding phoneNo, country, language, currency columns:', error.message);
104
+ throw error;
105
+ }
106
+ });
107
+ exports.up = up;
108
+ const down = (queryInterface) => __awaiter(void 0, void 0, void 0, function* () {
109
+ try {
110
+ // Remove the unique index
111
+ try {
112
+ yield queryInterface.removeIndex('guests', 'guests_phoneNo_projectId_unique');
113
+ }
114
+ catch (error) {
115
+ if (error.message.includes('does not exist')) {
116
+ console.log('phoneNo-projectId unique index does not exist, skipping.');
117
+ }
118
+ else {
119
+ throw error;
120
+ }
121
+ }
122
+ // Remove the phoneNo index
123
+ try {
124
+ yield queryInterface.removeIndex('guests', 'guests_phoneNo_idx');
125
+ }
126
+ catch (error) {
127
+ if (error.message.includes('does not exist')) {
128
+ console.log('phoneNo index does not exist, skipping.');
129
+ }
130
+ else {
131
+ throw error;
132
+ }
133
+ }
134
+ // Remove the new columns
135
+ yield queryInterface.removeColumn('guests', 'phoneNo');
136
+ yield queryInterface.removeColumn('guests', 'country');
137
+ yield queryInterface.removeColumn('guests', 'language');
138
+ yield queryInterface.removeColumn('guests', 'currency');
139
+ console.log('phoneNo, country, language, currency columns removed from guests table successfully.');
140
+ }
141
+ catch (error) {
142
+ console.error('Error while removing phoneNo, country, language, currency columns:', error.message);
143
+ throw error;
144
+ }
145
+ });
146
+ exports.down = down;
@@ -6,6 +6,10 @@ export declare class Guest extends Model<Partial<Guest>> {
6
6
  firstName: string;
7
7
  lastName: string;
8
8
  email: string;
9
+ phoneNo: string;
10
+ country: string;
11
+ language: string;
12
+ currency: string;
9
13
  password: string;
10
14
  projectId?: string;
11
15
  liteApiGuestId: number;
@@ -24,14 +28,20 @@ export declare class Guest extends Model<Partial<Guest>> {
24
28
  project?: Project;
25
29
  bookings?: Booking[];
26
30
  static checkEmail(email: string, projectId: string): Promise<void>;
31
+ static checkPhoneNo(phoneNo: string, projectId: string): Promise<void>;
32
+ static checkEmailOrPhone(email?: string, phoneNo?: string, projectId?: string): Promise<void>;
27
33
  generateSession(secret: string): string;
28
34
  static createAnonymous(projectId: string, sessionToken: string): Promise<Guest>;
29
35
  static findBySessionToken(sessionToken: string, projectId: string): Promise<Guest | null>;
30
36
  convertToRegistered(userData: {
31
- firstName: string;
32
- lastName: string;
33
- email: string;
37
+ firstName?: string;
38
+ lastName?: string;
39
+ email?: string;
40
+ phoneNo?: string;
34
41
  password?: string;
42
+ country?: string;
43
+ language?: string;
44
+ currency?: string;
35
45
  }): Promise<this>;
36
46
  updateLastSeen(): Promise<this>;
37
47
  }
@@ -38,6 +38,28 @@ let Guest = Guest_1 = class Guest extends sequelize_typescript_1.Model {
38
38
  }
39
39
  });
40
40
  }
41
+ static checkPhoneNo(phoneNo, projectId) {
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ if (!phoneNo)
44
+ return; // Handle null phone numbers
45
+ if (yield Guest_1.findOne({ where: { phoneNo, projectId } })) {
46
+ throw new Error('This phone number is already registered to another account.');
47
+ }
48
+ });
49
+ }
50
+ static checkEmailOrPhone(email, phoneNo, projectId) {
51
+ return __awaiter(this, void 0, void 0, function* () {
52
+ if (!email && !phoneNo) {
53
+ throw new Error('Either email or phone number must be provided.');
54
+ }
55
+ if (email) {
56
+ yield Guest_1.checkEmail(email, projectId);
57
+ }
58
+ if (phoneNo) {
59
+ yield Guest_1.checkPhoneNo(phoneNo, projectId);
60
+ }
61
+ });
62
+ }
41
63
  generateSession(secret) {
42
64
  return jsonwebtoken_1.default.sign({ id: this.id, projectId: this.projectId }, secret, {
43
65
  algorithm: 'HS256',
@@ -66,14 +88,32 @@ let Guest = Guest_1 = class Guest extends sequelize_typescript_1.Model {
66
88
  // New method to convert anonymous to registered
67
89
  convertToRegistered(userData) {
68
90
  return __awaiter(this, void 0, void 0, function* () {
69
- this.firstName = userData.firstName;
70
- this.lastName = userData.lastName;
71
- this.email = userData.email.toLowerCase();
91
+ if (userData.firstName) {
92
+ this.firstName = userData.firstName;
93
+ }
94
+ if (userData.lastName) {
95
+ this.lastName = userData.lastName;
96
+ }
97
+ if (userData.email) {
98
+ this.email = userData.email.toLowerCase();
99
+ }
100
+ if (userData.phoneNo) {
101
+ this.phoneNo = userData.phoneNo;
102
+ }
72
103
  if (userData.password) {
73
104
  this.password = userData.password;
74
105
  }
106
+ if (userData.country) {
107
+ this.country = userData.country;
108
+ }
109
+ if (userData.language) {
110
+ this.language = userData.language;
111
+ }
112
+ if (userData.currency) {
113
+ this.currency = userData.currency;
114
+ }
75
115
  this.isAnonymous = false;
76
- this.isVerified = false; // Will be verified through email verification process
116
+ this.isVerified = false; // Will be verified through email/phone verification process
77
117
  this.lastSeen = new Date();
78
118
  return yield this.save();
79
119
  });
@@ -118,6 +158,35 @@ __decorate([
118
158
  }),
119
159
  __metadata("design:type", String)
120
160
  ], Guest.prototype, "email", void 0);
161
+ __decorate([
162
+ sequelize_typescript_1.Index,
163
+ (0, sequelize_typescript_1.Column)({
164
+ type: sequelize_typescript_1.DataType.STRING,
165
+ allowNull: true,
166
+ }),
167
+ __metadata("design:type", String)
168
+ ], Guest.prototype, "phoneNo", void 0);
169
+ __decorate([
170
+ (0, sequelize_typescript_1.Column)({
171
+ type: sequelize_typescript_1.DataType.STRING,
172
+ allowNull: true,
173
+ }),
174
+ __metadata("design:type", String)
175
+ ], Guest.prototype, "country", void 0);
176
+ __decorate([
177
+ (0, sequelize_typescript_1.Column)({
178
+ type: sequelize_typescript_1.DataType.STRING,
179
+ allowNull: true,
180
+ }),
181
+ __metadata("design:type", String)
182
+ ], Guest.prototype, "language", void 0);
183
+ __decorate([
184
+ (0, sequelize_typescript_1.Column)({
185
+ type: sequelize_typescript_1.DataType.STRING,
186
+ allowNull: true,
187
+ }),
188
+ __metadata("design:type", String)
189
+ ], Guest.prototype, "currency", void 0);
121
190
  __decorate([
122
191
  (0, sequelize_typescript_1.Column)({
123
192
  type: sequelize_typescript_1.DataType.STRING,
@@ -252,6 +321,15 @@ exports.Guest = Guest = Guest_1 = __decorate([
252
321
  },
253
322
  },
254
323
  },
324
+ {
325
+ unique: true,
326
+ fields: ['phoneNo', 'projectId'],
327
+ where: {
328
+ phoneNo: {
329
+ [sequelize_1.Op.ne]: null,
330
+ },
331
+ },
332
+ },
255
333
  ],
256
334
  })
257
335
  ], Guest);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whitelabel-db",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",