Skip to content

Commit

Permalink
Merge branch 'main' into alex/members
Browse files Browse the repository at this point in the history
  • Loading branch information
No767 authored Feb 10, 2025
2 parents b2f81b2 + 1d8310a commit 4e2132a
Show file tree
Hide file tree
Showing 23 changed files with 919 additions and 37 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/auto-approve.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ on: pull_request_target

jobs:
auto-approve:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
pull-requests: write
if: github.actor == 'dependabot[bot]'
steps:
- uses: hmarr/auto-approve-action@v3
with:
github-token: ${{ secrets.PAT_TOKEN }}
github-token: ${{ secrets.PAT_TOKEN }}
8 changes: 4 additions & 4 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ on:
jobs:
Analyze:
name: Analyze
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
actions: read
contents: read
Expand All @@ -22,11 +22,11 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Set up Python 3.13
- name: Set up Python 3
id: setup-python
uses: actions/setup-python@v5
with:
python-version: '3.13'
python-version: '3.x'

- name: Cache Dependencies
id: cache-pip
Expand All @@ -50,4 +50,4 @@ jobs:
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
upload: true
upload: true
5 changes: 3 additions & 2 deletions .github/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@ permissions:

jobs:
dependabot:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: ${{ github.actor == 'dependabot[bot]'}}
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"

- name: Enable auto-merge for Dependabot PRs
if: ${{ steps.metadata.outputs.update-type != 'version-update:semver-major' }}
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 changes: 2 additions & 2 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
Build-and-Push:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04

# We want to filter out dependabot
# automated pushes to main
Expand Down Expand Up @@ -50,4 +50,4 @@ jobs:
cache-from: type=registry,ref=ghcr.io/ucmercedacm/kanae-build-cache:server
cache-to: type=registry,mode=max,ref=ghcr.io/ucmercedacm/kanae-build-cache:server
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
labels: ${{ steps.meta.outputs.labels }}
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:

jobs:
Analyze:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04

strategy:
fail-fast: false
Expand All @@ -34,4 +34,4 @@ jobs:
run: |
RAW_PYTHON_VERSION=${{ matrix.version }}
PYTHON_VERSION=$(echo $RAW_PYTHON_VERSION | sed 's/\.//')
tox -e py$PYTHON_VERSION
tox -e py$PYTHON_VERSION
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
- main
jobs:
Bundle:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: contains(github.event.head_commit.message, '#major') || contains(github.event.head_commit.message, '#minor') || contains(github.event.head_commit.message, '#patch')
steps:
- name: Checkout Repository
Expand All @@ -31,7 +31,7 @@ jobs:


Release:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
needs: Bundle
if: contains(github.event.head_commit.message, '#major') || contains(github.event.head_commit.message, '#minor') || contains(github.event.head_commit.message, '#patch')
steps:
Expand Down Expand Up @@ -66,4 +66,4 @@ jobs:
token: ${{ secrets.PAT_TOKEN }}
tag: ${{ steps.tag_version.outputs.new_tag }}
name: ${{ steps.tag_version.outputs.new_tag }}
artifacts: "releases/kanae-docker.zip,releases/kanae-docker.tar.gz"
artifacts: "releases/kanae-docker.zip,releases/kanae-docker.tar.gz"
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]
Copyright 2024-2025 ACM @ UC Merced

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -199,4 +199,4 @@
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.
6 changes: 6 additions & 0 deletions config-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ auth:
# MUST be replaced in production
website_domain: "http://localhost:5173"

# Allowed origins for CORS
# Leave this as is unless for production
allowed_origins:
- "http://localhost:5173"
- "http://127.0.0.1:5173"

# The backend SuperToken managed or core instance.
# https://try.supertokens.com can be used for demo purposes, but we will be replacing it with a developer version
connection_uri: ""
Expand Down
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

# Development libraries
lefthook>=1.7.22,<2
pyright>=1.1.355,<2
pyright[nodejs]>=1.1.355,<2
ruff>=0.3.4,<1
tox>=4.14.2,<5
47 changes: 43 additions & 4 deletions server/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import TYPE_CHECKING, Any, Generator, Optional, Self, Union, Unpack

import asyncpg
import orjson
from fastapi import Depends, FastAPI, status
from fastapi.exceptions import HTTPException, RequestValidationError
from fastapi.openapi.utils import get_openapi
Expand All @@ -17,7 +18,14 @@
)
from supertokens_python.asyncio import list_users_by_account_info
from supertokens_python.auth_utils import LinkingToSessionUserFailedError
from supertokens_python.recipe import dashboard, emailpassword, session, thirdparty
from supertokens_python.exceptions import GeneralError
from supertokens_python.recipe import (
dashboard,
emailpassword,
session,
thirdparty,
userroles,
)
from supertokens_python.recipe.session.interfaces import SessionContainer

# isort: off
Expand Down Expand Up @@ -81,6 +89,23 @@
]


async def init(conn: asyncpg.Connection):
# Refer to https://github.com/MagicStack/asyncpg/issues/140#issuecomment-301477123
def _encode_jsonb(value):
return b"\x01" + orjson.dumps(value)

def _decode_jsonb(value):
return orjson.loads(value[1:].decode("utf-8"))

await conn.set_type_codec(
"jsonb",
schema="pg_catalog",
encoder=_encode_jsonb,
decoder=_decode_jsonb,
format="binary",
)


