@yrpri/api 9.0.110 → 9.0.111

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.
@@ -1,4 +1,4 @@
1
- import { sequelize as psSequelize } from "@policysynth/agents/dbModels/index.js";
1
+ import { sequelize as psSequelize, User, } from "@policysynth/agents/dbModels/index.js";
2
2
  import { PsAiModel } from "@policysynth/agents/dbModels/aiModel.js";
3
3
  import { PsAgentClass } from "@policysynth/agents/dbModels/agentClass.js";
4
4
  import { PsAgentClassCategories } from "@policysynth/agents/agentCategories.js";
@@ -33,15 +33,15 @@ export class NewAiModelSetup {
33
33
  */
34
34
  static async initializeModels() {
35
35
  try {
36
+ if (process.env.FORCE_DB_SYNC || process.env.NODE_ENV === "development") {
37
+ await psSequelize.sync();
38
+ }
36
39
  console.log("All Models Loaded Init");
37
40
  for (const modelName of Object.keys(psModels)) {
38
41
  if (typeof psModels[modelName].associate === "function") {
39
42
  await psModels[modelName].associate(psSequelize.models);
40
43
  }
41
44
  }
42
- if (process.env.FORCE_DB_SYNC || process.env.NODE_ENV === "development") {
43
- await psSequelize.sync();
44
- }
45
45
  console.log("All models initialized successfully.");
46
46
  }
47
47
  catch (error) {
@@ -410,7 +410,7 @@ export class NewAiModelSetup {
410
410
  costInTokensPerMillion: 2,
411
411
  costOutTokensPerMillion: 8,
412
412
  currency: "USD",
413
- cacheCostInTokensPerMillion: 0.5
413
+ cacheCostInTokensPerMillion: 0.5,
414
414
  },
415
415
  maxTokensOut: 100000,
416
416
  defaultTemperature: 0.7,
@@ -587,7 +587,15 @@ export class NewAiModelSetup {
587
587
  static setupAiModels(userId) {
588
588
  setTimeout(async () => {
589
589
  console.log("Seeding AI models");
590
- await NewAiModelSetup.seedAiModels(userId);
590
+ const user = await User.findOne({
591
+ attributes: ["id"],
592
+ where: { id: userId },
593
+ });
594
+ if (!user) {
595
+ console.error("User not found");
596
+ return;
597
+ }
598
+ await NewAiModelSetup.seedAiModels(user.id);
591
599
  }, 100);
592
600
  }
593
601
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yrpri/api",
3
- "version": "9.0.110",
3
+ "version": "9.0.111",
4
4
  "license": "MIT",
5
5
  "author": "Robert Bjarnason & Citizens Foundation",
6
6
  "repository": {
@@ -98,6 +98,7 @@
98
98
  "@maxmind/geoip2-node": "^6.1.0",
99
99
  "@tsconfig/node18": "^18.2.4",
100
100
  "@tsconfig/node22": "^22.0.1",
101
+ "@types/bcrypt": "^5.0.2",
101
102
  "@types/bunyan": "^1.8.11",
102
103
  "@types/bunyan-prettystream": "^0.1.35",
103
104
  "@types/compression": "^1.7.5",
@@ -0,0 +1,2 @@
1
+ declare function seedAllModels(): Promise<void>;
2
+ export { seedAllModels };
@@ -0,0 +1,388 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath, pathToFileURL } from 'url';
4
+ import crypto from 'crypto';
5
+ import bcrypt from 'bcrypt';
6
+ import { Sequelize, DataTypes, Op } from "sequelize";
7
+ import { sequelize as psSequelize } from "@policysynth/agents/dbModels/index.js";
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ // PolicySynth Models
11
+ import { PsAiModel } from "@policysynth/agents/dbModels/aiModel.js";
12
+ import { PsAgentClass } from "@policysynth/agents/dbModels/agentClass.js";
13
+ import { PsExternalApiUsage } from "@policysynth/agents/dbModels/externalApiUsage.js";
14
+ import { PsExternalApi } from "@policysynth/agents/dbModels/externalApis.js";
15
+ import { PsModelUsage } from "@policysynth/agents/dbModels/modelUsage.js";
16
+ import { PsAgent } from "@policysynth/agents/dbModels/agent.js";
17
+ import { PsAgentAuditLog } from "@policysynth/agents/dbModels/agentAuditLog.js";
18
+ import { PsAgentConnector } from "@policysynth/agents/dbModels/agentConnector.js";
19
+ import { PsAgentConnectorClass } from "@policysynth/agents/dbModels/agentConnectorClass.js";
20
+ import { PsAgentRegistry } from "@policysynth/agents/dbModels/agentRegistry.js";
21
+ const psModels = {
22
+ PsAgentClass,
23
+ PsExternalApiUsage,
24
+ PsModelUsage,
25
+ PsAgentConnector,
26
+ PsAgent,
27
+ PsAgentAuditLog,
28
+ PsAgentConnectorClass,
29
+ PsAgentRegistry,
30
+ PsAiModel,
31
+ PsExternalApi,
32
+ };
33
+ const env = process.env.NODE_ENV || "development";
34
+ let mainSequelize;
35
+ // Operator aliases from server_api/models/index.cjs
36
+ const mainOperatorsAliases = {
37
+ $gt: Op.gt,
38
+ $gte: Op.gte,
39
+ $lt: Op.lt,
40
+ $lte: Op.lte,
41
+ $in: Op.in,
42
+ $and: Op.and,
43
+ $or: Op.or,
44
+ $eq: Op.eq,
45
+ $ne: Op.ne,
46
+ $is: Op.is,
47
+ $not: Op.not,
48
+ $between: Op.between,
49
+ $notBetween: Op.notBetween,
50
+ $like: Op.like,
51
+ $contains: Op.contains,
52
+ $any: Op.any,
53
+ };
54
+ if (env === "production") {
55
+ if (!process.env.DATABASE_URL) {
56
+ console.error("DATABASE_URL environment variable is not set for production.");
57
+ process.exit(1);
58
+ }
59
+ if (process.env.DISABLE_PG_SSL) {
60
+ mainSequelize = new Sequelize(process.env.DATABASE_URL, {
61
+ dialect: "postgres",
62
+ minifyAliases: true,
63
+ logging: false,
64
+ operatorsAliases: mainOperatorsAliases,
65
+ });
66
+ }
67
+ else {
68
+ mainSequelize = new Sequelize(process.env.DATABASE_URL, {
69
+ dialect: "postgres",
70
+ dialectOptions: {
71
+ ssl: {
72
+ rejectUnauthorized: false,
73
+ },
74
+ },
75
+ minifyAliases: true,
76
+ logging: false,
77
+ operatorsAliases: mainOperatorsAliases,
78
+ });
79
+ }
80
+ }
81
+ else {
82
+ if (!process.env.YP_DEV_DATABASE_NAME ||
83
+ !process.env.YP_DEV_DATABASE_USERNAME ||
84
+ !process.env.YP_DEV_DATABASE_PASSWORD ||
85
+ !process.env.YP_DEV_DATABASE_HOST ||
86
+ !process.env.YP_DEV_DATABASE_PORT) {
87
+ console.error("One or more YP_DEV_DATABASE environment variables are not set for development.");
88
+ process.exit(1);
89
+ }
90
+ try {
91
+ mainSequelize = new Sequelize(process.env.YP_DEV_DATABASE_NAME, process.env.YP_DEV_DATABASE_USERNAME, process.env.YP_DEV_DATABASE_PASSWORD, {
92
+ dialect: "postgres",
93
+ protocol: "postgres",
94
+ host: process.env.YP_DEV_DATABASE_HOST,
95
+ port: parseInt(process.env.YP_DEV_DATABASE_PORT, 10),
96
+ minifyAliases: true,
97
+ dialectOptions: {
98
+ ssl: false,
99
+ rejectUnauthorized: false,
100
+ },
101
+ logging: false, // Set to console.log for verbose output during seeding
102
+ operatorsAliases: mainOperatorsAliases,
103
+ });
104
+ }
105
+ catch (error) {
106
+ console.error("Error initializing Sequelize for development:", error);
107
+ process.exit(1);
108
+ }
109
+ }
110
+ const mainDb = {};
111
+ // Compound index commands from server_api/models/index.cjs
112
+ const mainCompoundIndexCommands = [
113
+ 'CREATE INDEX domainheaderimage_idx2_domain_id ON "DomainHeaderImage" (domain_id)',
114
+ 'CREATE INDEX domainlogoimage_idx2_domain_id ON "DomainLogoImage" (domain_id)',
115
+ 'CREATE INDEX domainlogovideo_idx2_domain_id ON "DomainLogoVideo" (domain_id)',
116
+ 'CREATE INDEX domainheaderimage_idx2_domain_id_u ON "DomainHeaderImage" (domain_id, updated_at)',
117
+ 'CREATE INDEX domainlogoimage_idx2_domain_id_u ON "DomainLogoImage" (domain_id, updated_at)',
118
+ 'CREATE INDEX domainlogovideo_idx2_domain_id_u ON "DomainLogoVideo" (domain_id, updated_at)',
119
+ 'CREATE INDEX domainheaderimage_idx2_domain_id_c ON "DomainHeaderImage" (domain_id, created_at)',
120
+ 'CREATE INDEX domainlogoimage_idx2_domain_id_c ON "DomainLogoImage" (domain_id, created_at)',
121
+ 'CREATE INDEX domainlogovideo_idx2_domain_id_c ON "DomainLogoVideo" (domain_id, created_at)',
122
+ 'CREATE INDEX communityheaderimage_idx2_community_id ON "CommunityHeaderImage" (community_id)',
123
+ 'CREATE INDEX communitylogoimage_idx2_community_id ON "CommunityLogoImage" (community_id)',
124
+ 'CREATE INDEX communitylogovideo_idx2_community_id ON "CommunityLogoVideo" (community_id)',
125
+ 'CREATE INDEX communityheaderimage_idx2_community_id_u ON "CommunityHeaderImage" (community_id, updated_at)',
126
+ 'CREATE INDEX communitylogoimage_idx2_community_id_u ON "CommunityLogoImage" (community_id, updated_at)',
127
+ 'CREATE INDEX communitylogovideo_idx2_community_id_u ON "CommunityLogoVideo" (community_id, updated_at)',
128
+ 'CREATE INDEX communityheaderimage_idx2_community_id_c ON "CommunityHeaderImage" (community_id, created_at)',
129
+ 'CREATE INDEX communitylogoimage_idx2_community_id_c ON "CommunityLogoImage" (community_id, created_at)',
130
+ 'CREATE INDEX communitylogovideo_idx2_community_id_c ON "CommunityLogoVideo" (community_id, created_at)',
131
+ 'CREATE INDEX groupheaderimage_idx2_group_id_u ON "GroupHeaderImage" (group_id, updated_at)',
132
+ 'CREATE INDEX grouplogoimage_idx2_group_id_u ON "GroupLogoImage" (group_id, updated_at)',
133
+ 'CREATE INDEX grouplogovideo_idx2_group_id_u ON "GroupLogoVideo" (group_id, updated_at)',
134
+ 'CREATE INDEX groupheaderimage_idx2_group_id_c ON "GroupHeaderImage" (group_id, created_at)',
135
+ 'CREATE INDEX grouplogoimage_idx2_group_id_c ON "GroupLogoImage" (group_id, created_at)',
136
+ 'CREATE INDEX grouplogovideo_idx2_group_id_c ON "GroupLogoVideo" (group_id, created_at)',
137
+ 'CREATE INDEX idx2_group_categories_name ON Categories (group_id, name)',
138
+ 'CREATE INDEX organizationlogoimag_idx_organization_id ON "OrganizationLogoImage" (organization_id)',
139
+ 'CREATE INDEX organizationlogoimag_idx_organization_id_u ON "OrganizationLogoImage" (organization_id, updated_at)',
140
+ 'CREATE INDEX organizationlogoimag_idx_organization_id_c ON "OrganizationLogoImage" (organization_id, created_at)',
141
+ 'CREATE INDEX organizationuser_idx2_user_id ON "OrganizationUser" (user_id)',
142
+ 'CREATE INDEX pointaudio_idx2_point_id ON "PointAudio" (point_id)',
143
+ 'CREATE INDEX pointvideo_idx2_point_id ON "PointVideo" (point_id)',
144
+ 'CREATE INDEX pointaudio_idx2_point_id_u ON "PointAudio" (point_id, updated_at)',
145
+ 'CREATE INDEX pointvideo_idx2_point_id_u ON "PointVideo" (point_id, updated_at)',
146
+ 'CREATE INDEX pointaudio_idx2_point_id_c ON "PointAudio" (point_id, created_at)',
147
+ 'CREATE INDEX pointvideo_idx2_point_id_c ON "PointVideo" (point_id, created_at)',
148
+ 'CREATE INDEX points_idx2_counter_sum_post_id_status_value_deleted ON points ((counter_quality_up-counter_quality_down), post_id, status, value, deleted)',
149
+ 'CREATE INDEX userprofileimage_idx2_user_id ON "UserProfileImage" (user_id)',
150
+ 'CREATE INDEX userprofileimage_idx2_user_id_u ON "UserProfileImage" (user_id, updated_at)',
151
+ 'CREATE INDEX userprofileimage_idx2_user_id_c ON "UserProfileImage" (user_id, created_at)',
152
+ 'CREATE INDEX categoryiconimage_idx2_category_id ON "CategoryIconImage" (category_id)',
153
+ 'CREATE INDEX idx2_category_icon_images ON "CategoryIconImage" (category_id, updated_at)',
154
+ 'CREATE INDEX idx2_category_icon_images_c ON "CategoryIconImage" (category_id, created_at)',
155
+ 'CREATE INDEX videoimage_idx2_video_id ON "VideoImage" (video_id)',
156
+ 'CREATE INDEX videoimage_idx2_video_id_u ON "VideoImage" (video_id, updated_at)',
157
+ 'CREATE INDEX videoimage_idx2_video_id_c ON "VideoImage" (video_id, created_at)',
158
+ 'CREATE INDEX postaudio_idx2_post_id ON "PostAudio" (post_id)',
159
+ 'CREATE INDEX postvideo_idx2_post_id ON "PostVideo" (post_id)',
160
+ 'CREATE INDEX postheaderimage_idx2_post_id ON "PostHeaderImage" (post_id)',
161
+ 'CREATE INDEX posimage_idx2_post_id ON "PostImage" (post_id)',
162
+ 'CREATE INDEX idx2_post_header_images_u ON "PostHeaderImage" (post_id, updated_at)',
163
+ 'CREATE INDEX idx2_post_images_u ON "PostImage" (post_id, updated_at)',
164
+ 'CREATE INDEX idx2_post_audios_u ON "PostAudio" (post_id, updated_at)',
165
+ 'CREATE INDEX idx2_post_videos_u ON "PostVideo" (post_id, updated_at)',
166
+ 'CREATE INDEX idx2_post_header_images_c ON "PostHeaderImage" (post_id, created_at)',
167
+ 'CREATE INDEX idx2_post_images_c ON "PostImage" (post_id, created_at)',
168
+ 'CREATE INDEX idx2_post_audios_c ON "PostAudio" (post_id, created_at)',
169
+ 'CREATE INDEX idx2_post_videos_c ON "PostVideo" (post_id, created_at)',
170
+ 'CREATE INDEX posts_idx2_counter_sum_group_id_deleted ON posts ((counter_endorsements_up-counter_endorsements_down),group_id,deleted)',
171
+ 'CREATE INDEX posts_idx2_counter_sum_group_id_category_id_deleted ON posts ((counter_endorsements_up-counter_endorsements_down),group_id,category_id,deleted)',
172
+ ];
173
+ async function createMainCompoundIndexes(sequelizeInstance, indexCommands) {
174
+ for (const command of indexCommands) {
175
+ try {
176
+ await sequelizeInstance.query(command);
177
+ console.log(`Successfully created main index: ${command.substring(0, 100)}...`);
178
+ }
179
+ catch (error) {
180
+ if (error.message && error.message.indexOf("already exists") > -1) {
181
+ // console.log(`Main index already exists: ${command.substring(0,100)}...`);
182
+ }
183
+ else {
184
+ console.error(`Error creating main index with command: ${command}`);
185
+ console.error(error.message);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ async function syncMainDatabase() {
191
+ console.log("Starting main database synchronization...");
192
+ const modelsPath = path.join(__dirname, "../models");
193
+ const modelFiles = fs.readdirSync(modelsPath)
194
+ .filter((file) => file.indexOf(".") !== 0 &&
195
+ file.endsWith(".cjs") &&
196
+ !file.endsWith(".d.cjs") &&
197
+ !file.endsWith(".d.cts") &&
198
+ file !== "index.cjs");
199
+ for (const file of modelFiles) {
200
+ const filePath = path.join(modelsPath, file);
201
+ const fileUrl = pathToFileURL(filePath).href;
202
+ try {
203
+ const module = await import(fileUrl);
204
+ const modelFactory = module.default; // Assuming the .cjs files use module.exports = ...
205
+ const model = modelFactory(mainSequelize, DataTypes);
206
+ mainDb[model.name] = model;
207
+ }
208
+ catch (err) {
209
+ console.error(`Error importing model ${file}:`, err);
210
+ throw err; // Re-throw to stop the process if a model fails to load
211
+ }
212
+ }
213
+ const acModelsPath = path.join(__dirname, "../active-citizen/models");
214
+ if (fs.existsSync(acModelsPath)) {
215
+ const acModelFiles = fs.readdirSync(acModelsPath)
216
+ .filter((file) => file.indexOf(".") !== 0 &&
217
+ file.endsWith(".cjs") &&
218
+ !file.endsWith(".d.cjs") &&
219
+ !file.endsWith(".d.cts"));
220
+ for (const file of acModelFiles) {
221
+ const filePath = path.join(acModelsPath, file);
222
+ const fileUrl = pathToFileURL(filePath).href;
223
+ try {
224
+ const module = await import(fileUrl);
225
+ const modelFactory = module.default; // Assuming the .cjs files use module.exports = ...
226
+ const model = modelFactory(mainSequelize, DataTypes);
227
+ mainDb[model.name] = model;
228
+ }
229
+ catch (err) {
230
+ console.error(`Error importing active-citizen model ${file}:`, err);
231
+ throw err; // Re-throw to stop the process if a model fails to load
232
+ }
233
+ }
234
+ }
235
+ else {
236
+ console.warn(`Directory not found, skipping active-citizen models: ${acModelsPath}`);
237
+ }
238
+ Object.keys(mainDb).forEach((modelName) => {
239
+ if (mainDb[modelName] && typeof mainDb[modelName].associate === "function") {
240
+ mainDb[modelName].associate(mainDb);
241
+ }
242
+ });
243
+ // This script is intended for creating a new database, so always force sync.
244
+ await mainSequelize.sync({ force: true });
245
+ console.log("Main database schema forcefully synchronized (tables dropped and recreated).");
246
+ await createMainCompoundIndexes(mainSequelize, mainCompoundIndexCommands);
247
+ if (mainDb.Post && typeof mainDb.Post.addFullTextIndex === "function") {
248
+ console.log("Adding full text index for Post model...");
249
+ await mainDb.Post.addFullTextIndex();
250
+ console.log("Full text index for Post model added.");
251
+ }
252
+ else {
253
+ console.warn("Post model or addFullTextIndex method not found in mainDb. Skipping full text index.");
254
+ }
255
+ console.log("Main database synchronization finished.");
256
+ }
257
+ async function syncPolicySynthDatabase() {
258
+ console.log("Starting PolicySynth database synchronization...");
259
+ try {
260
+ // This script is intended for creating a new database, so always force sync.
261
+ await psSequelize.sync({ force: true });
262
+ console.log("PolicySynth database schema forcefully synchronized (tables dropped and recreated).");
263
+ console.log("Associating PolicySynth models...");
264
+ for (const modelName of Object.keys(psModels)) {
265
+ const model = psModels[modelName];
266
+ if (model && typeof model.associate === "function") {
267
+ model.associate(psSequelize.models);
268
+ }
269
+ }
270
+ console.log("PolicySynth models associated successfully.");
271
+ }
272
+ catch (error) {
273
+ console.error("Error during PolicySynth database synchronization:", error);
274
+ process.exit(1);
275
+ }
276
+ console.log("PolicySynth database synchronization finished.");
277
+ }
278
+ async function seedAllModels() {
279
+ console.log("--- Starting Database Seeding and Synchronization ---");
280
+ console.log("NOTE: This script will forcefully synchronize the database (drop and recreate tables).");
281
+ console.log("NODE_ENV:", env);
282
+ // The following environment variables are logged for informational purposes,
283
+ // but this script will always force database synchronization.
284
+ console.log("FORCE_DB_SYNC (ignored, always true for this script):", process.env.FORCE_DB_SYNC);
285
+ console.log("FORCE_DB_INDEX_SYNC (ignored, indexes created after forced sync):", process.env.FORCE_DB_INDEX_SYNC);
286
+ const args = process.argv.slice(2);
287
+ if (args.length < 2) {
288
+ console.error("Usage: node <script_path> <username/email> <password>");
289
+ process.exit(1);
290
+ }
291
+ const userEmail = args[0].toLowerCase();
292
+ const userName = args[0]; // Using email as name for simplicity, or a fixed name
293
+ const userPassword = args[1];
294
+ // Check for main database config outside of production if not using DATABASE_URL
295
+ if (env !== "production") {
296
+ if (!process.env.YP_DEV_DATABASE_NAME ||
297
+ !process.env.YP_DEV_DATABASE_USERNAME ||
298
+ !process.env.YP_DEV_DATABASE_PASSWORD ||
299
+ !process.env.YP_DEV_DATABASE_HOST ||
300
+ !process.env.YP_DEV_DATABASE_PORT) {
301
+ console.error("Missing YP_DEV_DATABASE environment variables for main database in non-production.");
302
+ process.exit(1);
303
+ }
304
+ }
305
+ else {
306
+ if (!process.env.DATABASE_URL) {
307
+ console.error("Missing DATABASE_URL for production environment.");
308
+ process.exit(1);
309
+ }
310
+ }
311
+ try {
312
+ await syncMainDatabase();
313
+ await syncPolicySynthDatabase();
314
+ console.log("--- Databases Synchronized ---");
315
+ console.log("--- Creating User and Domain ---");
316
+ if (!mainDb.User) {
317
+ console.error("User model (mainDb.User) not found after sync.");
318
+ process.exit(1);
319
+ }
320
+ if (!mainDb.Domain) {
321
+ console.error("Domain model (mainDb.Domain) not found after sync.");
322
+ process.exit(1);
323
+ }
324
+ // Create User
325
+ console.log(`Attempting to create user: ${userEmail}`);
326
+ const newUser = mainDb.User.build({
327
+ email: userEmail,
328
+ name: userName, // Or a dedicated name argument if preferred
329
+ status: 'active',
330
+ // Attempt to set default notifications, fallback if AcNotification not on mainDb
331
+ notifications_settings: mainDb.AcNotification ? mainDb.AcNotification.defaultNotificationSettings : { email: true },
332
+ });
333
+ // createPasswordHash is an instance method on User model from user.cjs
334
+ const salt = bcrypt.genSaltSync(10);
335
+ newUser.encrypted_password = bcrypt.hashSync(userPassword, salt);
336
+ await newUser.save();
337
+ console.log(`User ${newUser.email} created with ID: ${newUser.id}`);
338
+ // Create Domain
339
+ const randomDomainName = crypto.randomBytes(8).toString('hex') + ".seed.local"; // Shorter and identifiable
340
+ console.log(`Attempting to create domain: ${randomDomainName} for user ${newUser.id}`);
341
+ const newDomain = mainDb.Domain.build({
342
+ name: `Default Domain for ${userName}`,
343
+ domain_name: randomDomainName,
344
+ access: mainDb.Domain.ACCESS_PUBLIC !== undefined ? mainDb.Domain.ACCESS_PUBLIC : 0, // Use constant if available
345
+ default_locale: "en",
346
+ ip_address: "::1",
347
+ user_agent: "seedModelsScript/1.0",
348
+ user_id: newUser.id, // Associate domain with the new user
349
+ configuration: {},
350
+ // Fill other required non-nullable fields based on domain.cjs definition if any
351
+ // deleted: false, (already defaults to false)
352
+ });
353
+ await newDomain.save();
354
+ console.log(`Domain ${newDomain.name} created with ID: ${newDomain.id} and domain_name: ${newDomain.domain_name}`);
355
+ // Associate User with Domain
356
+ if (typeof newDomain.addDomainUsers === 'function') {
357
+ await newDomain.addDomainUsers(newUser);
358
+ console.log(`User ${newUser.email} added to domain ${newDomain.domain_name} as a user.`);
359
+ }
360
+ else {
361
+ console.warn(`newDomain.addDomainUsers is not a function. Skipping adding user to domain users.`);
362
+ }
363
+ if (typeof newDomain.addDomainAdmins === 'function') {
364
+ await newDomain.addDomainAdmins(newUser);
365
+ console.log(`User ${newUser.email} added to domain ${newDomain.domain_name} as an admin.`);
366
+ }
367
+ else {
368
+ console.warn(`newDomain.addDomainAdmins is not a function. Skipping adding user to domain admins.`);
369
+ }
370
+ console.log("--- User and Domain Creation Complete ---");
371
+ console.log("--- All model seeding and synchronization complete. ---");
372
+ }
373
+ catch (error) {
374
+ console.error("Unhandled error during seeding process:", error);
375
+ process.exit(1);
376
+ }
377
+ finally {
378
+ console.log("Closing database connections...");
379
+ await mainSequelize.close();
380
+ await psSequelize.close();
381
+ console.log("Database connections closed.");
382
+ }
383
+ }
384
+ seedAllModels().catch((error) => {
385
+ console.error("Fatal error running seedAllModels:", error);
386
+ process.exit(1);
387
+ });
388
+ export { seedAllModels }; // Export if you plan to import and run this elsewhere