Skip to content
2 changes: 1 addition & 1 deletion backend/app/data_access/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from sqlalchemy import select, insert
from fastapi import Depends
from schemas.user import UserInDB
from models.users import User
from models import User


class UserDataAccess:
Expand Down
6 changes: 3 additions & 3 deletions backend/app/dependencies/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
from fastapi.security import OAuth2PasswordBearer
from typing import Annotated
from services.auth import AuthServiceDep
from schemas.user import User
from schemas.user import UserInDB

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token")


async def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)],
auth_service: AuthServiceDep,
) -> User:
) -> UserInDB:
token_data = auth_service.decode_token(token)
return await auth_service.get_user_by_username(token_data.username)


AuthUserDep = Annotated[User, Depends(get_current_user)]
AuthUserDep = Annotated[UserInDB, Depends(get_current_user)]
1 change: 1 addition & 0 deletions backend/app/dependencies/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ async def get_db() -> AsyncSession:
except Exception as e:
await session.rollback()
raise e
await session.commit()
Copy link

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Committing in the DB dependency will commit after all operations, including GET requests. Consider committing only in write operations or in service methods.

Suggested change
await session.commit()

Copilot uses AI. Check for mistakes.


DBSessionDep = Annotated[AsyncSession, Depends(get_db)]
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ def upgrade() -> None:
name="user_role_enum",
)
user_role.create(op.get_bind(), checkfirst=True)
op.add_column("users", sa.Column("level", sa.Integer(), nullable=False))
op.add_column("users", sa.Column("level", sa.Integer(), nullable=True))
op.add_column(
"users",
sa.Column(
"role",
user_role,
nullable=False,
nullable=True,
),
)
# ### end Alembic commands ###
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""change date to datetime format

Revision ID: 6e2af57b2c75
Revises: 843428e10d39
Create Date: 2025-07-09 16:56:58.620295

"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = "6e2af57b2c75"
down_revision: Union[str, Sequence[str], None] = "843428e10d39"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column(
"applications",
"created_at",
existing_type=sa.DATE(),
type_=sa.DateTime(),
existing_nullable=True,
)
op.alter_column(
"project_members",
"created_at",
existing_type=sa.DATE(),
type_=sa.DateTime(),
existing_nullable=True,
)
op.alter_column(
"projects",
"created_at",
existing_type=sa.DATE(),
type_=sa.DateTime(),
existing_nullable=True,
)
op.alter_column(
"projects", "is_public", existing_type=sa.BOOLEAN(), nullable=True
)
op.alter_column(
"projects", "ceo_id", existing_type=sa.INTEGER(), nullable=True
)
op.alter_column(
"projects", "is_opensource", existing_type=sa.BOOLEAN(), nullable=True
)
op.alter_column(
"projects", "is_dead", existing_type=sa.BOOLEAN(), nullable=True
)
# ### end Alembic commands ###


def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column(
"projects", "is_dead", existing_type=sa.BOOLEAN(), nullable=False
)
op.alter_column(
"projects", "is_opensource", existing_type=sa.BOOLEAN(), nullable=False
)
op.alter_column(
"projects", "ceo_id", existing_type=sa.INTEGER(), nullable=False
)
op.alter_column(
"projects", "is_public", existing_type=sa.BOOLEAN(), nullable=False
)
op.alter_column(
"projects",
"created_at",
existing_type=sa.DateTime(),
type_=sa.DATE(),
existing_nullable=True,
)
op.alter_column(
"project_members",
"created_at",
existing_type=sa.DateTime(),
type_=sa.DATE(),
existing_nullable=True,
)
op.alter_column(
"applications",
"created_at",
existing_type=sa.DateTime(),
type_=sa.DATE(),
existing_nullable=True,
)
# ### end Alembic commands ###
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""add applications, project_member, edit projects

Revision ID: 843428e10d39
Revises: 9486dd2901de
Create Date: 2025-07-09 13:50:32.127176

