diff --git a/server/src/app/__pycache__/config.cpython-311.pyc b/server/src/app/__pycache__/config.cpython-311.pyc index eed296b..d2c5931 100644 Binary files a/server/src/app/__pycache__/config.cpython-311.pyc and b/server/src/app/__pycache__/config.cpython-311.pyc differ diff --git a/server/src/app/api/v1/__pycache__/auth.cpython-311.pyc b/server/src/app/api/v1/__pycache__/auth.cpython-311.pyc index b8dfa3e..ae43a0d 100644 Binary files a/server/src/app/api/v1/__pycache__/auth.cpython-311.pyc and b/server/src/app/api/v1/__pycache__/auth.cpython-311.pyc differ diff --git a/server/src/app/api/v1/__pycache__/knowledges.cpython-311.pyc b/server/src/app/api/v1/__pycache__/knowledges.cpython-311.pyc index d9d22c3..5d042c4 100644 Binary files a/server/src/app/api/v1/__pycache__/knowledges.cpython-311.pyc and b/server/src/app/api/v1/__pycache__/knowledges.cpython-311.pyc differ diff --git a/server/src/app/api/v1/__pycache__/metrics.cpython-311.pyc b/server/src/app/api/v1/__pycache__/metrics.cpython-311.pyc index f4a966e..06ec38f 100644 Binary files a/server/src/app/api/v1/__pycache__/metrics.cpython-311.pyc and b/server/src/app/api/v1/__pycache__/metrics.cpython-311.pyc differ diff --git a/server/src/app/api/v1/auth.py b/server/src/app/api/v1/auth.py index f111dc8..4db286e 100644 --- a/server/src/app/api/v1/auth.py +++ b/server/src/app/api/v1/auth.py @@ -1,18 +1,18 @@ from typing import Annotated from fastapi import Depends, APIRouter, HTTPException, status -from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer +from fastapi.security import OAuth2PasswordRequestForm -from src.app.models.user import User -from src.app.data.user import create_user +from src.app.models.user import User, UserCreate +from src.app.data.user import create_user, get_user_by_username from src.app.auth.dependancies import get_current_user, authenticate_user from src.app.auth.security import hash_password, create_access_token from src.app.auth.schemas import Token -router = APIRouter(tags=["users"]) +router = APIRouter(prefix="/auth", tags=["auth"]) -@router.post("/token") +@router.post("/login") async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]) -> Token: user = authenticate_user(form_data.username, form_data.password) if not user: @@ -24,15 +24,19 @@ async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]) -> T access_token = create_access_token(data={"sub": user.username}) return Token(access_token=access_token, token_type="bearer") -oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/v1/token") - -@router.get("/user") +@router.get("/me") async def user(current_user: Annotated[str, Depends(get_current_user)]): return current_user -@router.post("/user") -async def create(username, password): - hashed_password = await hash_password(password) - user = User(username = username, hashed_password = hashed_password) +@router.post("/register") +async def create(user_data: UserCreate): + if get_user_by_username(user_data.username): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Username already registered" + ) + + hashed_password = hash_password(user_data.plain_password) + user = User(username = user_data.username, hashed_password = hashed_password) created_user = create_user(user) return created_user \ No newline at end of file diff --git a/server/src/app/api/v1/knowledges.py b/server/src/app/api/v1/knowledges.py index 4efbe11..3ba5824 100644 --- a/server/src/app/api/v1/knowledges.py +++ b/server/src/app/api/v1/knowledges.py @@ -1,48 +1,47 @@ from typing import Annotated -from fastapi import APIRouter +from fastapi import APIRouter, Depends, HTTPException, status -from src.app.models.knowledge import Knowledge +from src.app.auth.dependancies import get_current_user + +from src.app.models.knowledge import Knowledge, KnowledgeCreate from src.app.models.question import Question -from src.app.data.knowledge import create_knowledge, read_knowledges, read_knowledge, update_knowledge, delete_knowledge -from src.app.data.question import read_questions as read_questions_crud, create_question +from src.app.data.knowledge import create_knowledge, get_knowledges_by_user, get_knowledge_by_id +#from src.app.data.knowledge update_knowledge, delete_knowledge +from src.app.data.question import get_questions, create_question from src.app.services.language_generation import questions_generation #Added in __ini__ router = APIRouter(tags=["knowledges"]) +@router.get("/knowledges/") +def read(current_user: Annotated[str, Depends(get_current_user)]): + knowledges = get_knowledges_by_user(current_user) + return knowledges + +@router.get("/knowledges/{id}") +def read(id: int, current_user: Annotated[str, Depends(get_current_user)]): + knowledge = get_knowledge_by_id(id, current_user) + return knowledge + @router.post("/knowledges/") -def create(knowledge: Knowledge): +def create(knowledge_data: KnowledgeCreate, current_user: Annotated[str, Depends(get_current_user)]): + knowledge = Knowledge(content=knowledge_data.content, uri=knowledge_data.uri, user=current_user) created_knowledge = create_knowledge(knowledge) # if created_knowledge is None: # raise NotFoundException("Failed to create knowledge") return created_knowledge -@router.get("/knowledges/") -def read(): - knowledges = read_knowledges() - return knowledges - -@router.get("/knowledges/{id}") -def read(id: int): - knowledge = read_knowledge(id) - return knowledge - -#TODO: adapt with correct pattern -@router.post("/knowledges/{id}") -def update(id: int, content: str, uri: str): - knowledge = update_knowledge(id, content, uri) - return knowledge - -@router.delete("/knowledges/{id}") -def delete(id: int): - knowledge = delete_knowledge(id) - return knowledge - @router.post("/knowledges/{id}/questions") -def create_questions(id: int): - knowledge = read_knowledge(id) +def generate_questions(id: int, current_user: Annotated[str, Depends(get_current_user)]): + knowledge: Knowledge = get_knowledge_by_id(id, current_user) + if not knowledge: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Forbidden. The requested knowledge is not available for the provided ID.", + headers={"WWW-Authenticate": "Bearer"}, + ) questions_raw = questions_generation(knowledge) for q in questions_raw: question = Question(question = q, knowledge=knowledge) @@ -50,9 +49,24 @@ def create_questions(id: int): return questions_raw @router.get("/knowledges/{id}/questions") -def read_questions(id: int): - knowledge: Knowledge = read_knowledge(id) - #questions = knowledge.questions - #TODO : refacto ? - questions = read_questions_crud(knowledge) - return questions \ No newline at end of file +def read_questions(id: int, current_user: Annotated[str, Depends(get_current_user)]): + knowledge: Knowledge = get_knowledge_by_id(id, current_user) + if not knowledge: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Forbidden. The requested knowledge is not available for the provided ID.", + headers={"WWW-Authenticate": "Bearer"}, + ) + questions = get_questions(knowledge) + return questions + +#TODO: adapt with correct pattern +# @router.post("/knowledges/{id}") +# def update(id: int, content: str, uri: str): +# knowledge = update_knowledge(id, content, uri) +# return knowledge + +# @router.delete("/knowledges/{id}") +# def delete(id: int): +# knowledge = delete_knowledge(id) +# return knowledge \ No newline at end of file diff --git a/server/src/app/api/v1/metrics.py b/server/src/app/api/v1/metrics.py index e1127b7..de06eee 100644 --- a/server/src/app/api/v1/metrics.py +++ b/server/src/app/api/v1/metrics.py @@ -1,11 +1,15 @@ -from fastapi import APIRouter +from typing import Annotated +from fastapi import APIRouter, Depends -from src.app.models.metric import Metric +from src.app.models.metric import Metric, MetricCreate from src.app.data.metric import create_metric +from src.app.auth.dependancies import get_current_user + router = APIRouter(tags=["metrics"]) @router.post("/metrics/") -def create(metric: Metric): +def create(metric_data: MetricCreate, current_user: Annotated[str, Depends(get_current_user)]): + metric: Metric = Metric(question_id = metric_data.question_id, need_index = metric_data.need_index, user = current_user) created_metric: Metric = create_metric(metric) return created_metric diff --git a/server/src/app/auth/__pycache__/dependancies.cpython-311.pyc b/server/src/app/auth/__pycache__/dependancies.cpython-311.pyc index 369a640..cab1575 100644 Binary files a/server/src/app/auth/__pycache__/dependancies.cpython-311.pyc and b/server/src/app/auth/__pycache__/dependancies.cpython-311.pyc differ diff --git a/server/src/app/auth/__pycache__/security.cpython-311.pyc b/server/src/app/auth/__pycache__/security.cpython-311.pyc index 9e77eeb..a2b187c 100644 Binary files a/server/src/app/auth/__pycache__/security.cpython-311.pyc and b/server/src/app/auth/__pycache__/security.cpython-311.pyc differ diff --git a/server/src/app/auth/dependancies.py b/server/src/app/auth/dependancies.py index 7affdb4..3761dff 100644 --- a/server/src/app/auth/dependancies.py +++ b/server/src/app/auth/dependancies.py @@ -1,23 +1,19 @@ -from src.app.config import settings - from typing import Annotated from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from argon2 import PasswordHasher from argon2 import PasswordHasher -import jwt -from jwt.exceptions import InvalidTokenError from src.app.models.user import User -from src.app.data.user import get_user +from src.app.data.user import get_user_by_username from .schemas import TokenData -from .security import verify_password +from .security import verify_password, verify_token -oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/token") +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login") password_hasher = PasswordHasher() def authenticate_user(username: str, password: str): - user: User = get_user(username) + user: User = get_user_by_username(username) if not user: # Add timing to prevent attack password_hasher.hash(password) @@ -32,17 +28,19 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]) -> Use detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) - try: - payload = jwt.decode(token, settings.SECRET_KEY, algorithm=settings.ALGORITHM) - username = payload.get("sub") - if username is None: - raise credentials_exception - token_data = TokenData(username=username) - except InvalidTokenError: + payload = verify_token(token, token_type="access") + if payload is None: raise credentials_exception - user = get_user(username=token_data.username) + + username = payload.get("sub") + if username is None: + raise credentials_exception + token_data = TokenData(username=username) + + user = get_user_by_username(username=token_data.username) if user is None: raise credentials_exception + return user diff --git a/server/src/app/auth/security.py b/server/src/app/auth/security.py index f180f94..5cc3c27 100644 --- a/server/src/app/auth/security.py +++ b/server/src/app/auth/security.py @@ -1,4 +1,5 @@ from src.app.config import settings +from typing import Optional from datetime import timedelta, datetime, timezone from argon2 import PasswordHasher from argon2.exceptions import ( @@ -10,6 +11,7 @@ import jwt from jwt.exceptions import InvalidTokenError + password_hasher = PasswordHasher() def verify_password(plain_password: str, hashed_password: str) -> bool: @@ -28,6 +30,11 @@ def create_access_token(data: dict): encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM) return encoded_jwt -#def create_refresh_token(data: dict) -> str: +def verify_token(token: str, token_type: str = "access") -> Optional[dict]: + try: + payload = jwt.decode(jwt = token, key = settings.SECRET_KEY, algorithms = [settings.ALGORITHM]) + return payload + except InvalidTokenError: + return None -#def verify_token(token: str, token_type: str = "access") -> Optional[dict]: \ No newline at end of file +#def create_refresh_token(data: dict) -> str: \ No newline at end of file diff --git a/server/src/app/config.py b/server/src/app/config.py index 254a45a..9595cb1 100644 --- a/server/src/app/config.py +++ b/server/src/app/config.py @@ -12,7 +12,7 @@ class Settings(BaseSettings): # Security ORIGIN: str = Field('http://localhost:5173', env='ORIGIN') SECRET_KEY : str = Field('random_string', env='SECRET_KEY') - ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 + ACCESS_TOKEN_EXPIRE_MINUTES: int = 240 ALGORITHM: str = "HS256" class Config: diff --git a/server/src/app/data/__pycache__/knowledge.cpython-311.pyc b/server/src/app/data/__pycache__/knowledge.cpython-311.pyc index 0376b55..9f0aa66 100644 Binary files a/server/src/app/data/__pycache__/knowledge.cpython-311.pyc and b/server/src/app/data/__pycache__/knowledge.cpython-311.pyc differ diff --git a/server/src/app/data/__pycache__/metric.cpython-311.pyc b/server/src/app/data/__pycache__/metric.cpython-311.pyc index 10f169a..4af2578 100644 Binary files a/server/src/app/data/__pycache__/metric.cpython-311.pyc and b/server/src/app/data/__pycache__/metric.cpython-311.pyc differ diff --git a/server/src/app/data/__pycache__/question.cpython-311.pyc b/server/src/app/data/__pycache__/question.cpython-311.pyc index 4ca09b3..d424742 100644 Binary files a/server/src/app/data/__pycache__/question.cpython-311.pyc and b/server/src/app/data/__pycache__/question.cpython-311.pyc differ diff --git a/server/src/app/data/__pycache__/user.cpython-311.pyc b/server/src/app/data/__pycache__/user.cpython-311.pyc index d8f751b..4dcb68a 100644 Binary files a/server/src/app/data/__pycache__/user.cpython-311.pyc and b/server/src/app/data/__pycache__/user.cpython-311.pyc differ diff --git a/server/src/app/data/knowledge.py b/server/src/app/data/knowledge.py index e6182a9..5e3bfa7 100644 --- a/server/src/app/data/knowledge.py +++ b/server/src/app/data/knowledge.py @@ -1,9 +1,36 @@ -from fastapi import Depends from sqlmodel import Session, select from src.app.models.knowledge import Knowledge +from src.app.models.user import User from src.app.database import engine +def get_knowledge_by_id(knowledge_id: int, user: User): + with Session(engine) as session: + statement = select(Knowledge).where(Knowledge.id == knowledge_id, Knowledge.user_id == user.id) + results = session.exec(statement) + knowledge = results.first() + return knowledge + +# def get_knowledge_by_id(knowledge_id: int): +# with Session(engine) as session: +# knowledge = session.get(Knowledge, knowledge_id) +# return knowledge + +# No filter by user +def get_knowledges(): + with Session(engine) as session: + statement = select(Knowledge) + results = session.exec(statement) + knowledges = results.all() + return knowledges + +def get_knowledges_by_user(user: User): + with Session(engine) as session: + statement = select(Knowledge).where(Knowledge.user_id == user.id) + results = session.exec(statement) + knowledges = results.all() + return knowledges + def create_knowledge(knowledge: Knowledge): with Session(engine) as session: session.add(knowledge) @@ -11,32 +38,23 @@ def create_knowledge(knowledge: Knowledge): session.refresh(knowledge) return knowledge -def read_knowledges(): - with Session(engine) as session: - statement = select(Knowledge) - results = session.exec(statement) - knowledges = results.all() - return knowledges - -def read_knowledge(knowledge_id: int): - with Session(engine) as session: - knowledge = session.get(Knowledge, knowledge_id) - return knowledge - #TODO adapt logic with args -def update_knowledge(knowledge_id: int, content: str, uri: str): - with Session(engine) as session: - knowledge = session.get(Knowledge, knowledge_id) - knowledge.content = content if content else knowledge.content - knowledge.uri = uri if uri else knowledge.uri +# No filter by user +# Transform to update_knowledge(knowledge: Knowledge) +# def update_knowledge(knowledge_id: int, content: str, uri: str): +# with Session(engine) as session: +# knowledge = session.get(Knowledge, knowledge_id) +# knowledge.content = content if content else knowledge.content +# knowledge.uri = uri if uri else knowledge.uri - session.add(knowledge) - session.commit() - session.refresh(knowledge) - return knowledge +# session.add(knowledge) +# session.commit() +# session.refresh(knowledge) +# return knowledge -def delete_knowledge(knowledge_id: int): - with Session(engine) as session: - knowledge = session.get(Knowledge, knowledge_id) - session.delete(knowledge) - session.commit() \ No newline at end of file +# No filter by user +# def delete_knowledge(knowledge_id: int): +# with Session(engine) as session: +# knowledge = session.get(Knowledge, knowledge_id) +# session.delete(knowledge) +# session.commit() \ No newline at end of file diff --git a/server/src/app/data/metric.py b/server/src/app/data/metric.py index 93b6ff2..94bd61c 100644 --- a/server/src/app/data/metric.py +++ b/server/src/app/data/metric.py @@ -4,24 +4,24 @@ from src.app.models.metric import Metric from src.app.models.question import Question from src.app.database import engine +def get_metric_by_id(metric_id: int): + with Session(engine) as session: + metric = session.get(Metric, metric_id) + return metric + +def get_metrics(question): + with Session(engine) as session: + statement = select(Metric).where(Metric.question_id == question.id) + results = session.exec(statement) + metrics = results.all() + return metrics + def create_metric(metric: Metric): with Session(engine) as session: session.add(metric) session.commit() session.refresh(metric) return metric - -def read_metrics(question): - with Session(engine) as session: - statement = select(Metric).where(Metric.question_id == question.id) - results = session.exec(statement) - metrics = results.all() - return metrics - -def read_metric(metric_id: int): - with Session(engine) as session: - metric = session.get(Metric, metric_id) - return metric def delete_metric(metric_id: int): with Session(engine) as session: diff --git a/server/src/app/data/question.py b/server/src/app/data/question.py index f3c568e..382d12e 100644 --- a/server/src/app/data/question.py +++ b/server/src/app/data/question.py @@ -3,23 +3,23 @@ from sqlmodel import Session, select from src.app.models.question import Question from src.app.database import engine -def create_question(question: Question): +def get_question_by_id(question_id: int): with Session(engine) as session: - session.add(question) - session.commit() - session.refresh(question) + question = session.get(Question, question_id) return question - -def read_questions(knowledge): + +def get_questions(knowledge): with Session(engine) as session: statement = select(Question).where(Question.knowledge_id == knowledge.id) results = session.exec(statement) questions = results.all() return questions -def read_question(question_id: int): +def create_question(question: Question): with Session(engine) as session: - question = session.get(Question, question_id) + session.add(question) + session.commit() + session.refresh(question) return question def delete_question(question_id: int): diff --git a/server/src/app/data/user.py b/server/src/app/data/user.py index 028f6ce..ece553a 100644 --- a/server/src/app/data/user.py +++ b/server/src/app/data/user.py @@ -10,14 +10,14 @@ def create_user(user: User): session.refresh(user) return user -def get_user(username: str): +def get_user_by_username(username: str): with Session(engine) as session: statement = select(User).where(User.username == username) results = session.exec(statement) result = results.first() return result -def read_users(): +def get_users(): with Session(engine) as session: statement = select(User) results = session.exec(statement) diff --git a/server/src/app/models/__pycache__/knowledge.cpython-311.pyc b/server/src/app/models/__pycache__/knowledge.cpython-311.pyc index 5de219b..8558604 100644 Binary files a/server/src/app/models/__pycache__/knowledge.cpython-311.pyc and b/server/src/app/models/__pycache__/knowledge.cpython-311.pyc differ diff --git a/server/src/app/models/__pycache__/metric.cpython-311.pyc b/server/src/app/models/__pycache__/metric.cpython-311.pyc index 4557d95..5ec8f65 100644 Binary files a/server/src/app/models/__pycache__/metric.cpython-311.pyc and b/server/src/app/models/__pycache__/metric.cpython-311.pyc differ diff --git a/server/src/app/models/__pycache__/question.cpython-311.pyc b/server/src/app/models/__pycache__/question.cpython-311.pyc index b8b9649..18c887a 100644 Binary files a/server/src/app/models/__pycache__/question.cpython-311.pyc and b/server/src/app/models/__pycache__/question.cpython-311.pyc differ diff --git a/server/src/app/models/__pycache__/user.cpython-311.pyc b/server/src/app/models/__pycache__/user.cpython-311.pyc index 3fe7359..57af889 100644 Binary files a/server/src/app/models/__pycache__/user.cpython-311.pyc and b/server/src/app/models/__pycache__/user.cpython-311.pyc differ diff --git a/server/src/app/models/knowledge.py b/server/src/app/models/knowledge.py index 6e28b2c..68752ec 100644 --- a/server/src/app/models/knowledge.py +++ b/server/src/app/models/knowledge.py @@ -1,8 +1,17 @@ from sqlmodel import Field, SQLModel, Relationship +from pydantic import BaseModel +from src.app.models.user import User class Knowledge(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) content: str = Field(index=True) uri: str = Field(index=True) - questions: list["Question"] = Relationship(back_populates="knowledge", cascade_delete=True) # type: ignore \ No newline at end of file + questions: list["Question"] = Relationship(back_populates="knowledge", cascade_delete=True) # type: ignore + + user_id: int | None = Field(default=None, foreign_key="user.id", ondelete="CASCADE") + user: User | None = Relationship(back_populates="knowledges") + +class KnowledgeCreate(BaseModel): + content: str + uri:str \ No newline at end of file diff --git a/server/src/app/models/metric.py b/server/src/app/models/metric.py index 6671ffe..42ba569 100644 --- a/server/src/app/models/metric.py +++ b/server/src/app/models/metric.py @@ -1,5 +1,7 @@ from sqlmodel import Field, SQLModel, Relationship +from pydantic import BaseModel from src.app.models.question import Question +from src.app.models.user import User class Metric(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) @@ -8,3 +10,10 @@ class Metric(SQLModel, table=True): question: Question | None = Relationship(back_populates="metrics") need_index: int + + user_id: int | None = Field(default=None, foreign_key="user.id", ondelete="CASCADE") + user: User | None = Relationship(back_populates="metrics") + +class MetricCreate(BaseModel): + question_id: int + need_index: int \ No newline at end of file diff --git a/server/src/app/models/question.py b/server/src/app/models/question.py index 86b5aeb..6874925 100644 --- a/server/src/app/models/question.py +++ b/server/src/app/models/question.py @@ -1,5 +1,6 @@ from sqlmodel import Field, SQLModel, Relationship from src.app.models.knowledge import Knowledge +from src.app.models.user import User class Question(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) @@ -9,3 +10,6 @@ class Question(SQLModel, table=True): knowledge: Knowledge | None = Relationship(back_populates="questions") metrics: list["Metric"] = Relationship(back_populates="question", cascade_delete=True) # type: ignore + + user_id: int | None = Field(default=None, foreign_key="user.id", ondelete="CASCADE") + user: User | None = Relationship(back_populates="questions") diff --git a/server/src/app/models/user.py b/server/src/app/models/user.py index 48a4191..9d64ded 100644 --- a/server/src/app/models/user.py +++ b/server/src/app/models/user.py @@ -1,7 +1,16 @@ from sqlmodel import Field, SQLModel, Relationship +from pydantic import BaseModel class User(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) username: str hashed_password: str - #is_active: bool \ No newline at end of file + #is_active: bool + + knowledges: list["Knowledge"] = Relationship(back_populates="user", cascade_delete=True) # type: ignore + questions: list["Question"] = Relationship(back_populates="user", cascade_delete=True) # type: ignore + metrics: list["Metric"] = Relationship(back_populates="user", cascade_delete=True) # type: ignore + +class UserCreate(BaseModel): + username: str + plain_password: str \ No newline at end of file diff --git a/user-interface/package-lock.json b/user-interface/package-lock.json index 22f01aa..eef1b54 100644 --- a/user-interface/package-lock.json +++ b/user-interface/package-lock.json @@ -8,6 +8,7 @@ "name": "user-interface", "version": "0.0.0", "dependencies": { + "axios": "^1.13.6", "buefy": "^3.0.4", "pinia": "^3.0.4", "vue": "^3.5.27", @@ -1250,9 +1251,9 @@ } }, "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -3278,7 +3279,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, "license": "MIT" }, "node_modules/at-least-node": { @@ -3309,10 +3309,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", - "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", - "dev": true, + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", @@ -3324,7 +3323,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, "license": "MIT" }, "node_modules/balanced-match": { @@ -3564,7 +3562,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3798,7 +3795,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -4125,7 +4121,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -4135,7 +4130,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -4172,15 +4166,15 @@ } }, "node_modules/editorconfig": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", - "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz", + "integrity": "sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==", "dev": true, "license": "MIT", "dependencies": { "@one-ini/wasm": "0.1.1", "commander": "^10.0.0", - "minimatch": "9.0.1", + "minimatch": "^9.0.1", "semver": "^7.5.3" }, "bin": { @@ -4190,23 +4184,6 @@ "node": ">=14" } }, - "node_modules/editorconfig/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/editorconfig/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/editorconfig/node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -4217,22 +4194,6 @@ "node": ">=14" } }, - "node_modules/editorconfig/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/electron-to-chromium": { "version": "1.5.302", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", @@ -4298,7 +4259,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4308,7 +4268,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4325,7 +4284,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -4338,7 +4296,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4588,9 +4545,9 @@ } }, "node_modules/eslint/node_modules/minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -4965,7 +4922,6 @@ "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "dev": true, "funding": [ { "type": "individual", @@ -5026,7 +4982,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -5081,7 +5036,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5101,7 +5055,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -5126,7 +5079,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -5230,7 +5182,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5260,7 +5211,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5273,7 +5223,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -5306,7 +5255,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -6174,7 +6122,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6231,7 +6178,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -6241,7 +6187,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -6261,13 +6206,13 @@ } }, "node_modules/minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -6276,6 +6221,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimatch/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", diff --git a/user-interface/package.json b/user-interface/package.json index d9e2d5a..a8d7057 100644 --- a/user-interface/package.json +++ b/user-interface/package.json @@ -19,6 +19,7 @@ "format": "prettier --write --experimental-cli src/" }, "dependencies": { + "axios": "^1.13.6", "buefy": "^3.0.4", "pinia": "^3.0.4", "vue": "^3.5.27", diff --git a/user-interface/src/components/CollectKnowledge.vue b/user-interface/src/components/CollectKnowledge.vue index 7a359bc..95c0b3e 100644 --- a/user-interface/src/components/CollectKnowledge.vue +++ b/user-interface/src/components/CollectKnowledge.vue @@ -1,12 +1,15 @@ diff --git a/user-interface/src/components/GenerateQuestion.vue b/user-interface/src/components/GenerateQuestion.vue index e627605..ca74258 100644 --- a/user-interface/src/components/GenerateQuestion.vue +++ b/user-interface/src/components/GenerateQuestion.vue @@ -3,7 +3,7 @@ import { BProgress } from "buefy"; import type { Knowledge } from "@/types/types"; - import { apiClient } from "@/services/api"; + import api from "@/services/apiAxios"; import { useItemStore } from '@/stores/item' import { useStepStore } from '@/stores/step' @@ -17,7 +17,7 @@ }) async function generateQuestions (knowledge: Knowledge) { - await apiClient.post(`api/v1/knowledges/${knowledge.id}/questions`, null) + await api.post(`api/v1/knowledges/${knowledge.id}/questions`) stepStore.nextStep() } diff --git a/user-interface/src/router/index.ts b/user-interface/src/router/index.ts index 3b80ba2..e11d1bf 100644 --- a/user-interface/src/router/index.ts +++ b/user-interface/src/router/index.ts @@ -9,6 +9,12 @@ const router = createRouter({ name: 'app', component: () => import('@/views/ExperimentView.vue'), }, + { + path: '/login', + alias: '/login', + name: 'login', + component: () => import('@/views/AuthView.vue'), + } ], }) diff --git a/user-interface/src/services/apiAxios.ts b/user-interface/src/services/apiAxios.ts new file mode 100644 index 0000000..41befef --- /dev/null +++ b/user-interface/src/services/apiAxios.ts @@ -0,0 +1,26 @@ +import axios from "axios" + +const api = axios.create({ + baseURL: import.meta.env.VITE_API_URL +}); + +api.interceptors.request.use((config) => { + const token = localStorage.getItem('access_token') + if (token){ + config.headers.Authorization = `Bearer ${token}` + } + return config +}) + +export const authAPI = { + register: (data: unknown) => api.post('/api/v1/auth/register', data), + login: (username: string, password: string) => + api.post( + '/api/v1/auth/login', + new URLSearchParams({ username, password }), + { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }} + ), + getMe: () => api.get('/auth/me') +} + +export default api; \ No newline at end of file diff --git a/user-interface/src/types/types.d.ts b/user-interface/src/types/types.d.ts index e84d79f..b0e2d65 100644 --- a/user-interface/src/types/types.d.ts +++ b/user-interface/src/types/types.d.ts @@ -1,7 +1,13 @@ +interface KnowledgeCreate{ + content: string, + uri: string, +} + interface Knowledge { id: number | null, content: string, uri: string, + user: User } interface Question { @@ -9,12 +15,17 @@ interface Question { question: string, knowledgeId: number, metric: Metric | null + user: User } -interface Metric { - //id: number | null, +interface MetricCreate { question_id: number, need_index: number } -export type {Knowledge, Question, Metric} \ No newline at end of file +interface User { + username: string, + token: string +} + +export type {KnowledgeCreate, Knowledge, Question, MetricCreate} \ No newline at end of file diff --git a/user-interface/src/views/AuthView.vue b/user-interface/src/views/AuthView.vue new file mode 100644 index 0000000..ab85492 --- /dev/null +++ b/user-interface/src/views/AuthView.vue @@ -0,0 +1,58 @@ + + + + + + + + + + + + + Login + + + + \ No newline at end of file