Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added services/backend/db.sqlite3
Binary file not shown.
Binary file added services/backend/db.sqlite3-shm
Binary file not shown.
Binary file added services/backend/db.sqlite3-wal
Binary file not shown.
2 changes: 1 addition & 1 deletion services/backend/src/auth/jwthandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from src.database.models import Users


SECRET_KEY = os.environ.get("SECRET_KEY")
SECRET_KEY = os.environ.get("SECRET_KEY", "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

Expand Down
23 changes: 20 additions & 3 deletions services/backend/src/crud/notes.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from fastapi import HTTPException
from tortoise.exceptions import DoesNotExist

from src.database.models import Notes
from src.database.models import Notes, Tags
from src.schemas.notes import NoteOutSchema
from src.schemas.token import Status


async def get_notes():
async def get_notes(tag_id: int = None):
if tag_id:
return await NoteOutSchema.from_queryset(Notes.filter(tags__id=tag_id))
return await NoteOutSchema.from_queryset(Notes.all())


Expand All @@ -17,7 +19,11 @@ async def get_note(note_id) -> NoteOutSchema:
async def create_note(note, current_user) -> NoteOutSchema:
note_dict = note.dict(exclude_unset=True)
note_dict["author_id"] = current_user.id
tag_ids = note_dict.pop("tags", [])
note_obj = await Notes.create(**note_dict)
if tag_ids:
tags = await Tags.filter(id__in=tag_ids)
await note_obj.tags.add(*tags)
return await NoteOutSchema.from_tortoise_orm(note_obj)


Expand All @@ -28,7 +34,18 @@ async def update_note(note_id, note, current_user) -> NoteOutSchema:
raise HTTPException(status_code=404, detail=f"Note {note_id} not found")

if db_note.author.id == current_user.id:
await Notes.filter(id=note_id).update(**note.dict(exclude_unset=True))
note_dict = note.dict(exclude_unset=True)
tag_ids = note_dict.pop("tags", None)
if note_dict:
await Notes.filter(id=note_id).update(**note_dict)

if tag_ids is not None:
note_obj = await Notes.get(id=note_id)
await note_obj.tags.clear()
if tag_ids:
tags = await Tags.filter(id__in=tag_ids)
await note_obj.tags.add(*tags)

return await NoteOutSchema.from_queryset_single(Notes.get(id=note_id))

raise HTTPException(status_code=403, detail=f"Not authorized to update")
Expand Down
42 changes: 42 additions & 0 deletions services/backend/src/crud/tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from fastapi import HTTPException
from tortoise.exceptions import DoesNotExist

from src.database.models import Tags
from src.schemas.tags import TagOutSchema
from src.schemas.token import Status


async def get_tags():
return await TagOutSchema.from_queryset(Tags.all())


async def get_tag(tag_id) -> TagOutSchema:
return await TagOutSchema.from_queryset_single(Tags.get(id=tag_id))


async def create_tag(tag) -> TagOutSchema:
tag_dict = tag.dict(exclude_unset=True)
tag_obj = await Tags.create(**tag_dict)
return await TagOutSchema.from_tortoise_orm(tag_obj)


async def update_tag(tag_id, tag) -> TagOutSchema:
try:
await TagOutSchema.from_queryset_single(Tags.get(id=tag_id))
except DoesNotExist:
raise HTTPException(status_code=404, detail=f"Tag {tag_id} not found")

await Tags.filter(id=tag_id).update(**tag.dict(exclude_unset=True))
return await TagOutSchema.from_queryset_single(Tags.get(id=tag_id))


async def delete_tag(tag_id) -> Status:
try:
await TagOutSchema.from_queryset_single(Tags.get(id=tag_id))
except DoesNotExist:
raise HTTPException(status_code=404, detail=f"Tag {tag_id} not found")

deleted_count = await Tags.filter(id=tag_id).delete()
if not deleted_count:
raise HTTPException(status_code=404, detail=f"Tag {tag_id} not found")
return Status(message=f"Deleted tag {tag_id}")
6 changes: 3 additions & 3 deletions services/backend/src/crud/users.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
from fastapi import HTTPException
from passlib.context import CryptContext
from src.auth.users import get_password_hash
from tortoise.exceptions import DoesNotExist, IntegrityError

from src.database.models import Users
from src.schemas.token import Status
from src.schemas.users import UserOutSchema


pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")



async def create_user(user) -> UserOutSchema:
user.password = pwd_context.encrypt(user.password)
user.password = get_password_hash(user.password)

try:
user_obj = await Users.create(**user.dict(exclude_unset=True))
Expand Down
2 changes: 1 addition & 1 deletion services/backend/src/database/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


TORTOISE_ORM = {
"connections": {"default": os.environ.get("DATABASE_URL")},
"connections": {"default": os.environ.get("DATABASE_URL", "sqlite://./db.sqlite3")},
"apps": {
"models": {
"models": [
Expand Down
11 changes: 11 additions & 0 deletions services/backend/src/database/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,22 @@ class Users(models.Model):
modified_at = fields.DatetimeField(auto_now=True)


class Tags(models.Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=50, unique=True)
created_at = fields.DatetimeField(auto_now_add=True)
modified_at = fields.DatetimeField(auto_now=True)

def __str__(self):
return self.name


class Notes(models.Model):
id = fields.IntField(pk=True)
title = fields.CharField(max_length=225)
content = fields.TextField()
author = fields.ForeignKeyField("models.Users", related_name="note")
tags = fields.ManyToManyField("models.Tags", related_name="notes")
created_at = fields.DatetimeField(auto_now_add=True)
modified_at = fields.DatetimeField(auto_now=True)

Expand Down
5 changes: 3 additions & 2 deletions services/backend/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
why?
https://stackoverflow.com/questions/65531387/tortoise-orm-for-python-no-returns-relations-of-entities-pyndantic-fastapi
"""
from src.routes import users, notes
from src.routes import users, notes, tags

app = FastAPI()

Expand All @@ -27,8 +27,9 @@
)
app.include_router(users.router)
app.include_router(notes.router)
app.include_router(tags.router)

register_tortoise(app, config=TORTOISE_ORM, generate_schemas=False)
register_tortoise(app, config=TORTOISE_ORM, generate_schemas=True)


@app.get("/")
Expand Down
12 changes: 6 additions & 6 deletions services/backend/src/routes/notes.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from typing import List
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from tortoise.contrib.fastapi import HTTPNotFoundError
from tortoise.exceptions import DoesNotExist

import src.crud.notes as crud
from src.auth.jwthandler import get_current_user
from src.schemas.notes import NoteOutSchema, NoteInSchema, UpdateNote
from src.schemas.notes import NoteOutSchema, NoteInSchema, NoteCreate, UpdateNote
from src.schemas.token import Status
from src.schemas.users import UserOutSchema

Expand All @@ -19,8 +19,8 @@
response_model=List[NoteOutSchema],
dependencies=[Depends(get_current_user)],
)
async def get_notes():
return await crud.get_notes()
async def get_notes(tag_id: Optional[int] = Query(None, description="Filter notes by tag ID")):
return await crud.get_notes(tag_id)


@router.get(
Expand All @@ -42,7 +42,7 @@ async def get_note(note_id: int) -> NoteOutSchema:
"/notes", response_model=NoteOutSchema, dependencies=[Depends(get_current_user)]
)
async def create_note(
note: NoteInSchema, current_user: UserOutSchema = Depends(get_current_user)
note: NoteCreate, current_user: UserOutSchema = Depends(get_current_user)
) -> NoteOutSchema:
return await crud.create_note(note, current_user)

Expand Down
73 changes: 73 additions & 0 deletions services/backend/src/routes/tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from typing import List

from fastapi import APIRouter, Depends, HTTPException
from tortoise.contrib.fastapi import HTTPNotFoundError
from tortoise.exceptions import DoesNotExist

import src.crud.tags as crud
from src.auth.jwthandler import get_current_user
from src.schemas.tags import TagOutSchema, TagInSchema, UpdateTag
from src.schemas.token import Status
from src.schemas.users import UserOutSchema


router = APIRouter()


@router.get(
"/tags",
response_model=List[TagOutSchema],
dependencies=[Depends(get_current_user)],
)
async def get_tags():
return await crud.get_tags()


@router.get(
"/tag/{tag_id}",
response_model=TagOutSchema,
dependencies=[Depends(get_current_user)],
)
async def get_tag(tag_id: int) -> TagOutSchema:
try:
return await crud.get_tag(tag_id)
except DoesNotExist:
raise HTTPException(
status_code=404,
detail="Tag does not exist",
)


@router.post(
"/tags", response_model=TagOutSchema, dependencies=[Depends(get_current_user)]
)
async def create_tag(
tag: TagInSchema, current_user: UserOutSchema = Depends(get_current_user)
) -> TagOutSchema:
return await crud.create_tag(tag)


@router.patch(
"/tag/{tag_id}",
dependencies=[Depends(get_current_user)],
response_model=TagOutSchema,
responses={404: {"model": HTTPNotFoundError}},
)
async def update_tag(
tag_id: int,
tag: UpdateTag,
current_user: UserOutSchema = Depends(get_current_user),
) -> TagOutSchema:
return await crud.update_tag(tag_id, tag)


@router.delete(
"/tag/{tag_id}",
response_model=Status,
responses={404: {"model": HTTPNotFoundError}},
dependencies=[Depends(get_current_user)],
)
async def delete_tag(
tag_id: int, current_user: UserOutSchema = Depends(get_current_user)
):
return await crud.delete_tag(tag_id)
9 changes: 8 additions & 1 deletion services/backend/src/schemas/notes.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional
from typing import Optional, List

from pydantic import BaseModel
from tortoise.contrib.pydantic import pydantic_model_creator
Expand All @@ -15,6 +15,13 @@
)


class NoteCreate(BaseModel):
title: str
content: str
tags: Optional[List[int]] = []


class UpdateNote(BaseModel):
title: Optional[str]
content: Optional[str]
tags: Optional[List[int]]
19 changes: 19 additions & 0 deletions services/backend/src/schemas/tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import Optional, List

from pydantic import BaseModel
from tortoise.contrib.pydantic import pydantic_model_creator

from src.database.models import Tags


TagInSchema = pydantic_model_creator(
Tags, name="TagIn", exclude_readonly=True)
TagOutSchema = pydantic_model_creator(
Tags, name="Tag", exclude =[
"modified_at", "created_at"
]
)


class UpdateTag(BaseModel):
name: Optional[str]
4 changes: 3 additions & 1 deletion services/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
"rules": {
"vue/multi-word-component-names": "off"
}
},
"browserslist": [
"> 1%",
Expand Down
Loading