Compare commits

...

28 Commits

Author SHA1 Message Date
semantic-release-bot
2d7b4e7b7f chore(release): 1.2.0-dev.1 [skip ci]
# [1.2.0-dev.1](https://github.com/ReVanced/revanced-api/compare/v1.1.0...v1.2.0-dev.1) (2024-08-16)

### Features

* Move /latest routes to parent ([4e8e83d](4e8e83db1a))
2024-08-16 23:03:43 +00:00
oSumAtrIX
4e8e83db1a feat: Move /latest routes to parent
There is only ever current releases, so a latest route doesn't make sense.
2024-08-17 01:01:33 +02:00
oSumAtrIX
e113daa089 build: Bump dependencies to correctly serialize patches as JSON 2024-08-17 00:53:13 +02:00
oSumAtrIX
170edd3157 build: Bump Gradle 2024-08-17 00:20:07 +02:00
semantic-release-bot
d18e09cba3 chore(release): 1.1.0 [skip ci]
# [1.1.0](https://github.com/ReVanced/revanced-api/compare/v1.0.0...v1.1.0) (2024-07-15)

### Bug Fixes

* Don't encode public keys & instead send them raw ([435beae](435beae383))

### Features

* Add static file paths to remove env specific files in resources ([39d0b78](39d0b78c79))
* Convert static about file to documented route & add key parameter to about route ([dfe6df3](dfe6df3ef6))
2024-07-15 18:20:46 +00:00
oSumAtrIX
bc919b85f5 chore: Merge branch dev to main (#183) 2024-07-15 20:18:36 +02:00
semantic-release-bot
665de7bcd0 chore(release): 1.1.0-dev.1 [skip ci]
# [1.1.0-dev.1](https://github.com/ReVanced/revanced-api/compare/v1.0.0...v1.1.0-dev.1) (2024-07-15)

### Bug Fixes

* Don't encode public keys & instead send them raw ([435beae](435beae383))

### Features

* Add static file paths to remove env specific files in resources ([39d0b78](39d0b78c79))
* Convert static about file to documented route & add key parameter to about route ([dfe6df3](dfe6df3ef6))
2024-07-15 01:15:03 +00:00
oSumAtrIX
dfe6df3ef6 feat: Convert static about file to documented route & add key parameter to about route 2024-07-15 03:12:39 +02:00
oSumAtrIX
39d0b78c79 feat: Add static file paths to remove env specific files in resources 2024-07-15 02:30:21 +02:00
oSumAtrIX
435beae383 fix: Don't encode public keys & instead send them raw 2024-07-15 01:52:16 +02:00
semantic-release-bot
03fb28cd10 chore(release): 1.0.0 [skip ci]
# 1.0.0 (2024-07-13)

### Bug Fixes

* Add missing auth realm ([a6008a2](a6008a2fb6))
* Add missing OpenAPI docs ([d4ac471](d4ac47194e))
* add required headers ([#8](https://github.com/ReVanced/revanced-api/issues/8)) ([f4c10dc](f4c10dc064))
* Add uri to rate limiter request key ([e8c2488](e8c2488bc6))
* **ci:** add git dep ([d61ddcc](d61ddcc8ac))
* **ci:** fix git deps ([5564c2d](5564c2da09))
* Configure CORS correctly ([2ed4cf3](2ed4cf3b40))
* Correct env var comment ([9d7b049](9d7b049349))
* Correct persistence directory name ([6238e33](6238e33c42))
* **deps:** fix requirements file ([5f0ab26](5f0ab26ced))
* **deps:** missing pydantic ([089f29e](089f29e95f))
* **deps:** update poetry.lock ([b2c2fa7](b2c2fa7136))
* Don't configure server ([280dbc3](280dbc30f6))
* Encode defaults to fix OpenAPI spec ([e9d1c8f](e9d1c8fae0))
* Finish DB Model to API model transformation inside transaction ([89a577e](89a577e91a))
* fix codeql issues ([b5568db](b5568db79f))
* Fix OpenAPI docs casing of a word ([541783d](541783d959))
* Fix spelling mistake ([17ecf58](17ecf58e55))
* Increase pool size to mitigate overflow ([#113](https://github.com/ReVanced/revanced-api/issues/113)) ([5aed3d6](5aed3d6ce6))
* Move old API endpoint configuration from env to configuration file ([7e99e49](7e99e49af2))
* Move robots.txt to root ([2ade550](2ade550d58))
* Only list public members ([97a5d11](97a5d119ec))
* Remove punctuation ([f9cae1e](f9cae1ea56))
* remove revanced-api from tools map ([4800ee9](4800ee96a8))
* Serialize response correctly ([1dccfd2](1dccfd2def))
* Set body for all eligible request methods ([c6cacef](c6cacef907))
* unversioned endpoints placement under v0 should be at root ([8b29f49](8b29f49805))
* Use correct persistance folder path ([500a589](500a5896e3))
* Use correct proxy path ([ef92768](ef927688a3))
* Use correct resource path ([4dffd32](4dffd32c99))
* Use multidict 6.0.5, add setup tools as dev dependency ([af19446](af19446a67))

### Features

* Add `/list` route ([6c930ff](6c930fff9a))
* Add `preferred` field to socials ([#100](https://github.com/ReVanced/revanced-api/issues/100)) ([24c8f60](24c8f60a70))
* Add announcements API ([42f7318](42f731854d))
* Add announcements endpoints ([#91](https://github.com/ReVanced/revanced-api/issues/91)) ([8583e2a](8583e2a2bb))
* Add backend rate limit route ([b967170](b9671703be))
* Add bio field for team members ([c40d50c](c40d50c013))
* Add CLI ([a988ffb](a988ffbd23))
* Add configuration to specify public key id ([ad7d4b2](ad7d4b226f))
* add download_count to releases ([#118](https://github.com/ReVanced/revanced-api/issues/118)) ([665b913](665b913c04))
* add friendly crypto names ([#53](https://github.com/ReVanced/revanced-api/issues/53)) ([7b70780](7b707807cc))
* Add GPG key to team members ([71f58cf](71f58cf352))
* Add local ReVanced API server ([cd5d57f](cd5d57f8f8))
* Add manager route ([f814fe5](f814fe5825))
* add member gpg keys ([80cdb3b](80cdb3be6a))
* Add OpenAPI docs and cache to routes ([6ea63be](6ea63be490))
* add preferred field to donations ([#56](https://github.com/ReVanced/revanced-api/issues/56)) ([6b705b1](6b705b1054))
* Add proxy for old API ([39f54bb](39f54bbb32))
* Add rate limiting to routes ([80403f7](80403f7130))
* added branding ([#104](https://github.com/ReVanced/revanced-api/issues/104)) ([edcad62](edcad620f2))
* API Fixes and Adjustments ([#23](https://github.com/ReVanced/revanced-api/issues/23)) ([b18097e](b18097e030))
* API rewrite ([#2](https://github.com/ReVanced/revanced-api/issues/2)) ([45ef337](45ef33741c))
* better versioning engine ([8d36663](8d36663610))
* Change default port to avoid using existing port ([9825865](9825865bbc))
* Disallow all web crawlers ([#111](https://github.com/ReVanced/revanced-api/issues/111)) ([b69acfa](b69acfa8d7))
* Do not ignore, if `.env` file is missing ([24c6f4e](24c6f4e435))
* favicon ([a8126d7](a8126d785f))
* hostname filtering ([5482d9c](5482d9c442))
* Implement more routes and add configuration ([9999b24](9999b242ad))
* Improve routing paths ([df999c0](df999c00c4))
* info endpoint ([#71](https://github.com/ReVanced/revanced-api/issues/71)) ([9bbd056](9bbd056c1b))
* Initialize project ([8ae50b5](8ae50b543e))
* List more repository contributors ([19ebc82](19ebc827bf))
* Load system properties ([e373d26](e373d26998))
* manager-related endpoints ([3a128c4](3a128c4661))
* Move config file to CLI argument ([6a9f0ca](6a9f0cadac))
* **observability:** sentry ([99e645c](99e645c5f5))
* project init ([856fc66](856fc66717))
* remove appinfo capabilities ([10f5225](10f5225f51))
* remove codecov tests ([9c69fa3](9c69fa3b92))
* Remove Swagger and OpenAPI ([af0b086](af0b0865f4))
* Setup CI/CD ([c736a75](c736a75d92))
* Setup cors and cache ([205bcde](205bcde77a))
* Show default CLI option values ([db0bfc3](db0bfc3be5))
* Use auth digest instead of basic auth ([89e2acf](89e2acfebb))
* Use Jetty instead of Netty ([c23cd5c](c23cd5cdad))
* use objects for /socials and /donations ([#51](https://github.com/ReVanced/revanced-api/issues/51)) ([d4eac5c](d4eac5c757))
* use objects for contact field ([5922830](5922830e0b))

### Performance Improvements

* Cache latest announcements for constant access time ([1ca9952](1ca9952de8))
* Cache patches list instead of just the patches file ([7a1957d](7a1957d013))
* Make async db transactions and use List instead of Set ([a7d1892](a7d1892343))
2024-07-13 14:30:19 +00:00
oSumAtrIX
7214214d42 chore: Merge branch dev to main (#180) 2024-07-13 16:28:12 +02:00
semantic-release-bot
236e7e7907 chore(release): 1.0.0-dev.7 [skip ci]
# [1.0.0-dev.7](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.6...v1.0.0-dev.7) (2024-07-13)

### Bug Fixes

* Fix OpenAPI docs casing of a word ([541783d](541783d959))
2024-07-13 13:40:16 +00:00
oSumAtrIX
541783d959 fix: Fix OpenAPI docs casing of a word 2024-07-13 15:38:28 +02:00
semantic-release-bot
db41081155 chore(release): 1.0.0-dev.6 [skip ci]
# [1.0.0-dev.6](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.5...v1.0.0-dev.6) (2024-07-13)

### Features

* Add bio field for team members ([c40d50c](c40d50c013))
2024-07-13 00:48:57 +00:00
oSumAtrIX
c40d50c013 feat: Add bio field for team members 2024-07-13 02:47:03 +02:00
semantic-release-bot
b3c82535eb chore(release): 1.0.0-dev.5 [skip ci]
# [1.0.0-dev.5](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.4...v1.0.0-dev.5) (2024-07-12)

### Bug Fixes

* Move robots.txt to root ([2ade550](2ade550d58))
* Only list public members ([97a5d11](97a5d119ec))

### Features

* Add configuration to specify public key id ([ad7d4b2](ad7d4b226f))
* Add manager route ([f814fe5](f814fe5825))
2024-07-12 23:36:46 +00:00
oSumAtrIX
f814fe5825 feat: Add manager route 2024-07-13 01:34:42 +02:00
oSumAtrIX
a34b7c8c31 ci: Correct usage of repository variable 2024-07-13 00:46:19 +02:00
oSumAtrIX
ad7d4b226f feat: Add configuration to specify public key id 2024-07-13 00:28:16 +02:00
oSumAtrIX
97a5d119ec fix: Only list public members 2024-07-13 00:28:16 +02:00
oSumAtrIX
2ade550d58 fix: Move robots.txt to root 2024-07-13 00:28:15 +02:00
semantic-release-bot
f754ebb25c chore(release): 1.0.0-dev.4 [skip ci]
# [1.0.0-dev.4](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.3...v1.0.0-dev.4) (2024-07-11)

### Features

* List more repository contributors ([19ebc82](19ebc827bf))
2024-07-11 20:55:17 +00:00
oSumAtrIX
19ebc827bf feat: List more repository contributors 2024-07-11 22:53:27 +02:00
semantic-release-bot
e92fbdf1f4 chore(release): 1.0.0-dev.3 [skip ci]
# [1.0.0-dev.3](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.2...v1.0.0-dev.3) (2024-07-11)

### Bug Fixes

* Move old API endpoint configuration from env to configuration file ([7e99e49](7e99e49af2))
2024-07-11 02:28:50 +00:00
oSumAtrIX
7e99e49af2 fix: Move old API endpoint configuration from env to configuration file 2024-07-11 04:27:02 +02:00
semantic-release-bot
8f77736a69 chore(release): 1.0.0-dev.2 [skip ci]
# [1.0.0-dev.2](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.1...v1.0.0-dev.2) (2024-07-11)

### Bug Fixes

* Configure CORS correctly ([2ed4cf3](2ed4cf3b40))
2024-07-11 01:52:59 +00:00
oSumAtrIX
2ed4cf3b40 fix: Configure CORS correctly 2024-07-11 03:50:48 +02:00
29 changed files with 614 additions and 151 deletions

View File

@@ -1,7 +1,5 @@
# Optional token for API calls to the backend # Optional token for API calls to the backend
BACKEND_API_TOKEN= BACKEND_API_TOKEN=
# A URL to the old API to proxy for migration purposes
OLD_API_URL=
# Database connection details # Database connection details
DB_URL=jdbc:h2:./persistence/revanced-api DB_URL=jdbc:h2:./persistence/revanced-api

View File

@@ -45,7 +45,7 @@ jobs:
with: with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }} passphrase: ${{ secrets.GPG_PASSPHRASE }}
fingerprint: ${{ env.GPG_FINGERPRINT }} fingerprint: ${{ vars.GPG_FINGERPRINT }}
- name: Setup QEMU - name: Setup QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3

2
.gitignore vendored
View File

@@ -43,3 +43,5 @@ docker-compose.yml
patches-public-key.asc patches-public-key.asc
integrations-public-key.asc integrations-public-key.asc
node_modules/ node_modules/
static/
about.json

View File

@@ -1,3 +1,180 @@
# [1.2.0-dev.1](https://github.com/ReVanced/revanced-api/compare/v1.1.0...v1.2.0-dev.1) (2024-08-16)
### Features
* Move /latest routes to parent ([4e8e83d](https://github.com/ReVanced/revanced-api/commit/4e8e83db1a20c76a81967af4e7e3a8634649790a))
# [1.1.0](https://github.com/ReVanced/revanced-api/compare/v1.0.0...v1.1.0) (2024-07-15)
### Bug Fixes
* Don't encode public keys & instead send them raw ([435beae](https://github.com/ReVanced/revanced-api/commit/435beae3831fc8ce161aec676ff20f253b1caf66))
### Features
* Add static file paths to remove env specific files in resources ([39d0b78](https://github.com/ReVanced/revanced-api/commit/39d0b78c7919f684439b6f052ab3f064159c2a70))
* Convert static about file to documented route & add key parameter to about route ([dfe6df3](https://github.com/ReVanced/revanced-api/commit/dfe6df3ef6006d06681673bcfaf87c44c40ad446))
# [1.1.0-dev.1](https://github.com/ReVanced/revanced-api/compare/v1.0.0...v1.1.0-dev.1) (2024-07-15)
### Bug Fixes
* Don't encode public keys & instead send them raw ([435beae](https://github.com/ReVanced/revanced-api/commit/435beae3831fc8ce161aec676ff20f253b1caf66))
### Features
* Add static file paths to remove env specific files in resources ([39d0b78](https://github.com/ReVanced/revanced-api/commit/39d0b78c7919f684439b6f052ab3f064159c2a70))
* Convert static about file to documented route & add key parameter to about route ([dfe6df3](https://github.com/ReVanced/revanced-api/commit/dfe6df3ef6006d06681673bcfaf87c44c40ad446))
# 1.0.0 (2024-07-13)
### Bug Fixes
* Add missing auth realm ([a6008a2](https://github.com/ReVanced/revanced-api/commit/a6008a2fb6d01fb577dcf357a1d698b6c1068b31))
* Add missing OpenAPI docs ([d4ac471](https://github.com/ReVanced/revanced-api/commit/d4ac47194e51b3e708516150d1690a72830ab809))
* add required headers ([#8](https://github.com/ReVanced/revanced-api/issues/8)) ([f4c10dc](https://github.com/ReVanced/revanced-api/commit/f4c10dc064c4ac0fb6b8e612d680e4f6bda2b0c7))
* Add uri to rate limiter request key ([e8c2488](https://github.com/ReVanced/revanced-api/commit/e8c2488bc61793345c4b8171e520fb0127b34643))
* **ci:** add git dep ([d61ddcc](https://github.com/ReVanced/revanced-api/commit/d61ddcc8ac76d78b7a79c09bbfba812f652aa4cf))
* **ci:** fix git deps ([5564c2d](https://github.com/ReVanced/revanced-api/commit/5564c2da0946d67abab0f95e43fe6e6b108f5ec7))
* Configure CORS correctly ([2ed4cf3](https://github.com/ReVanced/revanced-api/commit/2ed4cf3b40caeb6181d068d411344e6732000f22))
* Correct env var comment ([9d7b049](https://github.com/ReVanced/revanced-api/commit/9d7b0493498bbf594928fc61181beadf6f59643b))
* Correct persistence directory name ([6238e33](https://github.com/ReVanced/revanced-api/commit/6238e33c42216885d5e9e8075bfc46eaf2a990ed))
* **deps:** fix requirements file ([5f0ab26](https://github.com/ReVanced/revanced-api/commit/5f0ab26cedef27cc95eec58ea4dce00904b53289))
* **deps:** missing pydantic ([089f29e](https://github.com/ReVanced/revanced-api/commit/089f29e95fc6b87f90885eb31b8e3460857224a8))
* **deps:** update poetry.lock ([b2c2fa7](https://github.com/ReVanced/revanced-api/commit/b2c2fa7136f6305703ba11d0bd5a87c0d563eaf3))
* Don't configure server ([280dbc3](https://github.com/ReVanced/revanced-api/commit/280dbc30f607483adad0bde6ab1b016d5da047ba))
* Encode defaults to fix OpenAPI spec ([e9d1c8f](https://github.com/ReVanced/revanced-api/commit/e9d1c8fae0bc46e761056197658c4bb045784104))
* Finish DB Model to API model transformation inside transaction ([89a577e](https://github.com/ReVanced/revanced-api/commit/89a577e91abbfcd2865e770088661eac4aeb4dd7))
* fix codeql issues ([b5568db](https://github.com/ReVanced/revanced-api/commit/b5568db79fa0620d06d6945b42a66744b6340bc5))
* Fix OpenAPI docs casing of a word ([541783d](https://github.com/ReVanced/revanced-api/commit/541783d9599c257f184d1b244e1b857b7c200227))
* Fix spelling mistake ([17ecf58](https://github.com/ReVanced/revanced-api/commit/17ecf58e550d13dd93ab69e1cf522366aeb3da3f))
* Increase pool size to mitigate overflow ([#113](https://github.com/ReVanced/revanced-api/issues/113)) ([5aed3d6](https://github.com/ReVanced/revanced-api/commit/5aed3d6ce6efcaf04b3cfa8f344623413518c9b1))
* Move old API endpoint configuration from env to configuration file ([7e99e49](https://github.com/ReVanced/revanced-api/commit/7e99e49af202c4ec0a0d7e61dd0182dd2097e867))
* Move robots.txt to root ([2ade550](https://github.com/ReVanced/revanced-api/commit/2ade550d58c0e4b53fa7417bef0064f4f476aed8))
* Only list public members ([97a5d11](https://github.com/ReVanced/revanced-api/commit/97a5d119ec415f9c25fbc50cb240603047defc73))
* Remove punctuation ([f9cae1e](https://github.com/ReVanced/revanced-api/commit/f9cae1ea56c93aded25159f6b0814bf84d192192))
* remove revanced-api from tools map ([4800ee9](https://github.com/ReVanced/revanced-api/commit/4800ee96a82c0a9fc1905c6960cd560b55304944))
* Serialize response correctly ([1dccfd2](https://github.com/ReVanced/revanced-api/commit/1dccfd2deff3c5de6a6cf2156cac8516b40bdd22))
* Set body for all eligible request methods ([c6cacef](https://github.com/ReVanced/revanced-api/commit/c6cacef907a5039ed029e1e26204aaba8e698aaa))
* unversioned endpoints placement under v0 should be at root ([8b29f49](https://github.com/ReVanced/revanced-api/commit/8b29f49805d4a651bdd26aa4958a3639760a2f4b))
* Use correct persistance folder path ([500a589](https://github.com/ReVanced/revanced-api/commit/500a5896e34f6a9600dcec2834b3d340161bb90a))
* Use correct proxy path ([ef92768](https://github.com/ReVanced/revanced-api/commit/ef927688a377c16fe37b578ea870207871c30056))
* Use correct resource path ([4dffd32](https://github.com/ReVanced/revanced-api/commit/4dffd32c99a5a3deafe21bc9e9960795ff93ff1d))
* Use multidict 6.0.5, add setup tools as dev dependency ([af19446](https://github.com/ReVanced/revanced-api/commit/af19446a67445e96624dfdb9f1fa6b4de77545c8))
### Features
* Add `/list` route ([6c930ff](https://github.com/ReVanced/revanced-api/commit/6c930fff9a99f9ee23edd3d661694d2074f53b23))
* Add `preferred` field to socials ([#100](https://github.com/ReVanced/revanced-api/issues/100)) ([24c8f60](https://github.com/ReVanced/revanced-api/commit/24c8f60a707ebec9465d9fdcd234b1c949a11a60))
* Add announcements API ([42f7318](https://github.com/ReVanced/revanced-api/commit/42f731854d0b91070bd7b075054d67d3d9e1d1b4))
* Add announcements endpoints ([#91](https://github.com/ReVanced/revanced-api/issues/91)) ([8583e2a](https://github.com/ReVanced/revanced-api/commit/8583e2a2bbdd62c4c10eb2af607f0bf6b686331c))
* Add backend rate limit route ([b967170](https://github.com/ReVanced/revanced-api/commit/b9671703be9f0842c00c38caa0953a177dc74491))
* Add bio field for team members ([c40d50c](https://github.com/ReVanced/revanced-api/commit/c40d50c01368cb6b9ab06857694ec51d27aba2cb))
* Add CLI ([a988ffb](https://github.com/ReVanced/revanced-api/commit/a988ffbd2303a79ee18be7263ef6cd45c7bc4daf))
* Add configuration to specify public key id ([ad7d4b2](https://github.com/ReVanced/revanced-api/commit/ad7d4b226f71fd965421dc2f2a51825c8e8b3036))
* add download_count to releases ([#118](https://github.com/ReVanced/revanced-api/issues/118)) ([665b913](https://github.com/ReVanced/revanced-api/commit/665b913c04edd8e55e7ba89839c5c39f8dfa42ac))
* add friendly crypto names ([#53](https://github.com/ReVanced/revanced-api/issues/53)) ([7b70780](https://github.com/ReVanced/revanced-api/commit/7b707807cc307c1a64abbb09983fb70d5b095698))
* Add GPG key to team members ([71f58cf](https://github.com/ReVanced/revanced-api/commit/71f58cf352e06b8504e00582e0c68aec55c4688b))
* Add local ReVanced API server ([cd5d57f](https://github.com/ReVanced/revanced-api/commit/cd5d57f8f87125df361e23715eda6e755203d727))
* Add manager route ([f814fe5](https://github.com/ReVanced/revanced-api/commit/f814fe5825eb8b864f2b0cba53ff721eba577eb4))
* add member gpg keys ([80cdb3b](https://github.com/ReVanced/revanced-api/commit/80cdb3be6ad2264977bf84269b09546a744576f5))
* Add OpenAPI docs and cache to routes ([6ea63be](https://github.com/ReVanced/revanced-api/commit/6ea63be490e7786c6486ee78c1fa38f302e8b81c))
* add preferred field to donations ([#56](https://github.com/ReVanced/revanced-api/issues/56)) ([6b705b1](https://github.com/ReVanced/revanced-api/commit/6b705b10545d5e6d2c9424275fbbd996bc45089f))
* Add proxy for old API ([39f54bb](https://github.com/ReVanced/revanced-api/commit/39f54bbb32512a6df255bbec6821f7f7acf91340))
* Add rate limiting to routes ([80403f7](https://github.com/ReVanced/revanced-api/commit/80403f7130cd48e68e802ee3111760256e49c77d))
* added branding ([#104](https://github.com/ReVanced/revanced-api/issues/104)) ([edcad62](https://github.com/ReVanced/revanced-api/commit/edcad620f29ee7e522bbc3a29db607f9823d9ce9))
* API Fixes and Adjustments ([#23](https://github.com/ReVanced/revanced-api/issues/23)) ([b18097e](https://github.com/ReVanced/revanced-api/commit/b18097e030c82e97f3880fd0788b562645f6e002))
* API rewrite ([#2](https://github.com/ReVanced/revanced-api/issues/2)) ([45ef337](https://github.com/ReVanced/revanced-api/commit/45ef33741c7a8fbd0144fda04370fe361a1d7c0c))
* better versioning engine ([8d36663](https://github.com/ReVanced/revanced-api/commit/8d36663610164d8198c18e5080184c1357f29254))
* Change default port to avoid using existing port ([9825865](https://github.com/ReVanced/revanced-api/commit/9825865bbc7505fc3e808d21a46a151151796591))
* Disallow all web crawlers ([#111](https://github.com/ReVanced/revanced-api/issues/111)) ([b69acfa](https://github.com/ReVanced/revanced-api/commit/b69acfa8d7f5385735f933a761239be6afd07384))
* Do not ignore, if `.env` file is missing ([24c6f4e](https://github.com/ReVanced/revanced-api/commit/24c6f4e4354b4e6da0e4a4e7f0ee0a7a5e3c90ed))
* favicon ([a8126d7](https://github.com/ReVanced/revanced-api/commit/a8126d785f8f828a746f7f1ce2fe20d1a605e8f8))
* hostname filtering ([5482d9c](https://github.com/ReVanced/revanced-api/commit/5482d9c44245bae75007935db6ec02fb553bfa2d))
* Implement more routes and add configuration ([9999b24](https://github.com/ReVanced/revanced-api/commit/9999b242ad05dcea9e6b021e3ed4eeead4567805))
* Improve routing paths ([df999c0](https://github.com/ReVanced/revanced-api/commit/df999c00c4c1f8645cc67ae19b732f0af9d71823))
* info endpoint ([#71](https://github.com/ReVanced/revanced-api/issues/71)) ([9bbd056](https://github.com/ReVanced/revanced-api/commit/9bbd056c1bc6a30059a8ed0a47823c228531e4c0))
* Initialize project ([8ae50b5](https://github.com/ReVanced/revanced-api/commit/8ae50b543eb78f2173c66acc2e9b676a7026f80a))
* List more repository contributors ([19ebc82](https://github.com/ReVanced/revanced-api/commit/19ebc827bfb54a597dd06f9d99bdc820ee9977ee))
* Load system properties ([e373d26](https://github.com/ReVanced/revanced-api/commit/e373d269982428357bd04b659d9d17110cf0d5d2))
* manager-related endpoints ([3a128c4](https://github.com/ReVanced/revanced-api/commit/3a128c4661b161dc9961837056a3b64b84824162))
* Move config file to CLI argument ([6a9f0ca](https://github.com/ReVanced/revanced-api/commit/6a9f0cadac9e09bcf834ef708fab180038ebd4bd))
* **observability:** sentry ([99e645c](https://github.com/ReVanced/revanced-api/commit/99e645c5f5604b5b1c8cbab0bed22177fc01c695))
* project init ([856fc66](https://github.com/ReVanced/revanced-api/commit/856fc667170d21ae5aba996bcc356ca44412e10d))
* remove appinfo capabilities ([10f5225](https://github.com/ReVanced/revanced-api/commit/10f5225f514a03b2532ec430b5caace6c6049b92))
* remove codecov tests ([9c69fa3](https://github.com/ReVanced/revanced-api/commit/9c69fa3b924b2ecda5c52256c383d8619ec61ece))
* Remove Swagger and OpenAPI ([af0b086](https://github.com/ReVanced/revanced-api/commit/af0b0865f4c2f22975a836b72ff0b902ddac1ce9))
* Setup CI/CD ([c736a75](https://github.com/ReVanced/revanced-api/commit/c736a75d92d97124e1aca392f37049449a786c84))
* Setup cors and cache ([205bcde](https://github.com/ReVanced/revanced-api/commit/205bcde77aad90e0eb49fc25961399f1e37698bb))
* Show default CLI option values ([db0bfc3](https://github.com/ReVanced/revanced-api/commit/db0bfc3be5b8d86466fa7f1a01a72247b4e878aa))
* Use auth digest instead of basic auth ([89e2acf](https://github.com/ReVanced/revanced-api/commit/89e2acfebb5e14f71d9ce3d962c5531070b7600e))
* Use Jetty instead of Netty ([c23cd5c](https://github.com/ReVanced/revanced-api/commit/c23cd5cdad01fee52aecdb36b93a619886edfa4d))
* use objects for /socials and /donations ([#51](https://github.com/ReVanced/revanced-api/issues/51)) ([d4eac5c](https://github.com/ReVanced/revanced-api/commit/d4eac5c757106c4762cb3e95f9e4b3509132b39f))
* use objects for contact field ([5922830](https://github.com/ReVanced/revanced-api/commit/5922830e0bf1c0504d3205d2fa864c76d0f09c02))
### Performance Improvements
* Cache latest announcements for constant access time ([1ca9952](https://github.com/ReVanced/revanced-api/commit/1ca9952de81feeae1333872a7741119d6d8e7814))
* Cache patches list instead of just the patches file ([7a1957d](https://github.com/ReVanced/revanced-api/commit/7a1957d013cfd0851dc0191b715e8f36c530d9d2))
* Make async db transactions and use List instead of Set ([a7d1892](https://github.com/ReVanced/revanced-api/commit/a7d1892343094ddb2762ca346759771e6a274081))
# [1.0.0-dev.7](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.6...v1.0.0-dev.7) (2024-07-13)
### Bug Fixes
* Fix OpenAPI docs casing of a word ([541783d](https://github.com/ReVanced/revanced-api/commit/541783d9599c257f184d1b244e1b857b7c200227))
# [1.0.0-dev.6](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.5...v1.0.0-dev.6) (2024-07-13)
### Features
* Add bio field for team members ([c40d50c](https://github.com/ReVanced/revanced-api/commit/c40d50c01368cb6b9ab06857694ec51d27aba2cb))
# [1.0.0-dev.5](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.4...v1.0.0-dev.5) (2024-07-12)
### Bug Fixes
* Move robots.txt to root ([2ade550](https://github.com/ReVanced/revanced-api/commit/2ade550d58c0e4b53fa7417bef0064f4f476aed8))
* Only list public members ([97a5d11](https://github.com/ReVanced/revanced-api/commit/97a5d119ec415f9c25fbc50cb240603047defc73))
### Features
* Add configuration to specify public key id ([ad7d4b2](https://github.com/ReVanced/revanced-api/commit/ad7d4b226f71fd965421dc2f2a51825c8e8b3036))
* Add manager route ([f814fe5](https://github.com/ReVanced/revanced-api/commit/f814fe5825eb8b864f2b0cba53ff721eba577eb4))
# [1.0.0-dev.4](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.3...v1.0.0-dev.4) (2024-07-11)
### Features
* List more repository contributors ([19ebc82](https://github.com/ReVanced/revanced-api/commit/19ebc827bfb54a597dd06f9d99bdc820ee9977ee))
# [1.0.0-dev.3](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.2...v1.0.0-dev.3) (2024-07-11)
### Bug Fixes
* Move old API endpoint configuration from env to configuration file ([7e99e49](https://github.com/ReVanced/revanced-api/commit/7e99e49af202c4ec0a0d7e61dd0182dd2097e867))
# [1.0.0-dev.2](https://github.com/ReVanced/revanced-api/compare/v1.0.0-dev.1...v1.0.0-dev.2) (2024-07-11)
### Bug Fixes
* Configure CORS correctly ([2ed4cf3](https://github.com/ReVanced/revanced-api/commit/2ed4cf3b40caeb6181d068d411344e6732000f22))
# 1.0.0-dev.1 (2024-07-10) # 1.0.0-dev.1 (2024-07-10)

View File

@@ -1,6 +1,7 @@
{ {
"name": "ReVanced", "name": "ReVanced",
"about": "ReVanced was born out of Vanced's discontinuation and it is our goal to continue the legacy of what Vanced left behind. Thanks to ReVanced Patcher, it's possible to create long-lasting patches for nearly any Android app. ReVanced's patching system is designed to allow patches to work on new versions of the apps automatically with bare minimum maintenance.", "about": "ReVanced was born out of Vanced's discontinuation and it is our goal to continue the legacy of what Vanced left behind. Thanks to ReVanced Patcher, it's possible to create long-lasting patches for nearly any Android app. ReVanced's patching system is designed to allow patches to work on new versions of the apps automatically with bare minimum maintenance.",
"keys": "https://api.revanced.app/keys",
"branding": { "branding": {
"logo": "https://raw.githubusercontent.com/ReVanced/revanced-branding/main/assets/revanced-logo/revanced-logo.svg" "logo": "https://raw.githubusercontent.com/ReVanced/revanced-branding/main/assets/revanced-logo/revanced-logo.svg"
}, },

View File

@@ -1,6 +1,7 @@
organization = "revanced" organization = "revanced"
patches = { repository = "revanced-patches", asset-regex = "jar$", signature-asset-regex = "asc$", public-key-file = "patches-public-key.asc" } patches = { repository = "revanced-patches", asset-regex = "jar$", signature-asset-regex = "asc$", public-key-file = "patches-public-key.asc", public-key-id = 0 }
integrations = { repository = "revanced-integrations", asset-regex = "apk$", signature-asset-regex = "asc$", public-key-file = "integrations-public-key.asc" } integrations = { repository = "revanced-integrations", asset-regex = "apk$", signature-asset-regex = "asc$", public-key-file = "integrations-public-key.asc", public-key-id = 0 }
manager = { repository = "revanced-manager", asset-regex = "apk$" }
contributors-repositories = [ contributors-repositories = [
"revanced-patcher", "revanced-patcher",
"revanced-patches", "revanced-patches",
@@ -10,5 +11,12 @@ contributors-repositories = [
"revanced-manager", "revanced-manager",
] ]
api-version = 1 api-version = 1
cors = { host = "*.revanced.app", sub-domains = [] } cors-allowed-hosts = [
"revanced.app",
"*.revanced.app"
]
endpoint = "https://api.revanced.app" endpoint = "https://api.revanced.app"
old-api-endpoint = "https://old-api.revanced.app"
static-files-path = "static/root"
versioned-static-files-path = "static/versioned"
about-json-file-path = "about.json"

View File

@@ -1,14 +0,0 @@
organization = "revanced"
patches = { repository = "revanced-patches", asset-regex = "jar$", signature-asset-regex = "asc$", public-key-file = "key.asc" }
integrations = { repository = "revanced-integrations", asset-regex = "apk$", signature-asset-regex = "asc$", public-key-file = "key.asc" }
contributors-repositories = [
"revanced-patcher",
"revanced-patches",
"revanced-integrations",
"revanced-website",
"revanced-cli",
"revanced-manager",
]
api-version = 1
cors = { host = "*.127.0.0.1:8888", sub-domains = [] }
endpoint = "http://127.0.0.1:8888/"

View File

@@ -8,6 +8,8 @@ services:
- /data/revanced-api/configuration.toml:/app/configuration.toml - /data/revanced-api/configuration.toml:/app/configuration.toml
- /data/revanced-api/patches-public-key.asc:/app/patches-public-key.asc - /data/revanced-api/patches-public-key.asc:/app/patches-public-key.asc
- /data/revanced-api/integrations-public-key.asc:/app/integrations-public-key.asc - /data/revanced-api/integrations-public-key.asc:/app/integrations-public-key.asc
- /data/revanced-api/static:/app/static
- /data/revanced-api/about.json:/app/about.json
environment: environment:
- COMMAND=start - COMMAND=start
ports: ports:

View File

@@ -1,4 +1,4 @@
org.gradle.parallel = true org.gradle.parallel = true
org.gradle.caching = true org.gradle.caching = true
kotlin.code.style = official kotlin.code.style = official
version = 1.0.0-dev.1 version = 1.2.0-dev.1

View File

@@ -10,8 +10,8 @@ ktor = "2.3.7"
ktoml = "0.5.2" ktoml = "0.5.2"
picocli = "4.7.6" picocli = "4.7.6"
datetime = "0.6.0" datetime = "0.6.0"
revanced-patcher = "19.3.1" revanced-patcher = "20.0.0"
revanced-library = "2.3.0" revanced-library = "3.0.1-dev.1"
caffeine = "3.1.8" caffeine = "3.1.8"
bouncy-castle = "1.78.1" bouncy-castle = "1.78.1"

Binary file not shown.

View File

@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612 distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

5
gradlew vendored
View File

@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum

2
gradlew.bat vendored
View File

@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################

View File

@@ -123,16 +123,18 @@ fun Application.configureDependencies(
AuthService(issuer, validityInMin, jwtSecret, authSHA256DigestString) AuthService(issuer, validityInMin, jwtSecret, authSHA256DigestString)
} }
single { single {
val configuration = get<ConfigurationRepository>()
OldApiService( OldApiService(
get { get {
val defaultRequestUri = get<Dotenv>()["OLD_API_URL"] parameterArrayOf(configuration.oldApiEndpoint)
parameterArrayOf(defaultRequestUri)
}, },
) )
} }
singleOf(::AnnouncementService) singleOf(::AnnouncementService)
singleOf(::SignatureService) singleOf(::SignatureService)
singleOf(::PatchesService) singleOf(::PatchesService)
singleOf(::ManagerService)
singleOf(::ApiService) singleOf(::ApiService)
} }

View File

@@ -4,8 +4,12 @@ import io.bkbn.kompendium.core.plugin.NotarizedRoute
import io.ktor.http.* import io.ktor.http.*
import io.ktor.http.content.* import io.ktor.http.content.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.http.content.*
import io.ktor.server.plugins.cachingheaders.* import io.ktor.server.plugins.cachingheaders.*
import io.ktor.server.response.* import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.io.File
import java.nio.file.Path
import kotlin.time.Duration import kotlin.time.Duration
internal suspend fun ApplicationCall.respondOrNotFound(value: Any?) = respond(value ?: HttpStatusCode.NotFound) internal suspend fun ApplicationCall.respondOrNotFound(value: Any?) = respond(value ?: HttpStatusCode.NotFound)
@@ -25,3 +29,14 @@ internal fun ApplicationCallPipeline.installCache(cacheControl: CacheControl) =
internal fun ApplicationCallPipeline.installNotarizedRoute(configure: NotarizedRoute.Config.() -> Unit = {}) = internal fun ApplicationCallPipeline.installNotarizedRoute(configure: NotarizedRoute.Config.() -> Unit = {}) =
install(NotarizedRoute(), configure) install(NotarizedRoute(), configure)
internal fun Route.staticFiles(
remotePath: String,
dir: Path,
block: StaticContentConfig<File>.() -> Unit = {
contentType {
ContentType.Application.Json
}
extensions("json")
},
) = staticFiles(remotePath, dir.toFile(), null, block)

View File

@@ -13,10 +13,12 @@ fun Application.configureHTTP() {
val configurationRepository = get<ConfigurationRepository>() val configurationRepository = get<ConfigurationRepository>()
install(CORS) { install(CORS) {
allowHost( configurationRepository.corsAllowedHosts.forEach { host ->
host = configurationRepository.cors.host, allowHost(
subDomains = configurationRepository.cors.subDomains, host = host,
) schemes = listOf("http", "https")
)
}
} }
install(RateLimit) { install(RateLimit) {

View File

@@ -1,12 +1,14 @@
package app.revanced.api.configuration package app.revanced.api.configuration
import app.revanced.api.configuration.repository.ConfigurationRepository import app.revanced.api.configuration.repository.ConfigurationRepository
import app.revanced.api.configuration.routes.*
import app.revanced.api.configuration.routes.announcementsRoute import app.revanced.api.configuration.routes.announcementsRoute
import app.revanced.api.configuration.routes.apiRoute
import app.revanced.api.configuration.routes.oldApiRoute import app.revanced.api.configuration.routes.oldApiRoute
import app.revanced.api.configuration.routes.patchesRoute import app.revanced.api.configuration.routes.patchesRoute
import app.revanced.api.configuration.routes.rootRoute
import io.bkbn.kompendium.core.routes.redoc import io.bkbn.kompendium.core.routes.redoc
import io.bkbn.kompendium.core.routes.swagger import io.bkbn.kompendium.core.routes.swagger
import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.minutes
@@ -18,9 +20,37 @@ internal fun Application.configureRouting() = routing {
installCache(5.minutes) installCache(5.minutes)
route("/v${configuration.apiVersion}") { route("/v${configuration.apiVersion}") {
patchesRoute()
announcementsRoute() announcementsRoute()
rootRoute() patchesRoute()
managerRoute()
apiRoute()
}
staticFiles("/", configuration.staticFilesPath) {
contentType {
when (it.extension) {
"json" -> ContentType.Application.Json
"asc" -> ContentType.Text.Plain
"ico" -> ContentType.Image.XIcon
"svg" -> ContentType.Image.SVG
"jpg", "jpeg" -> ContentType.Image.JPEG
"png" -> ContentType.Image.PNG
"gif" -> ContentType.Image.GIF
"mp4" -> ContentType.Video.MP4
"ogg" -> ContentType.Video.OGG
"mp3" -> ContentType.Audio.MPEG
"css" -> ContentType.Text.CSS
"js" -> ContentType.Application.JavaScript
"html" -> ContentType.Text.Html
"xml" -> ContentType.Application.Xml
"pdf" -> ContentType.Application.Pdf
"zip" -> ContentType.Application.Zip
"gz" -> ContentType.Application.GZip
else -> ContentType.Application.OctetStream
}
}
extensions("json", "asc")
} }
swagger(pageTitle = "ReVanced API", path = "/") swagger(pageTitle = "ReVanced API", path = "/")

View File

@@ -1,6 +1,9 @@
package app.revanced.api.configuration.repository package app.revanced.api.configuration.repository
import app.revanced.api.configuration.schema.APIAbout
import app.revanced.api.configuration.services.ManagerService
import app.revanced.api.configuration.services.PatchesService import app.revanced.api.configuration.services.PatchesService
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -9,7 +12,11 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNamingStrategy
import kotlinx.serialization.json.decodeFromStream
import java.io.File import java.io.File
import java.nio.file.Path
/** /**
* The repository storing the configuration for the API. * The repository storing the configuration for the API.
@@ -17,27 +24,46 @@ import java.io.File
* @property organization The API backends organization name where the repositories for the patches and integrations are. * @property organization The API backends organization name where the repositories for the patches and integrations are.
* @property patches The source of the patches. * @property patches The source of the patches.
* @property integrations The source of the integrations. * @property integrations The source of the integrations.
* @property manager The source of the manager.
* @property contributorsRepositoryNames The names of the repositories to get contributors from. * @property contributorsRepositoryNames The names of the repositories to get contributors from.
* @property apiVersion The version to use for the API. * @property apiVersion The version to use for the API.
* @property cors The CORS configuration. * @property corsAllowedHosts The hosts allowed to make requests to the API.
* @property endpoint The endpoint of the API. * @property endpoint The endpoint of the API.
* @property oldApiEndpoint The endpoint of the old API to proxy requests to.
* @property staticFilesPath The path to the static files to be served under the root path.
* @property versionedStaticFilesPath The path to the static files to be served under a versioned path.
* @property about The path to the json file deserialized to [APIAbout]
* (because com.akuleshov7.ktoml.Toml does not support nested tables).
*/ */
@Serializable @Serializable
internal class ConfigurationRepository( internal class ConfigurationRepository(
val organization: String, val organization: String,
val patches: AssetConfiguration, val patches: SignedAssetConfiguration,
val integrations: AssetConfiguration, val integrations: SignedAssetConfiguration,
val manager: AssetConfiguration,
@SerialName("contributors-repositories") @SerialName("contributors-repositories")
val contributorsRepositoryNames: Set<String>, val contributorsRepositoryNames: Set<String>,
@SerialName("api-version") @SerialName("api-version")
val apiVersion: Int = 1, val apiVersion: Int = 1,
val cors: Cors, @SerialName("cors-allowed-hosts")
val corsAllowedHosts: Set<String>,
val endpoint: String, val endpoint: String,
@SerialName("old-api-endpoint")
val oldApiEndpoint: String,
@Serializable(with = PathSerializer::class)
@SerialName("static-files-path")
val staticFilesPath: Path,
@Serializable(with = PathSerializer::class)
@SerialName("versioned-static-files-path")
val versionedStaticFilesPath: Path,
@Serializable(with = AboutSerializer::class)
@SerialName("about-json-file-path")
val about: APIAbout,
) { ) {
/** /**
* An asset configuration. * Am asset configuration whose asset is signed.
* *
* [PatchesService] uses [BackendRepository] to get assets from its releases. * [PatchesService] for example uses [BackendRepository] to get assets from its releases.
* A release contains multiple assets. * A release contains multiple assets.
* *
* This configuration is used in [ConfigurationRepository] * This configuration is used in [ConfigurationRepository]
@@ -47,9 +73,10 @@ internal class ConfigurationRepository(
* @property assetRegex The regex matching the asset name. * @property assetRegex The regex matching the asset name.
* @property signatureAssetRegex The regex matching the signature asset name to verify the asset. * @property signatureAssetRegex The regex matching the signature asset name to verify the asset.
* @property publicKeyFile The public key file to verify the signature of the asset. * @property publicKeyFile The public key file to verify the signature of the asset.
* @property publicKeyId The ID of the public key to verify the signature of the asset.
*/ */
@Serializable @Serializable
internal class AssetConfiguration( internal class SignedAssetConfiguration(
val repository: String, val repository: String,
@Serializable(with = RegexSerializer::class) @Serializable(with = RegexSerializer::class)
@SerialName("asset-regex") @SerialName("asset-regex")
@@ -60,19 +87,28 @@ internal class ConfigurationRepository(
@Serializable(with = FileSerializer::class) @Serializable(with = FileSerializer::class)
@SerialName("public-key-file") @SerialName("public-key-file")
val publicKeyFile: File, val publicKeyFile: File,
@SerialName("public-key-id")
val publicKeyId: Long,
) )
/** /**
* The CORS configuration. * Am asset configuration.
* *
* @property host The host of the API to configure CORS. * [ManagerService] for example uses [BackendRepository] to get assets from its releases.
* @property subDomains The subdomains to allow for CORS. * A release contains multiple assets.
*
* This configuration is used in [ConfigurationRepository]
* to determine which release assets from repositories to get and to verify them.
*
* @property repository The repository in which releases are made to get an asset.
* @property assetRegex The regex matching the asset name.
*/ */
@Serializable @Serializable
internal class Cors( internal class AssetConfiguration(
val host: String, val repository: String,
@SerialName("sub-domains") @Serializable(with = RegexSerializer::class)
val subDomains: List<String>, @SerialName("asset-regex")
val assetRegex: Regex,
) )
} }
@@ -91,3 +127,23 @@ private object FileSerializer : KSerializer<File> {
override fun deserialize(decoder: Decoder) = File(decoder.decodeString()) override fun deserialize(decoder: Decoder) = File(decoder.decodeString())
} }
private object PathSerializer : KSerializer<Path> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Path", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Path) = encoder.encodeString(value.toString())
override fun deserialize(decoder: Decoder): Path = Path.of(decoder.decodeString())
}
private object AboutSerializer : KSerializer<APIAbout> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("APIAbout", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: APIAbout) = error("Serializing APIAbout is not supported")
@OptIn(ExperimentalSerializationApi::class)
val json = Json { namingStrategy = JsonNamingStrategy.SnakeCase }
override fun deserialize(decoder: Decoder): APIAbout =
json.decodeFromStream(File(decoder.decodeString()).inputStream())
}

View File

@@ -16,6 +16,7 @@ import kotlinx.coroutines.*
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime import kotlinx.datetime.toLocalDateTime
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
class GitHubBackendRepository(client: HttpClient) : BackendRepository(client) { class GitHubBackendRepository(client: HttpClient) : BackendRepository(client) {
@@ -66,10 +67,10 @@ class GitHubBackendRepository(client: HttpClient) : BackendRepository(client) {
override suspend fun members(organization: String): List<BackendMember> { override suspend fun members(organization: String): List<BackendMember> {
// Get the list of members of the organization. // Get the list of members of the organization.
val members: List<GitHubOrganization.GitHubMember> = client.get(Organization.Members(organization)).body() val publicMembers: List<GitHubOrganization.GitHubMember> = client.get(Organization.PublicMembers(organization)).body()
return coroutineScope { return coroutineScope {
members.map { member -> publicMembers.map { member ->
async { async {
awaitAll( awaitAll(
async { async {
@@ -186,15 +187,14 @@ class User(val login: String) {
} }
class Organization { class Organization {
@Resource("/orgs/{org}/members") @Resource("/orgs/{org}/public_members")
class Members(val org: String) class PublicMembers(val org: String)
class Repository { class Repository {
@Resource("/repos/{owner}/{repo}/contributors") @Resource("/repos/{owner}/{repo}/contributors")
class Contributors(val owner: String, val repo: String) class Contributors(val owner: String, val repo: String, @SerialName("per_page") val perPage: Int = 100)
@Resource("/repos/{owner}/{repo}/releases") class Releases {
class Releases(val owner: String, val repo: String) {
@Resource("/repos/{owner}/{repo}/releases/tags/{tag}") @Resource("/repos/{owner}/{repo}/releases/tags/{tag}")
class Tag(val owner: String, val repo: String, val tag: String) class Tag(val owner: String, val repo: String, val tag: String)

View File

@@ -1,9 +1,11 @@
package app.revanced.api.configuration.routes package app.revanced.api.configuration.routes
import app.revanced.api.configuration.*
import app.revanced.api.configuration.installCache import app.revanced.api.configuration.installCache
import app.revanced.api.configuration.installNoCache import app.revanced.api.configuration.installNoCache
import app.revanced.api.configuration.installNotarizedRoute import app.revanced.api.configuration.installNotarizedRoute
import app.revanced.api.configuration.respondOrNotFound import app.revanced.api.configuration.respondOrNotFound
import app.revanced.api.configuration.schema.APIAbout
import app.revanced.api.configuration.schema.APIContributable import app.revanced.api.configuration.schema.APIContributable
import app.revanced.api.configuration.schema.APIMember import app.revanced.api.configuration.schema.APIMember
import app.revanced.api.configuration.schema.APIRateLimit import app.revanced.api.configuration.schema.APIRateLimit
@@ -13,14 +15,13 @@ import io.bkbn.kompendium.core.metadata.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.auth.* import io.ktor.server.auth.*
import io.ktor.server.http.content.*
import io.ktor.server.plugins.ratelimit.* import io.ktor.server.plugins.ratelimit.*
import io.ktor.server.response.* import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.days
import org.koin.ktor.ext.get as koinGet import org.koin.ktor.ext.get as koinGet
internal fun Route.rootRoute() { internal fun Route.apiRoute() {
val apiService = koinGet<ApiService>() val apiService = koinGet<ApiService>()
val authService = koinGet<AuthService>() val authService = koinGet<AuthService>()
@@ -56,6 +57,16 @@ internal fun Route.rootRoute() {
} }
} }
route("about") {
installCache(1.days)
installAboutRouteDocumentation()
get {
call.respond(apiService.about)
}
}
route("ping") { route("ping") {
installNoCache() installNoCache()
@@ -75,14 +86,26 @@ internal fun Route.rootRoute() {
} }
} }
staticResources("/", "/app/revanced/api/static") { staticFiles("/", apiService.versionedStaticFilesPath)
contentType { ContentType.Application.Json } }
extensions("json") }
private fun Route.installAboutRouteDocumentation() = installNotarizedRoute {
tags = setOf("API")
get = GetInfo.builder {
description("Get information about the API")
summary("Get about")
response {
description("Information about the API")
mediaTypes("application/json")
responseCode(HttpStatusCode.OK)
responseType<APIAbout>()
} }
} }
} }
fun Route.installRateLimitRouteDocumentation() = installNotarizedRoute { private fun Route.installRateLimitRouteDocumentation() = installNotarizedRoute {
tags = setOf("API") tags = setOf("API")
get = GetInfo.builder { get = GetInfo.builder {
@@ -97,7 +120,7 @@ fun Route.installRateLimitRouteDocumentation() = installNotarizedRoute {
} }
} }
fun Route.installPingRouteDocumentation() = installNotarizedRoute { private fun Route.installPingRouteDocumentation() = installNotarizedRoute {
tags = setOf("API") tags = setOf("API")
head = HeadInfo.builder { head = HeadInfo.builder {
@@ -111,7 +134,7 @@ fun Route.installPingRouteDocumentation() = installNotarizedRoute {
} }
} }
fun Route.installTeamRouteDocumentation() = installNotarizedRoute { private fun Route.installTeamRouteDocumentation() = installNotarizedRoute {
tags = setOf("API") tags = setOf("API")
get = GetInfo.builder { get = GetInfo.builder {
@@ -126,7 +149,7 @@ fun Route.installTeamRouteDocumentation() = installNotarizedRoute {
} }
} }
fun Route.installContributorsRouteDocumentation() = installNotarizedRoute { private fun Route.installContributorsRouteDocumentation() = installNotarizedRoute {
tags = setOf("API") tags = setOf("API")
get = GetInfo.builder { get = GetInfo.builder {
@@ -141,7 +164,7 @@ fun Route.installContributorsRouteDocumentation() = installNotarizedRoute {
} }
} }
fun Route.installTokenRouteDocumentation() = installNotarizedRoute { private fun Route.installTokenRouteDocumentation() = installNotarizedRoute {
tags = setOf("API") tags = setOf("API")
get = GetInfo.builder { get = GetInfo.builder {

View File

@@ -0,0 +1,64 @@
package app.revanced.api.configuration.routes
import app.revanced.api.configuration.installNotarizedRoute
import app.revanced.api.configuration.schema.APIManagerAsset
import app.revanced.api.configuration.schema.APIRelease
import app.revanced.api.configuration.schema.APIReleaseVersion
import app.revanced.api.configuration.services.ManagerService
import io.bkbn.kompendium.core.metadata.GetInfo
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.plugins.ratelimit.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import org.koin.ktor.ext.get as koinGet
internal fun Route.managerRoute() = route("manager") {
val managerService = koinGet<ManagerService>()
installManagerRouteDocumentation()
rateLimit(RateLimitName("weak")) {
get {
call.respond(managerService.latestRelease())
}
route("version") {
installManagerVersionRouteDocumentation()
get {
call.respond(managerService.latestVersion())
}
}
}
}
private fun Route.installManagerRouteDocumentation() = installNotarizedRoute {
tags = setOf("Manager")
get = GetInfo.builder {
description("Get the current manager release")
summary("Get current manager release")
response {
description("The latest manager release")
mediaTypes("application/json")
responseCode(HttpStatusCode.OK)
responseType<APIRelease<APIManagerAsset>>()
}
}
}
private fun Route.installManagerVersionRouteDocumentation() = installNotarizedRoute {
tags = setOf("Manager")
get = GetInfo.builder {
description("Get the current manager release version")
summary("Get current manager release version")
response {
description("The current manager release version")
mediaTypes("application/json")
responseCode(HttpStatusCode.OK)
responseType<APIReleaseVersion>()
}
}
}

View File

@@ -3,6 +3,7 @@ package app.revanced.api.configuration.routes
import app.revanced.api.configuration.installCache import app.revanced.api.configuration.installCache
import app.revanced.api.configuration.installNotarizedRoute import app.revanced.api.configuration.installNotarizedRoute
import app.revanced.api.configuration.schema.APIAssetPublicKeys import app.revanced.api.configuration.schema.APIAssetPublicKeys
import app.revanced.api.configuration.schema.APIPatchesAsset
import app.revanced.api.configuration.schema.APIRelease import app.revanced.api.configuration.schema.APIRelease
import app.revanced.api.configuration.schema.APIReleaseVersion import app.revanced.api.configuration.schema.APIReleaseVersion
import app.revanced.api.configuration.services.PatchesService import app.revanced.api.configuration.services.PatchesService
@@ -18,30 +19,28 @@ import org.koin.ktor.ext.get as koinGet
internal fun Route.patchesRoute() = route("patches") { internal fun Route.patchesRoute() = route("patches") {
val patchesService = koinGet<PatchesService>() val patchesService = koinGet<PatchesService>()
route("latest") { installPatchesRouteDocumentation()
installLatestPatchesRouteDocumentation()
rateLimit(RateLimitName("weak")) { rateLimit(RateLimitName("weak")) {
get { get {
call.respond(patchesService.latestRelease()) call.respond(patchesService.latestRelease())
}
route("version") {
installLatestPatchesVersionRouteDocumentation()
get {
call.respond(patchesService.latestVersion())
}
}
} }
rateLimit(RateLimitName("strong")) { route("version") {
route("list") { installPatchesVersionRouteDocumentation()
installLatestPatchesListRouteDocumentation()
get { get {
call.respondBytes(ContentType.Application.Json) { patchesService.list() } call.respond(patchesService.latestVersion())
} }
}
}
rateLimit(RateLimitName("strong")) {
route("list") {
installPatchesListRouteDocumentation()
get {
call.respondBytes(ContentType.Application.Json) { patchesService.list() }
} }
} }
} }
@@ -59,29 +58,29 @@ internal fun Route.patchesRoute() = route("patches") {
} }
} }
fun Route.installLatestPatchesRouteDocumentation() = installNotarizedRoute { private fun Route.installPatchesRouteDocumentation() = installNotarizedRoute {
tags = setOf("Patches") tags = setOf("Patches")
get = GetInfo.builder { get = GetInfo.builder {
description("Get the latest patches release") description("Get the current patches release")
summary("Get latest patches release") summary("Get current patches release")
response { response {
description("The latest patches release") description("The current patches release")
mediaTypes("application/json") mediaTypes("application/json")
responseCode(HttpStatusCode.OK) responseCode(HttpStatusCode.OK)
responseType<APIRelease>() responseType<APIRelease<APIPatchesAsset>>()
} }
} }
} }
fun Route.installLatestPatchesVersionRouteDocumentation() = installNotarizedRoute { private fun Route.installPatchesVersionRouteDocumentation() = installNotarizedRoute {
tags = setOf("Patches") tags = setOf("Patches")
get = GetInfo.builder { get = GetInfo.builder {
description("Get the latest patches release version") description("Get the current patches release version")
summary("Get latest patches release version") summary("Get current patches release version")
response { response {
description("The latest patches release version") description("The current patches release version")
mediaTypes("application/json") mediaTypes("application/json")
responseCode(HttpStatusCode.OK) responseCode(HttpStatusCode.OK)
responseType<APIReleaseVersion>() responseType<APIReleaseVersion>()
@@ -89,12 +88,12 @@ fun Route.installLatestPatchesVersionRouteDocumentation() = installNotarizedRout
} }
} }
fun Route.installLatestPatchesListRouteDocumentation() = installNotarizedRoute { private fun Route.installPatchesListRouteDocumentation() = installNotarizedRoute {
tags = setOf("Patches") tags = setOf("Patches")
get = GetInfo.builder { get = GetInfo.builder {
description("Get the list of patches from the latest patches release") description("Get the list of patches from the current patches release")
summary("Get list of patches from latest patches release") summary("Get list of patches from current patches release")
response { response {
description("The list of patches") description("The list of patches")
mediaTypes("application/json") mediaTypes("application/json")
@@ -104,7 +103,7 @@ fun Route.installLatestPatchesListRouteDocumentation() = installNotarizedRoute {
} }
} }
fun Route.installPatchesPublicKeyRouteDocumentation() = installNotarizedRoute { private fun Route.installPatchesPublicKeyRouteDocumentation() = installNotarizedRoute {
tags = setOf("Patches") tags = setOf("Patches")
get = GetInfo.builder { get = GetInfo.builder {

View File

@@ -3,15 +3,6 @@ package app.revanced.api.configuration.schema
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable
class APIRelease(
val version: String,
val createdAt: LocalDateTime,
val description: String,
// Using a list instead of a set because set semantics are unnecessary here.
val assets: List<APIAsset>,
)
interface APIUser { interface APIUser {
val name: String val name: String
val avatarUrl: String val avatarUrl: String
@@ -23,6 +14,7 @@ class APIMember(
override val name: String, override val name: String,
override val avatarUrl: String, override val avatarUrl: String,
override val url: String, override val url: String,
val bio: String?,
val gpgKey: APIGpgKey?, val gpgKey: APIGpgKey?,
) : APIUser ) : APIUser
@@ -48,7 +40,21 @@ class APIContributable(
) )
@Serializable @Serializable
class APIAsset( class APIRelease<T>(
val version: String,
val createdAt: LocalDateTime,
val description: String,
// Using a list instead of a set because set semantics are unnecessary here.
val assets: List<T>,
)
@Serializable
class APIManagerAsset(
val downloadUrl: String,
)
@Serializable
class APIPatchesAsset(
val downloadUrl: String, val downloadUrl: String,
val signatureDownloadUrl: String, val signatureDownloadUrl: String,
// TODO: Remove this eventually when integrations are merged into patches. // TODO: Remove this eventually when integrations are merged into patches.
@@ -114,3 +120,55 @@ class APIAssetPublicKeys(
val patchesPublicKey: String, val patchesPublicKey: String,
val integrationsPublicKey: String, val integrationsPublicKey: String,
) )
@Serializable
class APIAbout(
val name: String,
val about: String,
val keys: String,
val branding: Branding?,
val contact: Contact?,
// Using a list instead of a set because set semantics are unnecessary here.
val socials: List<Social>?,
val donations: Donations?,
) {
@Serializable
class Branding(
val logo: String,
)
@Serializable
class Contact(
val email: String,
)
@Serializable
class Social(
val name: String,
val url: String,
val preferred: Boolean? = false,
)
@Serializable
class Wallet(
val network: String,
val currencyCode: String,
val address: String,
val preferred: Boolean? = false,
)
@Serializable
class Link(
val name: String,
val url: String,
val preferred: Boolean? = false,
)
@Serializable
class Donations(
// Using a list instead of a set because set semantics are unnecessary here.
val wallets: List<Wallet>?,
// Using a list instead of a set because set semantics are unnecessary here.
val links: List<Link>?,
)
}

View File

@@ -12,6 +12,9 @@ internal class ApiService(
private val backendRepository: BackendRepository, private val backendRepository: BackendRepository,
private val configurationRepository: ConfigurationRepository, private val configurationRepository: ConfigurationRepository,
) { ) {
val versionedStaticFilesPath = configurationRepository.versionedStaticFilesPath
val about = configurationRepository.about
suspend fun contributors() = withContext(Dispatchers.IO) { suspend fun contributors() = withContext(Dispatchers.IO) {
configurationRepository.contributorsRepositoryNames.map { configurationRepository.contributorsRepositoryNames.map {
async { async {
@@ -30,6 +33,7 @@ internal class ApiService(
member.name, member.name,
member.avatarUrl, member.avatarUrl,
member.url, member.url,
member.bio,
if (member.gpgKeys.ids.isNotEmpty()) { if (member.gpgKeys.ids.isNotEmpty()) {
APIGpgKey( APIGpgKey(
// Must choose one of the GPG keys, because it does not make sense to have multiple GPG keys for the API. // Must choose one of the GPG keys, because it does not make sense to have multiple GPG keys for the API.
@@ -39,7 +43,6 @@ internal class ApiService(
} else { } else {
null null
}, },
) )
} }

View File

@@ -0,0 +1,38 @@
package app.revanced.api.configuration.services
import app.revanced.api.configuration.repository.BackendRepository
import app.revanced.api.configuration.repository.BackendRepository.BackendOrganization.BackendRepository.BackendRelease.Companion.first
import app.revanced.api.configuration.repository.ConfigurationRepository
import app.revanced.api.configuration.schema.*
internal class ManagerService(
private val backendRepository: BackendRepository,
private val configurationRepository: ConfigurationRepository,
) {
suspend fun latestRelease(): APIRelease<APIManagerAsset> {
val managerRelease = backendRepository.release(
configurationRepository.organization,
configurationRepository.manager.repository,
)
val managerAsset = APIManagerAsset(
managerRelease.assets.first(configurationRepository.manager.assetRegex).downloadUrl,
)
return APIRelease(
managerRelease.tag,
managerRelease.createdAt,
managerRelease.releaseNote,
listOf(managerAsset),
)
}
suspend fun latestVersion(): APIReleaseVersion {
val managerRelease = backendRepository.release(
configurationRepository.organization,
configurationRepository.manager.repository,
)
return APIReleaseVersion(managerRelease.tag)
}
}

View File

@@ -4,10 +4,9 @@ import app.revanced.api.configuration.repository.BackendRepository
import app.revanced.api.configuration.repository.BackendRepository.BackendOrganization.BackendRepository.BackendRelease.Companion.first import app.revanced.api.configuration.repository.BackendRepository.BackendOrganization.BackendRepository.BackendRelease.Companion.first
import app.revanced.api.configuration.repository.ConfigurationRepository import app.revanced.api.configuration.repository.ConfigurationRepository
import app.revanced.api.configuration.schema.* import app.revanced.api.configuration.schema.*
import app.revanced.library.PatchUtils import app.revanced.library.serializeTo
import app.revanced.patcher.PatchBundleLoader import app.revanced.patcher.patch.loadPatchesFromJar
import com.github.benmanes.caffeine.cache.Caffeine import com.github.benmanes.caffeine.cache.Caffeine
import io.ktor.util.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
@@ -18,7 +17,7 @@ internal class PatchesService(
private val backendRepository: BackendRepository, private val backendRepository: BackendRepository,
private val configurationRepository: ConfigurationRepository, private val configurationRepository: ConfigurationRepository,
) { ) {
suspend fun latestRelease(): APIRelease { suspend fun latestRelease(): APIRelease<APIPatchesAsset> {
val patchesRelease = backendRepository.release( val patchesRelease = backendRepository.release(
configurationRepository.organization, configurationRepository.organization,
configurationRepository.patches.repository, configurationRepository.patches.repository,
@@ -29,10 +28,10 @@ internal class PatchesService(
configurationRepository.integrations.repository, configurationRepository.integrations.repository,
) )
fun ConfigurationRepository.AssetConfiguration.asset( fun ConfigurationRepository.SignedAssetConfiguration.asset(
release: BackendRepository.BackendOrganization.BackendRepository.BackendRelease, release: BackendRepository.BackendOrganization.BackendRepository.BackendRelease,
assetName: APIAssetName, assetName: APIAssetName,
) = APIAsset( ) = APIPatchesAsset(
release.assets.first(assetRegex).downloadUrl, release.assets.first(assetRegex).downloadUrl,
release.assets.first(signatureAssetRegex).downloadUrl, release.assets.first(signatureAssetRegex).downloadUrl,
assetName, assetName,
@@ -92,9 +91,10 @@ internal class PatchesService(
patchesFile, patchesFile,
signatureDownloadUrl, signatureDownloadUrl,
configurationRepository.patches.publicKeyFile, configurationRepository.patches.publicKeyFile,
configurationRepository.patches.publicKeyId,
) )
) { ) {
PatchBundleLoader.Jar(patchesFile) loadPatchesFromJar(setOf(patchesFile))
} else { } else {
// Use an empty set of patches if the signature is invalid. // Use an empty set of patches if the signature is invalid.
emptySet() emptySet()
@@ -103,7 +103,7 @@ internal class PatchesService(
patchesFile.delete() patchesFile.delete()
ByteArrayOutputStream().use { stream -> ByteArrayOutputStream().use { stream ->
PatchUtils.Json.serialize(patches, outputStream = stream) patches.serializeTo(outputStream = stream)
stream.toByteArray() stream.toByteArray()
} }
@@ -112,12 +112,13 @@ internal class PatchesService(
} }
fun publicKeys(): APIAssetPublicKeys { fun publicKeys(): APIAssetPublicKeys {
fun publicKeyBase64(getAssetConfiguration: ConfigurationRepository.() -> ConfigurationRepository.AssetConfiguration) = fun readPublicKey(
configurationRepository.getAssetConfiguration().publicKeyFile.readBytes().encodeBase64() getSignedAssetConfiguration: ConfigurationRepository.() -> ConfigurationRepository.SignedAssetConfiguration,
) = configurationRepository.getSignedAssetConfiguration().publicKeyFile.readText()
return APIAssetPublicKeys( return APIAssetPublicKeys(
publicKeyBase64 { patches }, readPublicKey { patches },
publicKeyBase64 { integrations }, readPublicKey { integrations },
) )
} }
} }

View File

@@ -1,7 +1,6 @@
package app.revanced.api.configuration.services package app.revanced.api.configuration.services
import com.github.benmanes.caffeine.cache.Caffeine import com.github.benmanes.caffeine.cache.Caffeine
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider
@@ -9,7 +8,6 @@ import java.io.File
import java.io.InputStream import java.io.InputStream
import java.net.URL import java.net.URL
import java.security.MessageDigest import java.security.MessageDigest
import java.security.Security
internal class SignatureService { internal class SignatureService {
private val signatureCache = Caffeine private val signatureCache = Caffeine
@@ -21,6 +19,7 @@ internal class SignatureService {
file: File, file: File,
signatureDownloadUrl: String, signatureDownloadUrl: String,
publicKeyFile: File, publicKeyFile: File,
publicKeyId: Long,
): Boolean { ): Boolean {
val fileBytes = file.readBytes() val fileBytes = file.readBytes()
@@ -28,7 +27,8 @@ internal class SignatureService {
verify( verify(
fileBytes = fileBytes, fileBytes = fileBytes,
signatureInputStream = URL(signatureDownloadUrl).openStream(), signatureInputStream = URL(signatureDownloadUrl).openStream(),
publicKeyInputStream = publicKeyFile.inputStream(), publicKeyFileInputStream = publicKeyFile.inputStream(),
publicKeyId = publicKeyId,
) )
} }
} }
@@ -36,37 +36,32 @@ internal class SignatureService {
private fun verify( private fun verify(
fileBytes: ByteArray, fileBytes: ByteArray,
signatureInputStream: InputStream, signatureInputStream: InputStream,
publicKeyInputStream: InputStream, publicKeyFileInputStream: InputStream,
publicKeyId: Long,
) = getSignature(signatureInputStream).apply { ) = getSignature(signatureInputStream).apply {
init(BcPGPContentVerifierBuilderProvider(), getPublicKey(publicKeyInputStream)) init(BcPGPContentVerifierBuilderProvider(), getPublicKey(publicKeyFileInputStream, publicKeyId))
update(fileBytes) update(fileBytes)
}.verify() }.verify()
private fun getPublicKey(publicKeyInputStream: InputStream): PGPPublicKey { private fun getPublicKey(
val decoderStream = PGPUtil.getDecoderStream(publicKeyInputStream) publicKeyFileInputStream: InputStream,
publicKeyId: Long,
): PGPPublicKey {
val decoderStream = PGPUtil.getDecoderStream(publicKeyFileInputStream)
val pgpPublicKeyRingCollection = PGPPublicKeyRingCollection(decoderStream, BcKeyFingerprintCalculator())
val publicKeyRing = pgpPublicKeyRingCollection.getPublicKeyRing(publicKeyId)
?: throw IllegalArgumentException("Can't find public key ring with ID $publicKeyId.")
PGPPublicKeyRingCollection(decoderStream, BcKeyFingerprintCalculator()).forEach { keyRing -> return publicKeyRing.getPublicKey(publicKeyId)
keyRing.publicKeys.forEach { publicKey -> ?: throw IllegalArgumentException("Can't find public key with ID $publicKeyId.")
if (publicKey.isEncryptionKey) {
return publicKey
}
}
}
throw IllegalArgumentException("Can't find encryption key in key ring.")
} }
private fun getSignature(inputStream: InputStream): PGPSignature { private fun getSignature(inputStream: InputStream): PGPSignature {
val decoderStream = PGPUtil.getDecoderStream(inputStream) val decoderStream = PGPUtil.getDecoderStream(inputStream)
val pgpObjectFactory = PGPObjectFactory(decoderStream, BcKeyFingerprintCalculator()) val pgpSignatureList = PGPObjectFactory(decoderStream, BcKeyFingerprintCalculator()).first {
val signatureList = pgpObjectFactory.nextObject() as PGPSignatureList it is PGPSignatureList
} as PGPSignatureList
return signatureList.first() return pgpSignatureList.first()
}
private companion object {
init {
Security.addProvider(BouncyCastleProvider())
}
} }
} }

View File

@@ -1,2 +0,0 @@
User-agent: *
Disallow: /