4 Commits

Author SHA1 Message Date
00bd885873 test pr deploy 2025-10-05 23:03:50 +02:00
f492e0cc03 test pr deploy 2025-10-05 22:58:34 +02:00
cb3747357e test pr deploy 2025-10-05 22:56:35 +02:00
31bebaf247 test pr deploy 2025-10-05 22:53:51 +02:00
52 changed files with 71 additions and 4871 deletions

View File

@@ -20,15 +20,6 @@ jobs:
pr_number: ${{ github.event.pull_request.number }}
secrets: inherit
frontend:
if: github.event.action != 'closed'
name: Frontend - Build and Deploy to Cloudflare Pages (PR)
uses: ./.github/workflows/frontend-pages.yml
with:
mode: pr
pr_number: ${{ github.event.pull_request.number }}
secrets: inherit
deploy:
if: github.event.action != 'closed'
name: Helm upgrade/install (PR preview)
@@ -36,7 +27,7 @@ jobs:
concurrency:
group: pr-${{ github.event.pull_request.number }}
cancel-in-progress: false
needs: [build, frontend]
needs: [build]
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -85,11 +76,10 @@ jobs:
--set-string rabbitmq.password="$RABBITMQ_PASSWORD" \
--set-string database.password="$DB_PASSWORD"
- name: Post preview URLs as PR comment
- name: Post preview URL as PR comment
uses: actions/github-script@v7
env:
DEV_BASE_DOMAIN: ${{ secrets.BASE_DOMAIN }}
FRONTEND_URL: ${{ needs.frontend.outputs.deployed_url }}
with:
script: |
const pr = context.payload.pull_request;
@@ -97,11 +87,10 @@ jobs:
const prNumber = pr.number;
const domainBase = process.env.DEV_BASE_DOMAIN;
if (!domainBase) { core.setFailed('DEV_BASE_DOMAIN is required'); return; }
const backendDomain = `pr-${prNumber}.${domainBase}`;
const backendUrl = `https://${backendDomain}`;
const frontendUrl = process.env.FRONTEND_URL || '(not available)';
const domain = `pr-${prNumber}.${domainBase}`;
const url = `https://${domain}`;
const marker = '<!-- preview-link -->';
const body = `${marker}\nPreview environment is running\n- Frontend: ${frontendUrl}\n- Backend: ${backendUrl}\n`;
const body = `${marker}\nPreview environment is running: ${url}\n`;
const { owner, repo } = context.repo;
const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number: prNumber, per_page: 100 });
const existing = comments.find(c => c.body && c.body.includes(marker));

View File

