Removing old-style kapow.
This commit is contained in:
-10
@@ -1,10 +0,0 @@
|
|||||||
FROM python:3.7.3-alpine3.8
|
|
||||||
|
|
||||||
# Install Dependences
|
|
||||||
RUN pip install pipenv
|
|
||||||
COPY Pipfile Pipfile.lock ./
|
|
||||||
RUN pipenv install --system --deploy && rm Pipfile Pipfile.lock
|
|
||||||
|
|
||||||
# Install kapow!
|
|
||||||
COPY kapow.py /usr/sbin/kapow
|
|
||||||
RUN chmod a+x /usr/sbin/kapow
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
[[source]]
|
|
||||||
name = "pypi"
|
|
||||||
url = "https://pypi.org/simple"
|
|
||||||
verify_ssl = true
|
|
||||||
|
|
||||||
[dev-packages]
|
|
||||||
sphinx = "*"
|
|
||||||
ipython = "*"
|
|
||||||
ipdb = "*"
|
|
||||||
pylint = "*"
|
|
||||||
|
|
||||||
[packages]
|
|
||||||
pyparsing = "*"
|
|
||||||
aiohttp = "*"
|
|
||||||
click = "*"
|
|
||||||
aiofiles = "*"
|
|
||||||
|
|
||||||
[requires]
|
|
||||||
python_version = "3.7"
|
|
||||||
Generated
-538
@@ -1,538 +0,0 @@
|
|||||||
{
|
|
||||||
"_meta": {
|
|
||||||
"hash": {
|
|
||||||
"sha256": "fc23d2e0b455dc19dab220e38b1b8689a09c8aebd5b6657b06ec906462f5388a"
|
|
||||||
},
|
|
||||||
"pipfile-spec": 6,
|
|
||||||
"requires": {
|
|
||||||
"python_version": "3.7"
|
|
||||||
},
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"name": "pypi",
|
|
||||||
"url": "https://pypi.org/simple",
|
|
||||||
"verify_ssl": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"aiofiles": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:021ea0ba314a86027c166ecc4b4c07f2d40fc0f4b3a950d1868a0f2571c2bbee",
|
|
||||||
"sha256:1e644c2573f953664368de28d2aa4c89dfd64550429d0c27c4680ccd3aa4985d"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.4.0"
|
|
||||||
},
|
|
||||||
"aiohttp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:00d198585474299c9c3b4f1d5de1a576cc230d562abc5e4a0e81d71a20a6ca55",
|
|
||||||
"sha256:0155af66de8c21b8dba4992aaeeabf55503caefae00067a3b1139f86d0ec50ed",
|
|
||||||
"sha256:09654a9eca62d1bd6d64aa44db2498f60a5c1e0ac4750953fdd79d5c88955e10",
|
|
||||||
"sha256:199f1d106e2b44b6dacdf6f9245493c7d716b01d0b7fbe1959318ba4dc64d1f5",
|
|
||||||
"sha256:296f30dedc9f4b9e7a301e5cc963012264112d78a1d3094cd83ef148fdf33ca1",
|
|
||||||
"sha256:368ed312550bd663ce84dc4b032a962fcb3c7cae099dbbd48663afc305e3b939",
|
|
||||||
"sha256:40d7ea570b88db017c51392349cf99b7aefaaddd19d2c78368aeb0bddde9d390",
|
|
||||||
"sha256:629102a193162e37102c50713e2e31dc9a2fe7ac5e481da83e5bb3c0cee700aa",
|
|
||||||
"sha256:6d5ec9b8948c3d957e75ea14d41e9330e1ac3fed24ec53766c780f82805140dc",
|
|
||||||
"sha256:87331d1d6810214085a50749160196391a712a13336cd02ce1c3ea3d05bcf8d5",
|
|
||||||
"sha256:9a02a04bbe581c8605ac423ba3a74999ec9d8bce7ae37977a3d38680f5780b6d",
|
|
||||||
"sha256:9c4c83f4fa1938377da32bc2d59379025ceeee8e24b89f72fcbccd8ca22dc9bf",
|
|
||||||
"sha256:9cddaff94c0135ee627213ac6ca6d05724bfe6e7a356e5e09ec57bd3249510f6",
|
|
||||||
"sha256:a25237abf327530d9561ef751eef9511ab56fd9431023ca6f4803f1994104d72",
|
|
||||||
"sha256:a5cbd7157b0e383738b8e29d6e556fde8726823dae0e348952a61742b21aeb12",
|
|
||||||
"sha256:a97a516e02b726e089cffcde2eea0d3258450389bbac48cbe89e0f0b6e7b0366",
|
|
||||||
"sha256:acc89b29b5f4e2332d65cd1b7d10c609a75b88ef8925d487a611ca788432dfa4",
|
|
||||||
"sha256:b05bd85cc99b06740aad3629c2585bda7b83bd86e080b44ba47faf905fdf1300",
|
|
||||||
"sha256:c2bec436a2b5dafe5eaeb297c03711074d46b6eb236d002c13c42f25c4a8ce9d",
|
|
||||||
"sha256:cc619d974c8c11fe84527e4b5e1c07238799a8c29ea1c1285149170524ba9303",
|
|
||||||
"sha256:d4392defd4648badaa42b3e101080ae3313e8f4787cb517efd3f5b8157eaefd6",
|
|
||||||
"sha256:e1c3c582ee11af7f63a34a46f0448fca58e59889396ffdae1f482085061a2889"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==3.5.4"
|
|
||||||
},
|
|
||||||
"async-timeout": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
|
|
||||||
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
|
|
||||||
],
|
|
||||||
"version": "==3.0.1"
|
|
||||||
},
|
|
||||||
"attrs": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
|
|
||||||
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
|
|
||||||
],
|
|
||||||
"version": "==19.1.0"
|
|
||||||
},
|
|
||||||
"chardet": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
|
||||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
|
||||||
],
|
|
||||||
"version": "==3.0.4"
|
|
||||||
},
|
|
||||||
"click": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
|
|
||||||
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==7.0"
|
|
||||||
},
|
|
||||||
"idna": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
|
||||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
|
||||||
],
|
|
||||||
"version": "==2.8"
|
|
||||||
},
|
|
||||||
"multidict": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f",
|
|
||||||
"sha256:041e9442b11409be5e4fc8b6a97e4bcead758ab1e11768d1e69160bdde18acc3",
|
|
||||||
"sha256:045b4dd0e5f6121e6f314d81759abd2c257db4634260abcfe0d3f7083c4908ef",
|
|
||||||
"sha256:047c0a04e382ef8bd74b0de01407e8d8632d7d1b4db6f2561106af812a68741b",
|
|
||||||
"sha256:068167c2d7bbeebd359665ac4fff756be5ffac9cda02375b5c5a7c4777038e73",
|
|
||||||
"sha256:148ff60e0fffa2f5fad2eb25aae7bef23d8f3b8bdaf947a65cdbe84a978092bc",
|
|
||||||
"sha256:1d1c77013a259971a72ddaa83b9f42c80a93ff12df6a4723be99d858fa30bee3",
|
|
||||||
"sha256:1d48bc124a6b7a55006d97917f695effa9725d05abe8ee78fd60d6588b8344cd",
|
|
||||||
"sha256:31dfa2fc323097f8ad7acd41aa38d7c614dd1960ac6681745b6da124093dc351",
|
|
||||||
"sha256:34f82db7f80c49f38b032c5abb605c458bac997a6c3142e0d6c130be6fb2b941",
|
|
||||||
"sha256:3d5dd8e5998fb4ace04789d1d008e2bb532de501218519d70bb672c4c5a2fc5d",
|
|
||||||
"sha256:4a6ae52bd3ee41ee0f3acf4c60ceb3f44e0e3bc52ab7da1c2b2aa6703363a3d1",
|
|
||||||
"sha256:4b02a3b2a2f01d0490dd39321c74273fed0568568ea0e7ea23e02bd1fb10a10b",
|
|
||||||
"sha256:4b843f8e1dd6a3195679d9838eb4670222e8b8d01bc36c9894d6c3538316fa0a",
|
|
||||||
"sha256:5de53a28f40ef3c4fd57aeab6b590c2c663de87a5af76136ced519923d3efbb3",
|
|
||||||
"sha256:61b2b33ede821b94fa99ce0b09c9ece049c7067a33b279f343adfe35108a4ea7",
|
|
||||||
"sha256:6a3a9b0f45fd75dc05d8e93dc21b18fc1670135ec9544d1ad4acbcf6b86781d0",
|
|
||||||
"sha256:76ad8e4c69dadbb31bad17c16baee61c0d1a4a73bed2590b741b2e1a46d3edd0",
|
|
||||||
"sha256:7ba19b777dc00194d1b473180d4ca89a054dd18de27d0ee2e42a103ec9b7d014",
|
|
||||||
"sha256:7c1b7eab7a49aa96f3db1f716f0113a8a2e93c7375dd3d5d21c4941f1405c9c5",
|
|
||||||
"sha256:7fc0eee3046041387cbace9314926aa48b681202f8897f8bff3809967a049036",
|
|
||||||
"sha256:8ccd1c5fff1aa1427100ce188557fc31f1e0a383ad8ec42c559aabd4ff08802d",
|
|
||||||
"sha256:8e08dd76de80539d613654915a2f5196dbccc67448df291e69a88712ea21e24a",
|
|
||||||
"sha256:c18498c50c59263841862ea0501da9f2b3659c00db54abfbf823a80787fde8ce",
|
|
||||||
"sha256:c49db89d602c24928e68c0d510f4fcf8989d77defd01c973d6cbe27e684833b1",
|
|
||||||
"sha256:ce20044d0317649ddbb4e54dab3c1bcc7483c78c27d3f58ab3d0c7e6bc60d26a",
|
|
||||||
"sha256:d1071414dd06ca2eafa90c85a079169bfeb0e5f57fd0b45d44c092546fcd6fd9",
|
|
||||||
"sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7",
|
|
||||||
"sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b"
|
|
||||||
],
|
|
||||||
"version": "==4.5.2"
|
|
||||||
},
|
|
||||||
"pyparsing": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a",
|
|
||||||
"sha256:9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.4.0"
|
|
||||||
},
|
|
||||||
"yarl": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:024ecdc12bc02b321bc66b41327f930d1c2c543fa9a561b39861da9388ba7aa9",
|
|
||||||
"sha256:2f3010703295fbe1aec51023740871e64bb9664c789cba5a6bdf404e93f7568f",
|
|
||||||
"sha256:3890ab952d508523ef4881457c4099056546593fa05e93da84c7250516e632eb",
|
|
||||||
"sha256:3e2724eb9af5dc41648e5bb304fcf4891adc33258c6e14e2a7414ea32541e320",
|
|
||||||
"sha256:5badb97dd0abf26623a9982cd448ff12cb39b8e4c94032ccdedf22ce01a64842",
|
|
||||||
"sha256:73f447d11b530d860ca1e6b582f947688286ad16ca42256413083d13f260b7a0",
|
|
||||||
"sha256:7ab825726f2940c16d92aaec7d204cfc34ac26c0040da727cf8ba87255a33829",
|
|
||||||
"sha256:b25de84a8c20540531526dfbb0e2d2b648c13fd5dd126728c496d7c3fea33310",
|
|
||||||
"sha256:c6e341f5a6562af74ba55205dbd56d248daf1b5748ec48a0200ba227bb9e33f4",
|
|
||||||
"sha256:c9bb7c249c4432cd47e75af3864bc02d26c9594f49c82e2a28624417f0ae63b8",
|
|
||||||
"sha256:e060906c0c585565c718d1c3841747b61c5439af2211e185f6739a9412dfbde1"
|
|
||||||
],
|
|
||||||
"version": "==1.3.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"develop": {
|
|
||||||
"alabaster": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
|
|
||||||
"sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
|
|
||||||
],
|
|
||||||
"version": "==0.7.12"
|
|
||||||
},
|
|
||||||
"astroid": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4",
|
|
||||||
"sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4"
|
|
||||||
],
|
|
||||||
"version": "==2.2.5"
|
|
||||||
},
|
|
||||||
"babel": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6778d85147d5d85345c14a26aada5e478ab04e39b078b0745ee6870c2b5cf669",
|
|
||||||
"sha256:8cba50f48c529ca3fa18cf81fa9403be176d374ac4d60738b839122dfaaa3d23"
|
|
||||||
],
|
|
||||||
"version": "==2.6.0"
|
|
||||||
},
|
|
||||||
"backcall": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4",
|
|
||||||
"sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"
|
|
||||||
],
|
|
||||||
"version": "==0.1.0"
|
|
||||||
},
|
|
||||||
"certifi": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
|
|
||||||
"sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"
|
|
||||||
],
|
|
||||||
"version": "==2019.3.9"
|
|
||||||
},
|
|
||||||
"chardet": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
|
||||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
|
||||||
],
|
|
||||||
"version": "==3.0.4"
|
|
||||||
},
|
|
||||||
"decorator": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de",
|
|
||||||
"sha256:f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"
|
|
||||||
],
|
|
||||||
"version": "==4.4.0"
|
|
||||||
},
|
|
||||||
"docutils": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
|
|
||||||
"sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274",
|
|
||||||
"sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"
|
|
||||||
],
|
|
||||||
"version": "==0.14"
|
|
||||||
},
|
|
||||||
"idna": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
|
||||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
|
||||||
],
|
|
||||||
"version": "==2.8"
|
|
||||||
},
|
|
||||||
"imagesize": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8",
|
|
||||||
"sha256:f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5"
|
|
||||||
],
|
|
||||||
"version": "==1.1.0"
|
|
||||||
},
|
|
||||||
"ipdb": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:dce2112557edfe759742ca2d0fee35c59c97b0cc7a05398b791079d78f1519ce"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.12"
|
|
||||||
},
|
|
||||||
"ipython": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:b038baa489c38f6d853a3cfc4c635b0cda66f2864d136fe8f40c1a6e334e2a6b",
|
|
||||||
"sha256:f5102c1cd67e399ec8ea66bcebe6e3968ea25a8977e53f012963e5affeb1fe38"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==7.4.0"
|
|
||||||
},
|
|
||||||
"ipython-genutils": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
|
|
||||||
"sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
|
|
||||||
],
|
|
||||||
"version": "==0.2.0"
|
|
||||||
},
|
|
||||||
"isort": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:01cb7e1ca5e6c5b3f235f0385057f70558b70d2f00320208825fa62887292f43",
|
|
||||||
"sha256:268067462aed7eb2a1e237fcb287852f22077de3fb07964e87e00f829eea2d1a"
|
|
||||||
],
|
|
||||||
"version": "==4.3.17"
|
|
||||||
},
|
|
||||||
"jedi": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2bb0603e3506f708e792c7f4ad8fc2a7a9d9c2d292a358fbbd58da531695595b",
|
|
||||||
"sha256:2c6bcd9545c7d6440951b12b44d373479bf18123a401a52025cf98563fbd826c"
|
|
||||||
],
|
|
||||||
"version": "==0.13.3"
|
|
||||||
},
|
|
||||||
"jinja2": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
|
|
||||||
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
|
|
||||||
],
|
|
||||||
"version": "==2.10.1"
|
|
||||||
},
|
|
||||||
"lazy-object-proxy": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33",
|
|
||||||
"sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39",
|
|
||||||
"sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019",
|
|
||||||
"sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088",
|
|
||||||
"sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b",
|
|
||||||
"sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e",
|
|
||||||
"sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6",
|
|
||||||
"sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b",
|
|
||||||
"sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5",
|
|
||||||
"sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff",
|
|
||||||
"sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd",
|
|
||||||
"sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7",
|
|
||||||
"sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff",
|
|
||||||
"sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d",
|
|
||||||
"sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2",
|
|
||||||
"sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35",
|
|
||||||
"sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4",
|
|
||||||
"sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514",
|
|
||||||
"sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252",
|
|
||||||
"sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109",
|
|
||||||
"sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f",
|
|
||||||
"sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c",
|
|
||||||
"sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92",
|
|
||||||
"sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577",
|
|
||||||
"sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d",
|
|
||||||
"sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d",
|
|
||||||
"sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f",
|
|
||||||
"sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a",
|
|
||||||
"sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b"
|
|
||||||
],
|
|
||||||
"version": "==1.3.1"
|
|
||||||
},
|
|
||||||
"markupsafe": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
|
|
||||||
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
|
|
||||||
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
|
|
||||||
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
|
|
||||||
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
|
|
||||||
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
|
|
||||||
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
|
|
||||||
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
|
|
||||||
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
|
|
||||||
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
|
|
||||||
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
|
|
||||||
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
|
|
||||||
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
|
|
||||||
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
|
|
||||||
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
|
|
||||||
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
|
|
||||||
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
|
|
||||||
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
|
|
||||||
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
|
|
||||||
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
|
|
||||||
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
|
|
||||||
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
|
|
||||||
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
|
|
||||||
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
|
|
||||||
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
|
|
||||||
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
|
|
||||||
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
|
|
||||||
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
|
|
||||||
],
|
|
||||||
"version": "==1.1.1"
|
|
||||||
},
|
|
||||||
"mccabe": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
|
||||||
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
|
||||||
],
|
|
||||||
"version": "==0.6.1"
|
|
||||||
},
|
|
||||||
"packaging": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af",
|
|
||||||
"sha256:9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3"
|
|
||||||
],
|
|
||||||
"version": "==19.0"
|
|
||||||
},
|
|
||||||
"parso": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:17cc2d7a945eb42c3569d4564cdf49bde221bc2b552af3eca9c1aad517dcdd33",
|
|
||||||
"sha256:2e9574cb12e7112a87253e14e2c380ce312060269d04bd018478a3c92ea9a376"
|
|
||||||
],
|
|
||||||
"version": "==0.4.0"
|
|
||||||
},
|
|
||||||
"pexpect": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1",
|
|
||||||
"sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"
|
|
||||||
],
|
|
||||||
"markers": "sys_platform != 'win32'",
|
|
||||||
"version": "==4.7.0"
|
|
||||||
},
|
|
||||||
"pickleshare": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca",
|
|
||||||
"sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"
|
|
||||||
],
|
|
||||||
"version": "==0.7.5"
|
|
||||||
},
|
|
||||||
"prompt-toolkit": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780",
|
|
||||||
"sha256:2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1",
|
|
||||||
"sha256:977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"
|
|
||||||
],
|
|
||||||
"version": "==2.0.9"
|
|
||||||
},
|
|
||||||
"ptyprocess": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0",
|
|
||||||
"sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"
|
|
||||||
],
|
|
||||||
"version": "==0.6.0"
|
|
||||||
},
|
|
||||||
"pygments": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
|
|
||||||
"sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
|
|
||||||
],
|
|
||||||
"version": "==2.3.1"
|
|
||||||
},
|
|
||||||
"pylint": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09",
|
|
||||||
"sha256:723e3db49555abaf9bf79dc474c6b9e2935ad82230b10c1138a71ea41ac0fff1"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.3.1"
|
|
||||||
},
|
|
||||||
"pyparsing": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a",
|
|
||||||
"sha256:9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.4.0"
|
|
||||||
},
|
|
||||||
"pytz": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda",
|
|
||||||
"sha256:d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141"
|
|
||||||
],
|
|
||||||
"version": "==2019.1"
|
|
||||||
},
|
|
||||||
"requests": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
|
|
||||||
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
|
|
||||||
],
|
|
||||||
"version": "==2.21.0"
|
|
||||||
},
|
|
||||||
"six": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
|
||||||
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
|
||||||
],
|
|
||||||
"version": "==1.12.0"
|
|
||||||
},
|
|
||||||
"snowballstemmer": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128",
|
|
||||||
"sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89"
|
|
||||||
],
|
|
||||||
"version": "==1.2.1"
|
|
||||||
},
|
|
||||||
"sphinx": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:423280646fb37944dd3c85c58fb92a20d745793a9f6c511f59da82fa97cd404b",
|
|
||||||
"sha256:de930f42600a4fef993587633984cc5027dedba2464bcf00ddace26b40f8d9ce"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.0.1"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-applehelp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:edaa0ab2b2bc74403149cb0209d6775c96de797dfd5b5e2a71981309efab3897",
|
|
||||||
"sha256:fb8dee85af95e5c30c91f10e7eb3c8967308518e0f7488a2828ef7bc191d0d5d"
|
|
||||||
],
|
|
||||||
"version": "==1.0.1"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-devhelp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6c64b077937330a9128a4da74586e8c2130262f014689b4b89e2d08ee7294a34",
|
|
||||||
"sha256:9512ecb00a2b0821a146736b39f7aeb90759834b07e81e8cc23a9c70bacb9981"
|
|
||||||
],
|
|
||||||
"version": "==1.0.1"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-htmlhelp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422",
|
|
||||||
"sha256:d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7"
|
|
||||||
],
|
|
||||||
"version": "==1.0.2"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-jsmath": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
|
|
||||||
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
|
|
||||||
],
|
|
||||||
"version": "==1.0.1"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-qthelp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20",
|
|
||||||
"sha256:79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f"
|
|
||||||
],
|
|
||||||
"version": "==1.0.2"
|
|
||||||
},
|
|
||||||
"sphinxcontrib-serializinghtml": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227",
|
|
||||||
"sha256:db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768"
|
|
||||||
],
|
|
||||||
"version": "==1.1.3"
|
|
||||||
},
|
|
||||||
"traitlets": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835",
|
|
||||||
"sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"
|
|
||||||
],
|
|
||||||
"version": "==4.3.2"
|
|
||||||
},
|
|
||||||
"typed-ast": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:04894d268ba6eab7e093d43107869ad49e7b5ef40d1a94243ea49b352061b200",
|
|
||||||
"sha256:16616ece19daddc586e499a3d2f560302c11f122b9c692bc216e821ae32aa0d0",
|
|
||||||
"sha256:252fdae740964b2d3cdfb3f84dcb4d6247a48a6abe2579e8029ab3be3cdc026c",
|
|
||||||
"sha256:2af80a373af123d0b9f44941a46df67ef0ff7a60f95872412a145f4500a7fc99",
|
|
||||||
"sha256:2c88d0a913229a06282b285f42a31e063c3bf9071ff65c5ea4c12acb6977c6a7",
|
|
||||||
"sha256:2ea99c029ebd4b5a308d915cc7fb95b8e1201d60b065450d5d26deb65d3f2bc1",
|
|
||||||
"sha256:3d2e3ab175fc097d2a51c7a0d3fda442f35ebcc93bb1d7bd9b95ad893e44c04d",
|
|
||||||
"sha256:4766dd695548a15ee766927bf883fb90c6ac8321be5a60c141f18628fb7f8da8",
|
|
||||||
"sha256:56b6978798502ef66625a2e0f80cf923da64e328da8bbe16c1ff928c70c873de",
|
|
||||||
"sha256:5cddb6f8bce14325b2863f9d5ac5c51e07b71b462361fd815d1d7706d3a9d682",
|
|
||||||
"sha256:644ee788222d81555af543b70a1098f2025db38eaa99226f3a75a6854924d4db",
|
|
||||||
"sha256:64cf762049fc4775efe6b27161467e76d0ba145862802a65eefc8879086fc6f8",
|
|
||||||
"sha256:68c362848d9fb71d3c3e5f43c09974a0ae319144634e7a47db62f0f2a54a7fa7",
|
|
||||||
"sha256:6c1f3c6f6635e611d58e467bf4371883568f0de9ccc4606f17048142dec14a1f",
|
|
||||||
"sha256:b213d4a02eec4ddf622f4d2fbc539f062af3788d1f332f028a2e19c42da53f15",
|
|
||||||
"sha256:bb27d4e7805a7de0e35bd0cb1411bc85f807968b2b0539597a49a23b00a622ae",
|
|
||||||
"sha256:c9d414512eaa417aadae7758bc118868cd2396b0e6138c1dd4fda96679c079d3",
|
|
||||||
"sha256:f0937165d1e25477b01081c4763d2d9cdc3b18af69cb259dd4f640c9b900fe5e",
|
|
||||||
"sha256:fb96a6e2c11059ecf84e6741a319f93f683e440e341d4489c9b161eca251cf2a",
|
|
||||||
"sha256:fc71d2d6ae56a091a8d94f33ec9d0f2001d1cb1db423d8b4355debfe9ce689b7"
|
|
||||||
],
|
|
||||||
"markers": "implementation_name == 'cpython'",
|
|
||||||
"version": "==1.3.4"
|
|
||||||
},
|
|
||||||
"urllib3": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0",
|
|
||||||
"sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3"
|
|
||||||
],
|
|
||||||
"version": "==1.24.2"
|
|
||||||
},
|
|
||||||
"wcwidth": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
|
|
||||||
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
|
|
||||||
],
|
|
||||||
"version": "==0.1.7"
|
|
||||||
},
|
|
||||||
"wrapt": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533"
|
|
||||||
],
|
|
||||||
"version": "==1.11.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
POST /eval {
|
|
||||||
(@raw:request/body) > @fifo:response/stream/lines
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
FROM kapow
|
|
||||||
RUN apk add nmap
|
|
||||||
COPY nmap.pow .
|
|
||||||
CMD kapow nmap.pow
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
POST /list/{ip} {
|
|
||||||
nmap -sL @value:request/match/ip > @fifo:response/body
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
GET /list/files {
|
|
||||||
ls -la @value:request/params/path > @fifo:response/body
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /list/processes {
|
|
||||||
ps -aux > @fifo:response/body
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /show/cpuinfo {
|
|
||||||
cat /proc/cpuinfo > @fifo:response/body
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /show/memory {
|
|
||||||
free -m > @fifo:response/body
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /show/disk {
|
|
||||||
df -h > @fifo:response/body
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /show/connections {
|
|
||||||
ss -pluton > @fifo:response/body
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /show/mounts {
|
|
||||||
mount > @fifo:response/body
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /tail/dmesg {
|
|
||||||
echo "text/plain" > @fifo:response/header/Content-Type
|
|
||||||
dmesg -w > @fifo:response/stream/lines
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /tail/journal {
|
|
||||||
echo "text/plain" > @fifo:response/header/Content-Type
|
|
||||||
journalctl -f > @fifo:response/stream/lines
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
POST /convert/{from}/pdf {
|
|
||||||
tmpfile=$(mktemp --suffix=.pdf)
|
|
||||||
pandoc --from=@value:request/match/from \
|
|
||||||
--to=pdf \
|
|
||||||
--output=${tmpfile} \
|
|
||||||
-t latex \
|
|
||||||
@file:request/body
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
cat ${tmpfile} > @fifo:response/body
|
|
||||||
echo "application/pdf" > @fifo:response/header/Content-Type
|
|
||||||
echo 200 > @fifo:response/status
|
|
||||||
else
|
|
||||||
echo 500 > @fifo:response/status
|
|
||||||
fi
|
|
||||||
rm -f ${tmpfile}
|
|
||||||
}
|
|
||||||
|
|
||||||
POST /convert/{from}/{to} {
|
|
||||||
pandoc --from=@value:request/match/from \
|
|
||||||
--to=@value:request/match/to \
|
|
||||||
--output=@fifo:response/body \
|
|
||||||
@file:request/body
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /formats/input {
|
|
||||||
pandoc --list-input-formats > @fifo:response/body
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /formats/output {
|
|
||||||
pandoc --list-output-formats | grep -v pdf > @fifo:response/body
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>PDF Editor</title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/siimple/dist/siimple.min.css">
|
|
||||||
<head>
|
|
||||||
<body>
|
|
||||||
<div class="siimple-content siimple-content--extra-large">
|
|
||||||
<div class="siimple-grid">
|
|
||||||
<div class="siimple-grid-row">
|
|
||||||
<div class="siimple-grid-col siimple-grid-col--12">
|
|
||||||
<div class="siimple-grid-col siimple-grid-col--6">
|
|
||||||
<div class="siimple-form">
|
|
||||||
<form action="/editor/pdf" method="post" target="result" id="editor">
|
|
||||||
<div class="siimple-form-title">AWYSIWYG PDF Editor</div>
|
|
||||||
<div class="siimple-form-field">
|
|
||||||
<div class="siimple-form-field-label">InputFormat</div>
|
|
||||||
<select name="from">
|
|
||||||
<option value="markdown">Markdown</option>
|
|
||||||
<option value="rst">ReStructuredText</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="siimple-form-field">
|
|
||||||
<div class="siimple-form-field-label">InputFormat</div>
|
|
||||||
<textarea class="siimple-textarea siimple-textarea--fluid" rows="25" name="content">Example text</textarea>
|
|
||||||
</div>
|
|
||||||
<div class="siimple-form-field">
|
|
||||||
<div class="siimple-btn siimple-btn--blue" onclick="document.getElementById('editor').submit();">Preview!</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="siimple-grid-col siimple-grid-col--6">
|
|
||||||
<iframe name="result" src="" style="height: 100%; width: 100%;"></iframe>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
POST /editor/pdf {
|
|
||||||
tmpfile=$(mktemp --suffix=.pdf)
|
|
||||||
pandoc --from=@value:request/form/from \
|
|
||||||
--to=pdf \
|
|
||||||
--output=${tmpfile} \
|
|
||||||
-t latex \
|
|
||||||
@file:request/form/content
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
cat ${tmpfile} > @fifo:response/body
|
|
||||||
echo "application/pdf" > @fifo:response/header/Content-Type
|
|
||||||
echo 200 > @fifo:response/status
|
|
||||||
else
|
|
||||||
echo 500 > @fifo:response/status
|
|
||||||
fi
|
|
||||||
rm -f ${tmpfile}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
GET / = index.html
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
GET / {
|
|
||||||
echo "text/html" > @fifo:response/header/Content-Type
|
|
||||||
cat > @fifo:response/body <<-HTML
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<a href='javascript: Array.from(document.querySelectorAll("a")).filter(x => x.href.indexOf("magnet") != -1 ).map(x => x.href = "http://localhost:8080/save/magnet?link="+encodeURI(x.href))'>Add me to your bookmarks!</a>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
HTML
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /save/magnet {
|
|
||||||
set -x
|
|
||||||
link=@value:request/param/link
|
|
||||||
[ -z $link ] && echo 400 > @fifo:response/status && exit 0
|
|
||||||
|
|
||||||
watch_folder=/tmp
|
|
||||||
cd $watch_folder
|
|
||||||
[[ "$link" =~ xt=urn:btih:([^&/]+) ]] || exit;
|
|
||||||
echo "d10:magnet-uri${#link}:${link}e" > "meta-${BASH_REMATCH[1]}.torrent"
|
|
||||||
|
|
||||||
echo 302 > @fifo:response/status
|
|
||||||
echo "/torrent/list" > @fifo:response/header/Location
|
|
||||||
}
|
|
||||||
|
|
||||||
GET /torrent/list {
|
|
||||||
echo "..." > @fifo:response/body
|
|
||||||
}
|
|
||||||
@@ -1,504 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
"""
|
|
||||||
A Kapow! interpreter written in Python.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from shlex import quote as shell_quote
|
|
||||||
from string import Template
|
|
||||||
import asyncio
|
|
||||||
import contextlib
|
|
||||||
import io
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
import threading
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
from aiohttp import web
|
|
||||||
from pyparsing import alphas, nums, White
|
|
||||||
from pyparsing import LineStart, LineEnd, SkipTo
|
|
||||||
from pyparsing import Literal, Combine, Word, Suppress
|
|
||||||
from pyparsing import OneOrMore, Optional, delimitedList
|
|
||||||
import aiofiles
|
|
||||||
import click
|
|
||||||
|
|
||||||
log = logging.getLogger('kapow')
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
# Parser #
|
|
||||||
########################################################################
|
|
||||||
|
|
||||||
COMMENT = (Literal('#') + SkipTo(LineEnd()))(name="comment")
|
|
||||||
|
|
||||||
# Method
|
|
||||||
METHOD = (Literal('GET')
|
|
||||||
| Literal('POST')
|
|
||||||
| Literal('PUT')
|
|
||||||
| Literal('DELETE')
|
|
||||||
| Literal('PATCH'))
|
|
||||||
MULTI_METHOD = delimitedList(METHOD, delim="|", combine=True)
|
|
||||||
METHOD_SPEC = Combine(Literal('*') | MULTI_METHOD)
|
|
||||||
|
|
||||||
# Pattern
|
|
||||||
REGEX = Word(alphas + nums + '\\+*,.[]-_')(name="regex")
|
|
||||||
SYMBOL = Word(alphas)(name="symbol")
|
|
||||||
P_PATTERN = Combine('/{' + SYMBOL + Optional(':' + REGEX) + '}')
|
|
||||||
P_PATH = Word('/', alphas + nums + '$-_.+!*\'(),')
|
|
||||||
URLPATTERN = Combine(OneOrMore(P_PATTERN | P_PATH))(name="urlpattern")
|
|
||||||
|
|
||||||
# Body
|
|
||||||
BODY = (Suppress('{')
|
|
||||||
+ SkipTo(Combine(LineStart() + '}' + LineEnd()))(name="body"))
|
|
||||||
|
|
||||||
# Endpoint head
|
|
||||||
ENDPOINT = (Optional(METHOD_SPEC + Suppress(White()),
|
|
||||||
default='*')(name="method")
|
|
||||||
+ URLPATTERN
|
|
||||||
+ Suppress(White()))
|
|
||||||
|
|
||||||
# Endpoint bodies
|
|
||||||
CODE_EP = (ENDPOINT + BODY)(name="code_ep")
|
|
||||||
PATH_EP = (ENDPOINT + '=' + SkipTo(LineEnd())(name="path"))(name="path_ep")
|
|
||||||
|
|
||||||
KAPOW_PROGRAM = CODE_EP | PATH_EP | COMMENT
|
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
# Resources #
|
|
||||||
########################################################################
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ResourceManager:
|
|
||||||
"""A resource exposed to the subshell."""
|
|
||||||
#: Kapow resource representation
|
|
||||||
kapow_repr: str
|
|
||||||
#: Representation of the resource that can be understood by the shell
|
|
||||||
shell_repr: str
|
|
||||||
#: Coroutine capable of managing the resource internally
|
|
||||||
coro: object
|
|
||||||
#: Path to readed fifo. Needs to be written for coro to release.
|
|
||||||
#: XXX: Use proper fifo async instead
|
|
||||||
fifo_path: str = None
|
|
||||||
#: Fifo direction 'read'/'write'
|
|
||||||
fifo_direction: str = None
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def get_value(context, path):
|
|
||||||
"""Return the value of an http resource."""
|
|
||||||
def nrd(n):
|
|
||||||
"""Return the nrd element in a path."""
|
|
||||||
return path.split('/')[n]
|
|
||||||
|
|
||||||
try:
|
|
||||||
if path == 'request/method':
|
|
||||||
return context['request'].method.encode('utf-8')
|
|
||||||
elif path == 'request/path':
|
|
||||||
return context['request'].path.encode('utf-8')
|
|
||||||
elif path.startswith('request/match'):
|
|
||||||
return context['request'].match_info[nrd(2)].encode('utf-8')
|
|
||||||
elif path.startswith('request/param'):
|
|
||||||
return context['request'].rel_url.query[nrd(2)].encode('utf-8')
|
|
||||||
elif path.startswith('request/header'):
|
|
||||||
return context['request'].headers[nrd(2)].encode('utf-8')
|
|
||||||
elif path.startswith('request/cookie'):
|
|
||||||
return context['request'].cookies[nrd(2)].encode('utf-8')
|
|
||||||
elif path.startswith('request/form'):
|
|
||||||
return (await context['request'].post())[nrd(2)].encode('utf-8')
|
|
||||||
elif path.startswith('request/file'):
|
|
||||||
name = nrd(2)
|
|
||||||
content = nrd(3) # filename / content
|
|
||||||
field = (await context['request'].post())[name]
|
|
||||||
if content == 'filename':
|
|
||||||
try:
|
|
||||||
return field.filename.encode('utf-8')
|
|
||||||
except:
|
|
||||||
return b''
|
|
||||||
elif content == 'content':
|
|
||||||
try:
|
|
||||||
return field.file.read()
|
|
||||||
except:
|
|
||||||
return b''
|
|
||||||
else:
|
|
||||||
raise ValueError(f'Unknown content type {content!r}')
|
|
||||||
elif path == 'request/body':
|
|
||||||
return await context['request'].read()
|
|
||||||
else:
|
|
||||||
raise ValueError(f'Unknown path {path!r}')
|
|
||||||
except KeyError:
|
|
||||||
return b''
|
|
||||||
|
|
||||||
|
|
||||||
async def set_value(context, path, value):
|
|
||||||
"""
|
|
||||||
Write to an http resource.
|
|
||||||
|
|
||||||
File-like resources like `body` get write() calls so they have
|
|
||||||
append semantics. Non file-like resources are just set.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
|
|
||||||
def nrd(n):
|
|
||||||
return path.split('/')[n]
|
|
||||||
|
|
||||||
if path == 'response/status':
|
|
||||||
context['response_status'] = int(value.decode('utf-8'))
|
|
||||||
elif path == 'response/body':
|
|
||||||
context['response_body'].write(value)
|
|
||||||
elif path == 'response/body':
|
|
||||||
context['response_stream'].write(value)
|
|
||||||
elif path.startswith('response/header/'):
|
|
||||||
clean = value.rstrip(b'\n').decode('utf-8')
|
|
||||||
context['response_headers'][nrd(2)] = clean
|
|
||||||
elif path.startswith('response/cookie/'):
|
|
||||||
clean = value.rstrip(b'\n').decode('utf-8')
|
|
||||||
context['response_cookies'][nrd(2)] = clean
|
|
||||||
else:
|
|
||||||
raise ValueError(f'Unknown path {path!r}')
|
|
||||||
|
|
||||||
|
|
||||||
def is_readable(path):
|
|
||||||
return path.startswith('request/')
|
|
||||||
|
|
||||||
|
|
||||||
def is_writable(path):
|
|
||||||
return path.startswith('response/')
|
|
||||||
|
|
||||||
|
|
||||||
def get_manager(resource, context):
|
|
||||||
"""
|
|
||||||
Return an async context manager capable of manage the given
|
|
||||||
resource.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
view, path = resource.split(':')
|
|
||||||
except:
|
|
||||||
log.error(f"Invalid resource %r", resource)
|
|
||||||
raise
|
|
||||||
|
|
||||||
@contextlib.asynccontextmanager
|
|
||||||
async def manager():
|
|
||||||
"""
|
|
||||||
Manage the given `resource` as an async context manager.
|
|
||||||
|
|
||||||
This context manager has different behavior depending on the
|
|
||||||
`view` and/or `path` of the resource.
|
|
||||||
|
|
||||||
As a context manager it has three sections:
|
|
||||||
- Before `yield`: Prepare, if needed, the physical resource on
|
|
||||||
disk.
|
|
||||||
- `yield`: Return a `ResourceManager` object containing the
|
|
||||||
shell representation of the object and the coroutine
|
|
||||||
consuming/generating the resource data.
|
|
||||||
- After `yield`: Cleanup any disk resource.
|
|
||||||
"""
|
|
||||||
if view == 'raw':
|
|
||||||
if not is_readable(path):
|
|
||||||
raise ValueError(f'Non-readable path "{path}".')
|
|
||||||
else:
|
|
||||||
value = await get_value(context, path)
|
|
||||||
yield ResourceManager(
|
|
||||||
kapow_repr=resource,
|
|
||||||
shell_repr=value.decode('utf-8'),
|
|
||||||
coro=asyncio.sleep(0))
|
|
||||||
elif view == 'value':
|
|
||||||
if not is_readable(path):
|
|
||||||
raise ValueError(f'Non-readable path "{path}".')
|
|
||||||
else:
|
|
||||||
value = await get_value(context, path)
|
|
||||||
yield ResourceManager(
|
|
||||||
kapow_repr=resource,
|
|
||||||
shell_repr=shell_quote(value.decode('utf-8')),
|
|
||||||
coro=asyncio.sleep(0))
|
|
||||||
elif view == 'fifo':
|
|
||||||
# No race condition here? Shut your ass!!
|
|
||||||
# https://stackoverflow.com/a/1430566
|
|
||||||
filename = tempfile.mktemp()
|
|
||||||
os.mkfifo(filename)
|
|
||||||
if path.startswith('response/stream'):
|
|
||||||
async def manage_fifo():
|
|
||||||
initialized = False
|
|
||||||
try:
|
|
||||||
async with aiofiles.open(filename, 'rb') as fifo:
|
|
||||||
while True:
|
|
||||||
if path.endswith('/lines'):
|
|
||||||
chunk = await fifo.readline()
|
|
||||||
else:
|
|
||||||
chunk = await fifo.read(1024*10)
|
|
||||||
if chunk:
|
|
||||||
if not initialized:
|
|
||||||
# Give a chance to other coroutines
|
|
||||||
# to write changes to response
|
|
||||||
# (headers, etc)
|
|
||||||
await asyncio.sleep(0)
|
|
||||||
response = web.StreamResponse(
|
|
||||||
status=200,
|
|
||||||
headers=context["response_headers"],
|
|
||||||
reason="OK")
|
|
||||||
for name, value in context["response_cookies"]:
|
|
||||||
response.set_cookie(name, value)
|
|
||||||
context["stream"] = response
|
|
||||||
await response.prepare(context["request"])
|
|
||||||
initialized = True
|
|
||||||
await response.write(chunk)
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
finally:
|
|
||||||
os.unlink(filename)
|
|
||||||
else:
|
|
||||||
async def manage_fifo():
|
|
||||||
try:
|
|
||||||
if is_readable(path):
|
|
||||||
async with aiofiles.open(filename, 'wb') as fifo:
|
|
||||||
await fifo.write(await get_value(context, path))
|
|
||||||
elif is_writable(path):
|
|
||||||
async with aiofiles.open(filename, 'rb') as fifo:
|
|
||||||
buf = io.BytesIO()
|
|
||||||
while True:
|
|
||||||
chunk = await fifo.read(128)
|
|
||||||
if not chunk:
|
|
||||||
break
|
|
||||||
buf.write(chunk)
|
|
||||||
await set_value(context, path, buf.getvalue())
|
|
||||||
else:
|
|
||||||
raise RuntimeError('WTF!')
|
|
||||||
finally:
|
|
||||||
os.unlink(filename)
|
|
||||||
yield ResourceManager(
|
|
||||||
kapow_repr=resource,
|
|
||||||
shell_repr=shell_quote(filename),
|
|
||||||
coro=manage_fifo(),
|
|
||||||
fifo_path=filename,
|
|
||||||
fifo_direction='read' if is_readable(path) else 'write')
|
|
||||||
elif view == 'file':
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w+b', buffering=0) as tmp:
|
|
||||||
if is_readable(path):
|
|
||||||
value = await get_value(context, path)
|
|
||||||
tmp.write(value)
|
|
||||||
tmp.flush()
|
|
||||||
|
|
||||||
yield ResourceManager(
|
|
||||||
kapow_repr=resource,
|
|
||||||
shell_repr=shell_quote(tmp.name),
|
|
||||||
coro=asyncio.sleep(0))
|
|
||||||
|
|
||||||
if is_writable(path):
|
|
||||||
tmp.seek(0)
|
|
||||||
await set_value(context, path, tmp.read())
|
|
||||||
else:
|
|
||||||
raise ValueError(f'Unknown view type {view}')
|
|
||||||
|
|
||||||
return manager
|
|
||||||
|
|
||||||
|
|
||||||
class KapowTemplate(Template):
|
|
||||||
"""Shell-code templating for @view:path variables substitution"""
|
|
||||||
|
|
||||||
delimiter = '@'
|
|
||||||
idpattern = r'(?a:[_a-z][_a-z0-9]*:[_a-z][-_a-z0-9/]*)'
|
|
||||||
|
|
||||||
async def run(self, context):
|
|
||||||
"""Run this template allocating and deallocating resources."""
|
|
||||||
async with contextlib.AsyncExitStack() as stack:
|
|
||||||
# Initialize all resources creating a mapping
|
|
||||||
resources = dict() # resource: (shell_repr, manager)
|
|
||||||
for match in self.pattern.findall(self.template):
|
|
||||||
delim, resource, *rest = match
|
|
||||||
if not resource: # When is braced
|
|
||||||
resource = rest[0]
|
|
||||||
if delim and not resource and rest == ['', '']:
|
|
||||||
# Escaped
|
|
||||||
continue
|
|
||||||
if resource not in resources:
|
|
||||||
try:
|
|
||||||
manager = get_manager(resource, context)
|
|
||||||
except:
|
|
||||||
log.error(f"Invalid match %r, %r, %r", delim, resource, rest)
|
|
||||||
raise
|
|
||||||
resources[resource] = await stack.enter_async_context(manager())
|
|
||||||
|
|
||||||
code = self.substitute(**{k: v.shell_repr
|
|
||||||
for k, v in resources.items()})
|
|
||||||
|
|
||||||
log.debug("Creating tasks")
|
|
||||||
manager_tasks = {asyncio.create_task(v.coro): v
|
|
||||||
for k, v in resources.items()}
|
|
||||||
|
|
||||||
await asyncio.sleep(0)
|
|
||||||
log.debug("Creating subprocess")
|
|
||||||
shell_task = await asyncio.create_subprocess_shell(
|
|
||||||
code,
|
|
||||||
executable=os.environ.get('SHELL', '/bin/sh'))
|
|
||||||
|
|
||||||
log.debug("Waiting for subprocess")
|
|
||||||
await shell_task.wait() # Run the subshell process
|
|
||||||
|
|
||||||
done, pending = await asyncio.wait(manager_tasks.keys(), timeout=0.1)
|
|
||||||
|
|
||||||
if pending:
|
|
||||||
for task in pending:
|
|
||||||
resource = manager_tasks[task]
|
|
||||||
if resource.fifo_path is not None:
|
|
||||||
log.debug(f"Trying to stop %s", resource.kapow_repr)
|
|
||||||
if resource.fifo_direction == 'write':
|
|
||||||
os.system(f"echo -n > {resource.fifo_path} &")
|
|
||||||
elif resource.fifo_direction == 'read':
|
|
||||||
os.system(f"cat {resource.fifo_path} > /dev/null &")
|
|
||||||
else:
|
|
||||||
raise ValueError("Unknown direction")
|
|
||||||
else:
|
|
||||||
log.debug(f"Non-fifo resource pending!! %s", resource.kapow_repr)
|
|
||||||
|
|
||||||
log.debug("Waiting for pending resources...")
|
|
||||||
await asyncio.wait(pending)
|
|
||||||
|
|
||||||
await asyncio.sleep(0)
|
|
||||||
|
|
||||||
|
|
||||||
def create_context(request):
|
|
||||||
"""Create a request context with default values."""
|
|
||||||
context = dict()
|
|
||||||
context["request"] = request
|
|
||||||
context["stream"] = None
|
|
||||||
context["response_body"] = io.BytesIO()
|
|
||||||
context["response_status"] = 200
|
|
||||||
context["response_headers"] = dict()
|
|
||||||
context["response_cookies"] = dict()
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
async def response_from_context(context):
|
|
||||||
"""Return the appropia aiohttp response for a given context."""
|
|
||||||
if context["stream"] is not None:
|
|
||||||
await context["stream"].write_eof()
|
|
||||||
return context["stream"]
|
|
||||||
else:
|
|
||||||
body = context["response_body"].getvalue()
|
|
||||||
status = context["response_status"]
|
|
||||||
headers = context["response_headers"]
|
|
||||||
cookies = context["response_cookies"]
|
|
||||||
|
|
||||||
# Content-Type guessing (for demo only)
|
|
||||||
if "Content-Type" not in headers:
|
|
||||||
try:
|
|
||||||
body = body.decode("utf-8")
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
headers["Content-Type"] = "text/plain"
|
|
||||||
|
|
||||||
response = web.Response(body=body, status=status, headers=headers)
|
|
||||||
for name, value in cookies.items():
|
|
||||||
response.set_cookie(name, value)
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def generate_endpoint(code, path=None):
|
|
||||||
"""Return an aiohttp-endpoint coroutine to run kapow `code`."""
|
|
||||||
async def endpoint(request):
|
|
||||||
context = create_context(request)
|
|
||||||
log.debug("Running endpoint %r", path)
|
|
||||||
try:
|
|
||||||
await KapowTemplate(code).run(context) # Will change context
|
|
||||||
except:
|
|
||||||
log.exception("Template crashed!")
|
|
||||||
log.debug("Endpoint finished, creating response %r", path)
|
|
||||||
response = await response_from_context(context)
|
|
||||||
log.debug("Responding %r", path)
|
|
||||||
return response
|
|
||||||
return endpoint
|
|
||||||
|
|
||||||
|
|
||||||
def path_server(path):
|
|
||||||
"""Return an aiohttp-endpoint coroutine to serve the file in `path`."""
|
|
||||||
# At initialization check
|
|
||||||
if not os.path.isfile(path):
|
|
||||||
raise NotImplementedError("Only files can be served.")
|
|
||||||
|
|
||||||
async def serve_path(request):
|
|
||||||
# Per request check
|
|
||||||
if os.path.isdir(path):
|
|
||||||
raise NotImplementedError("Cannot serve whole directories yet.")
|
|
||||||
return web.FileResponse(path)
|
|
||||||
return serve_path
|
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
# Webserver #
|
|
||||||
########################################################################
|
|
||||||
|
|
||||||
def register_code_endpoint(app, methods, pattern, code):
|
|
||||||
"""Register all needed endpoints for the defined endpoint code."""
|
|
||||||
endpoint = generate_endpoint(code, pattern)
|
|
||||||
for method in methods: # May be '*'
|
|
||||||
app.add_routes([web.route(method, pattern, endpoint)])
|
|
||||||
|
|
||||||
|
|
||||||
def register_path_endpoint(app, methods, pattern, path):
|
|
||||||
"""Register all needed endpoints for the defined file."""
|
|
||||||
for method in methods:
|
|
||||||
if method != 'GET':
|
|
||||||
raise ValueError("Invalid method for serving files.")
|
|
||||||
else:
|
|
||||||
app.add_routes([web.static(pattern, path)])
|
|
||||||
|
|
||||||
|
|
||||||
async def debug_tasks():
|
|
||||||
while True:
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
log.debug("Tasks: %s | Threads: %s",
|
|
||||||
len(asyncio.Task.all_tasks()),
|
|
||||||
threading.active_count())
|
|
||||||
|
|
||||||
|
|
||||||
async def start_background_tasks(app):
|
|
||||||
app["debug_tasks"] = app.loop.create_task(debug_tasks())
|
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
|
||||||
@click.option('--expression', '-e')
|
|
||||||
@click.option('--verbose', '-v', count=True)
|
|
||||||
@click.argument('program', type=click.File(), required=False)
|
|
||||||
@click.pass_context
|
|
||||||
def main(ctx, program, verbose, expression):
|
|
||||||
"""Run the kapow server with the given command-line parameters."""
|
|
||||||
if program is None and expression is None:
|
|
||||||
program = sys.stdin
|
|
||||||
|
|
||||||
app = web.Application(client_max_size=1024*1024*1024)
|
|
||||||
|
|
||||||
if verbose == 0:
|
|
||||||
_print = lambda _: None
|
|
||||||
logging.basicConfig(stream=sys.stderr, level=logging.ERROR)
|
|
||||||
else:
|
|
||||||
_print = lambda s: print(s, file=sys.stderr)
|
|
||||||
if verbose == 1:
|
|
||||||
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
|
|
||||||
else:
|
|
||||||
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
|
|
||||||
if verbose > 2:
|
|
||||||
app.on_startup.append(start_background_tasks)
|
|
||||||
|
|
||||||
source = expression if program is None else program.read()
|
|
||||||
|
|
||||||
for ep, _, _ in KAPOW_PROGRAM.scanString(source):
|
|
||||||
if 'comment' in ep:
|
|
||||||
continue
|
|
||||||
methods = ep.method.asList()[0].split('|')
|
|
||||||
pattern = ''.join(ep.urlpattern)
|
|
||||||
if ep.body:
|
|
||||||
log.info(f"Registering [code] methods=%r pattern=%r", methods, pattern)
|
|
||||||
register_code_endpoint(app, methods, pattern, ep.body)
|
|
||||||
else:
|
|
||||||
log.info(f"Registering [path] methods=%r pattern=%r", methods, pattern)
|
|
||||||
register_path_endpoint(app, methods, pattern, ep.path)
|
|
||||||
web.run_app(app, print=_print)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
-130
@@ -1,130 +0,0 @@
|
|||||||
10-SECOND PROPOSAL
|
|
||||||
===================
|
|
||||||
|
|
||||||
*kapow* is a specialized language for marrying the **web** and the **shell**.
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
===========
|
|
||||||
|
|
||||||
The **web** and the **shell** are two different beasts, both packed with history.
|
|
||||||
|
|
||||||
There are some concepts in HTTP and the shell that **resemble each other**.
|
|
||||||
|
|
||||||
```
|
|
||||||
+------------------------+-------------------------+
|
|
||||||
| HTTP | SHELL |
|
|
||||||
+--------------+------------------------+-------------------------+
|
|
||||||
| Input | POST form-encoding | Command line parameters |
|
|
||||||
| Parameters | GET parameters | Environment variables |
|
|
||||||
| | Headers | |
|
|
||||||
| | Serialized body (JSON) | |
|
|
||||||
+--------------+------------------------+-------------------------+
|
|
||||||
| Data Streams | Response/Request Body | Stdin/Stdout/Stderr |
|
|
||||||
| | Websocket | Input/Output files |
|
|
||||||
| | Uploaded files | |
|
|
||||||
+--------------+------------------------+-------------------------+
|
|
||||||
| Control | Status codes | Signals |
|
|
||||||
| | HTTP Methods | |
|
|
||||||
+--------------+------------------------+-------------------------+
|
|
||||||
```
|
|
||||||
|
|
||||||
Any tool designed to give an HTTP interface to an existing shell command **must map concepts of boths**. For example:
|
|
||||||
|
|
||||||
- "GET parameters" to "Command line parameters"
|
|
||||||
- "Headers" to "Environment variables"
|
|
||||||
- "Stdout" to "Response body"
|
|
||||||
|
|
||||||
*kapow* is not opinionated about the different ways you can map both worlds. Instead it provides a concise language used to express the mapping and a set of common defaults.
|
|
||||||
|
|
||||||
|
|
||||||
Why not tool...?
|
|
||||||
----------------
|
|
||||||
|
|
||||||
All the alternatives we found are **rigid** about how they match between HTTP and shell concepts.
|
|
||||||
|
|
||||||
* [shell2http](https://github.com/msoap/shell2http): HTTP-server to execute shell commands. Designed for development, prototyping or remote control. Settings through two command line arguments, path and shell command.
|
|
||||||
* [websocketd](https://github.com/joewalnes/websocketd): Turn any program that uses STDIN/STDOUT into a WebSocket server. Like inetd, but for WebSockets.
|
|
||||||
* [webhook](https://github.com/adnanh/webhook): webhook is a lightweight incoming webhook server to run shell commands.
|
|
||||||
* [gotty](https://github.com/yudai/gotty): GoTTY is a simple command line tool that turns your CLI tools into web applications. (For interactive commands only)
|
|
||||||
|
|
||||||
Tools with a rigid matching **can't evade** *[impedance mismatch](https://haacked.com/archive/2004/06/15/impedance-mismatch.aspx/)*. Resulting is an easy-to-use software, convenient in some scenarios but incapable in others.
|
|
||||||
|
|
||||||
|
|
||||||
Why not my good-old programming language...?
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
* Boilerplate
|
|
||||||
* Custom code = More bugs
|
|
||||||
* Security issues (Command injection, etc)
|
|
||||||
* Dependency on developers
|
|
||||||
* "A programming language is low level when its programs require attention to the irrelevant" Alan Perlis
|
|
||||||
|
|
||||||
*kapow* aims to be halfway from one of the mentioned tools and a general programming language. A limited scripting language. Think of *awk*, 20 years later, for HTTP.
|
|
||||||
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
Imagine you want to improve your monitoring system to be able to check if a WiFi in a remote building is working. Suppose you already have a wireless capable linux server on that building.
|
|
||||||
|
|
||||||
What about exposing a wifi scan as a service?
|
|
||||||
|
|
||||||
```
|
|
||||||
$ iwlist scan | grep 'ESSID:"BBVA"'
|
|
||||||
```
|
|
||||||
|
|
||||||
The above command will list the visible WiFI networks and filter for the one we are interested in (**BBVA**); exiting with code **0** when found and **1** otherwise.
|
|
||||||
|
|
||||||
We want to signal the WiFI status with a meaningful HTTP status code: 200 for UP and 503 for DOWN.
|
|
||||||
|
|
||||||
So the only thing our service has to do is translate from the **command's exit code** to a meaningful **HTTP status code**.
|
|
||||||
|
|
||||||
Our *kapow* program (monitor.pow) should look **something like** this:
|
|
||||||
|
|
||||||
```
|
|
||||||
/monitor/wifi =
|
|
||||||
$(iwlist scan | grep 'ESSID:"BBVA')
|
|
||||||
| 0 = status 200 # OK
|
|
||||||
| 1 = status 503 # Service Unavailable
|
|
||||||
```
|
|
||||||
|
|
||||||
With this code, the user can run the service with:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ kapow monitor.pow
|
|
||||||
```
|
|
||||||
And then:
|
|
||||||
|
|
||||||
1. *kapow* will open a port with an HTTP server serving the URI **/monitor/wifi**.
|
|
||||||
2. When requested `curl http://<server>/monitor/wifi`. The command is executed resulting in a WiFI scan.
|
|
||||||
3. The command's **exit code** is translated to an HTTP **status code** and returned.
|
|
||||||
|
|
||||||
USE CASES OR VALUE
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Reuse 40+ years of existing computer programs as microservices (nanoservices?).
|
|
||||||
* Expose command line only tools as services. Eliminating the need of interactive SSH sessions.
|
|
||||||
* Fine-grained access control to CLI based on options/parameters
|
|
||||||
|
|
||||||
|
|
||||||
REQUIRED SKILLS
|
|
||||||
===============
|
|
||||||
|
|
||||||
- Knowledge of HTTP and the command line.
|
|
||||||
- Programming.
|
|
||||||
- Low-level stuff...
|
|
||||||
|
|
||||||
|
|
||||||
ESTIMATION
|
|
||||||
==========
|
|
||||||
|
|
||||||
- 2 weeks
|
|
||||||
|
|
||||||
|
|
||||||
OUTPUT
|
|
||||||
======
|
|
||||||
|
|
||||||
- A collection of **concrete** use cases, 5 to 10.
|
|
||||||
- A DSL design (over paper) to implement the use cases.
|
|
||||||
- A working **dirty** proof of concept.
|
|
||||||
Reference in New Issue
Block a user