@yrpri/api 9.0.117 → 9.0.120

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"],
@@ -932,6 +932,10 @@ auth.entity("group", function (req, done) {
932
932
  match = req.originalUrl.match(/allOurIdeas\/(\w+)/);
933
933
  if (!match)
934
934
  match = req.originalUrl.match(/agents\/(\w+)/);
935
+ if (!match)
936
+ match = req.originalUrl.match(/pdf_processing\/(\w+)/);
937
+ if (!match)
938
+ match = req.originalUrl.match(/feedback\/(\w+)/);
935
939
  if (!match)
936
940
  match = req.originalUrl.match(/assistants\/(\w+)/);
937
941
  if (!match)
@@ -1577,7 +1581,7 @@ auth.entity("category", function (req, done) {
1577
1581
  // CREATE
1578
1582
  // Create bulkStatusUpdate
1579
1583
  auth.role("createCommunityBulkStatusUpdate.createBulkStatusUpdate", function (community, req, done) {
1580
- auth.authNeedsCommunnityAdminForCreate(community, req, done);
1584
+ auth.authNeedsCommunityAdminForCreate(community, req, done);
1581
1585
  });
1582
1586
  auth.entity("createCommunityBulkStatusUpdate", function (req, done) {
1583
1587
  var match = req.originalUrl.match(/bulk_status_updates\/(\w+)/);
@@ -1772,7 +1776,7 @@ auth.role("createDomainOrganization.createDomainOrganization", function (domain,
1772
1776
  done(null, false);
1773
1777
  });
1774
1778
  });
1775
- auth.role("createCommunityOrganization.createCommunityOrganization", function (domain, req, done) {
1779
+ auth.role("createCommunityOrganization.createCommunityOrganization", function (community, req, done) {
1776
1780
  models.Community.findOne({
1777
1781
  where: { id: community.id },
1778
1782
  })
@@ -1780,7 +1784,7 @@ auth.role("createCommunityOrganization.createCommunityOrganization", function (d
1780
1784
  if (!auth.isAuthenticated(req)) {
1781
1785
  done(null, false);
1782
1786
  }
1783
- else if (community.access === models.Domain.ACCESS_PUBLIC) {
1787
+ else if (community.access === models.Community.ACCESS_PUBLIC) {
1784
1788
  done(null, true);
1785
1789
  }
1786
1790
  else if (community.user_id === req.user.id) {
@@ -26,7 +26,7 @@ export declare class AllOurIdeasController {
26
26
  showEarl(req: Request, res: Response): Promise<void>;
27
27
  vote(req: Request, res: Response): Promise<void>;
28
28
  createQuestion(req: Request, res: Response): Promise<void>;
29
- updateCoiceData(req: Request, res: Response): Promise<void>;
29
+ updateChoiceData(req: Request, res: Response): Promise<void>;
30
30
  updateActive(req: Request, res: Response): Promise<void>;
31
31
  updateQuestionName(req: Request, res: Response): Promise<void>;
32
32
  skip(req: Request, res: Response): Promise<void>;
@@ -47,9 +47,9 @@ export class AllOurIdeasController {
47
47
  this.router.post("/:groupId/questions/:questionId/prompts/:promptId/skips", auth.can("view group"), this.skip.bind(this));
48
48
  this.router.post("/:groupId/questions/:questionId/addIdea", auth.can("view group"), this.addIdea.bind(this));
49
49
  this.router.get("/:groupId/questions/:wsClientSocketId/:analysisIndex/:analysisTypeIndex/analysis", auth.can("view group"), this.analysis.bind(this));
50
- this.router.put("/:communityId/questions/:questionId/choices/:choiceId", auth.can("create group"), this.updateCoiceData.bind(this));
51
- this.router.put("/:domainId/questions/:questionId/choices/:choiceId/throughDomain", auth.can("create community"), this.updateCoiceData.bind(this));
52
- this.router.put("/:groupId/questions/:questionId/choices/:choiceId/throughGroup", auth.can("view group"), this.updateCoiceData.bind(this));
50
+ this.router.put("/:communityId/questions/:questionId/choices/:choiceId", auth.can("create group"), this.updateChoiceData.bind(this));
51
+ this.router.put("/:domainId/questions/:questionId/choices/:choiceId/throughDomain", auth.can("create community"), this.updateChoiceData.bind(this));
52
+ this.router.put("/:groupId/questions/:questionId/choices/:choiceId/throughGroup", auth.can("view group"), this.updateChoiceData.bind(this));
53
53
  this.router.put("/:communityId/questions/:questionId/choices/:choiceId/active", auth.can("create group"), this.updateActive.bind(this));
54
54
  this.router.put("/:domainId/questions/:questionId/choices/:choiceId/active/throughDomain", auth.can("create community"), this.updateActive.bind(this));
55
55
  this.router.put("/:communityId/questions/:questionId/name", auth.can("create group"), this.updateQuestionName.bind(this));
@@ -409,7 +409,7 @@ export class AllOurIdeasController {
409
409
  .json({ error: "An error occurred during question creation" });
410
410
  }
411
411
  }
412
- async updateCoiceData(req, res) {
412
+ async updateChoiceData(req, res) {
413
413
  try {
414
414
  const response = await fetch(`${PAIRWISE_API_HOST}/questions/${req.params.questionId}/choices/${req.params.choiceId}.json`, {
415
415
  method: "PUT",
@@ -92,16 +92,27 @@ router.post('/:groupId', auth.can('create category'), function (req, res) {
92
92
  });
93
93
  category.save().then(function () {
94
94
  log.info('Category Created', { category: category, context: 'create', user: toJson(req.user) });
95
- models.AcActivity.createActivity({
96
- type: 'activity.category.created',
97
- userId: req.user.id,
98
- domainId: req.ypDomain.id,
99
- groupId: req.params.groupId,
100
- // communityId: req.ypCommunity ? req.ypCommunity.id : null,
101
- object: { categoryId: category.id }
102
- }, function () {
103
- category.setupImages(req.body, function (error) {
104
- sendCategoryOrError(res, category, 'setupImages', req.user, error);
95
+ models.Group.findOne({
96
+ where: { id: req.params.groupId },
97
+ attributes: ['id', 'community_id'],
98
+ include: [
99
+ {
100
+ model: models.Community,
101
+ attributes: ['id', 'domain_id']
102
+ }
103
+ ]
104
+ }).then(function (group) {
105
+ models.AcActivity.createActivity({
106
+ type: 'activity.category.created',
107
+ userId: req.user.id,
108
+ domainId: group && group.Community ? group.Community.domain_id : req.ypDomain.id,
109
+ groupId: req.params.groupId,
110
+ // communityId: req.ypCommunity ? req.ypCommunity.id : null,
111
+ object: { categoryId: category.id }
112
+ }, function () {
113
+ category.setupImages(req.body, function (error) {
114
+ sendCategoryOrError(res, category, 'setupImages', req.user, error);
115
+ });
105
116
  });
106
117
  });
107
118
  }).catch(function (error) {
@@ -729,6 +729,7 @@ const updateCommunityConfigParameters = function (req, community) {
729
729
  community.set("configuration.disableGroupDynamicFontSizes", truthValueFromBody(req.body.disableGroupDynamicFontSizes));
730
730
  community.set("configuration.hideGroupListCardObjectives", truthValueFromBody(req.body.hideGroupListCardObjectives));
731
731
  community.set("configuration.alwaysHideLogoImage", truthValueFromBody(req.body.alwaysHideLogoImage));
732
+ community.set("configuration.hideItemCount", truthValueFromBody(req.body.hideItemCount));
732
733
  community.set("configuration.recalculateCountersRecursively", truthValueFromBody(req.body.recalculateCountersRecursively));
733
734
  if (req.body.google_analytics_code && req.body.google_analytics_code != "") {
734
735
  community.google_analytics_code = req.body.google_analytics_code;
@@ -812,6 +813,7 @@ const updateCommunityConfigParameters = function (req, community) {
812
813
  ? req.body.themeOverrideBackgroundColor
813
814
  : null);
814
815
  community.set("configuration.sortBySortOrder", truthValueFromBody(req.body.sortBySortOrder));
816
+ community.set("configuration.sortAlphabetically", truthValueFromBody(req.body.sortAlphabetically));
815
817
  community.set("configuration.orderByRandom", truthValueFromBody(req.body.orderByRandom));
816
818
  community.set("configuration.enableFraudDetection", truthValueFromBody(req.body.enableFraudDetection));
817
819
  community.set("configuration.useZiggeo", truthValueFromBody(req.body.useZiggeo));
@@ -1038,16 +1040,28 @@ router.post("/:communityId/:userEmail/invite_user", auth.can("edit community"),
1038
1040
  },
1039
1041
  function (callback) {
1040
1042
  if (!req.query.addToCommunityDirectly) {
1041
- models.AcActivity.inviteCreated({
1042
- email: req.params.userEmail,
1043
- user_id: user ? user.id : null,
1044
- sender_user_id: req.user.id,
1045
- community_id: req.params.communityId,
1046
- sender_name: req.user.name,
1047
- domain_id: req.ypDomain.id,
1048
- invite_id: invite.id,
1049
- token: token,
1050
- }, function (error) {
1043
+ models.Community.findOne({
1044
+ where: {
1045
+ id: req.params.communityId,
1046
+ },
1047
+ attributes: ["id", "domain_id"],
1048
+ })
1049
+ .then(function (community) {
1050
+ const domainId = community ? community.domain_id : req.ypDomain.id;
1051
+ models.AcActivity.inviteCreated({
1052
+ email: req.params.userEmail,
1053
+ user_id: user ? user.id : null,
1054
+ sender_user_id: req.user.id,
1055
+ community_id: req.params.communityId,
1056
+ sender_name: req.user.name,
1057
+ domain_id: domainId,
1058
+ invite_id: invite.id,
1059
+ token: token,
1060
+ }, function (error) {
1061
+ callback(error);
1062
+ });
1063
+ })
1064
+ .catch(function (error) {
1051
1065
  callback(error);
1052
1066
  });
1053
1067
  }
@@ -807,6 +807,7 @@ function updateDomainProperties(domain, req) {
807
807
  domain.set('configuration.ziggeoApplicationToken', (req.body.ziggeoApplicationToken && req.body.ziggeoApplicationToken !== "") ? req.body.ziggeoApplicationToken : null);
808
808
  domain.set('configuration.ga4Tag', (req.body.ga4Tag && req.body.ga4Tag !== "") ? req.body.ga4Tag : null);
809
809
  domain.set('configuration.useLoginOnDomainIfNotLoggedIn', truthValueFromBody(req.body.useLoginOnDomainIfNotLoggedIn));
810
+ domain.set('configuration.forceElectronicIds', truthValueFromBody(req.body.forceElectronicIds));
810
811
  if (req.body.google_analytics_code && req.body.google_analytics_code !== "") {
811
812
  domain.google_analytics_code = req.body.google_analytics_code;
812
813
  }
@@ -854,6 +855,8 @@ function updateDomainProperties(domain, req) {
854
855
  domain.set('configuration.hideDomainNews', truthValueFromBody(req.body.hideDomainNews));
855
856
  domain.set('configuration.hideDomainTabs', truthValueFromBody(req.body.hideDomainTabs));
856
857
  domain.set('configuration.hideAllTabs', truthValueFromBody(req.body.hideAllTabs));
858
+ domain.set('configuration.hideItemCount', truthValueFromBody(req.body.hideItemCount));
859
+ domain.set('configuration.sortAlphabetically', truthValueFromBody(req.body.sortAlphabetically));
857
860
  domain.set('configuration.useFixedTopAppBar', truthValueFromBody(req.body.useFixedTopAppBar));
858
861
  domain.set('configuration.disableArrowBasedTopNavigation', truthValueFromBody(req.body.disableArrowBasedTopNavigation));
859
862
  domain.set('configuration.onlyAllowCreateUserOnInvite', truthValueFromBody(req.body.onlyAllowCreateUserOnInvite));
@@ -181,6 +181,7 @@ var updateGroupConfigParameters = function (req, group) {
181
181
  group.set("configuration.canVote", truthValueFromBody(req.body.canVote));
182
182
  group.set("configuration.canAddNewPosts", truthValueFromBody(req.body.canAddNewPosts));
183
183
  group.set("configuration.disableDebate", truthValueFromBody(req.body.disableDebate));
184
+ group.set("configuration.hideDebate", truthValueFromBody(req.body.hideDebate));
184
185
  group.set("configuration.locationHidden", truthValueFromBody(req.body.locationHidden));
185
186
  group.set("configuration.showWhoPostedPosts", truthValueFromBody(req.body.showWhoPostedPosts));
186
187
  group.set("configuration.allowAnonymousUsers", truthValueFromBody(req.body.allowAnonymousUsers));
@@ -287,6 +288,7 @@ var updateGroupConfigParameters = function (req, group) {
287
288
  group.set("configuration.showNameUnderLogoImage", truthValueFromBody(req.body.showNameUnderLogoImage));
288
289
  group.set("configuration.alwaysHideLogoImage", truthValueFromBody(req.body.alwaysHideLogoImage));
289
290
  group.set("configuration.hideStatsAndMembership", truthValueFromBody(req.body.hideStatsAndMembership));
291
+ group.set("configuration.hideItemCount", truthValueFromBody(req.body.hideItemCount));
290
292
  group.set("configuration.centerGroupName", truthValueFromBody(req.body.centerGroupName));
291
293
  group.set("configuration.noGroupCardShadow", truthValueFromBody(req.body.noGroupCardShadow));
292
294
  group.set("configuration.hideNewestFromFilter", truthValueFromBody(req.body.hideNewestFromFilter));
@@ -1088,16 +1090,36 @@ router.post("/:groupId/:userEmail/invite_user", auth.can("edit group"), function
1088
1090
  },
1089
1091
  function (callback) {
1090
1092
  if (!req.query.addToGroupDirectly) {
1091
- models.AcActivity.inviteCreated({
1092
- email: req.params.userEmail,
1093
- user_id: user ? user.id : null,
1094
- sender_user_id: req.user.id,
1095
- sender_name: req.user.name,
1096
- group_id: req.params.groupId,
1097
- domain_id: req.ypDomain.id,
1098
- invite_id: invite.id,
1099
- token: token,
1100
- }, function (error) {
1093
+ models.Group.findOne({
1094
+ where: {
1095
+ id: req.params.groupId,
1096
+ },
1097
+ attributes: ["id", "community_id"],
1098
+ include: [
1099
+ {
1100
+ model: models.Community,
1101
+ attributes: ["id", "domain_id"],
1102
+ },
1103
+ ],
1104
+ })
1105
+ .then(function (group) {
1106
+ const domainId = group && group.Community
1107
+ ? group.Community.domain_id
1108
+ : req.ypDomain.id;
1109
+ models.AcActivity.inviteCreated({
1110
+ email: req.params.userEmail,
1111
+ user_id: user ? user.id : null,
1112
+ sender_user_id: req.user.id,
1113
+ sender_name: req.user.name,
1114
+ group_id: req.params.groupId,
1115
+ domain_id: domainId,
1116
+ invite_id: invite.id,
1117
+ token: token,
1118
+ }, function (error) {
1119
+ callback(error);
1120
+ });
1121
+ })
1122
+ .catch(function (error) {
1101
1123
  callback(error);
1102
1124
  });
1103
1125
  }
@@ -43,17 +43,28 @@ var sendError = function (res, image, context, user, error) {
43
43
  res.sendStatus(500);
44
44
  };
45
45
  var sendPostUserImageActivity = function (req, type, post, image, callback) {
46
- models.AcActivity.createActivity({
47
- type: type,
48
- userId: post.user_id,
49
- domainId: req.ypDomain.id,
50
- groupId: post.group_id,
51
- // communityId: req.ypCommunity ? req.ypCommunity.id : null,
52
- postId: post.id,
53
- imageId: image.id,
54
- access: models.AcActivity.ACCESS_PUBLIC,
55
- }, function (error) {
56
- callback(error);
46
+ models.Group.findOne({
47
+ where: { id: post.group_id },
48
+ attributes: ["id", "community_id"],
49
+ include: [
50
+ {
51
+ model: models.Community,
52
+ attributes: ["id", "domain_id"],
53
+ },
54
+ ],
55
+ }).then(function (group) {
56
+ models.AcActivity.createActivity({
57
+ type: type,
58
+ userId: post.user_id,
59
+ domainId: group && group.Community ? group.Community.domain_id : req.ypDomain.id,
60
+ groupId: post.group_id,
61
+ // communityId: req.ypCommunity ? req.ypCommunity.id : null,
62
+ postId: post.id,
63
+ imageId: image.id,
64
+ access: models.AcActivity.ACCESS_PUBLIC,
65
+ }, function (error) {
66
+ callback(error);
67
+ });
57
68
  });
58
69
  };
59
70
  var addUserImageToPost = function (postId, imageId, callback) {
@@ -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
  });
@@ -422,40 +422,53 @@ router.put("/:pointId", auth.can("edit point"), function (req, res) {
422
422
  userId: req.user ? req.user.id : -1,
423
423
  });
424
424
  queue.add("process-similarities", { type: "update-collection", pointId: point.id }, "low");
425
- models.AcActivity.createActivity({
426
- type: "activity.point.edited",
427
- userId: point.user_id,
428
- domainId: req.ypDomain.id,
429
- // communityId: req.ypCommunity ? req.ypCommunity.id : null,
430
- groupId: point.group_id,
431
- postId: point.post_id,
432
- pointId: point.id,
433
- access: models.AcActivity.ACCESS_PUBLIC,
434
- }, function (error) {
435
- loadPointWithAll(point.id, function (error, loadedPoint) {
436
- if (error) {
437
- log.error("Could not reload point point", {
438
- err: error,
439
- context: "createPoint",
440
- user: toJson(req.user.simple()),
441
- });
442
- res.sendStatus(500);
443
- }
444
- else {
445
- if (loadedPoint.PointRevisions &&
446
- loadedPoint.PointRevisions.length > 0 &&
447
- loadedPoint.PointRevisions[loadedPoint.PointRevisions.length - 1].content !== "") {
448
- log.info("process-moderation point toxicity after create point");
449
- queue.add("process-moderation", {
450
- type: "estimate-point-toxicity",
451
- pointId: loadedPoint.id,
452
- }, "high");
425
+ models.Group.findOne({
426
+ where: { id: point.group_id },
427
+ attributes: ["id", "community_id"],
428
+ include: [
429
+ {
430
+ model: models.Community,
431
+ attributes: ["id", "domain_id"],
432
+ },
433
+ ],
434
+ }).then(function (group) {
435
+ models.AcActivity.createActivity({
436
+ type: "activity.point.edited",
437
+ userId: point.user_id,
438
+ domainId: group && group.Community
439
+ ? group.Community.domain_id
440
+ : req.ypDomain.id,
441
+ // communityId: req.ypCommunity ? req.ypCommunity.id : null,
442
+ groupId: point.group_id,
443
+ postId: point.post_id,
444
+ pointId: point.id,
445
+ access: models.AcActivity.ACCESS_PUBLIC,
446
+ }, function (error) {
447
+ loadPointWithAll(point.id, function (error, loadedPoint) {
448
+ if (error) {
449
+ log.error("Could not reload point point", {
450
+ err: error,
451
+ context: "createPoint",
452
+ user: toJson(req.user.simple()),
453
+ });
454
+ res.sendStatus(500);
453
455
  }
454
456
  else {
455
- log.info("No process-moderation toxicity for empty text on point");
457
+ if (loadedPoint.PointRevisions &&
458
+ loadedPoint.PointRevisions.length > 0 &&
459
+ loadedPoint.PointRevisions[loadedPoint.PointRevisions.length - 1].content !== "") {
460
+ log.info("process-moderation point toxicity after create point");
461
+ queue.add("process-moderation", {
462
+ type: "estimate-point-toxicity",
463
+ pointId: loadedPoint.id,
464
+ }, "high");
465
+ }
466
+ else {
467
+ log.info("No process-moderation toxicity for empty text on point");
468
+ }
469
+ res.send(loadedPoint);
456
470
  }
457
- res.send(loadedPoint);
458
- }
471
+ });
459
472
  });
460
473
  });
461
474
  });
@@ -714,17 +727,30 @@ router.post("/:groupId", auth.can("create point"), function (req, res) {
714
727
  });
715
728
  },
716
729
  (parallelCallback) => {
717
- models.AcActivity.createActivity({
718
- type: "activity.point.new",
719
- userId: point.user_id,
720
- domainId: req.ypDomain.id,
721
- // communityId: req.ypCommunity ? req.ypCommunity.id : null,
722
- groupId: point.group_id,
723
- postId: point.post_id,
724
- pointId: point.id,
725
- access: models.AcActivity.ACCESS_PUBLIC,
726
- }, function (error) {
727
- parallelCallback(error);
730
+ models.Group.findOne({
731
+ where: { id: point.group_id },
732
+ attributes: ["id", "community_id"],
733
+ include: [
734
+ {
735
+ model: models.Community,
736
+ attributes: ["id", "domain_id"],
737
+ },
738
+ ],
739
+ }).then(function (group) {
740
+ models.AcActivity.createActivity({
741
+ type: "activity.point.new",
742
+ userId: point.user_id,
743
+ domainId: group && group.Community
744
+ ? group.Community.domain_id
745
+ : req.ypDomain.id,
746
+ // communityId: req.ypCommunity ? req.ypCommunity.id : null,
747
+ groupId: point.group_id,
748
+ postId: point.post_id,
749
+ pointId: point.id,
750
+ access: models.AcActivity.ACCESS_PUBLIC,
751
+ }, function (error) {
752
+ parallelCallback(error);
753
+ });
728
754
  });
729
755
  },
730
756
  (parallelCallback) => {
@@ -950,20 +976,33 @@ router.post("/:id/pointQuality", auth.can("vote on point"), function (req, res)
950
976
  }
951
977
  },
952
978
  function (seriesCallback) {
953
- models.AcActivity.createActivity({
954
- type: pointQuality.value > 0
955
- ? "activity.point.helpful.new"
956
- : "activity.point.unhelpful.new",
957
- userId: pointQuality.user_id,
958
- domainId: req.ypDomain.id,
959
- // communityId: req.ypCommunity ? req.ypCommunity.id : null,
960
- pointQualityId: pointQuality.id,
961
- groupId: point.group_id,
962
- postId: point.post_id,
963
- pointId: point.id,
964
- access: models.AcActivity.ACCESS_PUBLIC,
965
- }, function (error) {
966
- seriesCallback(error);
979
+ models.Group.findOne({
980
+ where: { id: point.group_id },
981
+ attributes: ["id", "community_id"],
982
+ include: [
983
+ {
984
+ model: models.Community,
985
+ attributes: ["id", "domain_id"],
986
+ },
987
+ ],
988
+ }).then(function (group) {
989
+ models.AcActivity.createActivity({
990
+ type: pointQuality.value > 0
991
+ ? "activity.point.helpful.new"
992
+ : "activity.point.unhelpful.new",
993
+ userId: pointQuality.user_id,
994
+ domainId: group && group.Community
995
+ ? group.Community.domain_id
996
+ : req.ypDomain.id,
997
+ // communityId: req.ypCommunity ? req.ypCommunity.id : null,
998
+ pointQualityId: pointQuality.id,
999
+ groupId: point.group_id,
1000
+ postId: point.post_id,
1001
+ pointId: point.id,
1002
+ access: models.AcActivity.ACCESS_PUBLIC,
1003
+ }, function (error) {
1004
+ seriesCallback(error);
1005
+ });
967
1006
  });
968
1007
  },
969
1008
  function (seriesCallback) {
@@ -1088,7 +1088,13 @@ router.post("/:groupId", auth.can("create post"), async function (req, res) {
1088
1088
  where: {
1089
1089
  id: req.params.groupId,
1090
1090
  },
1091
- attributes: ["id", "configuration"],
1091
+ attributes: ["id", "configuration", "community_id"],
1092
+ include: [
1093
+ {
1094
+ model: models.Community,
1095
+ attributes: ["id", "domain_id"],
1096
+ },
1097
+ ],
1092
1098
  })
1093
1099
  .then((group) => {
1094
1100
  var post = models.Post.build({
@@ -1135,7 +1141,9 @@ router.post("/:groupId", auth.can("create post"), async function (req, res) {
1135
1141
  models.AcActivity.createActivity({
1136
1142
  type: "activity.post.new",
1137
1143
  userId: post.user_id,
1138
- domainId: req.ypDomain.id,
1144
+ domainId: group && group.Community
1145
+ ? group.Community.domain_id
1146
+ : req.ypDomain.id,
1139
1147
  groupId: post.group_id,
1140
1148
  // communityId: req.ypCommunity ? req.ypCommunity.id : null,
1141
1149
  postId: post.id,
@@ -1702,7 +1710,9 @@ router.post("/:id/endorse", auth.can("vote on post"), async function (req, res)
1702
1710
  });
1703
1711
  async.series([
1704
1712
  function (seriesCallback) {
1705
- if (post) {
1713
+ if (post &&
1714
+ post.Group &&
1715
+ post.Group.Community) {
1706
1716
  endorsement.dataValues.Post = post;
1707
1717
  seriesCallback();
1708
1718
  }
@@ -1710,6 +1720,18 @@ router.post("/:id/endorse", auth.can("vote on post"), async function (req, res)
1710
1720
  models.Post.findOne({
1711
1721
  where: { id: endorsement.post_id },
1712
1722
  attributes: ["id", "group_id"],
1723
+ include: [
1724
+ {
1725
+ model: models.Group,
1726
+ attributes: ["id", "community_id"],
1727
+ include: [
1728
+ {
1729
+ model: models.Community,
1730
+ attributes: ["id", "domain_id"],
1731
+ },
1732
+ ],
1733
+ },
1734
+ ],
1713
1735
  }).then(function (results) {
1714
1736
  if (results) {
1715
1737
  post = results;
@@ -1728,7 +1750,11 @@ router.post("/:id/endorse", auth.can("vote on post"), async function (req, res)
1728
1750
  ? "activity.post.endorsement.new"
1729
1751
  : "activity.post.opposition.new",
1730
1752
  userId: endorsement.user_id,
1731
- domainId: req.ypDomain.id,
1753
+ domainId: post &&
1754
+ post.Group &&
1755
+ post.Group.Community
1756
+ ? post.Group.Community.domain_id
1757
+ : req.ypDomain.id,
1732
1758
  endorsementId: endorsement.id,
1733
1759
  // communityId: req.ypCommunity ? req.ypCommunity.id : null,
1734
1760
  groupId: post.group_id,
@@ -110,16 +110,38 @@ router.post('/:post_id/:type_index', auth.can('rate post'), function (req, res)
110
110
  });
111
111
  },
112
112
  function (seriesCallback) {
113
- models.AcActivity.createActivity({
114
- type: 'activity.post.rating.new',
115
- userId: rating.user_id,
116
- domainId: req.ypDomain.id,
117
- groupId: post.group_id,
118
- ratingId: rating.id,
119
- postId: post.id,
120
- access: models.AcActivity.ACCESS_PRIVATE
121
- }, function (error) {
122
- seriesCallback(error);
113
+ models.Post.findOne({
114
+ where: { id: rating.post_id },
115
+ attributes: ['id', 'group_id'],
116
+ include: [
117
+ {
118
+ model: models.Group,
119
+ attributes: ['id', 'community_id'],
120
+ include: [
121
+ {
122
+ model: models.Community,
123
+ attributes: ['id', 'domain_id']
124
+ }
125
+ ]
126
+ }
127
+ ]
128
+ }).then(function (loadedPost) {
129
+ const domainId = loadedPost &&
130
+ loadedPost.Group &&
131
+ loadedPost.Group.Community
132
+ ? loadedPost.Group.Community.domain_id
133
+ : req.ypDomain.id;
134
+ models.AcActivity.createActivity({
135
+ type: 'activity.post.rating.new',
136
+ userId: rating.user_id,
137
+ domainId: domainId,
138
+ groupId: post.group_id,
139
+ ratingId: rating.id,
140
+ postId: post.id,
141
+ access: models.AcActivity.ACCESS_PRIVATE
142
+ }, function (error) {
143
+ seriesCallback(error);
144
+ });
123
145
  });
124
146
  }
125
147
  ], function (error) {
@@ -1246,6 +1246,12 @@ const setSAMLSettingsOnUser = (req, user, done) => {
1246
1246
  customSamlLoginMessage = community.configuration.customSamlLoginMessage;
1247
1247
  }
1248
1248
  }
1249
+ if (!forceSecureSamlLogin &&
1250
+ req.ypDomain &&
1251
+ req.ypDomain.configuration &&
1252
+ req.ypDomain.configuration.forceElectronicIds) {
1253
+ forceSecureSamlLogin = true;
1254
+ }
1249
1255
  if (user.dataValues) {
1250
1256
  user.dataValues.forceSecureSamlLogin = forceSecureSamlLogin;
1251
1257
  user.dataValues.customSamlDeniedMessage = customSamlDeniedMessage;
@@ -1581,14 +1587,28 @@ router.post('/reset/:token', function (req, res) {
1581
1587
  },
1582
1588
  function (done) {
1583
1589
  if (req.user) {
1584
- models.AcActivity.createActivity({
1585
- type: 'activity.password.changed',
1586
- userId: req.user.id,
1587
- domainId: req.ypDomain.id,
1588
- groupId: req.params.groupId
1589
- // communityId: req.ypCommunity ? req.ypCommunity.id : null
1590
- }, function (error) {
1591
- done(error);
1590
+ const fetchGroup = req.params.groupId
1591
+ ? models.Group.findOne({
1592
+ where: { id: req.params.groupId },
1593
+ attributes: ['id', 'community_id'],
1594
+ include: [
1595
+ {
1596
+ model: models.Community,
1597
+ attributes: ['id', 'domain_id']
1598
+ }
1599
+ ]
1600
+ })
1601
+ : Promise.resolve(null);
1602
+ fetchGroup.then(function (group) {
1603
+ models.AcActivity.createActivity({
1604
+ type: 'activity.password.changed',
1605
+ userId: req.user.id,
1606
+ domainId: group && group.Community ? group.Community.domain_id : req.ypDomain.id,
1607
+ groupId: req.params.groupId
1608
+ // communityId: req.ypCommunity ? req.ypCommunity.id : null
1609
+ }, function (error) {
1610
+ done(error);
1611
+ });
1592
1612
  });
1593
1613
  }
1594
1614
  else {
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.117",
3
+ "version": "9.0.120",
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.95",
28
+ "@policysynth/agents": "^1.3.101",
29
29
  "async": "^3.2.6",
30
30
  "authorized": "^1.0.0",
31
31
  "aws-sdk": "^2.1692.0",
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ import bcrypt from 'bcrypt';
2
+ import models from '../../models/index.cjs';
3
+ (async () => {
4
+ try {
5
+ const [domainIdArg, email, name, password, ssn] = process.argv.slice(2);
6
+ if (!domainIdArg || !email || !name || !password) {
7
+ console.log('Usage: node createUserAddDomain.js <domainId> <email> <name> <password> [ssn]');
8
+ process.exit(1);
9
+ }
10
+ const domainId = Number(domainIdArg);
11
+ const domain = await models.Domain.findOne({ where: { id: domainId } });
12
+ if (!domain) {
13
+ console.error(`Domain ${domainId} not found`);
14
+ process.exit(1);
15
+ }
16
+ const existing = await models.User.findOne({ where: { email } });
17
+ if (existing) {
18
+ console.error(`User with email ${email} already exists`);
19
+ process.exit(1);
20
+ }
21
+ const hashed = await bcrypt.hash(password, 10);
22
+ const user = await models.User.create({
23
+ email,
24
+ name,
25
+ status: 'active',
26
+ ssn: ssn ? Number(ssn) : undefined,
27
+ encrypted_password: hashed,
28
+ });
29
+ await domain.addDomainUsers(user);
30
+ console.log(`User ${email} created and added to domain ${domain.name}`);
31
+ process.exit(0);
32
+ }
33
+ catch (err) {
34
+ console.error(err);
35
+ process.exit(1);
36
+ }
37
+ })();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import models from '../../models/index.cjs';
2
+ (async () => {
3
+ try {
4
+ const [domainIdArg] = process.argv.slice(2);
5
+ if (!domainIdArg) {
6
+ console.log('Usage: node listDomainUsersWithSsn.js <domainId>');
7
+ process.exit(1);
8
+ }
9
+ const domainId = Number(domainIdArg);
10
+ const domain = await models.Domain.findOne({
11
+ where: { id: domainId },
12
+ include: [{ model: models.User, as: 'DomainUsers' }],
13
+ });
14
+ if (!domain) {
15
+ console.error(`Domain ${domainId} not found`);
16
+ process.exit(1);
17
+ }
18
+ console.log('email,name,ssn');
19
+ for (const user of domain.DomainUsers) {
20
+ if (user.ssn) {
21
+ console.log(`${user.email},${user.name},${user.ssn}`);
22
+ }
23
+ }
24
+ process.exit(0);
25
+ }
26
+ catch (err) {
27
+ console.error(err);
28
+ process.exit(1);
29
+ }
30
+ })();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import models from '../../models/index.cjs';
2
+ (async () => {
3
+ try {
4
+ const [domainIdArg, email] = process.argv.slice(2);
5
+ if (!domainIdArg || !email) {
6
+ console.log('Usage: node removeUserFromDomain.js <domainId> <email>');
7
+ process.exit(1);
8
+ }
9
+ const domainId = Number(domainIdArg);
10
+ const domain = await models.Domain.findOne({ where: { id: domainId } });
11
+ if (!domain) {
12
+ console.error(`Domain ${domainId} not found`);
13
+ process.exit(1);
14
+ }
15
+ const user = await models.User.findOne({ where: { email } });
16
+ if (!user) {
17
+ console.error(`User ${email} not found`);
18
+ process.exit(1);
19
+ }
20
+ await domain.removeDomainUsers(user);
21
+ console.log(`Removed ${email} from domain ${domain.name}`);
22
+ process.exit(0);
23
+ }
24
+ catch (err) {
25
+ console.error(err);
26
+ process.exit(1);
27
+ }
28
+ })();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import bcrypt from 'bcrypt';
2
+ import models from '../../models/index.cjs';
3
+ (async () => {
4
+ try {
5
+ const [ssnArg, password] = process.argv.slice(2);
6
+ if (!ssnArg || !password) {
7
+ console.log('Usage: node updatePasswordFromSsn.js <ssn> <newPassword>');
8
+ process.exit(1);
9
+ }
10
+ const ssn = Number(ssnArg);
11
+ const user = await models.User.findOne({ where: { ssn } });
12
+ if (!user) {
13
+ console.error(`User with ssn ${ssn} not found`);
14
+ process.exit(1);
15
+ }
16
+ user.encrypted_password = await bcrypt.hash(password, 10);
17
+ await user.save();
18
+ console.log(`Updated password for ${user.email}`);
19
+ process.exit(0);
20
+ }
21
+ catch (err) {
22
+ console.error(err);
23
+ process.exit(1);
24
+ }
25
+ })();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ import models from '../../models/index.cjs';
2
+ (async () => {
3
+ try {
4
+ const [email, ssnArg] = process.argv.slice(2);
5
+ if (!email || !ssnArg) {
6
+ console.log('Usage: node updateUserSsnFromEmail.js <email> <ssn>');
7
+ process.exit(1);
8
+ }
9
+ const user = await models.User.findOne({ where: { email } });
10
+ if (!user) {
11
+ console.error(`User ${email} not found`);
12
+ process.exit(1);
13
+ }
14
+ const ssn = Number(ssnArg);
15
+ user.ssn = ssn;
16
+ await user.save();
17
+ console.log(`Updated SSN for ${email}`);
18
+ process.exit(0);
19
+ }
20
+ catch (err) {
21
+ console.error(err);
22
+ process.exit(1);
23
+ }
24
+ })();
@@ -32,16 +32,29 @@ class ImageLabelingBase {
32
32
  const fileNameWithPath = '/tmp/' + fileName;
33
33
  downloadFile(imageUrl, { filename: fileName, directory: '/tmp/' }, async (error) => {
34
34
  if (error) {
35
- fs.unlink(fileNameWithPath, unlinkError => {
35
+ fs.unlink(fileNameWithPath, () => {
36
36
  reject('Could not download file');
37
- return;
38
37
  });
38
+ return;
39
39
  }
40
40
  else {
41
- const [result] = await this.visionClient.annotateImage({
42
- ...this.visionRequesBase,
43
- image: { source: { filename: fileNameWithPath } }
44
- });
41
+ let result;
42
+ try {
43
+ ;
44
+ [result] = await this.visionClient.annotateImage({
45
+ ...this.visionRequesBase,
46
+ image: { content: fs.readFileSync(fileNameWithPath) }
47
+ });
48
+ }
49
+ catch (annotationError) {
50
+ log.error('Vision API annotateImage failed', annotationError);
51
+ fs.unlink(fileNameWithPath, unlinkErr => {
52
+ if (unlinkErr)
53
+ log.error(unlinkErr);
54
+ });
55
+ resolve();
56
+ return;
57
+ }
45
58
  try {
46
59
  fs.unlink(fileNameWithPath, async (unlinkError) => {
47
60
  if (unlinkError)
@@ -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`