Skip to content

Instantly share code, notes, and snippets.

@nwunderly
Last active August 5, 2021 01:45
Show Gist options
  • Save nwunderly/64a9c36325f40a2fadcf596909a96255 to your computer and use it in GitHub Desktop.
Save nwunderly/64a9c36325f40a2fadcf596909a96255 to your computer and use it in GitHub Desktop.
bulbe.rocks web stack
import asyncio
import os
import secrets
from os.path import splitext
from fastapi import FastAPI, HTTPException
from starlette.middleware.sessions import SessionMiddleware
from starlette.requests import Request
from starlette.responses import RedirectResponse, FileResponse
from starlette.staticfiles import StaticFiles
from starlette.templating import Jinja2Templates
app = FastAPI(redoc_url=None, docs_url=None)
app.token = None
image_folder = 'image/'
video_folder = 'video/'
file_folder = 'file/'
allowed_extension = ['.png', '.jpeg', '.jpg', '.gif', '.webm', '.mp4', '.py', '.txt', '.xml', '.log', '.sh', '.exe', '.php', '.css', '.html']
app.mount('/image', StaticFiles(directory='image'), name='image')
app.mount('/video', StaticFiles(directory='video'), name='video')
app.mount('/file', StaticFiles(directory='file'), name='file')
app.mount('/css', StaticFiles(directory='css'), name='css')
templates = Jinja2Templates(directory="templates")
@app.get('/')
async def index(request: Request):
session = request.session
if len(session) != 0:
if session['token'] == app.token:
return RedirectResponse('/dash', status_code=303)
return templates.TemplateResponse("index.html", {"request": request})
@app.post('/login')
async def login(request: Request):
session = request.session
form = await request.form()
if form['email'] == 'REPLACE_THIS' and form['password'] == 'REPLACE_THIS':
token = secrets.token_urlsafe()
app.token = token
session['token'] = token
return RedirectResponse('/dash')
else:
raise HTTPException(status_code=401)
@app.get('/dash')
async def dash(request: Request):
session = request.session
if len(session) != 0:
if session['token'] == app.token:
images = os.listdir('./image/')
files = os.listdir('./file/')
videos = os.listdir('./video/')
return templates.TemplateResponse("dash.html", {"request": request, "images": images, "files": files, "videos": videos})
else:
raise HTTPException(status_code=401)
else:
raise HTTPException(status_code=401)
@app.post('/dash') # this is here because login bugs out sometimes
async def _dash(request: Request):
return await dash(request)
@app.post('/upload')
async def upload(request: Request):
if str(request.method) == 'POST':
if request.headers.get('x-authorization') == 'REPLACE_THIS':
form = await request.form()
filename = form["upload_file"].filename
extension = splitext(filename)[1]
if extension not in allowed_extension:
raise HTTPException(status_code=415, detail=f"File type not supported, only {allowed_extension} is allowed.")
if extension in ['.png', '.jpeg', '.jpg']:
folder = image_folder
filename = secrets.token_urlsafe(5)
binary_file = open(f'{image_folder}{filename}{extension}', 'wb')
binary_file.write(await form['upload_file'].read())
binary_file.close()
elif extension in ['.gif', '.webm', '.mp4']:
folder = video_folder
filename = secrets.token_urlsafe(5)
binary_file = open(f'{video_folder}{filename}{extension}', 'wb')
binary_file.write(await form['upload_file'].read())
binary_file.close()
elif extension in ['.py', '.txt', '.xml', '.log', '.sh', '.exe', '.php', '.css', '.html']:
folder = file_folder
filename = secrets.token_urlsafe(5)
binary_file = open(f'{file_folder}{filename}{extension}', 'wb')
binary_file.write(await form["upload_file"].read())
binary_file.close()
return {"filename": filename, "extension": extension, "folder": folder}
else:
raise HTTPException(status_code=401)
@app.get('/{rest_of_path:path}')
async def catch_all(request: Request, rest_of_path: str = None):
for file in os.listdir('image'):
if file.split('.')[0] == rest_of_path or file == rest_of_path:
return FileResponse(f'image/{file}')
raise HTTPException(status_code=404)
app.add_middleware(SessionMiddleware, secret_key=str("REPLACE_THIS"), same_site="strict", https_only=True, max_age=14*24*60*60)
This gist has all of the code and configuration
information behind the web apps I'm hosting
at my domain, bulbe.rocks.
THIS IS OLD CODE. I AM NO LONGER USING OR MAINTAINING THIS GIST.
directory layout:
/HOME_DIR:
web.sh
/web
/Caddy
Caddyfile
/data
/config
/Hastebin
/data
/ShareX
app.py
Dockerfile
/css
/image
/file
/video
/templates
/Shlink
/data
Caddy
https://caddyserver.com/
https://www.youtube.com/watch?v=nk4EWHvvZtI
https://hub.docker.com/_/caddy
Hastebin
https://hub.docker.com/r/rlister/hastebin
Shlink (URL shortener)
https://hub.docker.com/r/shlinkio/shlink
FastAPI
https://fastapi.tiangolo.com/
VPN (NOTE: THIS IS HARD TO SET UP)
https://github.com/kylemanna/docker-openvpn
bulbe.rocks {
respond "I work"
}
i.bulbe.rocks {
reverse_proxy sharex:9000
}
paste.bulbe.rocks {
reverse_proxy hastebin:7777
}
smol.bulbe.rocks {
reverse_proxy shlink:8080
}
version: "3.7"
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- $PWD/Caddy/Caddyfile:/etc/caddy/Caddyfile
- $PWD/Caddy/data:/data
- $PWD/Caddy/config:/config
sharex:
build:
context: $PWD/ShareX
restart: unless-stopped
ports:
- "9000:9000"
volumes:
- $PWD/ShareX/image:/image
- $PWD/ShareX/video:/video
- $PWD/ShareX/file:/file
hastebin:
image: rlister/hastebin
restart: unless-stopped
environment:
STORAGE_TYPE: file
ports:
- "7777:7777"
volumes:
- $PWD/Hastebin/data:/app/data
shlink:
image: shlinkio/shlink
restart: unless-stopped
environment:
SHORT_DOMAIN_HOST: smol.bulbe.rocks
SHORT_DOMAIN_SCHEMA: https
volumes:
- $PWD/Shlink/data:/etc/shlink/data
FROM python:3.8
RUN pip install fastapi uvicorn starlette itsdangerous aiofiles jinja2 pyyaml python-multipart
COPY . .
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "9000"]
cd web
docker-compose stop
docker-compose rm --force
docker-compose up -d --build
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment