Skip to content

Commit fbfa4cc

Browse files
init. enable turbo. and alpha changelog support
1 parent 6ad4caf commit fbfa4cc

File tree

18 files changed

+589
-38
lines changed

18 files changed

+589
-38
lines changed

backend/src/main/java/io/easystartup/suggestfeature/beans/Changelog.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public class Changelog {
3333
public static final String FIELD_ORGANIZATION_ID = "organizationId";
3434
public static final String FIELD_TAGS = "tags";
3535
public static final String FIELD_CREATED_AT = "createdAt";
36+
public static final String FIELD_CHANGELOG_DATE = "changelogDate";
3637
public static final String FIELD_SLUG = "slug";
3738

3839
@Id
@@ -43,10 +44,16 @@ public class Changelog {
4344
@NotBlank
4445
@Size(max = 100_000)
4546
private String content;
47+
48+
private String coverImage;
4649
@Indexed
4750
private String organizationId;
4851
private String createdByUserId;
4952

53+
private Long changelogDate;
54+
55+
private boolean draft;
56+
5057
private List<String> postIds;
5158

5259
@Indexed
@@ -151,4 +158,28 @@ public List<String> getPostIds() {
151158
public void setPostIds(List<String> postIds) {
152159
this.postIds = postIds;
153160
}
161+
162+
public Long getChangelogDate() {
163+
return changelogDate;
164+
}
165+
166+
public void setChangelogDate(Long changelogDate) {
167+
this.changelogDate = changelogDate;
168+
}
169+
170+
public boolean isDraft() {
171+
return draft;
172+
}
173+
174+
public void setDraft(boolean draft) {
175+
this.draft = draft;
176+
}
177+
178+
public String getCoverImage() {
179+
return coverImage;
180+
}
181+
182+
public void setCoverImage(String coverImage) {
183+
this.coverImage = coverImage;
184+
}
154185
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package io.easystartup.suggestfeature.beans;
2+
3+
4+
import org.springframework.data.annotation.Id;
5+
import org.springframework.data.mongodb.core.index.Indexed;
6+
import org.springframework.data.mongodb.core.mapping.Document;
7+
8+
/*
9+
* @author indianBond
10+
*/
11+
@Document
12+
public class ChangelogSubscriber {
13+
14+
public static final String FIELD_ID = "_id";
15+
public static final String FIELD_ORGANIZATION_ID = "organizationId";
16+
public static final String FIELD_USER_ID = "userId";
17+
public static final String FIELD_CREATED_AT = "createdAt";
18+
19+
@Id
20+
private String id;
21+
@Indexed
22+
private String organizationId;
23+
private String userId;
24+
private Long createdAt;
25+
26+
public ChangelogSubscriber() {
27+
}
28+
29+
public String getId() {
30+
return id;
31+
}
32+
33+
public void setId(String id) {
34+
this.id = id;
35+
}
36+
37+
public String getOrganizationId() {
38+
return organizationId;
39+
}
40+
41+
public void setOrganizationId(String organizationId) {
42+
this.organizationId = organizationId;
43+
}
44+
45+
public String getUserId() {
46+
return userId;
47+
}
48+
49+
public void setUserId(String userId) {
50+
this.userId = userId;
51+
}
52+
53+
public Long getCreatedAt() {
54+
return createdAt;
55+
}
56+
57+
public void setCreatedAt(Long createdAt) {
58+
this.createdAt = createdAt;
59+
}
60+
}

backend/src/main/java/io/easystartup/suggestfeature/dto/ChangelogUpdateDTO.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public class ChangelogUpdateDTO {
1818
private String title;
1919
private String content;
2020
private List<String> tags;
21+
private Long changelogDate;
22+
private boolean draft;
2123

2224
public ChangelogUpdateDTO() {
2325
}
@@ -61,4 +63,20 @@ public List<String> getPostIds() {
6163
public void setPostIds(List<String> postIds) {
6264
this.postIds = postIds;
6365
}
66+
67+
public Long getChangelogDate() {
68+
return changelogDate;
69+
}
70+
71+
public void setChangelogDate(Long changelogDate) {
72+
this.changelogDate = changelogDate;
73+
}
74+
75+
public boolean isDraft() {
76+
return draft;
77+
}
78+
79+
public void setDraft(boolean draft) {
80+
this.draft = draft;
81+
}
6482
}

backend/src/main/java/io/easystartup/suggestfeature/jobqueue/executor/SendChangelogEmailExecutor.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33

44
import io.easystartup.suggestfeature.beans.Changelog;
5+
import io.easystartup.suggestfeature.beans.ChangelogSubscriber;
56
import io.easystartup.suggestfeature.beans.Organization;
67
import io.easystartup.suggestfeature.beans.User;
78
import io.easystartup.suggestfeature.jobqueue.scheduler.JobExecutor;
@@ -13,6 +14,8 @@
1314
import io.easystartup.suggestfeature.utils.RateLimiters;
1415
import io.easystartup.suggestfeature.utils.Util;
1516
import org.apache.commons.collections4.CollectionUtils;
17+
import org.springframework.data.mongodb.core.query.Criteria;
18+
import org.springframework.data.mongodb.core.query.Query;
1619

1720
import java.util.HashSet;
1821
import java.util.List;
@@ -49,10 +52,11 @@ public void execute(Map<String, Object> data, String orgId) {
4952
// Send every comment to post author and send to people who have interacted with the comment/post
5053
String senderEmail = Util.getEnvVariable("FROM_EMAIL", "fromEmail");
5154
Organization organization = authService.get().getOrgById(orgId);
52-
User commentCreatedByUser = authService.get().getUserByUserId(changelog.getCreatedByUserId());
55+
User changelogCreatedByUser = authService.get().getUserByUserId(changelog.getCreatedByUserId());
5356

5457
Set<String> userIds = new HashSet<>();
55-
// If admin commented root, send to all voters, including post creator, else send to all commenters
58+
mongoConnection.get().getDefaultMongoTemplate().find(Query.query(Criteria.where(ChangelogSubscriber.FIELD_ORGANIZATION_ID).is(orgId)), ChangelogSubscriber.class)
59+
.forEach(subscriber -> userIds.add(subscriber.getUserId()));
5660

5761
if (CollectionUtils.isEmpty(userIds)) {
5862
return;
@@ -62,7 +66,7 @@ public void execute(Map<String, Object> data, String orgId) {
6266

6367
// Prepare email content
6468
String subject = "Changelog added " + escapeHtml(changelog.getTitle());
65-
String bodyHtml = constructEmailBodyHtml(organization, changelog, commentCreatedByUser, changelogUrl);
69+
String bodyHtml = constructEmailBodyHtml(organization, changelog, changelogCreatedByUser, changelogUrl);
6670

6771
List<User> users = authService.get().getUsersByUserIds(userIds);
6872

backend/src/main/java/io/easystartup/suggestfeature/rest/admin/AdminDashboardRestApi.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import jakarta.ws.rs.Path;
1515
import jakarta.ws.rs.Produces;
1616
import jakarta.ws.rs.core.Response;
17-
import org.apache.commons.lang3.StringUtils;
1817
import org.springframework.beans.factory.annotation.Autowired;
1918
import org.springframework.data.mongodb.core.aggregation.Aggregation;
2019
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
@@ -73,6 +72,11 @@ public Response getDashboardData(GetDashboardDataRequest req) {
7372
Criteria criteria = Criteria.where(Post.FIELD_ORGANIZATION_ID).is(orgId);
7473
if (!boardId.equals("ALL")) {
7574
criteria.and(Post.FIELD_BOARD_ID).is(boardId);
75+
} else {
76+
// Don't include deleted boards
77+
List<Board> boardList = mongoConnection.getDefaultMongoTemplate().find(Query.query(Criteria.where(Board.FIELD_ORGANIZATION_ID).is(orgId)), Board.class);
78+
List<String> boardIds = boardList.stream().map(Board::getId).collect(Collectors.toList());
79+
criteria.and(Post.FIELD_BOARD_ID).in(boardIds);
7680
}
7781
addTimeCriteria(req.getTimeFrame(), criteria);
7882

backend/src/main/java/io/easystartup/suggestfeature/rest/admin/ChangelogRestApi.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,13 @@ public Response updateChangelogDetails(ChangelogUpdateDTO req) {
134134
}
135135
existingChangelog.setPostIds(req.getPostIds());
136136
existingChangelog.setModifiedAt(System.currentTimeMillis());
137+
existingChangelog.setDraft(req.isDraft());
138+
existingChangelog.setTags(req.getTags());
137139
// To ensure only this org postIds are being set
138140
existingChangelog.setPostIds(getPostIdsForCurrentOrg(req.getPostIds(), orgId));
141+
if (req.getChangelogDate() != null) {
142+
existingChangelog.setChangelogDate(req.getChangelogDate());
143+
}
139144

140145
mongoConnection.getDefaultMongoTemplate().save(existingChangelog);
141146
return Response.ok(JacksonMapper.toJson(existingChangelog)).build();
@@ -145,7 +150,7 @@ public Response updateChangelogDetails(ChangelogUpdateDTO req) {
145150
@Path("/create-changelog")
146151
@Consumes("application/json")
147152
@Produces("application/json")
148-
public Response createPost(Changelog changelog) {
153+
public Response createChangelog(Changelog changelog) {
149154
String userId = UserContext.current().getUserId();
150155
String orgId = UserContext.current().getOrgId();
151156

@@ -169,6 +174,9 @@ public Response createPost(Changelog changelog) {
169174
changelog.setSlug(existingChangelog.getSlug());
170175
}
171176
changelog.setOrganizationId(orgId);
177+
if (changelog.getChangelogDate() == null) {
178+
changelog.setChangelogDate(System.currentTimeMillis());
179+
}
172180
try {
173181
if (isNew) {
174182
try {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package io.easystartup.suggestfeature.rest.portal.auth;
2+
3+
4+
import io.easystartup.suggestfeature.beans.ChangelogSubscriber;
5+
import io.easystartup.suggestfeature.beans.Organization;
6+
import io.easystartup.suggestfeature.filters.UserContext;
7+
import io.easystartup.suggestfeature.services.db.MongoTemplateFactory;
8+
import jakarta.servlet.http.HttpServletRequest;
9+
import jakarta.ws.rs.GET;
10+
import jakarta.ws.rs.Path;
11+
import jakarta.ws.rs.Produces;
12+
import jakarta.ws.rs.QueryParam;
13+
import jakarta.ws.rs.core.Context;
14+
import jakarta.ws.rs.core.Response;
15+
import org.springframework.beans.factory.annotation.Autowired;
16+
import org.springframework.data.mongodb.core.query.Criteria;
17+
import org.springframework.data.mongodb.core.query.Query;
18+
import org.springframework.stereotype.Component;
19+
20+
import java.util.Collections;
21+
22+
/**
23+
* @author indianBond
24+
*/
25+
@Path("/portal/auth/changelog")
26+
@Component
27+
public class PublicPortalAuthChangelogRestApi {
28+
29+
private final MongoTemplateFactory mongoConnection;
30+
31+
@Autowired
32+
public PublicPortalAuthChangelogRestApi(MongoTemplateFactory mongoConnection) {
33+
this.mongoConnection = mongoConnection;
34+
}
35+
36+
@GET
37+
@Path("/subscribe-to-changelog")
38+
@Produces("application/json")
39+
public Response subscribeToChangelog(@Context HttpServletRequest request, @QueryParam("unsubscribe") Boolean unsubscribe) {
40+
String host = request.getHeader("host");
41+
Organization org = getOrg(host);
42+
if (org == null || (org.getChangelogSettings() != null && !org.getChangelogSettings().isEnabled())) {
43+
return Response.ok().entity(Collections.emptyList()).build();
44+
}
45+
46+
if (Boolean.TRUE.equals(unsubscribe)) {
47+
Criteria criteria = Criteria.where(ChangelogSubscriber.FIELD_ORGANIZATION_ID).is(org.getId())
48+
.and(ChangelogSubscriber.FIELD_USER_ID).is(UserContext.current().getUserId());
49+
mongoConnection.getDefaultMongoTemplate().remove(new Query(criteria), ChangelogSubscriber.class);
50+
return Response.ok().entity("{}").build();
51+
}
52+
53+
ChangelogSubscriber changelogSubscriber = new ChangelogSubscriber();
54+
changelogSubscriber.setId("changelog-subscriber-" + UserContext.current().getUserId() + "-" + org.getId());
55+
changelogSubscriber.setOrganizationId(org.getId());
56+
changelogSubscriber.setUserId(UserContext.current().getUserId());
57+
changelogSubscriber.setCreatedAt(System.currentTimeMillis());
58+
mongoConnection.getDefaultMongoTemplate().save(changelogSubscriber);
59+
60+
return Response.ok().entity("{}").build();
61+
}
62+
63+
private Organization getOrg(String host) {
64+
Criteria criteria;
65+
if (!host.endsWith(".suggestfeature.com")) {
66+
criteria = Criteria.where(Organization.FIELD_CUSTOM_DOMAIN).is(host);
67+
} else {
68+
criteria = Criteria.where(Organization.FIELD_SLUG).is(host.split("\\.")[0]);
69+
}
70+
return mongoConnection.getDefaultMongoTemplate().findOne(new Query(criteria), Organization.class);
71+
}
72+
73+
}

backend/src/main/java/io/easystartup/suggestfeature/rest/portal/unauth/PublicPortalChangelogRestApi.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ public PublicPortalChangelogRestApi(MongoTemplateFactory mongoConnection) {
5151
public Response getChangelog(@Context HttpServletRequest request) {
5252
String host = request.getHeader("host");
5353
Organization org = getOrg(host);
54-
if (org == null || (org.getRoadmapSettings() != null && !org.getRoadmapSettings().isEnabled())) {
54+
if (org == null || (org.getChangelogSettings() != null && !org.getChangelogSettings().isEnabled())) {
5555
return Response.ok().entity(Collections.emptyList()).build();
5656
}
5757
Criteria criteriaDefinition = Criteria.where(Changelog.FIELD_ORGANIZATION_ID).is(org.getId());
5858
Query query = new Query(criteriaDefinition);
59-
query.with(Sort.by(Sort.Direction.DESC, Changelog.FIELD_CREATED_AT));
59+
query.with(Sort.by(Sort.Direction.DESC, Changelog.FIELD_CHANGELOG_DATE));
6060
List<Changelog> changelogs = mongoConnection.getDefaultMongoTemplate().find(query, Changelog.class);
6161

6262
return Response.ok().entity(JacksonMapper.toJson(changelogs)).build();
@@ -72,7 +72,8 @@ public Response fetchPost(@Context HttpServletRequest request, @QueryParam("chan
7272
return Response.ok().entity(Collections.emptyList()).build();
7373
}
7474
Criteria criteriaDefinition = Criteria.where(Changelog.FIELD_SLUG).is(changelogSlug).and(Post.FIELD_ORGANIZATION_ID).is(org.getId());
75-
Changelog changelog = mongoConnection.getDefaultMongoTemplate().findOne(new Query(criteriaDefinition), Changelog.class);
75+
Query query = new Query(criteriaDefinition);
76+
Changelog changelog = mongoConnection.getDefaultMongoTemplate().findOne(query, Changelog.class);
7677

7778
return Response.ok().entity(JacksonMapper.toJson(changelog)).build();
7879
}

backend/src/main/java/io/easystartup/suggestfeature/rest/portal/unauth/PublicPortalPostRestApi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class PublicPortalPostRestApi {
4343
// Loading cache of host vs page
4444
private final Cache<String, String> hostOrgCache = CacheBuilder.newBuilder()
4545
.maximumSize(20_000)
46-
.expireAfterWrite(30, TimeUnit.SECONDS)
46+
.expireAfterWrite(10, TimeUnit.SECONDS)
4747
.build();
4848
@Autowired
4949
public PublicPortalPostRestApi(MongoTemplateFactory mongoConnection) {

backend/src/main/java/io/easystartup/suggestfeature/utils/ChangelogUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ public static String getChangelogUrl(Changelog changelog, Organization organizat
2222
baseDomain = "https://" + slug + ".suggestfeature.com";
2323
}
2424

25-
return baseDomain + "/c/" + changelog.getSlug();
25+
return baseDomain + "/changelog/" + changelog.getSlug();
2626
}
2727
}

0 commit comments

Comments
 (0)