@@ -5,11 +5,9 @@ on:
branches: [ "main" ]
paths:
- 7project/backend/**
- 7project/frontend/**
- 7project/charts/myapp-chart/**
- .github/workflows/deploy-prod.yaml
- .github/workflows/build-image.yaml
- .github/workflows/frontend-pages.yml
workflow_dispatch:
@@ -30,17 +28,10 @@ jobs:
context: 7project/backend
secrets: inherit
frontend:
name: Frontend - Build and Deploy to Cloudflare Pages (prod)
uses: ./.github/workflows/frontend-pages.yml
with:
mode: prod
secrets: inherit
deploy:
name: Helm upgrade/install (prod)
runs-on: vhs
needs: [build, frontend]
needs: [build]
steps:
- name: Checkout
uses: actions/checkout@v4

View File

@@ -1,180 +0,0 @@
name: Frontend - Build and Deploy to Cloudflare Pages
on:
workflow_call:
inputs:
mode:
description: "Build mode: 'prod' or 'pr'"
required: true
type: string
pr_number:
description: 'PR number (required when mode=pr)'
required: false
type: string
project_name:
description: 'Cloudflare Pages project name (overrides default)'
required: false
type: string
secrets:
CLOUDFLARE_API_TOKEN:
required: true
CLOUDFLARE_ACCOUNT_ID:
required: true
outputs:
deployed_url:
description: 'URL of deployed frontend'
value: ${{ jobs.deploy.outputs.deployed_url }}
# Required repository secrets:
# CLOUDFLARE_API_TOKEN - API token with Pages:Edit (or Account:Workers Scripts:Edit) permissions
# CLOUDFLARE_ACCOUNT_ID - Your Cloudflare account ID
# Optional repository variables:
# CF_PAGES_PROJECT_NAME - Default Cloudflare Pages project name
# PROD_DOMAIN - App domain for prod releases (e.g., api.example.com or https://api.example.com)
# BACKEND_URL_PR_TEMPLATE - Template for PR backend URL. Use {PR} placeholder for PR number (e.g., https://api-pr-{PR}.example.com)
jobs:
build:
name: Build frontend
runs-on: ubuntu-latest
defaults:
run:
working-directory: 7project/frontend
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: 7project/frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Compute backend URL for Vite
id: be
env:
EVENT_NAME: ${{ github.event_name }}
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
PR_TEMPLATE: ${{ vars.BACKEND_URL_PR_TEMPLATE }}
DEV_BASE_DOMAIN: ${{ secrets.BASE_DOMAIN }}
PROD_DOMAIN_VAR: ${{ vars.PROD_DOMAIN }}
PROD_DOMAIN_SECRET: ${{ secrets.PROD_DOMAIN }}
BACKEND_URL_OVERRIDE: ${{ vars.BACKEND_URL || secrets.BACKEND_URL }}
MODE: ${{ inputs.mode }}
run: |
set -euo pipefail
URL=""
# 1) Explicit override wins (from repo var or secret)
if [ -n "${BACKEND_URL_OVERRIDE:-}" ]; then
if echo "$BACKEND_URL_OVERRIDE" | grep -Eiq '^https?://'; then
URL="$BACKEND_URL_OVERRIDE"
else
URL="https://${BACKEND_URL_OVERRIDE}"
fi
else
# 2) PR-specific URL when building for PR
if [ "${MODE:-}" = "pr" ] || [ "${EVENT_NAME}" = "pull_request" ]; then
if [ -n "${PR_TEMPLATE:-}" ] && [ -n "${PR_NUMBER:-}" ] ; then
URL="${PR_TEMPLATE//\{PR\}/${PR_NUMBER}}"
elif [ -n "${DEV_BASE_DOMAIN:-}" ] && [ -n "${PR_NUMBER:-}" ]; then
URL="https://pr-${PR_NUMBER}.${DEV_BASE_DOMAIN}"
fi
fi
# 3) Fallback to PROD_DOMAIN (prefer repo var, then secret)
if [ -z "$URL" ]; then
PROD_DOMAIN="${PROD_DOMAIN_VAR:-${PROD_DOMAIN_SECRET:-}}"
if [ -n "$PROD_DOMAIN" ]; then
if echo "$PROD_DOMAIN" | grep -Eiq '^https?://'; then
URL="$PROD_DOMAIN"
else
URL="https://${PROD_DOMAIN}"
fi
fi
fi
fi
echo "Using backend URL: ${URL:-<empty>}"
echo "VITE_BACKEND_URL=${URL}" >> $GITHUB_ENV
- name: Build
run: npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: 7project/frontend/dist
deploy:
name: Deploy to Cloudflare Pages
needs: build
runs-on: ubuntu-latest
outputs:
deployed_url: ${{ steps.out.outputs.deployed_url }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: frontend-dist
path: dist
- name: Determine project name and branch
id: pname
env:
INPUT_MODE: ${{ inputs.mode }}
INPUT_PR: ${{ inputs.pr_number }}
run: |
set -euo pipefail
# Prefer manual input, then repo variable, fallback to repo-name
INPUT_NAME='${{ inputs.project_name }}'
VAR_NAME='${{ vars.CF_PAGES_PROJECT_NAME }}'
if [ -n "$INPUT_NAME" ]; then PNAME_RAW="$INPUT_NAME";
elif [ -n "$VAR_NAME" ]; then PNAME_RAW="$VAR_NAME";
else PNAME_RAW="${GITHUB_REPOSITORY##*/}-frontend"; fi
# Normalize project name to lowercase to satisfy Cloudflare Pages naming
PNAME="${PNAME_RAW,,}"
# Determine branch for Pages
if [ "${INPUT_MODE}" = "pr" ]; then
if [ -z "${INPUT_PR}" ]; then echo "pr_number is required when mode=pr"; exit 1; fi
PBRANCH="pr-${INPUT_PR}"
else
PBRANCH="main"
fi
echo "project_name=$PNAME" >> $GITHUB_OUTPUT
echo "branch=$PBRANCH" >> $GITHUB_OUTPUT
- name: Ensure Cloudflare Pages project exists
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
PNAME: ${{ steps.pname.outputs.project_name }}
run: |
set -euo pipefail
npx wrangler pages project create "$PNAME" --production-branch=main || true
- name: Deploy using Cloudflare Wrangler
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=${{ steps.pname.outputs.project_name }} --branch=${{ steps.pname.outputs.branch }}
- name: Compute deployed URL
id: out
env:
PNAME: ${{ steps.pname.outputs.project_name }}
PBRANCH: ${{ steps.pname.outputs.branch }}
run: |
set -euo pipefail
if [ "$PBRANCH" = "main" ]; then
URL="https://${PNAME}.pages.dev"
else
URL="https://${PBRANCH}.${PNAME}.pages.dev"
fi
echo "deployed_url=$URL" >> $GITHUB_OUTPUT

View File

