diff --git a/go-fullbuild.bat b/go-fullbuild.bat index 1fddeb5..152a3f3 100644 --- a/go-fullbuild.bat +++ b/go-fullbuild.bat @@ -1,6 +1,8 @@ @echo off +setlocal cd /d %~dp0 jps -lm|grep org.nkjmlab.go. | gawk "{print $1}" | xargs -r -n1 taskkill /F /T /PID call mvn clean install -DskipTests=true -f sorm4j call mvn clean install -DskipTests=true -f nkjmlab-utils call mvn clean install dependency:copy-dependencies -DoutputDirectory=target/lib -f nkjmlab-go-webapp +endlocal diff --git a/nkjmlab-go-webapp/pom.xml b/nkjmlab-go-webapp/pom.xml index fc6de6c..bad96dd 100644 --- a/nkjmlab-go-webapp/pom.xml +++ b/nkjmlab-go-webapp/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.nkjmlab nkjmlab-go-webapp - 0.10.0 + 0.10.2 Go Web Application https://github.com/nkjmlab/nkjmlab-go-webapp @@ -20,12 +20,12 @@ - scm:git:git@github.com:yuu-nkjmb/nkjmlab-go-webapp.git - https://github.com/yuu-nkjm/nkjmlab-go-webapp + scm:git:git@github.com:nkjmlab/nkjmlab-go.git + https://github.com/nkjmlab/nkjmlab-go HEAD - 0.9.4 + 0.9.5 1.4.16 UTF-8 true @@ -97,7 +97,7 @@ commons-io commons-io - 2.12.0 + 2.13.0 diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/DataSourceManager.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/DataSourceManager.java index b0e9dd8..6cf3e18 100644 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/DataSourceManager.java +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/DataSourceManager.java @@ -20,7 +20,7 @@ public class DataSourceManager { private static final int DEFAULT_TIMEOUT_SECONDS = 30; - private H2LocalDataSourceFactory factory; + private final H2LocalDataSourceFactory factory; public DataSourceManager() { FileDatabaseConfigJson fileDbConf = getFileDbConfig(); @@ -32,6 +32,21 @@ public DataSourceManager() { log.info("server jdbcUrl={}", factory.getServerModeJdbcUrl()); } + private static FileDatabaseConfigJson getFileDbConfig() { + try { + return JacksonMapper.getDefaultMapper() + .toObject(ResourceUtils.getResourceAsFile("/conf/h2.json"), + FileDatabaseConfigJson.Builder.class) + .build(); + } catch (Exception e) { + log.warn("Try to load h2.json.default"); + return JacksonMapper.getDefaultMapper() + .toObject(ResourceUtils.getResourceAsFile("/conf/h2.json.default"), + FileDatabaseConfigJson.Builder.class) + .build(); + } + } + public DataSource createHikariInMemoryDataSource() { return createHikariDataSource(factory.getInMemoryModeJdbcUrl(), factory.getUsername(), factory.getPassword()); @@ -76,20 +91,6 @@ private static JdbcConnectionPool createH2DataSource(String url, String user, St return ds; } - private static FileDatabaseConfigJson getFileDbConfig() { - try { - return JacksonMapper.getDefaultMapper() - .toObject(ResourceUtils.getResourceAsFile("/conf/h2.json"), - FileDatabaseConfigJson.Builder.class) - .build(); - } catch (Exception e) { - log.warn("Try to load h2.json.default"); - return JacksonMapper.getDefaultMapper() - .toObject(ResourceUtils.getResourceAsFile("/conf/h2.json.default"), - FileDatabaseConfigJson.Builder.class) - .build(); - } - } public H2LocalDataSourceFactory getFactory() { return factory; diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAccessManager.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAccessManager.java index 98d9b07..d1ae487 100644 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAccessManager.java +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAccessManager.java @@ -1,7 +1,7 @@ package org.nkjmlab.go.javalin; import java.util.Set; -import org.nkjmlab.go.javalin.auth.GoAuthService; +import org.nkjmlab.go.javalin.jsonrpc.GoAuthService; import org.nkjmlab.go.javalin.model.relation.UsersTable; import org.nkjmlab.go.javalin.model.relation.UsersTable.User; import io.javalin.http.Context; @@ -11,11 +11,11 @@ public class GoAccessManager implements AccessManager { - public enum UserRole implements RouteRole { + public enum AccessRole implements RouteRole { BEFORE_LOGIN, GUEST, STUDENT, ADMIN; - static final UserRole[] LOGIN_ROLES = new UserRole[] {GUEST, STUDENT, ADMIN}; + static final AccessRole[] LOGIN_ROLES = new AccessRole[] {GUEST, STUDENT, ADMIN}; } @@ -27,26 +27,26 @@ public GoAccessManager(UsersTable usersTable, GoAuthService authService) { this.authService = authService; } - UserRole toUserRole(UsersTable usersTable, String sessionId) { + AccessRole toUserRole(UsersTable usersTable, String sessionId) { if (!authService.isSignin(sessionId)) { - return UserRole.BEFORE_LOGIN; + return AccessRole.BEFORE_LOGIN; } User u = authService.toSigninSession(sessionId) .map(login -> usersTable.selectByPrimaryKey(login.userId())).orElse(null); if (u == null) { - return UserRole.BEFORE_LOGIN; + return AccessRole.BEFORE_LOGIN; } if (u.isAdmin()) { - return UserRole.ADMIN; + return AccessRole.ADMIN; } if (u.isStudent()) { - return UserRole.STUDENT; + return AccessRole.STUDENT; } if (u.isGuest()) { - return UserRole.GUEST; + return AccessRole.GUEST; } - return UserRole.BEFORE_LOGIN; + return AccessRole.BEFORE_LOGIN; } diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAppHandlers.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAppHandlers.java deleted file mode 100644 index 0abff3e..0000000 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoAppHandlers.java +++ /dev/null @@ -1,250 +0,0 @@ -package org.nkjmlab.go.javalin; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; -import org.nkjmlab.go.javalin.GoAccessManager.UserRole; -import org.nkjmlab.go.javalin.auth.GoAuthService; -import org.nkjmlab.go.javalin.auth.GoAuthService.SigninSession; -import org.nkjmlab.go.javalin.model.relation.GameRecordsTable.GameRecord; -import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameState; -import org.nkjmlab.go.javalin.model.relation.HandUpsTable.HandUp; -import org.nkjmlab.go.javalin.model.relation.LoginsTable.Login; -import org.nkjmlab.go.javalin.model.relation.MatchingRequestsTable.MatchingRequest; -import org.nkjmlab.go.javalin.model.relation.UsersTable; -import org.nkjmlab.go.javalin.model.relation.UsersTable.User; -import org.nkjmlab.go.javalin.websocket.WebsocketSessionsManager; -import org.nkjmlab.sorm4j.common.Tuple.Tuple2; -import org.nkjmlab.util.java.net.UrlUtils; -import org.nkjmlab.util.java.web.ViewModel; -import io.javalin.Javalin; -import io.javalin.http.Context; -import io.javalin.http.Handler; -import jakarta.servlet.http.HttpServletRequest; - -public class GoAppHandlers { - - public static class GoHandler implements Handler { - - private final Function>>>>> handler; - private final GoTables goTables; - private final GoAuthService authService; - - public GoHandler(GoTables goTables, GoAuthService authService, - Function>>>>> handler) { - this.handler = handler; - this.goTables = goTables; - this.authService = authService; - } - - @Override - public void handle(Context ctx) throws Exception { - String filePath = UrlUtils.of(ctx.url()).getPath().replaceFirst("^/app/", ""); - ViewModel.Builder model = createDefaultViewModelBuilder(goTables.usersTable, ctx.req()); - Optional session = authService.toSigninSession(ctx.req().getSession().getId()); - - handler.apply(goTables).apply(ctx).apply(filePath).apply(model).accept(session); - } - - private ViewModel.Builder createDefaultViewModelBuilder(UsersTable usersTable, - HttpServletRequest request) { - Map map = ViewModel.builder() - .setFileModifiedDate(GoWebAppConfig.WEB_APP_CONFIG.getWebRootDirectory(), 10, "js", "css") - .put("webjars", GoWebAppConfig.WEB_APP_CONFIG.getWebJars()) - .put("currentUser", getCurrentUserAccount(usersTable, request)).build(); - return ViewModel.builder(map); - } - - private User getCurrentUserAccount(UsersTable usersTable, HttpServletRequest request) { - Optional u = authService.toSigninSession(request.getSession().getId()) - .map(uid -> usersTable.selectByPrimaryKey(uid.userId())); - return u.orElse(new User()); - } - } - - private static class PlayGoHandler extends GoHandler { - - public PlayGoHandler(GoTables goTables, GoAuthService authService) { - super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> { - session.ifPresent(opts -> { - boolean attend = gtbl.loginsTable.isAttendance(opts.userId()); - model.put("isAttendance", attend); - model.put("problemGroupsJson", gtbl.problemsTable.getProblemGroupsNode()); - }); - ctx.render(filePath, model.build()); - }); - - } - } - - private static class AppIndexGoHandler extends GoHandler { - public AppIndexGoHandler(GoTables goTables, GoAuthService authService) { - super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> { - session.ifPresent(opts -> { - boolean attend = gtbl.loginsTable.isAttendance(opts.userId()); - model.put("isAttendance", attend); - model.put("problemGroupsJson", gtbl.problemsTable.getProblemGroupsNode()); - }); - ctx.render(filePath, model.build()); - }); - } - } - - private static class PlayersAllGoHandler extends GoHandler { - public PlayersAllGoHandler(GoTables goTables, GoAuthService authService) { - super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> { - List> users = gtbl.usersTable.readAllWithLastLogin(); - List loginJsons = - users.stream().map(t -> new GoAppHandlers.LoginJson(t.getT2(), t.getT1())) - .collect(Collectors.toList()); - model.put("userAccounts", loginJsons); - ctx.render("players.html", model.build()); - }); - } - } - private static class PlayersGoHandler extends GoHandler { - public PlayersGoHandler(GoTables goTables, GoAuthService authService) { - super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> { - List> users = gtbl.usersTable.readAllWithLastLogin(); - List loginJsons = users.stream().filter(t -> t.getT1().isStudent()) - .map(t -> new GoAppHandlers.LoginJson(t.getT2(), t.getT1())) - .collect(Collectors.toList()); - model.put("userAccounts", loginJsons); - ctx.render(filePath, model.build()); - }); - } - } - private static class GamesAllGoHandler extends GoHandler { - public GamesAllGoHandler(GoTables goTables, GoAuthService authService, - WebsocketSessionsManager websocketManager) { - super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> { - List tmp = - gtbl.gameStatesTables.readTodayGameJsons().stream().map(gsj -> { - String gameId = gsj.gameId(); - GoAppHandlers.GameStateViewJson json = new GoAppHandlers.GameStateViewJson(gsj, - gtbl.handsUpTable.selectByPrimaryKey(gameId), - websocketManager.getWatchingUniqueStudentsNum(gameId)); - return json; - }).collect(Collectors.toList()); - model.put("games", tmp); - ctx.render("games.html", model.build()); - }); - } - } - private static class GamesGoHandler extends GoHandler { - public GamesGoHandler(GoTables goTables, GoAuthService authService, - WebsocketSessionsManager websocketManager) { - super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> { - List gids = websocketManager.readActiveGameIdsOrderByGameId(); - List tmp = - gids.stream().map(gid -> gtbl.gameStatesTables.readLatestGameState(gid)) - .map(gsj -> new GoAppHandlers.GameStateViewJson(gsj, - gtbl.handsUpTable.selectByPrimaryKey(gsj.gameId()), - websocketManager.getWatchingUniqueStudentsNum(gsj.gameId()))) - .collect(Collectors.toList()); - model.put("games", - tmp.stream().filter(j -> j.watchingStudentsNum() > 0).collect(Collectors.toList())); - ctx.render(filePath, model.build()); - }); - } - } - private static class GameRecordTableGoHandler extends GoHandler { - public GameRecordTableGoHandler(GoTables goTables, GoAuthService authService) { - super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> { - String userId = ctx.queryParam("userId"); - List records = gtbl.gameRecordsTable.readByUserId(userId); - model.put("records", records); - ctx.render("fragment/game-record-table.html", model.build()); - }); - } - } - - private static class QuestionTableGoHandler extends GoHandler { - public QuestionTableGoHandler(GoTables goTables, GoAuthService authService) { - super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> { - List gids = gtbl.handsUpTable.readAllGameIds(); - List tmp = gtbl.gameStatesTables.readLatestBoardsJson(gids) - .stream().map(gsj -> new GoAppHandlers.GameStateViewJson(gsj, - gtbl.handsUpTable.selectByPrimaryKey(gsj.gameId()), 0)) - .toList(); - model.put("games", tmp); - ctx.render(filePath, model.build()); - }); - } - } - - private static class WaitingRequestGoHandler extends GoHandler { - public WaitingRequestGoHandler(GoTables goTables, GoAuthService authService) { - super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> { - String userId = ctx.queryParam("userId"); - if (userId != null) { - List tmp = gtbl.matchingRequestsTable.readRequests(); - model.put("requests", - tmp.stream().filter(r -> r.userId().equals(userId)).collect(Collectors.toList())); - } else { - model.put("requests", gtbl.matchingRequestsTable.readRequests()); - } - ctx.render(filePath, model.build()); - }); - } - } - private static class WaitingRequestSmallGoHandler extends GoHandler { - public WaitingRequestSmallGoHandler(GoTables goTables, GoAuthService authService) { - super(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> { - String userId = ctx.queryParam("userId"); - List tmp = gtbl.matchingRequestsTable.readRequests(); - MatchingRequest req = tmp.stream().filter(r -> r.userId().equals(userId)).findAny() - .orElse(new MatchingRequest()); - model.put("req", req); - model.put("reqNum", tmp.size()); - ctx.render(filePath, model.build()); - }); - } - } - - - public static void prepareGetHandler(Javalin app, WebsocketSessionsManager websocketManager, - GoTables goTables, GoAuthService authService) { - - app.get("/app", ctx -> ctx.redirect("/app/index.html")); - app.get("/app/index.html", new AppIndexGoHandler(goTables, authService)); - app.get("/app/play.html", new PlayGoHandler(goTables, authService), UserRole.LOGIN_ROLES); - app.get("/app/players-all.html", new PlayersAllGoHandler(goTables, authService), - UserRole.ADMIN); - app.get("/app/players.html", new PlayersGoHandler(goTables, authService), UserRole.ADMIN); - app.get("/app/games-all.html", new GamesAllGoHandler(goTables, authService, websocketManager), - UserRole.ADMIN); - app.get("/app/games.html", new GamesGoHandler(goTables, authService, websocketManager), - UserRole.ADMIN); - app.get("/app/fragment/game-record-table.html", - new GameRecordTableGoHandler(goTables, authService), UserRole.LOGIN_ROLES); - app.get("/app/fragment/question-table*", new QuestionTableGoHandler(goTables, authService), - UserRole.ADMIN); - - app.get("/app/fragment/waiting-request-table.html", - new WaitingRequestGoHandler(goTables, authService), UserRole.ADMIN); - - app.get("/app/fragment/waiting-request-table-small.html", - new WaitingRequestSmallGoHandler(goTables, authService), UserRole.LOGIN_ROLES); - - app.get("/app/*", - new GoHandler(goTables, authService, gtbl -> ctx -> filePath -> model -> session -> { - ctx.render(filePath, model.build()); - })); - - } - - public static record LoginJson(Login login, User user) { - - } - - public static record GameStateViewJson(GameState gameState, HandUp handUp, - int watchingStudentsNum) { - - } - - -} diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoApplication.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoApplication.java index 2e963ad..df56826 100644 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoApplication.java +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoApplication.java @@ -4,9 +4,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import org.nkjmlab.go.javalin.auth.AuthService; -import org.nkjmlab.go.javalin.auth.GoAuthService; +import org.nkjmlab.go.javalin.jsonrpc.AuthService; +import org.nkjmlab.go.javalin.jsonrpc.GoAuthService; import org.nkjmlab.go.javalin.jsonrpc.GoJsonRpcService; +import org.nkjmlab.go.javalin.model.relation.GoTables; import org.nkjmlab.go.javalin.websocket.WebsocketSessionsManager; import org.nkjmlab.sorm4j.util.h2.server.H2TcpServerProcess; import org.nkjmlab.sorm4j.util.h2.server.H2TcpServerProperties; @@ -17,7 +18,8 @@ import org.nkjmlab.util.java.lang.ProcessUtils; import org.nkjmlab.util.java.lang.ResourceUtils; import org.nkjmlab.util.java.lang.SystemPropertyUtils; -import org.nkjmlab.util.javalin.JavalinJsonRpcService; +import org.nkjmlab.util.java.web.WebApplicationConfig; +import org.nkjmlab.util.javalin.JsonRpcJavalinService; import org.nkjmlab.util.thymeleaf.ThymeleafTemplateEnginBuilder; import org.thymeleaf.TemplateEngine; import io.javalin.Javalin; @@ -31,6 +33,12 @@ public class GoApplication { private final Javalin app; + private static final WebApplicationConfig WEB_APP_CONFIG = WebApplicationConfig.builder() + .addWebJar("jquery", "sweetalert2", "bootstrap", "bootstrap-treeview", "clipboard", + "fortawesome__fontawesome-free", "stacktrace-js", "datatables", "firebase", "firebaseui", + "ua-parser-js", "blueimp-load-image", "emojionearea") + .build(); + public static void main(String[] args) { int port = 4567; @@ -59,7 +67,8 @@ public GoApplication() { DataSourceManager basicDataSource = new DataSourceManager(); - GoTables goTables = GoTables.prepareTables(basicDataSource); + GoTables goTables = GoTables.prepareTables(WEB_APP_CONFIG.getWebRootDirectory(), + WEB_APP_CONFIG.getAppRootDirectory(), basicDataSource); WebsocketSessionsManager webSocketManager = new WebsocketSessionsManager(goTables, basicDataSource.createHikariInMemoryDataSource()); @@ -74,8 +83,7 @@ public GoApplication() { this.app = Javalin.create(config -> { - config.staticFiles.add(GoWebAppConfig.WEB_APP_CONFIG.getWebRootDirectory().getName(), - Location.CLASSPATH); + config.staticFiles.add(WEB_APP_CONFIG.getWebRootDirectory().getName(), Location.CLASSPATH); config.staticFiles.enableWebjars(); config.http.generateEtags = true; config.plugins.enableCors(cors -> cors.add(corsConfig -> corsConfig.anyHost())); @@ -86,16 +94,17 @@ public GoApplication() { prepareWebSocket(app, webSocketManager); prepareJsonRpc(app, webSocketManager, new GoJsonRpcService(webSocketManager, goTables), - new AuthService.Factory(goTables.usersTable, goTables.loginsTable, goTables.passwordsTable, - authService)); + new AuthService.Factory(goTables, authService)); - GoAppHandlers.prepareGetHandler(app, webSocketManager, goTables, authService); + new GoGetHandlers(app, webSocketManager, WEB_APP_CONFIG, goTables, authService) + .prepareGetHandlers(); } private static void scheduleCheckMatchingRequest(WebsocketSessionsManager webSocketManager, GoTables goTables) { - final int INTERVAL_IN_WAITING_ROOM = 10; + // このインターバルが小さいと待合室に十分に人数が入っていない状態でマッチングが始まる可能性が高くなってしまう.30秒が妥当か. + final int MATCHING_INTERVAL_SEC = 30; ScheduledExecutorService srv = Executors.newSingleThreadScheduledExecutor(runnable -> { Thread t = Executors.defaultThreadFactory().newThread(runnable); @@ -107,7 +116,7 @@ private static void scheduleCheckMatchingRequest(WebsocketSessionsManager webSoc Set uids = goTables.matchingRequestsTable.createPairOfUsers(goTables.gameStatesTables); webSocketManager.sendUpdateWaitingRequestStatus(uids); - }, e -> log.error(e)), INTERVAL_IN_WAITING_ROOM, INTERVAL_IN_WAITING_ROOM, TimeUnit.SECONDS); + }, e -> log.error(e)), 0, MATCHING_INTERVAL_SEC, TimeUnit.SECONDS); } @@ -129,7 +138,8 @@ private static void prepareWebSocket(Javalin app, WebsocketSessionsManager webSo private static void prepareJsonRpc(Javalin app, WebsocketSessionsManager webSocketManager, GoJsonRpcService jsonRpcSrv, AuthService.Factory authServiceFactory) { - JavalinJsonRpcService srv = new JavalinJsonRpcService(GoApplication.getDefaultJacksonMapper()); + + JsonRpcJavalinService srv = new JsonRpcJavalinService(GoApplication.getDefaultJacksonMapper()); app.post("/app/json/GoJsonRpcService", ctx -> srv.handle(ctx, jsonRpcSrv)); app.post("/app/json/AuthRpcService", ctx -> srv.handle(ctx, authServiceFactory.create(ctx.req()))); diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoGetHandler.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoGetHandler.java new file mode 100644 index 0000000..3e98963 --- /dev/null +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoGetHandler.java @@ -0,0 +1,53 @@ +package org.nkjmlab.go.javalin; + +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import org.nkjmlab.go.javalin.jsonrpc.GoAuthService; +import org.nkjmlab.go.javalin.model.relation.GoTables; +import org.nkjmlab.go.javalin.model.relation.UsersTable.User; +import org.nkjmlab.util.java.net.UrlUtils; +import org.nkjmlab.util.java.web.ViewModel; +import org.nkjmlab.util.java.web.WebApplicationConfig; +import io.javalin.http.Context; +import io.javalin.http.Handler; +import jakarta.servlet.http.HttpServletRequest; + +class GoGetHandler implements Handler { + + private final GoViewHandler handler; + private final GoTables goTables; + private final GoAuthService authService; + private final WebApplicationConfig webAppConfig; + + public GoGetHandler(WebApplicationConfig webAppConfig, GoTables goTables, + GoAuthService authService, GoViewHandler handler) { + this.handler = handler; + this.goTables = goTables; + this.authService = authService; + this.webAppConfig = webAppConfig; + } + + @Override + public void handle(Context ctx) throws Exception { + String filePath = UrlUtils.of(ctx.url()).getPath().replaceFirst("^/app/", ""); + ViewModel.Builder model = createDefaultViewModelBuilder(ctx.req()); + handler.apply(ctx).apply(filePath).accept(model); + } + + private ViewModel.Builder createDefaultViewModelBuilder(HttpServletRequest request) { + User u = authService.toSigninSession(request.getSession().getId()) + .map(uid -> goTables.usersTable.selectByPrimaryKey(uid.userId())).orElse(new User()); + + Map map = + ViewModel.builder().setFileModifiedDate(webAppConfig.getWebRootDirectory(), 10, "js", "css") + .put("webjars", webAppConfig.getWebJars()).put("currentUser", u).build(); + return ViewModel.builder(map); + } + + static interface GoViewHandler + extends Function>> { + } + + +} diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoGetHandlers.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoGetHandlers.java new file mode 100644 index 0000000..c0f5c09 --- /dev/null +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoGetHandlers.java @@ -0,0 +1,196 @@ +package org.nkjmlab.go.javalin; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import org.nkjmlab.go.javalin.GoAccessManager.AccessRole; +import org.nkjmlab.go.javalin.GoGetHandler.GoViewHandler; +import org.nkjmlab.go.javalin.jsonrpc.GoAuthService; +import org.nkjmlab.go.javalin.jsonrpc.GoAuthService.SigninSession; +import org.nkjmlab.go.javalin.model.relation.GameRecordsTable.GameRecord; +import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameState; +import org.nkjmlab.go.javalin.model.relation.GoTables; +import org.nkjmlab.go.javalin.model.relation.HandUpsTable.HandUp; +import org.nkjmlab.go.javalin.model.relation.LoginsTable.Login; +import org.nkjmlab.go.javalin.model.relation.MatchingRequestsTable.MatchingRequest; +import org.nkjmlab.go.javalin.model.relation.UsersTable.User; +import org.nkjmlab.go.javalin.websocket.WebsocketSessionsManager; +import org.nkjmlab.sorm4j.common.Tuple.Tuple2; +import org.nkjmlab.util.java.web.ViewModel.Builder; +import org.nkjmlab.util.java.web.WebApplicationConfig; +import io.javalin.Javalin; +import io.javalin.http.Handler; + +public class GoGetHandlers { + + private final Javalin app; + private final WebsocketSessionsManager websocketManager; + private final WebApplicationConfig webAppConfig; + private final GoTables goTables; + private final GoAuthService authService; + + + public GoGetHandlers(Javalin app, WebsocketSessionsManager websocketManager, + WebApplicationConfig webAppConfig, GoTables goTables, GoAuthService authService) { + this.app = app; + this.websocketManager = websocketManager; + this.webAppConfig = webAppConfig; + this.goTables = goTables; + this.authService = authService; + } + + public void prepareGetHandlers() { + + app.get("/app/play.html", createPlayHandler(), AccessRole.LOGIN_ROLES); + app.get("/app/players-all.html", createPlayersAllHandler(), AccessRole.ADMIN); + app.get("/app/players.html", createPlayersHandler(), AccessRole.ADMIN); + app.get("/app/games-all.html", createGamesAllHandler(), AccessRole.ADMIN); + app.get("/app/games.html", createGamesHandler(), AccessRole.ADMIN); + app.get("/app/fragment/game-record-table.html", createGameRecordTableHandler(), + AccessRole.LOGIN_ROLES); + app.get("/app/fragment/question-table*", createQuestionTableHandler(), AccessRole.ADMIN); + + app.get("/app/fragment/waiting-request-table.html", createWaitingRequestHandler(), + AccessRole.ADMIN); + + app.get("/app/fragment/waiting-request-table-small.html", createWatingRequestSmallHandler(), + AccessRole.LOGIN_ROLES); + + app.get("/app", ctx -> ctx.redirect("/app/index.html")); + app.get("/app/*", createGoHandler(ctx -> filePath -> model -> { + ctx.render(filePath, model.build()); + })); + + } + + Handler createGoHandler(GoViewHandler handler) { + return new GoGetHandler(webAppConfig, goTables, authService, handler); + } + + + private Handler createPlayHandler() { + return createGoHandler(ctx -> filePath -> model -> { + Optional session = authService.toSigninSession(ctx.req().getSession().getId()); + session.ifPresent(opts -> { + boolean attend = goTables.loginsTable.isAttendance(opts.userId()); + model.put("isAttendance", attend); + model.put("problemGroupsJson", goTables.problemsTable.getProblemGroupsNode()); + ctx.render(filePath, model.build()); + }); + }); + } + + + private Handler createPlayersAllHandler() { + return createGoHandler(ctx -> filePath -> model -> { + putUserAccounts(model); + ctx.render("players.html", model.build()); + }); + } + + private Handler createPlayersHandler() { + return createGoHandler(ctx -> filePath -> model -> { + putUserAccounts(model); + ctx.render(filePath, model.build()); + }); + } + + private void putUserAccounts(Builder model) { + List> users = goTables.usersTable.readAllWithLastLogin(); + List loginJsons = users.stream().filter(t -> t.getT1().isStudent()) + .map(t -> new LoginJson(t.getT2(), t.getT1())).collect(Collectors.toList()); + model.put("userAccounts", loginJsons); + } + + private Handler createGamesAllHandler() { + return createGoHandler(ctx -> filePath -> model -> { + List tmp = + goTables.gameStatesTables.readTodayGameJsons().stream().map(gsj -> { + String gameId = gsj.gameId(); + GameStateViewJson json = + new GameStateViewJson(gsj, goTables.handsUpTable.selectByPrimaryKey(gameId), + websocketManager.getWatchingUniqueStudentsNum(gameId)); + return json; + }).collect(Collectors.toList()); + model.put("games", tmp); + ctx.render("games.html", model.build()); + }); + } + + private Handler createGamesHandler() { + return createGoHandler(ctx -> filePath -> model -> { + List gids = websocketManager.readActiveGameIdsOrderByGameId(); + List tmp = + gids.stream().map(gid -> goTables.gameStatesTables.readLatestGameState(gid)) + .map(gsj -> new GameStateViewJson(gsj, + goTables.handsUpTable.selectByPrimaryKey(gsj.gameId()), + websocketManager.getWatchingUniqueStudentsNum(gsj.gameId()))) + .collect(Collectors.toList()); + model.put("games", + tmp.stream().filter(j -> j.watchingStudentsNum() > 0).collect(Collectors.toList())); + ctx.render(filePath, model.build()); + }); + } + + + private Handler createGameRecordTableHandler() { + return createGoHandler(ctx -> filePath -> model -> { + String userId = ctx.queryParam("userId"); + List records = goTables.gameRecordsTable.readByUserId(userId); + model.put("records", records); + ctx.render("fragment/game-record-table.html", model.build()); + }); + } + + + private Handler createQuestionTableHandler() { + return createGoHandler(ctx -> filePath -> model -> { + List gids = goTables.handsUpTable.readAllGameIds(); + List tmp = goTables.gameStatesTables.readLatestBoardsJson(gids).stream() + .map(gsj -> new GameStateViewJson(gsj, + goTables.handsUpTable.selectByPrimaryKey(gsj.gameId()), 0)) + .toList(); + model.put("games", tmp); + ctx.render(filePath, model.build()); + }); + } + + private Handler createWaitingRequestHandler() { + return createGoHandler(ctx -> filePath -> model -> { + String userId = ctx.queryParam("userId"); + if (userId != null) { + List tmp = goTables.matchingRequestsTable.readRequests(); + model.put("requests", + tmp.stream().filter(r -> r.userId().equals(userId)).collect(Collectors.toList())); + } else { + model.put("requests", goTables.matchingRequestsTable.readRequests()); + } + ctx.render(filePath, model.build()); + }); + } + + private Handler createWatingRequestSmallHandler() { + return createGoHandler(ctx -> filePath -> model -> { + String userId = ctx.queryParam("userId"); + List tmp = goTables.matchingRequestsTable.readRequests(); + MatchingRequest req = tmp.stream().filter(r -> r.userId().equals(userId)).findAny() + .orElse(new MatchingRequest()); + model.put("req", req); + model.put("reqNum", tmp.size()); + ctx.render(filePath, model.build()); + }); + } + + + + public static record LoginJson(Login login, User user) { + + } + + public static record GameStateViewJson(GameState gameState, HandUp handUp, + int watchingStudentsNum) { + + } + + +} diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoWebAppConfig.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoWebAppConfig.java deleted file mode 100644 index e5dc517..0000000 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoWebAppConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.nkjmlab.go.javalin; - -import java.io.File; -import org.nkjmlab.util.java.web.WebApplicationConfig; - -public class GoWebAppConfig { - public static final WebApplicationConfig WEB_APP_CONFIG = WebApplicationConfig.builder() - .addWebJar("jquery", "sweetalert2", "bootstrap", "bootstrap-treeview", "clipboard", - "fortawesome__fontawesome-free", "stacktrace-js", "datatables", "firebase", - "firebaseui", "ua-parser-js", "blueimp-load-image", "emojionearea") - .build(); - public static final File PROBLEM_DIR = - new File(GoWebAppConfig.WEB_APP_CONFIG.getAppRootDirectory(), "problem"); - public static final File CURRENT_ICON_DIR = - new File(GoWebAppConfig.WEB_APP_CONFIG.getWebRootDirectory(), "img/icon"); - public static final File UPLOADED_ICON_DIR = - new File(GoWebAppConfig.WEB_APP_CONFIG.getWebRootDirectory(), "img/icon-uploaded"); -} \ No newline at end of file diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthService.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/AuthService.java similarity index 58% rename from nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthService.java rename to nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/AuthService.java index 62660fb..efd4caf 100644 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthService.java +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/AuthService.java @@ -1,12 +1,10 @@ -package org.nkjmlab.go.javalin.auth; +package org.nkjmlab.go.javalin.jsonrpc; import java.time.LocalDateTime; import java.util.Optional; -import org.nkjmlab.go.javalin.auth.GoAuthService.SigninSession; -import org.nkjmlab.go.javalin.model.relation.LoginsTable; +import org.nkjmlab.go.javalin.jsonrpc.GoAuthService.SigninSession; +import org.nkjmlab.go.javalin.model.relation.GoTables; import org.nkjmlab.go.javalin.model.relation.LoginsTable.Login; -import org.nkjmlab.go.javalin.model.relation.PasswordsTable; -import org.nkjmlab.go.javalin.model.relation.UsersTable; import org.nkjmlab.go.javalin.model.relation.UsersTable.User; import org.nkjmlab.go.javalin.model.relation.UsersTable.UserJson; import org.nkjmlab.sorm4j.result.RowMap; @@ -16,21 +14,16 @@ public class AuthService implements AuthServiceInterface { public static class Factory { - private final UsersTable usersTable; - private final LoginsTable loginsTable; - private final PasswordsTable passwordsTable; + private final GoTables goTables; private final GoAuthService firebaseService; - public Factory(UsersTable usersTable, LoginsTable loginsTable, PasswordsTable passwordsTable, - GoAuthService firebaseService) { - this.usersTable = usersTable; - this.loginsTable = loginsTable; - this.passwordsTable = passwordsTable; + public Factory(GoTables goTables, GoAuthService firebaseService) { + this.goTables = goTables; this.firebaseService = firebaseService; } public AuthService create(HttpServletRequest request) { - return new AuthService(usersTable, loginsTable, passwordsTable, firebaseService, request); + return new AuthService(goTables, firebaseService, request); } } @@ -38,17 +31,13 @@ public AuthService create(HttpServletRequest request) { private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(); - private final UsersTable usersTable; - private final LoginsTable loginsTable; - private final PasswordsTable passwordsTable; + private final GoTables goTables; private final GoAuthService authService; private final HttpServletRequest request; - private AuthService(UsersTable usersTable, LoginsTable loginsTable, PasswordsTable passwordsTable, - GoAuthService firebaseService, HttpServletRequest request) { - this.usersTable = usersTable; - this.loginsTable = loginsTable; - this.passwordsTable = passwordsTable; + private AuthService(GoTables goTables, GoAuthService firebaseService, + HttpServletRequest request) { + this.goTables = goTables; this.authService = firebaseService; this.request = request; } @@ -60,9 +49,9 @@ public boolean isSigninToFirebase() { @Override public boolean registerAttendance(String userId, String seatId) { - User u = usersTable.selectByPrimaryKey(userId); - usersTable.updateByPrimaryKey(RowMap.of("seat_id", seatId), u.userId()); - loginsTable.insert(new Login(-1, userId, seatId, u.userName(), LocalDateTime.now(), + User u = goTables.usersTable.selectByPrimaryKey(userId); + goTables.usersTable.updateByPrimaryKey(RowMap.of("seat_id", seatId), u.userId()); + goTables.loginsTable.insert(new Login(-1, userId, seatId, u.userName(), LocalDateTime.now(), HttpRequestUtils.getXForwardedFor(request).orElseGet(() -> request.getRemoteAddr()))); return true; } @@ -73,7 +62,7 @@ public UserJson signinWithFirebase(String idToken, String seatId) { Optional opt = authService.signinWithFirebase(idToken, request.getSession().getId()); return opt.map(ls -> { - User u = usersTable.selectByPrimaryKey(ls.userId()); + User u = goTables.usersTable.selectByPrimaryKey(ls.userId()); registerAttendance(ls.userId(), seatId); authService.signin(request.getSession().getId(), ls.userId()); return new UserJson(u, true); @@ -95,16 +84,16 @@ public boolean signupAsGuest(String userId, String username, String seatId) { return false; } - User u = usersTable.selectByPrimaryKey(userId); + User u = goTables.usersTable.selectByPrimaryKey(userId); if (u != null && !u.isGuest()) { log.error("Try guest signinup but userId [{}] conflict with a regular user", userId); return false; } - usersTable.merge(new User(userId, userId + "-guest@example.com", username, User.GUEST, seatId, - 30, LocalDateTime.now())); + goTables.usersTable.merge(new User(userId, userId + "-guest@example.com", username, User.GUEST, + seatId, 30, LocalDateTime.now())); registerAttendance(userId, seatId); - UsersTable.createIcon(userId); + goTables.icons.createIcon(userId); authService.signin(request.getSession().getId(), userId); return true; } @@ -116,13 +105,13 @@ public UserJson signinWithoutFirebase(String userId, String password, String sea return null; } - if (!passwordsTable.isValid(userId, password)) { + if (!goTables.passwordsTable.isValid(userId, password)) { return null; } - User u = usersTable.selectByPrimaryKey(userId); + User u = goTables.usersTable.selectByPrimaryKey(userId); registerAttendance(userId, seatId); - UsersTable.createIcon(userId); + goTables.icons.createIcon(userId); authService.signin(request.getSession().getId(), userId); return new UserJson(u, true); } diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthServiceInterface.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/AuthServiceInterface.java similarity index 88% rename from nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthServiceInterface.java rename to nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/AuthServiceInterface.java index 081298a..5669041 100644 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/AuthServiceInterface.java +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/AuthServiceInterface.java @@ -1,4 +1,4 @@ -package org.nkjmlab.go.javalin.auth; +package org.nkjmlab.go.javalin.jsonrpc; import org.nkjmlab.go.javalin.model.relation.UsersTable.UserJson; diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/GoAuthService.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoAuthService.java similarity index 95% rename from nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/GoAuthService.java rename to nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoAuthService.java index 278cc20..d0b2b68 100644 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/auth/GoAuthService.java +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoAuthService.java @@ -1,4 +1,4 @@ -package org.nkjmlab.go.javalin.auth; +package org.nkjmlab.go.javalin.jsonrpc; import java.util.Optional; import org.nkjmlab.go.javalin.model.relation.UsersTable; diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcService.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcService.java index 06d1955..3ef2aa2 100644 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcService.java +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/jsonrpc/GoJsonRpcService.java @@ -1,16 +1,18 @@ package org.nkjmlab.go.javalin.jsonrpc; +import java.awt.image.BufferedImage; import java.io.File; +import java.io.IOException; import java.time.LocalDateTime; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; import org.nkjmlab.go.javalin.GoApplication; -import org.nkjmlab.go.javalin.GoTables; -import org.nkjmlab.go.javalin.GoWebAppConfig; import org.nkjmlab.go.javalin.model.common.ProblemJson; import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameState; +import org.nkjmlab.go.javalin.model.relation.GoTables; import org.nkjmlab.go.javalin.model.relation.HandUpsTable.HandUp; import org.nkjmlab.go.javalin.model.relation.MatchingRequestsTable.MatchingRequest; import org.nkjmlab.go.javalin.model.relation.ProblemsTable.Problem; @@ -18,14 +20,14 @@ import org.nkjmlab.go.javalin.model.relation.UsersTable.UserJson; import org.nkjmlab.go.javalin.model.relation.VotesTable.Vote; import org.nkjmlab.go.javalin.model.relation.VotesTable.VoteResult; +import org.nkjmlab.go.javalin.util.CollectionUtils; import org.nkjmlab.go.javalin.util.CurrentTimeMillisIdGenerator; import org.nkjmlab.go.javalin.websocket.WebsocketSessionsManager; import org.nkjmlab.sorm4j.result.RowMap; -import org.nkjmlab.util.java.Base64Utils; import org.nkjmlab.util.java.json.JsonMapper; import org.nkjmlab.util.java.lang.ParameterizedStringFormatter; +import org.nkjmlab.util.java.util.Base64Utils; import org.nkjmlab.util.javax.imageio.ImageIoUtils; -import org.threeten.bp.Instant; public class GoJsonRpcService implements GoJsonRpcServiceInterface { private static final org.apache.logging.log4j.Logger log = @@ -88,18 +90,18 @@ public ProblemJson saveProblem(String gameId, long problemId, String groupId, St goTables.problemsTable.merge(newP); goTables.problemsTable.clearProblemsJson(); ProblemJson problemJson = ProblemJson.createFrom(newP); - saveProblemJsonToFile(problemJson); return problemJson; } - private final CurrentTimeMillisIdGenerator problemIdGenerator = new CurrentTimeMillisIdGenerator(); + private final CurrentTimeMillisIdGenerator problemIdGenerator = + new CurrentTimeMillisIdGenerator(); private Problem createNewProblem(String gameId, long problemId, String groupId, String name, String message) { Problem prevP = goTables.problemsTable.selectByPrimaryKey(problemId); GameState currentState = goTables.gameStatesTables.readLatestGameState(gameId); if (prevP != null) { - autoBackupProblemJsonToFile(ProblemJson.createFrom(prevP)); + goTables.problemsTable.autoBackupProblemJsonToFile(ProblemJson.createFrom(prevP)); } return new Problem( prevP != null ? prevP.id() : (problemId == -1 ? problemIdGenerator.getNewId() : problemId), @@ -108,34 +110,6 @@ private Problem createNewProblem(String gameId, long problemId, String groupId, mapper.toJson(currentState.handHistory()), message == null ? "" : message); } - private void autoBackupProblemJsonToFile(ProblemJson p) { - File bkupDir = getProblemAutoBackupDir(p.groupId()); - File o = new File(bkupDir, Instant.now().toEpochMilli() + "-copy-" + p.name() + ".json"); - mapper.toJsonAndWrite(p, o, true); - } - - private void saveProblemJsonToFile(ProblemJson p) { - File problemGroupDir = getProblemDir(p.groupId()); - File o = new File(problemGroupDir, p.name() + ".json"); - mapper.toJsonAndWrite(p, o, true); - log.info("Problem {} - {} is saved to {}", p.groupId(), p.name(), o); - - } - - private File getProblemDir(String groupId) { - File dir = new File(GoWebAppConfig.PROBLEM_DIR, groupId); - dir.mkdirs(); - return dir; - } - - private File getProblemAutoBackupDir(String groupId) { - File dir = - new File(new File(GoWebAppConfig.WEB_APP_CONFIG.getAppRootDirectory(), "problem-auto-bkup"), - groupId); - dir.mkdirs(); - return dir; - - } @Override public void deleteProblem(long problemId) { @@ -143,7 +117,7 @@ public void deleteProblem(long problemId) { if (p == null) { return; } - autoBackupProblemJsonToFile(ProblemJson.createFrom(p)); + goTables.problemsTable.autoBackupProblemJsonToFile(ProblemJson.createFrom(p)); goTables.problemsTable.delete(p); goTables.problemsTable.clearProblemsJson(); @@ -250,22 +224,73 @@ public void exitWaitingRoom(String userId) { @Override public File uploadImage(String userId, String base64EncodedImage) { try { - { - File outputFile = new File(GoWebAppConfig.UPLOADED_ICON_DIR, userId + ".png"); - outputFile.mkdirs(); - ImageIoUtils.write(Base64Utils.decodeToImage(base64EncodedImage, "png"), "png", outputFile); - } - File outputFile = new File(GoWebAppConfig.CURRENT_ICON_DIR, userId + ".png"); - outputFile.mkdirs(); - ImageIoUtils.write(Base64Utils.decodeToImage(base64EncodedImage, "png"), "png", outputFile); - log.debug("Icon is uploaded={}", outputFile); - return outputFile; + return goTables.icons.updateIcon(userId, base64EncodedImage); } catch (Exception e) { log.error(e, e); return null; } } + public static class Icons { + + private final File currentIconDIr; + + private final File initialIconDir; + + private final File randomIconDir; + + public final File uploadedIconDir; + + public Icons(File baseDir) { + this.currentIconDIr = new File(baseDir, "img/icon"); + this.initialIconDir = new File(baseDir, "img/icon-initial"); + this.randomIconDir = new File(baseDir, "img/icon-random"); + this.uploadedIconDir = new File(baseDir, "img/icon-uploaded"); + } + + public File updateIcon(String userId, String base64EncodedImage) { + BufferedImage img = Base64Utils.decodeToImage(base64EncodedImage, "png"); + saveImage(uploadedIconDir, userId, img); + File outputFile = saveImage(currentIconDIr, userId, img); + log.debug("Icon is uploaded={}", outputFile); + return outputFile; + } + + private File saveImage(File dir, String userId, BufferedImage img) { + File outputFile = new File(dir, userId + ".png"); + outputFile.mkdirs(); + ImageIoUtils.write(img, "png", outputFile); + return outputFile; + } + + public void createIcon(String userId) { + File currentIcon = new File(currentIconDIr, userId + ".png"); + if (currentIcon.exists()) { + return; + } + + File initialIcon = new File(initialIconDir, userId + ".png"); + + File srcFile = initialIcon.exists() ? initialIcon : getRandomIcon(); + try { + org.apache.commons.io.FileUtils.copyFile(srcFile, + new File(currentIconDIr, userId + ".png")); + } catch (IOException e) { + log.warn(e, e); + } + } + + private File getRandomIcon() { + return CollectionUtils.getRandom(Stream.of(randomIconDir.listFiles()) + .filter(f -> f.getName().toLowerCase().endsWith(".png") + || f.getName().toLowerCase().endsWith(".jpg")) + .toList()).orElseThrow(); + } + + + + } + @Override public boolean sendLog(String logLevel, String location, String msg, String options) { log.error("{},{},{},{}", logLevel, location, msg, options); diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoTables.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/GoTables.java similarity index 80% rename from nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoTables.java rename to nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/GoTables.java index 12a34ae..191752d 100644 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/GoTables.java +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/GoTables.java @@ -1,18 +1,10 @@ -package org.nkjmlab.go.javalin; +package org.nkjmlab.go.javalin.model.relation; import java.io.File; import javax.sql.DataSource; -import org.nkjmlab.go.javalin.model.relation.GameRecordsTable; -import org.nkjmlab.go.javalin.model.relation.GameStatesTable; +import org.nkjmlab.go.javalin.DataSourceManager; +import org.nkjmlab.go.javalin.jsonrpc.GoJsonRpcService.Icons; import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameState; -import org.nkjmlab.go.javalin.model.relation.GameStatesTables; -import org.nkjmlab.go.javalin.model.relation.HandUpsTable; -import org.nkjmlab.go.javalin.model.relation.LoginsTable; -import org.nkjmlab.go.javalin.model.relation.MatchingRequestsTable; -import org.nkjmlab.go.javalin.model.relation.PasswordsTable; -import org.nkjmlab.go.javalin.model.relation.ProblemsTable; -import org.nkjmlab.go.javalin.model.relation.UsersTable; -import org.nkjmlab.go.javalin.model.relation.VotesTable; import org.nkjmlab.util.java.io.SystemFileUtils; import org.nkjmlab.util.java.lang.ResourceUtils; @@ -30,8 +22,9 @@ public class GoTables { public final HandUpsTable handsUpTable; public final GameRecordsTable gameRecordsTable; public final PasswordsTable passwordsTable; + public final Icons icons; - private GoTables(GameStatesTables gameStatesTables, ProblemsTable problemsTable, + private GoTables(File webrootDir, GameStatesTables gameStatesTables, ProblemsTable problemsTable, UsersTable usersTable, PasswordsTable passwordsTable, LoginsTable loginsTable, MatchingRequestsTable matchingRequestsTable, VotesTable votesTable, HandUpsTable handsUpTable, GameRecordsTable gameRecordsTable) { @@ -44,15 +37,17 @@ private GoTables(GameStatesTables gameStatesTables, ProblemsTable problemsTable, this.votesTable = votesTable; this.handsUpTable = handsUpTable; this.gameRecordsTable = gameRecordsTable; + this.icons = new Icons(webrootDir); } - public static GoTables prepareTables(DataSourceManager basicDataSource) { + public static GoTables prepareTables(File webrootDir, File appRootDir, + DataSourceManager basicDataSource) { DataSource memDbDataSource = basicDataSource.createHikariInMemoryDataSource(); DataSource fileDbDataSource = basicDataSource.createHikariServerModeDataSource(); - final ProblemsTable problemsTable = prepareProblemTables(memDbDataSource); + final ProblemsTable problemsTable = prepareProblemTables(appRootDir, memDbDataSource); final HandUpsTable handsUpTable = new HandUpsTable(memDbDataSource); final MatchingRequestsTable matchingRequestsTable = prepareMatchingRequestsTable(memDbDataSource); @@ -67,8 +62,9 @@ public static GoTables prepareTables(DataSourceManager basicDataSource) { final LoginsTable loginsTable = prepareLoginsTable(fileDbDataSource); - GoTables goTables = new GoTables(gameStatesTables, problemsTable, usersTable, passwordsTable, - loginsTable, matchingRequestsTable, votesTable, handsUpTable, gameRecordsTable); + GoTables goTables = + new GoTables(webrootDir, gameStatesTables, problemsTable, usersTable, passwordsTable, + loginsTable, matchingRequestsTable, votesTable, handsUpTable, gameRecordsTable); return goTables; } @@ -150,9 +146,10 @@ private static UsersTable prepareUsersTable(DataSource dataSource) { return usersTable; } - private static ProblemsTable prepareProblemTables(DataSource memDbDataSource) { - ProblemsTable problemsTable = new ProblemsTable(memDbDataSource); - problemsTable.dropAndInsertInitialProblemsToTable(GoWebAppConfig.PROBLEM_DIR); + private static ProblemsTable prepareProblemTables(File appRootDir, DataSource memDbDataSource) { + final File PROBLEM_DIR = new File(appRootDir, "problem"); + ProblemsTable problemsTable = new ProblemsTable(memDbDataSource, PROBLEM_DIR); + problemsTable.dropAndInsertInitialProblemsToTable(); return problemsTable; } diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/ProblemsTable.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/ProblemsTable.java index 01dbe3a..c14b5ee 100644 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/ProblemsTable.java +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/ProblemsTable.java @@ -21,6 +21,8 @@ import org.nkjmlab.sorm4j.util.h2.BasicH2Table; import org.nkjmlab.sorm4j.util.table_def.annotation.Index; import org.nkjmlab.sorm4j.util.table_def.annotation.PrimaryKey; +import org.nkjmlab.util.java.json.JsonMapper; +import org.threeten.bp.Instant; import com.google.firebase.database.annotations.NotNull; public class ProblemsTable extends BasicH2Table { @@ -37,16 +39,57 @@ public class ProblemsTable extends BasicH2Table { public static final String HAND_HISTORY = "hand_history"; public static final String MESSAGE = "message"; + private static final JsonMapper mapper = GoApplication.getDefaultJacksonMapper(); + private static List groupNames = List.of("投票", "第1回", "第2回", "第3回", "第4回", "問題集 Part 1", "問題集 Part 2", "問題集 Part 3", "問題集 Part 4", "問題集 Part 5", "セキ", "模範碁"); - public ProblemsTable(DataSource dataSource) { + + private final File problemDir; + + public ProblemsTable(DataSource dataSource, File problemDir) { super(Sorm.create(dataSource), Problem.class); this.problemGroupNodeFactory = new ProblemGroupNodeFactory(this); createTableIfNotExists().createIndexesIfNotExists(); + this.problemDir = problemDir; + } + + + @Override + public int merge(Problem p) { + ProblemJson problemJson = ProblemJson.createFrom(p); + saveProblemJsonToFile(problemJson); + return super.merge(p); } + public void autoBackupProblemJsonToFile(ProblemJson p) { + File bkupDir = getProblemAutoBackupDir(p.groupId()); + File o = new File(bkupDir, Instant.now().toEpochMilli() + "-copy-" + p.name() + ".json"); + mapper.toJsonAndWrite(p, o, true); + } + + private void saveProblemJsonToFile(ProblemJson p) { + File problemGroupDir = getProblemDir(p.groupId()); + File o = new File(problemGroupDir, p.name() + ".json"); + mapper.toJsonAndWrite(p, o, true); + log.info("Problem {} - {} is saved to {}", p.groupId(), p.name(), o); + + } + + private File getProblemDir(String groupId) { + File dir = new File(problemDir, groupId); + dir.mkdirs(); + return dir; + } + + private File getProblemAutoBackupDir(String groupId) { + File dir = + new File(new File(problemDir.getAbsolutePath() + File.separator + "auto-bkup"), groupId); + dir.mkdirs(); + return dir; + + } private List getGroupsOrderByAsc() { return getOrm().readList(String.class, @@ -59,7 +102,7 @@ private List readProblemsByGroupId(String groupId) { } - public void dropAndInsertInitialProblemsToTable(File problemDir) { + public void dropAndInsertInitialProblemsToTable() { log.info("{} is problem dir", problemDir); deleteAll(); List probs = readProblemJsons(problemDir.toPath()); diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/UsersTable.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/UsersTable.java index 237c51e..8f696e8 100644 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/UsersTable.java +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/model/relation/UsersTable.java @@ -2,17 +2,12 @@ import static org.nkjmlab.sorm4j.util.sql.SelectSql.selectStarFrom; import java.io.File; -import java.io.IOException; import java.time.LocalDateTime; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Optional; -import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.sql.DataSource; -import org.nkjmlab.go.javalin.GoWebAppConfig; import org.nkjmlab.go.javalin.model.relation.LoginsTable.Login; import org.nkjmlab.go.javalin.model.relation.UsersTable.User; import org.nkjmlab.sorm4j.Sorm; @@ -34,7 +29,7 @@ * */ public class UsersTable extends BasicH2Table { - private static final org.apache.logging.log4j.Logger log = + static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(); private static final String EMAIL = "email"; @@ -74,12 +69,11 @@ public User readByEmail(String email) { public void readFromFileAndMerge(File usersCsvFile) { BasicH2Table table = new BasicH2Table<>(getOrm(), UserCsv.class); - transformToUser(table.readCsvWithHeader(usersCsvFile)).forEach(user -> { - createIcon(user.userId()); - insert(user); - }); + transformToUser(table.readCsvWithHeader(usersCsvFile)).forEach(user -> insert(user)); } + + @OrmRecord public static record UserCsv(String userId, String email, String username, String role) { @@ -119,36 +113,6 @@ public boolean isAdmin(String userId) { - public static void createIcon(String userId) { - File uploadedIcon = new File(GoWebAppConfig.UPLOADED_ICON_DIR, userId + ".png"); - File initialIcon = - new File(new File(GoWebAppConfig.WEB_APP_CONFIG.getWebRootDirectory(), "img/icon-initial"), - userId + ".png"); - - File srcFile = uploadedIcon.exists() ? uploadedIcon - : (initialIcon.exists() ? initialIcon - : getRandom(Stream - .of(new File(GoWebAppConfig.WEB_APP_CONFIG.getWebRootDirectory(), - "img/icon-random").listFiles()) - .filter(f -> f.getName().toLowerCase().endsWith(".png") - || f.getName().toLowerCase().endsWith(".jpg")) - .toList()).orElseThrow()); - try { - org.apache.commons.io.FileUtils.copyFile(srcFile, - new File(GoWebAppConfig.CURRENT_ICON_DIR, userId + ".png")); - } catch (IOException e) { - log.warn(e, e); - } - } - - - private static Optional getRandom(Collection e) { - if (e.size() == 0) { - return Optional.empty(); - } - return e.stream().skip((ThreadLocalRandom.current().nextInt(e.size()))).findFirst(); - } - public List readAll() { return selectAll(); } diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/util/CollectionUtils.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/util/CollectionUtils.java new file mode 100644 index 0000000..b34ad35 --- /dev/null +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/util/CollectionUtils.java @@ -0,0 +1,17 @@ +package org.nkjmlab.go.javalin.util; + +import java.util.Collection; +import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; + +public class CollectionUtils { + + private CollectionUtils() {} + + public static Optional getRandom(Collection e) { + if (e == null || e.size() == 0) { + return Optional.empty(); + } + return e.stream().skip((ThreadLocalRandom.current().nextInt(e.size()))).findFirst(); + } +} diff --git a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/websocket/WebsocketSessionsManager.java b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/websocket/WebsocketSessionsManager.java index 387aefa..5b040c1 100644 --- a/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/websocket/WebsocketSessionsManager.java +++ b/nkjmlab-go-webapp/src/main/java/org/nkjmlab/go/javalin/websocket/WebsocketSessionsManager.java @@ -23,13 +23,13 @@ import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.WriteCallback; import org.nkjmlab.go.javalin.GoApplication; -import org.nkjmlab.go.javalin.GoTables; import org.nkjmlab.go.javalin.model.common.Agehama; import org.nkjmlab.go.javalin.model.common.Hand; import org.nkjmlab.go.javalin.model.common.Hand.HandType; import org.nkjmlab.go.javalin.model.common.ProblemJson; import org.nkjmlab.go.javalin.model.relation.GameStatesTable.GameState; import org.nkjmlab.go.javalin.model.relation.ProblemsTable.Problem; +import org.nkjmlab.go.javalin.model.relation.GoTables; import org.nkjmlab.go.javalin.model.relation.UsersTable; import org.nkjmlab.go.javalin.model.relation.UsersTable.User; import org.nkjmlab.go.javalin.model.relation.UsersTable.UserJson; @@ -60,7 +60,7 @@ public class WebsocketSessionsManager { private final Queue globalMessages = new ConcurrentLinkedQueue<>(); private final WebSocketJsonSenderService jsonSenderService = new WebSocketJsonSenderService(); - private GoTables goTables; + private final GoTables goTables; public WebsocketSessionsManager(GoTables goTables, DataSource memDbDataSource) {