whitelabel-db 1.1.93 → 1.1.95

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.
@@ -5,4 +5,4 @@ export interface ConnectionDetails {
5
5
  password: string;
6
6
  dbname: string;
7
7
  }
8
- export declare const connect: (connectionDetails: ConnectionDetails) => Promise<void | Sequelize>;
8
+ export declare const connect: (connectionDetails: ConnectionDetails) => Promise<Sequelize>;
@@ -72,14 +72,17 @@ const connect = (connectionDetails) => __awaiter(void 0, void 0, void 0, functio
72
72
  CollectionItemCustomField_1.CollectionItemCustomField,
73
73
  ],
74
74
  });
75
- return connection
76
- .authenticate()
77
- .then(() => {
75
+ try {
76
+ yield connection.authenticate();
78
77
  console.log('Database connection has been established successfully.');
79
- return connection.sync();
80
- })
81
- .catch((err) => {
78
+ // Ensure sync completes and creates necessary tables including SequelizeMeta
79
+ yield connection.sync({ alter: false });
80
+ console.log('Database sync completed successfully.');
81
+ return connection;
82
+ }
83
+ catch (err) {
82
84
  console.error('Unable to connect to the database:', err);
83
- });
85
+ throw err;
86
+ }
84
87
  });
85
88
  exports.connect = connect;
@@ -44,14 +44,37 @@ const runMigrations = (sequelize) => __awaiter(void 0, void 0, void 0, function*
44
44
  console.error(`Migrations directory not found at: ${migrationsPath}`);
45
45
  return;
46
46
  }
47
+ // Ensure SequelizeMeta table exists - this handles cases where sync() didn't create it
48
+ try {
49
+ yield sequelize.query(`
50
+ CREATE TABLE IF NOT EXISTS "SequelizeMeta" (
51
+ "name" VARCHAR(255) NOT NULL,
52
+ PRIMARY KEY ("name")
53
+ );
54
+ `);
55
+ console.log('SequelizeMeta table ensured to exist');
56
+ }
57
+ catch (error) {
58
+ console.error('Error creating SequelizeMeta table:', error);
59
+ throw error;
60
+ }
47
61
  // Read and sort migration files
48
62
  const migrationFiles = fs_1.default
49
63
  .readdirSync(migrationsPath)
50
64
  .filter((file) => file.match(/^\d{3}-.+\.ts$/) || file.match(/^\d{3}-.+\.js$/)) // Matches .ts and .js files
51
65
  .filter((file) => !file.endsWith('.d.ts')) // Exclude .d.ts files
52
66
  .sort(); // Sort files by name
53
- const [results] = yield sequelize.query('SELECT name FROM "SequelizeMeta"');
54
- const executedMigrations = results.map((row) => row.name);
67
+ // Get executed migrations - now this should always work since we ensured the table exists
68
+ let executedMigrations = [];
69
+ try {
70
+ const [results] = yield sequelize.query('SELECT name FROM "SequelizeMeta"');
71
+ executedMigrations = results.map((row) => row.name);
72
+ console.log(`Found ${executedMigrations.length} previously executed migrations`);
73
+ }
74
+ catch (error) {
75
+ console.error('Unexpected error fetching executed migrations:', error);
76
+ throw error;
77
+ }
55
78
  for (const file of migrationFiles) {
56
79
  if (!executedMigrations.includes(file)) {
57
80
  const migration = yield Promise.resolve(`${path_1.default.join(migrationsPath, file)}`).then(s => __importStar(require(s)));
@@ -59,10 +82,11 @@ const runMigrations = (sequelize) => __awaiter(void 0, void 0, void 0, function*
59
82
  console.log(`Running migration: ${file}`);
60
83
  yield migration.up(sequelize.getQueryInterface());
61
84
  yield sequelize.query(`INSERT INTO "SequelizeMeta" (name) VALUES ('${file}')`);
85
+ console.log(`Completed migration: ${file}`);
62
86
  }
63
87
  }
64
88
  else {
65
- console.log(`Skipping migration: ${file}`);
89
+ console.log(`Skipping migration: ${file} (already executed)`);
66
90
  }
67
91
  }
68
92
  console.log('Migrations completed.');
