Skip to content

Commit

Permalink
Merge branch 'main' into feature/page/landing
Browse files Browse the repository at this point in the history
  • Loading branch information
guilherme096 authored Aug 28, 2024
2 parents bf9bec3 + c7bbb98 commit 5b4c841
Show file tree
Hide file tree
Showing 19 changed files with 367 additions and 40 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/build-backend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Build Frontend
on:
push:
paths:
- 'backend/**'
- '.github/workflows/**'
branches:
- main

pull_request:
paths:
- 'backend/**'
- '.github/workflows/**'
types:
- opened
- synchronize
- reopened

jobs:
sonarcloud:
name: SonarCloud Backend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
with:
projectBaseDir: backend
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_BACKEND }}
33 changes: 33 additions & 0 deletions .github/workflows/build-frontend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Build Frontend
on:
push:
paths:
- 'frontend/**'
- '.github/workflows/**'
branches:
- main

pull_request:
paths:
- 'frontend/**'
- '.github/workflows/**'
types:
- opened
- synchronize
- reopened

jobs:
sonarcloud:
name: SonarCloud Frontend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
with:
projectBaseDir: frontend
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_FRONTEND }}
62 changes: 62 additions & 0 deletions backend/app/dependencies/qrvalidation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import os
from hashlib import sha256
import hmac
import time
import base64


validity = 3600 # seconds (int)

k = os.urandom(32)


def sign(data: str) -> str:
hmac_sha256 = hmac.new(k, digestmod=sha256)
hmac_sha256.update(data.encode())
t = int(time.time())
expiration = t + validity
expiration = expiration.to_bytes(4, byteorder='big')
hmac_sha256.update(expiration)
hash = hmac_sha256.digest()
# xor the first 16 bytes with the last 16 bytes
hash = bytes([a ^ b for a, b in zip(hash[:16], hash[16:])])
# base85 encode the hash
signature = base64.b85encode(expiration + hash)
return signature.decode()


def verify(data: str) -> bool:
if len(data) < 25:
return False

signature = base64.b85decode(data[-25:].encode())
expiration = int.from_bytes(signature[:4], byteorder='big')
if expiration < time.time():
return False

msg = data[:-25]
hmac_sha256 = hmac.new(k, digestmod=sha256)
hmac_sha256.update(msg.encode())
hmac_sha256.update(signature[:4])
hash = hmac_sha256.digest()
hash = bytes([a ^ b for a, b in zip(hash[:16], hash[16:])])
return hash == signature[4:]


def encode(data: str) -> str:
return data + sign(data)


def decode(data: str) -> str:
if verify(data):
return data[:-25]
return None


if __name__ == "__main__":
signed_msg = sign("Hello World")
print(signed_msg)
print(verify(signed_msg))
signed_msg = signed_msg[:-1] + "A" # tamper with the message
print(verify(signed_msg))

4 changes: 2 additions & 2 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
from fastapi.middleware.cors import CORSMiddleware

from app.dependencies.database import Base, engine

from app.routes import auth, desafios
from app.routes import auth, qrcode, desafios
from app import models

Base.metadata.create_all(bind=engine)
Expand All @@ -12,6 +11,7 @@


app.include_router(auth.router)
app.include_router(qrcode.router)
app.include_router(desafios.router)

origins = [
Expand Down
8 changes: 2 additions & 6 deletions backend/app/models/atividade.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@ class Atividade(Base):
id: Mapped[int] = mapped_column("id", Integer, primary_key=True)
nome: Mapped[str] = mapped_column(String(255), nullable=False)
pontos: Mapped[int] = mapped_column(Integer, default=0)
#empresa_id: Mapped[int] = mapped_column(
# ForeignKey(f"empresas.id", ondelete="CASCADE"),
# nullable=False
#)

#empresa: Mapped["Empresas"] = relationship("Empresas", back_populates="atividades")

empresa: Mapped[List["Empresas"]] = relationship("Empresas", back_populates="atividades")
eventos: Mapped[List["Eventos"]] = relationship("Eventos", back_populates="atividade")
desafios: Mapped[List["Desafios"]] = relationship("Desafios", back_populates="atividade")
participacoes: Mapped[List["Participacao"]] = relationship("Participacao", back_populates="atividade")
10 changes: 5 additions & 5 deletions backend/app/models/desafios.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ class Desafios(Base):
nullable=False
)
descricao: Mapped[str] = mapped_column(Text, nullable=False)
#empresa_id: Mapped[int] = mapped_column(
# ForeignKey(f"empresas.id", ondelete="CASCADE"),
# nullable=False
#)
empresa_id: Mapped[int] = mapped_column(
ForeignKey(f"empresas.id", ondelete="CASCADE"),
nullable=False
)

atividade: Mapped["Atividade"] = relationship("Atividade", back_populates="desafios")
#empresa: Mapped["Empresas"] = relationship("Empresas", back_populates="desafios")
empresa: Mapped["Empresas"] = relationship("Empresas", back_populates="desafios")
9 changes: 7 additions & 2 deletions backend/app/models/empresas.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ class Empresas(Base):
nullable=False
)
sponsor_type: Mapped[str] = mapped_column(String(50), nullable=True)

atividade_id: Mapped[int] = mapped_column(
ForeignKey(f"atividade.id", ondelete="CASCADE"),
nullable=False
)
spotlight_time: Mapped[TIMESTAMP] = mapped_column(TIMESTAMP, nullable=True)

