Skip to content

Commit

Permalink
GH-1903 Generate checksums for manually deployed files through UI (Re…
Browse files Browse the repository at this point in the history
…solve #1903)
  • Loading branch information
dzikoysk committed Aug 8, 2023
1 parent 90f4688 commit 830d72c
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.reposilite.maven

import com.reposilite.maven.api.METADATA_FILE
import com.reposilite.maven.api.REPOSITORY_NAME_MAX_LENGTH
import com.reposilite.shared.ErrorResponse
import com.reposilite.storage.StorageProvider
Expand All @@ -39,8 +40,13 @@ class Repository internal constructor(
check(name.length < REPOSITORY_NAME_MAX_LENGTH) { "Repository name cannot exceed $REPOSITORY_NAME_MAX_LENGTH characters" }
}

@Suppress("unused")
fun writeFileChecksums(location: Location, bytes: ByteArray): Result<Unit, ErrorResponse> {
fun acceptsDeploymentOf(location: Location): Boolean =
redeployment || location.getSimpleName().contains(METADATA_FILE) || !storageProvider.exists(location)

fun writeFileChecksums(location: Location, bytes: ByteArray): Result<Unit, ErrorResponse> =
writeFileChecksums(location, bytes.inputStream())

fun writeFileChecksums(location: Location, bytes: InputStream): Result<Unit, ErrorResponse> {
val md5 = location.resolveSibling(location.getSimpleName() + ".md5")
val sha1 = location.resolveSibling(location.getSimpleName() + ".sha1")
val sha256 = location.resolveSibling(location.getSimpleName() + ".sha256")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import com.reposilite.storage.api.SimpleDirectoryInfo
import com.reposilite.token.AccessTokenIdentifier
import panda.std.Result
import panda.std.asSuccess
import panda.std.ok
import java.io.InputStream

internal class RepositoryService(
Expand Down Expand Up @@ -70,30 +71,41 @@ internal class RepositoryService(
".asc"
)

fun deployFile(deployRequest: DeployRequest): Result<Unit, ErrorResponse> {
val (repository, path) = deployRequest

if (repository.redeployment.not() && !path.getSimpleName().contains(METADATA_FILE) && repository.storageProvider.exists(path)) {
return badRequestError("Redeployment is not allowed")
}

return repository.storageProvider.putFile(path, deployRequest.content).peek {
logger.info("DEPLOY | Artifact $path successfully deployed to ${repository.name} by ${deployRequest.by}")
extensions.emitEvent(DeployEvent(repository, path, deployRequest.by))
}
}

fun deleteFile(deleteRequest: DeleteRequest): Result<Unit, ErrorResponse> {
val (accessToken, repository, path) = deleteRequest

if (!securityProvider.canModifyResource(accessToken, repository, path)) {
return unauthorizedError("Unauthorized access request")
fun deployFile(deployRequest: DeployRequest): Result<Unit, ErrorResponse> =
with(deployRequest) {
when {
repository.acceptsDeploymentOf(gav) ->
repository.storageProvider
.putFile(gav, deployRequest.content)
.peek { logger.info("DEPLOY | Artifact $gav successfully deployed to ${repository.name} by ${deployRequest.by}") }
.peek { extensions.emitEvent(DeployEvent(repository, gav, deployRequest.by)) }
.flatMap { _ ->
when {
deployRequest.generateChecksums ->
repository.storageProvider
.getFile(gav)
.peek { logger.info("DEPLOY | Generating checksums for $gav") }
.flatMap { repository.writeFileChecksums(gav, it) }
else -> {
logger.debug("DEPLOY | Skipping checksums generation for $gav")
ok()
}
}
}
else -> badRequestError("Redeployment is not allowed")
}
}

return repository.storageProvider.removeFile(path).peek {
logger.info("DELETE | File $path has been deleted from ${repository.name} by ${deleteRequest.by}")
fun deleteFile(deleteRequest: DeleteRequest): Result<Unit, ErrorResponse> =
with(deleteRequest) {
when {
securityProvider.canModifyResource(accessToken, repository, gav) ->
repository.storageProvider
.removeFile(gav)
.peek { logger.info("DELETE | File $gav has been deleted from ${repository.name} by ${deleteRequest.by}") }
else -> unauthorizedError("Unauthorized access request")
}
}
}

fun findDetails(lookupRequest: LookupRequest): Result<out FileDetails, ErrorResponse> =
resolve(lookupRequest) { repository, gav -> findDetails(lookupRequest.accessToken, repository, gav) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ data class DeployRequest(
val repository: Repository,
val gav: Location,
val by: String,
val content: InputStream
val content: InputStream,
val generateChecksums: Boolean
)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import io.javalin.openapi.OpenApiContent
import io.javalin.openapi.OpenApiParam
import io.javalin.openapi.OpenApiResponse

const val X_GENERATE_CHECKSUMS = "X-Generate-Checksums"

internal class MavenEndpoints(
mavenFacade: MavenFacade,
private val frontendFacade: FrontendFacade,
Expand Down Expand Up @@ -80,6 +82,9 @@ internal class MavenEndpoints(
OpenApiParam(name = "repository", description = "Destination repository", required = true),
OpenApiParam(name = "gav", description = "Artifact path qualifier", required = true)
],
headers = [
OpenApiParam(name = X_GENERATE_CHECKSUMS, description = "Determines if Reposilite should generate checksums for this file", required = false)
],
responses = [
OpenApiResponse(status = "200", description = "Input stream of requested file", content = [OpenApiContent(type = FORM_DATA_MULTIPART)]),
OpenApiResponse(status = "401", description = "Returns 401 for invalid credentials"),
Expand All @@ -90,7 +95,7 @@ internal class MavenEndpoints(
authorized {
requireGav { gav ->
requireRepository { repository ->
response = DeployRequest(repository, gav, getSessionIdentifier(), ctx.bodyInputStream())
response = DeployRequest(repository, gav, getSessionIdentifier(), ctx.bodyInputStream(), ctx.header(X_GENERATE_CHECKSUMS) == "true")
.let { request -> mavenFacade.deployFile(request) }
.onError { error -> logger.debug("Cannot deploy artifact due to: ${error.message}") }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,7 @@ data class Location private constructor(private val uri: String) {
"$uri/${subLocation.uri}".toLocation()

fun resolveSibling(sibling: String): Location =
uri.substringBeforeLast("/")
.toLocation()
.resolve(sibling)
getParent().resolve(sibling)

fun replace(element: String, replacement: String): Location =
uri.replace(element, replacement).toLocation()
Expand Down
7 changes: 6 additions & 1 deletion reposilite-frontend/src/components/browser/FileUpload.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const to = ref(defaultTo)
const destination = computed(() => `${repository.value}/${to.value.replace(/(^\/+)|(\/+$)/g, '')}`)
const customDestination = ref(false)
const checksumsEnabled = ref(false)
const stubPomEnabled = ref(false)
const artifactId = ref('')
const groupId = ref('')
Expand Down Expand Up @@ -80,7 +81,7 @@ const uploadFiles = () => {
}
files.value.forEach(vueFile =>
client.value.maven.deploy(`${destination.value}/${vueFile.name}`, vueFile.file)
client.value.maven.deploy(`${destination.value}/${vueFile.name}`, vueFile.file, checksumsEnabled.value)
.then(() => createSuccessToast(`File ${vueFile.name} has been uploaded`))
.then(() => removeFile(vueFile))
.then(() => refreshQualifier())
Expand Down Expand Up @@ -139,6 +140,10 @@ const uploadFiles = () => {
</div>
</div>
<div class="px-6 pb-4">
<div>
<input type="checkbox" v-model="checksumsEnabled" class="mb-1 ml-1 dark:bg-gray-900" />
<span class="pl-3" @click="checksumsEnabled = !checksumsEnabled" >Generate default checksums</span>
</div>
<div>
<input type="checkbox" v-model="stubPomEnabled" class="mb-1 ml-1 dark:bg-gray-900" />
<span class="pl-3" @click="stubPomEnabled = !stubPomEnabled" >Generate stub POM file</span>
Expand Down
50 changes: 24 additions & 26 deletions reposilite-frontend/src/store/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,6 @@ const createClient = (defaultName, defaultSecret) => {
...(credentials || defaultAuthorization()),
})

const del = (endpoint, credentials) =>
axios.delete(createURL(endpoint), {
...(credentials || defaultAuthorization()),
})

const post = (endpoint, data, credentials) =>
axios.post(createURL(endpoint), data, {
...(credentials || defaultAuthorization()),
})

const put = (endpoint, content, credentials) =>
axios.put(createURL(endpoint), content, {
headers: {
"Content-Type": "text/plain",
...(credentials || defaultAuthorization()).headers,
},
})

return {
auth: {
me() {
Expand Down Expand Up @@ -90,18 +72,34 @@ const createClient = (defaultName, defaultSecret) => {
...defaultAuthorization(),
})
},
deploy(gav, file) {
return put(`/${gav}`, file)
deploy(gav, file, generateChecksums) {
return axios.put(createURL(`/${gav}`), file, {
headers: {
"Content-Type": "application/octet-stream",
"X-Generate-Checksums": generateChecksums,
...defaultAuthorization().headers,
}
})
},
generatePom(gav, groupId, artifactId, version) {
return post(`/api/maven/generate/pom/${gav}/${artifactId}-${version}.pom`, {
groupId,
artifactId,
version
})
return axios.post(
createURL(`/api/maven/generate/pom/${gav}/${artifactId}-${version}.pom`),
{
groupId,
artifactId,
version
}, {
headers: {
"Content-Type": "application/json",
...defaultAuthorization().headers,
}
}
)
},
delete(gav) {
return del(`/${gav}`)
return axios.delete(createURL(`/${gav}`), {
...defaultAuthorization(),
})
}
},
settings: {
Expand Down

0 comments on commit 830d72c

Please sign in to comment.