diff options
author | Erg <uinarf@autistici.org> | 2024-11-28 16:42:41 +0100 |
---|---|---|
committer | Erg <uinarf@autistici.org> | 2024-11-28 16:42:41 +0100 |
commit | d12675c238c77dc83a92eaaf8ec70a9dcf26a363 (patch) | |
tree | 5cf27ecfab92577e574e832ea5a7824474066460 /jupyter_files/creating_fastapi_static_blog.ipynb | |
parent | bf8efbce7f202f1f7650f628955fb0e42d343a1f (diff) | |
download | fastapi_blog-private.tar.gz fastapi_blog-private.tar.bz2 fastapi_blog-private.zip |
Add Private files :Pprivate
Diffstat (limited to 'jupyter_files/creating_fastapi_static_blog.ipynb')
-rw-r--r-- | jupyter_files/creating_fastapi_static_blog.ipynb | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/jupyter_files/creating_fastapi_static_blog.ipynb b/jupyter_files/creating_fastapi_static_blog.ipynb new file mode 100644 index 0000000..b2c1851 --- /dev/null +++ b/jupyter_files/creating_fastapi_static_blog.ipynb @@ -0,0 +1,507 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d745c9ae-31f8-47e5-83df-42c0a39e700e", + "metadata": {}, + "source": [ + "# Setting up FastAPI with Uvicorn and Openrc to serve Jupyter Notebook generated html files." + ] + }, + { + "cell_type": "markdown", + "id": "5dec85d7-1ac2-4bdd-a5c4-40dc8ec80c1f", + "metadata": {}, + "source": [ + "## Prerequisites:\n", + "- system with openrc\n", + "- fastapi\n", + "- uvicorn\n", + "- jupyter notebook (optional)\n", + "- nginx" + ] + }, + { + "cell_type": "markdown", + "id": "3d5a9177-1fcd-4bc6-97dd-f85c712e8a9e", + "metadata": {}, + "source": [ + "## Creating FastAPI application" + ] + }, + { + "cell_type": "markdown", + "id": "6f2751d9-cdfe-417e-8a30-8768e2fd828e", + "metadata": {}, + "source": [ + "Create a directory for project and enter it:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58fe2a08-afb0-400b-a8a2-063da88a2559", + "metadata": {}, + "outputs": [], + "source": [ + "$ mkdir -p ~/Fastapi_blog/static && cd ~/Fastapi_blog" + ] + }, + { + "cell_type": "markdown", + "id": "2ad52653-7f16-489f-8a4b-e2fd08f90d05", + "metadata": {}, + "source": [ + "Create a file containing code and html template folder and template for the main page:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27e461b5-279f-4143-ac69-5ef61b75e6af", + "metadata": {}, + "outputs": [], + "source": [ + "$ touch main.py\n", + "$ mkdir templates\n", + "$ touch templates/main_page.html" + ] + }, + { + "cell_type": "markdown", + "id": "9c3d9c08-81ea-43b0-bea8-8486adb06d85", + "metadata": {}, + "source": [ + "main_page.html can be really minimal:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6a61c2c-708e-462d-b487-0ddd23b5e08a", + "metadata": {}, + "outputs": [], + "source": [ + "<html>\n", + "<body>\n", + " <h1>List of articles</h1>\n", + " <h2>Number of posts: {{ files|length }}</h2>\n", + " {% if files %}\n", + " {% for item in files %}\n", + " <a href=\"static/html/{{ item }}\">{{ item }}</a><br>\n", + " {% endfor %}\n", + " {% endif %}\n", + "</body>\n", + "</html>" + ] + }, + { + "cell_type": "markdown", + "id": "b15b4217-9343-4c73-aa44-f069f6d5e776", + "metadata": {}, + "source": [ + "Create basic FastAPI application serving static files at '/'. I want that as I'll be serving if from behind Nginx reverse proxy. Prefix will be set with Nginx conf file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90f95ae9-4079-440e-9f40-2889541cb727", + "metadata": {}, + "outputs": [], + "source": [ + "#!/usr/bin/env python\n", + "\n", + "\"\"\"\n", + "FastAPI site serving static html files.\n", + "\"\"\"\n", + "\n", + "from os import listdir\n", + "from fastapi import FastAPI\n", + "from fastapi.staticfiles import StaticFiles\n", + "from fastapi import FastAPI, Request\n", + "from fastapi.staticfiles import StaticFiles\n", + "from fastapi.templating import Jinja2Templates\n", + "\n", + "\n", + "ARTICLES = 'static/html'\n", + "templates = Jinja2Templates(directory=\"templates\")\n", + "LIST_OF_ARTICLES = [i for i in listdir(ARTICLES) if i.endswith('.html')]\n", + "\n", + "app = FastAPI()\n", + "\n", + "app.mount(\"/\", StaticFiles(directory=\"static\", html=True), name=\"static\")\n", + "\n", + "\n", + "@app.get(\"/\")\n", + "async def main_page(request: Request):\n", + " \"\"\"Generate main page\"\"\"\n", + " return templates.TemplateResponse(\n", + " \"main_page.html\", {\n", + " \"request\": request,\n", + " \"files\": LIST_OF_ARTICLES,\n", + " \"static\": ARTICLES,\n", + " }\n", + " )\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " uvicorn.run(\n", + " app,\n", + " port=8000,\n", + " host='127.0.0.1',\n", + " log_level=\"info\",\n", + " # uds=\"/run/tech_blog.sock\", <- uncomment if you want Unix socket\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "0def2ab2-e037-4997-b472-59784554d12f", + "metadata": {}, + "source": [ + "Test run:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e4b78e2-9412-40a0-ad23-b1ebdc32b34d", + "metadata": {}, + "outputs": [], + "source": [ + "$ uvicorn main:app --reload" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "1ff45780-0f1a-4f8a-a50d-7c52ad08f52d", + "metadata": {}, + "source": [ + "If we try to run it as regular user with 'uds=\"/run/tech_blog.sock\"' line uncommented we should get permission denied error as we don't have rights to create files in /run.\n", + "\n", + "In browser, take a peek at http://localhost:8000 and you should already see something.\n", + "\n", + "Create a file with a test html code:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2cb8101-4914-494e-9c6e-222cd9cdb148", + "metadata": {}, + "outputs": [], + "source": [ + "$ touch static/test.html" + ] + }, + { + "cell_type": "markdown", + "id": "0496d679-15a2-4610-815b-9e1b57285ebf", + "metadata": {}, + "source": [ + "Put this in it:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3db82cd2-3f9f-4c74-b98b-2cf1b19dd353", + "metadata": {}, + "outputs": [], + "source": [ + "<html>\n", + "<body>\n", + "\n", + "<h1>Fun stuff comming up</h1>\n", + "<p>Bob's your uncle!</p>\n", + "\n", + "</body>\n", + "</html> " + ] + }, + { + "cell_type": "markdown", + "id": "fd3b37dc-e3d3-48a0-a496-7f0060bc69c0", + "metadata": {}, + "source": [ + "Visit http://localhost:8000/\n", + "You should see the main page with test article and you should be able to navigate to the article clicking the link." + ] + }, + { + "cell_type": "markdown", + "id": "cba92c27-c741-4959-88c5-b9790de7eddf", + "metadata": {}, + "source": [ + "## Jupyter side note" + ] + }, + { + "cell_type": "markdown", + "id": "f7fefa06-9d13-4793-8bb6-3f96f2171f25", + "metadata": {}, + "source": [ + "To write stuff with Jupyter Notebook start it up:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7eadb7f-e811-4bee-a8d7-68c68ec9f9ca", + "metadata": {}, + "outputs": [], + "source": [ + "$ jupyter-notebook" + ] + }, + { + "cell_type": "markdown", + "id": "d0ce0f2c-e633-4974-8ed4-1f4b3a021d6a", + "metadata": {}, + "source": [ + "In browser a new tab with jupyter notebook should have started. Write your fun stuff. Save it. If you don't want to execute cell content convert to html with:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a4da315-d47b-4bec-8b17-f2cbf494f4d4", + "metadata": {}, + "outputs": [], + "source": [ + "$ jupyter nbconvert --to html <path to your file>" + ] + }, + { + "cell_type": "markdown", + "id": "80c327c4-d985-47d6-94fa-10e8e5b7d2b4", + "metadata": {}, + "source": [ + "If you want to execute cell content convert with:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54af67cd-0d26-4373-9647-8943dec2f4ab", + "metadata": {}, + "outputs": [], + "source": [ + "$ jupyter nbconvert --execute --to html <path to your file>" + ] + }, + { + "cell_type": "markdown", + "id": "f8653115-4f64-40a7-9d72-0e088a607a07", + "metadata": {}, + "source": [ + "Want to change theme to dark?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52d5866b-813d-4ef3-9886-c775e1c67d5b", + "metadata": {}, + "outputs": [], + "source": [ + "$ jupyter nbconvert --execute --to html <path to your file> --HTMLExporter.theme=dark" + ] + }, + { + "cell_type": "markdown", + "id": "e73f69fc-6c8a-4617-90bd-a0e788b89756", + "metadata": {}, + "source": [ + "Now put it in your `static` folder. It will be served by your FastAPI application." + ] + }, + { + "cell_type": "markdown", + "id": "c56ef398-9f2e-4253-bc37-22b5926b8c7c", + "metadata": {}, + "source": [ + "## Bash script" + ] + }, + { + "cell_type": "markdown", + "id": "f37aba78-c09b-4807-ba02-c72f48b9dba5", + "metadata": {}, + "source": [ + "In order to run our python script with Openrc first we need to create a Bash script:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f4052ac-f582-47fa-b702-34f1341dec88", + "metadata": {}, + "outputs": [], + "source": [ + "#!/usr/bin/env bash\n", + "\n", + "cd /home/discordia/Fastapi_blog || exit # If your login is different, change home directory name.\n", + "exec python main.py" + ] + }, + { + "cell_type": "markdown", + "id": "dedf511e-f6fd-4c41-8b02-a2eb25e9604a", + "metadata": {}, + "source": [ + "Save it (here as fastapi_blog.sh) as a root user to /usr/local/bin and make executable:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c6e7b2d-33a3-4305-917b-c24155a648f2", + "metadata": {}, + "outputs": [], + "source": [ + "# chmod ugo+x /usr/local/bin/fastapi_blog.sh" + ] + }, + { + "cell_type": "markdown", + "id": "1bf1a799-25b0-4008-b1d2-1a555cef79c5", + "metadata": {}, + "source": [ + "## Openrc service file" + ] + }, + { + "cell_type": "markdown", + "id": "43794484-8e10-400d-a598-d1acefd455ee", + "metadata": {}, + "source": [ + "Now create a minimal service file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f14c6b71-3f1f-4dd8-b62d-d79721a8d6fe", + "metadata": {}, + "outputs": [], + "source": [ + "#!/sbin/openrc-run\n", + "\n", + "supervisor=supervise-daemon\n", + "command=\"/usr/local/bin/fastapi_blog.sh\"\n", + "pidfile=\"/run/${RC_SVCNAME}.pid\"\n", + "command_args=\"-p ${pidfile}\"\n", + "command_background=True" + ] + }, + { + "cell_type": "markdown", + "id": "8982eef9-a095-4b57-b0a6-5d59ac12be78", + "metadata": {}, + "source": [ + "Save it as a root user in /etc/init.d and make executable:" + ] + }, + { + "cell_type": "markdown", + "id": "6336a60f-6e8a-489b-8bc8-9b9e6f8e02fc", + "metadata": {}, + "source": [ + "Save it as a root user in /etc/init.d and make executable:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "445a5744-8e71-4104-8601-ad444ca0f9c1", + "metadata": {}, + "outputs": [], + "source": [ + "# chmod ugo+x /etc/init.d/fastapi_blog" + ] + }, + { + "cell_type": "markdown", + "id": "0985e123-559c-4f9b-8bfc-7f75e9251756", + "metadata": {}, + "source": [ + "You should be able to start/restart/stop it normally:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c856613b-8541-43e4-b7e0-1d0f2a4a6d22", + "metadata": {}, + "outputs": [], + "source": [ + "# rc-service fastapi_blog start\n", + "# rc-service fastapi_blog restart\n", + "# rc-service fastapi_blog stop" + ] + }, + { + "cell_type": "markdown", + "id": "719552bc-b736-41db-939c-6a1010ca9557", + "metadata": {}, + "source": [ + "If you're using Unix socket, check for existence of /run/fastapi_blog.sock.\n", + "Else navigate to http://127.0.0.1:8000/test.html to see page is indeed there." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8f408bf-120c-4adf-8e8b-3dc019a015ae", + "metadata": {}, + "outputs": [], + "source": [ + "$ ps aux | grep \"python main.py\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "13c17523-3603-4b8a-b8a2-563ae6dc27d5", + "metadata": {}, + "source": [ + "You want to see only one 'python main.py'.\n", + "At first I did not use exec which resulted in lingering processess. You don't want that.\n", + "\n", + "Now you can plug it in to your preffered proxy server using either a unix socket or a TCP socket." + ] + }, + { + "cell_type": "markdown", + "id": "3134cb86-be0f-456c-b755-cd4f43ef2810", + "metadata": {}, + "source": [ + "If you're using unix socket, check for existence of /run/fastapi_blog.sock, else either use lsof to see what's using port 8000 or navigate to http://127.0.0.1:8000/test.html" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} |