@@ -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,104 @@
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 anonymous user support to guests table
14
+ const sequelize_1 = require("sequelize");
15
+ const up = (queryInterface) => __awaiter(void 0, void 0, void 0, function* () {
16
+ try {
17
+ // Step 1: Drop the existing unique index on (email, projectId)
18
+ yield queryInterface.removeIndex('guests', ['email', 'projectId']);
19
+ // Step 2: Make firstName, lastName, and email nullable
20
+ yield queryInterface.changeColumn('guests', 'firstName', {
21
+ type: sequelize_1.DataTypes.STRING,
22
+ allowNull: true,
23
+ });
24
+ yield queryInterface.changeColumn('guests', 'lastName', {
25
+ type: sequelize_1.DataTypes.STRING,
26
+ allowNull: true,
27
+ });
28
+ yield queryInterface.changeColumn('guests', 'email', {
29
+ type: sequelize_1.DataTypes.STRING,
30
+ allowNull: true,
31
+ });
32
+ // Step 3: Add new columns for anonymous user support
33
+ yield queryInterface.addColumn('guests', 'isAnonymous', {
34
+ type: sequelize_1.DataTypes.BOOLEAN,
35
+ allowNull: false,
36
+ defaultValue: false, // Existing users are not anonymous
37
+ });
38
+ yield queryInterface.addColumn('guests', 'sessionToken', {
39
+ type: sequelize_1.DataTypes.STRING,
40
+ allowNull: true,
41
+ });
42
+ yield queryInterface.addColumn('guests', 'lastSeen', {
43
+ type: sequelize_1.DataTypes.DATE,
44
+ allowNull: false,
45
+ defaultValue: sequelize_1.DataTypes.NOW,
46
+ });
47
+ // Step 4: Create new partial unique index for (email, projectId) only when email is not null
48
+ yield queryInterface.addIndex('guests', ['email', 'projectId'], {
49
+ unique: true,
50
+ where: {
51
+ email: {
52
+ [sequelize_1.Op.ne]: null,
53
+ },
54
+ },
55
+ name: 'guests_email_projectId_unique',
56
+ });
57
+ // Step 5: Add index for sessionToken for faster lookups
58
+ yield queryInterface.addIndex('guests', ['sessionToken'], {
59
+ name: 'guests_sessionToken_idx',
60
+ });
61
+ console.log('Anonymous user support added to guests table successfully.');
62
+ }
63
+ catch (error) {
64
+ console.error('Error while adding anonymous user support:', error.message);
65
+ throw error;
66
+ }
67
+ });
68
+ exports.up = up;
69
+ const down = (queryInterface) => __awaiter(void 0, void 0, void 0, function* () {
70
+ try {
71
+ // Step 1: Remove the new indexes
72
+ yield queryInterface.removeIndex('guests', 'guests_email_projectId_unique');
73
+ yield queryInterface.removeIndex('guests', 'guests_sessionToken_idx');
74
+ // Step 2: Remove the new columns
75
+ yield queryInterface.removeColumn('guests', 'isAnonymous');
76
+ yield queryInterface.removeColumn('guests', 'sessionToken');
77
+ yield queryInterface.removeColumn('guests', 'lastSeen');
78
+ // Step 3: Revert firstName, lastName, and email to not nullable
79
+ // Note: This might fail if there are existing null values
80
+ yield queryInterface.changeColumn('guests', 'firstName', {
81
+ type: sequelize_1.DataTypes.STRING,
82
+ allowNull: false,
83
+ });
84
+ yield queryInterface.changeColumn('guests', 'lastName', {
85
+ type: sequelize_1.DataTypes.STRING,
86
+ allowNull: false,
87
+ });
88
+ yield queryInterface.changeColumn('guests', 'email', {
89
+ type: sequelize_1.DataTypes.STRING,
90
+ allowNull: false,
91
+ });
92
+ // Step 4: Recreate the original unique index
93
+ yield queryInterface.addIndex('guests', ['email', 'projectId'], {
94
+ unique: true,
95
+ name: 'guests_email_projectId_unique',
96
+ });
97
+ console.log('Anonymous user support removed from guests table successfully.');
98
+ }
99
+ catch (error) {
100
+ console.error('Error while removing anonymous user support:', error.message);
101
+ throw error;
102
+ }
103
+ });
104
+ exports.down = down;
@@ -14,6 +14,9 @@ export declare class Guest extends Model<Partial<Guest>> {
14
14
  isVerified: boolean;
15
15
  isAgent: boolean;
16
16
  bypassPayment: boolean;
17
+ isAnonymous: boolean;
18
+ sessionToken: string;
19
+ lastSeen: Date;
17
20
  vouchers: number[];
18
21
  favoriteHotelIds: string[];
19
22
  feedbackCount: number;
@@ -22,4 +25,13 @@ export declare class Guest extends Model<Partial<Guest>> {
22
25
  bookings?: Booking[];
23
26
  static checkEmail(email: string, projectId: string): Promise<void>;
24
27
  generateSession(secret: string): string;
28
+ static createAnonymous(projectId: string, sessionToken: string): Promise<Guest>;
29
+ static findBySessionToken(sessionToken: string, projectId: string): Promise<Guest | null>;
30
+ convertToRegistered(userData: {
31
+ firstName: string;
32
+ lastName: string;
33
+ email: string;
34
+ password?: string;
35
+ }): Promise<this>;
36
+ updateLastSeen(): Promise<this>;
25
37
  }
@@ -24,12 +24,15 @@ var Guest_1;
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.Guest = void 0;
26
26
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
27
+ const sequelize_1 = require("sequelize");
27
28
  const sequelize_typescript_1 = require("sequelize-typescript");
28
29
  const Project_1 = require("./Project");
29
30
  const Booking_1 = require("./Booking");
30
31
  let Guest = Guest_1 = class Guest extends sequelize_typescript_1.Model {
31
32
  static checkEmail(email, projectId) {
32
33
  return __awaiter(this, void 0, void 0, function* () {
34
+ if (!email)
35
+ return; // Handle null emails for anonymous users
33
36
  if (yield Guest_1.findOne({ where: { email: email.toLowerCase(), projectId } })) {
34
37
  throw new Error('This email is already registered to another account.');
35
38
  }
@@ -41,6 +44,47 @@ let Guest = Guest_1 = class Guest extends sequelize_typescript_1.Model {
41
44
  expiresIn: 1209600, // 14 days
42
45
  });
43
46
  }
47
+ // New method to create anonymous guest
48
+ static createAnonymous(projectId, sessionToken) {
49
+ return __awaiter(this, void 0, void 0, function* () {
50
+ return yield Guest_1.create({
51
+ projectId,
52
+ isAnonymous: true,
53
+ sessionToken,
54
+ lastSeen: new Date(),
55
+ });
56
+ });
57
+ }
58
+ // New method to find guest by session token
59
+ static findBySessionToken(sessionToken, projectId) {
60
+ return __awaiter(this, void 0, void 0, function* () {
61
+ return yield Guest_1.findOne({
62
+ where: { sessionToken, projectId },
63
+ });
64
+ });
65
+ }
66
+ // New method to convert anonymous to registered
67
+ convertToRegistered(userData) {
68
+ return __awaiter(this, void 0, void 0, function* () {
69
+ this.firstName = userData.firstName;
70
+ this.lastName = userData.lastName;
71
+ this.email = userData.email.toLowerCase();
72
+ if (userData.password) {
73
+ this.password = userData.password;
74
+ }
75
+ this.isAnonymous = false;
76
+ this.isVerified = false; // Will be verified through email verification process
77
+ this.lastSeen = new Date();
78
+ return yield this.save();
79
+ });
80
+ }
81
+ // New method to update last seen
82
+ updateLastSeen() {
83
+ return __awaiter(this, void 0, void 0, function* () {
84
+ this.lastSeen = new Date();
85
+ return yield this.save();
86
+ });
87
+ }
44
88
  };
45
89
  exports.Guest = Guest;
46
90
  __decorate([
@@ -55,14 +99,14 @@ __decorate([
55
99
  __decorate([
56
100
  (0, sequelize_typescript_1.Column)({
57
101
  type: sequelize_typescript_1.DataType.STRING,
58
- allowNull: false,
102
+ allowNull: true, // Changed from false to support anonymous users
59
103
  }),
60
104
  __metadata("design:type", String)
61
105
  ], Guest.prototype, "firstName", void 0);
62
106
  __decorate([
63
107
  (0, sequelize_typescript_1.Column)({
64
108
  type: sequelize_typescript_1.DataType.STRING,
65
- allowNull: false,
109
+ allowNull: true, // Changed from false to support anonymous users
66
110
  }),
67
111
  __metadata("design:type", String)
68
112
  ], Guest.prototype, "lastName", void 0);
@@ -70,7 +114,7 @@ __decorate([
70
114
  sequelize_typescript_1.Index,
71
115
  (0, sequelize_typescript_1.Column)({
72
116
  type: sequelize_typescript_1.DataType.STRING,
73
- allowNull: false,
117
+ allowNull: true, // Changed from false to support anonymous users
74
118
  }),
75
119
  __metadata("design:type", String)
76
120
  ], Guest.prototype, "email", void 0);
@@ -131,6 +175,28 @@ __decorate([
131
175
  }),
132
176
  __metadata("design:type", Boolean)
133
177
  ], Guest.prototype, "bypassPayment", void 0);
178
+ __decorate([
179
+ (0, sequelize_typescript_1.Column)({
180
+ type: sequelize_typescript_1.DataType.BOOLEAN,
181
+ defaultValue: false, // Default to anonymous
182
+ }),
183
+ __metadata("design:type", Boolean)
184
+ ], Guest.prototype, "isAnonymous", void 0);
185
+ __decorate([
186
+ sequelize_typescript_1.Index,
187
+ (0, sequelize_typescript_1.Column)({
188
+ type: sequelize_typescript_1.DataType.STRING,
189
+ allowNull: true,
190
+ }),
191
+ __metadata("design:type", String)
192
+ ], Guest.prototype, "sessionToken", void 0);
193
+ __decorate([
194
+ (0, sequelize_typescript_1.Column)({
195
+ type: sequelize_typescript_1.DataType.DATE,
196
+ defaultValue: sequelize_typescript_1.DataType.NOW,
197
+ }),
198
+ __metadata("design:type", Date)
199
+ ], Guest.prototype, "lastSeen", void 0);
134
200
  __decorate([
135
201
  (0, sequelize_typescript_1.Column)({
136
202
  type: sequelize_typescript_1.DataType.ARRAY(sequelize_typescript_1.DataType.JSONB),
@@ -180,6 +246,11 @@ exports.Guest = Guest = Guest_1 = __decorate([
180
246
  {
181
247
  unique: true,
182
248
  fields: ['email', 'projectId'],
249
+ where: {
250
+ email: {
251
+ [sequelize_1.Op.ne]: null,
252
+ },
253
+ },
183
254
  },
184
255
  ],
185
256
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whitelabel-db",
3
- "version": "1.1.93",
3
+ "version": "1.1.95",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",