Rewrite it in Rust
For the curious: this wasn't completely for no reason, although it appears that way from the outside. I deploy this code to AWS via the CDK, and the Python Lambda function bundler in the CDK has two unwanted properties: 1. The assets produced by the bundler are not reproducible, due to the timestamps stored in the .pyc files. The bundler does nothing to assist with this. 2. A bug (that was reported, and that the CDK maintainers converted from an issue into discussion without any investigation) prevents the use of arm64 runtimes with this bundler. This requires emulating x86_64 on my development machines, which are now only ARM. ... whereas the Rust function bundler I wrote does not have these problems. So.
This commit is contained in:
parent
7ed4c03bf3
commit
67b8bd9432
|
@ -0,0 +1 @@
|
|||
.gitignore
|
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -1,7 +0,0 @@
|
|||
[settings]
|
||||
multi_line_output = 3
|
||||
include_trailing_comma = True
|
||||
force_grid_wrap = 0
|
||||
use_parentheses = True
|
||||
ensure_newline_before_comments = True
|
||||
line_length = 88
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
|||
[package]
|
||||
name = "emojos-dot-in"
|
||||
version = "2.0.0"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0-or-later"
|
||||
description = "Shows custom emoji for Mastodon/Pleroma instances"
|
||||
homepage = "https://emojos.in"
|
||||
repository = "https://github.com/iliana/emojos.in"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
askama = "0.11"
|
||||
lambda_http = "0.5"
|
||||
reqwest = { version = "0.11", features = ["gzip", "brotli", "deflate", "json"] }
|
||||
rocket = { version = "0.5.0-rc.2", default-features = false }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tokio = { version = "1", features = ["rt-multi-thread"] }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1"
|
||||
zip = { version = "0.6", default-features = false, features = ["deflate"] }
|
18
Pipfile
18
Pipfile
|
@ -1,18 +0,0 @@
|
|||
[[source]]
|
||||
url = "https://pypi.python.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
flask = "~=2.1.2"
|
||||
requests = "~=2.28.0"
|
||||
serverless-wsgi = "~=3.0.0"
|
||||
|
||||
[dev-packages]
|
||||
black = "*"
|
||||
botocore = "==1.23.32"
|
||||
flake8 = "*"
|
||||
isort = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.9"
|
|
@ -1,302 +0,0 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "c23c0e8860e8b957ce01c6c6cc2ab6ff85b1d0535e9bdd12e27cbe93be7668dc"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.9"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.python.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
|
||||
"sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.6.15"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
|
||||
"sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
|
||||
],
|
||||
"markers": "python_full_version >= '3.5.0'",
|
||||
"version": "==2.0.12"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
||||
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.3"
|
||||
},
|
||||
"flask": {
|
||||
"hashes": [
|
||||
"sha256:315ded2ddf8a6281567edb27393010fe3406188bafbfe65a3339d5787d89e477",
|
||||
"sha256:fad5b446feb0d6db6aec0c3184d16a8c1f6c3e464b511649c8918a9be100b4fe"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.1.2"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
|
||||
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
|
||||
],
|
||||
"markers": "python_full_version >= '3.5.0'",
|
||||
"version": "==3.3"
|
||||
},
|
||||
"itsdangerous": {
|
||||
"hashes": [
|
||||
"sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44",
|
||||
"sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.1.2"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
|
||||
"sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.1.2"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
|
||||
"sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88",
|
||||
"sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5",
|
||||
"sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7",
|
||||
"sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a",
|
||||
"sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603",
|
||||
"sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1",
|
||||
"sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135",
|
||||
"sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247",
|
||||
"sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6",
|
||||
"sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601",
|
||||
"sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77",
|
||||
"sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02",
|
||||
"sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e",
|
||||
"sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63",
|
||||
"sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f",
|
||||
"sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980",
|
||||
"sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b",
|
||||
"sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812",
|
||||
"sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff",
|
||||
"sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96",
|
||||
"sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1",
|
||||
"sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925",
|
||||
"sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a",
|
||||
"sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6",
|
||||
"sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e",
|
||||
"sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f",
|
||||
"sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4",
|
||||
"sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f",
|
||||
"sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3",
|
||||
"sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c",
|
||||
"sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a",
|
||||
"sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417",
|
||||
"sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a",
|
||||
"sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a",
|
||||
"sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37",
|
||||
"sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452",
|
||||
"sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933",
|
||||
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
|
||||
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f",
|
||||
"sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.28.0"
|
||||
},
|
||||
"serverless-wsgi": {
|
||||
"hashes": [
|
||||
"sha256:2bb42fbb23eb8f0ccfc5666c7b6fc6af36f9948336dbb99d033416447e5a1d4c",
|
||||
"sha256:e5d699dfefc3e593c11a07b9af2d89cdccd3110d9a1ee28c83a9fcf301c6fcbc"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.0.0"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
|
||||
"sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"version": "==1.26.9"
|
||||
},
|
||||
"werkzeug": {
|
||||
"hashes": [
|
||||
"sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6",
|
||||
"sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.1.2"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b",
|
||||
"sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176",
|
||||
"sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09",
|
||||
"sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a",
|
||||
"sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015",
|
||||
"sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79",
|
||||
"sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb",
|
||||
"sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20",
|
||||
"sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464",
|
||||
"sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968",
|
||||
"sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82",
|
||||
"sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21",
|
||||
"sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0",
|
||||
"sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265",
|
||||
"sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b",
|
||||
"sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a",
|
||||
"sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72",
|
||||
"sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce",
|
||||
"sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0",
|
||||
"sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a",
|
||||
"sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163",
|
||||
"sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad",
|
||||
"sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==22.3.0"
|
||||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:49c39a4e0ece6e486aefa23d7c96c5f9a4c41a6ceab3cc41b157e8b5bea749e3",
|
||||
"sha256:a33e6b30c5d64156d10ec6986fc96dfdf5c723ad7dc83e0d60af6407192b16b4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.23.32"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
||||
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.3"
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d",
|
||||
"sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.0.1"
|
||||
},
|
||||
"isort": {
|
||||
"hashes": [
|
||||
"sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7",
|
||||
"sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.10.1"
|
||||
},
|
||||
"jmespath": {
|
||||
"hashes": [
|
||||
"sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9",
|
||||
"sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
||||
],
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
||||
],
|
||||
"version": "==0.4.3"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a",
|
||||
"sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"
|
||||
],
|
||||
"version": "==0.9.0"
|
||||
},
|
||||
"platformdirs": {
|
||||
"hashes": [
|
||||
"sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788",
|
||||
"sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.5.2"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20",
|
||||
"sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==2.8.0"
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c",
|
||||
"sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.4.0"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
|
||||
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.8.2"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"tomli": {
|
||||
"hashes": [
|
||||
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
|
||||
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
|
||||
],
|
||||
"markers": "python_version < '3.11'",
|
||||
"version": "==2.0.1"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
|
||||
"sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"version": "==1.26.9"
|
||||
}
|
||||
}
|
||||
}
|
99
app.py
99
app.py
|
@ -1,99 +0,0 @@
|
|||
# Copyright (c) 2021 iliana etaoin
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import operator
|
||||
import urllib.parse
|
||||
|
||||
import botocore.session
|
||||
import requests
|
||||
import serverless_wsgi
|
||||
from flask import Flask, redirect, render_template, request, url_for
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/<domain>")
|
||||
def emojo(domain):
|
||||
if request.args.get("show_all", "") == "on":
|
||||
show_all = True
|
||||
else:
|
||||
show_all = False
|
||||
if request.args.get("show_animated", "") == "on":
|
||||
show_animated = True
|
||||
else:
|
||||
show_animated = False
|
||||
|
||||
try:
|
||||
url = urllib.parse.urlunsplit(
|
||||
("https", domain, "/api/v1/custom_emojis", "", "")
|
||||
)
|
||||
response = requests.get(url)
|
||||
if response.status_code == 401:
|
||||
return render_template("forbidden.html", domain=domain)
|
||||
|
||||
if show_all:
|
||||
emojo = sorted(response.json(), key=operator.itemgetter("shortcode"))
|
||||
else:
|
||||
emojo = sorted(
|
||||
filter(lambda x: x.get("visible_in_picker", True), response.json()),
|
||||
key=operator.itemgetter("shortcode"),
|
||||
)
|
||||
return render_template(
|
||||
"emojo.html", domain=domain, emojo=emojo, show_animated=show_animated
|
||||
)
|
||||
except requests.exceptions.RequestException:
|
||||
return render_template("oh_no.html", domain=domain)
|
||||
|
||||
|
||||
@app.route("/favicon.ico")
|
||||
@app.route("/robots.txt")
|
||||
def no_content():
|
||||
return ("", 204)
|
||||
|
||||
|
||||
@app.route("/code")
|
||||
def code():
|
||||
context = request.environ.get("context")
|
||||
session = botocore.session.get_session()
|
||||
# region name is detected from lambda environment
|
||||
client = session.create_client("lambda")
|
||||
code = client.get_function(
|
||||
FunctionName=context.function_name, Qualifier=context.function_version
|
||||
)
|
||||
return redirect(code["Code"]["Location"], code=303)
|
||||
|
||||
|
||||
@app.route("/", methods=("GET", "POST"))
|
||||
def index():
|
||||
if request.method == "POST":
|
||||
if "instance" in request.form:
|
||||
show_all = request.form.get("show_all")
|
||||
show_animated = request.form.get("show_animated")
|
||||
return redirect(
|
||||
url_for(
|
||||
"emojo",
|
||||
domain=request.form["instance"],
|
||||
show_all=show_all,
|
||||
show_animated=show_animated,
|
||||
)
|
||||
)
|
||||
else:
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
return render_template("index.html")
|
||||
|
||||
|
||||
def handle_request(event, context):
|
||||
return serverless_wsgi.handle_request(app, event, context)
|
|
@ -0,0 +1,38 @@
|
|||
use anyhow::{ensure, Result};
|
||||
use std::fs::File;
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use zip::ZipWriter;
|
||||
|
||||
fn main() {
|
||||
let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||
let mut rs_file = File::create(out_dir.join("zip.rs")).unwrap();
|
||||
match attempt(&out_dir) {
|
||||
Ok(()) => writeln!(rs_file, r#"Some(include_bytes!("source.zip"))"#).unwrap(),
|
||||
Err(_) => writeln!(rs_file, "None").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn attempt(out_dir: &Path) -> Result<()> {
|
||||
let output = Command::new("cargo")
|
||||
.args(["package", "--list", "--allow-dirty"])
|
||||
.output()?;
|
||||
ensure!(output.status.success(), "unsuccessful");
|
||||
|
||||
let mut writer = ZipWriter::new(File::create(out_dir.join("source.zip"))?);
|
||||
for path in String::from_utf8(output.stdout)?.lines() {
|
||||
if path == "Cargo.toml.orig" {
|
||||
continue;
|
||||
}
|
||||
|
||||
writer.start_file(path, Default::default())?;
|
||||
io::copy(
|
||||
&mut File::open(Path::new(env!("CARGO_MANIFEST_DIR")).join(path))?,
|
||||
&mut writer,
|
||||
)?;
|
||||
}
|
||||
|
||||
writer.finish()?;
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
use askama::Template;
|
||||
use reqwest::{Client, StatusCode, Url};
|
||||
use rocket::form::{Form, FromForm};
|
||||
use rocket::http::{ContentType, Header};
|
||||
use rocket::response::status::NoContent;
|
||||
use rocket::response::{Debug, Redirect, Responder};
|
||||
use rocket::{get, post, routes, uri, Build, Rocket, State};
|
||||
use serde::Deserialize;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub fn rocket() -> Rocket<Build> {
|
||||
rocket::build()
|
||||
.manage(
|
||||
Client::builder()
|
||||
.user_agent(format!(
|
||||
"emojos.in/{} (+https://github.com/iliana/emojos.in)",
|
||||
env!("CARGO_PKG_VERSION")
|
||||
))
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
.mount(
|
||||
"/",
|
||||
routes![
|
||||
code,
|
||||
copy_js,
|
||||
css,
|
||||
favicon_ico,
|
||||
index,
|
||||
instance,
|
||||
instance_form,
|
||||
robots_txt
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
fn index() -> Result<(ContentType, String), Debug<askama::Error>> {
|
||||
#[derive(Template)]
|
||||
#[template(path = "index.html")]
|
||||
struct Index;
|
||||
|
||||
Ok((ContentType::HTML, Index.render()?))
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
struct InstanceForm<'a> {
|
||||
instance: &'a str,
|
||||
show_all: bool,
|
||||
show_animated: bool,
|
||||
}
|
||||
|
||||
#[post("/", data = "<form>")]
|
||||
fn instance_form(form: Form<InstanceForm<'_>>) -> Redirect {
|
||||
Redirect::to(uri!(instance(
|
||||
form.instance,
|
||||
form.show_all.then(|| true),
|
||||
form.show_animated.then(|| true),
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/<instance>?<show_all>&<show_animated>")]
|
||||
async fn instance(
|
||||
client: &State<Client>,
|
||||
instance: &str,
|
||||
show_all: Option<bool>,
|
||||
show_animated: Option<bool>,
|
||||
) -> Result<(ContentType, String), Debug<anyhow::Error>> {
|
||||
#[derive(Deserialize)]
|
||||
struct Emojo {
|
||||
shortcode: String,
|
||||
url: String,
|
||||
static_url: String,
|
||||
visible_in_picker: bool,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "emojo.html")]
|
||||
struct Output<'a> {
|
||||
instance: &'a str,
|
||||
show_animated: bool,
|
||||
emojo: Vec<Emojo>,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "oh_no.html")]
|
||||
struct OhNo<'a> {
|
||||
instance: &'a str,
|
||||
status: StatusCode,
|
||||
why: &'a str,
|
||||
}
|
||||
|
||||
let show_all = show_all.unwrap_or_default();
|
||||
let show_animated = show_animated.unwrap_or_default();
|
||||
|
||||
let output = async {
|
||||
let mut url = Url::from_str("https://host.invalid/api/v1/custom_emojis").unwrap();
|
||||
url.set_host(Some(instance))?;
|
||||
|
||||
let response = client.get(url).send().await?;
|
||||
if response.status().is_client_error() || response.status().is_server_error() {
|
||||
return Ok(OhNo {
|
||||
instance,
|
||||
status: response.status(),
|
||||
why: match response.status() {
|
||||
StatusCode::FORBIDDEN => "This instance's emoji list is private.",
|
||||
StatusCode::NOT_FOUND => {
|
||||
"This instance doesn't support the Mastodon custom emoji API."
|
||||
}
|
||||
_ => "That's all we know.",
|
||||
},
|
||||
}
|
||||
.render()?);
|
||||
}
|
||||
|
||||
let mut emojo: Vec<Emojo> = response.json().await?;
|
||||
if !show_all {
|
||||
emojo = emojo.into_iter().filter(|x| x.visible_in_picker).collect();
|
||||
}
|
||||
|
||||
anyhow::Ok(
|
||||
Output {
|
||||
instance,
|
||||
show_animated,
|
||||
emojo,
|
||||
}
|
||||
.render()?,
|
||||
)
|
||||
}
|
||||
.await?;
|
||||
Ok((ContentType::HTML, output))
|
||||
}
|
||||
|
||||
#[derive(Responder)]
|
||||
struct Code {
|
||||
zip: &'static [u8],
|
||||
content_type: ContentType,
|
||||
disposition: Header<'static>,
|
||||
}
|
||||
|
||||
#[get("/code")]
|
||||
fn code() -> Option<Code> {
|
||||
let zip = include!(concat!(env!("OUT_DIR"), "/zip.rs"))?;
|
||||
Some(Code {
|
||||
zip,
|
||||
content_type: ContentType::ZIP,
|
||||
disposition: Header::new(
|
||||
"content-disposition",
|
||||
r#"attachment; filename="emojos.in.zip""#,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
#[get("/static/site.css")]
|
||||
fn css() -> (ContentType, &'static str) {
|
||||
(
|
||||
ContentType::CSS,
|
||||
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/static/site.css")),
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/static/copy.js")]
|
||||
fn copy_js() -> (ContentType, &'static str) {
|
||||
(
|
||||
ContentType::JavaScript,
|
||||
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/static/copy.js")),
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/favicon.ico")]
|
||||
fn favicon_ico() -> NoContent {
|
||||
NoContent
|
||||
}
|
||||
|
||||
#[get("/robots.txt")]
|
||||
fn robots_txt() -> NoContent {
|
||||
NoContent
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) 2022 iliana etaoin
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(
|
||||
clippy::map_unwrap_or,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::no_effect_underscore_binding
|
||||
)]
|
||||
|
||||
mod app;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use lambda_http::{http, service_fn, Body};
|
||||
use rocket::http::Header;
|
||||
use rocket::local::asynchronous::Client;
|
||||
|
||||
fn main() {
|
||||
// Rocket needs its own async runtime, but that runtime doesn't support the !Send lambda_http
|
||||
// future.
|
||||
if std::env::var_os("AWS_LAMBDA_RUNTIME_API").is_some() {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
let client = Client::untracked(app::rocket()).await.unwrap();
|
||||
lambda_http::run(service_fn(|request| async {
|
||||
Ok(handler(request, &client).await?)
|
||||
}))
|
||||
.await
|
||||
.unwrap();
|
||||
});
|
||||
} else {
|
||||
rocket::execute(async {
|
||||
app::rocket().launch().await.ok();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn handler(request: http::Request<Body>, client: &Client) -> Result<http::Response<Body>> {
|
||||
let mut local_request = client.req(
|
||||
request
|
||||
.method()
|
||||
.to_string()
|
||||
.parse()
|
||||
.map_err(|()| anyhow!("invalid method"))?,
|
||||
request
|
||||
.uri()
|
||||
.path_and_query()
|
||||
.context("request has no path")?
|
||||
.as_str(),
|
||||
);
|
||||
for (name, value) in request.headers() {
|
||||
local_request.add_header(Header::new(
|
||||
name.as_str().to_owned(),
|
||||
value.to_str()?.to_owned(),
|
||||
));
|
||||
}
|
||||
let local_response = local_request.body(request.body()).dispatch().await;
|
||||
|
||||
let mut response = http::Response::builder().status(local_response.status().code);
|
||||
for header in local_response.headers().iter() {
|
||||
let name: &str = header.name.as_ref();
|
||||
let value: &str = header.value.as_ref();
|
||||
response = response.header(name, value);
|
||||
}
|
||||
Ok(response.body(
|
||||
local_response
|
||||
.into_bytes()
|
||||
.await
|
||||
.map(Body::Binary)
|
||||
.unwrap_or(Body::Empty),
|
||||
)?)
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>{% block title %}{% endblock title %}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='site.css') }}">
|
||||
<link rel="stylesheet" href="/static/site.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
|
@ -14,7 +14,7 @@
|
|||
·
|
||||
AGPLv3
|
||||
·
|
||||
<a href="{{ url_for('code') }}">source code download</a>
|
||||
<a href="/code">source code download</a>
|
||||
·
|
||||
<a href="https://github.com/iliana/emojos.in">github</a>
|
||||
</p>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Custom emoji list for {{ domain }}{% endblock title %}
|
||||
{% block title %}Custom emoji list for {{ instance }}{% endblock title %}
|
||||
{% block body %}
|
||||
<p>
|
||||
<b>{{ domain }}</b> emojo list<br>
|
||||
<b>{{ instance }}</b> emojo list<br>
|
||||
click/touch to copy to clipboard
|
||||
</p>
|
||||
<dl class="emojo">
|
||||
|
@ -24,5 +24,5 @@
|
|||
</dl>
|
||||
{% endblock body %}
|
||||
{% block script %}
|
||||
<script src="{{ url_for('static', filename='copy.js') }}"></script>
|
||||
<script src="/static/copy.js"></script>
|
||||
{% endblock script %}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Custom emoji list for {{ domain }}{% endblock title %}
|
||||
{% block body %}
|
||||
<p><b>{{ domain }}</b> doesn't allow access to the <code>v1/custom_emojis</code> endpoint. :(</p>
|
||||
{% endblock body %}
|
|
@ -1,5 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Custom emoji list for {{ domain }}{% endblock title %}
|
||||
{% block title %}Custom emoji list for {{ instance }}{% endblock title %}
|
||||
{% block body %}
|
||||
<p><b>{{ domain }}</b> isn't a fediverse instance or doesn't have a <code>v1/custom_emojis</code> endpoint. :(</p>
|
||||
<p>
|
||||
<b>{{ instance }}</b>'s custom emoji API (/api/v1/custom_emojis) returned {{ status }}.
|
||||
{{ why }} :(
|
||||
</p>
|
||||
{% endblock body %}
|
||||
|
|
Loading…
Reference in New Issue