diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index 6b22396..86be5f8 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -24,32 +24,28 @@ dependencies { implementation("org.jsoup:jsoup:1.16.1") // Databases - implementation("org.jetbrains.exposed:exposed-core:0.37.3") - implementation("org.jetbrains.exposed:exposed-jdbc:0.37.3") - implementation("org.jetbrains.exposed:exposed-dao:0.37.3") - implementation("org.jetbrains.exposed:exposed-kotlin-datetime:0.36.2") - implementation("org.postgresql:postgresql:42.3.3") - implementation("com.zaxxer:HikariCP:5.0.1") + implementation("org.jetbrains.exposed:exposed-core:0.55.0") + implementation("org.jetbrains.exposed:exposed-jdbc:0.55.0") + implementation("org.jetbrains.exposed:exposed-dao:0.55.0") + implementation("org.jetbrains.exposed:exposed-kotlin-datetime:0.55.0") + implementation("org.jetbrains.exposed:exposed-json:0.55.0") + implementation("org.postgresql:postgresql:42.7.4") + implementation("com.zaxxer:HikariCP:6.0.0") implementation("io.github.microutils:kotlin-logging:2.1.21") - implementation("net.perfectdreams.dreamstorageservice:client:2.0.2") - implementation("pw.forst", "exposed-upsert", "1.1.0") // Caching implementation("com.github.ben-manes.caffeine:caffeine:3.0.5") // Internationalization + LanguageManager api("net.perfectdreams.i18nhelper.formatters:icu-messageformat-jvm:${Versions.I18N_HELPER}") - implementation("com.charleskorn.kaml:kaml:0.35.0") + implementation("com.charleskorn.kaml:kaml:0.61.0") implementation("com.ibm.icu:icu4j:71.1") - implementation("org.yaml:snakeyaml:1.30") + implementation("org.yaml:snakeyaml:2.3") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.KOTLINX_SERIALIZATION}") // https://mvnrepository.com/artifact/club.minnced/discord-webhooks - implementation("club.minnced:discord-webhooks:0.8.2") - - testImplementation("io.ktor:ktor-server-tests:${Versions.KTOR}") - testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.5.31") + implementation("club.minnced:discord-webhooks:0.8.4") } jib { diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/GalleryOfDreamsBackend.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/GalleryOfDreamsBackend.kt index 3481d2a..e70c1cb 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/GalleryOfDreamsBackend.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/GalleryOfDreamsBackend.kt @@ -18,16 +18,12 @@ import io.ktor.server.routing.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import mu.KotlinLogging -import net.perfectdreams.dreamstorageservice.client.DreamStorageServiceClient import net.perfectdreams.galleryofdreams.backend.plugins.configureRouting import net.perfectdreams.galleryofdreams.backend.routes.* import net.perfectdreams.galleryofdreams.backend.routes.api.GetFanArtArtistByDiscordIdRoute import net.perfectdreams.galleryofdreams.backend.routes.api.GetFanArtsRoute import net.perfectdreams.galleryofdreams.backend.routes.api.GetLanguageInfoRoute import net.perfectdreams.galleryofdreams.backend.routes.api.PatchFanArtRoute -import net.perfectdreams.galleryofdreams.backend.routes.api.PostArtistWithFanArtRoute -import net.perfectdreams.galleryofdreams.backend.routes.api.PostCheckFanArtRoute -import net.perfectdreams.galleryofdreams.backend.routes.api.PostFanArtRoute import net.perfectdreams.galleryofdreams.backend.tables.AuthorizationTokens import net.perfectdreams.galleryofdreams.backend.tables.FanArtArtists import net.perfectdreams.galleryofdreams.backend.tables.FanArtTags @@ -71,9 +67,6 @@ class GalleryOfDreamsBackend(val languageManager: LanguageManager) { GetFanArtsRoute(this), GetLanguageInfoRoute(this), GetFanArtArtistByDiscordIdRoute(this), - PostArtistWithFanArtRoute(this), - PostFanArtRoute(this), - PostCheckFanArtRoute(this), PatchFanArtRoute(this) ) @@ -97,11 +90,6 @@ class GalleryOfDreamsBackend(val languageManager: LanguageManager) { this.socketTimeoutMillis = 120_000 } } - val dreamStorageServiceClient = DreamStorageServiceClient( - System.getenv("GALLERYOFDREAMS_DSS_URL"), - System.getenv("GALLERYOFDREAMS_DSS_TOKEN"), - http - ) private val typesToCache = listOf( ContentType.Text.CSS, @@ -208,7 +196,7 @@ class GalleryOfDreamsBackend(val languageManager: LanguageManager) { Database.connect( HikariDataSource(dataSource), databaseConfig = DatabaseConfig { - defaultRepetitionAttempts = DEFAULT_REPETITION_ATTEMPTS + this.defaultMaxAttempts = 5 defaultIsolationLevel = ISOLATION_LEVEL.levelId // Change our default isolation level } ) @@ -277,19 +265,14 @@ class GalleryOfDreamsBackend(val languageManager: LanguageManager) { } results.map { - val discordSocialConnections = FanArtArtistDiscordConnections.select { - FanArtArtistDiscordConnections.artist eq it[FanArtArtists.id] - } - val twitterSocialConnections = FanArtArtistTwitterConnections.select { - FanArtArtistTwitterConnections.artist eq it[FanArtArtists.id] - } - val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.select { - FanArtArtistDeviantArtConnections.artist eq it[FanArtArtists.id] - } + val discordSocialConnections = FanArtArtistDiscordConnections.selectAll() + .where { FanArtArtistDiscordConnections.artist eq it[FanArtArtists.id] } + val twitterSocialConnections = FanArtArtistTwitterConnections.selectAll() + .where { FanArtArtistTwitterConnections.artist eq it[FanArtArtists.id] } + val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.selectAll() + .where { FanArtArtistDeviantArtConnections.artist eq it[FanArtArtists.id] } - val count = FanArts.select { - FanArts.artist eq it[FanArtArtists.id] - }.count() + val count = FanArts.selectAll().where { FanArts.artist eq it[FanArtArtists.id] }.count() FanArtArtistWithFanArtCount( FanArtArtistX( @@ -304,9 +287,8 @@ class GalleryOfDreamsBackend(val languageManager: LanguageManager) { } + deviantArtSocialConnections.map { DeviantArtSocialConnection(it[FanArtArtistDeviantArtConnections.handle]) }, - FanArts.select { - FanArts.artist eq it[FanArtArtists.id].value - }.orderBy(FanArts.createdAt, SortOrder.DESC).limit(1).firstOrNull()?.let { + FanArts.selectAll().where { FanArts.artist eq it[FanArtArtists.id].value } + .orderBy(FanArts.createdAt, SortOrder.DESC).limit(1).firstOrNull()?.let { convertToFanArt(it) } ), @@ -325,9 +307,8 @@ class GalleryOfDreamsBackend(val languageManager: LanguageManager) { fanArt[FanArts.dreamStorageServiceImageId], fanArt[FanArts.file], fanArt[FanArts.preferredMediaType], - FanArtTags.slice(FanArtTags.tag).select { - FanArtTags.fanArt eq fanArt[FanArts.id] - }.map { it[FanArtTags.tag] } + FanArtTags.select(FanArtTags.tag).where { FanArtTags.fanArt eq fanArt[FanArts.id] } + .map { it[FanArtTags.tag] } ) fun execAndMap(string: String, transform : (ResultSet) -> T) : List { diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtArtist.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtArtist.kt index bd7134d..67650d7 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtArtist.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtArtist.kt @@ -11,8 +11,6 @@ import net.perfectdreams.i18nhelper.core.I18nContext fun FlowContent.fanArtArtist( i18nContext: I18nContext, - dssBaseUrl: String, - namespace: String, artist: FanArtArtistX, fanArtCount: Long ) { diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtCard.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtCard.kt index 815a6b1..ae9fcca 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtCard.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtCard.kt @@ -13,7 +13,7 @@ import net.perfectdreams.galleryofdreams.common.data.FanArtArtistX import net.perfectdreams.galleryofdreams.common.i18n.I18nKeysData import net.perfectdreams.i18nhelper.core.I18nContext -fun FlowContent.fanArtCard(m: GalleryOfDreamsBackend, i18nContext: I18nContext, dssBaseUrl: String, namespace: String, fanArtArtist: FanArtArtistX, fanArt: FanArt) { +fun FlowContent.fanArtCard(m: GalleryOfDreamsBackend, i18nContext: I18nContext, fanArtArtist: FanArtArtistX, fanArt: FanArt) { aHtmx(classes = "fan-art-card", href = "/${i18nContext.websiteLocaleIdPath}/artists/${fanArtArtist.slug}/${fanArt.slug}", hxTarget = "#content") { div(classes = "fan-art-info-card") { div(classes = "fan-art-tags") { diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtCardGrid.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtCardGrid.kt index 2a10d54..768d97e 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtCardGrid.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/components/FanArtCardGrid.kt @@ -5,7 +5,7 @@ import net.perfectdreams.galleryofdreams.backend.GalleryOfDreamsBackend import net.perfectdreams.galleryofdreams.backend.utils.FanArtArtistWithFanArt import net.perfectdreams.i18nhelper.core.I18nContext -fun FlowContent.fanArtCardGrid(m: GalleryOfDreamsBackend, i18nContext: I18nContext, dssBaseUrl: String, namespace: String, fanArts: List) { +fun FlowContent.fanArtCardGrid(m: GalleryOfDreamsBackend, i18nContext: I18nContext, fanArts: List) { if (fanArts.isEmpty()) { div { h1 { @@ -21,7 +21,7 @@ fun FlowContent.fanArtCardGrid(m: GalleryOfDreamsBackend, i18nContext: I18nConte "display: grid; grid-template-columns: repeat(auto-fill, minmax(192px, 1fr)); grid-template-rows: repeat(auto-fill, minmax(192px, 1fr)); gap: 1em; justify-content: space-between; width: 100%;" for (fanArt in fanArts) { - fanArtCard(m, i18nContext, dssBaseUrl, namespace, fanArt.fanArtArtist, fanArt.fanArt) + fanArtCard(m, i18nContext, fanArt.fanArtArtist, fanArt.fanArt) } } } diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtArtistRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtArtistRoute.kt index a01ca75..93d6b15 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtArtistRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtArtistRoute.kt @@ -22,6 +22,8 @@ import net.perfectdreams.galleryofdreams.common.i18n.I18nKeysData import net.perfectdreams.i18nhelper.core.I18nContext import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList +import org.jetbrains.exposed.sql.SqlExpressionBuilder.inSubQuery class GetFanArtArtistRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/artists/{artistSlug}") { override suspend fun onLocalizedRequest(call: ApplicationCall, i18nContext: I18nContext) { @@ -33,22 +35,19 @@ class GetFanArtArtistRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/arti val zeroIndexedPage = page - 1 val fanArts = m.transaction { - val fanArtArtist = FanArtArtists.select { - FanArtArtists.slug eq artistSlug - }.first() + val fanArtArtist = FanArtArtists.selectAll().where { FanArtArtists.slug eq artistSlug }.first() - val discordSocialConnections = FanArtArtistDiscordConnections.select { - FanArtArtistDiscordConnections.artist eq fanArtArtist[FanArtArtists.id] - } - val twitterSocialConnections = FanArtArtistTwitterConnections.select { - FanArtArtistTwitterConnections.artist eq fanArtArtist[FanArtArtists.id] - } - val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.select { - FanArtArtistDeviantArtConnections.artist eq fanArtArtist[FanArtArtists.id] - } + val discordSocialConnections = FanArtArtistDiscordConnections.selectAll() + .where { FanArtArtistDiscordConnections.artist eq fanArtArtist[FanArtArtists.id] } + val twitterSocialConnections = FanArtArtistTwitterConnections.selectAll() + .where { FanArtArtistTwitterConnections.artist eq fanArtArtist[FanArtArtists.id] } + val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.selectAll() + .where { FanArtArtistDeviantArtConnections.artist eq fanArtArtist[FanArtArtists.id] } - val query = FanArts.select { - FanArts.artist eq fanArtArtist[FanArtArtists.id] and (if (tags == null) Op.TRUE eq Op.TRUE else FanArts.id inSubQuery FanArtTags.slice(FanArtTags.fanArt).select { FanArtTags.tag inList tags }) + val query = FanArts.selectAll().where { + FanArts.artist eq fanArtArtist[FanArtArtists.id] and (if (tags == null) Op.TRUE eq Op.TRUE else FanArts.id inSubQuery FanArtTags.select( + FanArtTags.fanArt + ).where { FanArtTags.tag inList tags }) }.orderBy( FanArts.createdAt, when (sortOrder) { FanArtSortOrder.DATE_ASCENDING -> SortOrder.ASC @@ -58,7 +57,8 @@ class GetFanArtArtistRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/arti // YES THE ORDER MATTERS BECAUSE THE QUERY IS MUTABLE val totalFanArts = query.count() - val fanArts = query.limit(GalleryOfDreamsBackend.FAN_ARTS_PER_PAGE, (zeroIndexedPage * 20).toLong()).toList() + val fanArts = + query.limit(GalleryOfDreamsBackend.FAN_ARTS_PER_PAGE, (zeroIndexedPage * 20).toLong()).toList() QueryResult( FanArtArtistX( @@ -73,9 +73,8 @@ class GetFanArtArtistRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/arti } + deviantArtSocialConnections.map { DeviantArtSocialConnection(it[FanArtArtistDeviantArtConnections.handle]) }, - FanArts.select { - FanArts.artist eq fanArtArtist[FanArtArtists.id].value - }.orderBy(FanArts.createdAt, SortOrder.DESC).limit(1).firstOrNull()?.let { + FanArts.selectAll().where { FanArts.artist eq fanArtArtist[FanArtArtists.id].value } + .orderBy(FanArts.createdAt, SortOrder.DESC).limit(1).firstOrNull()?.let { m.convertToFanArt(it) } ), @@ -89,9 +88,8 @@ class GetFanArtArtistRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/arti it[FanArts.dreamStorageServiceImageId], it[FanArts.file], it[FanArts.preferredMediaType], - FanArtTags.slice(FanArtTags.tag).select { - FanArtTags.fanArt eq it[FanArts.id] - }.map { it[FanArtTags.tag] } + FanArtTags.select(FanArtTags.tag).where { FanArtTags.fanArt eq it[FanArts.id] } + .map { it[FanArtTags.tag] } ) }, totalFanArts @@ -103,8 +101,6 @@ class GetFanArtArtistRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/arti i18nContext, i18nContext.get(I18nKeysData.WebsiteTitle), call.request.pathWithoutLocale(), - m.dreamStorageServiceClient.baseUrl, - m.dreamStorageServiceClient.getCachedNamespaceOrRetrieve(), artistSlug, sortOrder, tags, diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtRoute.kt index 5231db9..26b7572 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtRoute.kt @@ -34,28 +34,26 @@ import net.perfectdreams.galleryofdreams.common.data.FanArtArtistX import net.perfectdreams.galleryofdreams.common.data.TwitterSocialConnection import net.perfectdreams.galleryofdreams.common.i18n.I18nKeysData import net.perfectdreams.i18nhelper.core.I18nContext -import org.jetbrains.exposed.sql.SortOrder +import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq -import org.jetbrains.exposed.sql.and -import org.jetbrains.exposed.sql.select -import org.jetbrains.exposed.sql.selectAll class GetFanArtRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/artists/{artistSlug}/{fanArtSlug}") { override suspend fun onLocalizedRequest(call: ApplicationCall, i18nContext: I18nContext) { val fanArtAndArtist = m.transaction { val data = FanArts.innerJoin(FanArtArtists) - .select { FanArtArtists.slug eq call.parameters.getOrFail("artistSlug") and (FanArts.slug eq call.parameters.getOrFail("fanArtSlug")) } + .selectAll().where { + FanArtArtists.slug eq call.parameters.getOrFail("artistSlug") and (FanArts.slug eq call.parameters.getOrFail( + "fanArtSlug" + )) + } .first() - val discordSocialConnections = FanArtArtistDiscordConnections.select { - FanArtArtistDiscordConnections.artist eq data[FanArtArtists.id] - } - val twitterSocialConnections = FanArtArtistTwitterConnections.select { - FanArtArtistTwitterConnections.artist eq data[FanArtArtists.id] - } - val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.select { - FanArtArtistDeviantArtConnections.artist eq data[FanArtArtists.id] - } + val discordSocialConnections = FanArtArtistDiscordConnections.selectAll() + .where { FanArtArtistDiscordConnections.artist eq data[FanArtArtists.id] } + val twitterSocialConnections = FanArtArtistTwitterConnections.selectAll() + .where { FanArtArtistTwitterConnections.artist eq data[FanArtArtists.id] } + val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.selectAll() + .where { FanArtArtistDeviantArtConnections.artist eq data[FanArtArtists.id] } FanArtArtistWithFanArt( FanArtArtistX( @@ -70,9 +68,8 @@ class GetFanArtRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/artists/{a } + deviantArtSocialConnections.map { DeviantArtSocialConnection(it[FanArtArtistDeviantArtConnections.handle]) }, - FanArts.select { - FanArts.artist eq data[FanArtArtists.id].value - }.orderBy(FanArts.createdAt, SortOrder.DESC).limit(1).firstOrNull()?.let { + FanArts.selectAll().where { FanArts.artist eq data[FanArtArtists.id].value } + .orderBy(FanArts.createdAt, SortOrder.DESC).limit(1).firstOrNull()?.let { m.convertToFanArt(it) } ), @@ -85,8 +82,6 @@ class GetFanArtRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/artists/{a i18nContext, i18nContext.get(I18nKeysData.WebsiteTitle), call.request.pathWithoutLocale(), - m.dreamStorageServiceClient.baseUrl, - m.dreamStorageServiceClient.getCachedNamespaceOrRetrieve(), fanArtAndArtist.fanArtArtist, fanArtAndArtist.fanArt ) diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtsListRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtsListRoute.kt index ae4e31e..1b9805f 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtsListRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetFanArtsListRoute.kt @@ -30,8 +30,8 @@ class GetFanArtsListRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/fan-a val zeroIndexedPage = page - 1 val result = m.transaction { - val query = FanArts.select { - (if (tags == null) Op.TRUE eq Op.TRUE else FanArts.id inSubQuery FanArtTags.slice(FanArtTags.fanArt).select { FanArtTags.tag inList tags }) + val query = FanArts.selectAll().where { + (if (tags == null) Op.TRUE eq Op.TRUE else FanArts.id inSubQuery FanArtTags.select(FanArtTags.fanArt).where { FanArtTags.tag inList tags }) }.orderBy( FanArts.createdAt, when (sortOrder) { FanArtSortOrder.DATE_ASCENDING -> SortOrder.ASC @@ -41,24 +41,21 @@ class GetFanArtsListRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/fan-a // YES THE ORDER MATTERS BECAUSE THE QUERY IS MUTABLE val totalFanArts = query.count() - val fanArts = query.limit(GalleryOfDreamsBackend.FAN_ARTS_PER_PAGE, (zeroIndexedPage * 20).toLong()).toList() + val fanArts = + query.limit(GalleryOfDreamsBackend.FAN_ARTS_PER_PAGE, (zeroIndexedPage * 20).toLong()).toList() val mappedFanArts = mutableListOf() for (fanArt in fanArts) { - val fanArtArtist = FanArtArtists.select { - FanArtArtists.id eq fanArt[FanArts.artist] - }.first() + val fanArtArtist = + FanArtArtists.selectAll().where { FanArtArtists.id eq fanArt[FanArts.artist] }.first() - val discordSocialConnections = FanArtArtistDiscordConnections.select { - FanArtArtistDiscordConnections.artist eq fanArtArtist[FanArtArtists.id] - } - val twitterSocialConnections = FanArtArtistTwitterConnections.select { - FanArtArtistTwitterConnections.artist eq fanArtArtist[FanArtArtists.id] - } - val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.select { - FanArtArtistDeviantArtConnections.artist eq fanArtArtist[FanArtArtists.id] - } + val discordSocialConnections = FanArtArtistDiscordConnections.selectAll() + .where { FanArtArtistDiscordConnections.artist eq fanArtArtist[FanArtArtists.id] } + val twitterSocialConnections = FanArtArtistTwitterConnections.selectAll() + .where { FanArtArtistTwitterConnections.artist eq fanArtArtist[FanArtArtists.id] } + val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.selectAll() + .where { FanArtArtistDeviantArtConnections.artist eq fanArtArtist[FanArtArtists.id] } mappedFanArts.add( FanArtArtistWithFanArt( @@ -74,9 +71,8 @@ class GetFanArtsListRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/fan-a } + deviantArtSocialConnections.map { DeviantArtSocialConnection(it[FanArtArtistDeviantArtConnections.handle]) }, - FanArts.select { - FanArts.artist eq fanArtArtist[FanArtArtists.id].value - }.orderBy(FanArts.createdAt, SortOrder.DESC).limit(1).firstOrNull()?.let { + FanArts.selectAll().where { FanArts.artist eq fanArtArtist[FanArtArtists.id].value } + .orderBy(FanArts.createdAt, SortOrder.DESC).limit(1).firstOrNull()?.let { m.convertToFanArt(it) } ), @@ -96,8 +92,6 @@ class GetFanArtsListRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/fan-a i18nContext, i18nContext.get(I18nKeysData.WebsiteTitle), call.request.pathWithoutLocale(), - m.dreamStorageServiceClient.baseUrl, - m.dreamStorageServiceClient.getCachedNamespaceOrRetrieve(), sortOrder, tags, page, diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetHomeRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetHomeRoute.kt index 32ad26f..d834aca 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetHomeRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetHomeRoute.kt @@ -19,9 +19,7 @@ class GetHomeRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m, "/") { m, i18nContext, i18nContext.get(I18nKeysData.WebsiteTitle), - call.request.pathWithoutLocale(), - m.dreamStorageServiceClient.baseUrl, - m.dreamStorageServiceClient.getCachedNamespaceOrRetrieve(), + call.request.pathWithoutLocale() ) when (call.htmxElementTarget) { diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetSitemapRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetSitemapRoute.kt index c32531e..0730f0a 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetSitemapRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/GetSitemapRoute.kt @@ -19,6 +19,9 @@ import net.perfectdreams.galleryofdreams.common.data.FanArtArtist import net.perfectdreams.galleryofdreams.common.data.TwitterSocialConnection import net.perfectdreams.galleryofdreams.common.i18n.I18nKeysData import net.perfectdreams.sequins.ktor.BaseRoute +import org.jetbrains.exposed.sql.Op +import org.jetbrains.exposed.sql.SqlExpressionBuilder +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.selectAll import java.io.StringWriter @@ -38,8 +41,6 @@ class GetSitemapRoute(val m: GalleryOfDreamsBackend) : BaseRoute("/sitemap.xml") } override suspend fun onRequest(call: ApplicationCall) { - val namespace = m.dreamStorageServiceClient.getCachedNamespaceOrRetrieve() - val docFactory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance() .apply { // https://stackoverflow.com/a/41538813/7271796 @@ -49,19 +50,14 @@ class GetSitemapRoute(val m: GalleryOfDreamsBackend) : BaseRoute("/sitemap.xml") // TODO: Move this somewhere else val fanArtArtistsData = m.transaction { val fanArtArtists = FanArtArtists.selectAll().map { fanArtArtist -> - val discordSocialConnections = FanArtArtistDiscordConnections.select { - FanArtArtistDiscordConnections.artist eq fanArtArtist[FanArtArtists.id] - } - val twitterSocialConnections = FanArtArtistTwitterConnections.select { - FanArtArtistTwitterConnections.artist eq fanArtArtist[FanArtArtists.id] - } - val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.select { - FanArtArtistDeviantArtConnections.artist eq fanArtArtist[FanArtArtists.id] - } - - val fanArts = FanArts.select { - FanArts.artist eq fanArtArtist[FanArtArtists.id] - }.map { + val discordSocialConnections = FanArtArtistDiscordConnections.selectAll() + .where { FanArtArtistDiscordConnections.artist eq fanArtArtist[FanArtArtists.id] } + val twitterSocialConnections = FanArtArtistTwitterConnections.selectAll() + .where { FanArtArtistTwitterConnections.artist eq fanArtArtist[FanArtArtists.id] } + val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.selectAll() + .where { FanArtArtistDeviantArtConnections.artist eq fanArtArtist[FanArtArtists.id] } + + val fanArts = FanArts.selectAll().where { FanArts.artist eq fanArtArtist[FanArtArtists.id] }.map { FanArt( it[FanArts.id].value, it[FanArts.slug], @@ -71,9 +67,7 @@ class GetSitemapRoute(val m: GalleryOfDreamsBackend) : BaseRoute("/sitemap.xml") it[FanArts.dreamStorageServiceImageId], it[FanArts.file], it[FanArts.preferredMediaType], - FanArtTags.select { - FanArtTags.fanArt eq it[FanArts.id] - }.map { + FanArtTags.selectAll().where { FanArtTags.fanArt eq it[FanArts.id] }.map { it[FanArtTags.tag] }, ) @@ -181,7 +175,7 @@ class GetSitemapRoute(val m: GalleryOfDreamsBackend) : BaseRoute("/sitemap.xml") doc.createElement("image:image").apply { appendChild( doc.createElement("image:loc").apply { - textContent = m.dreamStorageServiceClient.baseUrl + "/${namespace}/${StoragePaths.FanArt("${fanArt.file}.${MediaTypeUtils.convertContentTypeToExtension(fanArt.preferredMediaType)}").join()}" + textContent = "https://assets.perfectdreams.media/galleryofdreams/fan-arts/${StoragePaths.FanArt("${fanArt.file}.${MediaTypeUtils.convertContentTypeToExtension(fanArt.preferredMediaType)}").join()}" } ) } diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/PostFanArtArtistsSearchRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/PostFanArtArtistsSearchRoute.kt index 2b34821..2d6054a 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/PostFanArtArtistsSearchRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/PostFanArtArtistsSearchRoute.kt @@ -35,13 +35,10 @@ class PostFanArtArtistsSearchRoute(m: GalleryOfDreamsBackend) : LocalizedRoute(m val results = m.searchFanArtArtists(sort, query, GalleryOfDreamsBackend.ARTIST_LIST_COUNT_PER_QUERY, offset) - val baseUrl = m.dreamStorageServiceClient.baseUrl - val namespace = m.dreamStorageServiceClient.getCachedNamespaceOrRetrieve() - call.respondHtml { body { for (fanArtArtist in results) { - fanArtArtist(i18nContext, baseUrl, namespace, fanArtArtist.fanArtArtist, fanArtArtist.fanArtCount) + fanArtArtist(i18nContext, fanArtArtist.fanArtArtist, fanArtArtist.fanArtCount) } if (results.size == GalleryOfDreamsBackend.ARTIST_LIST_COUNT_PER_QUERY) { diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/RequiresAPIAuthenticationRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/RequiresAPIAuthenticationRoute.kt index d156938..8f74827 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/RequiresAPIAuthenticationRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/RequiresAPIAuthenticationRoute.kt @@ -9,7 +9,11 @@ import net.perfectdreams.galleryofdreams.backend.GalleryOfDreamsBackend import net.perfectdreams.galleryofdreams.backend.tables.AuthorizationTokens import net.perfectdreams.galleryofdreams.backend.utils.AuthorizationToken import net.perfectdreams.sequins.ktor.BaseRoute +import org.jetbrains.exposed.sql.Op +import org.jetbrains.exposed.sql.SqlExpressionBuilder +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.selectAll abstract class RequiresAPIAuthenticationRoute(val m: GalleryOfDreamsBackend, path: String) : BaseRoute(path) { companion object { @@ -29,7 +33,7 @@ abstract class RequiresAPIAuthenticationRoute(val m: GalleryOfDreamsBackend, pat } val validKey = m.transaction { - AuthorizationTokens.select { AuthorizationTokens.token eq auth }.firstOrNull() + AuthorizationTokens.selectAll().where { AuthorizationTokens.token eq auth }.firstOrNull() } logger.trace { "$auth is trying to access $path (${clazzName}), using key $validKey" } diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/GetFanArtArtistByDiscordIdRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/GetFanArtArtistByDiscordIdRoute.kt index 084a315..c2f8fcd 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/GetFanArtArtistByDiscordIdRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/GetFanArtArtistByDiscordIdRoute.kt @@ -18,35 +18,36 @@ import net.perfectdreams.galleryofdreams.common.data.FanArt import net.perfectdreams.galleryofdreams.common.data.FanArtArtist import net.perfectdreams.galleryofdreams.common.data.TwitterSocialConnection import net.perfectdreams.sequins.ktor.BaseRoute +import org.jetbrains.exposed.sql.Op +import org.jetbrains.exposed.sql.SqlExpressionBuilder +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.selectAll class GetFanArtArtistByDiscordIdRoute(private val m: GalleryOfDreamsBackend) : BaseRoute("/api/v1/social/discord/{discordId}") { override suspend fun onRequest(call: ApplicationCall) { val discordId = call.parameters.getOrFail("discordId").toLong() val artistData = m.transaction { - val discordConnectionArtistId = FanArtArtistDiscordConnections.select { FanArtArtistDiscordConnections.discordId eq discordId } + val discordConnectionArtistId = FanArtArtistDiscordConnections.selectAll() + .where { FanArtArtistDiscordConnections.discordId eq discordId } .limit(1) .firstOrNull() ?: return@transaction null val fanArtArtist = FanArtArtists - .select { FanArtArtists.id eq discordConnectionArtistId[FanArtArtistDiscordConnections.artist] } + .selectAll() + .where { FanArtArtists.id eq discordConnectionArtistId[FanArtArtistDiscordConnections.artist] } .limit(1) .first() - val discordSocialConnections = FanArtArtistDiscordConnections.select { - FanArtArtistDiscordConnections.artist eq fanArtArtist[FanArtArtists.id] - } - val twitterSocialConnections = FanArtArtistTwitterConnections.select { - FanArtArtistTwitterConnections.artist eq fanArtArtist[FanArtArtists.id] - } - val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.select { - FanArtArtistDeviantArtConnections.artist eq fanArtArtist[FanArtArtists.id] - } + val discordSocialConnections = FanArtArtistDiscordConnections.selectAll() + .where { FanArtArtistDiscordConnections.artist eq fanArtArtist[FanArtArtists.id] } + val twitterSocialConnections = FanArtArtistTwitterConnections.selectAll() + .where { FanArtArtistTwitterConnections.artist eq fanArtArtist[FanArtArtists.id] } + val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.selectAll() + .where { FanArtArtistDeviantArtConnections.artist eq fanArtArtist[FanArtArtists.id] } - val fanArts = FanArts.select { - FanArts.artist eq fanArtArtist[FanArtArtists.id] - }.map { + val fanArts = FanArts.selectAll().where { FanArts.artist eq fanArtArtist[FanArtArtists.id] }.map { FanArt( it[FanArts.id].value, it[FanArts.slug], @@ -56,9 +57,7 @@ class GetFanArtArtistByDiscordIdRoute(private val m: GalleryOfDreamsBackend) : B it[FanArts.dreamStorageServiceImageId], it[FanArts.file], it[FanArts.preferredMediaType], - FanArtTags.select { - FanArtTags.fanArt eq it[FanArts.id] - }.map { + FanArtTags.selectAll().where { FanArtTags.fanArt eq it[FanArts.id] }.map { it[FanArtTags.tag] }, ) diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/GetFanArtsRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/GetFanArtsRoute.kt index 8154baf..8b22519 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/GetFanArtsRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/GetFanArtsRoute.kt @@ -24,6 +24,9 @@ import net.perfectdreams.galleryofdreams.common.data.TwitterSocialConnection import net.perfectdreams.sequins.ktor.BaseRoute import org.apache.commons.codec.binary.Hex import org.apache.commons.codec.digest.DigestUtils +import org.jetbrains.exposed.sql.Op +import org.jetbrains.exposed.sql.SqlExpressionBuilder +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.TransactionManager @@ -32,8 +35,6 @@ import java.sql.ResultSet class GetFanArtsRoute(private val m: GalleryOfDreamsBackend) : BaseRoute("/api/v1/fan-arts") { override suspend fun onRequest(call: ApplicationCall) { - val namespace = m.dreamStorageServiceClient.getCachedNamespaceOrRetrieve() - val result = m.transaction { // Calculate md5 hash of the elements, used for the ETag (Cache Busting) // This also has the advantage of being waaaay faster than the query below, so if the hash matches it will be way faster :3 @@ -53,19 +54,14 @@ class GetFanArtsRoute(private val m: GalleryOfDreamsBackend) : BaseRoute("/api/v } val fanArtArtists = FanArtArtists.selectAll().map { fanArtArtist -> - val discordSocialConnections = FanArtArtistDiscordConnections.select { - FanArtArtistDiscordConnections.artist eq fanArtArtist[FanArtArtists.id] - } - val twitterSocialConnections = FanArtArtistTwitterConnections.select { - FanArtArtistTwitterConnections.artist eq fanArtArtist[FanArtArtists.id] - } - val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.select { - FanArtArtistDeviantArtConnections.artist eq fanArtArtist[FanArtArtists.id] - } + val discordSocialConnections = FanArtArtistDiscordConnections.selectAll() + .where { FanArtArtistDiscordConnections.artist eq fanArtArtist[FanArtArtists.id] } + val twitterSocialConnections = FanArtArtistTwitterConnections.selectAll() + .where { FanArtArtistTwitterConnections.artist eq fanArtArtist[FanArtArtists.id] } + val deviantArtSocialConnections = FanArtArtistDeviantArtConnections.selectAll() + .where { FanArtArtistDeviantArtConnections.artist eq fanArtArtist[FanArtArtists.id] } - val fanArts = FanArts.select { - FanArts.artist eq fanArtArtist[FanArtArtists.id] - }.map { + val fanArts = FanArts.selectAll().where { FanArts.artist eq fanArtArtist[FanArtArtists.id] }.map { FanArt( it[FanArts.id].value, it[FanArts.slug], @@ -75,9 +71,7 @@ class GetFanArtsRoute(private val m: GalleryOfDreamsBackend) : BaseRoute("/api/v it[FanArts.dreamStorageServiceImageId], it[FanArts.file], it[FanArts.preferredMediaType], - FanArtTags.select { - FanArtTags.fanArt eq it[FanArts.id] - }.map { + FanArtTags.selectAll().where { FanArtTags.fanArt eq it[FanArts.id] }.map { it[FanArtTags.tag] }, ) @@ -102,8 +96,8 @@ class GetFanArtsRoute(private val m: GalleryOfDreamsBackend) : BaseRoute("/api/v Pair( GalleryOfDreamsDataResponse( DreamStorageServiceData( - m.dreamStorageServiceClient.baseUrl, - namespace + "unused", + "unused" ), fanArtArtists ), diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PatchFanArtRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PatchFanArtRoute.kt index 7881d5a..25f158a 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PatchFanArtRoute.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PatchFanArtRoute.kt @@ -15,9 +15,8 @@ import net.perfectdreams.galleryofdreams.backend.tables.FanArts import net.perfectdreams.galleryofdreams.backend.utils.AuthorizationToken import net.perfectdreams.galleryofdreams.backend.utils.exposed.respondJson import net.perfectdreams.galleryofdreams.common.data.api.PatchFanArtRequest -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq class PatchFanArtRoute(m: GalleryOfDreamsBackend) : RequiresAPIAuthenticationRoute(m, "/api/v1/fan-arts/{fanArtSlug}") { override suspend fun onAuthenticatedRequest(call: ApplicationCall, token: AuthorizationToken) { @@ -31,7 +30,7 @@ class PatchFanArtRoute(m: GalleryOfDreamsBackend) : RequiresAPIAuthenticationRou // To do this, we will remove all current tags and reinsert them! m.transaction { // Get the fan art - val fanArt = FanArts.select { FanArts.slug eq fanArtSlug } + val fanArt = FanArts.selectAll().where { FanArts.slug eq fanArtSlug } .limit(1) .firstOrNull() ?: return@transaction false diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostArtistWithFanArtRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostArtistWithFanArtRoute.kt deleted file mode 100644 index 075d5dc..0000000 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostArtistWithFanArtRoute.kt +++ /dev/null @@ -1,166 +0,0 @@ -package net.perfectdreams.galleryofdreams.backend.routes.api - -import club.minnced.discord.webhook.send.AllowedMentions -import club.minnced.discord.webhook.send.WebhookMessageBuilder -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.server.application.* -import io.ktor.server.request.* -import io.ktor.utils.io.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import kotlinx.io.readByteArray -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json -import net.perfectdreams.dreamstorageservice.data.api.CreateImageLinkRequest -import net.perfectdreams.dreamstorageservice.data.api.UploadImageRequest -import net.perfectdreams.galleryofdreams.backend.GalleryOfDreamsBackend -import net.perfectdreams.galleryofdreams.backend.routes.RequiresAPIAuthenticationRoute -import net.perfectdreams.galleryofdreams.backend.tables.FanArtArtists -import net.perfectdreams.galleryofdreams.backend.tables.FanArtTags -import net.perfectdreams.galleryofdreams.backend.tables.FanArts -import net.perfectdreams.galleryofdreams.backend.tables.connections.FanArtArtistDeviantArtConnections -import net.perfectdreams.galleryofdreams.backend.tables.connections.FanArtArtistDiscordConnections -import net.perfectdreams.galleryofdreams.backend.tables.connections.FanArtArtistTwitterConnections -import net.perfectdreams.galleryofdreams.backend.utils.AuthorizationToken -import net.perfectdreams.galleryofdreams.backend.utils.exposed.respondJson -import net.perfectdreams.galleryofdreams.common.data.DeviantArtSocialConnection -import net.perfectdreams.galleryofdreams.common.data.DiscordSocialConnection -import net.perfectdreams.galleryofdreams.common.data.FanArt -import net.perfectdreams.galleryofdreams.common.data.TwitterSocialConnection -import net.perfectdreams.galleryofdreams.common.data.api.CreateArtistWithFanArtRequest -import net.perfectdreams.galleryofdreams.common.data.api.UploadFanArtResponse -import org.jetbrains.exposed.sql.insert - -class PostArtistWithFanArtRoute(m: GalleryOfDreamsBackend) : RequiresAPIAuthenticationRoute(m, "/api/v1/artists") { - override suspend fun onAuthenticatedRequest(call: ApplicationCall, token: AuthorizationToken) { - val (fanArtArtist, response) = withContext(Dispatchers.IO) { - // Receive the uploaded file - val multipart = call.receiveMultipart() - - var attributesString: String? = null - var contentType: ContentType? = null - var fileToBeStored: ByteArray? = null - - multipart.forEachPart { part -> - when (part) { - is PartData.FormItem -> { - if (part.name == "file") - attributesString = part.value - } - - is PartData.FileItem -> { - if (part.name == "file") { - contentType = part.contentType - fileToBeStored = part.provider().readRemaining().readByteArray() - } - } - - else -> {} - } - part.dispose() - } - - val attributes = Json.decodeFromString(attributesString!!) - - val uploadResult = m.dreamStorageServiceClient.uploadImage( - fileToBeStored!!, - contentType!!, - UploadImageRequest(false) - ) - - val r = m.dreamStorageServiceClient.createImageLink( - CreateImageLinkRequest( - uploadResult.info.imageId, - "fan-arts", - "%s" - ) - ) - - val (fanArtArtist, fanArt, tags) = m.transaction { - val fanArtArtist = FanArtArtists.insert { - it[FanArtArtists.name] = attributes.name - it[FanArtArtists.slug] = attributes.slug - } - - attributes.socialConnections.forEach { socialConnection -> - when (socialConnection) { - is DeviantArtSocialConnection -> { - FanArtArtistDeviantArtConnections.insert { - it[FanArtArtistDeviantArtConnections.artist] = fanArtArtist[FanArtArtists.id] - it[FanArtArtistDeviantArtConnections.handle] = socialConnection.handle - } - } - is DiscordSocialConnection -> { - FanArtArtistDiscordConnections.insert { - it[FanArtArtistDiscordConnections.artist] = fanArtArtist[FanArtArtists.id] - it[FanArtArtistDiscordConnections.discordId] = socialConnection.id - } - } - is TwitterSocialConnection -> { - FanArtArtistTwitterConnections.insert { - it[FanArtArtistTwitterConnections.artist] = fanArtArtist[FanArtArtists.id] - it[FanArtArtistTwitterConnections.handle] = socialConnection.handle - } - } - } - } - - val fanArtData = attributes.fanArt - - val fanArt = FanArts.insert { - it[FanArts.slug] = fanArtData.slug - it[FanArts.title] = fanArtData.title - it[FanArts.description] = fanArtData.description - it[FanArts.artist] = fanArtArtist[FanArtArtists.id] - it[FanArts.createdAt] = fanArtData.createdAt - it[FanArts.dreamStorageServiceImageId] = uploadResult.info.imageId - it[FanArts.file] = r.link.file - it[FanArts.preferredMediaType] = contentType.toString() - } - - val tags = fanArtData.tags.map { tag -> - FanArtTags.insert { - it[FanArtTags.fanArt] = fanArt[FanArts.id] - it[FanArtTags.tag] = tag - } - } - - Triple(fanArtArtist, fanArt, tags) - } - - Pair( - fanArtArtist, - UploadFanArtResponse( - FanArt( - fanArt[FanArts.id].value, - fanArt[FanArts.slug], - fanArt[FanArts.title], - fanArt[FanArts.description], - fanArt[FanArts.createdAt], - fanArt[FanArts.dreamStorageServiceImageId], - fanArt[FanArts.file], - fanArt[FanArts.preferredMediaType], - tags.map { - it[FanArtTags.tag] - }, - ) - ) - ) - } - - GlobalScope.launch { - m.webhookClient?.send( - WebhookMessageBuilder() - // No mentions are allowed! - .setAllowedMentions(AllowedMentions.none()) - .setContent("<:gabriela_brush:727259143903248486> **Artista e Fan Art adicionados!** ${m.websiteUrl}/artists/${fanArtArtist[FanArtArtists.slug]}/${response.fanArt.slug}") - .build() - ) - } - - call.respondJson(response) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostCheckFanArtRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostCheckFanArtRoute.kt deleted file mode 100644 index ce596e6..0000000 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostCheckFanArtRoute.kt +++ /dev/null @@ -1,81 +0,0 @@ -package net.perfectdreams.galleryofdreams.backend.routes.api - -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.http.content.* -import io.ktor.server.request.* -import io.ktor.utils.io.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import kotlinx.io.readByteArray -import net.perfectdreams.dreamstorageservice.data.api.CheckImageRequest -import net.perfectdreams.dreamstorageservice.data.api.ImageDoesNotExistResponse -import net.perfectdreams.dreamstorageservice.data.api.ImageExistsResponse -import net.perfectdreams.galleryofdreams.backend.GalleryOfDreamsBackend -import net.perfectdreams.galleryofdreams.backend.routes.RequiresAPIAuthenticationRoute -import net.perfectdreams.galleryofdreams.backend.tables.FanArts -import net.perfectdreams.galleryofdreams.backend.utils.AuthorizationToken -import net.perfectdreams.galleryofdreams.backend.utils.exposed.respondJson -import net.perfectdreams.galleryofdreams.common.data.api.CheckFanArtResponse -import net.perfectdreams.galleryofdreams.common.data.api.FanArtDoesNotExistResponse -import net.perfectdreams.galleryofdreams.common.data.api.FanArtExistsResponse -import org.jetbrains.exposed.sql.select - -class PostCheckFanArtRoute(m: GalleryOfDreamsBackend) : RequiresAPIAuthenticationRoute(m, "/api/v1/fan-arts/check") { - override suspend fun onAuthenticatedRequest(call: ApplicationCall, token: AuthorizationToken) { - val response = withContext(Dispatchers.IO) { - // Receive the uploaded file - val multipart = call.receiveMultipart() - - var attributesString: String? = null - var contentType: ContentType? = null - var fileToBeStored: ByteArray? = null - - multipart.forEachPart { part -> - when (part) { - is PartData.FormItem -> { - if (part.name == "file") - attributesString = part.value - } - - is PartData.FileItem -> { - if (part.name == "file") { - contentType = part.contentType - fileToBeStored = part.provider().readRemaining().readByteArray() - } - } - - else -> {} - } - part.dispose() - } - - val checkResult = m.dreamStorageServiceClient.checkImage( - fileToBeStored!!, - contentType!!, - CheckImageRequest(false) - ) - - when (checkResult) { - is ImageExistsResponse -> { - // If the image exists, check if it is in our database - val fanArtCount = m.transaction { - FanArts.select { - FanArts.dreamStorageServiceImageId eq checkResult.imageId - }.count() - } - - if (fanArtCount != 0L) - FanArtExistsResponse() - else - FanArtDoesNotExistResponse() - } - is ImageDoesNotExistResponse -> { - FanArtDoesNotExistResponse() - } - } - } - - call.respondJson(response) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostFanArtRoute.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostFanArtRoute.kt deleted file mode 100644 index ade3959..0000000 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/routes/api/PostFanArtRoute.kt +++ /dev/null @@ -1,141 +0,0 @@ -package net.perfectdreams.galleryofdreams.backend.routes.api - -import club.minnced.discord.webhook.send.AllowedMentions -import club.minnced.discord.webhook.send.WebhookMessageBuilder -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.server.application.* -import io.ktor.server.request.* -import io.ktor.server.util.* -import io.ktor.utils.io.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import kotlinx.io.readByteArray -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json -import net.perfectdreams.dreamstorageservice.data.api.CreateImageLinkRequest -import net.perfectdreams.dreamstorageservice.data.api.UploadImageRequest -import net.perfectdreams.galleryofdreams.backend.GalleryOfDreamsBackend -import net.perfectdreams.galleryofdreams.backend.routes.RequiresAPIAuthenticationRoute -import net.perfectdreams.galleryofdreams.backend.tables.FanArtArtists -import net.perfectdreams.galleryofdreams.backend.tables.FanArtTags -import net.perfectdreams.galleryofdreams.backend.tables.FanArts -import net.perfectdreams.galleryofdreams.backend.utils.AuthorizationToken -import net.perfectdreams.galleryofdreams.backend.utils.exposed.respondJson -import net.perfectdreams.galleryofdreams.common.data.FanArt -import net.perfectdreams.galleryofdreams.common.data.api.UploadFanArtRequest -import net.perfectdreams.galleryofdreams.common.data.api.UploadFanArtResponse -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.select - -class PostFanArtRoute(m: GalleryOfDreamsBackend) : RequiresAPIAuthenticationRoute(m, "/api/v1/artists/{artistId}/fan-arts") { - override suspend fun onAuthenticatedRequest(call: ApplicationCall, token: AuthorizationToken) { - val artistId = call.parameters.getOrFail("artistId") - .toLong() - - val (fanArtArtist, response) = withContext(Dispatchers.IO) { - val fanArtArtist = m.transaction { - FanArtArtists.select { FanArtArtists.id eq artistId } - .limit(1) - .firstOrNull() - } ?: error("Artist with ID $artistId does not exist!") - - // Receive the uploaded file - val multipart = call.receiveMultipart() - - var attributesString: String? = null - var contentType: ContentType? = null - var fileToBeStored: ByteArray? = null - - multipart.forEachPart { part -> - when (part) { - is PartData.FormItem -> { - if (part.name == "file") - attributesString = part.value - } - - is PartData.FileItem -> { - if (part.name == "file") { - contentType = part.contentType - fileToBeStored = part.provider().readRemaining().readByteArray() - } - } - - else -> {} - } - part.dispose() - } - - val attributes = Json.decodeFromString(attributesString!!) - - val uploadResult = m.dreamStorageServiceClient.uploadImage( - fileToBeStored ?: error("Missing fileToBeStored!"), - contentType ?: error("Missing Content-Type!"), - UploadImageRequest(false) - ) - - val r = m.dreamStorageServiceClient.createImageLink( - CreateImageLinkRequest( - uploadResult.info.imageId, - "fan-arts", - "%s" - ) - ) - - val (fanArt, tags) = m.transaction { - val fanArt = FanArts.insert { - it[FanArts.slug] = attributes.slug - it[FanArts.title] = attributes.title - it[FanArts.description] = attributes.description - it[FanArts.artist] = fanArtArtist[FanArtArtists.id] - it[FanArts.createdAt] = attributes.createdAt - it[FanArts.dreamStorageServiceImageId] = uploadResult.info.imageId - it[FanArts.file] = r.link.file - it[FanArts.preferredMediaType] = contentType.toString() - } - - val tags = attributes.tags.map { tag -> - FanArtTags.insert { - it[FanArtTags.fanArt] = fanArt[FanArts.id] - it[FanArtTags.tag] = tag - } - } - - Pair(fanArt, tags) - } - - Pair( - fanArtArtist, - UploadFanArtResponse( - FanArt( - fanArt[FanArts.id].value, - fanArt[FanArts.slug], - fanArt[FanArts.title], - fanArt[FanArts.description], - fanArt[FanArts.createdAt], - fanArt[FanArts.dreamStorageServiceImageId], - fanArt[FanArts.file], - fanArt[FanArts.preferredMediaType], - tags.map { - it[FanArtTags.tag] - }, - ) - ) - ) - } - - GlobalScope.launch { - m.webhookClient?.send( - WebhookMessageBuilder() - // No mentions are allowed! - .setAllowedMentions(AllowedMentions.none()) - .setContent("<:gabriela_brush:727259143903248486> **Fan Art adicionada!** ${m.websiteUrl}/artists/${fanArtArtist[FanArtArtists.slug]}/${response.fanArt.slug}") - .build() - ) - } - - call.respondJson(response) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/utils/exposed/jsonb.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/utils/exposed/jsonb.kt deleted file mode 100644 index 5c07412..0000000 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/utils/exposed/jsonb.kt +++ /dev/null @@ -1,41 +0,0 @@ -package net.perfectdreams.galleryofdreams.backend.utils.exposed - -import org.jetbrains.exposed.sql.Column -import org.jetbrains.exposed.sql.ColumnType -import org.jetbrains.exposed.sql.Table -import org.jetbrains.exposed.sql.statements.api.PreparedStatementApi -import org.postgresql.util.PGobject - -fun Table.jsonb(name: String): Column - = registerColumn(name, JsonBinary()) - -class JsonBinary : ColumnType() { - override fun sqlType() = "jsonb" - - override fun setParameter(stmt: PreparedStatementApi, index: Int, value: Any?) { - val obj = PGobject() - obj.type = sqlType() - obj.value = value as String? - stmt[index] = obj - } - - override fun valueFromDB(value: Any): Any { - if (value !is PGobject) - return value - - return try { - return value.value!! - } catch (e: Exception) { - e.printStackTrace() - throw RuntimeException("Can't parse JSON: $value") - } - } - - override fun valueToString(value: Any?): String = when (value) { - is Iterable<*> -> nonNullValueToString(value) - else -> super.valueToString(value) - } - - @Suppress("UNCHECKED_CAST") - override fun notNullValueToDB(value: Any) = value -} \ No newline at end of file diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/ArtistFanArtsView.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/ArtistFanArtsView.kt index 196cda8..5bfe070 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/ArtistFanArtsView.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/ArtistFanArtsView.kt @@ -29,8 +29,6 @@ class ArtistFanArtsView( i18nContext: I18nContext, title: String, pathWithoutLocaleId: String, - dssBaseUrl: String, - namespace: String, private val artistSlug: String, private val fanArtSortOrder: FanArtSortOrder, private val fanArtTags: List?, @@ -38,7 +36,7 @@ class ArtistFanArtsView( private val fanArtArtist: FanArtArtistX, private val fanArts: List, private val totalFanArts: Long -) : DashboardView(m, i18nContext, title, pathWithoutLocaleId, dssBaseUrl, namespace) { +) : DashboardView(m, i18nContext, title, pathWithoutLocaleId) { override fun rightSidebar(): FlowContent.() -> (Unit) = { div { id = "fan-arts-wrapper" @@ -92,7 +90,7 @@ class ArtistFanArtsView( id = "fan-arts-grid-and-pagination" - fanArtCardGrid(m, i18nContext, dssBaseUrl, this@ArtistFanArtsView.namespace, fanArts.map { FanArtArtistWithFanArt(fanArtArtist, it) }) + fanArtCardGrid(m, i18nContext, fanArts.map { FanArtArtistWithFanArt(fanArtArtist, it) }) div { style = "text-align: center;" diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/DashboardView.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/DashboardView.kt index 97df679..18f72a7 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/DashboardView.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/DashboardView.kt @@ -18,9 +18,7 @@ abstract class DashboardView( val m: GalleryOfDreamsBackend, val i18nContext: I18nContext, val title: String, - val pathWithoutLocaleId: String, - val dssBaseUrl: String, - val namespace: String, + val pathWithoutLocaleId: String ) { fun generateHtml(totalFanArtCount: Long, fanArtistsSidebar: List): HTML.() -> (Unit) = { attributes["lang"] = i18nContext.get(I18nKeysData.WebsiteLocaleIdPath) @@ -28,8 +26,8 @@ abstract class DashboardView( head { meta(charset = "utf-8") meta(name = "viewport", content = "width=device-width, initial-scale=1, viewport-fit=cover") - // We are sure that we will access the DreamStorageService URL, so let's preconnect! - link(href = m.dreamStorageServiceClient.baseUrl, rel = "preconnect") + // We are sure that we will access the EtherealGambi URL, so let's preconnect! + link(href = "https://assets.perfectdreams.media/", rel = "preconnect") title(this@DashboardView.title) @@ -178,7 +176,7 @@ abstract class DashboardView( id = "fan-art-artists" for (fanArtist in fanArtistsSidebar) { - fanArtArtist(i18nContext, dssBaseUrl, this@DashboardView.namespace, fanArtist.fanArtArtist, fanArtist.fanArtCount) + fanArtArtist(i18nContext, fanArtist.fanArtArtist, fanArtist.fanArtCount) } div { diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/FanArtView.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/FanArtView.kt index 743167d..ad72b77 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/FanArtView.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/FanArtView.kt @@ -29,11 +29,9 @@ class FanArtView( i18nContext: I18nContext, title: String, pathWithoutLocaleId: String, - dssBaseUrl: String, - namespace: String, private val fanArtArtist: FanArtArtistX, private val fanArt: FanArt -) : DashboardView(m, i18nContext, title, pathWithoutLocaleId, dssBaseUrl, namespace) { +) : DashboardView(m, i18nContext, title, pathWithoutLocaleId) { override fun rightSidebar(): FlowContent.() -> (Unit) = { val extension = MediaTypeUtils.convertContentTypeToExtension(fanArt.preferredMediaType) val url = "https://assets.perfectdreams.media/galleryofdreams/fan-arts/${fanArt.file}" @@ -94,7 +92,7 @@ class FanArtView( meta(content = i18nContext.get(I18nKeysData.WebsiteTitle)) { attributes["property"] = "og:site_name" } - meta(content = m.dreamStorageServiceClient.baseUrl + "/${this@FanArtView.namespace}/${StoragePaths.FanArt("${fanArt.file}.${MediaTypeUtils.convertContentTypeToExtension(fanArt.preferredMediaType)}").join()}") { + meta(content = "https://assets.perfectdreams.media/galleryofdreams/fan-arts/${StoragePaths.FanArt("${fanArt.file}.${MediaTypeUtils.convertContentTypeToExtension(fanArt.preferredMediaType)}").join()}") { attributes["property"] = "og:image" } meta(name = "twitter:card", content = "summary_large_image") diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/FanArtsView.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/FanArtsView.kt index f4c6642..5f29838 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/FanArtsView.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/FanArtsView.kt @@ -23,14 +23,12 @@ class FanArtsView( i18nContext: I18nContext, title: String, pathWithoutLocaleId: String, - dssBaseUrl: String, - namespace: String, private val fanArtSortOrder: FanArtSortOrder, private val fanArtTags: List?, private val page: Int, private val fanArts: List, private val totalFanArts: Long -) : DashboardView(m, i18nContext, title, pathWithoutLocaleId, dssBaseUrl, namespace) { +) : DashboardView(m, i18nContext, title, pathWithoutLocaleId) { override fun rightSidebar(): FlowContent.() -> (Unit) = { div { id = "fan-arts-grid" @@ -78,7 +76,7 @@ class FanArtsView( } } - fanArtCardGrid(m, i18nContext, dssBaseUrl, this@FanArtsView.namespace, fanArts) + fanArtCardGrid(m, i18nContext, fanArts) div { style = "text-align: center;" diff --git a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/HomeView.kt b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/HomeView.kt index 1cdd6ad..62c3766 100644 --- a/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/HomeView.kt +++ b/backend/src/main/kotlin/net/perfectdreams/galleryofdreams/backend/views/HomeView.kt @@ -11,10 +11,8 @@ class HomeView( m: GalleryOfDreamsBackend, i18nContext: I18nContext, title: String, - pathWithoutLocaleId: String, - dssBaseUrl: String, - namespace: String, -) : DashboardView(m, i18nContext, title, pathWithoutLocaleId, dssBaseUrl, namespace) { + pathWithoutLocaleId: String +) : DashboardView(m, i18nContext, title, pathWithoutLocaleId) { override fun rightSidebar(): FlowContent.() -> (Unit) = { div { style = "text-align: center;"