├── .checkstyle ├── checkstyle.xml └── suppressions.xml ├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── build.yml │ ├── deploy.yml │ └── validate-gradle-wrapper.yml ├── .gitignore ├── .spotless └── bibliothek.importorder ├── build.gradle.kts ├── cli ├── insertBuild.js ├── package.json ├── promoteBuild.js └── yarn.lock ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── license.txt ├── license_header.txt ├── readme.md ├── renovate.json ├── settings.gradle.kts └── src └── main ├── java └── io │ └── papermc │ └── bibliothek │ ├── BibliothekApplication.java │ ├── configuration │ ├── AppConfiguration.java │ ├── MongoConfiguration.java │ ├── OpenAPIConfiguration.java │ └── WebConfiguration.java │ ├── controller │ ├── RootController.java │ └── v2 │ │ ├── DownloadController.java │ │ ├── ProjectController.java │ │ ├── ProjectsController.java │ │ ├── VersionBuildController.java │ │ ├── VersionBuildsController.java │ │ ├── VersionController.java │ │ ├── VersionFamilyBuildsController.java │ │ └── VersionFamilyController.java │ ├── database │ ├── model │ │ ├── Build.java │ │ ├── Project.java │ │ ├── Version.java │ │ └── VersionFamily.java │ └── repository │ │ ├── BuildCollection.java │ │ ├── ProjectCollection.java │ │ ├── VersionCollection.java │ │ └── VersionFamilyCollection.java │ ├── exception │ ├── Advice.java │ ├── BuildNotFound.java │ ├── DownloadFailed.java │ ├── DownloadNotFound.java │ ├── ProjectNotFound.java │ └── VersionNotFound.java │ ├── filter │ └── CorsFilter.java │ └── util │ ├── BringOrderToChaos.java │ ├── HTTP.java │ ├── MediaTypes.java │ ├── NameSource.java │ └── TimeSource.java └── resources └── application.yaml /.checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /.checkstyle/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # vim: ft=gitignore 2 | # Everything ignored by default 3 | * 4 | # Only whitelist what's needed 5 | !*.gradle.kts 6 | !docker/ 7 | !gradle/ 8 | !gradlew 9 | !license*.txt 10 | !src/ 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 2 6 | indent_style = space 7 | insert_final_newline = true 8 | max_line_length = off 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.bat text eol=crlf 4 | *.sh text eol=lf 5 | 6 | gradlew text eol=lf 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "build" 2 | 3 | on: ["pull_request", "push"] 4 | 5 | jobs: 6 | build: 7 | runs-on: "ubuntu-latest" 8 | steps: 9 | - name: "checkout repository" 10 | uses: "actions/checkout@v4" 11 | - name: "setup java" 12 | uses: "actions/setup-java@v4" 13 | with: 14 | distribution: "zulu" 15 | java-version: "17" 16 | - name: "build" 17 | run: "./gradlew build" 18 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: "deploy" 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | 7 | concurrency: 8 | cancel-in-progress: true 9 | group: "${{ github.workflow }}-${{ github.event.number || github.ref }}" 10 | 11 | jobs: 12 | deploy: 13 | runs-on: "ubuntu-latest" 14 | outputs: 15 | image-json: "${{ steps.publish.outputs.imageJson }}" # output produced from printJibMeta Gradle task 16 | steps: 17 | - name: "checkout" 18 | uses: "actions/checkout@v4" 19 | - name: "setup java" 20 | uses: "actions/setup-java@v4" 21 | with: 22 | distribution: "zulu" 23 | java-version: "17" 24 | - name: "check" 25 | run: ./gradlew check 26 | - name: "login" 27 | uses: "docker/login-action@v3" 28 | with: 29 | registry: "ghcr.io" 30 | username: "${{ github.actor }}" 31 | password: "${{ secrets.GITHUB_TOKEN }}" 32 | - name: "publish" 33 | id: publish 34 | run: "./gradlew jib -Djib.console=plain" 35 | promote: 36 | needs: "deploy" 37 | runs-on: "ubuntu-latest" 38 | environment: "promoted" 39 | steps: 40 | - name: "login" 41 | uses: "docker/login-action@v3" 42 | with: 43 | registry: "ghcr.io" 44 | username: "${{ github.actor }}" 45 | password: "${{ secrets.GITHUB_TOKEN }}" 46 | - name: "tag" 47 | env: 48 | IMAGE_ID: "${{ fromJSON(needs.deploy.outputs.image-json).imageId }}" 49 | IMAGE_NAME: "${{ fromJSON(needs.deploy.outputs.image-json).image }}" 50 | PROMOTED_TAG_NAME: "stable" 51 | run: | 52 | skopeo copy -a "docker://$IMAGE_NAME@$IMAGE_ID" "docker://$IMAGE_NAME:$PROMOTED_TAG_NAME" 53 | -------------------------------------------------------------------------------- /.github/workflows/validate-gradle-wrapper.yml: -------------------------------------------------------------------------------- 1 | name: "validate gradle wrapper" 2 | 3 | on: ["pull_request", "push"] 4 | 5 | jobs: 6 | build: 7 | runs-on: "ubuntu-latest" 8 | steps: 9 | - name: "checkout repository" 10 | uses: "actions/checkout@v4" 11 | - name: "validate gradle wrapper" 12 | uses: "gradle/wrapper-validation-action@v1" 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle/ 2 | /.idea/ 3 | /build/ 4 | /cli/node_modules/ 5 | /run/ 6 | /*.iml 7 | -------------------------------------------------------------------------------- /.spotless/bibliothek.importorder: -------------------------------------------------------------------------------- 1 | #Organize Import Order 2 | #Mon Dec 05 23:26:49 PST 2022 3 | 0= 4 | 1=\# 5 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.nio.file.StandardOpenOption 2 | import java.time.Instant 3 | import kotlin.io.path.Path 4 | import kotlin.io.path.bufferedWriter 5 | 6 | buildscript { 7 | dependencies { 8 | classpath("com.google.cloud.tools:jib-spring-boot-extension-gradle:0.1.0") 9 | } 10 | } 11 | 12 | plugins { 13 | id("java") 14 | 15 | alias(libs.plugins.indra) 16 | alias(libs.plugins.indra.checkstyle) 17 | alias(libs.plugins.indra.git) 18 | alias(libs.plugins.jib) 19 | alias(libs.plugins.spotless) 20 | alias(libs.plugins.spring.dependency.management) 21 | alias(libs.plugins.spring.boot) 22 | } 23 | 24 | repositories { 25 | mavenCentral() 26 | } 27 | 28 | indra { 29 | javaVersions { 30 | target(17) 31 | } 32 | 33 | github("PaperMC", "bibliothek") 34 | mitLicense() 35 | } 36 | 37 | spotless { 38 | java { 39 | endWithNewline() 40 | importOrderFile(rootProject.file(".spotless/bibliothek.importorder")) 41 | indentWithSpaces(2) 42 | licenseHeaderFile(rootProject.file("license_header.txt")) 43 | trimTrailingWhitespace() 44 | } 45 | } 46 | 47 | jib { 48 | to { 49 | image = "ghcr.io/papermc/bibliothek" 50 | tags = setOf( 51 | "latest", 52 | "${indraGit.branchName()}-${indraGit.commit()?.name()?.take(8)}-${Instant.now().epochSecond}" 53 | ) 54 | } 55 | 56 | from { 57 | image = "azul/zulu-openjdk-alpine:${indra.javaVersions().target().get()}-jre" 58 | platforms { 59 | // We can only build multi-arch images when pushing to a registry, not when building locally 60 | val requestedTasks = gradle.startParameter.taskNames 61 | if ("jibBuildTar" in requestedTasks || "jibDockerBuild" in requestedTasks) { 62 | platform { 63 | // todo: better logic 64 | architecture = when (System.getProperty("os.arch")) { 65 | "aarch64" -> "arm64" 66 | else -> "amd64" 67 | } 68 | os = "linux" 69 | } 70 | } else { 71 | platform { 72 | architecture = "amd64" 73 | os = "linux" 74 | } 75 | platform { 76 | architecture = "arm64" 77 | os = "linux" 78 | } 79 | } 80 | } 81 | } 82 | 83 | pluginExtensions { 84 | pluginExtension { 85 | implementation = "com.google.cloud.tools.jib.gradle.extension.springboot.JibSpringBootExtension" 86 | } 87 | } 88 | 89 | container { 90 | args = listOf("--spring.config.additional-location=optional:file:/config/") 91 | ports = listOf("8080") 92 | labels.put("org.opencontainers.image.source", indra.scm().map { it.url() }) 93 | } 94 | } 95 | 96 | dependencies { 97 | annotationProcessor("org.springframework.boot", "spring-boot-configuration-processor") 98 | checkstyle(libs.stylecheck) 99 | implementation(libs.jetbrains.annotations) 100 | implementation(libs.springdoc.openapi.starter.webmvc.ui) 101 | implementation("org.springframework.boot", "spring-boot-starter-data-mongodb") 102 | implementation("org.springframework.boot", "spring-boot-starter-validation") 103 | implementation("org.springframework.boot", "spring-boot-starter-web") 104 | implementation("org.springframework.boot", "spring-boot-starter-actuator") 105 | testImplementation("org.springframework.boot", "spring-boot-starter-test") { 106 | exclude(group = "org.junit.vintage", module = "junit-vintage-engine") 107 | } 108 | } 109 | 110 | tasks { 111 | val outputImageId = register("printJibMeta") { 112 | description = "Expose image information as an output for GitHub Actions" 113 | 114 | val jibImageJson = project.jib.outputPaths.imageJson 115 | val githubOutput = providers.environmentVariable("GITHUB_OUTPUT") 116 | inputs.property("jibImageJson", jibImageJson) 117 | inputs.property("githubOutput", githubOutput).optional(true) 118 | 119 | doLast { 120 | if (!githubOutput.isPresent) { 121 | didWork = false 122 | return@doLast 123 | } 124 | 125 | Path(githubOutput.get()).bufferedWriter(Charsets.UTF_8, options = arrayOf(StandardOpenOption.CREATE, StandardOpenOption.APPEND)).use { 126 | it.write("imageJson=") 127 | file(jibImageJson).bufferedReader(Charsets.UTF_8).use { meta -> meta.transferTo(it) } 128 | } 129 | } 130 | } 131 | 132 | sequenceOf(jib, jibDockerBuild, jibBuildTar).forEach { 133 | it.configure { 134 | finalizedBy(outputImageId.name) 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /cli/insertBuild.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const gitlog = require("gitlog").default; 3 | const {MongoClient} = require("mongodb"); 4 | const path = require("path"); 5 | const yargs = require("yargs"); 6 | 7 | const argv = yargs 8 | .option("projectName", optionOf("string")) 9 | .option("projectFriendlyName", optionOf("string")) 10 | .option("versionGroupName", optionOf("string")) 11 | .option("versionName", optionOf("string")) 12 | .option("buildNumber", optionOf("number")) 13 | .option("repositoryPath", optionOf("string")) 14 | .option("storagePath", optionOf("string")) 15 | .option("download", optionOf("string")) 16 | .option("buildChannel", optionOf("string", false)) 17 | .default("buildChannel", "default") 18 | .help() 19 | .version(false) 20 | .argv; 21 | 22 | const projectName = argv.projectName; 23 | const projectFriendlyName = argv.projectFriendlyName; 24 | const versionGroupName = argv.versionGroupName; 25 | const versionName = argv.versionName; 26 | const buildNumber = argv.buildNumber; 27 | const repositoryPath = argv.repositoryPath; 28 | const storagePath = argv.storagePath; 29 | // type:path:hash:name 30 | let downloads = argv.download; 31 | const buildChannel = argv.buildChannel.toUpperCase(); 32 | 33 | // Validate buildChannel 34 | if (buildChannel !== "DEFAULT" && buildChannel !== "EXPERIMENTAL") { 35 | console.log(`Invalid buildChannel: ${buildChannel}`); 36 | return; 37 | } 38 | 39 | if(typeof downloads === "string") { 40 | const tempDownloads = downloads; 41 | downloads = [tempDownloads]; 42 | } 43 | 44 | // Validate downloads 45 | let foundPrimary = false; 46 | for(let download of downloads) { 47 | const info = download.split(":"); 48 | if(info.length === 3) { 49 | if(foundPrimary === true) { 50 | console.log("Too many primary files."); 51 | return; 52 | } else { 53 | foundPrimary = true; 54 | } 55 | } 56 | } 57 | 58 | // ---------------------------------------------------------------------------------------------------- 59 | 60 | const downloadsPath = path.join( 61 | storagePath, 62 | projectName, 63 | versionName, 64 | buildNumber.toString() 65 | ); 66 | 67 | if(!fs.existsSync(downloadsPath)) { 68 | fs.mkdirSync(downloadsPath, { 69 | recursive: true 70 | }); 71 | } 72 | 73 | for(let download of downloads) { 74 | const info = download.split(":"); 75 | if(info.length === 3) { 76 | const downloadPath = path.join( 77 | downloadsPath, 78 | projectName + "-" + versionName + "-" + buildNumber + ".jar" 79 | ); 80 | fs.copyFileSync(info[1], downloadPath); 81 | } else if(info.length === 4) { 82 | const downloadPath = path.join( 83 | downloadsPath, 84 | info[3] 85 | ); 86 | fs.copyFileSync(info[1], downloadPath); 87 | } 88 | } 89 | 90 | const client = new MongoClient(process.env.MONGODB_URL || "mongodb://localhost:27017", { 91 | useUnifiedTopology: true 92 | }); 93 | 94 | async function run() { 95 | try { 96 | await client.connect(); 97 | const database = client.db("library"); // "library" instead of "bibliothek" is intentional here 98 | const project = await database.collection("projects").findOneAndUpdate( 99 | {"name": projectName}, 100 | { 101 | $setOnInsert: { 102 | "name": projectName, 103 | "friendlyName": projectFriendlyName 104 | } 105 | }, 106 | { 107 | new: true, 108 | returnDocument: "after", 109 | upsert: true 110 | } 111 | ); 112 | const versionGroup = await database.collection("version_groups").findOneAndUpdate( 113 | { 114 | "project": project.value._id, 115 | "name": versionGroupName 116 | }, 117 | { 118 | $setOnInsert: { 119 | "project": project.value._id, 120 | "name": versionGroupName 121 | } 122 | }, 123 | { 124 | new: true, 125 | returnDocument: "after", 126 | upsert: true 127 | } 128 | ); 129 | const version = await database.collection("versions").findOneAndUpdate( 130 | { 131 | "project": project.value._id, 132 | "name": versionName 133 | }, 134 | { 135 | $setOnInsert: { 136 | "project": project.value._id, 137 | "group": versionGroup.value._id, 138 | "name": versionName 139 | } 140 | }, 141 | { 142 | new: true, 143 | returnDocument: "after", 144 | upsert: true 145 | } 146 | ); 147 | const oldBuild = await database.collection("builds").findOne({ 148 | "project": project.value._id, 149 | "version": version.value._id 150 | }, {sort: {_id: -1}}); 151 | let changes = []; 152 | const lastBuild = oldBuild && oldBuild.changes.length ? oldBuild.changes.slice(0, 1)[0].commit : "HEAD^1"; 153 | const commits = gitlog({ 154 | repo: repositoryPath, 155 | fields: ["hash", "subject", "rawBody"], 156 | branch: lastBuild + "...HEAD" 157 | }); 158 | commits.forEach(function (commit) { 159 | changes.push({ 160 | "commit": commit.hash, 161 | "summary": commit.subject, 162 | "message": commit.rawBody 163 | }); 164 | }); 165 | const buildDownloads = {}; 166 | for(let download of downloads) { 167 | const info = download.split(":"); 168 | if(info.length === 3) { 169 | buildDownloads[info[0].replace(".", ":")] = { 170 | "name": projectName + "-" + versionName + "-" + buildNumber + ".jar", 171 | "sha256": info[2] 172 | }; 173 | } else if(info.length === 4) { 174 | buildDownloads[info[0].replace(".", ":")] = { 175 | "name": info[3], 176 | "sha256": info[2] 177 | }; 178 | } 179 | } 180 | const build = await database.collection("builds").insertOne({ 181 | "project": project.value._id, 182 | "version": version.value._id, 183 | "number": buildNumber, 184 | "time": new Date(), 185 | "changes": changes, 186 | "downloads": buildDownloads, 187 | "promoted": false, 188 | "channel": buildChannel 189 | }); 190 | console.log("Inserted build " + buildNumber + " (channel: " + buildChannel + ") for project " + project.value.name + " (" + project.value._id + ") version " + version.value.name + " (" + version.value._id + "): " + build.insertedId); 191 | } finally { 192 | await client.close(); 193 | } 194 | } 195 | 196 | run().catch(console.dir); 197 | 198 | function optionOf(type, required = true) { 199 | return { 200 | type: type, 201 | required: required 202 | }; 203 | } 204 | -------------------------------------------------------------------------------- /cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ruler-cli", 3 | "version": "1.0.0-SNAPSHOT", 4 | "main": "insertBuild.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "gitlog": "^4.0.4", 8 | "mongodb": "^4.2.1", 9 | "yargs": "^17.7.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /cli/promoteBuild.js: -------------------------------------------------------------------------------- 1 | const yargs = require("yargs"); 2 | const {MongoClient} = require("mongodb"); 3 | const ObjectId = require("mongodb").ObjectId; 4 | 5 | const argv = yargs 6 | .option("build", optionOf("string")) 7 | .option("promoted", optionOf("boolean")) 8 | .help() 9 | .alias("help", "h") 10 | .version(false) 11 | .argv; 12 | 13 | const client = new MongoClient("mongodb://localhost:27017", { 14 | useUnifiedTopology: true 15 | }); 16 | 17 | async function run() { 18 | try { 19 | await client.connect(); 20 | const database = client.db("library"); 21 | 22 | const build = await database.collection("builds").findOne({ 23 | _id: ObjectId(argv.build) 24 | }); 25 | 26 | if (!build) { 27 | console.log(`Build not found with ID ${argv.build}`); 28 | return; 29 | } 30 | 31 | await database.collection("builds").updateOne( 32 | { 33 | _id: ObjectId(argv.build) 34 | }, 35 | { 36 | $set: { 37 | promoted: argv.promoted === true 38 | } 39 | } 40 | ); 41 | 42 | console.log(`Build ${argv.build} promotion state changed to: ${argv.promoted}`); 43 | } finally { 44 | await client.close(); 45 | } 46 | } 47 | 48 | run().catch(console.dir); 49 | 50 | function optionOf(type) { 51 | return { 52 | type, 53 | required: true 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /cli/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/node@*": 6 | version "16.11.10" 7 | resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.10.tgz#2e3ad0a680d96367103d3e670d41c2fed3da61ae" 8 | integrity sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA== 9 | 10 | "@types/webidl-conversions@*": 11 | version "6.1.1" 12 | resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz#e33bc8ea812a01f63f90481c666334844b12a09e" 13 | integrity sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q== 14 | 15 | "@types/whatwg-url@^8.2.1": 16 | version "8.2.1" 17 | resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.1.tgz#f1aac222dab7c59e011663a0cb0a3117b2ef05d4" 18 | integrity sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ== 19 | dependencies: 20 | "@types/node" "*" 21 | "@types/webidl-conversions" "*" 22 | 23 | ansi-regex@^5.0.1: 24 | version "5.0.1" 25 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 26 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 27 | 28 | ansi-styles@^4.0.0: 29 | version "4.3.0" 30 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 31 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 32 | dependencies: 33 | color-convert "^2.0.1" 34 | 35 | base64-js@^1.3.1: 36 | version "1.5.1" 37 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" 38 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== 39 | 40 | bson@^4.6.0: 41 | version "4.6.0" 42 | resolved "https://registry.yarnpkg.com/bson/-/bson-4.6.0.tgz#15c3b39ba3940c3d915a0c44d51459f4b4fbf1b2" 43 | integrity sha512-8jw1NU1hglS+Da1jDOUYuNcBJ4cNHCFIqzlwoFNnsTOg2R/ox0aTYcTiBN4dzRa9q7Cvy6XErh3L8ReTEb9AQQ== 44 | dependencies: 45 | buffer "^5.6.0" 46 | 47 | buffer@^5.6.0: 48 | version "5.7.1" 49 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" 50 | integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== 51 | dependencies: 52 | base64-js "^1.3.1" 53 | ieee754 "^1.1.13" 54 | 55 | cliui@^8.0.1: 56 | version "8.0.1" 57 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" 58 | integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== 59 | dependencies: 60 | string-width "^4.2.0" 61 | strip-ansi "^6.0.1" 62 | wrap-ansi "^7.0.0" 63 | 64 | color-convert@^2.0.1: 65 | version "2.0.1" 66 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 67 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 68 | dependencies: 69 | color-name "~1.1.4" 70 | 71 | color-name@~1.1.4: 72 | version "1.1.4" 73 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 74 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 75 | 76 | debug@^4.1.1: 77 | version "4.3.3" 78 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" 79 | integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== 80 | dependencies: 81 | ms "2.1.2" 82 | 83 | denque@^2.0.1: 84 | version "2.0.1" 85 | resolved "https://registry.yarnpkg.com/denque/-/denque-2.0.1.tgz#bcef4c1b80dc32efe97515744f21a4229ab8934a" 86 | integrity sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ== 87 | 88 | emoji-regex@^8.0.0: 89 | version "8.0.0" 90 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 91 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 92 | 93 | escalade@^3.1.1: 94 | version "3.1.1" 95 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 96 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 97 | 98 | get-caller-file@^2.0.5: 99 | version "2.0.5" 100 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 101 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 102 | 103 | gitlog@^4.0.4: 104 | version "4.0.4" 105 | resolved "https://registry.yarnpkg.com/gitlog/-/gitlog-4.0.4.tgz#8da6c08748dc290eb6c2fc11e3c505fb73715564" 106 | integrity sha512-jeY2kO7CVyTa6cUM7ZD2ZxIyBkna1xvW2esV/3o8tbhiUneX1UBQCH4D9aMrHgGiohBjyXbuZogyjKXslnY5Yg== 107 | dependencies: 108 | debug "^4.1.1" 109 | tslib "^1.14.1" 110 | 111 | ieee754@^1.1.13: 112 | version "1.2.1" 113 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" 114 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== 115 | 116 | is-fullwidth-code-point@^3.0.0: 117 | version "3.0.0" 118 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 119 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 120 | 121 | memory-pager@^1.0.2: 122 | version "1.5.0" 123 | resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" 124 | integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== 125 | 126 | mongodb-connection-string-url@^2.2.0: 127 | version "2.2.0" 128 | resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.2.0.tgz#e2422bae91a953dc4ae5882e401301f5be39a227" 129 | integrity sha512-U0cDxLUrQrl7DZA828CA+o69EuWPWEJTwdMPozyd7cy/dbtncUZczMw7wRHcwMD7oKOn0NM2tF9jdf5FFVW9CA== 130 | dependencies: 131 | "@types/whatwg-url" "^8.2.1" 132 | whatwg-url "^11.0.0" 133 | 134 | mongodb@^4.2.1: 135 | version "4.2.1" 136 | resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.2.1.tgz#c167db158ab0bf6552b3f9c91b396cb4100c2f04" 137 | integrity sha512-nDC+ulM/Ea3Q2VG5eemuGfB7T4ORwrtKegH2XW9OLlUBgQF6OTNrzFCS1Z3SJGVA+T0Sr1xBYV6DMnp0A7us0g== 138 | dependencies: 139 | bson "^4.6.0" 140 | denque "^2.0.1" 141 | mongodb-connection-string-url "^2.2.0" 142 | optionalDependencies: 143 | saslprep "^1.0.3" 144 | 145 | ms@2.1.2: 146 | version "2.1.2" 147 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 148 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 149 | 150 | punycode@^2.1.1: 151 | version "2.1.1" 152 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 153 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 154 | 155 | require-directory@^2.1.1: 156 | version "2.1.1" 157 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 158 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= 159 | 160 | saslprep@^1.0.3: 161 | version "1.0.3" 162 | resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226" 163 | integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag== 164 | dependencies: 165 | sparse-bitfield "^3.0.3" 166 | 167 | sparse-bitfield@^3.0.3: 168 | version "3.0.3" 169 | resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" 170 | integrity sha1-/0rm5oZWBWuks+eSqzM004JzyhE= 171 | dependencies: 172 | memory-pager "^1.0.2" 173 | 174 | string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: 175 | version "4.2.3" 176 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 177 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 178 | dependencies: 179 | emoji-regex "^8.0.0" 180 | is-fullwidth-code-point "^3.0.0" 181 | strip-ansi "^6.0.1" 182 | 183 | strip-ansi@^6.0.0, strip-ansi@^6.0.1: 184 | version "6.0.1" 185 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 186 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 187 | dependencies: 188 | ansi-regex "^5.0.1" 189 | 190 | tr46@^3.0.0: 191 | version "3.0.0" 192 | resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" 193 | integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== 194 | dependencies: 195 | punycode "^2.1.1" 196 | 197 | tslib@^1.14.1: 198 | version "1.14.1" 199 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" 200 | integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== 201 | 202 | webidl-conversions@^7.0.0: 203 | version "7.0.0" 204 | resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" 205 | integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== 206 | 207 | whatwg-url@^11.0.0: 208 | version "11.0.0" 209 | resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" 210 | integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== 211 | dependencies: 212 | tr46 "^3.0.0" 213 | webidl-conversions "^7.0.0" 214 | 215 | wrap-ansi@^7.0.0: 216 | version "7.0.0" 217 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" 218 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== 219 | dependencies: 220 | ansi-styles "^4.0.0" 221 | string-width "^4.1.0" 222 | strip-ansi "^6.0.0" 223 | 224 | y18n@^5.0.5: 225 | version "5.0.8" 226 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" 227 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== 228 | 229 | yargs-parser@^21.1.1: 230 | version "21.1.1" 231 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" 232 | integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== 233 | 234 | yargs@^17.7.1: 235 | version "17.7.1" 236 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" 237 | integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== 238 | dependencies: 239 | cliui "^8.0.1" 240 | escalade "^3.1.1" 241 | get-caller-file "^2.0.5" 242 | require-directory "^2.1.1" 243 | string-width "^4.2.3" 244 | y18n "^5.0.5" 245 | yargs-parser "^21.1.1" 246 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | group = io.papermc 2 | version = 1.0.0-SNAPSHOT 3 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | indra = "3.1.3" 3 | 4 | [plugins] 5 | indra = { id = "net.kyori.indra", version.ref = "indra" } 6 | indra-checkstyle = { id = "net.kyori.indra.checkstyle", version.ref = "indra" } 7 | indra-git = { id = "net.kyori.indra.git", version.ref = "indra" } 8 | jib = { id = "com.google.cloud.tools.jib", version = "3.4.3" } 9 | spotless = { id = "com.diffplug.spotless", version = "6.25.0" } 10 | spring-boot = { id = "org.springframework.boot", version = "3.2.5" } 11 | spring-dependency-management = { id = "io.spring.dependency-management", version = "1.1.5" } 12 | 13 | [libraries] 14 | jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version = "24.1.0" } 15 | springdoc-openapi-starter-webmvc-ui = { group = "org.springdoc", name = "springdoc-openapi-starter-webmvc-ui", version = "2.5.0" } 16 | stylecheck = { group = "ca.stellardrift", name = "stylecheck", version = "0.2.1" } 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaperMC/bibliothek/1608a700b8462b0016d38f2459c92bf898c21522/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2024 PaperMC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /license_header.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # bibliothek 2 | 3 | A downloads API. 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 3 | } 4 | 5 | rootProject.name = "bibliothek" 6 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/BibliothekApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek; 25 | 26 | import io.papermc.bibliothek.configuration.AppConfiguration; 27 | import org.springframework.boot.SpringApplication; 28 | import org.springframework.boot.autoconfigure.SpringBootApplication; 29 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 30 | import org.springframework.boot.web.servlet.ServletComponentScan; 31 | 32 | @EnableConfigurationProperties({ 33 | AppConfiguration.class 34 | }) 35 | @SpringBootApplication 36 | @ServletComponentScan 37 | public class BibliothekApplication { 38 | public static void main(final String[] args) { 39 | SpringApplication.run(BibliothekApplication.class, args); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/configuration/AppConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.configuration; 25 | 26 | import jakarta.validation.constraints.NotNull; 27 | import java.net.URL; 28 | import java.nio.file.Path; 29 | import org.springframework.boot.context.properties.ConfigurationProperties; 30 | import org.springframework.validation.annotation.Validated; 31 | 32 | @ConfigurationProperties(prefix = "app") 33 | @Validated 34 | public class AppConfiguration { 35 | private URL apiBaseUrl; 36 | private String apiTitle; 37 | private String apiVersion; 38 | private @NotNull Path storagePath; 39 | 40 | public URL getApiBaseUrl() { 41 | return this.apiBaseUrl; 42 | } 43 | 44 | public void setApiBaseUrl(final URL apiBaseUrl) { 45 | this.apiBaseUrl = apiBaseUrl; 46 | } 47 | 48 | public String getApiTitle() { 49 | return this.apiTitle; 50 | } 51 | 52 | public void setApiTitle(final String apiTitle) { 53 | this.apiTitle = apiTitle; 54 | } 55 | 56 | public String getApiVersion() { 57 | return this.apiVersion; 58 | } 59 | 60 | public void setApiVersion(final String apiVersion) { 61 | this.apiVersion = apiVersion; 62 | } 63 | 64 | public Path getStoragePath() { 65 | return this.storagePath; 66 | } 67 | 68 | public void setStoragePath(final Path storagePath) { 69 | this.storagePath = storagePath; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/configuration/MongoConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.configuration; 25 | 26 | import org.springframework.context.annotation.Bean; 27 | import org.springframework.context.annotation.Configuration; 28 | import org.springframework.data.mongodb.MongoDatabaseFactory; 29 | import org.springframework.data.mongodb.core.convert.DbRefResolver; 30 | import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; 31 | import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper; 32 | import org.springframework.data.mongodb.core.convert.MappingMongoConverter; 33 | import org.springframework.data.mongodb.core.convert.MongoCustomConversions; 34 | import org.springframework.data.mongodb.core.mapping.MongoMappingContext; 35 | 36 | @Configuration 37 | class MongoConfiguration { 38 | @Bean 39 | MappingMongoConverter mappingMongoConverter( 40 | final MongoDatabaseFactory mongoDatabaseFactory, 41 | final MongoMappingContext context, 42 | final MongoCustomConversions mongoCustomConversions 43 | ) { 44 | final DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory); 45 | final MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context); 46 | mappingConverter.setCustomConversions(mongoCustomConversions); 47 | mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null)); // to remove _class 48 | return mappingConverter; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/configuration/OpenAPIConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.configuration; 25 | 26 | import io.swagger.v3.oas.models.OpenAPI; 27 | import io.swagger.v3.oas.models.info.Info; 28 | import io.swagger.v3.oas.models.media.Schema; 29 | import io.swagger.v3.oas.models.servers.Server; 30 | import java.net.URL; 31 | import java.util.List; 32 | import java.util.Map; 33 | import java.util.TreeMap; 34 | import org.springdoc.core.customizers.OpenApiCustomizer; 35 | import org.springframework.context.annotation.Bean; 36 | import org.springframework.context.annotation.Configuration; 37 | 38 | @Configuration 39 | class OpenAPIConfiguration { 40 | @Bean 41 | OpenAPI openAPI(final AppConfiguration configuration) { 42 | final OpenAPI api = new OpenAPI(); 43 | api.info( 44 | new Info() 45 | .title(configuration.getApiTitle()) 46 | .version(configuration.getApiVersion()) 47 | ); 48 | final URL apiBaseUrl = configuration.getApiBaseUrl(); 49 | if (apiBaseUrl != null) { 50 | api.servers(List.of(new Server().url(apiBaseUrl.toExternalForm()))); 51 | } 52 | return api; 53 | } 54 | 55 | @Bean 56 | @SuppressWarnings("rawtypes") // nothing we can do, the API exposes it raw 57 | OpenApiCustomizer sortSchemasAlphabetically() { 58 | return openApi -> { 59 | final Map schemas = openApi.getComponents().getSchemas(); 60 | openApi.getComponents().setSchemas(new TreeMap<>(schemas)); 61 | }; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/configuration/WebConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.configuration; 25 | 26 | import jakarta.servlet.Filter; 27 | import org.springframework.context.annotation.Bean; 28 | import org.springframework.context.annotation.Configuration; 29 | import org.springframework.web.filter.ShallowEtagHeaderFilter; 30 | 31 | @Configuration 32 | class WebConfiguration { 33 | @Bean 34 | Filter shallowETagHeaderFilter() { 35 | return new ShallowEtagHeaderFilter(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/controller/RootController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.controller; 25 | 26 | import java.net.URI; 27 | import org.springframework.http.HttpStatus; 28 | import org.springframework.http.ResponseEntity; 29 | import org.springframework.stereotype.Controller; 30 | import org.springframework.web.bind.annotation.GetMapping; 31 | 32 | @Controller 33 | public class RootController { 34 | @GetMapping({ 35 | "/", 36 | "/docs" // without trailing / 37 | }) 38 | public ResponseEntity redirectToDocs() { 39 | return ResponseEntity.status(HttpStatus.FOUND) 40 | .location(URI.create("docs/")) 41 | .build(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/controller/v2/DownloadController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.controller.v2; 25 | 26 | import io.papermc.bibliothek.configuration.AppConfiguration; 27 | import io.papermc.bibliothek.database.model.Build; 28 | import io.papermc.bibliothek.database.model.Project; 29 | import io.papermc.bibliothek.database.model.Version; 30 | import io.papermc.bibliothek.database.repository.BuildCollection; 31 | import io.papermc.bibliothek.database.repository.ProjectCollection; 32 | import io.papermc.bibliothek.database.repository.VersionCollection; 33 | import io.papermc.bibliothek.exception.BuildNotFound; 34 | import io.papermc.bibliothek.exception.DownloadFailed; 35 | import io.papermc.bibliothek.exception.DownloadNotFound; 36 | import io.papermc.bibliothek.exception.ProjectNotFound; 37 | import io.papermc.bibliothek.exception.VersionNotFound; 38 | import io.papermc.bibliothek.util.HTTP; 39 | import io.papermc.bibliothek.util.MediaTypes; 40 | import io.swagger.v3.oas.annotations.Operation; 41 | import io.swagger.v3.oas.annotations.Parameter; 42 | import io.swagger.v3.oas.annotations.headers.Header; 43 | import io.swagger.v3.oas.annotations.media.Schema; 44 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 45 | import jakarta.validation.constraints.Pattern; 46 | import jakarta.validation.constraints.Positive; 47 | import java.io.IOException; 48 | import java.nio.file.Files; 49 | import java.nio.file.Path; 50 | import java.time.Duration; 51 | import java.util.Map; 52 | import org.springframework.beans.factory.annotation.Autowired; 53 | import org.springframework.core.io.FileSystemResource; 54 | import org.springframework.http.CacheControl; 55 | import org.springframework.http.HttpHeaders; 56 | import org.springframework.http.HttpStatus; 57 | import org.springframework.http.MediaType; 58 | import org.springframework.http.ResponseEntity; 59 | import org.springframework.web.bind.annotation.GetMapping; 60 | import org.springframework.web.bind.annotation.PathVariable; 61 | import org.springframework.web.bind.annotation.RequestMapping; 62 | import org.springframework.web.bind.annotation.RestController; 63 | 64 | @RestController 65 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 66 | public class DownloadController { 67 | private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofDays(7)); 68 | private final AppConfiguration configuration; 69 | private final ProjectCollection projects; 70 | private final VersionCollection versions; 71 | private final BuildCollection builds; 72 | 73 | @Autowired 74 | private DownloadController( 75 | final AppConfiguration configuration, 76 | final ProjectCollection projects, 77 | final VersionCollection versions, 78 | final BuildCollection builds 79 | ) { 80 | this.configuration = configuration; 81 | this.projects = projects; 82 | this.versions = versions; 83 | this.builds = builds; 84 | } 85 | 86 | @ApiResponse( 87 | responseCode = "200", 88 | headers = { 89 | @Header( 90 | name = "Content-Disposition", 91 | description = "A header indicating that the content is expected to be displayed as an attachment, that is downloaded and saved locally.", 92 | schema = @Schema(type = "string") 93 | ), 94 | @Header( 95 | name = "ETag", 96 | description = "An identifier for a specific version of a resource. It lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed.", 97 | schema = @Schema(type = "string") 98 | ), 99 | @Header( 100 | name = "Last-Modified", 101 | description = "The date and time at which the origin server believes the resource was last modified.", 102 | schema = @Schema(type = "string") 103 | ) 104 | } 105 | ) 106 | @GetMapping( 107 | value = "/v2/projects/{project:[a-z]+}/versions/{version:" + Version.PATTERN + "}/builds/{build:\\d+}/downloads/{download:" + Build.Download.PATTERN + "}", 108 | produces = { 109 | MediaType.APPLICATION_JSON_VALUE, 110 | MediaType.ALL_VALUE 111 | } 112 | ) 113 | @Operation(summary = "Downloads the given file from a build's data.") 114 | public ResponseEntity download( 115 | @Parameter(name = "project", description = "The project identifier.", example = "paper") 116 | @PathVariable("project") 117 | @Pattern(regexp = "[a-z]+") // 118 | final String projectName, 119 | @Parameter(description = "A version of the project.") 120 | @PathVariable("version") 121 | @Pattern(regexp = Version.PATTERN) // 122 | final String versionName, 123 | @Parameter(description = "A build of the version.") 124 | @PathVariable("build") 125 | @Positive // 126 | final int buildNumber, 127 | @Parameter(description = "A download of the build.") 128 | @PathVariable("download") 129 | @Pattern(regexp = Build.Download.PATTERN) // 130 | final String downloadName 131 | ) { 132 | final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); 133 | final Version version = this.versions.findByProjectAndName(project._id(), versionName).orElseThrow(VersionNotFound::new); 134 | final Build build = this.builds.findByProjectAndVersionAndNumber(project._id(), version._id(), buildNumber).orElseThrow(BuildNotFound::new); 135 | 136 | for (final Map.Entry download : build.downloads().entrySet()) { 137 | if (download.getValue().name().equals(downloadName)) { 138 | try { 139 | return new JavaArchive( 140 | this.configuration.getStoragePath() 141 | .resolve(project.name()) 142 | .resolve(version.name()) 143 | .resolve(String.valueOf(build.number())) 144 | .resolve(download.getValue().name()), 145 | CACHE 146 | ); 147 | } catch (final IOException e) { 148 | throw new DownloadFailed(e); 149 | } 150 | } 151 | } 152 | throw new DownloadNotFound(); 153 | } 154 | 155 | private static class JavaArchive extends ResponseEntity { 156 | JavaArchive(final Path path, final CacheControl cache) throws IOException { 157 | super(new FileSystemResource(path), headersFor(path, cache), HttpStatus.OK); 158 | } 159 | 160 | private static HttpHeaders headersFor(final Path path, final CacheControl cache) throws IOException { 161 | final HttpHeaders headers = new HttpHeaders(); 162 | headers.setCacheControl(cache); 163 | headers.setContentDisposition(HTTP.attachmentDisposition(path.getFileName())); 164 | headers.setContentType(MediaTypes.fromFileName(path.getFileName().toString())); 165 | headers.setLastModified(Files.getLastModifiedTime(path).toInstant()); 166 | return headers; 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/controller/v2/ProjectController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.controller.v2; 25 | 26 | import io.papermc.bibliothek.database.model.Project; 27 | import io.papermc.bibliothek.database.model.Version; 28 | import io.papermc.bibliothek.database.model.VersionFamily; 29 | import io.papermc.bibliothek.database.repository.ProjectCollection; 30 | import io.papermc.bibliothek.database.repository.VersionCollection; 31 | import io.papermc.bibliothek.database.repository.VersionFamilyCollection; 32 | import io.papermc.bibliothek.exception.ProjectNotFound; 33 | import io.papermc.bibliothek.util.HTTP; 34 | import io.swagger.v3.oas.annotations.Operation; 35 | import io.swagger.v3.oas.annotations.Parameter; 36 | import io.swagger.v3.oas.annotations.media.Content; 37 | import io.swagger.v3.oas.annotations.media.Schema; 38 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 39 | import jakarta.validation.constraints.Pattern; 40 | import java.time.Duration; 41 | import java.util.List; 42 | import org.springframework.beans.factory.annotation.Autowired; 43 | import org.springframework.http.CacheControl; 44 | import org.springframework.http.MediaType; 45 | import org.springframework.http.ResponseEntity; 46 | import org.springframework.web.bind.annotation.GetMapping; 47 | import org.springframework.web.bind.annotation.PathVariable; 48 | import org.springframework.web.bind.annotation.RequestMapping; 49 | import org.springframework.web.bind.annotation.RestController; 50 | 51 | @RestController 52 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 53 | public class ProjectController { 54 | private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofMinutes(30)); 55 | private final ProjectCollection projects; 56 | private final VersionFamilyCollection families; 57 | private final VersionCollection versions; 58 | 59 | @Autowired 60 | private ProjectController( 61 | final ProjectCollection projects, 62 | final VersionFamilyCollection families, 63 | final VersionCollection versions 64 | ) { 65 | this.projects = projects; 66 | this.families = families; 67 | this.versions = versions; 68 | } 69 | 70 | @ApiResponse( 71 | content = @Content( 72 | schema = @Schema(implementation = ProjectResponse.class) 73 | ), 74 | responseCode = "200" 75 | ) 76 | @GetMapping("/v2/projects/{project:[a-z]+}") 77 | @Operation(summary = "Gets information about a project.") 78 | public ResponseEntity project( 79 | @Parameter(name = "project", description = "The project identifier.", example = "paper") 80 | @PathVariable("project") 81 | @Pattern(regexp = "[a-z]+") // 82 | final String projectName 83 | ) { 84 | final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); 85 | final List families = this.families.findAllByProject(project._id()); 86 | final List versions = this.versions.findAllByProject(project._id()); 87 | return HTTP.cachedOk(ProjectResponse.from(project, families, versions), CACHE); 88 | } 89 | 90 | @Schema 91 | private record ProjectResponse( 92 | @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") 93 | String project_id, 94 | @Schema(name = "project_name", example = "Paper") 95 | String project_name, 96 | @Schema(name = "version_groups") 97 | List version_groups, 98 | @Schema(name = "versions") 99 | List versions 100 | ) { 101 | static ProjectResponse from(final Project project, final List families, final List versions) { 102 | return new ProjectResponse( 103 | project.name(), 104 | project.friendlyName(), 105 | families.stream().sorted(VersionFamily.COMPARATOR).map(VersionFamily::name).toList(), 106 | versions.stream().sorted(Version.COMPARATOR).map(Version::name).toList() 107 | ); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/controller/v2/ProjectsController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.controller.v2; 25 | 26 | import io.papermc.bibliothek.database.model.Project; 27 | import io.papermc.bibliothek.database.repository.ProjectCollection; 28 | import io.papermc.bibliothek.util.HTTP; 29 | import io.swagger.v3.oas.annotations.Operation; 30 | import io.swagger.v3.oas.annotations.media.Content; 31 | import io.swagger.v3.oas.annotations.media.Schema; 32 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 33 | import java.time.Duration; 34 | import java.util.List; 35 | import org.springframework.beans.factory.annotation.Autowired; 36 | import org.springframework.http.CacheControl; 37 | import org.springframework.http.MediaType; 38 | import org.springframework.http.ResponseEntity; 39 | import org.springframework.web.bind.annotation.GetMapping; 40 | import org.springframework.web.bind.annotation.RequestMapping; 41 | import org.springframework.web.bind.annotation.RestController; 42 | 43 | @RestController 44 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 45 | public class ProjectsController { 46 | private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofDays(7)); 47 | private final ProjectCollection projects; 48 | 49 | @Autowired 50 | private ProjectsController(final ProjectCollection projects) { 51 | this.projects = projects; 52 | } 53 | 54 | @ApiResponse( 55 | content = @Content( 56 | schema = @Schema(implementation = ProjectsResponse.class) 57 | ), 58 | responseCode = "200" 59 | ) 60 | @GetMapping("/v2/projects") 61 | @Operation(summary = "Gets a list of all available projects.") 62 | public ResponseEntity projects() { 63 | final List projects = this.projects.findAll(); 64 | return HTTP.cachedOk(ProjectsResponse.from(projects), CACHE); 65 | } 66 | 67 | @Schema 68 | private record ProjectsResponse( 69 | @Schema(name = "projects") 70 | List projects 71 | ) { 72 | static ProjectsResponse from(final List projects) { 73 | return new ProjectsResponse(projects.stream().map(Project::name).toList()); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/controller/v2/VersionBuildController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.controller.v2; 25 | 26 | import io.papermc.bibliothek.database.model.Build; 27 | import io.papermc.bibliothek.database.model.Project; 28 | import io.papermc.bibliothek.database.model.Version; 29 | import io.papermc.bibliothek.database.repository.BuildCollection; 30 | import io.papermc.bibliothek.database.repository.ProjectCollection; 31 | import io.papermc.bibliothek.database.repository.VersionCollection; 32 | import io.papermc.bibliothek.exception.BuildNotFound; 33 | import io.papermc.bibliothek.exception.ProjectNotFound; 34 | import io.papermc.bibliothek.exception.VersionNotFound; 35 | import io.papermc.bibliothek.util.HTTP; 36 | import io.swagger.v3.oas.annotations.Operation; 37 | import io.swagger.v3.oas.annotations.Parameter; 38 | import io.swagger.v3.oas.annotations.media.Content; 39 | import io.swagger.v3.oas.annotations.media.Schema; 40 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 41 | import jakarta.validation.constraints.Pattern; 42 | import jakarta.validation.constraints.Positive; 43 | import java.time.Duration; 44 | import java.time.Instant; 45 | import java.util.List; 46 | import java.util.Map; 47 | import org.springframework.beans.factory.annotation.Autowired; 48 | import org.springframework.http.CacheControl; 49 | import org.springframework.http.MediaType; 50 | import org.springframework.http.ResponseEntity; 51 | import org.springframework.web.bind.annotation.GetMapping; 52 | import org.springframework.web.bind.annotation.PathVariable; 53 | import org.springframework.web.bind.annotation.RequestMapping; 54 | import org.springframework.web.bind.annotation.RestController; 55 | 56 | @RestController 57 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 58 | public class VersionBuildController { 59 | private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofDays(7)); 60 | private final ProjectCollection projects; 61 | private final VersionCollection versions; 62 | private final BuildCollection builds; 63 | 64 | @Autowired 65 | private VersionBuildController( 66 | final ProjectCollection projects, 67 | final VersionCollection versions, 68 | final BuildCollection builds 69 | ) { 70 | this.projects = projects; 71 | this.versions = versions; 72 | this.builds = builds; 73 | } 74 | 75 | @ApiResponse( 76 | content = @Content( 77 | schema = @Schema(implementation = BuildResponse.class) 78 | ), 79 | responseCode = "200" 80 | ) 81 | @GetMapping("/v2/projects/{project:[a-z]+}/versions/{version:" + Version.PATTERN + "}/builds/{build:\\d+}") 82 | @Operation(summary = "Gets information related to a specific build.") 83 | public ResponseEntity build( 84 | @Parameter(name = "project", description = "The project identifier.", example = "paper") 85 | @PathVariable("project") 86 | @Pattern(regexp = "[a-z]+") // 87 | final String projectName, 88 | @Parameter(description = "A version of the project.") 89 | @PathVariable("version") 90 | @Pattern(regexp = Version.PATTERN) // 91 | final String versionName, 92 | @Parameter(description = "A build of the version.") 93 | @PathVariable("build") 94 | @Positive // 95 | final int buildNumber 96 | ) { 97 | final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); 98 | final Version version = this.versions.findByProjectAndName(project._id(), versionName).orElseThrow(VersionNotFound::new); 99 | final Build build = this.builds.findByProjectAndVersionAndNumber(project._id(), version._id(), buildNumber).orElseThrow(BuildNotFound::new); 100 | return HTTP.cachedOk(BuildResponse.from(project, version, build), CACHE); 101 | } 102 | 103 | @Schema 104 | private record BuildResponse( 105 | @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") 106 | String project_id, 107 | @Schema(name = "project_name", example = "Paper") 108 | String project_name, 109 | @Schema(name = "version", pattern = Version.PATTERN, example = "1.18") 110 | String version, 111 | @Schema(name = "build", pattern = "\\d+", example = "10") 112 | int build, 113 | @Schema(name = "time") 114 | Instant time, 115 | @Schema(name = "channel") 116 | Build.Channel channel, 117 | @Schema(name = "promoted") 118 | boolean promoted, 119 | @Schema(name = "changes") 120 | List changes, 121 | @Schema(name = "downloads") 122 | Map downloads 123 | ) { 124 | static BuildResponse from(final Project project, final Version version, final Build build) { 125 | return new BuildResponse( 126 | project.name(), 127 | project.friendlyName(), 128 | version.name(), 129 | build.number(), 130 | build.time(), 131 | build.channelOrDefault(), 132 | build.promotedOrDefault(), 133 | build.changes(), 134 | build.downloads() 135 | ); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/controller/v2/VersionBuildsController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.controller.v2; 25 | 26 | import io.papermc.bibliothek.database.model.Build; 27 | import io.papermc.bibliothek.database.model.Project; 28 | import io.papermc.bibliothek.database.model.Version; 29 | import io.papermc.bibliothek.database.repository.BuildCollection; 30 | import io.papermc.bibliothek.database.repository.ProjectCollection; 31 | import io.papermc.bibliothek.database.repository.VersionCollection; 32 | import io.papermc.bibliothek.exception.ProjectNotFound; 33 | import io.papermc.bibliothek.exception.VersionNotFound; 34 | import io.papermc.bibliothek.util.HTTP; 35 | import io.swagger.v3.oas.annotations.Operation; 36 | import io.swagger.v3.oas.annotations.Parameter; 37 | import io.swagger.v3.oas.annotations.media.Content; 38 | import io.swagger.v3.oas.annotations.media.Schema; 39 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 40 | import jakarta.validation.constraints.Pattern; 41 | import java.time.Duration; 42 | import java.time.Instant; 43 | import java.util.List; 44 | import java.util.Map; 45 | import org.springframework.beans.factory.annotation.Autowired; 46 | import org.springframework.http.CacheControl; 47 | import org.springframework.http.MediaType; 48 | import org.springframework.http.ResponseEntity; 49 | import org.springframework.web.bind.annotation.GetMapping; 50 | import org.springframework.web.bind.annotation.PathVariable; 51 | import org.springframework.web.bind.annotation.RequestMapping; 52 | import org.springframework.web.bind.annotation.RestController; 53 | 54 | @RestController 55 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 56 | public class VersionBuildsController { 57 | private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofMinutes(5)); 58 | private final ProjectCollection projects; 59 | private final VersionCollection versions; 60 | private final BuildCollection builds; 61 | 62 | @Autowired 63 | private VersionBuildsController( 64 | final ProjectCollection projects, 65 | final VersionCollection versions, 66 | final BuildCollection builds 67 | ) { 68 | this.projects = projects; 69 | this.versions = versions; 70 | this.builds = builds; 71 | } 72 | 73 | @ApiResponse( 74 | content = @Content( 75 | schema = @Schema(implementation = BuildsResponse.class) 76 | ), 77 | responseCode = "200" 78 | ) 79 | @GetMapping("/v2/projects/{project:[a-z]+}/versions/{version:" + Version.PATTERN + "}/builds") 80 | @Operation(summary = "Gets all available builds for a project's version.") 81 | public ResponseEntity builds( 82 | @Parameter(name = "project", description = "The project identifier.", example = "paper") 83 | @PathVariable("project") 84 | @Pattern(regexp = "[a-z]+") // 85 | final String projectName, 86 | @Parameter(description = "A version of the project.") 87 | @PathVariable("version") 88 | @Pattern(regexp = Version.PATTERN) // 89 | final String versionName 90 | ) { 91 | final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); 92 | final Version version = this.versions.findByProjectAndName(project._id(), versionName).orElseThrow(VersionNotFound::new); 93 | final List builds = this.builds.findAllByProjectAndVersion(project._id(), version._id()); 94 | return HTTP.cachedOk(BuildsResponse.from(project, version, builds), CACHE); 95 | } 96 | 97 | @Schema 98 | private record BuildsResponse( 99 | @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") 100 | String project_id, 101 | @Schema(name = "project_name", example = "Paper") 102 | String project_name, 103 | @Schema(name = "version", pattern = Version.PATTERN, example = "1.18") 104 | String version, 105 | @Schema(name = "builds") 106 | List builds 107 | ) { 108 | static BuildsResponse from(final Project project, final Version version, final List builds) { 109 | return new BuildsResponse( 110 | project.name(), 111 | project.friendlyName(), 112 | version.name(), 113 | builds.stream().map(build -> new VersionBuild( 114 | build.number(), 115 | build.time(), 116 | build.channelOrDefault(), 117 | build.promotedOrDefault(), 118 | build.changes(), 119 | build.downloads() 120 | )).toList() 121 | ); 122 | } 123 | 124 | @Schema 125 | public record VersionBuild( 126 | @Schema(name = "build", pattern = "\\d+", example = "10") 127 | int build, 128 | @Schema(name = "time") 129 | Instant time, 130 | @Schema(name = "channel") 131 | Build.Channel channel, 132 | @Schema(name = "promoted") 133 | boolean promoted, 134 | @Schema(name = "changes") 135 | List changes, 136 | @Schema(name = "downloads") 137 | Map downloads 138 | ) { 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/controller/v2/VersionController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.controller.v2; 25 | 26 | import io.papermc.bibliothek.database.model.Build; 27 | import io.papermc.bibliothek.database.model.Project; 28 | import io.papermc.bibliothek.database.model.Version; 29 | import io.papermc.bibliothek.database.repository.BuildCollection; 30 | import io.papermc.bibliothek.database.repository.ProjectCollection; 31 | import io.papermc.bibliothek.database.repository.VersionCollection; 32 | import io.papermc.bibliothek.exception.ProjectNotFound; 33 | import io.papermc.bibliothek.exception.VersionNotFound; 34 | import io.papermc.bibliothek.util.HTTP; 35 | import io.swagger.v3.oas.annotations.Operation; 36 | import io.swagger.v3.oas.annotations.Parameter; 37 | import io.swagger.v3.oas.annotations.media.Content; 38 | import io.swagger.v3.oas.annotations.media.Schema; 39 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 40 | import jakarta.validation.constraints.Pattern; 41 | import java.time.Duration; 42 | import java.util.List; 43 | import org.springframework.beans.factory.annotation.Autowired; 44 | import org.springframework.http.CacheControl; 45 | import org.springframework.http.MediaType; 46 | import org.springframework.http.ResponseEntity; 47 | import org.springframework.web.bind.annotation.GetMapping; 48 | import org.springframework.web.bind.annotation.PathVariable; 49 | import org.springframework.web.bind.annotation.RequestMapping; 50 | import org.springframework.web.bind.annotation.RestController; 51 | 52 | @RestController 53 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 54 | public class VersionController { 55 | private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofMinutes(5)); 56 | private final ProjectCollection projects; 57 | private final VersionCollection versions; 58 | private final BuildCollection builds; 59 | 60 | @Autowired 61 | private VersionController( 62 | final ProjectCollection projects, 63 | final VersionCollection versions, 64 | final BuildCollection builds 65 | ) { 66 | this.projects = projects; 67 | this.versions = versions; 68 | this.builds = builds; 69 | } 70 | 71 | @ApiResponse( 72 | content = @Content( 73 | schema = @Schema(implementation = VersionResponse.class) 74 | ), 75 | responseCode = "200" 76 | ) 77 | @GetMapping("/v2/projects/{project:[a-z]+}/versions/{version:" + Version.PATTERN + "}") 78 | @Operation(summary = "Gets information about a version.") 79 | public ResponseEntity version( 80 | @Parameter(name = "project", description = "The project identifier.", example = "paper") 81 | @PathVariable("project") 82 | @Pattern(regexp = "[a-z]+") // 83 | final String projectName, 84 | @Parameter(description = "A version of the project.") 85 | @PathVariable("version") 86 | @Pattern(regexp = Version.PATTERN) // 87 | final String versionName 88 | ) { 89 | final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); 90 | final Version version = this.versions.findByProjectAndName(project._id(), versionName).orElseThrow(VersionNotFound::new); 91 | final List builds = this.builds.findAllByProjectAndVersion(project._id(), version._id()); 92 | return HTTP.cachedOk(VersionResponse.from(project, version, builds), CACHE); 93 | } 94 | 95 | @Schema 96 | private record VersionResponse( 97 | @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") 98 | String project_id, 99 | @Schema(name = "project_name", example = "Paper") 100 | String project_name, 101 | @Schema(name = "version", pattern = Version.PATTERN, example = "1.18") 102 | String version, 103 | @Schema(name = "builds") 104 | List builds 105 | ) { 106 | static VersionResponse from(final Project project, final Version version, final List builds) { 107 | return new VersionResponse( 108 | project.name(), 109 | project.friendlyName(), 110 | version.name(), 111 | builds.stream().map(Build::number).toList() 112 | ); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/controller/v2/VersionFamilyBuildsController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.controller.v2; 25 | 26 | import io.papermc.bibliothek.database.model.Build; 27 | import io.papermc.bibliothek.database.model.Project; 28 | import io.papermc.bibliothek.database.model.Version; 29 | import io.papermc.bibliothek.database.model.VersionFamily; 30 | import io.papermc.bibliothek.database.repository.BuildCollection; 31 | import io.papermc.bibliothek.database.repository.ProjectCollection; 32 | import io.papermc.bibliothek.database.repository.VersionCollection; 33 | import io.papermc.bibliothek.database.repository.VersionFamilyCollection; 34 | import io.papermc.bibliothek.exception.ProjectNotFound; 35 | import io.papermc.bibliothek.exception.VersionNotFound; 36 | import io.papermc.bibliothek.util.HTTP; 37 | import io.swagger.v3.oas.annotations.Operation; 38 | import io.swagger.v3.oas.annotations.Parameter; 39 | import io.swagger.v3.oas.annotations.media.Content; 40 | import io.swagger.v3.oas.annotations.media.Schema; 41 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 42 | import jakarta.validation.constraints.Pattern; 43 | import java.time.Duration; 44 | import java.time.Instant; 45 | import java.util.List; 46 | import java.util.Map; 47 | import java.util.function.Function; 48 | import java.util.stream.Collectors; 49 | import org.bson.types.ObjectId; 50 | import org.springframework.beans.factory.annotation.Autowired; 51 | import org.springframework.http.CacheControl; 52 | import org.springframework.http.MediaType; 53 | import org.springframework.http.ResponseEntity; 54 | import org.springframework.web.bind.annotation.GetMapping; 55 | import org.springframework.web.bind.annotation.PathVariable; 56 | import org.springframework.web.bind.annotation.RequestMapping; 57 | import org.springframework.web.bind.annotation.RestController; 58 | 59 | @RestController 60 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 61 | public class VersionFamilyBuildsController { 62 | private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofMinutes(5)); 63 | private final ProjectCollection projects; 64 | private final VersionFamilyCollection families; 65 | private final VersionCollection versions; 66 | private final BuildCollection builds; 67 | 68 | @Autowired 69 | private VersionFamilyBuildsController( 70 | final ProjectCollection projects, 71 | final VersionFamilyCollection families, 72 | final VersionCollection versions, 73 | final BuildCollection builds 74 | ) { 75 | this.projects = projects; 76 | this.families = families; 77 | this.versions = versions; 78 | this.builds = builds; 79 | } 80 | 81 | @ApiResponse( 82 | content = @Content( 83 | schema = @Schema(implementation = VersionFamilyBuildsResponse.class) 84 | ), 85 | responseCode = "200" 86 | ) 87 | @GetMapping("/v2/projects/{project:[a-z]+}/version_group/{family:" + Version.PATTERN + "}/builds") 88 | @Operation(summary = "Gets all available builds for a project's version group.") 89 | public ResponseEntity familyBuilds( 90 | @Parameter(name = "project", description = "The project identifier.", example = "paper") 91 | @PathVariable("project") 92 | @Pattern(regexp = "[a-z]+") // 93 | final String projectName, 94 | @Parameter(description = "The version group name.") 95 | @PathVariable("family") 96 | @Pattern(regexp = Version.PATTERN) // 97 | final String familyName 98 | ) { 99 | final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); 100 | final VersionFamily family = this.families.findByProjectAndName(project._id(), familyName).orElseThrow(VersionNotFound::new); 101 | final Map versions = this.versions.findAllByProjectAndGroup(project._id(), family._id()).stream() 102 | .collect(Collectors.toMap(Version::_id, Function.identity())); 103 | final List builds = this.builds.findAllByProjectAndVersionIn(project._id(), versions.keySet()); 104 | return HTTP.cachedOk(VersionFamilyBuildsResponse.from(project, family, versions, builds), CACHE); 105 | } 106 | 107 | @Schema 108 | private record VersionFamilyBuildsResponse( 109 | @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") 110 | String project_id, 111 | @Schema(name = "project_name", example = "Paper") 112 | String project_name, 113 | @Schema(name = "version_group", pattern = Version.PATTERN, example = "1.18") 114 | String version_group, 115 | @Schema(name = "versions") 116 | List versions, 117 | @Schema(name = "builds") 118 | List builds 119 | ) { 120 | static VersionFamilyBuildsResponse from(final Project project, final VersionFamily family, final Map versions, final List builds) { 121 | return new VersionFamilyBuildsResponse( 122 | project.name(), 123 | project.friendlyName(), 124 | family.name(), 125 | versions.values().stream().sorted(Version.COMPARATOR).map(Version::name).toList(), 126 | builds.stream().map(build -> new VersionFamilyBuild( 127 | versions.get(build.version()).name(), 128 | build.number(), 129 | build.time(), 130 | build.channelOrDefault(), 131 | build.promotedOrDefault(), 132 | build.changes(), 133 | build.downloads() 134 | )).toList() 135 | ); 136 | } 137 | 138 | @Schema 139 | public static record VersionFamilyBuild( 140 | @Schema(name = "version", pattern = Version.PATTERN, example = "1.18") 141 | String version, 142 | @Schema(name = "build", pattern = "\\d+", example = "10") 143 | int build, 144 | @Schema(name = "time") 145 | Instant time, 146 | @Schema(name = "channel") 147 | Build.Channel channel, 148 | @Schema(name = "promoted") 149 | boolean promoted, 150 | @Schema(name = "changes") 151 | List changes, 152 | @Schema(name = "downloads") 153 | Map downloads 154 | ) { 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/controller/v2/VersionFamilyController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.controller.v2; 25 | 26 | import io.papermc.bibliothek.database.model.Project; 27 | import io.papermc.bibliothek.database.model.Version; 28 | import io.papermc.bibliothek.database.model.VersionFamily; 29 | import io.papermc.bibliothek.database.repository.ProjectCollection; 30 | import io.papermc.bibliothek.database.repository.VersionCollection; 31 | import io.papermc.bibliothek.database.repository.VersionFamilyCollection; 32 | import io.papermc.bibliothek.exception.ProjectNotFound; 33 | import io.papermc.bibliothek.exception.VersionNotFound; 34 | import io.papermc.bibliothek.util.HTTP; 35 | import io.swagger.v3.oas.annotations.Operation; 36 | import io.swagger.v3.oas.annotations.Parameter; 37 | import io.swagger.v3.oas.annotations.media.Content; 38 | import io.swagger.v3.oas.annotations.media.Schema; 39 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 40 | import jakarta.validation.constraints.Pattern; 41 | import java.time.Duration; 42 | import java.util.List; 43 | import org.springframework.beans.factory.annotation.Autowired; 44 | import org.springframework.http.CacheControl; 45 | import org.springframework.http.MediaType; 46 | import org.springframework.http.ResponseEntity; 47 | import org.springframework.web.bind.annotation.GetMapping; 48 | import org.springframework.web.bind.annotation.PathVariable; 49 | import org.springframework.web.bind.annotation.RequestMapping; 50 | import org.springframework.web.bind.annotation.RestController; 51 | 52 | @RestController 53 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 54 | public class VersionFamilyController { 55 | private static final CacheControl CACHE = HTTP.sMaxAgePublicCache(Duration.ofMinutes(5)); 56 | private final ProjectCollection projects; 57 | private final VersionFamilyCollection families; 58 | private final VersionCollection versions; 59 | 60 | @Autowired 61 | private VersionFamilyController( 62 | final ProjectCollection projects, 63 | final VersionFamilyCollection families, 64 | final VersionCollection versions 65 | ) { 66 | this.projects = projects; 67 | this.families = families; 68 | this.versions = versions; 69 | } 70 | 71 | @ApiResponse( 72 | content = @Content( 73 | schema = @Schema(implementation = VersionFamilyResponse.class) 74 | ), 75 | responseCode = "200" 76 | ) 77 | @GetMapping("/v2/projects/{project:[a-z]+}/version_group/{family:" + Version.PATTERN + "}") 78 | @Operation(summary = "Gets information about a project's version group.") 79 | public ResponseEntity family( 80 | @Parameter(name = "project", description = "The project identifier.", example = "paper") 81 | @PathVariable("project") 82 | @Pattern(regexp = "[a-z]+") // 83 | final String projectName, 84 | @Parameter(description = "The version group name.") 85 | @PathVariable("family") 86 | @Pattern(regexp = Version.PATTERN) // 87 | final String familyName 88 | ) { 89 | final Project project = this.projects.findByName(projectName).orElseThrow(ProjectNotFound::new); 90 | final VersionFamily family = this.families.findByProjectAndName(project._id(), familyName).orElseThrow(VersionNotFound::new); 91 | final List versions = this.versions.findAllByProjectAndGroup(project._id(), family._id()); 92 | return HTTP.cachedOk(VersionFamilyResponse.from(project, family, versions), CACHE); 93 | } 94 | 95 | @Schema 96 | private record VersionFamilyResponse( 97 | @Schema(name = "project_id", pattern = "[a-z]+", example = "paper") 98 | String project_id, 99 | @Schema(name = "project_name", example = "Paper") 100 | String project_name, 101 | @Schema(name = "version_group", pattern = Version.PATTERN, example = "1.18") 102 | String version_group, 103 | @Schema(name = "versions") 104 | List versions 105 | ) { 106 | static VersionFamilyResponse from(final Project project, final VersionFamily family, final List versions) { 107 | return new VersionFamilyResponse( 108 | project.name(), 109 | project.friendlyName(), 110 | family.name(), 111 | versions.stream().sorted(Version.COMPARATOR).map(Version::name).toList() 112 | ); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/database/model/Build.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.database.model; 25 | 26 | import com.fasterxml.jackson.annotation.JsonInclude; 27 | import com.fasterxml.jackson.annotation.JsonProperty; 28 | import io.swagger.v3.oas.annotations.media.Schema; 29 | import java.time.Instant; 30 | import java.util.List; 31 | import java.util.Map; 32 | import java.util.Objects; 33 | import org.bson.types.ObjectId; 34 | import org.intellij.lang.annotations.Language; 35 | import org.jetbrains.annotations.Nullable; 36 | import org.springframework.data.annotation.Id; 37 | import org.springframework.data.mongodb.core.index.CompoundIndex; 38 | import org.springframework.data.mongodb.core.mapping.Document; 39 | 40 | @CompoundIndex(def = "{'project': 1, 'version': 1}") 41 | @CompoundIndex(def = "{'project': 1, 'version': 1, 'number': 1}") 42 | @Document(collection = "builds") 43 | public record Build( 44 | @Id ObjectId _id, 45 | ObjectId project, 46 | ObjectId version, 47 | int number, 48 | Instant time, 49 | List changes, 50 | Map downloads, 51 | @JsonProperty 52 | @Nullable Channel channel, 53 | @JsonInclude(JsonInclude.Include.NON_NULL) 54 | @Nullable Boolean promoted 55 | ) { 56 | public Channel channelOrDefault() { 57 | return Objects.requireNonNullElse(this.channel(), Build.Channel.DEFAULT); 58 | } 59 | 60 | public boolean promotedOrDefault() { 61 | return Objects.requireNonNullElse(this.promoted(), false); 62 | } 63 | 64 | public enum Channel { 65 | @JsonProperty("default") 66 | DEFAULT, 67 | @JsonProperty("experimental") 68 | EXPERIMENTAL; 69 | } 70 | 71 | @Schema 72 | public record Change( 73 | @Schema(name = "commit") 74 | String commit, 75 | @Schema(name = "summary") 76 | String summary, 77 | @Schema(name = "message") 78 | String message 79 | ) { 80 | } 81 | 82 | @Schema 83 | public record Download( 84 | @Schema(name = "name", pattern = "[a-z0-9._-]+", example = "paper-1.18-10.jar") 85 | String name, 86 | @Schema(name = "sha256", pattern = "[a-f0-9]{64}", example = "f065e2d345d9d772d5cf2a1ce5c495c4cc56eb2fcd6820e82856485fa19414c8") 87 | String sha256 88 | ) { 89 | // NOTE: this pattern cannot contain any capturing groups 90 | @Language("RegExp") 91 | public static final String PATTERN = "[a-zA-Z0-9._-]+"; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/database/model/Project.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.database.model; 25 | 26 | import org.bson.types.ObjectId; 27 | import org.springframework.data.annotation.Id; 28 | import org.springframework.data.mongodb.core.index.Indexed; 29 | import org.springframework.data.mongodb.core.mapping.Document; 30 | 31 | @Document(collection = "projects") 32 | public record Project( 33 | @Id ObjectId _id, 34 | @Indexed String name, 35 | String friendlyName 36 | ) { 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/database/model/Version.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.database.model; 25 | 26 | import io.papermc.bibliothek.util.BringOrderToChaos; 27 | import io.papermc.bibliothek.util.NameSource; 28 | import io.papermc.bibliothek.util.TimeSource; 29 | import java.time.Instant; 30 | import java.util.Comparator; 31 | import org.bson.types.ObjectId; 32 | import org.intellij.lang.annotations.Language; 33 | import org.jetbrains.annotations.Nullable; 34 | import org.springframework.data.annotation.Id; 35 | import org.springframework.data.mongodb.core.index.CompoundIndex; 36 | import org.springframework.data.mongodb.core.mapping.Document; 37 | 38 | @CompoundIndex(def = "{'project': 1, 'group': 1}") 39 | @CompoundIndex(def = "{'project': 1, 'name': 1}") 40 | @Document(collection = "versions") 41 | public record Version( 42 | @Id ObjectId _id, 43 | ObjectId project, 44 | ObjectId group, 45 | String name, 46 | @Nullable Instant time 47 | ) implements NameSource, TimeSource { 48 | // NOTE: this pattern cannot contain any capturing groups 49 | @Language("RegExp") 50 | public static final String PATTERN = "[0-9.]+-?(?:pre|SNAPSHOT)?(?:[0-9.]+)?"; 51 | public static final Comparator COMPARATOR = BringOrderToChaos.timeOrNameComparator(); 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/database/model/VersionFamily.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.database.model; 25 | 26 | import io.papermc.bibliothek.util.BringOrderToChaos; 27 | import io.papermc.bibliothek.util.NameSource; 28 | import io.papermc.bibliothek.util.TimeSource; 29 | import java.time.Instant; 30 | import java.util.Comparator; 31 | import org.bson.types.ObjectId; 32 | import org.jetbrains.annotations.Nullable; 33 | import org.springframework.data.annotation.Id; 34 | import org.springframework.data.mongodb.core.index.CompoundIndex; 35 | import org.springframework.data.mongodb.core.mapping.Document; 36 | 37 | @CompoundIndex(def = "{'project': 1, 'name': 1}") 38 | @Document(collection = "version_groups") 39 | public record VersionFamily( 40 | @Id ObjectId _id, 41 | ObjectId project, 42 | String name, 43 | @Nullable Instant time 44 | ) implements NameSource, TimeSource { 45 | public static final Comparator COMPARATOR = BringOrderToChaos.timeOrNameComparator(); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/database/repository/BuildCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.database.repository; 25 | 26 | import io.papermc.bibliothek.database.model.Build; 27 | import java.util.Collection; 28 | import java.util.List; 29 | import java.util.Optional; 30 | import org.bson.types.ObjectId; 31 | import org.springframework.data.mongodb.repository.MongoRepository; 32 | import org.springframework.stereotype.Repository; 33 | 34 | @Repository 35 | public interface BuildCollection extends MongoRepository { 36 | List findAllByProjectAndVersion(final ObjectId project, final ObjectId version); 37 | 38 | List findAllByProjectAndVersionIn(final ObjectId project, final Collection version); 39 | 40 | Optional findByProjectAndVersionAndNumber(final ObjectId project, final ObjectId version, final int number); 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/database/repository/ProjectCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.database.repository; 25 | 26 | import io.papermc.bibliothek.database.model.Project; 27 | import java.util.Optional; 28 | import org.bson.types.ObjectId; 29 | import org.springframework.data.mongodb.repository.MongoRepository; 30 | import org.springframework.stereotype.Repository; 31 | 32 | @Repository 33 | public interface ProjectCollection extends MongoRepository { 34 | Optional findByName(final String name); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/database/repository/VersionCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.database.repository; 25 | 26 | import io.papermc.bibliothek.database.model.Version; 27 | import java.util.List; 28 | import java.util.Optional; 29 | import org.bson.types.ObjectId; 30 | import org.springframework.data.mongodb.repository.MongoRepository; 31 | import org.springframework.stereotype.Repository; 32 | 33 | @Repository 34 | public interface VersionCollection extends MongoRepository { 35 | List findAllByProject(final ObjectId project); 36 | 37 | List findAllByProjectAndGroup(final ObjectId project, final ObjectId group); 38 | 39 | Optional findByProjectAndName(final ObjectId project, final String name); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/database/repository/VersionFamilyCollection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.database.repository; 25 | 26 | import io.papermc.bibliothek.database.model.VersionFamily; 27 | import java.util.List; 28 | import java.util.Optional; 29 | import org.bson.types.ObjectId; 30 | import org.springframework.data.mongodb.repository.MongoRepository; 31 | import org.springframework.stereotype.Repository; 32 | 33 | @Repository 34 | public interface VersionFamilyCollection extends MongoRepository { 35 | List findAllByProject(final ObjectId project); 36 | 37 | Optional findByProjectAndName(final ObjectId project, final String name); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/exception/Advice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.exception; 25 | 26 | import com.fasterxml.jackson.databind.ObjectMapper; 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.http.HttpStatus; 29 | import org.springframework.http.ResponseEntity; 30 | import org.springframework.web.bind.annotation.ControllerAdvice; 31 | import org.springframework.web.bind.annotation.ExceptionHandler; 32 | import org.springframework.web.bind.annotation.ResponseBody; 33 | import org.springframework.web.servlet.NoHandlerFoundException; 34 | 35 | @ControllerAdvice 36 | class Advice { 37 | private final ObjectMapper json; 38 | 39 | @Autowired 40 | private Advice(final ObjectMapper json) { 41 | this.json = json; 42 | } 43 | 44 | @ExceptionHandler(BuildNotFound.class) 45 | @ResponseBody 46 | public ResponseEntity buildNotFound(final BuildNotFound exception) { 47 | return this.error(HttpStatus.NOT_FOUND, "Build not found."); 48 | } 49 | 50 | @ExceptionHandler(DownloadFailed.class) 51 | @ResponseBody 52 | public ResponseEntity downloadFailed(final DownloadFailed exception) { 53 | return this.error(HttpStatus.INTERNAL_SERVER_ERROR, "An internal error occurred while serving your download."); 54 | } 55 | 56 | @ExceptionHandler(DownloadNotFound.class) 57 | @ResponseBody 58 | public ResponseEntity downloadNotFound(final DownloadNotFound exception) { 59 | return this.error(HttpStatus.NOT_FOUND, "Download not found."); 60 | } 61 | 62 | @ExceptionHandler(ProjectNotFound.class) 63 | @ResponseBody 64 | public ResponseEntity projectNotFound(final ProjectNotFound exception) { 65 | return this.error(HttpStatus.NOT_FOUND, "Project not found."); 66 | } 67 | 68 | @ExceptionHandler(VersionNotFound.class) 69 | @ResponseBody 70 | public ResponseEntity versionNotFound(final VersionNotFound exception) { 71 | return this.error(HttpStatus.NOT_FOUND, "Version not found."); 72 | } 73 | 74 | @ExceptionHandler(NoHandlerFoundException.class) 75 | @ResponseBody 76 | public ResponseEntity endpointNotFound(final NoHandlerFoundException exception) { 77 | return this.error(HttpStatus.NOT_FOUND, "Endpoint not found."); 78 | } 79 | 80 | private ResponseEntity error(final HttpStatus status, final String error) { 81 | return new ResponseEntity<>( 82 | this.json.createObjectNode() 83 | .put("error", error), 84 | status 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/exception/BuildNotFound.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.exception; 25 | 26 | import java.io.Serial; 27 | 28 | public class BuildNotFound extends RuntimeException { 29 | @Serial 30 | private static final long serialVersionUID = 4345323173317573160L; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/exception/DownloadFailed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.exception; 25 | 26 | import java.io.Serial; 27 | 28 | public class DownloadFailed extends RuntimeException { 29 | @Serial 30 | private static final long serialVersionUID = -1573093686056663600L; 31 | 32 | public DownloadFailed(final Throwable cause) { 33 | super(cause); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/exception/DownloadNotFound.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.exception; 25 | 26 | import java.io.Serial; 27 | 28 | public class DownloadNotFound extends RuntimeException { 29 | @Serial 30 | private static final long serialVersionUID = -1709491048606353671L; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/exception/ProjectNotFound.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.exception; 25 | 26 | import java.io.Serial; 27 | 28 | public class ProjectNotFound extends RuntimeException { 29 | @Serial 30 | private static final long serialVersionUID = 210738408624095602L; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/exception/VersionNotFound.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.exception; 25 | 26 | import java.io.Serial; 27 | 28 | public class VersionNotFound extends RuntimeException { 29 | @Serial 30 | private static final long serialVersionUID = 1716350953824887164L; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/filter/CorsFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.filter; 25 | 26 | import jakarta.servlet.Filter; 27 | import jakarta.servlet.FilterChain; 28 | import jakarta.servlet.ServletException; 29 | import jakarta.servlet.ServletRequest; 30 | import jakarta.servlet.ServletResponse; 31 | import jakarta.servlet.annotation.WebFilter; 32 | import jakarta.servlet.http.HttpServletResponse; 33 | import java.io.IOException; 34 | 35 | @WebFilter("/*") 36 | public class CorsFilter implements Filter { 37 | 38 | @Override 39 | public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { 40 | final HttpServletResponse httpServletResponse = (HttpServletResponse) response; 41 | httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); 42 | httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET"); 43 | chain.doFilter(request, response); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/util/BringOrderToChaos.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.util; 25 | 26 | import java.time.Instant; 27 | import java.util.Comparator; 28 | import java.util.Objects; 29 | 30 | public final class BringOrderToChaos { 31 | private BringOrderToChaos() { 32 | } 33 | 34 | public static Comparator timeOrNameComparator() { 35 | return (o1, o2) -> { 36 | final Instant t1 = o1.time(); 37 | final Instant t2 = o2.time(); 38 | // Both objects are not guaranteed to have a time present, but are guaranteed 39 | // to have a name present - we prefer to compare them by time, but in cases where 40 | // the time is not available on both objects we will compare them using their name 41 | if (t1 != null && t2 != null) { 42 | return t1.compareTo(t2); 43 | } 44 | final String n1 = Objects.requireNonNull(o1.name(), () -> "name of " + o1); 45 | final String n2 = Objects.requireNonNull(o2.name(), () -> "name of " + o2); 46 | return n1.compareTo(n2); 47 | }; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/util/HTTP.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.util; 25 | 26 | import java.nio.charset.StandardCharsets; 27 | import java.nio.file.Path; 28 | import java.time.Duration; 29 | import org.springframework.http.CacheControl; 30 | import org.springframework.http.ContentDisposition; 31 | import org.springframework.http.ResponseEntity; 32 | 33 | public final class HTTP { 34 | private HTTP() { 35 | } 36 | 37 | public static ResponseEntity cachedOk(final T response, final CacheControl cache) { 38 | return ResponseEntity.ok().cacheControl(cache).body(response); 39 | } 40 | 41 | public static CacheControl sMaxAgePublicCache(final Duration sMaxAge) { 42 | return CacheControl.empty() 43 | .cachePublic() 44 | .sMaxAge(sMaxAge); 45 | } 46 | 47 | public static ContentDisposition attachmentDisposition(final Path filename) { 48 | return ContentDisposition.attachment().filename(filename.getFileName().toString(), StandardCharsets.UTF_8).build(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/util/MediaTypes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.util; 25 | 26 | import org.jetbrains.annotations.Nullable; 27 | import org.springframework.http.MediaType; 28 | import org.springframework.http.MediaTypeFactory; 29 | 30 | public final class MediaTypes { 31 | 32 | public static final String APPLICATION_ZIP_VALUE = "application/zip"; 33 | public static final MediaType APPLICATION_ZIP = MediaType.parseMediaType(APPLICATION_ZIP_VALUE); 34 | 35 | private MediaTypes() { 36 | } 37 | 38 | public static @Nullable MediaType fromFileName(final String name) { 39 | final int index = name.lastIndexOf('.'); 40 | if (index != -1) { 41 | return fromFileExtension(name.substring(index + 1)); 42 | } 43 | return null; 44 | } 45 | 46 | public static @Nullable MediaType fromFileExtension(final String extension) { 47 | return switch (extension) { 48 | case "mcpack" -> APPLICATION_ZIP; 49 | default -> MediaTypeFactory.getMediaType("." + extension).orElse(MediaType.APPLICATION_OCTET_STREAM); 50 | }; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/util/NameSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.util; 25 | 26 | public interface NameSource { 27 | String name(); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/papermc/bibliothek/util/TimeSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of bibliothek, licensed under the MIT License. 3 | * 4 | * Copyright (c) 2019-2024 PaperMC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package io.papermc.bibliothek.util; 25 | 26 | import java.time.Instant; 27 | import org.jetbrains.annotations.UnknownNullability; 28 | 29 | public interface TimeSource { 30 | @UnknownNullability Instant time(); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | data: 3 | mongodb: 4 | # "library" instead of "bibliothek" is intentional here 5 | database: "library" 6 | mvc: 7 | throw-exception-if-no-handler-found: true 8 | web: 9 | resources: 10 | add-mappings: false 11 | springdoc: 12 | api-docs: 13 | path: "/openapi" 14 | show-actuator: false 15 | swagger-ui: 16 | disable-swagger-default-url: true 17 | operations-sorter: "alpha" 18 | path: "/docs/" 19 | show-common-extensions: true 20 | server: 21 | error: 22 | whitelabel: 23 | enabled: false 24 | --------------------------------------------------------------------------------