user: Mapped["Users"] = relationship("Users", back_populates="empresas")
#atividades: Mapped[List["Atividade"]] = relationship("Atividade", back_populates="empresa")
atividades: Mapped["Atividade"] = relationship("Atividade", back_populates="empresa")
eventos: Mapped[List["Eventos"]] = relationship("Eventos", back_populates="empresa")
#desafios: Mapped[List["Desafios"]] = relationship("Desafios", back_populates="empresa")
desafios: Mapped[List["Desafios"]] = relationship("Desafios", back_populates="empresa")
44 changes: 32 additions & 12 deletions backend/app/routes/desafios.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ def create_desafio(

# return joined object
return Desafio(
id=db_desafio.id,
nome=db_atividade.nome,
pontos=db_atividade.pontos,
descricao=db_desafio.descricao,
)
id=db_desafio.id,
nome=db_atividade.nome,
pontos=db_atividade.pontos,
descricao=db_desafio.descricao,
)
except Exception as e:
db.rollback()
return HTTPException(status_code=400, detail=str(e))
Expand All @@ -57,7 +57,12 @@ def create_desafio(
def read_desafios(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
# Join 'Atividade' and 'Desafios' tables
db_desafios = (
db.query(Desafios.id, Atividade.nome, Atividade.pontos, Desafios.descricao)
db.query(
Desafios.id,
Atividade.nome,
Atividade.pontos,
Desafios.descricao,
)
.join(Atividade, Desafios.atividade_id == Atividade.id)
.offset(skip)
.limit(limit)
Expand All @@ -71,7 +76,12 @@ def read_desafios(skip: int = 0, limit: int = 10, db: Session = Depends(get_db))
def read_desafio(desafio_id: int, db: Session = Depends(get_db)):
# Join 'Atividade' and 'Desafios' tables
db_desafio = (
db.query(Desafios.id, Atividade.nome, Atividade.pontos, Desafios.descricao)
db.query(
Desafios.id,
Atividade.nome,
Atividade.pontos,
Desafios.descricao,
)
.join(Atividade, Desafios.atividade_id == Atividade.id)
.filter(Desafios.id == desafio_id)
.first()
Expand All @@ -92,7 +102,12 @@ def update_desafio(

# Join 'Atividade' and 'Desafios' tables
db_desafio = (
db.query(Desafios.id, Atividade.nome, Atividade.pontos, Desafios.descricao)
db.query(
Desafios.id,
Atividade.nome,
Atividade.pontos,
Desafios.descricao,
)
.join(Atividade, Desafios.atividade_id == Atividade.id)
.filter(Desafios.id == desafio_id)
.first()
Expand All @@ -105,9 +120,9 @@ def update_desafio(
{"nome": desafio.nome, "pontos": desafio.pontos}
)
# update 'Desafios' object
db.query(Desafios).filter(Desafios.id == db_desafio.id).update(
{"descricao": desafio.descricao}
)
db.query(Desafios).filter(Desafios.id == db_desafio.id).update({
"descricao": desafio.descricao,
})
db.commit()

# return updated object
Expand All @@ -129,7 +144,12 @@ def delete_desafio(

# Join 'Atividade' and 'Desafios' tables
db_desafio = (
db.query(Desafios.id, Atividade.nome, Atividade.pontos, Desafios.descricao)
db.query(
Desafios.id,
Atividade.nome,
Atividade.pontos,
Desafios.descricao,
)
.join(Atividade, Desafios.atividade_id == Atividade.id)
.filter(Desafios.id == desafio_id)
.first()
Expand Down
53 changes: 53 additions & 0 deletions backend/app/routes/qrcode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from fastapi import APIRouter, HTTPException
from fastapi.responses import StreamingResponse

import io
import qrcode

from app.dependencies import qrvalidation
from app.schemas.qrcode import QRCodeRequest

router = APIRouter(
prefix="/qrcode",
tags=["qrcode"],
responses={404: {"description": "Not found"}}
)

@router.post("/encode")
async def qrcode_encode(data: QRCodeRequest):
if not data:
raise HTTPException(status_code=400, detail="No data provided")

# TODO: Use auth to validate user

msg = data.msg

qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(qrvalidation.encode(msg))
qr.make(fit=True)

img = qr.make_image(fill='black', back_color='white')
img_io = io.BytesIO()
img.save(img_io, 'PNG')
img_io.seek(0)

return StreamingResponse(img_io, media_type="image/png")

@router.post("/decode")
async def qrcode_decode(data: QRCodeRequest):
if not data:
raise HTTPException(status_code=400, detail="No data provided")

msg = data.msg

userId = qrvalidation.decode(msg)

if userId is None:
raise HTTPException(status_code=400, detail="Invalid QR code")

return {"msg": userId}
12 changes: 0 additions & 12 deletions backend/app/schemas/atividades.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,3 @@
class AtividadeBase(BaseModel):
nome: str
pontos: Optional[int] = 0

class AtividadeCreate(AtividadeBase):
pass

class AtividadeUpdate(AtividadeBase):
pass

class Atividade(AtividadeBase):
id: int

class Config:
from_attributes = True
2 changes: 1 addition & 1 deletion backend/app/schemas/desafios.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

class DesafioBase(AtividadeBase):
descricao: str
#empresa_id: int
empresa_id: int

class DesafioCreate(DesafioBase):
pass
Expand Down
6 changes: 6 additions & 0 deletions backend/app/schemas/qrcode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from pydantic import BaseModel



class QRCodeRequest(BaseModel):
msg: str
5 changes: 5 additions & 0 deletions backend/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Examples

These examples demonstrate the integration between different components of the system.

**They only serve as support for project development and do not constitute an integral part of the system.**
Loading

0 comments on commit 5b4c841

Please sign in to comment.