Skip to content
This repository has been archived by the owner on Dec 3, 2024. It is now read-only.

Commit

Permalink
404 on unknown site, only update given values
Browse files Browse the repository at this point in the history
  • Loading branch information
x86-39 committed Nov 16, 2023
1 parent 2dfacda commit 01a96cc
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 53 deletions.
117 changes: 73 additions & 44 deletions src/main/kotlin/dev/queercoded/webring/SiteResource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ class SiteResource(val siteRepository: SiteRepository) {
@Location("index.html")
lateinit var index: Template

@Inject
@Location("nosuchdomain.html")
lateinit var noSuchDomain: Template

@RestHeader("Authorization")
lateinit var authHeader: String

Expand Down Expand Up @@ -58,15 +62,17 @@ class SiteResource(val siteRepository: SiteRepository) {

fun getNextSite(sourceDomain: String): Site {
val sites = listEnabledSites()
val site = sites.find { it.domain == sourceDomain } ?: return Site()
val site = sites.find { it.domain == sourceDomain }
?: throw NullPointerException("Source domain not found: $sourceDomain")
val index = sites.indexOf(site)
val nextIndex = if (index == sites.lastIndex) 0 else index + 1
return sites[nextIndex]
}

fun getPrevSite(sourceDomain: String): Site {
val sites = listEnabledSites()
val site = sites.find { it.domain == sourceDomain } ?: return Site()
val site = sites.find { it.domain == sourceDomain }
?: throw NullPointerException("Source domain not found: $sourceDomain")
val index = sites.indexOf(site)
val prevIndex = if (index == 0) sites.lastIndex else index - 1
return sites[prevIndex]
Expand All @@ -76,48 +82,66 @@ class SiteResource(val siteRepository: SiteRepository) {
@Produces(MediaType.APPLICATION_JSON)
@Path("/sites/next/{source_domain}")
fun getNextSiteJson(@PathParam("source_domain") sourceDomain: String): Response {
val site = getNextSite(sourceDomain)
return Response.ok(site).status(200).build()
return try {
val site = getNextSite(sourceDomain)
Response.ok(site).status(200).build()
} catch (e: NullPointerException) {
Response.status(Response.Status.NOT_FOUND).entity("Domain not found: $sourceDomain").build()
}
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/sites/prev/{source_domain}")
fun getPrevSiteJson(@PathParam("source_domain") sourceDomain: String): Response {
val site = getPrevSite(sourceDomain)
return Response.ok(site).status(200).build()
return try {
val site = getPrevSite(sourceDomain)
Response.ok(site).status(200).build()
} catch (e: NullPointerException) {
Response.status(Response.Status.NOT_FOUND).entity("Domain not found: $sourceDomain").build()
}
}

@GET
@Path("/next")
// Get the next site in order of creation. Find the given source_domain and return the next site. If the source_domain is the last site, return the first site.
fun gotoNextSite(@QueryParam("source") sourceDomain: String): Response {
val nextSite = getNextSite(sourceDomain)

// Send redirect to the next
// Vars are domain, https: Bool, path, build this into URL
return if (nextSite.https) {
Response.status(Response.Status.TEMPORARY_REDIRECT)
.location(URI("https://${nextSite.domain}${nextSite.path}")).build()
} else {
Response.status(Response.Status.TEMPORARY_REDIRECT)
.location(URI("http://${nextSite.domain}${nextSite.path}")).build()
return try {
val nextSite = getNextSite(sourceDomain)
if (nextSite.https) {
Response.status(Response.Status.TEMPORARY_REDIRECT)
.location(URI("https://${nextSite.domain}${nextSite.path}")).build()
} else {
Response.status(Response.Status.TEMPORARY_REDIRECT)
.location(URI("http://${nextSite.domain}${nextSite.path}")).build()
}
} catch (e: NullPointerException) {
serveNoSuchDomainTemplate(sourceDomain)
}
}

@GET
@Path("/prev")
// Get the previous site in order of creation. Find the given source_domain and return the previous site. If the source_domain is the first site, return the last site.
fun gotoPrevSite(@QueryParam("source") source_domain: String): Response {
val prevSite = getPrevSite(source_domain)

// Send redirect to the prev
// Vars are domain, https: Bool, path, build this into URL
return if (prevSite.https) {
Response.status(Response.Status.TEMPORARY_REDIRECT)
.location(URI("https://${prevSite.domain}${prevSite.path}")).build()
} else {
Response.status(Response.Status.TEMPORARY_REDIRECT)
.location(URI("http://${prevSite.domain}${prevSite.path}")).build()
fun gotoPrevSite(@QueryParam("source") sourceDomain: String): Response {
return try {
val prevSite = getPrevSite(sourceDomain)
if (prevSite.https) {
Response.status(Response.Status.TEMPORARY_REDIRECT)
.location(URI("https://${prevSite.domain}${prevSite.path}")).build()
} else {
Response.status(Response.Status.TEMPORARY_REDIRECT)
.location(URI("http://${prevSite.domain}${prevSite.path}")).build()
}
} catch (e: NullPointerException) {
serveNoSuchDomainTemplate(sourceDomain)
}
}

private fun serveNoSuchDomainTemplate(sourceDomain: String): Response {
// Assuming notFoundTemplate is accessible here
return noSuchDomain.data("domain", sourceDomain).render().let {
Response.status(Response.Status.NOT_FOUND).entity(it)
.header("Content-Type", "text/html")
.build()
}
}
// ADMIN API CALLS
Expand Down Expand Up @@ -229,34 +253,39 @@ class SiteResource(val siteRepository: SiteRepository) {
@Transactional
@Consumes(MediaType.APPLICATION_JSON)
@Path("/sites/id/{id}/update")
fun updateSiteById(@PathParam("id") id: Long, site: Site): Response {
fun updateSiteById(@PathParam("id") id: Long, site: HashMap<String, String>): Response {
// Check if authHeader is set
if (!checkIfAuthenticated()) {
return Response.ok().status(401).build()
}

// This hashmap may be incomplete, so check whether a value exists before checking it

// Check if site with new name exists
val siteWithNewName = siteRepository.findByName(site.name)
if (siteWithNewName != null && siteWithNewName.id != id) {
return Response.ok().status(409).build()
site["name"]?.let {
if (siteRepository.findByName(it) != null) {
return Response.ok().status(409).build()
}
}

// Check if site with new domain exists
val siteWithNewDomain = siteRepository.findBydomain(site.domain)
if (siteWithNewDomain != null && siteWithNewDomain.id != id) {
return Response.ok().status(409).build()
site["domain"]?.let {
if (siteRepository.findBydomain(it) != null) {
return Response.ok().status(409).build()
}
}

val oldSite = siteRepository.findById(id) ?: return Response.ok().status(404).build()
oldSite.name = site.name
oldSite.domain = site.domain
oldSite.path = site.path
oldSite.https = site.https
oldSite.author = site.author
oldSite.enabled = site.enabled
oldSite.disable_checks = site.disable_checks
oldSite.dead_end = site.dead_end

// Only update changed fields
site["name"]?.let { if (oldSite.name != it) oldSite.name = it }
site["domain"]?.let { if (oldSite.domain != it) oldSite.domain = it }
site["path"]?.let { if (oldSite.path != it) oldSite.path = it }
site["https"]?.let { it.toBoolean().let { value -> if (oldSite.https != value) oldSite.https = value } }
site["author"]?.let { if (oldSite.author != it) oldSite.author = it }
site["enabled"]?.let { it.toBoolean().let { value -> if (oldSite.enabled != value) oldSite.enabled = value } }
site["disable_checks"]?.let { it.toBoolean().let { value -> if (oldSite.disable_checks != value) oldSite.disable_checks = value } }
site["dead_end"]?.let { it.toBoolean().let { value -> if (oldSite.dead_end != value) oldSite.dead_end = value } }

siteRepository.persist(oldSite)
return Response.ok(oldSite).status(200).build()
Expand Down
30 changes: 30 additions & 0 deletions src/main/resources/templates/nosuchdomain.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Queer Coded Webring - Index</title>
<meta property="og:site_name" content="Queer Coded Webring" />
<meta property="og:title" content="Queer Coded Webring - 404" />
<meta property="og:type" content="website" />
<meta content="#00ff00" name="theme-color" />
<link rel="stylesheet" type="text/css" href="/css/index.css">
</head>
<body>
<div class="container">
<span class="top-container">
<div class="imgcontainer">
<img src="/logo.svg" alt="Queer Coded Logo" />
<img src="/logo.svg" alt="Queer Coded Logo" aria-hidden="true" />
</div>
<h1>Queer Coded</h1>
<h1>&lt;Webring&gt;</h1>
</span>
<div class="content">
<h2>404</h2>
<p>Sorry, we could not find a domain "{domain}".</p>
<p>Please contact us if you would like to join the webring</p>
<p>Maybe you were looking for <a href="/">the index</a>?</p>
</div>
</div>
</body>
</html>
59 changes: 50 additions & 9 deletions src/test/kotlin/dev/queercoded/webring/SiteResourceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class SiteResourceTest {
siteC.author = "Test User C"
siteC.path = "/"
siteC.enabled = false
siteC.https = false
siteC.disable_checks = true

initialSites = arrayListOf(siteA, siteB, siteC)
Expand Down Expand Up @@ -288,35 +289,75 @@ class SiteResourceTest {
}

@Test
@Order(18)
@Order(17)
fun testUpdateSiteEndpoint1() {
var siteC = given()
val siteC = given()
.`when`()
.header("Authorization", "Bearer ${api_token}")
.get("/sites/name/test-site-c.example.com")
.then()
.statusCode(200)
.extract().`as`(Site::class.java)

siteC.name = "Test Site C Updated"
siteC.domain = "test-site-c-updated.example.com"
siteC.author = "Test User C Updated"
siteC.path = "/updated/"
siteC.https = false
val updateBody = hashMapOf("name" to "Test Site C Updated")

given()
.`when`()
.contentType("application/json")
.body(siteC)
.body(updateBody)
.header("Authorization", "Bearer ${api_token}")
.put("/sites/id/${siteC.id}/update")
.then()
.statusCode(200)
.assertThat().body("name", org.hamcrest.Matchers.equalTo("Test Site C Updated"))
.assertThat().body("domain", org.hamcrest.Matchers.equalTo("test-site-c.example.com"))
.assertThat().body("author", org.hamcrest.Matchers.equalTo("Test User C"))
.assertThat().body("path", org.hamcrest.Matchers.equalTo("/"))
.assertThat().body("https", org.hamcrest.Matchers.equalTo(false))

}

@Test
@Order(18)
fun testUpdateSiteEndpoint2() {
val siteC = given()
.`when`()
.header("Authorization", "Bearer ${api_token}")
.get("/sites/name/test-site-c.example.com")
.then()
.statusCode(200)
.extract().`as`(Site::class.java)

val updateBody = hashMapOf(
"name" to "Test Site C Updated Again",
"domain" to "test-site-c-updated.example.com",
"author" to "Test User C Updated",
"path" to "/updated/",
"https" to true.toString() // Converting boolean to string
)

given()
.`when`()
.contentType("application/json")
.body(updateBody)
.header("Authorization", "Bearer ${api_token}")
.put("/sites/id/${siteC.id}/update")
.then()
.statusCode(200)
.assertThat().body("name", org.hamcrest.Matchers.equalTo("Test Site C Updated Again"))
.assertThat().body("domain", org.hamcrest.Matchers.equalTo("test-site-c-updated.example.com"))
.assertThat().body("author", org.hamcrest.Matchers.equalTo("Test User C Updated"))
.assertThat().body("path", org.hamcrest.Matchers.equalTo("/updated/"))
.assertThat().body("https", org.hamcrest.Matchers.equalTo(false))
.assertThat().body("https", org.hamcrest.Matchers.equalTo(true))
}

@Test
@Order(19)
fun testGetNextSiteEndpoint3() {
given()
.`when`()
.get("/sites/prev/test-site-nonexistent.example.com")
.then()
.statusCode(404)
}
}

0 comments on commit 01a96cc

Please sign in to comment.