diff --git a/build.mill b/build.mill
index 7da1905..6ab22a6 100644
--- a/build.mill
+++ b/build.mill
@@ -27,7 +27,10 @@ object summercms extends ScalaModule {
// Flyway for migrations
mvn"org.flywaydb:flyway-core:10.21.0",
- mvn"org.flywaydb:flyway-database-postgresql:10.21.0"
+ mvn"org.flywaydb:flyway-database-postgresql:10.21.0",
+
+ // Logging
+ mvn"org.slf4j:slf4j-simple:2.0.9"
)
def scalacOptions = Seq(
diff --git a/favico.jpg b/summercms/resources/public/assets/images/favico.jpg
similarity index 100%
rename from favico.jpg
rename to summercms/resources/public/assets/images/favico.jpg
diff --git a/summercms/resources/public/assets/images/logo.png b/summercms/resources/public/assets/images/logo.png
new file mode 100644
index 0000000..0e40e16
Binary files /dev/null and b/summercms/resources/public/assets/images/logo.png differ
diff --git a/summercms.jpg b/summercms/resources/public/assets/images/summercms.jpg
similarity index 100%
rename from summercms.jpg
rename to summercms/resources/public/assets/images/summercms.jpg
diff --git a/summercms/resources/public/landing.html b/summercms/resources/public/landing.html
new file mode 100644
index 0000000..50b2453
--- /dev/null
+++ b/summercms/resources/public/landing.html
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+ SummerCMS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
SUMMERCMS
+
A New Dawn in Content Management
+
+
+ Server Running
+
+
+
+ SummerCMS v0.1.0 | Scala + ZIO
+
+
diff --git a/summercms/src/Main.scala b/summercms/src/Main.scala
index 9b7afa1..280fd47 100644
--- a/summercms/src/Main.scala
+++ b/summercms/src/Main.scala
@@ -10,15 +10,18 @@ object Main extends ZIOAppDefault {
private val banner: String =
"""
- | .
- | \ | /
- | '-.ooooo.-'
- | --- ooooo ---
- | .-'ooooo'-.
- | / | \
- | '
|
- | S U M M E R C M S
+ | | .
+ | `. * | .'
+ | `. ._|_* .' .
+ | . * .' `. *
+ | -------| |-------
+ | . *`.___.' * .
+ | .' |* `. *
+ | .' * | . `.
+ | . |
+ |
+ | S U M M E R C M S
|""".stripMargin
override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] =
@@ -28,7 +31,7 @@ object Main extends ZIOAppDefault {
for {
cfg <- ZIO.config[SummerConfig](SummerConfig.config)
_ <- Console.printLine(banner)
- _ <- Console.printLine(s" Starting on port ${cfg.server.port}...")
+ _ <- Console.printLine(s"Starting on port ${cfg.server.port}...")
_ <- Console.printLine("")
// Note: Migrations are NOT auto-run. Use CLI to run migrations (Phase 5).
_ <- Server.serve(Routes.routes).provide(
diff --git a/summercms/src/api/LandingRoutes.scala b/summercms/src/api/LandingRoutes.scala
new file mode 100644
index 0000000..1d4cbe0
--- /dev/null
+++ b/summercms/src/api/LandingRoutes.scala
@@ -0,0 +1,62 @@
+package api
+
+import zio.*
+import zio.http.*
+import scala.io.Source
+
+/** Landing page and static asset routes
+ */
+object LandingRoutes {
+
+ private def loadResource(path: String): Option[String] =
+ Option(getClass.getClassLoader.getResourceAsStream(path)).map { stream =>
+ val content = Source.fromInputStream(stream, "UTF-8").mkString
+ stream.close()
+ content
+ }
+
+ private def serveLanding: ZIO[Any, Nothing, Response] =
+ ZIO.succeed {
+ loadResource("public/landing.html") match {
+ case Some(html) =>
+ Response(
+ status = Status.Ok,
+ headers = Headers(Header.ContentType(MediaType.text.html)),
+ body = Body.fromString(html)
+ )
+ case None =>
+ Response.text("Landing page not found").status(Status.InternalServerError)
+ }
+ }
+
+ private def serveImage(filename: String): ZIO[Any, Nothing, Response] =
+ ZIO.attemptBlocking {
+ val path = s"public/assets/images/$filename"
+ val stream = getClass.getClassLoader.getResourceAsStream(path)
+ if (stream == null) {
+ Response.notFound
+ } else {
+ val bytes = stream.readAllBytes()
+ stream.close()
+ val mediaType = if (filename.endsWith(".png")) MediaType.image.png
+ else if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) MediaType.image.jpeg
+ else if (filename.endsWith(".svg")) MediaType.image.`svg+xml`
+ else if (filename.endsWith(".gif")) MediaType.image.gif
+ else MediaType.application.`octet-stream`
+ Response(
+ status = Status.Ok,
+ headers = Headers(Header.ContentType(mediaType)),
+ body = Body.fromChunk(Chunk.fromArray(bytes))
+ )
+ }
+ }.catchAll(_ => ZIO.succeed(Response.notFound))
+
+ val routes: Routes[Any, Response] =
+ Routes(
+ Method.GET / Root -> handler(serveLanding),
+ Method.GET / "assets" / "images" / string("filename") -> handler { (filename: String, _: Request) =>
+ serveImage(filename)
+ }
+ )
+
+}
diff --git a/summercms/src/api/Routes.scala b/summercms/src/api/Routes.scala
index bb15855..28fc81a 100644
--- a/summercms/src/api/Routes.scala
+++ b/summercms/src/api/Routes.scala
@@ -10,6 +10,6 @@ import javax.sql.DataSource
object Routes {
val routes: Routes[DataSource, Response] =
- HealthRoutes.routes
+ LandingRoutes.routes ++ HealthRoutes.routes
}