class Kanae(FastAPI):
pool: asyncpg.Pool

Expand Down Expand Up @@ -153,6 +178,7 @@ def __init__(
)
),
dashboard.init(),
userroles.init(),
],
mode="asgi",
)
Expand All @@ -165,6 +191,10 @@ def __init__(
RequestValidationError,
self.request_validation_error_handler, # type: ignore
)
self.add_exception_handler(
GeneralError,
self.general_error_handler, # type: ignore
)

# SuperTokens recipes overrides

Expand Down Expand Up @@ -369,21 +399,30 @@ async def request_validation_error_handler(
message = RequestValidationErrorMessage(
errors=[
RequestValidationErrorDetails(
detail=exception["msg"], context=exception["ctx"]["error"]
detail=exception["msg"],
context=exception["ctx"]["error"] if exception.get("ctx") else None,
)
for exception in exc.errors()
]
)

return ORJSONResponse(
content=message.model_dump(), status_code=status.HTTP_400_BAD_REQUEST
)

async def general_error_handler(
self, request: RouteRequest, exc: GeneralError
) -> ORJSONResponse:
return ORJSONResponse(
content={"error": str(exc)}, status_code=status.HTTP_400_BAD_REQUEST
)

### Server-related utilities

@asynccontextmanager
async def lifespan(self, app: Self):
async with asyncpg.create_pool(dsn=self.config["postgres_uri"]) as app.pool:
async with asyncpg.create_pool(
dsn=self.config["postgres_uri"], init=init
) as app.pool:
yield

def get_db(self) -> Generator[asyncpg.Pool, None, None]:
Expand Down
2 changes: 1 addition & 1 deletion server/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
app.include_router(router)
app.add_middleware(
CORSMiddleware,
allow_origins=[config["auth"]["website_domain"]],
allow_origins=config["auth"]["allowed_origins"],
allow_credentials=True,
allow_methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
allow_headers=["Content-Type"] + get_all_cors_headers(),
Expand Down
3 changes: 2 additions & 1 deletion server/migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

BE = TypeVar("BE", bound=BaseException)

REVISION_FILE = re.compile(r"(?P<kind>V)(?P<version>[0-9]+)__(?P<description>.+).sql")
REVISION_FILE = re.compile(r"(?P<kind>V)(?P<version>[\d]+)__(?P<description>.+).sql")
POSTGRES_URI = config["postgres_uri"]

CREATE_MIGRATIONS_TABLE = """
Expand Down Expand Up @@ -169,6 +169,7 @@ async def create_migrations_table() -> None:

@click.group(short_help="database migrations util", options_metavar="[options]")
def main():
# We don't have any commands to use as the base group
pass


Expand Down
67 changes: 67 additions & 0 deletions server/migrations/V3__projects.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
-- Revision Version: V3
-- Revises: V2
-- Creation Date: 2024-12-26 09:27:35.701551+00:00 UTC
-- Reason: projects

CREATE TYPE project_type AS ENUM (
'independent',
'sig_ai',
'sig_swe',
'sig_cyber',
'sig_data',
'sig_arch',
'sig_graph'
);

CREATE TYPE project_role AS ENUM (
'unaffiliated',
'member',
'former',
'lead',
'manager'
);

ALTER TABLE IF EXISTS members ADD COLUMN IF NOT EXISTS role project_role DEFAULT 'unaffiliated';

-- Projects by themselves, are basically the same type of relationship compared to events
-- They are many-to-many
-- Ex. A member can be in multiples projects (e.g. Website, UniFoodi, Fishtank, etc), and a project can have multiple members
CREATE TABLE IF NOT EXISTS projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
description TEXT,
link TEXT,
type project_type DEFAULT 'independent',
active BOOL DEFAULT TRUE,
founded_at TIMESTAMP WITH TIME ZONE DEFAULT (NOW() AT TIME ZONE 'utc')
);

-- A project also is associated with a set of "tags"
-- Meaning that many projects can have many tags
-- This basically implies that we need bridge tables to overcome the gap.
CREATE TABLE IF NOT EXISTS tags (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
description TEXT
);

-- Entirely overkill index for "performance reasons"
-- Realistically, given the scale of the data now, it doesn't matter
CREATE INDEX IF NOT EXISTS tags_title_idx ON tags (title);
CREATE INDEX IF NOT EXISTS tags_title_lower_idx ON tags (LOWER(title));

-- Bridge table for Projects <--> Tags
-- Many need to adjust the cascade for deletions later.
CREATE TABLE IF NOT EXISTS project_tags (
project_id UUID REFERENCES projects (id) ON DELETE CASCADE ON UPDATE NO ACTION,
tag_id INTEGER REFERENCES tags (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
PRIMARY KEY (project_id, tag_id)
);

-- Bridge table for Projects <--> Members
-- Many need to adjust the cascade for deletions later.
CREATE TABLE IF NOT EXISTS project_members (
project_id UUID REFERENCES projects (id) ON DELETE CASCADE ON UPDATE NO ACTION,
member_id UUID REFERENCES members (id) ON DELETE CASCADE ON UPDATE NO ACTION,
PRIMARY KEY (project_id, member_id)
);
Loading

0 comments on commit 4e2132a

Please sign in to comment.