Compare commits
16 Commits
018285c109
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 66fa985a33 | |||
| df0db226a9 | |||
| 57418213f5 | |||
| 9fd843da78 | |||
| 5e74228959 | |||
| 370912a091 | |||
| 5a74e1cb5b | |||
| c293f20559 | |||
| 8943641baf | |||
| 2ca997fca9 | |||
| aa669a98a9 | |||
| 5032ce5d63 | |||
| 07b90d1256 | |||
| 22bde7ef79 | |||
| cafde860a7 | |||
| ded34c5f43 |
@@ -7,7 +7,8 @@ FROM mvn as builder
|
|||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
ADD . .
|
ADD . .
|
||||||
RUN mvn package
|
ARG KC_HOSTNAME
|
||||||
|
RUN mvn package -DKC_HOSTNAME=${KC_HOSTNAME}
|
||||||
|
|
||||||
|
|
||||||
FROM eclipse-temurin:21-jdk-alpine
|
FROM eclipse-temurin:21-jdk-alpine
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||||||
public class JodelPost {
|
public class JodelPost {
|
||||||
// id of the post
|
// id of the post
|
||||||
public Long id;
|
public Long id;
|
||||||
|
// Parent id
|
||||||
|
public Optional<Long> parent = Optional.empty();
|
||||||
// id of the author in db
|
// id of the author in db
|
||||||
private final Long authorID;
|
private final Long authorID;
|
||||||
// anonymized authorID
|
// anonymized authorID
|
||||||
@@ -25,31 +27,24 @@ public class JodelPost {
|
|||||||
public Timestamp date;
|
public Timestamp date;
|
||||||
// location if the post
|
// location if the post
|
||||||
public Location location;
|
public Location location;
|
||||||
// list of all comments for the post
|
|
||||||
public Vector<JodelPost> comments = new Vector<>();
|
|
||||||
// the own reaction (null = none, true = positive, false = negative)
|
// the own reaction (null = none, true = positive, false = negative)
|
||||||
public Optional<Boolean> reaction;
|
public Optional<Boolean> reaction;
|
||||||
// all other reactions
|
// all other reactions
|
||||||
public Reactions reactions;
|
public Reactions reactions;
|
||||||
// Parent id
|
// list of all comments for the post
|
||||||
public Optional<Long> parent = Optional.empty();
|
public Vector<JodelPost> comments = new Vector<>();
|
||||||
|
|
||||||
// anonymize function to recursively anonymize the posts
|
// anonymize function to recursively anonymize the posts
|
||||||
public void anonymize(Optional<Vector<Long>> idCache) {
|
public void anonymize(Vector<Long> idCache) {
|
||||||
// check if this is the first post in this process
|
|
||||||
if (idCache.isEmpty()) {
|
|
||||||
// create a new Vector as cache
|
|
||||||
idCache = Optional.of(new Vector<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the anonymized id as index in cached authorIDs
|
// get the anonymized id as index in cached authorIDs
|
||||||
int i = idCache.get().indexOf(this.authorID);
|
int i = idCache.indexOf(this.authorID);
|
||||||
// if the index is -1 the authorID has not been cached jet
|
// if the index is -1 the authorID has not been cached jet
|
||||||
if (i == -1) {
|
if (i == -1) {
|
||||||
// set the current anonymousID as length of the cache (== next index)
|
// set the current anonymousID as length of the cache (== next index)
|
||||||
this.anonymousID = (long) idCache.get().size();
|
this.anonymousID = (long) idCache.size();
|
||||||
// push the current authorID to to cache
|
// push the current authorID to to cache
|
||||||
idCache.get().add(this.authorID);
|
idCache.add(this.authorID);
|
||||||
}
|
}
|
||||||
// the authorID has been anonymized once before, so we can get it from cache
|
// the authorID has been anonymized once before, so we can get it from cache
|
||||||
else this.anonymousID = (long) i;
|
else this.anonymousID = (long) i;
|
||||||
@@ -88,6 +83,7 @@ public class JodelPost {
|
|||||||
this.content = rs.getString("content");
|
this.content = rs.getString("content");
|
||||||
this.date = rs.getTimestamp("postdate");
|
this.date = rs.getTimestamp("postdate");
|
||||||
this.location = new Location(rs.getFloat("longitude"), rs.getFloat("latitude"));
|
this.location = new Location(rs.getFloat("longitude"), rs.getFloat("latitude"));
|
||||||
|
this.reaction = rs.getString("reaction") == null ? Optional.empty() : Optional.of(rs.getBoolean("reaction"));
|
||||||
this.reactions = new Reactions(rs.getLong("positive"), rs.getLong("negative"));
|
this.reactions = new Reactions(rs.getLong("positive"), rs.getLong("negative"));
|
||||||
this.parent = Optional.of(rs.getLong("parent"));
|
this.parent = Optional.of(rs.getLong("parent"));
|
||||||
}
|
}
|
||||||
@@ -102,10 +98,8 @@ public class JodelPost {
|
|||||||
@JsonProperty("content") String content,
|
@JsonProperty("content") String content,
|
||||||
@JsonProperty("date") Timestamp date,
|
@JsonProperty("date") Timestamp date,
|
||||||
@JsonProperty("location") Location location,
|
@JsonProperty("location") Location location,
|
||||||
@JsonProperty("parent") Optional<Long> parent,
|
@JsonProperty("parent") Optional<Long> parent) {
|
||||||
// TODO: getter from Keycloak
|
this.authorID = User.getID(); // TODO: getter from Keycloak
|
||||||
@JsonProperty("authorID") long author) {
|
|
||||||
this.authorID = author; // TODO: getter from Keycloak
|
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.date = date;
|
this.date = date;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import com.fasterxml.jackson.annotation.JsonCreator;
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
public class Location {
|
public class Location {
|
||||||
public float longitude;
|
|
||||||
public float latitude;
|
public float latitude;
|
||||||
|
public float longitude;
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public Location(@JsonProperty("longitude") float longitude, @JsonProperty("latitude") float latitude ) {
|
public Location(@JsonProperty("longitude") float longitude, @JsonProperty("latitude") float latitude ) {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package de.anxietyprime.swajodel;
|
package de.anxietyprime.swajodel;
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
@@ -15,11 +13,14 @@ import java.util.Vector;
|
|||||||
@CrossOrigin(origins = "*", allowedHeaders = "*", maxAge = 3600)
|
@CrossOrigin(origins = "*", allowedHeaders = "*", maxAge = 3600)
|
||||||
public class Routes {
|
public class Routes {
|
||||||
|
|
||||||
@GetMapping("/posts/{longitude}/{latitude}")
|
@GetMapping("/posts/{latitude}/{longitude}")
|
||||||
public Vector<JodelPost> getPostsByLocation(@PathVariable("longitude") float longitude, @PathVariable("latitude") float latitude) {
|
public Vector<JodelPost> getPostsByLocation(@PathVariable("longitude") float longitude, @PathVariable("latitude") float latitude) {
|
||||||
// list of all posts (not comments) in range
|
// list of all posts (not comments) in range
|
||||||
Vector<JodelPost> posts = new Vector<>();
|
Vector<JodelPost> posts = new Vector<>();
|
||||||
|
|
||||||
|
// get UserID
|
||||||
|
Long userID = User.getID();
|
||||||
|
|
||||||
// DB connection and statement
|
// DB connection and statement
|
||||||
Connection c;
|
Connection c;
|
||||||
PreparedStatement stmt;
|
PreparedStatement stmt;
|
||||||
@@ -46,8 +47,8 @@ public class Routes {
|
|||||||
posts.title,
|
posts.title,
|
||||||
posts.content,
|
posts.content,
|
||||||
posts.postdate,
|
posts.postdate,
|
||||||
posts.postlocation[0] AS longitude,
|
posts.postlocation[0] AS latitude,
|
||||||
posts.postlocation[1] AS latitude,
|
posts.postlocation[1] AS longitude,
|
||||||
(SELECT count(*) FROM reactions WHERE reactions.post = posts.id AND reactions.positive = TRUE) AS positive,
|
(SELECT count(*) FROM reactions WHERE reactions.post = posts.id AND reactions.positive = TRUE) AS positive,
|
||||||
(SELECT count(*) FROM reactions WHERE reactions.post = posts.id AND reactions.positive = FALSE) AS negative
|
(SELECT count(*) FROM reactions WHERE reactions.post = posts.id AND reactions.positive = FALSE) AS negative
|
||||||
FROM
|
FROM
|
||||||
@@ -75,10 +76,11 @@ public class Routes {
|
|||||||
(SELECT * FROM comments inner join posts ON comments.child = posts.id) com
|
(SELECT * FROM comments inner join posts ON comments.child = posts.id) com
|
||||||
inner join targets ON targets.id = com.parent
|
inner join targets ON targets.id = com.parent
|
||||||
)
|
)
|
||||||
SELECT * FROM targets;""");
|
SELECT * FROM targets LEFT JOIN (SELECT post, positive as reaction FROM reactions WHERE userid = (?)) ON post = id;""");
|
||||||
|
|
||||||
stmt.setObject(1, longitude);
|
stmt.setObject(1, latitude);
|
||||||
stmt.setObject(2, latitude);
|
stmt.setObject(2, longitude);
|
||||||
|
stmt.setObject(3, userID);
|
||||||
|
|
||||||
// query recursively for posts inside a 10km radius
|
// query recursively for posts inside a 10km radius
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
@@ -117,7 +119,12 @@ public class Routes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// calculate anonymous IDs for the posts
|
// calculate anonymous IDs for the posts
|
||||||
posts.forEach(post -> post.anonymize(Optional.empty()));
|
Vector<Long> anonymousIDs = new Vector<Long>();
|
||||||
|
anonymousIDs.add(userID);
|
||||||
|
posts.forEach(post -> {
|
||||||
|
post.anonymize(anonymousIDs);
|
||||||
|
});
|
||||||
|
|
||||||
// return the posts
|
// return the posts
|
||||||
return posts;
|
return posts;
|
||||||
}
|
}
|
||||||
@@ -127,6 +134,9 @@ public class Routes {
|
|||||||
// list of all posts (not comments) in range
|
// list of all posts (not comments) in range
|
||||||
Optional<JodelPost> root_post = Optional.empty();
|
Optional<JodelPost> root_post = Optional.empty();
|
||||||
|
|
||||||
|
// get UserID
|
||||||
|
Long userID = User.getID();
|
||||||
|
|
||||||
// DB connection and statement
|
// DB connection and statement
|
||||||
Connection c;
|
Connection c;
|
||||||
PreparedStatement stmt;
|
PreparedStatement stmt;
|
||||||
@@ -153,8 +163,8 @@ public class Routes {
|
|||||||
posts.title,
|
posts.title,
|
||||||
posts.content,
|
posts.content,
|
||||||
posts.postdate,
|
posts.postdate,
|
||||||
posts.postlocation[0] AS longitude,
|
posts.postlocation[0] AS latitude,
|
||||||
posts.postlocation[1] AS latitude,
|
posts.postlocation[1] AS longitude,
|
||||||
(SELECT count(*) FROM reactions WHERE reactions.post = posts.id AND reactions.positive = TRUE) AS positive,
|
(SELECT count(*) FROM reactions WHERE reactions.post = posts.id AND reactions.positive = TRUE) AS positive,
|
||||||
(SELECT count(*) FROM reactions WHERE reactions.post = posts.id AND reactions.positive = FALSE) AS negative
|
(SELECT count(*) FROM reactions WHERE reactions.post = posts.id AND reactions.positive = FALSE) AS negative
|
||||||
FROM
|
FROM
|
||||||
@@ -179,9 +189,10 @@ public class Routes {
|
|||||||
(SELECT * FROM comments inner join posts ON comments.child = posts.id) com
|
(SELECT * FROM comments inner join posts ON comments.child = posts.id) com
|
||||||
inner join targets ON targets.id = com.parent
|
inner join targets ON targets.id = com.parent
|
||||||
)
|
)
|
||||||
SELECT * FROM targets;""");
|
SELECT * FROM targets LEFT JOIN (SELECT post, positive as reaction FROM reactions WHERE userid = (?)) ON post = id;""");
|
||||||
|
|
||||||
stmt.setObject(1, id);
|
stmt.setObject(1, id);
|
||||||
|
stmt.setObject(2, userID);
|
||||||
|
|
||||||
// query recursively for posts inside a 10km radius
|
// query recursively for posts inside a 10km radius
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
@@ -220,7 +231,9 @@ public class Routes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// calculate anonymous IDs for the post
|
// calculate anonymous IDs for the post
|
||||||
root_post.ifPresent(root -> root.anonymize(Optional.empty()));
|
Vector<Long> anonymousIDs = new Vector<Long>();
|
||||||
|
anonymousIDs.add(userID);
|
||||||
|
root_post.ifPresent(root -> root.anonymize(anonymousIDs));
|
||||||
|
|
||||||
// return the posts
|
// return the posts
|
||||||
if (root_post.isEmpty()) throw new ResponseStatusException(HttpStatus.NOT_FOUND, "No post found");
|
if (root_post.isEmpty()) throw new ResponseStatusException(HttpStatus.NOT_FOUND, "No post found");
|
||||||
@@ -253,8 +266,8 @@ public class Routes {
|
|||||||
stmt.setObject(2, post.title);
|
stmt.setObject(2, post.title);
|
||||||
stmt.setObject(3, post.content);
|
stmt.setObject(3, post.content);
|
||||||
stmt.setObject(4, post.date);
|
stmt.setObject(4, post.date);
|
||||||
stmt.setObject(5, post.location.longitude);
|
stmt.setObject(5, post.location.latitude);
|
||||||
stmt.setObject(6, post.location.latitude);
|
stmt.setObject(6, post.location.longitude);
|
||||||
|
|
||||||
// insert post and get its id
|
// insert post and get its id
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
@@ -296,11 +309,73 @@ public class Routes {
|
|||||||
post.reaction = Optional.empty();
|
post.reaction = Optional.empty();
|
||||||
|
|
||||||
// anonymize code
|
// anonymize code
|
||||||
post.anonymize(Optional.empty());
|
Vector<Long> anonymousIDs = new Vector<Long>();
|
||||||
|
anonymousIDs.add(0L);
|
||||||
|
post.anonymize(anonymousIDs);
|
||||||
|
|
||||||
return post;
|
return post;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/post/{id}")
|
||||||
|
public void reactToPost(@PathVariable long id, @RequestBody JodelPost post) {
|
||||||
|
System.out.println(post.reaction);
|
||||||
|
long userID = User.getID();
|
||||||
|
|
||||||
|
// DB connection and statement
|
||||||
|
Connection c;
|
||||||
|
PreparedStatement stmt;
|
||||||
|
|
||||||
|
// try to get data from db
|
||||||
|
try {
|
||||||
|
// check for the driver
|
||||||
|
Class.forName("org.postgresql.Driver");
|
||||||
|
// get the connection with credentials from env variables
|
||||||
|
c = DriverManager
|
||||||
|
.getConnection("jdbc:postgresql://"+
|
||||||
|
System.getenv("POSTGRES_IP")+"/"+System.getenv("POSTGRES_DB"),
|
||||||
|
System.getenv("POSTGRES_USER"), System.getenv("POSTGRES_PASSWORD"));
|
||||||
|
// disable auto commits
|
||||||
|
c.setAutoCommit(false);
|
||||||
|
|
||||||
|
// create a new statement
|
||||||
|
stmt = c.prepareStatement("UPDATE Reactions SET positive = (?) WHERE post = (?) AND userid = (?) RETURNING id, positive");
|
||||||
|
|
||||||
|
stmt.setObject(1, post.reaction.orElse(null));
|
||||||
|
stmt.setObject(2, id);
|
||||||
|
stmt.setObject(3, userID);
|
||||||
|
|
||||||
|
// insert post and get its id
|
||||||
|
ResultSet rs = stmt.executeQuery();
|
||||||
|
|
||||||
|
// check if there is a parent
|
||||||
|
if (!rs.next()) {
|
||||||
|
// create a new statement
|
||||||
|
stmt = c.prepareStatement("INSERT INTO Reactions(userid, post, positive) VALUES ((?), (?), (?))");
|
||||||
|
|
||||||
|
// fill statement
|
||||||
|
stmt.setObject(1, userID);
|
||||||
|
stmt.setObject(2, id);
|
||||||
|
stmt.setObject(3, post.reaction.orElse(null));
|
||||||
|
|
||||||
|
// execute statement
|
||||||
|
stmt.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit the changes
|
||||||
|
c.commit();
|
||||||
|
|
||||||
|
// close all connections to db
|
||||||
|
stmt.close();
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// else log the error
|
||||||
|
catch ( Exception e ) {
|
||||||
|
System.err.println( e.getClass().getName()+": "+ e.getMessage() );
|
||||||
|
throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "Database is offline");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@DeleteMapping("/post/{id}")
|
@DeleteMapping("/post/{id}")
|
||||||
public void deletePost(@PathVariable long id) {
|
public void deletePost(@PathVariable long id) {
|
||||||
// DB connection and statement
|
// DB connection and statement
|
||||||
@@ -320,9 +395,10 @@ public class Routes {
|
|||||||
c.setAutoCommit(false);
|
c.setAutoCommit(false);
|
||||||
|
|
||||||
// create a new statement
|
// create a new statement
|
||||||
stmt = c.prepareStatement("UPDATE Posts SET deleted = now() WHERE id = ? AND deleted IS NULL");
|
stmt = c.prepareStatement("UPDATE Posts SET deleted = now() WHERE id = ? AND author = (?) AND deleted IS NULL");
|
||||||
|
|
||||||
stmt.setObject(1, id);
|
stmt.setObject(1, id);
|
||||||
|
stmt.setObject(2, User.getID());
|
||||||
|
|
||||||
// insert delete time
|
// insert delete time
|
||||||
stmt.execute();
|
stmt.execute();
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ public class User {
|
|||||||
return SecurityContextHolder.getContext().getAuthentication().getName();
|
return SecurityContextHolder.getContext().getAuthentication().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
static public Long getUserID () {
|
static public Long getID() {
|
||||||
String uuid = SecurityContextHolder.getContext().getAuthentication().getName();
|
String uuid = SecurityContextHolder.getContext().getAuthentication().getName();
|
||||||
Long id = -1L;
|
long id;
|
||||||
|
|
||||||
// DB connection and statement
|
// DB connection and statement
|
||||||
Connection c;
|
Connection c;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
spring.application.name=SWA-Jodel
|
spring.application.name=SWA-Jodel
|
||||||
|
|
||||||
# Security Configuration
|
# Security Configuration
|
||||||
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://keycloak.anxietyprime.de/realms/Jodel
|
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://${KC_HOSTNAME}/realms/Jodel
|
||||||
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs
|
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs
|
||||||
|
|||||||
@@ -3,44 +3,9 @@ package de.anxietyprime.swajodel;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Vector;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
class SwaJodelApplicationTests {
|
class SwaJodelApplicationTests {
|
||||||
|
|
||||||
@Test
|
|
||||||
void anonymousTest() {
|
|
||||||
JodelPost post = new JodelPost(1000);
|
|
||||||
post.comments.add(new JodelPost(1001));
|
|
||||||
post.comments.add(new JodelPost(1002));
|
|
||||||
post.comments.get(0).comments.add(new JodelPost(1000));
|
|
||||||
post.comments.get(0).comments.add(new JodelPost(1001));
|
|
||||||
post.comments.get(0).comments.add(new JodelPost(1000));
|
|
||||||
post.comments.add(new JodelPost(1003));
|
|
||||||
post.comments.get(2).comments.add(new JodelPost(1001));
|
|
||||||
post.comments.get(2).comments.add(new JodelPost(1002));
|
|
||||||
post.comments.get(2).comments.add(new JodelPost(1003));
|
|
||||||
post.comments.get(2).comments.add(new JodelPost(1000));
|
|
||||||
post.comments.add(new JodelPost(1000));
|
|
||||||
post.comments.add(new JodelPost(1001));
|
|
||||||
post.comments.add(new JodelPost(1001));
|
|
||||||
|
|
||||||
post.anonymize(Optional.empty());
|
|
||||||
|
|
||||||
assert (post.anonymousID == 0);
|
|
||||||
assert (post.comments.get(0).anonymousID == 1);
|
|
||||||
assert (post.comments.get(1).anonymousID == 2);
|
|
||||||
assert (post.comments.get(2).anonymousID == 3);
|
|
||||||
assert (post.comments.get(3).anonymousID == 0);
|
|
||||||
assert (post.comments.get(4).anonymousID == 1);
|
|
||||||
assert (post.comments.get(5).anonymousID == 1);
|
|
||||||
assert (post.comments.get(0).comments.get(0).anonymousID == 0);
|
|
||||||
assert (post.comments.get(0).comments.get(1).anonymousID == 1);
|
|
||||||
assert (post.comments.get(0).comments.get(2).anonymousID == 0);
|
|
||||||
assert (post.comments.get(2).comments.get(0).anonymousID == 1);
|
|
||||||
assert (post.comments.get(2).comments.get(1).anonymousID == 2);
|
|
||||||
assert (post.comments.get(2).comments.get(2).anonymousID == 3);
|
|
||||||
assert (post.comments.get(2).comments.get(3).anonymousID == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user