@yrpri/api 9.0.116 → 9.0.119

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/app.js CHANGED
@@ -507,10 +507,13 @@ export class YourPrioritiesApi {
507
507
  this.app.set("views", __dirname + "/views");
508
508
  this.app.set("view engine", "pug");
509
509
  const store = new RedisStore({ client: this.redisClient, ttl: 86400 });
510
+ if (!process.env.SESSION_SECRET) {
511
+ throw new Error("SESSION_SECRET is not set");
512
+ }
510
513
  const sessionConfig = {
511
514
  store: store,
512
515
  name: "yrpri.sid",
513
- secret: process.env.SESSION_SECRET || "not so secret... use env var.",
516
+ secret: process.env.SESSION_SECRET,
514
517
  resave: false,
515
518
  proxy: process.env.USING_NGINX_PROXY ? true : undefined,
516
519
  cookie: { autoSubDomain: true },
package/authorization.cjs CHANGED
@@ -270,7 +270,7 @@ auth.authNeedsGroupAdminForCreate = function (group, req, done) {
270
270
  done(null, false);
271
271
  });
272
272
  };
273
- auth.authNeedsCommunnityAdminForCreate = function (community, req, done) {
273
+ auth.authNeedsCommunityAdminForCreate = function (community, req, done) {
274
274
  models.Community.findOne({
275
275
  where: { id: community.id },
276
276
  attributes: ["id", "access", "user_id", "configuration"],
@@ -1577,7 +1577,7 @@ auth.entity("category", function (req, done) {
1577
1577
  // CREATE
1578
1578
  // Create bulkStatusUpdate
1579
1579
  auth.role("createCommunityBulkStatusUpdate.createBulkStatusUpdate", function (community, req, done) {
1580
- auth.authNeedsCommunnityAdminForCreate(community, req, done);
1580
+ auth.authNeedsCommunityAdminForCreate(community, req, done);
1581
1581
  });
1582
1582
  auth.entity("createCommunityBulkStatusUpdate", function (req, done) {
1583
1583
  var match = req.originalUrl.match(/bulk_status_updates\/(\w+)/);
@@ -1772,7 +1772,7 @@ auth.role("createDomainOrganization.createDomainOrganization", function (domain,
1772
1772
  done(null, false);
1773
1773
  });
1774
1774
  });
1775
- auth.role("createCommunityOrganization.createCommunityOrganization", function (domain, req, done) {
1775
+ auth.role("createCommunityOrganization.createCommunityOrganization", function (community, req, done) {
1776
1776
  models.Community.findOne({
1777
1777
  where: { id: community.id },
1778
1778
  })
@@ -1780,7 +1780,7 @@ auth.role("createCommunityOrganization.createCommunityOrganization", function (d
1780
1780
  if (!auth.isAuthenticated(req)) {
1781
1781
  done(null, false);
1782
1782
  }
1783
- else if (community.access === models.Domain.ACCESS_PUBLIC) {
1783
+ else if (community.access === models.Community.ACCESS_PUBLIC) {
1784
1784
  done(null, true);
1785
1785
  }
1786
1786
  else if (community.user_id === req.user.id) {
@@ -421,7 +421,9 @@ async function initializeIndexCache() {
421
421
  log.error("Failed to initialize index cache", { error });
422
422
  }
423
423
  }
424
- initializeIndexCache();
424
+ if (!process.env.SKIP_INDEX_HTML_CACHE) {
425
+ initializeIndexCache();
426
+ }
425
427
  router.get("/", function (req, res) {
426
428
  sendIndex(req, res);
427
429
  });
@@ -143,7 +143,7 @@ router.post('/login', function (req, res) {
143
143
  const startTime = new Date();
144
144
  log.info('User Login start', { elapsedTime: (new Date() - startTime), userId: req.user ? req.user.id : null });
145
145
  req.sso.authenticate('local-strategy', {}, req, res, function (err, user) {
146
- log.info(`User Login before get ${req.user ? "HASUSER" : "NOUSER"}`, { elapsedTime: (new Date() - startTime), userId: req.user ? req.user.id : null });
146
+ log.info(`User Login before get ${req.user ? "HASUSER" : "NOUSER"}`, err, { elapsedTime: (new Date() - startTime), userId: req.user ? req.user.id : null });
147
147
  getUserWithAll(req.user.id, true, async function (error, user) {
148
148
  log.info('User Login completed', { elapsedTime: (new Date() - startTime), userId: req.user ? req.user.id : null });
149
149
  if (error || !user) {
package/models/domain.cjs CHANGED
@@ -365,6 +365,7 @@ module.exports = (sequelize, DataTypes) => {
365
365
  req.url.indexOf("/login") > -1 ||
366
366
  req.url.indexOf("saml_assertion") > -1) {
367
367
  sequelize.models.Domain.getLoginProviders(req, domain, (error, providers) => {
368
+ log.info("Login Providers", { providers });
368
369
  req.ypDomain.loginProviders = providers;
369
370
  sequelize.models.Domain.getLoginHosts(domain, (error, hosts) => {
370
371
  req.ypDomain.loginHosts = hosts;
package/models/post.cjs CHANGED
@@ -206,6 +206,7 @@ module.exports = (sequelize, DataTypes) => {
206
206
  Post.CONTENT_BLOG = 4;
207
207
  Post.CONTENT_QUESTION = 5;
208
208
  Post.CONTENT_SURVEY = 6;
209
+ Post.CONTENT_AGENT_CONVERSATION = 7;
209
210
  Post.getSearchVector = () => {
210
211
  return 'PostText';
211
212
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yrpri/api",
3
- "version": "9.0.116",
3
+ "version": "9.0.119",
4
4
  "license": "MIT",
5
5
  "author": "Robert Bjarnason & Citizens Foundation",
6
6
  "repository": {
@@ -25,7 +25,7 @@
25
25
  "@google-cloud/vertexai": "^1.10.0",
26
26
  "@google-cloud/vision": "^5.1.0",
27
27
  "@node-saml/passport-saml": "^5.0.1",
28
- "@policysynth/agents": "^1.3.94",
28
+ "@policysynth/agents": "^1.3.99",
29
29
  "async": "^3.2.6",
30
30
  "authorized": "^1.0.0",
31
31
  "aws-sdk": "^2.1692.0",
@@ -4,7 +4,7 @@ export declare class ChatGptImageGenerator implements IImageGenerator {
4
4
  private readonly openAiKey?;
5
5
  constructor(openAiKey?: string);
6
6
  /**
7
- * Generates an image URL from a prompt using OpenAIs gpt-image-1 model.
7
+ * Generates an image URL from a prompt using OpenAI's gpt-image-1 model.
8
8
  * The returned link remains live for ~60 minutes – be sure to download
9
9
  * or cache it right away in the calling service.
10
10
  */
@@ -7,7 +7,7 @@ export class ChatGptImageGenerator {
7
7
  this.openAiKey = openAiKey;
8
8
  }
9
9
  /**
10
- * Generates an image URL from a prompt using OpenAIs gpt-image-1 model.
10
+ * Generates an image URL from a prompt using OpenAI's gpt-image-1 model.
11
11
  * The returned link remains live for ~60 minutes – be sure to download
12
12
  * or cache it right away in the calling service.
13
13
  */
@@ -33,12 +33,15 @@ export class ChatGptImageGenerator {
33
33
  prompt: finalPrompt,
34
34
  quality: "medium",
35
35
  n: 1,
36
- size
36
+ size,
37
37
  });
38
- const url = res?.data?.[0]?.url;
39
- if (url)
40
- return url;
41
- throw new Error("No URL returned");
38
+ console.log("res", JSON.stringify(res, null, 2));
39
+ const b64Json = res?.data?.[0]?.b64_json;
40
+ if (b64Json) {
41
+ // Assuming the image is a PNG, adjust if another format is expected
42
+ return `data:image/png;base64,${b64Json}`;
43
+ }
44
+ throw new Error("No b64_json returned");
42
45
  }
43
46
  catch (err) {
44
47
  retryCount += 1;
@@ -69,11 +69,22 @@ export class CollectionImageGenerator {
69
69
  newImageUrl = imageUrl;
70
70
  }
71
71
  else {
72
- // 2) Download image to temporary location
73
- await this.imageProcessorService.downloadImage(imageUrl, imageFilePath, axios);
74
- console.debug(fs.existsSync(imageFilePath)
75
- ? "File downloaded successfully."
76
- : "File download failed.");
72
+ // 2) Download image to temporary location or write data URI to file
73
+ if (imageUrl.startsWith("data:image")) {
74
+ const base64Data = imageUrl.split(",")[1];
75
+ if (!base64Data) {
76
+ return reject("Invalid data URI format.");
77
+ }
78
+ const imageBuffer = Buffer.from(base64Data, "base64");
79
+ fs.writeFileSync(imageFilePath, imageBuffer);
80
+ console.debug("Data URI written to file successfully.");
81
+ }
82
+ else {
83
+ await this.imageProcessorService.downloadImage(imageUrl, imageFilePath, axios);
84
+ console.debug(fs.existsSync(imageFilePath)
85
+ ? "File downloaded successfully."
86
+ : "File download failed.");
87
+ }
77
88
  // (Optional) If you want to resize the image before upload:
78
89
  // const resizedPath = await this.imageProcessorService.resizeImage(imageFilePath, 1024, 1024);
79
90
  // Upload the `resizedPath` instead of `imageFilePath`
@@ -1,8 +1,8 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
- import { fileURLToPath, pathToFileURL } from 'url';
4
- import crypto from 'crypto';
5
- import bcrypt from 'bcrypt';
3
+ import { fileURLToPath, pathToFileURL } from "url";
4
+ import crypto from "crypto";
5
+ import bcrypt from "bcrypt";
6
6
  import { Sequelize, DataTypes, Op } from "sequelize";
7
7
  import { sequelize as psSequelize } from "@policysynth/agents/dbModels/index.js";
8
8
  const __filename = fileURLToPath(import.meta.url);
@@ -134,7 +134,7 @@ const mainCompoundIndexCommands = [
134
134
  'CREATE INDEX groupheaderimage_idx2_group_id_c ON "GroupHeaderImage" (group_id, created_at)',
135
135
  'CREATE INDEX grouplogoimage_idx2_group_id_c ON "GroupLogoImage" (group_id, created_at)',
136
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)',
137
+ "CREATE INDEX idx2_group_categories_name ON Categories (group_id, name)",
138
138
  'CREATE INDEX organizationlogoimag_idx_organization_id ON "OrganizationLogoImage" (organization_id)',
139
139
  'CREATE INDEX organizationlogoimag_idx_organization_id_u ON "OrganizationLogoImage" (organization_id, updated_at)',
140
140
  'CREATE INDEX organizationlogoimag_idx_organization_id_c ON "OrganizationLogoImage" (organization_id, created_at)',
@@ -145,7 +145,7 @@ const mainCompoundIndexCommands = [
145
145
  'CREATE INDEX pointvideo_idx2_point_id_u ON "PointVideo" (point_id, updated_at)',
146
146
  'CREATE INDEX pointaudio_idx2_point_id_c ON "PointAudio" (point_id, created_at)',
147
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)',
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
149
  'CREATE INDEX userprofileimage_idx2_user_id ON "UserProfileImage" (user_id)',
150
150
  'CREATE INDEX userprofileimage_idx2_user_id_u ON "UserProfileImage" (user_id, updated_at)',
151
151
  'CREATE INDEX userprofileimage_idx2_user_id_c ON "UserProfileImage" (user_id, created_at)',
@@ -167,8 +167,8 @@ const mainCompoundIndexCommands = [
167
167
  'CREATE INDEX idx2_post_images_c ON "PostImage" (post_id, created_at)',
168
168
  'CREATE INDEX idx2_post_audios_c ON "PostAudio" (post_id, created_at)',
169
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)',
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
172
  ];
173
173
  async function createMainCompoundIndexes(sequelizeInstance, indexCommands) {
174
174
  for (const command of indexCommands) {
@@ -190,7 +190,8 @@ async function createMainCompoundIndexes(sequelizeInstance, indexCommands) {
190
190
  async function syncMainDatabase() {
191
191
  console.log("Starting main database synchronization...");
192
192
  const modelsPath = path.join(__dirname, "../models");
193
- const modelFiles = fs.readdirSync(modelsPath)
193
+ const modelFiles = fs
194
+ .readdirSync(modelsPath)
194
195
  .filter((file) => file.indexOf(".") !== 0 &&
195
196
  file.endsWith(".cjs") &&
196
197
  !file.endsWith(".d.cjs") &&
@@ -212,7 +213,8 @@ async function syncMainDatabase() {
212
213
  }
213
214
  const acModelsPath = path.join(__dirname, "../services/models");
214
215
  if (fs.existsSync(acModelsPath)) {
215
- const acModelFiles = fs.readdirSync(acModelsPath)
216
+ const acModelFiles = fs
217
+ .readdirSync(acModelsPath)
216
218
  .filter((file) => file.indexOf(".") !== 0 &&
217
219
  file.endsWith(".cjs") &&
218
220
  !file.endsWith(".d.cjs") &&
@@ -236,7 +238,8 @@ async function syncMainDatabase() {
236
238
  console.warn(`Directory not found, skipping services models: ${acModelsPath}`);
237
239
  }
238
240
  Object.keys(mainDb).forEach((modelName) => {
239
- if (mainDb[modelName] && typeof mainDb[modelName].associate === "function") {
241
+ if (mainDb[modelName] &&
242
+ typeof mainDb[modelName].associate === "function") {
240
243
  mainDb[modelName].associate(mainDb);
241
244
  }
242
245
  });
@@ -326,9 +329,11 @@ async function seedAllModels() {
326
329
  const newUser = mainDb.User.build({
327
330
  email: userEmail,
328
331
  name: userName, // Or a dedicated name argument if preferred
329
- status: 'active',
332
+ status: "active",
330
333
  // Attempt to set default notifications, fallback if AcNotification not on mainDb
331
- notifications_settings: mainDb.AcNotification ? mainDb.AcNotification.defaultNotificationSettings : { email: true },
334
+ notifications_settings: mainDb.AcNotification
335
+ ? mainDb.AcNotification.defaultNotificationSettings
336
+ : { email: true },
332
337
  });
333
338
  // createPasswordHash is an instance method on User model from user.cjs
334
339
  const salt = bcrypt.genSaltSync(10);
@@ -336,31 +341,38 @@ async function seedAllModels() {
336
341
  await newUser.save();
337
342
  console.log(`User ${newUser.email} created with ID: ${newUser.id}`);
338
343
  // Create Domain
339
- const randomDomainName = crypto.randomBytes(8).toString('hex') + ".seed.local"; // Shorter and identifiable
344
+ const randomDomainName = crypto.randomBytes(8).toString("hex") + ".seed.local"; // Shorter and identifiable
340
345
  console.log(`Attempting to create domain: ${randomDomainName} for user ${newUser.id}`);
341
346
  const newDomain = mainDb.Domain.build({
342
347
  name: `Default Domain for ${userName}`,
343
348
  domain_name: randomDomainName,
344
- access: mainDb.Domain.ACCESS_PUBLIC !== undefined ? mainDb.Domain.ACCESS_PUBLIC : 0, // Use constant if available
349
+ access: mainDb.Domain.ACCESS_PUBLIC !== undefined
350
+ ? mainDb.Domain.ACCESS_PUBLIC
351
+ : 0, // Use constant if available
345
352
  default_locale: "en",
346
- ip_address: "::1",
353
+ ip_address: "::1", // Using localhost IP
347
354
  user_agent: "seedModelsScript/1.0",
348
355
  user_id: newUser.id, // Associate domain with the new user
349
356
  configuration: {},
357
+ secret_api_keys: {},
358
+ data: {},
359
+ other_social_media_info: {},
360
+ public_api_keys: {},
361
+ info_texts: {},
350
362
  // Fill other required non-nullable fields based on domain.cjs definition if any
351
363
  // deleted: false, (already defaults to false)
352
364
  });
353
365
  await newDomain.save();
354
366
  console.log(`Domain ${newDomain.name} created with ID: ${newDomain.id} and domain_name: ${newDomain.domain_name}`);
355
367
  // Associate User with Domain
356
- if (typeof newDomain.addDomainUsers === 'function') {
368
+ if (typeof newDomain.addDomainUsers === "function") {
357
369
  await newDomain.addDomainUsers(newUser);
358
370
  console.log(`User ${newUser.email} added to domain ${newDomain.domain_name} as a user.`);
359
371
  }
360
372
  else {
361
373
  console.warn(`newDomain.addDomainUsers is not a function. Skipping adding user to domain users.`);
362
374
  }
363
- if (typeof newDomain.addDomainAdmins === 'function') {
375
+ if (typeof newDomain.addDomainAdmins === "function") {
364
376
  await newDomain.addDomainAdmins(newUser);
365
377
  console.log(`User ${newUser.email} added to domain ${newDomain.domain_name} as an admin.`);
366
378
  }