Skip to content

Commit

Permalink
Merge pull request #482 from Shikkanime/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Ziedelth committed May 22, 2024
2 parents d3fb432 + f283ad7 commit 627018e
Show file tree
Hide file tree
Showing 45 changed files with 1,121 additions and 141 deletions.
10 changes: 6 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
val ktorVersion = "2.3.11"
val ktorSwaggerUiVersion = "2.10.0"
val hibernateCoreVersion = "6.5.1.Final"
val hibernateCoreVersion = "6.5.2.Final"
val ehcacheVersion = "3.10.8"
val glassfishJaxbVersion = "4.0.5"
val hibernateSearchVersion = "7.1.1.Final"
val tikaVersion = "3.0.0-BETA"
val postgresqlVersion = "42.7.3"
val reflectionsVersion = "0.10.2"
val guiceVersion = "7.0.0"
val liquibaseCoreVersion = "4.27.0"
val liquibaseCoreVersion = "4.28.0"
val quartzVersion = "2.5.0-rc1"
val guavaVersion = "33.2.0-jre"
val playwrightVersion = "1.44.0"
Expand All @@ -17,7 +17,8 @@ val gsonVersion = "2.11.0"
val openCvVersion = "4.9.0-0"
val bcprovVersion = "1.78.1"
val javaImageScalingVersion = "0.8.6"
val firebaseVersion = "9.2.0"
val firebaseVersion = "9.3.0"
val angusMailVersion = "2.0.3"

val jdaVersion = "5.0.0-beta.24"
val twitter4jVersion = "4.0.7"
Expand All @@ -28,7 +29,7 @@ val h2Version = "2.2.224"
val mockitoVersion = "5.12.0"

