initial commit
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author _bastler@bstly.de
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/auth")
|
||||
public class AuthenticationController extends BaseController {
|
||||
|
||||
private static String authorizationRequestBaseUri = "oauth2/authorization";
|
||||
|
||||
@Autowired
|
||||
private ClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GetMapping
|
||||
public Authentication me() {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
return auth;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@GetMapping("external")
|
||||
public List<Client> getExternalLoginUrls() {
|
||||
List<Client> clients = Lists.newArrayList();
|
||||
Iterable<ClientRegistration> clientRegistrations = null;
|
||||
ResolvableType type = ResolvableType.forInstance(clientRegistrationRepository)
|
||||
.as(Iterable.class);
|
||||
if (type != ResolvableType.NONE
|
||||
&& ClientRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {
|
||||
clientRegistrations = (Iterable<ClientRegistration>) clientRegistrationRepository;
|
||||
}
|
||||
|
||||
clientRegistrations
|
||||
.forEach(registration -> clients.add(new Client(registration.getRegistrationId(),
|
||||
authorizationRequestBaseUri
|
||||
+ "/"
|
||||
+ registration.getRegistrationId())));
|
||||
|
||||
return clients;
|
||||
}
|
||||
|
||||
protected static class Client {
|
||||
|
||||
private String id;
|
||||
private String loginUrl;
|
||||
|
||||
/**
|
||||
* @param id
|
||||
* @param loginUrl
|
||||
*/
|
||||
public Client(String id, String loginUrl) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.loginUrl = loginUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the id
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id the id to set
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the loginUrl
|
||||
*/
|
||||
public String getLoginUrl() {
|
||||
return loginUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loginUrl the loginUrl to set
|
||||
*/
|
||||
public void setLoginUrl(String loginUrl) {
|
||||
this.loginUrl = loginUrl;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import de.bstly.board.businesslogic.UserManager;
|
||||
import de.bstly.board.model.LocalUser;
|
||||
|
||||
/**
|
||||
* @author monitoring@bstly.de
|
||||
*
|
||||
*/
|
||||
public class BaseController {
|
||||
|
||||
@Autowired
|
||||
private UserManager localUserManager;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean authenticated() {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
return auth != null && auth.isAuthenticated();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String getCurrentUsername() {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
return auth != null ? auth.getName() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected LocalUser getLocalUser() {
|
||||
return localUserManager.getByAuth(SecurityContextHolder.getContext().getAuthentication());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import de.bstly.board.businesslogic.CommentManager;
|
||||
import de.bstly.board.businesslogic.VoteManager;
|
||||
import de.bstly.board.controller.support.EntityResponseStatusException;
|
||||
import de.bstly.board.controller.support.RequestBodyErrors;
|
||||
import de.bstly.board.controller.validation.CommentValidator;
|
||||
import de.bstly.board.model.Comment;
|
||||
import de.bstly.board.model.Types;
|
||||
import de.bstly.board.model.Vote;
|
||||
import de.bstly.board.model.VoteType;
|
||||
|
||||
/**
|
||||
* @author Lurkars
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/c")
|
||||
public class CommentController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private CommentManager commentManager;
|
||||
@Autowired
|
||||
private CommentValidator commentValidator;
|
||||
@Autowired
|
||||
private VoteManager voteManager;
|
||||
|
||||
@Value("${bstly.board.size:30}")
|
||||
private int SIZE;
|
||||
@Value("${bstly.board.ranking.gravity:1.8}")
|
||||
private double GRAVITY;
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping({ "/e/{target}", "/e/{target}/{parent}" })
|
||||
public Page<Comment> rankedComments(@PathVariable("target") Long target,
|
||||
@PathVariable("parent") Optional<Long> parent,
|
||||
@RequestParam("page") Optional<Integer> pageParameter,
|
||||
@RequestParam("size") Optional<Integer> sizeParameter,
|
||||
@RequestParam("date") Optional<Instant> dateParameter,
|
||||
@RequestParam("gravity") Optional<Double> gravityParameter) {
|
||||
|
||||
Page<Comment> comments = newComments(target, parent, pageParameter, sizeParameter,
|
||||
dateParameter);
|
||||
commentManager.applyMetadata(getCurrentUsername(), comments.getContent());
|
||||
return comments;
|
||||
|
||||
// Page<Comment> comments = commentManager.fetchByRanking(target, parent.orElse(null),
|
||||
// dateParameter.orElse(Instant.now()), gravityParameter.orElse(GRAVITY),
|
||||
// pageParameter.orElse(0), sizeParameter.orElse(SIZE));
|
||||
// return comments;
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping({ "/c/{target}", "/c/{target}/{parent}" })
|
||||
public Long countComments(@PathVariable("target") Long target,
|
||||
@PathVariable("parent") Optional<Long> parent) {
|
||||
return commentManager.count(target, parent.orElse(null));
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping({ "/e/new/{target}", "/e/new/{target}/{parent}" })
|
||||
public Page<Comment> newComments(@PathVariable("target") Long target,
|
||||
@PathVariable("parent") Optional<Long> parent,
|
||||
@RequestParam("page") Optional<Integer> pageParameter,
|
||||
@RequestParam("size") Optional<Integer> sizeParameter,
|
||||
@RequestParam("date") Optional<Instant> dateParameter) {
|
||||
Page<Comment> comments = commentManager.fetchByDate(target, parent.orElse(null),
|
||||
dateParameter.orElse(Instant.now()), pageParameter.orElse(0),
|
||||
sizeParameter.orElse(SIZE));
|
||||
commentManager.applyMetadata(getCurrentUsername(), comments.getContent());
|
||||
return comments;
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping("/{id}")
|
||||
public Comment getComment(@PathVariable("id") Long id) {
|
||||
Comment comment = commentManager.get(id);
|
||||
|
||||
if (comment == null) {
|
||||
throw new EntityResponseStatusException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
commentManager.applyMetadata(getCurrentUsername(), comment);
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@PostMapping()
|
||||
public Comment createComment(@RequestBody Comment comment) {
|
||||
RequestBodyErrors bindingResult = new RequestBodyErrors(comment);
|
||||
commentValidator.validate(comment, bindingResult);
|
||||
|
||||
if (bindingResult.hasErrors()) {
|
||||
throw new EntityResponseStatusException(bindingResult.getAllErrors(),
|
||||
HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
comment.setCreated(Instant.now());
|
||||
comment.setAuthor(getCurrentUsername());
|
||||
comment = commentManager.save(comment);
|
||||
|
||||
Vote vote = new Vote();
|
||||
vote.setTarget(comment.getId());
|
||||
vote.setTargetType(Types.comment);
|
||||
vote.setType(VoteType.up);
|
||||
vote.setAuthor(getCurrentUsername());
|
||||
voteManager.save(vote);
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import de.bstly.board.businesslogic.EntryManager;
|
||||
import de.bstly.board.model.Comment;
|
||||
import de.bstly.board.model.Entry;
|
||||
import de.bstly.board.model.EntryStatus;
|
||||
import de.bstly.board.model.EntryType;
|
||||
import de.bstly.board.model.LocalUser;
|
||||
import de.bstly.board.model.QLocalUser;
|
||||
import de.bstly.board.model.Types;
|
||||
import de.bstly.board.model.Vote;
|
||||
import de.bstly.board.model.VoteType;
|
||||
import de.bstly.board.repository.CommentRepository;
|
||||
import de.bstly.board.repository.LocalUserRepository;
|
||||
import de.bstly.board.repository.VoteRepository;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author _bastler@bstly.de
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/debug")
|
||||
public class DebugController extends BaseController {
|
||||
|
||||
/**
|
||||
* logger
|
||||
*/
|
||||
private Logger logger = LogManager.getLogger(DebugController.class);
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
@Autowired
|
||||
private LocalUserRepository localUserRepository;
|
||||
@Autowired
|
||||
private CommentRepository commentRepository;
|
||||
@Autowired
|
||||
private VoteRepository voteRepository;
|
||||
@Autowired
|
||||
private EntryManager entryManager;
|
||||
|
||||
@Value("${debug.random.users:0}")
|
||||
private int users;
|
||||
@Value("${debug.random.minEntries:0}")
|
||||
private int minEntries;
|
||||
@Value("${debug.random.maxEntries:10}")
|
||||
private int maxEntries;
|
||||
@Value("${debug.random.entryAge:63115200}")
|
||||
private long entryAge;
|
||||
@Value("${debug.random.minComments:0}")
|
||||
private int minComments;
|
||||
@Value("${debug.random.maxComments:10}")
|
||||
private int maxComments;
|
||||
@Value("${debug.random.subCommentsFactor:0.5}")
|
||||
private double subCommentsFactor;
|
||||
@Value("${debug.random.subCommentsThresh:0.3}")
|
||||
private double subCommentsThresh;
|
||||
@Value("${debug.random.subCommentsDepth:2}")
|
||||
private int subCommentsDepth;
|
||||
@Value("${debug.random.minUpvotes:5}")
|
||||
private int minUpvotes;
|
||||
@Value("${debug.random.maxUpvotes:10}")
|
||||
private int maxUpvotes;
|
||||
@Value("${debug.random.minDownvotes:0}")
|
||||
private int minDownvotes;
|
||||
@Value("${debug.random.maxDownvotes:10}")
|
||||
private int maxDownvotes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/random")
|
||||
public void random() {
|
||||
logger.warn("start random generation");
|
||||
|
||||
long userCount = localUserRepository
|
||||
.count(QLocalUser.localUser.username.startsWith("user"));
|
||||
|
||||
for (long i = userCount; i < userCount + users; i++) {
|
||||
LocalUser localUser = new LocalUser();
|
||||
String username = "user" + i;
|
||||
localUser.setUsername(username);
|
||||
localUser.setPasswordHash(passwordEncoder.encode(username));
|
||||
localUserRepository.save(localUser);
|
||||
logger.trace("Created user: '" + username + "'");
|
||||
}
|
||||
|
||||
logger.info("Created " + users + " users");
|
||||
|
||||
for (long id = 0; id <= userCount; id++) {
|
||||
entries("user" + id, userCount);
|
||||
}
|
||||
|
||||
logger.warn("finished random generation");
|
||||
}
|
||||
|
||||
protected void entries(String username, long userCount) {
|
||||
long numEntries = RandomUtils.nextLong(minEntries, maxEntries);
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
Entry entry = new Entry();
|
||||
entry.setEntryType(EntryType.INTERN);
|
||||
entry.setAuthor(username);
|
||||
entry.setCreated(
|
||||
Instant.now().minus(RandomUtils.nextLong(0, entryAge), ChronoUnit.SECONDS));
|
||||
entry.setTitle(RandomStringUtils.randomAscii(RandomUtils.nextInt(10, 250)));
|
||||
entry.setText(RandomStringUtils.randomAscii(RandomUtils.nextInt(0, 2500)));
|
||||
entry.setEntryStatus(EntryStatus.NORMAL);
|
||||
entry = entryManager.save(entry);
|
||||
logger.trace("Created entry: '" + entry.getId() + "'");
|
||||
comments(entry.getId(), entry.getCreated(), userCount);
|
||||
votes(entry.getId(), Types.entry, userCount);
|
||||
}
|
||||
logger.info("Created " + numEntries + " entries of '" + username + "'");
|
||||
}
|
||||
|
||||
protected void comments(Long target, Instant date, long userCount) {
|
||||
long numComments = RandomUtils.nextLong(minComments, maxComments);
|
||||
logger.debug("Create " + numComments + " comments for '" + target + "'");
|
||||
for (int i = 0; i < numComments; i++) {
|
||||
Comment comment = new Comment();
|
||||
comment.setTarget(target);
|
||||
comment.setAuthor("user" + RandomUtils.nextLong(0, userCount));
|
||||
comment.setText(RandomStringUtils.randomAscii(RandomUtils.nextInt(0, 2500)));
|
||||
comment.setCreated(Instant.now()
|
||||
.minus(RandomUtils.nextLong(0,
|
||||
(Instant.now().toEpochMilli() - date.toEpochMilli()) / 1000),
|
||||
ChronoUnit.SECONDS));
|
||||
comment = commentRepository.save(comment);
|
||||
logger.trace("Created comment: '" + comment.getId() + "'");
|
||||
subComments(target, comment.getId(), comment.getCreated(), subCommentsFactor,
|
||||
subCommentsThresh, 0, userCount);
|
||||
}
|
||||
}
|
||||
|
||||
protected void subComments(Long target, Long parent, Instant date, double factor, double thresh,
|
||||
int depth, long userCount) {
|
||||
if (depth < subCommentsDepth && RandomUtils.nextDouble(0, 1) < thresh) {
|
||||
long numSubComments = RandomUtils.nextLong(0, Math.round(maxComments * factor));
|
||||
logger.debug("Create " + numSubComments + " subComments for '" + parent + "'");
|
||||
for (int i = 0; i < numSubComments; i++) {
|
||||
Comment comment = new Comment();
|
||||
comment.setTarget(target);
|
||||
comment.setParent(parent);
|
||||
comment.setAuthor("user" + RandomUtils.nextLong(0, userCount));
|
||||
comment.setText(RandomStringUtils.randomAscii(RandomUtils.nextInt(0, 2500)));
|
||||
comment.setCreated(Instant.now()
|
||||
.minus(RandomUtils.nextLong(0,
|
||||
(Instant.now().toEpochMilli() - date.toEpochMilli()) / 1000),
|
||||
ChronoUnit.SECONDS));
|
||||
comment = commentRepository.save(comment);
|
||||
logger.trace("Created subComment: '" + comment.getId() + "'");
|
||||
subComments(target, comment.getId(), comment.getCreated(), factor * 0.5,
|
||||
thresh * 0.5, depth++, userCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void votes(Long target, Types targetType, long userCount) {
|
||||
long numUpvotes = RandomUtils.nextLong(minUpvotes, maxUpvotes);
|
||||
logger.debug("Create " + numUpvotes + " upvotes for '" + target + "'");
|
||||
for (int i = 0; i < numUpvotes; i++) {
|
||||
Vote upvote = new Vote();
|
||||
upvote.setTarget(target);
|
||||
upvote.setType(VoteType.up);
|
||||
upvote.setTargetType(targetType);
|
||||
upvote.setAuthor("user" + RandomUtils.nextLong(0, userCount));
|
||||
upvote = voteRepository.save(upvote);
|
||||
logger.trace("Created upvote: '" + upvote.getId() + "'");
|
||||
}
|
||||
|
||||
long numDownvotes = RandomUtils.nextLong(minDownvotes, maxDownvotes);
|
||||
logger.debug("Create " + numDownvotes + " downvotes for '" + target + "'");
|
||||
for (int i = 0; i < numDownvotes; i++) {
|
||||
Vote downvote = new Vote();
|
||||
downvote.setTarget(target);
|
||||
downvote.setType(VoteType.down);
|
||||
downvote.setTargetType(targetType);
|
||||
downvote.setAuthor("user" + RandomUtils.nextLong(0, userCount));
|
||||
downvote = voteRepository.save(downvote);
|
||||
logger.trace("Created downvote: '" + downvote.getId() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import de.bstly.board.businesslogic.EntryManager;
|
||||
import de.bstly.board.businesslogic.VoteManager;
|
||||
import de.bstly.board.controller.support.EntityResponseStatusException;
|
||||
import de.bstly.board.controller.support.RequestBodyErrors;
|
||||
import de.bstly.board.controller.validation.EntryValidator;
|
||||
import de.bstly.board.model.Entry;
|
||||
import de.bstly.board.model.EntryStatus;
|
||||
import de.bstly.board.model.RankedEntry;
|
||||
import de.bstly.board.model.Types;
|
||||
import de.bstly.board.model.Vote;
|
||||
import de.bstly.board.model.VoteType;
|
||||
|
||||
/**
|
||||
* @author Lurkars
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/e")
|
||||
public class EntryController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private EntryManager entryManager;
|
||||
@Autowired
|
||||
private EntryValidator entryValidator;
|
||||
@Autowired
|
||||
private VoteManager voteManager;
|
||||
|
||||
@Value("${bstly.board.size:30}")
|
||||
private int SIZE;
|
||||
@Value("${bstly.board.ranking.gravity:1.8}")
|
||||
private double GRAVITY;
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping()
|
||||
public Page<Entry> rankedEntries(@RequestParam("page") Optional<Integer> pageParameter,
|
||||
@RequestParam("size") Optional<Integer> sizeParameter,
|
||||
@RequestParam("date") Optional<Instant> dateParameter,
|
||||
@RequestParam("gravity") Optional<Double> gravityParameter) {
|
||||
|
||||
if (sizeParameter.isPresent() && sizeParameter.get() > 100) {
|
||||
sizeParameter = Optional.of(100);
|
||||
}
|
||||
|
||||
Page<RankedEntry> entries = entryManager.directFetchByRanking(
|
||||
dateParameter.orElse(Instant.now()), gravityParameter.orElse(GRAVITY),
|
||||
pageParameter.orElse(0), sizeParameter.orElse(SIZE));
|
||||
|
||||
Page<Entry> transformed = new PageImpl<Entry>(
|
||||
entries.getContent().stream().map(rankedEntry -> Entry.fromRankedEntry(rankedEntry))
|
||||
.collect(Collectors.toList()),
|
||||
entries.getPageable(), entries.getTotalElements());
|
||||
|
||||
entryManager.applyMetadata(getCurrentUsername(), transformed.getContent());
|
||||
return transformed;
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping("ranked")
|
||||
public Page<Entry> rankedEntries(@RequestParam("page") Optional<Integer> pageParameter,
|
||||
@RequestParam("size") Optional<Integer> sizeParameter) {
|
||||
|
||||
if (sizeParameter.isPresent() && sizeParameter.get() > 100) {
|
||||
sizeParameter = Optional.of(100);
|
||||
}
|
||||
|
||||
Page<Entry> entries = entryManager.fetchByRanking(pageParameter.orElse(0),
|
||||
sizeParameter.orElse(SIZE));
|
||||
|
||||
entryManager.applyMetadata(getCurrentUsername(), entries.getContent());
|
||||
return entries;
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping("/new")
|
||||
public Page<Entry> newEntries(@RequestParam("page") Optional<Integer> pageParameter,
|
||||
@RequestParam("size") Optional<Integer> sizeParameter,
|
||||
@RequestParam("date") Optional<Instant> dateParameter) {
|
||||
|
||||
if (sizeParameter.isPresent() && sizeParameter.get() > 100) {
|
||||
sizeParameter = Optional.of(100);
|
||||
}
|
||||
|
||||
Page<Entry> entries = entryManager.fetchByDate(dateParameter.orElse(Instant.now()),
|
||||
pageParameter.orElse(0), sizeParameter.orElse(SIZE));
|
||||
entryManager.applyMetadata(getCurrentUsername(), entries.getContent());
|
||||
return entries;
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping("/{id}")
|
||||
public Entry getEntry(@PathVariable("id") Long id) {
|
||||
Entry entry = entryManager.get(id);
|
||||
|
||||
if (entry == null) {
|
||||
throw new EntityResponseStatusException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
entryManager.applyMetadata(getCurrentUsername(), entry);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@PostMapping()
|
||||
public Entry createEntry(@RequestBody Entry entry) {
|
||||
RequestBodyErrors bindingResult = new RequestBodyErrors(entry);
|
||||
entryValidator.validate(entry, bindingResult);
|
||||
|
||||
if (bindingResult.hasErrors()) {
|
||||
throw new EntityResponseStatusException(bindingResult.getAllErrors(),
|
||||
HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
entry.setCreated(Instant.now());
|
||||
entry.setAuthor(getCurrentUsername());
|
||||
entry.setEntryStatus(EntryStatus.NORMAL);
|
||||
entry = entryManager.save(entry);
|
||||
|
||||
Vote vote = new Vote();
|
||||
vote.setTarget(entry.getId());
|
||||
vote.setType(VoteType.up);
|
||||
vote.setTargetType(Types.entry);
|
||||
vote.setAuthor(getCurrentUsername());
|
||||
voteManager.save(vote);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import de.bstly.board.businesslogic.UserManager;
|
||||
import de.bstly.board.controller.support.EntityResponseStatusException;
|
||||
import de.bstly.board.model.LocalUser;
|
||||
|
||||
/**
|
||||
* @author monitoring@bstly.de
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/u")
|
||||
public class UserController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private UserManager userManager;
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping({ "", "/{username}" })
|
||||
public LocalUser getUser(@PathVariable("username") Optional<String> usernameParameter) {
|
||||
String username = usernameParameter.orElse(getCurrentUsername());
|
||||
|
||||
LocalUser user = userManager.getByUsername(username);
|
||||
|
||||
if (user == null) {
|
||||
throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
if (user.getUsername() != getCurrentUsername()) {
|
||||
LocalUser otherUser = new LocalUser();
|
||||
otherUser.setUsername(user.getUsername());
|
||||
otherUser.setAbout(user.getAbout());
|
||||
}
|
||||
|
||||
user.setPasswordHash(null);
|
||||
|
||||
userManager.applyMetadata(getCurrentUsername(), user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@PostMapping()
|
||||
public LocalUser updateUser(@RequestBody LocalUser user) {
|
||||
if (!getCurrentUsername().equals(user.getUsername())) {
|
||||
throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
LocalUser orgUser = userManager.getByUsername(user.getUsername());
|
||||
|
||||
orgUser.setAbout(user.getAbout());
|
||||
orgUser.setDarkTheme(user.isDarkTheme());
|
||||
orgUser.setEmail(user.getEmail());
|
||||
orgUser.setLocale(user.getLocale());
|
||||
orgUser.setSettings(user.getSettings());
|
||||
|
||||
user = userManager.save(orgUser);
|
||||
|
||||
user.setPasswordHash(null);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import de.bstly.board.businesslogic.CommentManager;
|
||||
import de.bstly.board.businesslogic.EntryManager;
|
||||
import de.bstly.board.businesslogic.VoteManager;
|
||||
import de.bstly.board.controller.support.EntityResponseStatusException;
|
||||
import de.bstly.board.model.Types;
|
||||
import de.bstly.board.model.Vote;
|
||||
import de.bstly.board.model.VoteType;
|
||||
|
||||
/**
|
||||
* @author monitoring@bstly.de
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/v")
|
||||
public class VoteController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private VoteManager voteManager;
|
||||
@Autowired
|
||||
private EntryManager entryManager;
|
||||
@Autowired
|
||||
private CommentManager commentManager;
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping("/e/{id}")
|
||||
public long getEntryPoints(@PathVariable("id") Long id) {
|
||||
if (!entryManager.exists(id)) {
|
||||
throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
return voteManager.getPoints(id, Types.entry);
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@PutMapping("/e/{id}")
|
||||
public void voteEntryUp(@PathVariable("id") Long id) {
|
||||
if (!entryManager.exists(id)) {
|
||||
throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
Vote vote = voteManager.get(getCurrentUsername(), Types.entry, id);
|
||||
if (vote == null) {
|
||||
vote = new Vote();
|
||||
vote.setTarget(id);
|
||||
vote.setAuthor(getCurrentUsername());
|
||||
vote.setType(VoteType.up);
|
||||
vote.setTargetType(Types.entry);
|
||||
voteManager.save(vote);
|
||||
throw new EntityResponseStatusException(HttpStatus.CREATED);
|
||||
} else if (!VoteType.up.equals(vote.getType())) {
|
||||
voteManager.delete(vote);
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@DeleteMapping("/e/{id}")
|
||||
public void voteEntryDown(@PathVariable("id") Long id) {
|
||||
if (!entryManager.exists(id)) {
|
||||
throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
Vote vote = voteManager.get(getCurrentUsername(), Types.entry, id);
|
||||
if (vote == null) {
|
||||
vote = new Vote();
|
||||
vote.setTarget(id);
|
||||
vote.setAuthor(getCurrentUsername());
|
||||
vote.setType(VoteType.down);
|
||||
vote.setTargetType(Types.entry);
|
||||
voteManager.save(vote);
|
||||
throw new EntityResponseStatusException(HttpStatus.CREATED);
|
||||
} else if (!VoteType.down.equals(vote.getType())) {
|
||||
voteManager.delete(vote);
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@GetMapping("/c/{id}")
|
||||
public long getCommentPoints(@PathVariable("id") Long id) {
|
||||
if (!commentManager.exists(id)) {
|
||||
throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
return voteManager.getPoints(id, Types.comment);
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@PutMapping("/c/{id}")
|
||||
public void voteCommentUp(@PathVariable("id") Long id) {
|
||||
if (!commentManager.exists(id)) {
|
||||
throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
Vote vote = voteManager.get(getCurrentUsername(), Types.comment, id);
|
||||
if (vote == null) {
|
||||
vote = new Vote();
|
||||
vote.setTarget(id);
|
||||
vote.setAuthor(getCurrentUsername());
|
||||
vote.setType(VoteType.up);
|
||||
vote.setTargetType(Types.comment);
|
||||
voteManager.save(vote);
|
||||
throw new EntityResponseStatusException(HttpStatus.CREATED);
|
||||
} else if (!VoteType.up.equals(vote.getType())) {
|
||||
voteManager.delete(vote);
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@DeleteMapping("/c/{id}")
|
||||
public void voteCommentDown(@PathVariable("id") Long id) {
|
||||
if (!commentManager.exists(id)) {
|
||||
throw new EntityResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
Vote vote = voteManager.get(getCurrentUsername(), Types.comment, id);
|
||||
if (vote == null) {
|
||||
vote = new Vote();
|
||||
vote.setTarget(id);
|
||||
vote.setAuthor(getCurrentUsername());
|
||||
vote.setType(VoteType.down);
|
||||
vote.setTargetType(Types.comment);
|
||||
voteManager.save(vote);
|
||||
throw new EntityResponseStatusException(HttpStatus.CREATED);
|
||||
} else if (!VoteType.down.equals(vote.getType())) {
|
||||
voteManager.delete(vote);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller.support;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author monitoring@bstly.de
|
||||
*
|
||||
*/
|
||||
@ControllerAdvice
|
||||
public class ControllerExceptionHandler extends ResponseEntityExceptionHandler {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param exception
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@ExceptionHandler(value = { EntityResponseStatusException.class })
|
||||
protected ResponseEntity<Object> handleResponseEntityStatusException(RuntimeException exception,
|
||||
WebRequest request) {
|
||||
EntityResponseStatusException entityResponseStatusException = (EntityResponseStatusException) exception;
|
||||
return handleExceptionInternal(exception, entityResponseStatusException.getBody(),
|
||||
new HttpHeaders(), entityResponseStatusException.getStatus(), request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller.support;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.springframework.core.NestedExceptionUtils;
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author monitoring@bstly.de
|
||||
*
|
||||
*/
|
||||
public class EntityResponseStatusException extends NestedRuntimeException {
|
||||
|
||||
/**
|
||||
* default serialVersionUID
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final HttpStatus status;
|
||||
|
||||
@Nullable
|
||||
private final Object body;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param status
|
||||
*/
|
||||
public EntityResponseStatusException(HttpStatus status) {
|
||||
this(null, status);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
* @param status
|
||||
*/
|
||||
public EntityResponseStatusException(@Nullable Object body, HttpStatus status) {
|
||||
this(body, status, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
* @param status
|
||||
* @param cause
|
||||
*/
|
||||
public EntityResponseStatusException(@Nullable Object body, HttpStatus status, @Nullable Throwable cause) {
|
||||
super(null, cause);
|
||||
Assert.notNull(status, "HttpStatus is required");
|
||||
this.status = status;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public HttpStatus getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public Object getBody() {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String msg = this.status + (this.body != null ? " \"" + this.body + "\"" : "");
|
||||
return NestedExceptionUtils.buildMessage(msg, getCause());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller.support;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.validation.AbstractBindingResult;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author _bastler@bstly.de
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RequestBodyErrors extends AbstractBindingResult {
|
||||
|
||||
@Nullable
|
||||
private final Object target;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param target
|
||||
* @param objectName
|
||||
*/
|
||||
public RequestBodyErrors(@Nullable Object target) {
|
||||
super("request-body");
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.springframework.validation.AbstractBindingResult#getTarget()
|
||||
*/
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see
|
||||
* org.springframework.validation.AbstractBindingResult#getActualFieldValue(java
|
||||
* .lang.String)
|
||||
*/
|
||||
@Override
|
||||
protected Object getActualFieldValue(String field) {
|
||||
// Not necessary
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller.validation;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
import de.bstly.board.businesslogic.CommentManager;
|
||||
import de.bstly.board.businesslogic.EntryManager;
|
||||
import de.bstly.board.model.Comment;
|
||||
|
||||
/**
|
||||
* @author monitoring@bstly.de
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class CommentValidator implements Validator {
|
||||
|
||||
@Autowired
|
||||
private CommentManager commentManager;
|
||||
@Autowired
|
||||
private EntryManager entryManager;
|
||||
|
||||
/*
|
||||
* @see org.springframework.validation.Validator#supports(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return clazz.isAssignableFrom(Comment.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.springframework.validation.Validator#validate(java.lang.Object,
|
||||
* org.springframework.validation.Errors)
|
||||
*/
|
||||
@Override
|
||||
public void validate(Object target, Errors errors) {
|
||||
Comment comment = (Comment) target;
|
||||
if (comment.getTarget() == null) {
|
||||
errors.rejectValue("target", "REQUIRED");
|
||||
} else if (!entryManager.exists(comment.getTarget())) {
|
||||
errors.rejectValue("target", "INVALID");
|
||||
}
|
||||
|
||||
if (comment.getParent() != null) {
|
||||
Comment parent = commentManager.get(comment.getParent());
|
||||
if (parent == null || !parent.getTarget().equals(comment.getTarget())) {
|
||||
errors.rejectValue("parent", "INVALID");
|
||||
}
|
||||
}
|
||||
|
||||
if (!StringUtils.hasText(comment.getText())) {
|
||||
errors.rejectValue("text", "REQUIRED");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.board.controller.validation;
|
||||
|
||||
import org.apache.commons.validator.routines.UrlValidator;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
import de.bstly.board.model.Entry;
|
||||
import de.bstly.board.model.EntryType;
|
||||
|
||||
/**
|
||||
* @author monitoring@bstly.de
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class EntryValidator implements Validator {
|
||||
|
||||
private UrlValidator urlValidator = new UrlValidator();
|
||||
|
||||
/*
|
||||
* @see org.springframework.validation.Validator#supports(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return clazz.isAssignableFrom(Entry.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.springframework.validation.Validator#validate(java.lang.Object,
|
||||
* org.springframework.validation.Errors)
|
||||
*/
|
||||
@Override
|
||||
public void validate(Object target, Errors errors) {
|
||||
Entry entry = (Entry) target;
|
||||
if (!StringUtils.hasText(entry.getTitle())) {
|
||||
errors.rejectValue("title", "REQUIRED");
|
||||
}
|
||||
|
||||
if (entry.getEntryType() == null) {
|
||||
errors.rejectValue("entrytype", "REQUIRED");
|
||||
} else if (EntryType.LINK.equals(entry.getEntryType())) {
|
||||
entry.setText(null);
|
||||
if (!StringUtils.hasText(entry.getUrl())) {
|
||||
errors.rejectValue("url", "REQUIRED");
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(entry.getUrl()) && !urlValidator.isValid(entry.getUrl())) {
|
||||
errors.rejectValue("url", "INVALID");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user