In this post I will explain how PlaneView is set up and works. I will explain how the back-end is set up, how the aircraft classification works, how the front-end is set up, the image storage, and the deployment.
Back-end
Let's start with the back-end. For the back-end I chose to go with FastAPI, as it provides a simple and fast way to create API endpoints that the front-end can access.
Let's look at an example. A user uploads an image of an aircraft. The image is sent to the API endpoint associated with the upload button. The code on the back-end now checks if the uploaded image actually is an image. If it passes the check, inference is run on the image to find the plane type shown in the image. Once that is complete, the image and associated data like the user that uploaded the image, the inference result, time of upload, the image’s unique name, and more are stored in the PostgreSQL database that is running. The image is stored in MinIO object storage.
Authentication
As this is a learning project, I chose to implement my own authentication system. Account creation and login also work over a FastAPI API endpoint. This is a common way of doing it and is safe, as traffic always uses HTTPS and therefore is encrypted. The API endpoint calls necessary functions to encrypt the user’s password, initialize email verification, and store the needed user data in the database. As everyone should know, passwords should not be stored in plain text. When the API endpoint receives a password it is directly encrypted, and only the hash is stored. The unencrypted password is never stored. As only the hash of a password is stored, it is easy to check if the provided password by a user on login is correct, as most encryption libraries provide a comparison tool.
To keep a user logged in and to show user-specific data like a user’s images or account data, we use session cookies. When a user logs in to his account, a session token is created, hashed, and stored. If the user now navigates to the account page, for example, the browser sends the session token to the back-end. The back-end checks if it is valid and, if it is, displays user-specific data.
Image Inference
The core feature of PlaneView is to find the aircraft type from an image. This is done by training Yolov8-cls on pre-labeled images of different aircraft types. Using the YOLO Python library from Ultralytics, we can use the weights file our training generated to run inference on an image. The model will then output the top 5 aircraft types with the highest probability. The type with the highest probability is then displayed to the user. Right now the inference runs directly inside the code of the API endpoint. As this is a blocking operation, the process is blocked for that time. Right now this is not noticeable, as inference takes about 500 ms and PlaneView does not really have any users that run inference at the same time. As this is a learning project, I decided to migrate the annotation job to workers that only run inference. This will remove the inference from the API endpoint, removing the blocking operation and making the site more responsive if it ever should get more users. This will be achieved by a producer–consumer pattern and a job queue. The API endpoint (the producer) will add an annotation job to the job queue, and the workers (the consumer) will take these jobs, run inference, and return the result.
I am building this queue myself in Rust to learn more about Rust and this type of program. A simplified overview of the queue is that the producer can send the data to the queue over TCP. The consumer can retrieve the data from the queue by sending a request over TCP to the queue. This is the very basic concept. I am still working on how to notify the producer that the consumer finished and how to pass the results. You can follow the development of the project here TaxiWay. Once it's done I will write a blog post about it.
Front-end
As I am not a front-end developer or a big fan of it, I have kept things very simple, some would even call it old school. The front-end is nothing else than HTML, CSS, and a bit of JavaScript. Everything is kept simple, and I only use JS where I really need to. JS does not handle any business logic.
Deployment
As mentioned before, I am using FastAPI, MinIO, and PostgreSQL. So how do all of these things talk together, and how is PlaneView accessible from the internet? The setup is actually quite simple. Everything runs in a Docker container and communicates over the Docker network. The web app runs through Uvicorn on the Docker network. It is then made public through a reverse proxy by Caddy.
To make the site available 24/7 and not host it from my local network on a dedicated machine, the Docker container runs on a Hetzner VPS. If at some point the one instance of my Uvicorn web server should not be enough to handle traffic, which I doubt, I can scale up using docker compose scale.
Conclusion
In general I’m very happy with the architecture and overall setup of PlaneView There are naturally areas that could be improved, such as moving model inference out of the API endpoint, but the project has been a great learning process. Researching new approaches and correcting decisions I made earlier, when I had less experience, has taught me a lot. That’s ultimately the point of the project: learning by doing, iterating, and understanding both the good choices and the mistakes.
If I were starting PlaneView today, I wouldn’t build my own authentication and email-verification system again. There are reliable, well-tested solutions available, and using them frees up time but especially reduces risk, bugs and the possibility for attacks. Building my own taught me exactly why mature, proven systems are the better choice in real products, especially when they’re meant to support external users, companies or handle sensitive data.
Comments
Post a Comment