diff --git a/src/main/kotlin/fr/shikkanime/Application.kt b/src/main/kotlin/fr/shikkanime/Application.kt index 04b102de..27443615 100644 --- a/src/main/kotlin/fr/shikkanime/Application.kt +++ b/src/main/kotlin/fr/shikkanime/Application.kt @@ -14,10 +14,10 @@ import io.ktor.server.application.* import io.ktor.server.cio.* import io.ktor.server.engine.* -private val logger = LoggerFactory.getLogger("Shikkanime") +private val logger = LoggerFactory.getLogger(Constant.NAME) fun main() { - logger.info("Starting ShikkAnime...") + logger.info("Starting ${Constant.NAME}...") ImageService.loadCache() val memberService = Constant.injector.getInstance(MemberService::class.java) diff --git a/src/main/kotlin/fr/shikkanime/controllers/admin/AdminController.kt b/src/main/kotlin/fr/shikkanime/controllers/admin/AdminController.kt index 4c26923e..033e1427 100644 --- a/src/main/kotlin/fr/shikkanime/controllers/admin/AdminController.kt +++ b/src/main/kotlin/fr/shikkanime/controllers/admin/AdminController.kt @@ -5,6 +5,7 @@ import fr.shikkanime.converters.AbstractConverter import fr.shikkanime.dtos.TokenDto import fr.shikkanime.entities.Anime import fr.shikkanime.entities.Episode +import fr.shikkanime.entities.enums.LangType import fr.shikkanime.entities.enums.Link import fr.shikkanime.services.AnimeService import fr.shikkanime.services.EpisodeService @@ -126,14 +127,16 @@ class AdminController { animeService.update(anime) } - episodeService.findAll().forEach { episode -> - val anime = animeService.find(episode.anime!!.uuid!!)!! - episodeService.addSimulcastToAnime(anime, episodeService.getSimulcast(episode)) + episodeService.findAll() + .filter { it.langType == LangType.SUBTITLES } + .forEach { episode -> + val anime = animeService.find(episode.anime!!.uuid!!)!! + episodeService.addSimulcastToAnime(anime, episodeService.getSimulcast(episode)) - if (episode.anime != anime) { - animeService.update(anime) + if (episode.anime != anime) { + animeService.update(anime) + } } - } MapCache.invalidate(Anime::class.java, Episode::class.java) return Response.redirect(Link.SIMULCASTS.href) diff --git a/src/main/kotlin/fr/shikkanime/controllers/site/SiteController.kt b/src/main/kotlin/fr/shikkanime/controllers/site/SiteController.kt index b160670c..99a46b5a 100644 --- a/src/main/kotlin/fr/shikkanime/controllers/site/SiteController.kt +++ b/src/main/kotlin/fr/shikkanime/controllers/site/SiteController.kt @@ -14,6 +14,7 @@ import fr.shikkanime.utils.routes.Path import fr.shikkanime.utils.routes.Response import fr.shikkanime.utils.routes.method.Get import fr.shikkanime.utils.routes.param.PathParam +import io.ktor.http.* import java.util.* @Controller("/") @@ -61,27 +62,32 @@ class SiteController { ) } + @Path("robots.txt") + @Get + private fun robots(): Response { + return Response.template( + "/site/seo/robots.ftl", + null, + contentType = ContentType.Text.Plain + ) + } + + @Path("sitemap.xml") + @Get + private fun sitemap(): Response { + return Response.template( + "/site/seo/sitemap.ftl", + null, + contentType = ContentType.Text.Xml + ) + } + @Path("catalog") @Get private fun catalog(): Response { val findAll = simulcastCacheService.findAll()!! val currentSimulcast = findAll.first() - - return Response.template( - Link.CATALOG, - mutableMapOf( - "description" to configCacheService.getValueAsString(ConfigPropertyKey.SEO_DESCRIPTION), - "simulcasts" to findAll, - "currentSimulcast" to currentSimulcast, - "animes" to animeCacheService.findAllBy( - CountryCode.FR, - currentSimulcast.uuid, - listOf(SortParameter("name", SortParameter.Order.ASC)), - 1, - 102 - )!!.data, - ) - ) + return Response.redirect("/catalog/${currentSimulcast.uuid}") } @Path("catalog/{uuid}") @@ -125,7 +131,7 @@ class SiteController { "/site/anime.ftl", anime.shortName, mutableMapOf( - "description" to anime.description, + "description" to configCacheService.getValueAsString(ConfigPropertyKey.SEO_DESCRIPTION), "anime" to anime, "episodes" to episodeCacheService.findAllBy( CountryCode.FR, diff --git a/src/main/kotlin/fr/shikkanime/entities/enums/Link.kt b/src/main/kotlin/fr/shikkanime/entities/enums/Link.kt index 8cfeedad..aaa1d6db 100644 --- a/src/main/kotlin/fr/shikkanime/entities/enums/Link.kt +++ b/src/main/kotlin/fr/shikkanime/entities/enums/Link.kt @@ -1,6 +1,8 @@ package fr.shikkanime.entities.enums -enum class Link(val href: String, val template: String, val icon: String, val label: String) { +import fr.shikkanime.utils.Constant + +enum class Link(val href: String, val template: String, val icon: String, val label: String, val title: String = label) { // Admin DASHBOARD("/admin/dashboard", "/admin/dashboard.ftl", "bi bi-pc-display", "Dashboard"), PLATFORMS("/admin/platforms", "/admin/platforms/list.ftl", "bi bi-display", "Platforms"), @@ -11,7 +13,7 @@ enum class Link(val href: String, val template: String, val icon: String, val la SIMULCASTS("/admin/simulcasts", "/admin/simulcasts.ftl", "bi bi-calendar-event", "Simulcasts"), // Site - HOME("/", "/site/home.ftl", "", "Accueil"), + HOME("/", "/site/home.ftl", "", "Accueil", "${Constant.NAME} : Ne manquez plus jamais un épisode d'animé !"), CATALOG("/catalog", "/site/catalog.ftl", "", "Catalogue"), ; } \ No newline at end of file diff --git a/src/main/kotlin/fr/shikkanime/plugins/HTTP.kt b/src/main/kotlin/fr/shikkanime/plugins/HTTP.kt index 56bc7d59..38cf8be2 100644 --- a/src/main/kotlin/fr/shikkanime/plugins/HTTP.kt +++ b/src/main/kotlin/fr/shikkanime/plugins/HTTP.kt @@ -38,6 +38,10 @@ fun Application.configureHTTP() { status(HttpStatusCode.NotFound) { call, _ -> val path = call.request.path() + if (path.contains(".")) { + return@status + } + if (!path.startsWith("/api") && !path.startsWith("/admin")) { call.respondRedirect("/404") } @@ -60,7 +64,7 @@ fun Application.configureHTTP() { forwardRoot = false } info { - title = "Shikkanime API" + title = "${Constant.NAME} API" version = "1.0" description = "API for testing and demonstration purposes" } diff --git a/src/main/kotlin/fr/shikkanime/plugins/Routing.kt b/src/main/kotlin/fr/shikkanime/plugins/Routing.kt index 4b6594b6..50128531 100644 --- a/src/main/kotlin/fr/shikkanime/plugins/Routing.kt +++ b/src/main/kotlin/fr/shikkanime/plugins/Routing.kt @@ -216,60 +216,43 @@ private suspend fun handleRequest( try { val response = callMethodWithParameters(method, controller, call, parameters) - if (method.hasAnnotation()) { - val cached = method.findAnnotation()!!.maxAgeSeconds - call.caching = CachingOptions(CacheControl.MaxAge(maxAgeSeconds = cached)) - } - - if (response.session != null) { - call.sessions.set(response.session) - } + response.session?.let { call.sessions.set(it) } when (response.type) { - ResponseType.MULTIPART -> { - val map = response.data as Map - - call.respondBytes( - map["image"] as ByteArray, - map["contentType"] as ContentType, - ) - } - - ResponseType.TEMPLATE -> { - val map = response.data as Map - val modelMap = map["model"] as MutableMap - - val list = if (controller.javaClass.simpleName.startsWith("Admin")) - LinkObject.list().filter { it.href.startsWith("/admin") } - else - LinkObject.list().filter { !it.href.startsWith("/admin") } + ResponseType.MULTIPART -> handleMultipartResponse(call, response) + ResponseType.TEMPLATE -> handleTemplateResponse(call, controller, replacedPath, response) + ResponseType.REDIRECT -> call.respondRedirect(response.data as String) + else -> call.respond(response.status, response.data ?: "") + } + } catch (e: Exception) { + logger.log(Level.SEVERE, "Error while calling method $method", e) + call.respond(HttpStatusCode.BadRequest) + } +} - modelMap["links"] = list.map { link -> - link.active = if (link.href == "/") replacedPath == link.href else replacedPath.startsWith(link.href) - link - } +private suspend fun handleMultipartResponse(call: ApplicationCall, response: Response) { + val map = response.data as Map + call.respondBytes(map["image"] as ByteArray, map["contentType"] as ContentType) +} - val title = map["title"] as String? - modelMap["title"] = (if (!title.isNullOrBlank()) "$title - " else "") + "Shikkanime" +private suspend fun handleTemplateResponse(call: ApplicationCall, controller: Any, replacedPath: String, response: Response) { + val map = response.data as Map + val modelMap = (map["model"] as Map).toMutableMap() - call.respond(FreeMarkerContent(map["template"] as String, modelMap, "")) - } + val list = if (controller.javaClass.simpleName.startsWith("Admin")) + LinkObject.list().filter { it.href.startsWith("/admin") } + else + LinkObject.list().filter { !it.href.startsWith("/admin") } - ResponseType.REDIRECT -> { - call.respondRedirect(response.data as String) - } + modelMap["links"] = list.map { link -> + link.active = if (link.href == "/") replacedPath == link.href else replacedPath.startsWith(link.href) + link + } - else -> { - call.respond(response.status, response.data ?: "") - } - } - } catch (e: Exception) { - if (e.message?.contains("Broken pipe") != true && e.message?.contains("Relais brisé (pipe)") != true) { - logger.log(Level.SEVERE, "Error while calling method $method", e) - } + val title = map["title"] as String? + modelMap["title"] = if (title?.contains(Constant.NAME) == true) title else (if (!title.isNullOrBlank()) "$title - " else "") + Constant.NAME - call.respond(HttpStatusCode.BadRequest) - } + call.respond(FreeMarkerContent(map["template"] as String, modelMap, "", response.contentType)) } private fun replacePathWithParameters(path: String, parameters: Map>): String = diff --git a/src/main/kotlin/fr/shikkanime/socialnetworks/DiscordSocialNetwork.kt b/src/main/kotlin/fr/shikkanime/socialnetworks/DiscordSocialNetwork.kt index 8f809ced..4db4b8da 100644 --- a/src/main/kotlin/fr/shikkanime/socialnetworks/DiscordSocialNetwork.kt +++ b/src/main/kotlin/fr/shikkanime/socialnetworks/DiscordSocialNetwork.kt @@ -3,6 +3,7 @@ package fr.shikkanime.socialnetworks import fr.shikkanime.dtos.EpisodeDto import fr.shikkanime.entities.enums.ConfigPropertyKey import fr.shikkanime.services.ImageService +import fr.shikkanime.utils.Constant import fr.shikkanime.utils.LoggerFactory import fr.shikkanime.utils.StringUtils import net.dv8tion.jda.api.EmbedBuilder @@ -72,7 +73,7 @@ class DiscordSocialNetwork : AbstractSocialNetwork() { embedMessage.setThumbnail(episodeDto.anime.image) embedMessage.setDescription("**${episodeDto.title ?: "Untitled"}**\n${StringUtils.toEpisodeString(episodeDto)}") embedMessage.setImage(episodeDto.image) - embedMessage.setFooter("Shikkanime", "https://www.shikkanime.fr/assets/img/favicons/favicon-64x64.png") + embedMessage.setFooter(Constant.NAME, "https://www.shikkanime.fr/assets/img/favicons/favicon-64x64.png") embedMessage.setTimestamp(ZonedDateTime.parse(episodeDto.releaseDateTime).toInstant()) val embed = embedMessage.build() diff --git a/src/main/kotlin/fr/shikkanime/utils/Constant.kt b/src/main/kotlin/fr/shikkanime/utils/Constant.kt index 314cabe6..54057207 100644 --- a/src/main/kotlin/fr/shikkanime/utils/Constant.kt +++ b/src/main/kotlin/fr/shikkanime/utils/Constant.kt @@ -12,6 +12,7 @@ import java.io.File import java.time.ZoneId object Constant { + const val NAME = "Shikkanime" val reflections = Reflections("fr.shikkanime") val injector: Injector = Guice.createInjector(DefaultModule()) val abstractPlatforms = reflections.getSubTypesOf(AbstractPlatform::class.java).map { injector.getInstance(it) } diff --git a/src/main/kotlin/fr/shikkanime/utils/routes/Response.kt b/src/main/kotlin/fr/shikkanime/utils/routes/Response.kt index 3f23876f..04a2f213 100644 --- a/src/main/kotlin/fr/shikkanime/utils/routes/Response.kt +++ b/src/main/kotlin/fr/shikkanime/utils/routes/Response.kt @@ -16,6 +16,7 @@ open class Response( val type: ResponseType = ResponseType.JSON, val session: TokenDto? = null, val data: Any? = null, + val contentType: ContentType = ContentType.Application.Json, ) { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -24,6 +25,7 @@ open class Response( if (status != other.status) return false if (data != other.data) return false if (session != other.session) return false + if (contentType != other.contentType) return false return true } @@ -32,6 +34,7 @@ open class Response( var result = status.hashCode() result = 31 * result + (data?.hashCode() ?: 0) result = 31 * result + (session?.hashCode() ?: 0) + result = 31 * result + contentType.hashCode() return result } @@ -60,20 +63,23 @@ open class Response( fun template( template: String, title: String? = null, - model: MutableMap = mutableMapOf(), - session: TokenDto? = null + model: Map = mapOf(), + session: TokenDto? = null, + contentType: ContentType = ContentType.Text.Html.withCharset(Charsets.UTF_8), ): Response = Response( HttpStatusCode.OK, type = ResponseType.TEMPLATE, data = mapOf("template" to template, "title" to title, "model" to model), - session = session + session = session, + contentType = contentType ) fun template( link: Link, - model: MutableMap = mutableMapOf(), - session: TokenDto? = null - ): Response = template(link.template, link.label, model, session) + model: Map = mapOf(), + session: TokenDto? = null, + contentType: ContentType = ContentType.Text.Html.withCharset(Charsets.UTF_8), + ): Response = template(link.template, link.title, model, session, contentType) fun redirect(path: String, session: TokenDto? = null): Response = Response(HttpStatusCode.Found, type = ResponseType.REDIRECT, data = path, session = session) diff --git a/src/main/resources/templates/site/_layout.ftl b/src/main/resources/templates/site/_layout.ftl index b1b2cdcb..e3b8b810 100644 --- a/src/main/resources/templates/site/_layout.ftl +++ b/src/main/resources/templates/site/_layout.ftl @@ -36,9 +36,8 @@ -
- <#nested 0> -
+ + <#nested 0> diff --git a/src/main/resources/templates/site/_navigation.ftl b/src/main/resources/templates/site/_navigation.ftl index 9ae21bb6..2c927503 100644 --- a/src/main/resources/templates/site/_navigation.ftl +++ b/src/main/resources/templates/site/_navigation.ftl @@ -4,35 +4,85 @@ <@layout.main header="${header}"> <#assign margin = "mx-md-2"> - + + +
+
+
+
+ + Logo + + +

+ ${description} +

+ + © 2023 - ${.now?string("yyyy")} Shikkanime +
+
-
- <#nested 1> + +
\ No newline at end of file diff --git a/src/main/resources/templates/site/seo/robots.ftl b/src/main/resources/templates/site/seo/robots.ftl new file mode 100644 index 00000000..2f0317ae --- /dev/null +++ b/src/main/resources/templates/site/seo/robots.ftl @@ -0,0 +1,3 @@ +User-agent: * +Allow: / +Sitemap: https://www.shikkanime.fr/sitemap.xml \ No newline at end of file diff --git a/src/main/resources/templates/site/seo/sitemap.ftl b/src/main/resources/templates/site/seo/sitemap.ftl new file mode 100644 index 00000000..29b70032 --- /dev/null +++ b/src/main/resources/templates/site/seo/sitemap.ftl @@ -0,0 +1,9 @@ + + + + https://www.shikkanime.fr/ + ${.now?string("yyyy-MM-dd")} + daily + 1 + + \ No newline at end of file