diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..24ea0ee --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +FROM adoptopenjdk/openjdk16:alpine AS base-builder +ARG SBT_VERSION=1.3.13 +RUN apk add --no-cache bash +ENV JAVA_HOME="/usr/lib/jvm/default-jvm/" +ENV PATH=$PATH:${JAVA_HOME}/bin +RUN \ + wget -O sbt-$SBT_VERSION.tgz https://github.com/sbt/sbt/releases/download/v$SBT_VERSION/sbt-$SBT_VERSION.tgz && \ + tar -xzvf sbt-$SBT_VERSION.tgz && \ + rm sbt-$SBT_VERSION.tgz + +ENV PATH=$PATH:/sbt/bin/ + + +FROM base-builder AS sbt-builder +WORKDIR /build +COPY lib/ lib/ +COPY project/plugins.sbt project/ +COPY build.sbt . +RUN sbt assembly + +FROM sbt-builder as builder +COPY src/ src/ +RUN sbt assembly + +FROM adoptopenjdk/openjdk16:alpine-jre AS base-core +ENV JAVA_HOME="/usr/lib/jvm/default-jvm/" +RUN apk add --update ttf-dejavu +ENV PATH=$PATH:${JAVA_HOME}/bin + + +FROM base-core +WORKDIR /lc-core +COPY --from=builder /build/target/scala-2.13/LibreCaptcha.jar . +RUN mkdir data/ + +EXPOSE 8888 + +CMD [ "java", "-jar", "LibreCaptcha.jar" ] diff --git a/build.sbt b/build.sbt index 4691280..797904c 100644 --- a/build.sbt +++ b/build.sbt @@ -22,5 +22,8 @@ scalacOptions ++= List( ) javacOptions += "-g:none" compileOrder := CompileOrder.JavaThenScala +mainClass in assembly := Some("lc.LCFramework") +mainClass in (Compile, run) := Some("lc.LCFramework") +assemblyJarName in assembly := "LibreCaptcha.jar" fork in run := true diff --git a/config.json b/config.json deleted file mode 100644 index 727b973..0000000 --- a/config.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "randomSeed": 20, - "port": 8888, - "captchaExpiryTimeLimit": 5, - "threadDelay": 2, - "throttle": 10, - "captchas":[ - { - "name": "FilterChallenge", - "allowedLevels":["medium", "hard"], - "allowedMedia":["image/png"], - "allowedInputType":["text"], - "config":{} - }, - { - "name": "GifCaptcha", - "allowedLevels":["hard"], - "allowedMedia":["image/gif"], - "allowedInputType":["text"], - "config":{} - }, - { - "name": "ShadowTextCaptcha", - "allowedLevels":["easy"], - "allowedMedia":["image/png"], - "allowedInputType":["text"], - "config": {} - }, - { - "name": "RainDropsCaptcha", - "allowedLevels":["easy","medium"], - "allowedMedia":["image/gif"], - "allowedInputType":["text"], - "config":{} - } - ] -} \ No newline at end of file diff --git a/data/config.json b/data/config.json new file mode 100644 index 0000000..41d3c90 --- /dev/null +++ b/data/config.json @@ -0,0 +1,32 @@ +{ + "randomSeed" : 20, + "port" : 8888, + "captchaExpiryTimeLimit" : 5, + "throttle" : 10, + "threadDelay" : 2, + "captchas" : [ { + "name" : "FilterChallenge", + "allowedLevels" : [ "medium", "hard" ], + "allowedMedia" : [ "image/png" ], + "allowedInputType" : [ "text" ], + "config" : { } + }, { + "name" : "GifCaptcha", + "allowedLevels" : [ "hard" ], + "allowedMedia" : [ "image/gif" ], + "allowedInputType" : [ "text" ], + "config" : { } + }, { + "name" : "ShadowTextCaptcha", + "allowedLevels" : [ "easy" ], + "allowedMedia" : [ "image/png" ], + "allowedInputType" : [ "text" ], + "config" : { } + }, { + "name" : "RainDropsCaptcha", + "allowedLevels" : [ "easy", "medium" ], + "allowedMedia" : [ "image/gif" ], + "allowedInputType" : [ "text" ], + "config" : { } + } ] +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..315720b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: "3.8" + +services: + lc-core: + container_name: "libre-captcha" + build: ./ + image: librecapthca/lc-core:latest + volumes: + - "./docker-data:/lc-core/data" + ports: + - "8888:8888" diff --git a/project/plugins.sbt b/project/plugins.sbt index a5d94ca..9f4cd3e 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,4 @@ addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.25") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.0") addSbtPlugin("com.lightbend.sbt" % "sbt-java-formatter" % "0.6.0") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0") diff --git a/src/main/scala/lc/core/captchaFields.scala b/src/main/scala/lc/core/captchaFields.scala index 5418ee9..82eb89f 100644 --- a/src/main/scala/lc/core/captchaFields.scala +++ b/src/main/scala/lc/core/captchaFields.scala @@ -10,6 +10,19 @@ object ParametersEnum extends Enumeration { val ALLOWEDLEVELS: Value = Value("allowedLevels") val ALLOWEDMEDIA: Value = Value("allowedMedia") val ALLOWEDINPUTTYPE: Value = Value("allowedInputType") + +} + +object AttributesEnum extends Enumeration { + type Attribute = Value + + val NAME: Value = Value("name") + val RANDOM_SEED: Value = Value("randomSeed") + val PORT: Value = Value("port") + val CAPTCHA_EXPIRY_TIME_LIMIT: Value = Value("captchaExpiryTimeLimit") + val THROTTLE: Value = Value("throttle") + val THREAD_DELAY: Value = Value("threadDelay") + val CONFIG: Value = Value("config") } object ResultEnum extends Enumeration { diff --git a/src/main/scala/lc/core/config.scala b/src/main/scala/lc/core/config.scala index 2bf02be..0538e25 100644 --- a/src/main/scala/lc/core/config.scala +++ b/src/main/scala/lc/core/config.scala @@ -2,23 +2,37 @@ package lc.core import scala.io.Source.fromFile import org.json4s.{DefaultFormats, JValue, JObject, JField, JString} -import org.json4s.jackson.JsonMethods.parse +import org.json4s.jackson.JsonMethods.{parse, render, pretty} +import org.json4s.JsonDSL._ +import java.io.{FileNotFoundException, File, PrintWriter} object Config { implicit val formats: DefaultFormats.type = DefaultFormats - private val configFile = fromFile("config.json") + private val configFilePath = "data/config.json" private val configString = - try configFile.mkString - finally configFile.close + try { + val configFile = fromFile(configFilePath) + val configFileContent = configFile.mkString + configFile.close + configFileContent + } catch { + case _: FileNotFoundException => + val configFileContent = getDefaultConfig() + val configFile = new PrintWriter(new File(configFilePath)) + configFile.write(configFileContent) + configFile.close + configFileContent + } + private val configJson = parse(configString) - val port: Int = (configJson \ "port").extract[Int] - val throttle: Int = (configJson \ "throttle").extract[Int] - val seed: Int = (configJson \ "randomSeed").extract[Int] - val captchaExpiryTimeLimit: Int = (configJson \ "captchaExpiryTimeLimit").extract[Int] - val threadDelay: Int = (configJson \ "threadDelay").extract[Int] + val port: Int = (configJson \ AttributesEnum.PORT.toString).extract[Int] + val throttle: Int = (configJson \ AttributesEnum.THROTTLE.toString).extract[Int] + val seed: Int = (configJson \ AttributesEnum.RANDOM_SEED.toString).extract[Int] + val captchaExpiryTimeLimit: Int = (configJson \ AttributesEnum.CAPTCHA_EXPIRY_TIME_LIMIT.toString).extract[Int] + val threadDelay: Int = (configJson \ AttributesEnum.THREAD_DELAY.toString).extract[Int] private val captchaConfigJson = (configJson \ "captchas") val captchaConfigTransform: JValue = captchaConfigJson transformField { @@ -45,4 +59,45 @@ object Config { valueSet } + private def getDefaultConfig(): String = { + val defaultConfigMap = + (AttributesEnum.RANDOM_SEED.toString -> 20) ~ + (AttributesEnum.PORT.toString -> 8888) ~ + (AttributesEnum.CAPTCHA_EXPIRY_TIME_LIMIT.toString -> 5) ~ + (AttributesEnum.THROTTLE.toString -> 10) ~ + (AttributesEnum.THREAD_DELAY.toString -> 2) ~ + ("captchas" -> List( + ( + (AttributesEnum.NAME.toString -> "FilterChallenge") ~ + (ParametersEnum.ALLOWEDLEVELS.toString -> List("medium", "hard")) ~ + (ParametersEnum.ALLOWEDMEDIA.toString -> List("image/png")) ~ + (ParametersEnum.ALLOWEDINPUTTYPE.toString -> List("text")) ~ + (AttributesEnum.CONFIG.toString -> JObject()) + ), + ( + (AttributesEnum.NAME.toString -> "GifCaptcha") ~ + (ParametersEnum.ALLOWEDLEVELS.toString -> List("hard")) ~ + (ParametersEnum.ALLOWEDMEDIA.toString -> List("image/gif")) ~ + (ParametersEnum.ALLOWEDINPUTTYPE.toString -> List("text")) ~ + (AttributesEnum.CONFIG.toString -> JObject()) + ), + ( + (AttributesEnum.NAME.toString -> "ShadowTextCaptcha") ~ + (ParametersEnum.ALLOWEDLEVELS.toString -> List("easy")) ~ + (ParametersEnum.ALLOWEDMEDIA.toString -> List("image/png")) ~ + (ParametersEnum.ALLOWEDINPUTTYPE.toString -> List("text")) ~ + (AttributesEnum.CONFIG.toString -> JObject()) + ), + ( + (AttributesEnum.NAME.toString -> "RainDropsCaptcha") ~ + (ParametersEnum.ALLOWEDLEVELS.toString -> List("easy", "medium")) ~ + (ParametersEnum.ALLOWEDMEDIA.toString -> List("image/gif")) ~ + (ParametersEnum.ALLOWEDINPUTTYPE.toString -> List("text")) ~ + (AttributesEnum.CONFIG.toString -> JObject()) + ) + )) + + pretty(render(defaultConfigMap)) + } + }