plugins {
val kotlinVersion = "1.9.24"
val kotlinVersion = "2.0.0"

kotlin("jvm") version kotlinVersion
kotlin("kapt") version kotlinVersion
Expand Down Expand Up @@ -95,6 +96,7 @@ dependencies {
implementation("org.apache.tika:tika-core:$tikaVersion")
implementation("org.apache.tika:tika-langdetect-optimaize:$tikaVersion")
implementation("com.google.firebase:firebase-admin:$firebaseVersion")
implementation("org.eclipse.angus:angus-mail:$angusMailVersion")

// Social networks
implementation("net.dv8tion:JDA:$jdaVersion")
Expand Down
24 changes: 5 additions & 19 deletions src/main/kotlin/fr/shikkanime/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import fr.shikkanime.jobs.*
import fr.shikkanime.modules.configureHTTP
import fr.shikkanime.modules.configureRouting
import fr.shikkanime.modules.configureSecurity
import fr.shikkanime.services.*
import fr.shikkanime.services.AnimeService
import fr.shikkanime.services.ImageService
import fr.shikkanime.services.MemberService
import fr.shikkanime.socialnetworks.DiscordSocialNetwork
import fr.shikkanime.utils.Constant
import fr.shikkanime.utils.JobManager
Expand All @@ -19,10 +21,9 @@ private val logger = LoggerFactory.getLogger(Constant.NAME)
fun main() {
logger.info("Starting ${Constant.NAME}...")
val animeService = Constant.injector.getInstance(AnimeService::class.java)
val episodeMappingService = Constant.injector.getInstance(EpisodeMappingService::class.java)
animeService.preIndex()

updateAndDeleteData(episodeMappingService, animeService)
updateAndDeleteData(animeService)

ImageService.loadCache()
ImageService.addAll()
Expand Down Expand Up @@ -60,14 +61,8 @@ fun main() {
).start(wait = true)
}

private fun updateAndDeleteData(episodeMappingService: EpisodeMappingService, animeService: AnimeService) {
val episodeVariantService = Constant.injector.getInstance(EpisodeVariantService::class.java)

private fun updateAndDeleteData(animeService: AnimeService) {
animeService.findAll().forEach {
val mappingVariants = episodeVariantService.findAllByAnime(it)
it.releaseDateTime = mappingVariants.minOf { it.releaseDateTime }
it.lastReleaseDateTime = mappingVariants.maxOf { it.releaseDateTime }

val toSlug = StringUtils.toSlug(StringUtils.getShortName(it.name!!))

if (toSlug != it.slug) {
Expand All @@ -78,15 +73,6 @@ private fun updateAndDeleteData(episodeMappingService: EpisodeMappingService, an
it.status = StringUtils.getStatus(it)
animeService.update(it)
}

episodeMappingService.findAll().forEach {
val mappingVariants = episodeVariantService.findAllByMapping(it)
it.releaseDateTime = mappingVariants.minOf { it.releaseDateTime }
it.lastReleaseDateTime = mappingVariants.maxOf { it.releaseDateTime }

it.status = StringUtils.getStatus(it)
episodeMappingService.update(it)
}
}

fun Application.module() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package fr.shikkanime.controllers.api

import com.google.inject.Inject
import fr.shikkanime.dtos.MessageDto
import fr.shikkanime.services.MemberActionService
import fr.shikkanime.utils.routes.Controller
import fr.shikkanime.utils.routes.JWTAuthenticated
import fr.shikkanime.utils.routes.Path
import fr.shikkanime.utils.routes.Response
import fr.shikkanime.utils.routes.method.Post
import fr.shikkanime.utils.routes.openapi.OpenAPI
import fr.shikkanime.utils.routes.openapi.OpenAPIResponse
import fr.shikkanime.utils.routes.param.BodyParam
import fr.shikkanime.utils.routes.param.QueryParam
import java.util.*

@Controller("/api/v1/member-actions")
class MemberActionController {
@Inject
private lateinit var memberActionService: MemberActionService

@Path("/validate")
@Post
@JWTAuthenticated
@OpenAPI(
"Validate an action",
[
OpenAPIResponse(
200,
"Action validated",
Response::class
),
OpenAPIResponse(
400,
"UUID is required OR Action is required",
MessageDto::class
),
],
security = true
)
fun validateAction(
@QueryParam("uuid", required = true) uuid: UUID?,
@BodyParam action: String?
): Response {
if (uuid == null) {
return Response.badRequest(
MessageDto(
MessageDto.Type.ERROR,
"UUID is required"
)
)
}

if (action.isNullOrEmpty()) {
return Response.badRequest(
MessageDto(
MessageDto.Type.ERROR,
"Action is required"
)
)
}

memberActionService.validateAction(uuid, action)
return Response.ok()
}
}
68 changes: 63 additions & 5 deletions src/main/kotlin/fr/shikkanime/controllers/api/MemberController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import fr.shikkanime.utils.routes.method.Put
import fr.shikkanime.utils.routes.openapi.OpenAPI
import fr.shikkanime.utils.routes.openapi.OpenAPIResponse
import fr.shikkanime.utils.routes.param.BodyParam
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import java.util.*

@Controller("/api/v1/members")
Expand All @@ -37,16 +39,30 @@ class MemberController {
description = "Register a private member",
responses = [
OpenAPIResponse(201, "Private member registered", Map::class),
]
],
deprecated = true
)
@Deprecated("You should use /register instead", ReplaceWith("/register"))
private fun registerPrivateMember(): Response {
return registerMember()
}

@Path("/register")
@Post
@OpenAPI(
description = "Register member",
responses = [
OpenAPIResponse(201, "Member registered", Map::class),
]
)
private fun registerMember(): Response {
var identifier: String

do {
identifier = StringUtils.generateRandomString(12)
} while (memberCacheService.findPrivateMember(identifier) != null)
} while (memberCacheService.findByIdentifier(identifier) != null)

memberService.savePrivateMember(identifier)
memberService.save(identifier)
return Response.created(mapOf("identifier" to identifier))
}

Expand All @@ -56,12 +72,54 @@ class MemberController {
description = "Login a private member",
responses = [
OpenAPIResponse(200, "Private member logged in"),
]
],
deprecated = true
)
@Deprecated("You should use /login instead", ReplaceWith("/login"))
private fun loginPrivateMember(@BodyParam identifier: String): Response {
return Response.ok(memberCacheService.findPrivateMember(identifier) ?: return Response.notFound())
return loginMember(identifier)
}

@Path("/login")
@Post
@OpenAPI(
description = "Login member",
responses = [
OpenAPIResponse(200, "Member logged in"),
]
)
private fun loginMember(@BodyParam identifier: String): Response {
return Response.ok(memberCacheService.findByIdentifier(identifier) ?: return runBlocking {
delay(1000)
Response.notFound()
})
}

@Path("/associate-email")
@Post
@JWTAuthenticated
@OpenAPI(
description = "Associate email to member",
responses = [
OpenAPIResponse(201, "Member action created", GenericDto::class),
OpenAPIResponse(401, "Unauthorized"),
],
security = true
)
private fun associateEmail(@JWTUser uuidUser: UUID, @BodyParam email: String): Response {
// Verify email
if (!StringUtils.isValidEmail(email)) {
return Response.badRequest("Invalid email")
}

if (memberService.findByEmail(email) != null) {
return Response.conflict("Email already associated to an account")
}

return Response.created(GenericDto(memberService.associateEmail(uuidUser, email)))
}


@Path("/animes")
@Put
@JWTAuthenticated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class MemberToMemberDtoConverter : AbstractConverter<Member, MemberDto>() {
creationDateTime = from.creationDateTime.withUTCString(),
lastUpdateDateTime = from.lastUpdateDateTime.withUTCString(),
isPrivate = from.isPrivate,
email = from.email,
followedAnimes = memberFollowAnimeService.findAllFollowedAnimesUUID(from),
followedEpisodes = memberFollowEpisodeService.findAllFollowedEpisodesUUID(from),
totalDuration = memberFollowEpisodeService.getTotalDuration(from),
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/fr/shikkanime/dtos/MemberDto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ data class MemberDto(
val creationDateTime: String,
val lastUpdateDateTime: String,
val isPrivate: Boolean,
val email: String?,
val followedAnimes: List<UUID>,
val followedEpisodes: List<UUID>,
val totalDuration: Long,
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/fr/shikkanime/entities/EpisodeMapping.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,7 @@ class EpisodeMapping(
@Column(nullable = true, name = "status")
@Enumerated(EnumType.STRING)
var status: Status = Status.VALID,
@OneToMany(mappedBy = "episode", fetch = FetchType.LAZY)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var memberFollowEpisodes: MutableSet<MemberFollowEpisode> = mutableSetOf(),
) : ShikkEntity(uuid)
2 changes: 2 additions & 0 deletions src/main/kotlin/fr/shikkanime/entities/Member.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class Member(
val isPrivate: Boolean = false,
@Column(nullable = false, unique = true)
val username: String? = null,
@Column(nullable = true, unique = true)
var email: String? = null,
@Column(nullable = false, name = "encrypted_password")
val encryptedPassword: ByteArray? = null,
@ElementCollection(fetch = FetchType.EAGER)
Expand Down
32 changes: 32 additions & 0 deletions src/main/kotlin/fr/shikkanime/entities/MemberAction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package fr.shikkanime.entities

import fr.shikkanime.entities.enums.Action
import jakarta.persistence.*
import org.hibernate.annotations.Cache
import org.hibernate.annotations.CacheConcurrencyStrategy
import java.time.ZonedDateTime
import java.util.*

@Entity
@Table(name = "member_action")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
class MemberAction(
uuid: UUID? = null,
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
var member: Member? = null,
@Column(nullable = false, name = "creation_date_time")
var creationDateTime: ZonedDateTime = ZonedDateTime.now(),
@Column(nullable = false, name = "update_date_time")
var updateDateTime: ZonedDateTime = creationDateTime,
@Column(nullable = false)
var email: String? = null,
@Column(nullable = false)
@Enumerated(EnumType.STRING)
var action: Action? = null,
@Column(nullable = false)
var validated: Boolean = false,
@Column(nullable = false)
var code: String? = null,
) : ShikkEntity(uuid)
5 changes: 5 additions & 0 deletions src/main/kotlin/fr/shikkanime/entities/enums/Action.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package fr.shikkanime.entities.enums

enum class Action {
VALIDATE_EMAIL,
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ enum class ConfigPropertyKey(val key: String) {
LAST_FETCH_OLD_EPISODES("last_fetch_old_episodes"),
FETCH_OLD_EPISODES_RANGE("fetch_old_episodes_range"),
FETCH_OLD_EPISODES_LIMIT("fetch_old_episodes_limit"),
EMAIL_HOST("email_host"),
EMAIL_PORT("email_port"),
EMAIL_USERNAME("email_username"),
EMAIL_PASSWORD("email_password"),
}
9 changes: 5 additions & 4 deletions src/main/kotlin/fr/shikkanime/modules/Routing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,14 @@ private fun setSecurityHeaders(call: ApplicationCall, configCacheService: Config

private fun logCallDetails(call: ApplicationCall) {
val startTime = call.attributes[callStartTime]
val duration = ZonedDateTime.now().toInstant().toEpochMilli() - startTime.toInstant().toEpochMilli()
val path = call.request.path()
val httpMethod = call.request.httpMethod.value
val userAgent = call.request.userAgent()
val status = call.response.status()?.value ?: 0
val duration = ZonedDateTime.now().toInstant().toEpochMilli() - startTime.toInstant().toEpochMilli()
val path = call.request.path()
val ipAddress = call.request.origin.remoteHost
val userAgent = call.request.userAgent() ?: "Unknown"

logger.info("$httpMethod ${call.request.origin.uri} [$status - $duration ms] -> $path${if (userAgent != null) " ($userAgent)" else ""}")
logger.info("[$ipAddress - $userAgent] ($status - $duration ms) $httpMethod ${call.request.origin.uri} -> $path")
}

private fun Routing.createRoutes() {
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/fr/shikkanime/modules/SwaggerRouting.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ fun swagger(
hidden = hiddenRoute || openApi.hidden
summary = openApi.description
description = openApi.description
deprecated = openApi.deprecated
swaggerRequest(method)
swaggerResponse(openApi)
}
Expand Down
Loading

0 comments on commit 627018e

Please sign in to comment.