"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision: str = "843428e10d39"
down_revision: Union[str, Sequence[str], None] = "9486dd2901de"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"applications",
sa.Column("project_id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=False),
sa.Column("created_at", sa.Date(), nullable=True),
sa.Column("is_approved", sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(
["project_id"],
["projects.id"],
),
sa.ForeignKeyConstraint(
["user_id"],
["users.id"],
),
sa.PrimaryKeyConstraint("project_id", "user_id"),
)
op.create_index(
"ix_applications_project_user",
"applications",
["project_id", "user_id"],
unique=True,
)
op.create_table(
"project_members",
sa.Column("project_id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=False),
sa.Column("created_at", sa.Date(), nullable=True),
sa.ForeignKeyConstraint(
["project_id"],
["projects.id"],
),
sa.ForeignKeyConstraint(
["user_id"],
["users.id"],
),
sa.PrimaryKeyConstraint("project_id", "user_id"),
)
op.create_index(
"ix_project_members_project_id_user_id",
"project_members",
["project_id", "user_id"],
unique=False,
)
op.add_column("projects", sa.Column("ceo_id", sa.Integer(), nullable=True))
op.add_column("projects", sa.Column("is_opensource", sa.Boolean(), nullable=True))
op.add_column("projects", sa.Column("is_dead", sa.Boolean(), nullable=True))
op.create_foreign_key(None, "projects", "users", ["ceo_id"], ["id"])
# ### end Alembic commands ###


def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "projects", type_="foreignkey")
op.drop_column("projects", "is_dead")
op.drop_column("projects", "is_opensource")
op.drop_column("projects", "ceo_id")
op.drop_index("ix_project_members_project_id_user_id", table_name="project_members")
op.drop_table("project_members")
op.drop_index("ix_applications_project_user", table_name="applications")
op.drop_table("applications")
# ### end Alembic commands ###
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"users",
sa.Column("level", sa.INTEGER(), autoincrement=False, nullable=False),
sa.Column("level", sa.INTEGER(), autoincrement=False, nullable=True),
)
# ### end Alembic commands ###
2 changes: 1 addition & 1 deletion backend/app/migrations/versions/d48f4d45faf0_.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"projects",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("id", sa.Integer(), nullable=False, autoincrement=True),
sa.Column("title", sa.String(), nullable=False),
sa.Column("description", sa.String(), nullable=True),
sa.Column("created_at", sa.Date(), nullable=True),
Expand Down
4 changes: 4 additions & 0 deletions backend/app/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from .projects import Project
from .users import User
from .project_member import ProjectMember
from .applications import Application

__all__ = [
"Project",
"User",
"ProjectMember",
"Application",
]
25 changes: 25 additions & 0 deletions backend/app/models/applications.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from sqlalchemy import (
Column,
Integer,
Index,
DateTime,
func,
Boolean,
ForeignKey,
PrimaryKeyConstraint,
)
from dependencies.database import base


class Application(base):
__tablename__ = "applications"

project_id = Column(Integer, ForeignKey("projects.id"))
user_id = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime, default=func.current_timestamp())
is_approved = Column(Boolean, nullable=True)

__table_args__ = (
Index("ix_applications_project_user", "project_id", "user_id", unique=True),
PrimaryKeyConstraint("project_id", "user_id"),
)
23 changes: 23 additions & 0 deletions backend/app/models/project_member.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from sqlalchemy import (
Column,
Integer,
Index,
DateTime,
func,
ForeignKey,
PrimaryKeyConstraint,
)
from dependencies.database import base


class ProjectMember(base):
__tablename__ = "project_members"

project_id = Column(Integer, ForeignKey("projects.id"), nullable=False)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
created_at = Column(DateTime, default=func.current_timestamp())

__table_args__ = (
PrimaryKeyConstraint("project_id", "user_id"),
Index("ix_project_members_project_id_user_id", "project_id", "user_id"),
)
14 changes: 10 additions & 4 deletions backend/app/models/projects.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
from sqlalchemy import Column, Integer, String, Date, func, Boolean
from sqlalchemy import Column, Integer, String, DateTime, func, Boolean, ForeignKey
from dependencies.database import base


class Project(base):
__tablename__ = "projects"

id = Column(Integer, primary_key=True, index=True)
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
title = Column(String, unique=True, nullable=False)
description = Column(String)
created_at = Column(Date, default=func.current_date())
is_public = Column(Boolean, nullable=False)
created_at = Column(DateTime, default=func.current_timestamp())
is_public = Column(Boolean, nullable=True, default=True)
status = Column(String)
ceo_id = Column(Integer, ForeignKey("users.id"), nullable=True)
is_opensource = Column(Boolean, nullable=True, default=True)
is_dead = Column(Boolean, nullable=True, default=False)

def __repr__(self):
return f"<Project({', '.join(f'{k}={getattr(self, k)!r}' for k in self.__table__.columns.keys())})>"
2 changes: 1 addition & 1 deletion backend/app/models/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ class User(base):
hashed_password = Column(String, nullable=False)
role = Column(
PgEnum(UserRole, name="user_role_enum", create_type=True),
nullable=False,
nullable=True,
default=UserRole.VIEWER,
)
18 changes: 16 additions & 2 deletions backend/app/routes/api/project/__tests__/test_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,24 @@ def mock_user():
def sample_projects():
return [
ProjectSchema(
id=1, title="Project 1", description="Description 1", is_public=True
id=1,
title="Project 1",
description="Description 1",
is_public=True,
is_dead=False,
is_opensource=True,
ceo_id=1,
created_at="2023-10-01T00:00:00Z",
),
ProjectSchema(
id=2, title="Project 2", description="Description 2", is_public=False
id=2,
title="Project 2",
description="Description 2",
is_public=True,
is_dead=False,
is_opensource=True,
ceo_id=1,
created_at="2023-10-01T00:00:00Z",
),
]

Expand Down
Loading
Loading