diff --git a/modules/backend/src/main/scala/cz/kamenitxan/jakon/JakonInit.scala b/modules/backend/src/main/scala/cz/kamenitxan/jakon/JakonInit.scala index 3b9c6839..50186e2a 100644 --- a/modules/backend/src/main/scala/cz/kamenitxan/jakon/JakonInit.scala +++ b/modules/backend/src/main/scala/cz/kamenitxan/jakon/JakonInit.scala @@ -83,41 +83,47 @@ class JakonInit { adminControllers() taskSetup() - websocketSetup() - before(new Filter { - override def handle(req: Request, res: Response): Unit = PageContext.init(req, res) - }) - afterAfter((_: Request, _: Response) => PageContext.destroy()) - if (Settings.getDeployMode != DeployMode.PRODUCTION) { - before((request: Request, _: Response) => { - DevRender.rerender(request.pathInfo()) + if (Settings.isInitRoutes) { + websocketSetup() + before(new Filter { + override def handle(req: Request, res: Response): Unit = PageContext.init(req, res) }) - exception(classOf[Exception], new LoggingExceptionHandler) + afterAfter((_: Request, _: Response) => PageContext.destroy()) + if (Settings.getDeployMode != DeployMode.PRODUCTION) { + before((request: Request, _: Response) => { + DevRender.rerender(request.pathInfo()) + }) + exception(classOf[Exception], new LoggingExceptionHandler) - get("/upload/*", (req: Request, res: Response) => new UploadFilesController().doGet(req, res)) + get("/upload/*", (req: Request, res: Response) => new UploadFilesController().doGet(req, res)) - notFound((req: Request, res: Response) => new StaticFilesController().doGet(req, res)) - } - routesSetup() - annotationScanner.load() - if (Settings.getDeployMode != DeployMode.DEVEL) { - PageletInitializer.protectedPrefixes.filter(_ != Routes.AdminPrefix).foreach(pp => { - before(pp + "*", new Filter { - Logger.debug(s"Adding protected prefix '$pp*'") - override def handle(req: Request, res: Response): Unit = { - val user: JakonUser = req.session.attribute("user") - if (user == null || (!user.acl.adminAllowed && !user.acl.allowedFrontendPrefixes.contains(pp))) { - Logger.debug(s"User $user denied access to '$pp*'") - if (req.pathInfo().startsWith(Routes.AdminPrefix)) { - res.redirect(Routes.AdminPrefix + s"?redirectTo=${req.pathInfo()}", 302) - } else { - res.redirect(Settings.getLoginPath + s"?redirectTo=${req.pathInfo()}", 302) + notFound((req: Request, res: Response) => new StaticFilesController().doGet(req, res)) + } + routesSetup() + if (Settings.getDeployMode != DeployMode.DEVEL) { + PageletInitializer.protectedPrefixes.filter(_ != Routes.AdminPrefix).foreach(pp => { + before(pp + "*", new Filter { + Logger.debug(s"Adding protected prefix '$pp*'") + + override def handle(req: Request, res: Response): Unit = { + val user: JakonUser = req.session.attribute("user") + if (user == null || (!user.acl.adminAllowed && !user.acl.allowedFrontendPrefixes.contains(pp))) { + Logger.debug(s"User $user denied access to '$pp*'") + if (req.pathInfo().startsWith(Routes.AdminPrefix)) { + res.redirect(Routes.AdminPrefix + s"?redirectTo=${req.pathInfo()}", 302) + } else { + res.redirect(Settings.getLoginPath + s"?redirectTo=${req.pathInfo()}", 302) + } } } - } - } ) - }) + }) + }) + } } + + + annotationScanner.load() + Director.start() afterInit() } diff --git a/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/Director.scala b/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/Director.scala index 10d33596..d0c068df 100644 --- a/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/Director.scala +++ b/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/Director.scala @@ -5,13 +5,14 @@ import cz.kamenitxan.jakon.core.configuration.Settings import cz.kamenitxan.jakon.core.controller.IController import cz.kamenitxan.jakon.core.custom_pages.AbstractCustomPage import cz.kamenitxan.jakon.core.database.DBInitializer -import cz.kamenitxan.jakon.core.task.TaskRunner +import cz.kamenitxan.jakon.core.task.{RenderTask, TaskRunner} import cz.kamenitxan.jakon.core.template.Pebble import cz.kamenitxan.jakon.core.template.utils.TemplateUtils -import cz.kamenitxan.jakon.logging.Logger +import cz.kamenitxan.jakon.logging.{LogService, Logger} import cz.kamenitxan.jakon.webui.Routes import java.nio.charset.Charset +import java.util.concurrent.TimeUnit import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -42,7 +43,22 @@ object Director { DBInitializer.checkDbConsistency() } - TaskRunner.startTaskRunner() + if (Settings.isOnlyRender) { + TaskRunner.runSingle(new RenderTask(5, TimeUnit.MINUTES)) + if (TaskRunner.shutdown()) { + if (LogService.criticalCount > 0) { + System.exit(2) + } else if (LogService.errorCount > 0) { + System.exit(3) + } else { + System.exit(0) + } + } else { + System.exit(1) + } + } else { + TaskRunner.startTaskRunner() + } Routes.init() Logger.info("Jakon started") @@ -64,8 +80,6 @@ object Director { if (Settings.getStaticDir != null && Settings.getOutputDir != null) { TemplateUtils.copy(Settings.getStaticDir, Settings.getOutputDir) } - //TODO: moznost vypnout administraci - //TemplateUtils.copy("templates/admin/static", Settings.getOutputDir) Logger.info("Render complete") } diff --git a/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/configuration/AnnotationScanner.scala b/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/configuration/AnnotationScanner.scala index 56db48c0..18e01afe 100644 --- a/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/configuration/AnnotationScanner.scala +++ b/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/configuration/AnnotationScanner.scala @@ -32,9 +32,11 @@ class AnnotationScanner { def load(): Unit = { try { - loadControllers(scanResult) + if (Settings.isInitRoutes) { + loadControllers(scanResult) + loadObjectExtensions(scanResult) + } loadCustomPages(scanResult) - loadObjectExtensions(scanResult) copyResources() } finally { scanResult.close() diff --git a/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/configuration/Settings.scala b/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/configuration/Settings.scala index 4d43dc70..18e3f42d 100644 --- a/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/configuration/Settings.scala +++ b/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/configuration/Settings.scala @@ -122,6 +122,12 @@ object Settings { @ConfigurationValue(name = "HCAPTCHA.siteKey", required = false) private var hCaptchaSiteKey: String = _ + @ConfigurationValue(name = "initRoutes", required = true, defaultValue = "true") + private var initRoutes: Boolean = _ + + @ConfigurationValue(name = "onlyRender", required = true, defaultValue = "false") + private var onlyRender: Boolean = _ + def getTemplateDir: String = templateDir def setTemplateDir(templateDir: String): Unit = this.templateDir = templateDir @@ -234,4 +240,11 @@ object Settings { def getHCaptchaSecret: String = hCaptchaSecret def getHCaptchaSiteKey: String = hCaptchaSiteKey + + /** + * @return true if routes should be initialized + */ + def isInitRoutes: Boolean = initRoutes + + def isOnlyRender: Boolean = onlyRender } \ No newline at end of file diff --git a/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/dynamic/arguments/CirceJsonParser.scala b/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/dynamic/arguments/CirceJsonParser.scala index cb33f1cb..edb36f54 100644 --- a/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/dynamic/arguments/CirceJsonParser.scala +++ b/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/dynamic/arguments/CirceJsonParser.scala @@ -24,7 +24,15 @@ object CirceJsonParser extends ArgumentParser { def mapToString(hc: HCursor, f: Field): (Field, ParsedValue) = { val name = f.getName val value = f.getType match { - case STRING | ZONED_DATETIME | DATE=> ParsedValue(hc.downField(name).focus.flatMap(_.asString).getOrElse(""), null, null, null) + case STRING | ZONED_DATETIME | DATE=> ParsedValue(hc.downField(name).focus.flatMap(jsonObject => { + if (jsonObject.isBoolean) { + jsonObject.asBoolean.map(_.toString) + } else if (jsonObject.isNumber) { + jsonObject.asNumber.map(_.toString) + } else { + jsonObject.asString + } + }).getOrElse(""), null, null, null) case INTEGER | DOUBLE | FLOAT | DOUBLE_j | INTEGER_j => ParsedValue(hc.downField(name).focus.map(v => { v.toString.replace("\"", "").replace("\'", "") }).getOrElse(""), null, null, null) diff --git a/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/task/TaskRunner.scala b/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/task/TaskRunner.scala index a3f966dc..a9e7671c 100644 --- a/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/task/TaskRunner.scala +++ b/modules/backend/src/main/scala/cz/kamenitxan/jakon/core/task/TaskRunner.scala @@ -50,4 +50,9 @@ object TaskRunner { taskFutures -= task.name.value }) } + + def shutdown(): Boolean = { + scheduledExecutor.shutdown() + scheduledExecutor.awaitTermination(30, TimeUnit.SECONDS) + } } diff --git a/modules/backend/src/main/scala/cz/kamenitxan/jakon/logging/InMemoryLogRepository.scala b/modules/backend/src/main/scala/cz/kamenitxan/jakon/logging/InMemoryLogRepository.scala index 767092c0..f3b3fe69 100644 --- a/modules/backend/src/main/scala/cz/kamenitxan/jakon/logging/InMemoryLogRepository.scala +++ b/modules/backend/src/main/scala/cz/kamenitxan/jakon/logging/InMemoryLogRepository.scala @@ -17,7 +17,11 @@ class InMemoryLogRepository extends LogRepository { private val locked = new AtomicBoolean() scheduledExecutor.scheduleAtFixedRate(() => { - if (locked.compareAndSet(false, true)) { + moveLogs() + }, 0, 10, TimeUnit.SECONDS) + + private def moveLogs(): Unit = { + if (!locked.get() && locked.compareAndSet(false, true)) { try { val size = newLogs.size logs.appendAll(newLogs.take(size)) @@ -26,7 +30,7 @@ class InMemoryLogRepository extends LogRepository { locked.set(false) } } - }, 0, 10, TimeUnit.SECONDS) + } def addLog(log: Log): Unit = { if (LoggingSetting.getMaxLimit != 0 && logs.size >= LoggingSetting.getMaxLimit) { @@ -66,5 +70,8 @@ class InMemoryLogRepository extends LogRepository { } - override def getLogs: Seq[Log] = logs.toSeq + override def getLogs: Seq[Log] = { + moveLogs() + logs.toSeq + } } diff --git a/modules/backend/src/main/scala/cz/kamenitxan/jakon/logging/LogService.scala b/modules/backend/src/main/scala/cz/kamenitxan/jakon/logging/LogService.scala index 22fdb3fb..8be8ef05 100644 --- a/modules/backend/src/main/scala/cz/kamenitxan/jakon/logging/LogService.scala +++ b/modules/backend/src/main/scala/cz/kamenitxan/jakon/logging/LogService.scala @@ -6,8 +6,16 @@ object LogService { LoggingSetting.getLogRepository } - def getLogs:Seq[Log] = { + def getLogs: Seq[Log] = { LoggingSetting.getLogRepository.getLogs } + def criticalCount: Int = { + getLogs.count(_.severity == Critical) + } + + def errorCount: Int = { + getLogs.count(_.severity == Error) + } + } diff --git a/modules/backend/src/main/scala/cz/kamenitxan/jakon/webui/Routes.scala b/modules/backend/src/main/scala/cz/kamenitxan/jakon/webui/Routes.scala index 3fcfe725..c2e0411b 100644 --- a/modules/backend/src/main/scala/cz/kamenitxan/jakon/webui/Routes.scala +++ b/modules/backend/src/main/scala/cz/kamenitxan/jakon/webui/Routes.scala @@ -23,6 +23,12 @@ object Routes { val AdminPrefix = "/admin" def init(): Unit = { + if (Settings.isInitRoutes) { + initRoutes() + } + } + + private def initRoutes(): Unit = { val te = Settings.getAdminEngine val gson = new Gson val gsonTransformer: ResponseTransformer = (model: Any) => gson.toJson(model) diff --git a/modules/backend/src/test/scala/core/pagelet/JsonParserTest.scala b/modules/backend/src/test/scala/core/pagelet/JsonParserTest.scala index 6c68cd3b..efb87f37 100644 --- a/modules/backend/src/test/scala/core/pagelet/JsonParserTest.scala +++ b/modules/backend/src/test/scala/core/pagelet/JsonParserTest.scala @@ -40,6 +40,18 @@ class JsonParserTest extends AnyFunSuite { assert(data.name == "test") } + test("number to string") { + val data = parse("""{"name": 42}""", classOf[StringData]).asInstanceOf[StringData] + assert(data.name == "42") + } + + test("boolean to string") { + val data = parse("""{"name": true}""", classOf[StringData]).asInstanceOf[StringData] + assert(data.name == "true") + val data2 = parse("""{"name": false}""", classOf[StringData]).asInstanceOf[StringData] + assert(data2.name == "false") + } + test("int") { val data = parse("""{"v": 42}""", classOf[IntData]).asInstanceOf[IntData] assert(data.v == 42)