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.
- package/dist/libs/database.d.ts +1 -1
- package/dist/libs/database.js +10 -7
- package/dist/libs/migration.js +27 -3
- package/dist/migrations/023-add-anonymous-user-support.d.ts +3 -0
- package/dist/migrations/023-add-anonymous-user-support.js +104 -0
- package/dist/models/Guest.d.ts +12 -0
- package/dist/models/Guest.js +74 -3
- package/package.json +1 -1
package/dist/libs/database.d.ts
CHANGED
package/dist/libs/database.js
CHANGED
|
@@ -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
|
-
|
|
76
|
-
.authenticate()
|
|
77
|
-
.then(() => {
|
|
75
|
+
try {
|
|
76
|
+
yield connection.authenticate();
|
|
78
77
|
console.log('Database connection has been established successfully.');
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
.
|
|
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;
|
package/dist/libs/migration.js
CHANGED
|
@@ -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
|
-
|
|
54
|
-
|
|
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,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;
|
package/dist/models/Guest.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/models/Guest.js
CHANGED
|
@@ -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
|
})
|