Skip to content

Commit

Permalink
Merge branch 'main' into foldseek-add
Browse files Browse the repository at this point in the history
  • Loading branch information
xnought committed Feb 28, 2024
2 parents 5f4ca0e + 65090a3 commit dc82434
Show file tree
Hide file tree
Showing 20 changed files with 347 additions and 470 deletions.
125 changes: 0 additions & 125 deletions DEVELOPMENT.md

This file was deleted.

88 changes: 77 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,92 @@
<img width="100%" alt="banner" src="https://github.com/xnought/venome/assets/65095341/9abaef4e-b4ac-488f-b190-86fca1a1c940">
<img src="./docs/assets/logo-v3.svg" alt="venome title" />


![Github Actions CI tests](https://github.com/xnought/venome/actions/workflows/ci.yml/badge.svg)

A wikipedia 📖 for venom proteins: upload, search, organize, visualize, and download protein files all open source.
A wikipedia for venom proteins: upload, search, organize, visualize, and download protein files all open source.

🤝 In collaboration with the 🧪[Venom Biochemistry & Molecular Biology Laboratory](https://venombiochemistrylab.weebly.com/) at Oregon State University 🦫.
In collaboration with the [Venom Biochemistry & Molecular Biology Laboratory](https://venombiochemistrylab.weebly.com/) at Oregon State University 🦫.

> [!IMPORTANT]
> - [`DEVELOPMENT.md`](./DEVELOPMENT.md) contains the developer getting started docs
> - [`docs/`](./docs/) contains code documentation
> - [Getting Started](#getting-started) to quickly run the docker container locally
> - [`docs/`](./docs/) contains deeper code documentation
> - [`frontend/`](./frontend/) contains the user interface in Svelte/TypeScript
> - [`backend/`](./backend/) contains the backend HTTP server and Database
**`v0.0.0` demo / current progress**
## Getting Started

If you want to run the frontend and backend yourself, you can! Keep reading...

By the time you finish this guide, you will be able to run the JS frontend [Svelte](https://kit.svelte.dev/) and the Python backend [FastAPI](https://fastapi.tiangolo.com/).

Note that we use `docker-compose` as defined in the [`docker-compose.yml`](./docker-compose.yml).

If you want to see more specific docs go to the [ `docs/` ](./docs/README.md) for more info.

> [!IMPORTANT]
> You must have [Docker Desktop](https://www.docker.com/products/docker-desktop/) GUI installed and the `docker-compose` bash command.
You can run everything by doing

```bash
sh run.sh
```

or directly executing

```bash
./run.sh
```

this will download a docker container running the svelte frontend, the python backend, and the postgres database all in development hot-reload mode. Installation may take a few minutes.

Now navigate to [http://localhost:5173](http://localhost:5173) to see the frontend.

> [!TIP]
> Check the [`docs/run.md`](./docs/run.md) for documentation on the [`run.sh`](./run.sh) file for more build commands
### Local Development Environment

We use VSCode for all development and make sure to download a few extensions like

- Ruff code formatter (for backend)
- Prettier code formatter (for frontend)
- Svelte (for frontend)
- Python (for backend)

for a better experience. Then to get autocomplete with those languages, you'll need to manually install the packages that have already been installed in the docker, but locally. Otherwise your VSCode won't know how to autocomplete.

First install the [Poetry Python Package Manager](https://python-poetry.org/) by doing

```bash
pip3 install poetry
```

Then install the packages listed under [`pyproject.toml`](./backend/pyproject.toml) by doing

```bash
poetry config virtualenvs.in-project true # required for the .venv to get created
poetry install
```

Then install frontend packages

```bash
cd frontend
yarn
```

### Where to look

I'd recommend going to the [`frontend/package.json`](./frontend/package.json) and [`frontend/src/App.svelte`](./frontend/src/App.svelte) if you want to get started in the frontend.

I'd recommend going to the [`backend/src/server.py`](./backend/src/server.py) and [`backend/init.sql`](./backend/init.sql) for the backend/db.


<a href="https://www.youtube.com/watch?v=g6Klv4NIvH4">
<img width="363" alt="thumbnail" src="https://github.com/xnought/venome/assets/65095341/63ff81a4-aee8-4389-8fba-ea6c5e37aa2f">
</a>

## Quick start
> [!TIP]
> If you have more questions, create an issue on this github or look for specific documentation in the [`docs/`](./docs/) directory
Go to [`DEVELOPMENT.md`](./DEVELOPMENT.md) to get up and running.

## Resources

Expand Down
14 changes: 9 additions & 5 deletions backend/src/api/protein.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
from ..db import Database, bytea_to_str, str_to_bytea

from ..api_types import ProteinEntry, UploadBody, UploadError, EditBody, CamelModel
from ..auth import requiresAuthentication
from io import BytesIO
from fastapi import APIRouter
from fastapi.responses import FileResponse, StreamingResponse
from fastapi.requests import Request

router = APIRouter()

Expand Down Expand Up @@ -186,7 +188,8 @@ def get_protein_entry(protein_name: str):

# TODO: add permissions so only the creator can delete not just anyone
@router.delete("/protein/entry/{protein_name:str}", response_model=None)
def delete_protein_entry(protein_name: str):
def delete_protein_entry(protein_name: str, req: Request):
requiresAuthentication(req)
# Todo, have a meaningful error if the delete fails
with Database() as db:
# remove protein
Expand Down Expand Up @@ -214,9 +217,10 @@ def upload_protein_png(body: UploadPNGBody):

# None return means success
@router.post("/protein/upload", response_model=UploadError | None)
def upload_protein_entry(body: UploadBody):
body.name = format_protein_name(body.name)
def upload_protein_entry(body: UploadBody, req: Request):
requiresAuthentication(req)

body.name = format_protein_name(body.name)
# check that the name is not already taken in the DB
if protein_name_found(body.name):
return UploadError.NAME_NOT_UNIQUE
Expand Down Expand Up @@ -271,10 +275,10 @@ def upload_protein_entry(body: UploadBody):

# TODO: add more edits, now only does name and content edits
@router.put("/protein/edit", response_model=UploadError | None)
def edit_protein_entry(body: EditBody):
def edit_protein_entry(body: EditBody, req: Request):
# check that the name is not already taken in the DB
# TODO: check if permission so we don't have people overriding other people's names

requiresAuthentication(req)
try:
if body.new_name != body.old_name:
os.rename(
Expand Down
1 change: 1 addition & 0 deletions backend/src/api/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def login(body: LoginBody):

# Generates the token and returns
token = generateAuthToken(email, admin)
log.warn("Giving token:", token)
return LoginResponse(token=token, error="")

except Exception as e:
Expand Down
24 changes: 21 additions & 3 deletions backend/src/auth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import jwt
from datetime import datetime, timezone, timedelta
from fastapi.requests import Request
from fastapi import HTTPException
import logging as log

# TODO: This method of secret key generation is, obviously, extremely unsafe.
# This needs to be changed.
Expand All @@ -15,13 +18,28 @@ def generateAuthToken(userId, admin):
return jwt.encode(payload, secret_key, algorithm="HS256")


# TODO: Find out how FastAPI handles middleware functions, and turn this into one.
def authenticateToken(token):
# Return the decoded token if it's valid.
try:
decoded = jwt.decode(token, secret_key, algorithms="HS256")
# Valid token is always is in the form "Bearer [token]", so we need to slice off the "Bearer" portion.
sliced_token = token[7:]
log.warn(sliced_token)
decoded = jwt.decode(sliced_token, secret_key, algorithms="HS256")
log.warn("Valid token")
log.warn(decoded)
return decoded

# If the token is invalid, return None.
except Exception:
except Exception as err:
log.error("Invalid token:", err)
return None


# Use this function with a request if you want.
def requiresAuthentication(req: Request):
userInfo = authenticateToken(req.headers["authorization"])
if not userInfo or not userInfo.get("admin"):
log.error("Unauthorized User")
raise HTTPException(status_code=403, detail="Unauthorized")
else:
log.warn("User authorized.")
7 changes: 0 additions & 7 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
# Docs

Below are all the documentation for how to do things in this codebase. The only documentation not in this dir. is the [`DEVELOPMENT.md`](../DEVELOPMENT.md)

**Table of Contents**

- [`database.md`](./database.md)
- [`testing.md`](./testing.md)
- [`frontend.md`](./frontend.md)
- [`backend.md`](./backend.md)
- [`run.md`](./run.md)
- [`docker.md`](./docker.md)
Loading

0 comments on commit dc82434

Please sign in to comment.