@@ -45,11 +45,11 @@ flowchart LR
proc_cron[Task planner] --> proc_queue
proc_queue_worker --> ext_bank[(Bank API)]
proc_queue_worker --> db
client[Client/UI] <--> api[API Gateway / Web Server]
api <--> svc[Web API]
client[Client/UI] --> api[API Gateway / Web Server]
api --> svc[Web API]
svc --> proc_queue
svc <--> db[(Database)]
svc <--> cache[(Cache)]
svc --> db[(Database)]
svc --> cache[(Cache)]
```
- Components and responsibilities: What does each box do?

View File

@@ -11,7 +11,7 @@ script_location = %(here)s/alembic
# Uncomment the line below if you want the files to be prepended with date and time
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
# for all available tokens
file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory. for multiple paths, the path separator

View File

@@ -1,34 +0,0 @@
"""update categories unique
Revision ID: 390041bd839e
Revises: 63e072f09836
Create Date: 2025-10-09 15:14:31.557686
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '390041bd839e'
down_revision: Union[str, Sequence[str], None] = '63e072f09836'
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.drop_index(op.f('name'), table_name='categories')
op.create_unique_constraint('uix_name_user_id', 'categories', ['name', 'user_id'])
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('uix_name_user_id', 'categories', type_='unique')
op.create_index(op.f('name'), 'categories', ['name'], unique=True)
# ### end Alembic commands ###

View File

@@ -1,8 +1,8 @@
"""add categories
"""Init migration
Revision ID: 63e072f09836
Revision ID: 81f275275556
Revises:
Create Date: 2025-10-09 14:56:14.653249
Create Date: 2025-09-24 17:39:25.346690
"""
from typing import Sequence, Union
@@ -13,7 +13,7 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '63e072f09836'
revision: str = '81f275275556'
down_revision: Union[str, Sequence[str], None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
@@ -22,6 +22,12 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('transaction',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('amount', sa.Float(), nullable=False),
sa.Column('description', sa.String(length=255), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_table('user',
sa.Column('first_name', sa.String(length=100), nullable=True),
sa.Column('last_name', sa.String(length=100), nullable=True),
@@ -34,38 +40,13 @@ def upgrade() -> None:
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True)
op.create_table('categories',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('name', sa.String(length=100), nullable=False),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('user_id', fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
)
op.create_table('transaction',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('amount', sa.Float(), nullable=False),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('user_id', fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('category_transaction',
sa.Column('id_category', sa.Integer(), nullable=True),
sa.Column('id_transaction', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['id_category'], ['categories.id'], ),
sa.ForeignKeyConstraint(['id_transaction'], ['transaction.id'], )
)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('category_transaction')
op.drop_table('transaction')
op.drop_table('categories')
op.drop_index(op.f('ix_user_email'), table_name='user')
op.drop_table('user')
op.drop_table('transaction')
# ### end Alembic commands ###

View File

@@ -48,7 +48,7 @@ app.include_router(
# Liveness/root endpoint
@app.get("/", include_in_schema=False)
async def root():
return {"status": "ok"}
return {"status": "ok", "message": "Welcome to the FastAPI application!"}
@app.get("/authenticated-route")

View File

@@ -17,7 +17,6 @@ if not DATABASE_URL:
# Load all models to register them
from app.models.user import User
from app.models.transaction import Transaction
from app.models.categories import Category
ssl_enabled = os.getenv("MARIADB_HOST", "localhost") != "localhost"
connect_args = {"ssl": {"ssl": True}} if ssl_enabled else {}

View File

@@ -1,25 +0,0 @@
from fastapi_users_db_sqlalchemy import GUID
from sqlalchemy import Column, Integer, String, ForeignKey, Table, UniqueConstraint
from sqlalchemy.orm import relationship
from app.core.base import Base
association_table = Table(
"category_transaction",
Base.metadata,
Column("id_category", Integer, ForeignKey("categories.id")),
Column("id_transaction", Integer, ForeignKey("transaction.id"))
)
class Category(Base):
__tablename__ = "categories"
__table_args__ = (
UniqueConstraint("name", "user_id", name="uix_name_user_id"),
)
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(length=100), nullable=False)
description = Column(String(length=255), nullable=True)
user_id = Column(GUID, ForeignKey("user.id"), nullable=False)
user = relationship("User", back_populates="categories")
transactions = relationship("Transaction", secondary=association_table, back_populates="categories")

View File

@@ -1,17 +1,9 @@
from fastapi_users_db_sqlalchemy import GUID
from sqlalchemy import Column, Integer, String, Float, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, Float
from app.core.base import Base
from app.models.categories import association_table
class Transaction(Base):
__tablename__ = "transaction"
id = Column(Integer, primary_key=True, autoincrement=True)
amount = Column(Float, nullable=False)
description = Column(String(length=255), nullable=True)
user_id = Column(GUID, ForeignKey("user.id"), nullable=False)
# Relationship
user = relationship("User", back_populates="transactions")
categories = relationship("Category", secondary=association_table, back_populates="transactions")

View File

@@ -1,13 +1,7 @@
from sqlalchemy import Column, String
from sqlalchemy.orm import relationship
from fastapi_users.db import SQLAlchemyBaseUserTableUUID
from app.core.base import Base
class User(SQLAlchemyBaseUserTableUUID, Base):
first_name = Column(String(length=100), nullable=True)
last_name = Column(String(length=100), nullable=True)
# Relationship
transactions = relationship("Transaction", back_populates="user")
categories = relationship("Category", back_populates="user")

View File

@@ -25,7 +25,7 @@ spec:
- containerPort: {{ .Values.app.port }}
env:
- name: MARIADB_HOST
value: "mariadb-repl-maxscale-internal.mariadb-operator.svc.cluster.local"
value: {{ printf "%s.%s.svc.cluster.local" .Values.mariadb.mariaDbRef.name .Values.mariadb.mariaDbRef.namespace | quote }}
- name: MARIADB_PORT
value: '3306'
- name: MARIADB_DB

View File

@@ -29,7 +29,6 @@ worker:
# Queue name for Celery worker and for CRD Queue
mailQueueName: "mail_queue"
service:
port: 80

View File

@@ -1,81 +0,0 @@
# Project Evaluation Checklist
The group earn points by completing items from the categories below.
You are not expected to complete all items.
Focus on areas that align with your project goals and interests.
The core deliverables are required.
This means that you must get at least 2 points for each item in this category.
| **Category** | **Item** | **Max Points** | **Points** |
| -------------------------------- | --------------------------------------- | -------------- | ---------------- |
| **Core Deliverables (Required)** | | | |
| Codebase & Organization | Well-organized project structure | 5 | |
| | Clean, readable code | 5 | |
| | Use planning tool (e.g., GitHub issues) | 5 | |
| | Proper version control usage | 5 | |
| | Complete source code | 5 | |
| Documentation | Comprehensive reproducibility report | 10 | |
| | Updated design document | 5 | |
| | Clear build/deployment instructions | 5 | |
| | Troubleshooting guide | 5 | |
| | Completed self-assessment table | 5 | |
| | Hour sheets for all members | 5 | |
| Presentation Video | Project demonstration | 5 | |
| | Code walk-through | 5 | |
| | Deployment showcase | 5 | |
| **Technical Implementation** | | | |
| Application Functionality | Basic functionality works | 10 | |
| | Advanced features implemented | 10 | |
| | Error handling & robustness | 10 | |
| | User-friendly interface | 5 | |
| Backend & Architecture | Stateless web server | 5 | |
| | Stateful application | 10 | |
| | Database integration | 10 | |
| | API design | 5 | |
| | Microservices architecture | 10 | |
| Cloud Integration | Basic cloud deployment | 10 | |
| | Cloud APIs usage | 10 | |
| | Serverless components | 10 | |
| | Advanced cloud services | 5 | |
| **DevOps & Deployment** | | | |
| Containerization | Basic Dockerfile | 5 | |
| | Optimized Dockerfile | 5 | |
| | Docker Compose | 5 | |
| | Persistent storage | 5 | |
| Deployment & Scaling | Manual deployment | 5 | |
| | Automated deployment | 5 | |
| | Multiple replicas | 5 | |
| | Kubernetes deployment | 10 | |
| **Quality Assurance** | | | |
| Testing | Unit tests | 5 | |
| | Integration tests | 5 | |
| | End-to-end tests | 5 | |
| | Performance testing | 5 | |
| Monitoring & Operations | Health checks | 5 | |
| | Logging | 5 | |
| | Metrics/Monitoring | 5 | |
| Security | HTTPS/TLS | 5 | |
| | Authentication | 5 | |
| | Authorization | 5 | |
| **Innovation & Excellence** | | | |
| Advanced Features and | AI/ML Integration | 10 | |
| Technical Excellence | Real-time features | 10 | |
| | Creative problem solving | 10 | |
| | Performance optimization | 5 | |
| | Exceptional user experience | 5 | |
| **Total** | | **255** | **[Your Total]** |
## Grading Scale
- **Minimum Required: 100 points**
- **Maximum: 200+ points**
| Grade | Points |
| ----- | -------- |
| A | 180-200+ |
| B | 160-179 |
| C | 140-159 |
| D | 120-139 |
| E | 100-119 |
| F | 0-99 |

View File

@@ -15,7 +15,7 @@ services:
volumes:
- redis_data:/data
rabbitmq:
image: bitnamilegacy/rabbitmq:3.13.3-debian-12-r0
image: bitnami/rabbitmq:3.13.3-debian-12-r0
network_mode: host
ports:
- "5672:5672"

View File

@@ -8,8 +8,4 @@ fi
cd backend || { echo "Directory 'backend' does not exist"; exit 1; }
alembic revision --autogenerate -m "$1"
git add alembic/versions/*
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${YELLOW}Don't forget to check imports in the new migration file!${NC}"
cd - || exit

View File

@@ -1,24 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -1,73 +0,0 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

View File

@@ -1,23 +0,0 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs['recommended-latest'],
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])

View File

@@ -1,13 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>frontend</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +0,0 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.1.1",
"react-dom": "^19.1.1"
},
"devDependencies": {
"@eslint/js": "^9.36.0",
"@types/node": "^24.6.0",
"@types/react": "^19.1.16",
"@types/react-dom": "^19.1.9",
"@vitejs/plugin-react": "^5.0.4",
"eslint": "^9.36.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.22",
"globals": "^16.4.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.45.0",
"vite": "^7.1.7"
}
}

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,42 +0,0 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}

View File

@@ -1,39 +0,0 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import { BACKEND_URL } from './config'
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
<p style={{ fontSize: 12, color: '#888' }}>
Backend URL: <code>{BACKEND_URL || '(not configured)'}</code>
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
export default App

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -1,2 +0,0 @@
export const BACKEND_URL: string =
import.meta.env.VITE_BACKEND_URL ?? '';

View File

@@ -1,68 +0,0 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

View File

@@ -1,10 +0,0 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)

View File

@@ -1,28 +0,0 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"types": ["vite/client"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

View File

@@ -1,7 +0,0 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@@ -1,26 +0,0 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -1,7 +0,0 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})

View File

@@ -1,54 +0,0 @@
# Weekly Meeting Notes
- Group 8 - Personal finance tracker
- Mentor: Jaychander
Keep all meeting notes in the `meetings.md` file in your project folder.
Just copy the template below for each weekly meeting and fill in the details.
## Administrative Info
- Date: 2025-10-08
- Attendees: Dejan Ribarovski, Lukas Trkan
- Notetaker: Dejan Ribarovski
## Progress Update (Before Meeting)
Summary of what has been accomplished since the last meeting in the following categories.
### Coding
Lukas has implemented the template source directories, source files and config files necessary for deployment
- docker compose for database, redis cache and rabbit MQ
- tofu
- backend template
- frontend template
- charts templates
### Documentation
- Created GitHub issues for the next steps
- Added this document + checklist and report
## Questions and Topics for Discussion (Before Meeting)
Prepare 3-5 questions and topics you want to discuss with your mentor.
1. Anything we should add structure-wise?
2. Anything you would like us to prioritize until next week?
## Discussion Notes (During Meeting)
- start working on the report
- start coding the actual code
- write problems solved
- redo the system diagram - see the response as well
- create a meetings folder wih seperate meetings files
## Action Items for Next Week (During Meeting)
Last 3 minutes of the meeting, summarize action items.
- [ ] start coding the app logic
- [ ] start writing the report so it matches the actual progress
- [ ] redo the system diagram so it includes a response flow
---

View File

@@ -1,41 +0,0 @@
# Weekly Meeting Notes
- Group X - Project Title
- Mentor: Mentor Name
Keep all meeting notes in the `meetings.md` file in your project folder.
Just copy the template below for each weekly meeting and fill in the details.
## Administrative Info
- Date: 2025-09-19
- Attendees: Name1, Name2, Name3
- Notetaker: Name1
## Progress Update (Before Meeting)
Summary of what has been accomplished since the last meeting in the following categories.
### Coding
### Documentation
## Questions and Topics for Discussion (Before Meeting)
Prepare 3-5 questions and topics you want to discuss with your mentor.
1. Question 1
2. Question 2
3. Question 3
## Discussion Notes (During Meeting)
## Action Items for Next Week (During Meeting)
Last 3 minutes of the meeting, summarize action items.
- [ ] Action Item 1
- [ ] Action Item 2
- [ ] Action Item 3
---

View File

@@ -1,302 +0,0 @@
# Project Report
> **Instructions**:
> This template provides the structure for your project report.
> Replace the placeholder text with your actual content.
> Remove instructions that are not relevant for your project, but leave the headings along with a (NA) label.
## Project Overview
**Project Name**: [Your project name]
**Group Members**:
- Student number, Name, GitHub username
- Student number, Name, GitHub username
- Student number, Name, GitHub username
**Brief Description**:
[2-3 sentences describing what your application does and its main purpose]
## Architecture Overview
### High-Level Architecture
[Describe the overall system architecture. Consider including a diagram using mermaid or linking to an image]
```mermaid
graph TD
A[Component A] --> B[Component B]
B --> C[Component C]
```
### Components
- **Component 1**: [Description of what this component does]
- **Component 2**: [Description of what this component does]
- **Component 3**: [Description of what this component does]
### Technologies Used
- **Backend**: [e.g., Go, Node.js, Python]
- **Database**: [e.g., PostgreSQL, MongoDB, Redis]
- **Cloud Services**: [e.g., AWS EC2, Google Cloud Run, Azure Functions]
- **Container Orchestration**: [e.g., Docker, Kubernetes]
- **Other**: [List other significant technologies]
## Prerequisites
### System Requirements
- Operating System: [e.g., Linux, macOS, Windows]
- Minimum RAM: [e.g., 8GB]
- Storage: [e.g., 10GB free space]
### Required Software
- [Software 1] (version X.X or higher)
- [Software 2] (version X.X or higher)
- [etc.]
### Dependencies
```bash
# List key dependencies that need to be installed
# For example:
# Docker Engine 20.10+
# Node.js 18+
# Go 1.25+
```
## Build Instructions
### 1. Clone the Repository
```bash
git clone [your-repository-url]
cd [repository-name]
```
### 2. Install Dependencies
```bash
# Provide step-by-step commands
# For example:
# npm install
# go mod download
```
### 3. Build the Application
```bash
# Provide exact build commands
# For example:
# make build
# docker build -t myapp .
```
### 4. Configuration
```bash
# Any configuration steps needed
# Environment variables to set
# Configuration files to create
```
## Deployment Instructions
### Local Deployment
```bash
# Step-by-step commands for local deployment
# For example:
# docker-compose up -d
# kubectl apply -f manifests/
```
### Cloud Deployment
```bash
# Commands for cloud deployment
# Include any cloud-specific setup
```
### Verification
```bash
# Commands to verify deployment worked
# How to check if services are running
# Example health check endpoints
```
## Testing Instructions
### Unit Tests
```bash
# Commands to run unit tests
# For example:
# go test ./...
# npm test
```
### Integration Tests
```bash
# Commands to run integration tests
# Any setup required for integration tests
```
### End-to-End Tests
```bash
# Commands to run e2e tests
# How to set up test environment
```
## Usage Examples
### Basic Usage
```bash
# Examples of how to use the application
# Common commands or API calls
# Sample data or test scenarios
```
### Advanced Features
```bash
# Examples showcasing advanced functionality
```
---
## Presentation Video
**YouTube Link**: [Insert your YouTube link here]
**Duration**: [X minutes Y seconds]
**Video Includes**:
- [ ] Project overview and architecture
- [ ] Live demonstration of key features
- [ ] Code walkthrough
- [ ] Build and deployment showcase
## Troubleshooting
### Common Issues
#### Issue 1: [Common problem]
**Symptoms**: [What the user sees]
**Solution**: [Step-by-step fix]
#### Issue 2: [Another common problem]
**Symptoms**: [What the user sees]
**Solution**: [Step-by-step fix]
### Debug Commands
```bash
# Useful commands for debugging
# Log viewing commands
# Service status checks
```
---
## Self-Assessment Table
> Be honest and detailed in your assessments.
> This information is used for individual grading.
> Link to the specific commit on GitHub for each contribution.
| Task/Component | Assigned To | Status | Time Spent | Difficulty | Notes |
| ------------------------------------------------------------------- | ----------- | ------------- | ---------- | ---------- | ----------- |
| Project Setup & Repository | [Name] | ✅ Complete | [X hours] | Medium | [Any notes] |
| [Design Document](https://github.com/dat515-2025/group-name) | [Name] | ✅ Complete | [X hours] | Easy | [Any notes] |
| [Backend API Development](https://github.com/dat515-2025/group-name) | [Name] | ✅ Complete | [X hours] | Hard | [Any notes] |
| [Database Setup & Models](https://github.com/dat515-2025/group-name) | [Name] | ✅ Complete | [X hours] | Medium | [Any notes] |
| [Frontend Development](https://github.com/dat515-2025/group-name) | [Name] | 🔄 In Progress | [X hours] | Medium | [Any notes] |
| [Docker Configuration](https://github.com/dat515-2025/group-name) | [Name] | ✅ Complete | [X hours] | Easy | [Any notes] |
| [Cloud Deployment](https://github.com/dat515-2025/group-name) | [Name] | ✅ Complete | [X hours] | Hard | [Any notes] |
| [Testing Implementation](https://github.com/dat515-2025/group-name) | [Name] | ⏳ Pending | [X hours] | Medium | [Any notes] |
| [Documentation](https://github.com/dat515-2025/group-name) | [Name] | ✅ Complete | [X hours] | Easy | [Any notes] |
| [Presentation Video](https://github.com/dat515-2025/group-name) | [Name] | ✅ Complete | [X hours] | Medium | [Any notes] |
**Legend**: ✅ Complete | 🔄 In Progress | ⏳ Pending | ❌ Not Started
## Hour Sheet
> Link to the specific commit on GitHub for each contribution.
### [Team Member 1 Name]
| Date | Activity | Hours | Description |
| --------- | ------------------- | ---------- | ----------------------------------- |
| [Date] | Initial Setup | [X.X] | Repository setup, project structure |
| [Date] | Backend Development | [X.X] | Implemented user authentication |
| [Date] | Testing | [X.X] | Unit tests for API endpoints |
| [Date] | Documentation | [X.X] | Updated README and design doc |
| **Total** | | **[XX.X]** | |
### [Team Member 2 Name]
| Date | Activity | Hours | Description |
| --------- | -------------------- | ---------- | ----------------------------------------- |
| [Date] | Frontend Development | [X.X] | Created user interface mockups |
| [Date] | Integration | [X.X] | Connected frontend to backend API |
| [Date] | Deployment | [X.X] | Docker configuration and cloud deployment |
| [Date] | Testing | [X.X] | End-to-end testing |
| **Total** | | **[XX.X]** | |
### [Team Member 3 Name] (if applicable)
| Date | Activity | Hours | Description |
| --------- | ------------------------ | ---------- | -------------------------------- |
| [Date] | Database Design | [X.X] | Schema design and implementation |
| [Date] | Cloud Configuration | [X.X] | AWS/GCP setup and configuration |
| [Date] | Performance Optimization | [X.X] | Caching and query optimization |
| [Date] | Monitoring | [X.X] | Logging and monitoring setup |
| **Total** | | **[XX.X]** | |
### Group Total: [XXX.X] hours
---
## Final Reflection
### What We Learned
[Reflect on the key technical and collaboration skills learned during this project]
### Challenges Faced
[Describe the main challenges and how you overcame them]
### If We Did This Again
[What would you do differently? What worked well that you'd keep?]
### Individual Growth
#### [Team Member 1 Name]
[Personal reflection on growth, challenges, and learning]
#### [Team Member 2 Name]
[Personal reflection on growth, challenges, and learning]
#### [Team Member 3 Name] (if applicable)
[Personal reflection on growth, challenges, and learning]
---
**Report Completion Date**: [Date]
**Last Updated**: [Date]

View File

@@ -42,27 +42,23 @@ provider "helm" {
}
module "storage" {
source = "./modules/storage"
}
module "metrics_server" {
source = "./modules/metrics-server"#
source = "${path.module}/modules/storage"
}
module "loadbalancer" {
source = "./modules/metallb"
source = "${path.module}/modules/metallb"
depends_on = [module.storage]
metallb_ip_range = var.metallb_ip_range
}
module "cert-manager" {
source = "./modules/cert-manager"
source = "${path.module}/modules/cert-manager"
depends_on = [module.loadbalancer]
}
module "cloudflare" {
source = "./modules/cloudflare"
source = "${path.module}/modules/cloudflare"
depends_on = [module.cert-manager]
cloudflare_api_token = var.cloudflare_api_token
@@ -73,14 +69,14 @@ module "cloudflare" {
}
module "monitoring" {
source = "./modules/prometheus"
source = "${path.module}/modules/prometheus"
depends_on = [module.cloudflare]
cloudflare_domain = var.cloudflare_domain
}
module "database" {
source = "./modules/maxscale"
source = "${path.module}/modules/maxscale"
depends_on = [module.monitoring]
mariadb_password = var.mariadb_password
@@ -96,13 +92,6 @@ module "database" {
phpmyadmin_enabled = var.phpmyadmin_enabled
cloudflare_domain = var.cloudflare_domain
s3_enabled = var.s3_enabled
s3_bucket = var.s3_bucket
s3_region = var.s3_region
s3_endpoint = var.s3_endpoint
s3_key_id = var.s3_key_id
s3_key_secret = var.s3_key_secret
}
#module "argocd" {
@@ -120,7 +109,7 @@ module "database" {
#}
module "rabbitmq" {
source = "./modules/rabbitmq"
source = "${path.module}/modules/rabbitmq"
depends_on = [module.database]
base_domain = var.cloudflare_domain
rabbitmq-password = var.rabbitmq-password

View File

@@ -1,4 +1,4 @@
apiVersion: v2
name: maxscale-helm
version: 1.0.14
version: 1.0.7
description: Helm chart for MaxScale related Kubernetes manifests

View File

@@ -1,42 +0,0 @@
{{- if .Values.s3.enabled }}
apiVersion: k8s.mariadb.com/v1alpha1
kind: Backup
metadata:
name: backup
namespace: mariadb-operator
spec:
mariaDbRef:
name: mariadb-repl
namespace: mariadb-operator
schedule:
cron: "0 */3 * * *"
suspend: false
timeZone: "Europe/Prague"
maxRetention: 720h # 30 days
compression: bzip2
storage:
s3:
bucket: {{ .Values.s3.bucket | quote }}
endpoint: {{ .Values.s3.endpoint | quote }}
accessKeyIdSecretKeyRef:
name: s3-credentials
key: key_id
secretAccessKeySecretKeyRef:
name: s3-credentials
key: secret_key
region: {{ .Values.s3.region | quote }}
tls:
enabled: true
# Define a PVC to use as staging area for keeping the backups while they are being processed.
stagingStorage:
persistentVolumeClaim:
resources:
requests:
storage: 10Gi
accessModes:
- ReadWriteOnce
args:
- --single-transaction
- --all-databases
logLevel: info
{{- end }}

View File

@@ -60,8 +60,6 @@ spec:
scrapeTimeout: 10s
prometheusRelease: kube-prometheus-stack
jobLabel: mariadb-monitoring
auth:
generate: true
tls:
enabled: true

View File

@@ -1,11 +0,0 @@
{{- if .Values.s3.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: s3-credentials
namespace: mariadb-operator
type: Opaque
stringData:
key_id: "{{ .Values.s3.key_id }}"
secret_key: "{{ .Values.s3.key_secret }}"
{{- end }}

View File

@@ -28,7 +28,7 @@ spec:
- name: DATABASE_ENABLE_SSL
value: "yes"
- name: DATABASE_HOST
value: "mariadb-repl-maxscale-internal"
value: "mariadb-repl"
- name: DATABASE_PORT_NUMBER
value: "3306"
- name: PHPMYADMIN_ALLOW_NO_PASSWORD

View File

@@ -14,12 +14,4 @@ metallb:
phpmyadmin:
enabled: true
s3:
enabled: false
endpoint: ""
region: ""
bucket: ""
key_id: ""
key_secret: ""
base_domain: example.com

View File

@@ -52,14 +52,13 @@ resource "helm_release" "mariadb-operator" {
chart = "mariadb-operator"
depends_on = [ helm_release.mariadb-operator-crds, kubectl_manifest.secrets ]
namespace = "mariadb-operator"
version = "25.8.3"
timeout = 3600
}
resource "helm_release" "maxscale_helm" {
name = "maxscale-helm"
chart = "${path.module}/charts/maxscale-helm"
version = "1.0.14"
version = "1.0.7"
depends_on = [ helm_release.mariadb-operator-crds, kubectl_manifest.secrets ]
timeout = 3600
@@ -71,12 +70,6 @@ resource "helm_release" "maxscale_helm" {
{ name = "metallb.primary_ip", value = var.primary_ip },
{ name = "metallb.secondary_ip", value = var.secondary_ip },
{ name = "phpmyadmin.enabled", value = tostring(var.phpmyadmin_enabled) },
{ name = "base_domain", value = var.cloudflare_domain },
{ name = "s3.key_id", value = var.s3_key_id },
{ name = "s3.key_secret", value = var.s3_key_secret },
{ name = "s3.enabled", value = var.s3_enabled },
{ name = "s3.endpoint", value = var.s3_endpoint },
{ name = "s3.region", value = var.s3_region },
{ name = "s3.bucket", value = var.s3_bucket },
{ name = "base_domain", value = var.cloudflare_domain }
]
}

View File

@@ -56,35 +56,3 @@ variable "cloudflare_domain" {
default = "Base cloudflare domain, e.g. example.com"
nullable = false
}
variable "s3_key_id" {
description = "S3 Key ID for backups"
type = string
sensitive = true
}
variable "s3_key_secret" {
description = "S3 Key Secret for backups"
type = string
sensitive = true
}
variable "s3_enabled" {
description = "Enable S3 backups"
type = bool
}
variable "s3_endpoint" {
description = "S3 endpoint for backups"
type = string
}
variable "s3_region" {
description = "S3 region for backups"
type = string
}
variable "s3_bucket" {
description = "S3 bucket name for backups"
type = string
}

View File

@@ -1,33 +0,0 @@
terraform {
required_providers {
kubectl = {
source = "gavinbunney/kubectl"
version = "1.19.0"
}
helm = {
source = "hashicorp/helm"
version = "3.0.2"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "2.38.0"
}
}
}
# Deploy metrics-server via Helm into kube-system namespace
resource "helm_release" "metrics_server" {
name = "metrics-server"
repository = "https://kubernetes-sigs.github.io/metrics-server/"
chart = "metrics-server"
namespace = "kube-system"
wait = true
timeout = 600
recreate_pods = false
force_update = false
values = [
file("${path.module}/values.yaml")
]
}

View File

@@ -1,15 +0,0 @@
# Values overriding defaults for metrics-server Helm chart
# Fix TLS and address selection issues when scraping kubelets (common on Talos)
args:
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
- --kubelet-use-node-status-port=true
# Using hostNetwork often helps in restricted CNI/DNS environments
#hostNetwork: true
# Required when hostNetwork is true so DNS works as expected
#dnsPolicy: ClusterFirstWithHostNet
# Enable metrics API service monitor if Prometheus Operator is present (optional)
# serviceMonitor:
# enabled: true

View File

@@ -16,12 +16,6 @@ terraform {
}
}
resource "kubernetes_namespace" "rabbitmq_namespace" {
metadata {
name = "rabbitmq-system"
}
}
resource "helm_release" "rabbitmq_operator" {
name = "rabbitmq-cluster-operator"
@@ -31,6 +25,7 @@ resource "helm_release" "rabbitmq_operator" {
version = "4.4.34"
namespace = "rabbitmq-system"
create_namespace = true
# Zde můžete přepsat výchozí hodnoty chartu, pokud by bylo potřeba
# Například sledovat jen určité namespace, nastavit tolerations atd.
@@ -64,7 +59,6 @@ resource "helm_release" "rabbitmq_operator" {
value = "true"
}
]
depends_on = [kubernetes_namespace.rabbitmq_namespace]
}

View File

@@ -2,4 +2,4 @@ apiVersion: rabbitmq.com/v1beta1
kind: RabbitmqCluster
metadata:
name: 'rabbitmq-cluster'
namespace: "rabbitmq-system"
namespace: "rabbitmq"

View File

@@ -2,7 +2,7 @@ apiVersion: networking.cfargotunnel.com/v1alpha1
kind: TunnelBinding
metadata:
name: rabbit-tunnel-binding
namespace: rabbitmq-system
namespace: rabbitmq
subjects:
- name: rabbit-gui
spec:

View File

@@ -108,40 +108,3 @@ variable "rabbitmq-password" {
sensitive = true
description = "Admin password for RabbitMQ user"
}
variable "s3_key_id" {
description = "S3 Key ID for backups"
type = string
sensitive = true
nullable = false
}
variable "s3_key_secret" {
description = "S3 Key Secret for backups"
type = string
sensitive = true
nullable = false
}
variable "s3_enabled" {
description = "Enable S3 backups"
type = bool
}
variable "s3_endpoint" {
description = "S3 endpoint for backups"
type = string
}
variable "s3_region" {
description = "S3 region for backups"
type = string
}
variable "s3_bucket" {
description = "S3 bucket name for backups"
type = string
}