package de.skuzzle.polly.core.internal.httpv2;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import de.skuzzle.polly.http.annotations.Get;
import de.skuzzle.polly.http.annotations.OnRegister;
import de.skuzzle.polly.http.annotations.Param;
import de.skuzzle.polly.http.annotations.Post;
import de.skuzzle.polly.http.api.AlternativeAnswerException;
import de.skuzzle.polly.http.api.Controller;
import de.skuzzle.polly.http.api.HttpCookie;
import de.skuzzle.polly.http.api.HttpException;
import de.skuzzle.polly.http.api.HttpServer;
import de.skuzzle.polly.http.api.answers.HttpAnswer;
import de.skuzzle.polly.http.api.answers.HttpAnswers;
import de.skuzzle.polly.sdk.IrcManager;
import de.skuzzle.polly.sdk.MyPolly;
import de.skuzzle.polly.sdk.Types;
import de.skuzzle.polly.sdk.User;
import de.skuzzle.polly.sdk.UserManager;
import de.skuzzle.polly.sdk.eventlistener.IrcUser;
import de.skuzzle.polly.sdk.exceptions.DatabaseException;
import de.skuzzle.polly.sdk.httpv2.GsonHttpAnswer;
import de.skuzzle.polly.sdk.httpv2.PollyController;
import de.skuzzle.polly.sdk.httpv2.SuccessResult;
import de.skuzzle.polly.sdk.httpv2.WebinterfaceManager;
import de.skuzzle.polly.sdk.roles.RoleManager;
import de.skuzzle.polly.sdk.time.Milliseconds;
import de.skuzzle.polly.tools.concurrent.RunLater;
import de.skuzzle.polly.tools.strings.StringUtils;
public class IndexController extends PollyController {
private final static String ADMIN_CATEGORY_KEY = "indexAdminCategory"; //$NON-NLS-1$
private final static String GENERAL_CATEGORY_KEY = "indexGeneralCategory"; //$NON-NLS-1$
private final static String HOME_NAME_KEY = "indexHomePage"; //$NON-NLS-1$
private final static String HOME_DESC_KEY = "indexHomeDesc"; //$NON-NLS-1$
private final static String STATUS_NAME_KEY = "indexStatusPage"; //$NON-NLS-1$
private final static String STATUS_DESC_KEY = "indexStatusDesc"; //$NON-NLS-1$
public final static String PAGE_INDEX = "/"; //$NON-NLS-1$
public final static String PAGE_STATUS = "/pages/status"; //$NON-NLS-1$
public final static String PAGE_RECOVER_PW = "/pages/recover"; //$NON-NLS-1$
private final static String CONTENT_INDEX = "templatesv2/home.html"; //$NON-NLS-1$
private static final String CONTENT_STATUS = "templatesv2/status.html"; //$NON-NLS-1$
private final static String CONTENT_RECOVER_PW = "templatesv2/recover.pw.html"; //$NON-NLS-1$
public final static String API_LOGIN = "/api/login"; //$NON-NLS-1$
public final static String API_CHECK_LOGIN = "/api/checkLogin"; //$NON-NLS-1$
public final static String API_LOGOUT = "/api/logout"; //$NON-NLS-1$
public final static String API_CALC_EXPRESSION = "/api/calculateExpression"; //$NON-NLS-1$
public final static String API_RUN_GC = "/api/runGC"; //$NON-NLS-1$
public final static String API_SHUTDOWN = "/api/shutdown"; //$NON-NLS-1$
public final static String API_ADD_NEWS = "/api/postNews"; //$NON-NLS-1$
public final static String API_DELETE_NEWS = "/api/deleteNews"; //$NON-NLS-1$
public final static String API_RECOVER_PW = "/api/recoverPw"; //$NON-NLS-1$
public final static String API_VERIFY_RECOVERY = "/api/verifyRecovery"; //$NON-NLS-1$
private final static PasswordRecoverer RECOVERER = new PasswordRecoverer();
private final NewsManager newsManager;
public IndexController(MyPolly myPolly, NewsManager newsManager) {
super(myPolly);
this.newsManager = newsManager;
}
@Override
protected Controller createInstance() {
return new IndexController(this.getMyPolly(), this.newsManager);
}
@Get(STYLE_SHEET_NAME)
public HttpAnswer getCSS() {
return HttpAnswers.newTemplateAnswer(
STYLE_SHEET_NAME, new HashMap<String, Object>());
}
public final static class LoginResult {
public boolean success;
public String userName;
public LoginResult(boolean success, String userName) {
super();
this.success = success;
this.userName = userName;
}
}
@Post(API_LOGIN)
public HttpAnswer login(
@Param("name") String name,
@Param("pw") String pw) {
final UserManager um = this.getMyPolly().users();
final User user = um.getUser(name);
if (user != null && user.checkPassword(pw)) {
this.getSession().set(WebinterfaceManager.USER, user);
this.getSession().set(WebinterfaceManager.LOGIN_TIME, new Date());
final Date now = new Date();
this.getSession().setExpirationDate(
new Date(now.getTime() + this.getServer().getSessionType()));
// cookie to renew the session time out to count it from login time
final HttpCookie renewTimeout = new HttpCookie(
HttpServer.SESSION_ID_NAME, this.getSession().getId(),
Milliseconds.toSeconds(this.getServer().sessionLiveTime()));
final LoginResult result = new LoginResult(true, name);
return new GsonHttpAnswer(200, result).addCookie(renewTimeout);
}
return new GsonHttpAnswer(200, new LoginResult(false, "")); //$NON-NLS-1$
}
public final static class SessionTimeResult {
public final String timeLeft;
public final boolean loggedIn;
public final String userName;
public SessionTimeResult(String name, String timeLeft, boolean loggedIn) {
super();
this.userName = name;
this.timeLeft = timeLeft;
this.loggedIn = loggedIn;
}
}
@Get(API_CHECK_LOGIN)
public HttpAnswer checkLogin() {
final User user = this.getSessionUser();
if (user != null) {
final long now = new Date().getTime();
final long start = ((Date) this.getSession().get(
WebinterfaceManager.LOGIN_TIME)).getTime();
final long diff = now - start;
long tl = this.getServer().sessionLiveTime() - diff;
tl = (int) Math.ceil(tl / (double) 60000) * 60000; // round to minutes
final Object result = new SessionTimeResult(user.getName(),
this.getMyPolly().formatting().formatTimeSpanMs(tl), true);
return new GsonHttpAnswer(200, result);
} else {
return new GsonHttpAnswer(200, new SessionTimeResult("", "", false)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
@Get(API_LOGOUT)
public HttpAnswer logout() {
this.getSession().detach(WebinterfaceManager.USER);
this.getSession().kill();
return HttpAnswers.newRedirectAnswer(PAGE_INDEX);
}
@Get(value = PAGE_INDEX, name = HOME_NAME_KEY)
@OnRegister({
WebinterfaceManager.ADD_MENU_ENTRY,
MSG.FAMILY,
GENERAL_CATEGORY_KEY,
HOME_DESC_KEY
})
public HttpAnswer index() {
final Map<String, Object> c = this.createContext(CONTENT_INDEX);
c.put("allNews", this.newsManager.getAllNews()); //$NON-NLS-1$
c.put("commits", GitHubController.getLatestCommits()); //$NON-NLS-1$
c.put("lastRefresh", GitHubController.getLastRefresh()); //$NON-NLS-1$
c.put("Strings", StringUtils.class); //$NON-NLS-1$
return this.makeAnswer(c);
}
@Get(PAGE_RECOVER_PW)
public HttpAnswer recoverPw() {
final Map<String, Object> c = this.createContext(CONTENT_RECOVER_PW);
return this.makeAnswer(c);
}
@Get(API_RECOVER_PW)
public HttpAnswer startRecoverPw(@Param("nickName") String nickName) {
final IrcManager irc = this.getMyPolly().irc();
if (!irc.isOnline(nickName)) {
return new GsonHttpAnswer(200, new SuccessResult(false,
String.format("Nickname %s nicht online", nickName)));
} else if (!this.getMyPolly().users().isSignedOn(new IrcUser(nickName, "", ""))) { //$NON-NLS-1$ //$NON-NLS-2$
return new GsonHttpAnswer(200, new SuccessResult(false,
String.format("Benutzer %s ist nicht angemeldet", nickName)));
}
RECOVERER.startRecovery(this.getMyPolly().irc(), nickName);
return new GsonHttpAnswer(200, new SuccessResult(true, "")); //$NON-NLS-1$
}
@Get(API_VERIFY_RECOVERY)
public HttpAnswer finishRecoverPw(@Param("nickName") String nickName,
@Param("vCode") String vCode, @Param("pw") String pw) {
if (!RECOVERER.verifyRecovery(nickName, vCode)) {
return new GsonHttpAnswer(200, new SuccessResult(false,
vCode + " entspricht nicht dem erwarteten Code"));
}
final UserManager users = this.getMyPolly().users();
final User user = users.getUser(new IrcUser(nickName, "", "")); //$NON-NLS-1$ //$NON-NLS-2$
try {
this.getMyPolly().persistence().writeAtomic(write -> user.setPassword(pw));
return new GsonHttpAnswer(200,
new SuccessResult(true, "Passwort wurde geƤndert"));
} catch (DatabaseException e) {
return new GsonHttpAnswer(200, new SuccessResult(false, e.getMessage()));
}
}
@Get(API_CALC_EXPRESSION)
public HttpAnswer calculateExpression(
@Param(value = "expr", optional = true) String expression) {
final Types result = this.getMyPolly().parse(expression);
return new GsonHttpAnswer(200,
new SuccessResult(true, result.valueString(this.getMyPolly().formatting())));
}
@Get(value = PAGE_STATUS, name = STATUS_NAME_KEY)
@OnRegister({
WebinterfaceManager.ADD_MENU_ENTRY,
MSG.FAMILY,
ADMIN_CATEGORY_KEY,
STATUS_DESC_KEY,
RoleManager.ADMIN_PERMISSION
})
public HttpAnswer statusPage() throws AlternativeAnswerException {
this.requirePermissions(RoleManager.ADMIN_PERMISSION);
final Map<String, Object> c = this.createContext(CONTENT_STATUS);
c.put("maxMemory", Runtime.getRuntime().maxMemory()); //$NON-NLS-1$
c.put("totalMemory", Runtime.getRuntime().totalMemory()); //$NON-NLS-1$
c.put("freeMemory", Runtime.getRuntime().freeMemory()); //$NON-NLS-1$
c.put("status", this.getMyPolly().status().getCurrentStatusMap()); //$NON-NLS-1$
return this.makeAnswer(c);
}
@Get(API_RUN_GC)
public HttpAnswer runGC() throws AlternativeAnswerException {
this.requirePermissions(RoleManager.ADMIN_PERMISSION);
System.gc();
return HttpAnswers.newRedirectAnswer(PAGE_STATUS);
}
@Post(API_ADD_NEWS)
public HttpAnswer postNews(
@Param("caption") String caption,
@Param("body") String body) throws HttpException {
this.requirePermissions(NewsManager.ADD_NEWS_PERMISSION);
try {
this.newsManager.addNewsEntry(this.getSessionUser(), caption, body);
return HttpAnswers.newRedirectAnswer(PAGE_INDEX);
} catch (DatabaseException e) {
throw new HttpException(e);
}
}
@Get(API_DELETE_NEWS)
public HttpAnswer deleteNews(@Param("newsId") int newsId) throws HttpException {
this.requirePermissions(NewsManager.DELETE_NEWS_PERMISSION);
try {
this.newsManager.deleteNewsEntry(newsId);
return HttpAnswers.newRedirectAnswer(PAGE_INDEX);
} catch (DatabaseException e) {
throw new HttpException(e);
}
}
@Get(API_SHUTDOWN)
public HttpAnswer shutdownPolly(@Param("restart") final Boolean restart)
throws AlternativeAnswerException {
this.requirePermissions(RoleManager.ADMIN_PERMISSION);
final int shutdownTimeSeconds = 10;
new RunLater("SHUTDOWN_TIMER", Milliseconds.fromSeconds(shutdownTimeSeconds)) { //$NON-NLS-1$
@Override
public void run() {
if (restart) {
getMyPolly().shutdownManager().restart();
} else {
getMyPolly().shutdownManager().shutdown();
}
}
}.start();
final String s = restart ? MSG.indexRestart : MSG.indexShutdown;
return HttpAnswers.newStringAnswer(MSG.bind(s, shutdownTimeSeconds));
}
}